diff options
Diffstat (limited to 'rapid')
56 files changed, 0 insertions, 28093 deletions
diff --git a/rapid/AUTHORS b/rapid/AUTHORS deleted file mode 100644 index c89d793..0000000 --- a/rapid/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Damon Lynch <damonlynch@gmail.com> diff --git a/rapid/COPYING b/rapid/COPYING deleted file mode 100644 index d511905..0000000 --- a/rapid/COPYING +++ /dev/null @@ -1,339 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -License is intended to guarantee your freedom to share and change free -software--to make sure the software is free for all its users. This -General Public License applies to most of the Free Software -Foundation's software and to any other program whose authors commit to -using it. (Some other Free Software Foundation software is covered by -the GNU Lesser General Public License instead.) You can apply it to -your programs, too. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -this service if you wish), that you receive source code or can get it -if you want it, that you can change the software or use pieces of it -in new free programs; and that you know you can do these things. - - To protect your rights, we need to make restrictions that forbid -anyone to deny you these rights or to ask you to surrender the rights. -These restrictions translate to certain responsibilities for you if you -distribute copies of the software, or if you modify it. - - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must give the recipients all the rights that -you have. You must make sure that they, too, receive or can get the -source code. And you must show them these terms so they know their -rights. - - We protect your rights with two steps: (1) copyright the software, and -(2) offer you this license which gives you legal permission to copy, -distribute and/or modify the software. - - Also, for each author's protection and ours, we want to make certain -that everyone understands that there is no warranty for this free -software. If the software is modified by someone else and passed on, we -want its recipients to know that what they have is not the original, so -that any problems introduced by others will not reflect on the original -authors' reputations. - - Finally, any free program is threatened constantly by software -patents. We wish to avoid the danger that redistributors of a free -program will individually obtain patent licenses, in effect making the -program proprietary. To prevent this, we have made it clear that any -patent must be licensed for everyone's free use or not licensed at all. - - The precise terms and conditions for copying, distribution and -modification follow. - - GNU GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License applies to any program or other work which contains -a notice placed by the copyright holder saying it may be distributed -under the terms of this General Public License. The "Program", below, -refers to any such program or work, and a "work based on the Program" -means either the Program or any derivative work under copyright law: -that is to say, a work containing the Program or a portion of it, -either verbatim or with modifications and/or translated into another -language. (Hereinafter, translation is included without limitation in -the term "modification".) Each licensee is addressed as "you". - -Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running the Program is not restricted, and the output from the Program -is covered only if its contents constitute a work based on the -Program (independent of having been made by running the Program). -Whether that is true depends on what the Program does. - - 1. You may copy and distribute verbatim copies of the Program's -source code as you receive it, in any medium, provided that you -conspicuously and appropriately publish on each copy an appropriate -copyright notice and disclaimer of warranty; keep intact all the -notices that refer to this License and to the absence of any warranty; -and give any other recipients of the Program a copy of this License -along with the Program. - -You may charge a fee for the physical act of transferring a copy, and -you may at your option offer warranty protection in exchange for a fee. - - 2. You may modify your copy or copies of the Program or any portion -of it, thus forming a work based on the Program, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) You must cause the modified files to carry prominent notices - stating that you changed the files and the date of any change. - - b) You must cause any work that you distribute or publish, that in - whole or in part contains or is derived from the Program or any - part thereof, to be licensed as a whole at no charge to all third - parties under the terms of this License. - - c) If the modified program normally reads commands interactively - when run, you must cause it, when started running for such - interactive use in the most ordinary way, to print or display an - announcement including an appropriate copyright notice and a - notice that there is no warranty (or else, saying that you provide - a warranty) and that users may redistribute the program under - these conditions, and telling the user how to view a copy of this - License. (Exception: if the Program itself is interactive but - does not normally print such an announcement, your work based on - the Program is not required to print an announcement.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Program, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Program, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Program. - -In addition, mere aggregation of another work not based on the Program -with the Program (or with a work based on the Program) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may copy and distribute the Program (or a work based on it, -under Section 2) in object code or executable form under the terms of -Sections 1 and 2 above provided that you also do one of the following: - - a) Accompany it with the complete corresponding machine-readable - source code, which must be distributed under the terms of Sections - 1 and 2 above on a medium customarily used for software interchange; or, - - b) Accompany it with a written offer, valid for at least three - years, to give any third party, for a charge no more than your - cost of physically performing source distribution, a complete - machine-readable copy of the corresponding source code, to be - distributed under the terms of Sections 1 and 2 above on a medium - customarily used for software interchange; or, - - c) Accompany it with the information you received as to the offer - to distribute corresponding source code. (This alternative is - allowed only for noncommercial distribution and only if you - received the program in object code or executable form with such - an offer, in accord with Subsection b above.) - -The source code for a work means the preferred form of the work for -making modifications to it. For an executable work, complete source -code means all the source code for all modules it contains, plus any -associated interface definition files, plus the scripts used to -control compilation and installation of the executable. However, as a -special exception, the source code distributed need not include -anything that is normally distributed (in either source or binary -form) with the major components (compiler, kernel, and so on) of the -operating system on which the executable runs, unless that component -itself accompanies the executable. - -If distribution of executable or object code is made by offering -access to copy from a designated place, then offering equivalent -access to copy the source code from the same place counts as -distribution of the source code, even though third parties are not -compelled to copy the source along with the object code. - - 4. You may not copy, modify, sublicense, or distribute the Program -except as expressly provided under this License. Any attempt -otherwise to copy, modify, sublicense or distribute the Program is -void, and will automatically terminate your rights under this License. -However, parties who have received copies, or rights, from you under -this License will not have their licenses terminated so long as such -parties remain in full compliance. - - 5. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Program or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Program (or any work based on the -Program), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Program or works based on it. - - 6. Each time you redistribute the Program (or any work based on the -Program), the recipient automatically receives a license from the -original licensor to copy, distribute or modify the Program subject to -these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties to -this License. - - 7. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Program at all. For example, if a patent -license would not permit royalty-free redistribution of the Program by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Program. - -If any portion of this section is held invalid or unenforceable under -any particular circumstance, the balance of the section is intended to -apply and the section as a whole is intended to apply in other -circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system, which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 8. If the distribution and/or use of the Program is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Program under this License -may add an explicit geographical distribution limitation excluding -those countries, so that distribution is permitted only in or among -countries not thus excluded. In such case, this License incorporates -the limitation as if written in the body of this License. - - 9. The Free Software Foundation may publish revised and/or new versions -of the General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - -Each version is given a distinguishing version number. If the Program -specifies a version number of this License which applies to it and "any -later version", you have the option of following the terms and conditions -either of that version or of any later version published by the Free -Software Foundation. If the Program does not specify a version number of -this License, you may choose any version ever published by the Free Software -Foundation. - - 10. If you wish to incorporate parts of the Program into other free -programs whose distribution conditions are different, write to the author -to ask for permission. For software which is copyrighted by the Free -Software Foundation, write to the Free Software Foundation; we sometimes -make exceptions for this. Our decision will be guided by the two goals -of preserving the free status of all derivatives of our free software and -of promoting the sharing and reuse of software generally. - - NO WARRANTY - - 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY -FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN -OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES -PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED -OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS -TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE -PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, -REPAIR OR CORRECTION. - - 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR -REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, -INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING -OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED -TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY -YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER -PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE -POSSIBILITY OF SUCH DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) <year> <name of author> - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - -Also add information on how to contact you by electronic and paper mail. - -If the program is interactive, make it output a short notice like this -when it starts in an interactive mode: - - Gnomovision version 69, Copyright (C) year name of author - Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, the commands you use may -be called something other than `show w' and `show c'; they could even be -mouse-clicks or menu items--whatever suits your program. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the program, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the program - `Gnomovision' (which makes passes at compilers) written by James Hacker. - - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice - -This General Public License does not permit incorporating your program into -proprietary programs. If your program is a subroutine library, you may -consider it more useful to permit linking proprietary applications with the -library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. diff --git a/rapid/ChangeLog b/rapid/ChangeLog deleted file mode 100644 index ca66533..0000000 --- a/rapid/ChangeLog +++ /dev/null @@ -1,1447 +0,0 @@ -Version 0.4.11 --------------- - -2015-10-22 - -Updated Brazilian, Catalan, Croatian, Czech, German, Japanese, Norwegian, -Polish, Portuguese and Swedish translations. - -Fixed crash on systems using the library Pillow 3.0. - -Updated AppData file. - - -Version 0.4.10 --------------- - -2014-02-23 - -Updated Catalan and Portuguese translations. - -Fixed bug in translations for term "Back up". - - -Version 0.4.9 -------------- - -2014-01-21 - -Updated Catalan and Spanish translations. - -Fixed occasional incorrect use of term "backup". - - -Version 0.4.9 Beta 3 --------------------- - -2014-01-20 - -Fixed packaging bug. - - -Version 0.4.9 Beta 2 --------------------- - -2014-01-20 - -Added file verification of downloaded and backed up files. - -Updated Dutch, Hungarian, Italian, Polish, Serbian, Spanish and Swedish -translations. Added Catalan translation. - - -Version 0.4.9 Beta 1 --------------------- - -2014-01-16 - -Fixed bugs #1025908 and #1186955: Finalize fix for severe performance problems -and crashes that arose from the combination of Gnome's GIO file functionality -and python's multiprocessing. The solution was to remove GIO and replace it with -regular python file processing. A nice side effect is that the program now runs -faster than ever before. - -Fixed bug #1268291: Handle cases where filesystem metadata (e.g. file -permissions) could not be copied when writing to certain file systems such as -NTFS. The program will now consider a file is copied succesfully even if the -filesystem metadata could not be updated. - -Fixed bug #1269032: When Sync RAW + JPEG sequence numbers is enabled, the -program fails to properly deal with photos with corrupt EXIF metadata. - -Fixed bug #1269079: Download failure when folder exists for only one of photo or -video on auto detected back devices. - -Updated Norwegian and Serbian translations. - - -Version 0.4.8 -------------- - -2013-12-31 - -Fixed bug #1263237: Added support for MPO files (3D images). Thanks to Jan -Kaluza for reporting it. - -Fixed bug #1263483: Some terms in the user interface are not being translated. -Thanks to Jose Luis Tirado for alerting me to the problem, which has probably -existed for some time. - -Updated Dutch, French Italian, Polish and Spanish translations. - -Version 0.4.7 -------------- - -2013-10-19 - -Added feature to download audio files that are associated with photos such as -those created by the Canon 1D series of cameras. - -Fixed bug #1242119: Choosing a new folder does not work in Ubuntu 13.10. In -Ubuntu 13.10, choosing a destination or source folder from its bookmark does not -work. The correct value is displayed in the file chooser button, but this value -is not used by Rapid Photo Downloader. - -Fixed bug #1206853: Crashes when system message notifications not functioning -properly. - -Fixed bug #909405: Allow selections by row (and not GTK default by square) when -user is dragging the mouse or using the keyboard to select. Thank you to -user 'Salukibob' for the patch. - -Added a KDE Solid action. Solid is KDE4's hardware-related framework. It detects -when the user connects a new device and display a list of related actions. -Thanks to dju` for the patch. - -Added Belarusian translation -- thanks go to Ilya Tsimokhin. Updated Swedish and -Ukrainian translations. - -Version 0.4.6 -------------- - -2013-01-22 - -Fixed bug #1083756: Application shows duplicate sources. -Fixed bug #1093330: Photo rename ignores SubSeconds when 00. - -Added extra debugging output to help trace program execution progress. - -Updated German and Spanish translations. - -Version 0.4.6 Beta 1 --------------------- - -2012-11-26 - -Fixed bug #1023586: Added RAW file support for Nikon NRW files. Rapid Photo -Downloader uses the exiv2 program to read a photo's metadata. Although the NRW -format is not officially supported by exiv2, it appears to work. If you have -NRW files and Rapid Photo Downloader crashes while reading this files, please -file a bug report. - -Preliminary and tentative fix for bug #1025908: Application freezes under -Ubuntu 12.10. This fix should not be considered final, and needs further -testing. - -Added Arabic translation. Updated Czech, Danish, French, Italian, Norwegian, -Russian, Serbian, Spanish and Swedish translations. - -Fixed missing dependencies on python-dbus and exiv2 in Debian/control file. - -Added extra debugging output to help trace program execution progress. - - -Version 0.4.5 -------------- - -2012-06-24 - -Updated Dutch, Estonian, German, Italian, Norwegian and Polish translations. - -Updated man page. - - -Version 0.4.5 Beta 1 --------------------- - -2012-06-17 - -To increase performance, thumbnails are now no longer displayed until all -devices have finished being scanned. To indicate the scan is occurring, the -progress bar now pulses and it displays a running total of the number of photos -and videos found. If scanning a very large number of files from a fast device, -the progress bar may pause. If this happens, just wait for the scan to complete. - -Fixed bug #1014203: Very poor program performance after download device changed. -The program now displays the results of scanning files much quicker if the -program's download device preferences are changed and a scan begins of a new -device. - -You can now specify via the command line whether you would like to automatically -detect devices from which to download, or manually specify the path of the -device. If specified, the option will overwrite the existing program -preferences. - -Added extra information to debugging output. - -Fixed bug #1014219: File Modify process crashes if program exits during -download. - - -Version 0.4.4 -------------- - -2012-05-30 - -Fixed bug #998320: Applied patch from Dmitry Kazimirov for option to have -subfolder generation and file renaming use a month in text format. Thanks -Dmitry! - -Fixed bug #986681: Crash when showing question dialog on some non-Gnome systems. -Thanks go to Liudas Ališauskas for the suggested fix. - -Fixed bug #995769: The Help button in the preferences dialog does not work. - -Fixed bug #996613: Updated Free Software Foundation address. - -Added Estonian translation. Updated Brazilian, Dutch, French, German, Norwegian -Bokmal, Polish, Spanish and Russian translations. - - -Version 0.4.3 -------------- - -2012-01-07 - -ExifTool is now a required dependency for Rapid Photo Downloader. ExifTool -can be used to help download videos on Linux distributions that have not -packaged hachoir-metadata, such as Fedora. - -Exiftran is another new dependency. It is used to automatically rotate -JPEG images. - -Fixed bug #704482: Delete photos option should be easily accessible - - -Added a toolbar at the top of the main program window, which gives immediate -access to the most commonly changed configuration options: where files will -be transferred from, whether they will be copied or moved, and where they will -be transferred to. - -Please when the move option is chosen, all files in the download from a device -are first copied before any are deleted. In other words, only once all -source files have been successfully copied from a device to their destination -are the source files deleted from that device. - -Fixed bug #754531: extract Exif.CanonFi.FileNumber metadata - - -Added FileNumber metadata renaming option, which is a Canon-specific Exif value -in the form xxx-yyyy, where xxx is the folder number and yyyy is the image -number. Uses ExifTool. Thanks go to Etieene Charlier for researching the fix -and contributing code to get it implemented. - -Fixed bug #695517: Added functionality to download MTS video files. There is -currently no python based library to read metadata from MTS files, but ExifTool -works. - -Fixed bug #859998: Download THM video thumbnail files - - -Some video files have THM video thumbnail files associated with them. Rapid -Photo Downloader now downloads them and renames them to match the name of the -video it is associated with. - -Fixed bug #594533: Lossless JPEG rotation based on EXIF data after picture -transfer - - -There is now an option to automatically rotate JPEG photos as they are -downloaded. The program exiftran is used to do the rotation. The feature is -turned on default. - -Fixed bug #859012: Confirm if really want to download from /home, /media or / - - -It is possible for the program's preferences to be set to download from /home, -/media or / (the root of the file system). This can result in the program -scanning a very large number of files, possibly causing the system to become -unresponsive. The program now queries the user before commencing this scan to -confirm if this is really what they want to do. - -Fixed bug #792228: clear all thumbnails when refresh command issued. - -Fixed bug #890949: Panasonic MOD format and duplicate filename issue - -Fixed a bug where the device progress bar would occasionally disappear when -the download device was changed. - -Fixed a bug where the file extensions the program downloads could not be -displayed from the command line. - -Fixed a bug where the program would crash when trying to convert a malformed -thumbnail from one image mode to another. - -Updated Czech, Danish, Dutch, French, German, Hungarian, Italian, Norwegian, -Polish, Serbian, Slovak, Spanish and Swedish translations. - - -Version 0.4.2 -------------- - -2011-10-01 - -Added feature in Preferences window to remove any paths that have previously -been marked to always be scanned or ignored. These paths can be specified when -automatic detection of Portable Storage Devices is enabled. - -Fixed bug #768026: added option to ignore paths from which to download - - -You can now specify paths never to scan for photos or videos. By default, any -path ending in .Trash or .thumbnails is ignored. Advanced users can specify -paths to never scan using python-style regular expressions. - -Fixed bug #774488: added manual back up path for videos, in addition to photos - - -You can now manually specify a path specifically in which to back up videos. This -can be the same as or different than the path in which to back up photos. - -Fixed bug #838722: wrong file types may be backed up to external devices - - -Fixed a bug when auto detection of backup devices is enabled, files of the wrong -type might be backed up. For instance, if the backup device is only meant to -store videos, and the download contains photos, photos would incorrectly be -backed up to the device in addition to videos. - -Fixed bug #815727: Back up errors and warnings incorrectly displayed in log -window - - -Fixed a bug that occurred when backing up errors are encountered, the log window -did not display them correctly, although they were correctly outputted to the -terminal window. This only occurred when more than one back up device was being -used during a download. - -Fixed bug #859242: Crash when displaying a preview of file without an extracted -thumbnail. - -Fixed bug #810559: Crash when generating thumbnail images - -Fixed bug #789995: crash when --reset-settings option is given on the command -line. - -Fixed bugs #795446 and #844714: small errors in translation template. - -Fixed a bug in the Swedish translation. - -Added Danish translation, by Torben Gundtofte-Bruun. Updated Brazilian, Czech, -Dutch, French, German, Hungarian, Italian, Japanese, Norwegian, Polish, Russian, -Serbian, Slovak, Spanish, Swedish and Turkish translations. - - -Version 0.4.1 -------------- - -2011-05-19 - -Added exif Artist and Copyright metadata options to file and subfolder name -generation. - -Fixed bug #774476: thumbnails occasionally not sorted by file modification -time. - -Fixed bug #784399: job code not prompted for after preference change. - -Fixed bug #778085: crash when trying to scan inaccessible files on mounted -camera. - -Relaxed startup test to check whether pynotify is working. On some systems, -pynotify reports it is not working even though it is. - -Added the start of an Indonesian translation. Updated Brazilian, Dutch, French, -German, Hungarian, Italian, Polish, Russian, Spanish and Ukrainian translations. - - -Version 0.4.0 -------------- - -2011-04-28 - -Features added since Release Candidate 1: - -* Allow multiple selection of files to check or uncheck for downloading. -* Automation feature to delete downloaded files from a device. - -Bug fix: translation fixes. - -Bug fix: don't crash when completing download with backups enabled and no backup -devices detected. - -Updated Dutch, French, German, Polish, Russian, Serbian and Spanish -translations. - - -Version 0.4.0 RC 1 ------------------- - -2011-04-21 - -Features added since beta 1: - -Backups have been implemented. If you are backing up to more than one device, -Rapid Photo Downloader will backup to each device simultaneously instead of one -after the other. - -When clicking the Download button before thumbnails are finished generating, -the download proceeds immediately and the thumbnails remaining to be generated -will rendered during the download itself. - -Added preferences option to disable thumbnail generation. When auto start is -enabled, this can speed-up transfers when downloading from high-speed devices. - -Access to the preferences window is now disabled while a download is occurring, -as changing preferences when files are being download can cause problems. - -Bug fix: don't crash when downloading some files after having previously -downloaded some others in the same session. - -Updated Brazilian, Dutch, German and Russian translations. - - -Version 0.4.0 beta 1 --------------------- - -2011-04-10 - -Features added since alpha 4: - -* Job Code functionality, mimicking that found in version 0.2.3. -* Eject device button for each unmountable device in main window. -* When not all files have been downloaded from a device, the number remaining - is displayed in the device's progress bar -* Overall download progress is displayed in progress bar at bottom of window -* Time remaining and download speed are displayed in the status bar -* System notification messages -* Automation features: - * Automatically start a download at program startup or when a device - is inserted. When this is enabled, to optimize performance instead of - thumbnails being generated before the files are downloaded, they are - generated during the download. - * Eject a device when all files have been downloaded from it. - * Exit when all files have been downloaded. - -The automation feature to delete downloaded files from a device will be added -only when the non-alpha/beta of version 0.4.0 is released. - -The major feature currently not implemented is backups. - -Note: if videos are downloaded, the device may not be able to be unmounted -until Rapid Photo Downloader is exited. See bug #744012 for details. - -Bug fix: adjust vertical pane position when additional devices are inserted -Bug fix: display file and subfolder naming warnings in error log - -Updated Czech, French and Russian translations. - - -Version 0.3.6 -------------- - -2011-04-05 - -This release contains a minor fix to allow program preferences to be changed -on upcoming Linux distributions like Ubuntu 11.04 and Fedora 15. - -It also contains a minor packaging change so it can be installed in Ubuntu -11.04. - - -Version 0.4.0 alpha 4 ---------------------- - -2011-04-04 - -Fixed bug #750808: errorlog.ui not included in setup.py. - - -Version 0.4.0 alpha 3 ---------------------- - -2011-04-04 - -Features added since alpha 2: - -* Error log window to display download warnings and errors. -* Synchronize RAW + JPEG Sequence values. - -Fixed bug #739021: unable to set subfolder and file rename preferences on -alpha and beta Linux distributions such as Ubuntu 11.04 or Fedora 15. - -Updated Brazilian, Dutch, French, German and Spanish translations. - -Version 0.4.0 alpha 2 ---------------------- - -2011-03-31 - -Features added since alpha 1: - -* Sample file names and subfolders are now displayed in the preferences dialog - window. -* The option to add a unique identifier to a filename if a file with the same - name already exists - -Other changes: - -* Updated INSTALL file to match new package requirements. - -* Added program icon to main window. - -* Bug fix: leave file preview mode when download devices are changed in the - preferences. - -* Bug fix: don't crash on startup when trying to display free space and photo or - video download folders do not exist. - - -Version 0.4.0 alpha 1 ---------------------- - -2011-03-24 - -Rapid Photo Downloader is much faster and sports a new user interface. It is -about 50 times faster in tasks like scanning photos and videos before the -download. It also performs the actual downloads quicker. It will use -multiple CPU cores if they are available. - -Rapid Photo Downloader now requires version 0.3.0 or newer of pyexiv2. It also -requires Python Imaging (PIL) to run. It will only run on recent Linux -distributions such as Ubuntu 10.04 or newer. It has been tested on Ubuntu 10.04, -10.10 and 11.04, as well as Fedora 14. (There is currently an unusual bug -adjusting some preferences when running Ubuntu 11.04. See bug #739021). - -This is an alpha release because it is missing features that are present in -version 0.3.5. Missing features include: - -* System Notifications of download completion -* Job Codes -* Backups as you download -* Automation features, e.g. automatically start download at startup -* Error log window (currently you must check the command line for error output) -* Time remaining status messages -* Synchronize RAW + JPEG Sequence Numbers -* Add unique identifier to a filename if a file with the same name already - exists -* Sample file names and subfolders are not displayed in the preferences window - -These missing features will be added in subsequent alpha and beta releases. - -Kaa-metadata is no longer required to download videos. However, if you -want to use Frames Per Second or Codec metadata information in subfolder or -video file names, you must ensure it is installed. This is no longer checked at -program startup. - -Thanks go to Robert Park for refreshing the translations code. - -Added Romanian translation. - - -Version 0.3.5 -------------- - -2011-03-23 - -The primary purpose of this release is update translations and fix bug #714039, -where under certain circumstances the program could crash while downloading -files. - -This is intended to be the last release in the 0.3.x series. In the upcoming -version 0.4.0, Rapid Photo Downloader is much faster and sports a new user -interface. - -Added Romanian translation. Updated Brazilian, Chinese, Croatian, Czech, Dutch, -Finnish, German, Italian, Polish and Russian translations. - - -Version 0.3.4 -------------- - -2010-12-31 - -You can now change the size of the preview image by zooming in and out using a -slider. The maximum size is double that of the previous fixed size, which was -160px. On computers with small screens such as netbooks, the maximum preview -image size is the same as the previous fixed size. Please note that Rapid Photo -Downloader only extracts thumbnails of photos; for performance reasons, it does -not create them. This means for some file formats, the thumbnails will contain -jpeg artifacts when scaled up (this is particularly true when using a version of -pyexiv2 < 0.2.0). For users who require larger preview images, this will be of -little consequence. - -When the "Strip compatible characters" feature is enabled in the Preferences -(which is the default), any white space (e.g. spaces) beginning or ending a -folder name will now be removed. - -Bug fix: camera serial numbers are now stripped of any spaces preceding or -following the actual value. - -Fixed bug #685335: inaccurate description of python packages required for -downloading videos. - -Added Croatian translation. Updated French, Norwegian Bokmal, Polish and Russian -translations. - - -Version 0.3.3 -------------- - -2010-10-24 - -Added support for mod, tod and 3gp video files. - -Hachoir-metadata is now used to extract selected metadata from video files. It -has less bugs than kaa-metadata, and is better maintained. One benefit of this -change is that more video file types can have their metadata extracted. Another -is that the video creation date is now correctly read (the creation time read by -kaa metadata was sometimes wrong by a few hours). Kaa-metadata is still used to -extract some the codec, fourcc and frames per second (FPS) metadata. - -Fixed bug #640722: Added preliminary support for Samsung SRW files. Current -versions of Exiv2 and pyexiv2 can read some but not all metadata from this new -RAW format. If you try to use metadata that cannot be extracted, Rapid Photo -Downloader will issue a warning. - -Fixed bug #550883: Generation of subfolders and filenames using the time a -download was started. - -Fixed bugs related to missing video download directory at program startup. - -Added command line option to output to the terminal information useful for -debugging. - -Added Norwegian Bokmal and Portuguese translations. Updated Brazilian -Portuguese, Dutch, Finnish, German, Hungarian, Italian, Norwegian Nynorsk, -Polish, Russian, Serbian, Slovak and Ukrainian translations. - - -Version 0.3.2 -------------- - -2010-09-12 - -Added Norwegian Nynorsk translation. Updated Chinese, Finnish, Hungarian, Dutch, -Occitan (post 1500), Polish, Brazilian Portuguese, and Russian translations. - -Fixed crash on startup when checking for free space, and the download folder does -not exist. - - -Version 0.3.1 -------------- - -2010-08-13 - -The main window now works more effectively on tiny screens, such as those found -on netbooks. If the screen height is less than or equal to 650 pixels, elements -in the preview pane are removed, and the spacing is tightened. - -The amount of free space available on the file-system where photos are to be -downloaded is now displayed in the status bar. (Note this is only the case on -moderately up-to-date Linux distributions that use GVFS, such as Ubuntu 8.10 or -higher). - -Add Chinese (simplified) translation. A big thanks goes out to the Ubuntu -Chinese translation team. Partial translations of Bulgarian, Japanese, Occitan -(post 1500), Persian, Portuguese (Brazilian), and Turkish have been added. In -the past only translations that were largely finished were added, but hopefully -adding incomplete translations will speed up their completion. Updated Finnish, -French, Hungarian, Russian, Serbian and Spanish translations. - - -Version 0.3.0 -------------- - -2010-07-10 - -The major new feature of this release is the generation of previews before -a download takes place. You can now select which photos and videos you wish to -download. - -You can now assign different Job Codes to photos and videos in the same -download. Simply select photos and videos, and from the main window choose a Job -Code for them. You can select a new Job Code,or enter a new one (press Enter -to apply it). - -The errors and warnings reported have been completely overhauled, and are now -more concise. - -Now that you can select photos and videos to download, the "Report an error" -option in case of filename conflicts has been removed. If you try to download a -photo or video that already exists, an error will be reported. If you backup a -photo or video that already exists in the backup location, a warning will be -reported (regardless of whether overwriting or skipping of backups with -conflicting filenames is chosen). - -Likewise, the option of whether to report an error or warning in case of missing -backup devices has been removed. If you have chosen to backup your photos and -videos, and a backup device or location is not found, the files will be -downloaded with warnings. - -For each device in the main window, the progress bar is now updated much more -smoothly than before. This is useful when downloading and backing up large files -such as videos. (Note this is only the case on moderately up-to-date Linux -distributions that use GVFS, such as Ubuntu 8.10 or higher). - -The minimum version of python-gtk2 (pygtk) required to run the program is now -2.12. This will affect only outdated Linux distributions. - - -Version 0.3.0 beta 6 --------------------- - -2010-07-06 - -Fixed bug #598736: don't allow file to jump to the bottom when it has a Job Code -assigned to it. - -Fixed bug #601993: don't prompt for a Job Code when downloading file of one type -(photo or video), and it's only a file of the other type that needs it. - -Log error messages are now cleaned up where a file already exists and there were -problems generating the file / subfolder name. - -Fixed crash on startup when using an old version of GIO. - -Fix crash in updating the time remaining in when downloading from extremely -slow devices. - -Set the default height to be 50 pixels taller. - -Bug fix: don't download from device that has been inserted after program starts -unless device auto detection is enabled. - -Updated German translation. - - -Version 0.3.0 beta 5 --------------------- - -2010-07-04 - -Added warning dialog if attempting to download directly from a camera. - -Add backup errors details to error log window. - -Fixed program notifications. - -Fixed corner cases with problematic file and subfolder names. - -Disabled Download All button if all files that have not been downloaded have -errors. - -Enabled and disabled Download All button, depending on status, after subfolder -or filename preferences are modified after device has been scanned. - -Don't stop a file being downloaded if a valid subfolder or filename can be -generated using a Job Code. - -Bug fix: don't automatically exit if there were errors or warnings and a -download was occurring from more than one device. - -Auto start now works correctly again. - -Job Codes are now assigned correctly when multiple downloads occur. - -Default column sorting is by date, unless a warning or error occurs when -doing the initial scan of the devices, in which case it is set to status (unless -you have already clicked on a column heading yourself, in which case it will -not change). - -Use the command xdg-user-dir to get default download directories. - -Updated Czech, Dutch, Finnish, French, Italian, Polish, Russian and Ukrainian -translations. - - -Version 0.3.0 beta 4 --------------------- - -2010-06-25 - -Fixed bug in Job Code addition in the preferences window. - -Made Job Code entry completion case insensitive. - -Update preview to be the most recently selected photo / video when -multiple files are selected. - -Don't crash when user selects a row that has its status set to be -download pending. - -Improve error log status messages and problem notifications. - - -Version 0.3.0 beta 3 --------------------- - -2010-06-23 - -First beta release of 0.3.0. - - -Version 0.2.3 -------------- - -2010-06-23 - -Updated Hungarian, Russian, Swedish and Ukrainian translations. - -Fixed bug #590725: don't crash if the theme does not associate an icon with -the detected device. - -Bug fix: update example filenames and folders when Job codes are manually -modified in the preferences window. - -This is the final release before 0.3.0, which will be a major update. - - -Version 0.2.2 -------------- - -2010-06-06 - -Added Ukrainian translation by Sergiy Gavrylov. - -Bug fix: in systems where exiv2 is not installed, don't crash on startup. - - -Version 0.2.1 -------------- - -2010-06-05 - -Bug fix: display sample photo and video names in preferences dialog using -first photo and video found on download device, where possible. This used to -work but was inadvertently disabled in a recent release. - -Bug fix: prompt for Job code when only video names or video subfolder names -use a job code. - -Bug fix: filter out Null bytes from Exif string values. These can occur when -the Exif data is corrupted. - -Updated Spanish, Russian and Finnish translations. - - -Version 0.2.0 -------------- - -2010-05-30 - -Videos can now be downloaded in much the same way photos can. - -The package kaa metadata is required to download videos. ffmpegthumbnailer is -used to display thumbnail images of certain types of videos as the download -occurs. - -kaa metadata and ffmpegthumbnailer are optional. The program will run without -them. See the INSTALL file for details. - -If a THM file with the same name as the video is present, it will be used to -generate a thumbnail for the video. If not, if ffmpegthumbnailer is installed, -Rapid Photo Downloader will use it to attempt to extract a thumbnail from the -video. THM files are not downloaded. - -For now, sequence values are shared between the downloads of videos and photos. -There may be an option to have two sets of sequence numbers in a future release. - -Due to the number of changes in the code, it is possible that regressions in the -photo downloading code may have been introduced. - -This is the first release to use version 0.2.x of the pyexiv2 library. The -most immediate benefit of this change is that thumbnail images from Nikon and -other brand cameras can be displayed. This fixes bugs #369640 and #570378. - -Please note pyexiv2 0.2.x requires exiv2 0.1.9 or above. - -Rapid Photo Downloader will still work with pyexiv2 0.1.x. However it will not -be able to display the thumbnails of some brands of camera. - -If Rapid Photo Downloader detects version 0.18.1 or higher of the exiv2 -library, it will download Panasonic's RW2 files. If it detects version 0.18.0 or -higher of the exiv2 library, it will download Mamiya's MEF files. For Rapid -Photo Downloader to be able to detect which version of the exiv2 library your -system has, it must either be running pyexiv2 >= 0.2.0, or have exiv2 installed. - -Fixed bug #483222: sometimes images could not be downloaded to NTFS partitions. -This fix was a welcome side effect of using GIO to copy images, instead of -relying on the python standard library. - -Error message headings in the Error Log are now displayed in a red font. - -Program settings and preferences can be reset using a new command line option. - -Program preferences are now more thoroughly checked for validity when the -program starts. - -Further work was done to fix bug #505492, to handle cases where the system -notification system is not working properly. - - -Version 0.1.3 -------------- - -2010-01-22 - -Fixed bug #509348: When both the backup and "Delete images from image device -upon download completion" options are selected, the program will only delete -an image from the image device if it was both downloaded to the download folder -and backed up. Previously it did not check to ensure it was backed up -correctly too. - -Fixed bug #505492: Program failed to start in environments where the -notification system has problems. - -Fixed bug #508304: User is now prompted to confirm if they really want to -remove all of their Job Codes after clicking on "Remove All" in the preferences -dialog window. - -Fixed bug #510484: Crashes when fails to create temporary download directory. - -Fixed bug #510516: Program now checks to see if the download folder exists and -is writable. If automatic detection of image devices is not enabled, it checks -to see if the image location path exists. - -Updated Czech, Dutch, Finnish, French, German, Hungarian, Italian, Polish, -Russian, Serbian, Spanish and Swedish translations. - - -Version 0.1.2 -------------- - -2010-01-16 - -New feature: photographers using RAW + JPEG mode now have the option to -synchronize sequence numbers for the matching pair of images. This option is -useful if you use the RAW + JPEG feature on your camera and you use sequence -numbers or letters in your image renaming. Enabling this option will cause the -program to detect matching pairs of RAW and JPEG images, and when they are -detected, the same sequence numbers and letters will be applied to both image -names. Furthermore, sequences will be updated as if the images were one. For -example, if 200 RAW images and 200 matching JPEG images are downloaded, the -value of Downloads today will be incremented by 200, and not 400. The same goes -for the rest of the sequence values, including the Stored number sequence -number. Images are detected by comparing filename, as well as the exif value for -the date and time the image was created (including sub seconds when the camera -records this value). This option will take effect regardless of whether the RAW -and JPEG images are stored on different memory cards or the same memory card. -Furthermore, if they are stored on separate memory cards, you can download from -them simultaneously or one after the other. The only requirement is to download -the images in the same session--in other words, for the feature to work, use as -many memory cards as you need, but do not exit the program between downloads of -the matching sets of images. - -Increased maximum sequence number length to seven digits by user request. - -Fixed bug #503704: changes in values for downloads today and stored number not -updated when changed via program preferences while a download is ready to begin. - -Fixed a rare startup bug, where the program could crash when starting a thread. - -Added Serbian translation by Milos Popovic. Updated Czech, Dutch, Finnish, -French, German, Hungarian, Italian, Polish, Russian, Slovak, Spanish and -Swedish translations. - - -Version 0.1.1 -------------- - -2010-01-05 - -Added auto delete feature. When enabled, upon the completion of a download, -images that were successfully downloaded will be deleted from the image device -they were downloaded from. Images that were not downloaded successfully will not -be deleted. - -Added keyboard accelerators for Preferences and Help. - -Added Dutch translation by Alian J. Baudrez. Updated Czech, French, German, -Hungarian, Italian, Polish, Slovak and Spanish translations. - - -Version 0.1.0 -------------- - -2009-12-07 - -Added icons to notification messages. - -Updated Czech, French, German, Hungarian, Polish, Russian, Slovak, Spanish and -Swedish translations. - -Bug fix: properly handle devices being unmounted, fixing a bug introduced in -Version 0.0.9 beta 2. - -Bug fix: When program preferences are changed, image and backup devices are now -refreshed only when the preferences dialog window is closed. - -Bug fix: Minutes component of image and folder renaming had the same code as -months. - - -Version 0.1.0 beta 2 --------------------- - -2009-11-22 - -New feature: when detection of portable storage devices is selected, the program -will prompt you whether or not to download from each device it automatically -detects. You can choose whether the program should remember the choice you make -every time it runs. This fixes bug #376020. - -Fixed bug #484432: error in adding job codes via the preferences dialog. - -Fixed bug #486886: Job code prompt can appear multiple times. - -Updated Hungarian and French translations. - - -Version 0.1.0 beta 1 --------------------- - -2009-11-14 - -This code is ready for full release, but given the magnitude of changes, a beta -seems like a good idea, simply to catch any undetected bugs. - -Added a "Job codes" option. Like the "text" option in image and subfolder name -generation, this allows you to specify text that will be placed into the file -and subfolder names. However, unlike the "text" option, which requires that the -text be directly entered via the program preferences, when using the "Job code" -option, the program will prompt for it each time a download begins. - -Made Download button the default button. Hitting enter while the main window -has focus will now start the download. - -Fixed bug #387002: added dependency in Ubuntu packages for librsvg2-common. -Thanks go to user hasp for this fix. - -Fixed bug #478620: problem with corrupted image files. Thanks go to user Katrin -Krieger for tracking this one down. - -Fixed bug #479424: some camera model names do not have numbers, but it still -makes sense to return a shortened name. Thanks go to user Wesley Harp for -highlighting this problem. - -Fixed bug #482831: program no longer crashes when auto-download is off, and a -device is inserted before another download has completed. - -Added Czech translation by Tomas Novak. - -Added French translation by Julien Valroff, Michel Ange, and Cenwen. - -Added Hungarian translation by Balazs Oveges and Andras Lorincz. - -Added Slovak translation by Tomas Novak. - -Added Swedish translation by Ulf Urden and Michal Predotka. - -Added dependency on gnome-icon-theme in Ubuntu packages. - -Added additional hour, minute and second options in image renaming and subfolder -creation. Thanks to Art Zemon for the patch. - -Malformed image date time exif values have are minimally checked to see if they -can still be used for subfolder and image renaming. Some software programs seem -to make a mess of them. - -Updated man page, including a bug fix by Julien Valroff. - - -Version 0.0.10 --------------- - -2009-06-05 - -Updated Russian translation by Sergei Sedov. - -Fixed bug #383028: program would crash when using an automatically configured -backup device and gvfs. - - - -Version 0.0.9 -------------- - -2009-06-02 - -Added Italian translation by Marco Solari and Luca Reverberi. - -Added German translation by Martin Egger and Daniel Passler. - -Added Russian translation by Sergei Sedov. - -Added Finnish translation by Mikko Ruohola. - -A Help button has been added to Preferences dialog window. Clicking it takes you -to the documentation found online at the program's website. This documentation -is now complete. - -The Preferences Dialog Window is now navigated using a list control, as it was -in early versions of the program. This change was necessary because with some -translations, the dialog window was becoming too wide with the normal tab -layout. Usability of the preferences dialog is improved: it will now resize -itself based on its content. - -Better integration with Nautilus is now possible through the setting of -MimeType=x-content/image-dcf in the program's .desktop file. - - - -Version 0.0.9 beta 4 --------------------- - -2009-05-26 - -Added Spanish translation by Jose Luis Navarro and Abel O'Rian. - -Whenever subfolder preferences are modified in the Preferences Dialog window, -they are now checked to see if they contain any extraneous entries. If -necessary, any entries like this are removed when the dialog window is closed. - -Bug fix: Changes in preferences should be applied to devices that have already -been scanned, but their images not yet downloaded. This bug was introduced in -beta 2 when fixing bug #368098. - -Bug fix: check subfolder preferences for validity before beginning download. -While image rename preferences were checked, this check was neglected. - -Bug fix: do not allow automatic downloading when there is an error in the -preferences. - - - -Version 0.0.9 beta 3 --------------------- - -2009-05-25 - -Added command line options for controlling verbosity, displaying which image -file types are recognized, and printing the program version. - -Updated man page to reflect recent program changes and new command line options. - -Prepared program for translation into other languages. Thanks go to Mark Mruss -and his blog http://www.learningpython.com for code examples and explanations. - -Polish translation by Michal Predotka. Coming soon: French, German and -Spanish translations. - -To install the program using python setup.py, the program msgfmt must now be -present. On most Linux distributions, this is found in the package gettext. - -Updated INSTALL file to reflect minimum version of pyexiv2 needed, and included -information about handling any error related to msgfmt not being installed. - -Minor fixes to logic that checks whether the Download button should be disabled -or not. This should now be more reliable. - -Bug fix: error log window can now be reopened after being closed with the "x" -button. Thanks go to ESR and his Python FAQ entry for this fix. - -Bug fix: example of subfolder name now has word wrap. Thanks go to Michal -Predotka for reporting this. - -Bug fix: don't crash when a thumbnail image is missing and the 'orientation' -variable has not yet been assigned. - - - -Version 0.0.9 beta 2 --------------------- - -2009-05-12 - -By popular demand, allow direct downloading from cameras. This support is -experimental and may not work with your camera. This is possible through the use -of the new gvfs service, provided by GIO, that exists in recent versions of -Linux. A recent version of Linux is a must. The camera must also be supported by -libgphoto2 in combination with gvfs. If you cannot browse the camera's contents -in a file manager (e.g. Nautilus), the camera download will not work until the -gvfs support is improved. - -Although this is a popular request, the reality is that downloading images -directly from the camera is often extremely slow in comparison to popping the -memory card into a card reader and downloading from that. - -Fix bug #368098: the program now starts more quickly and does not become -unresponsive when scanning devices with a large number of images. This will -hardly be noticeable by users that download from memory cards, but for those -who download from hard drives with hundreds of GBs of files -- they'll notice -a big difference. - -Fix bug #372284: for image renaming, the "image number" component is more -robust. Now, only the series of digits at the end of a filename are recognized -as the image number (obviously the file's extension is not included as being -part of the filename in this case). This allows takes in account files from -cameras like the Canon 1D series, which can have filenames like VD1D7574.CR2. - -Bug fix: don't download from volumes mounted while the program is already -running unless auto detection is specified. This bug could occur when auto -detection was enabled, then disabled, and then a volume was mounted. - - - -Version 0.0.8 -------------- - -2009-05-01 - -Added stored and downloads today sequence numbers: - -* The stored sequence number is remembered each time the program is run. - -* Downloads today tracks how many downloads are made on a given day. The time a - day "starts" is set via a new preference value, day start. This is useful if - you often photograph something late at night (e.g. concerts) and want a new - day to "start" at 3am, for instance. - -Make estimate of time remaining to download images much more accurate. - -Display download speed in status bar. - -Reorganized sequence number/letter selection in preferences. - -Add feature to detect change in program version, upgrading preferences where -necessary. - -Only allow one instance of the program to be run -- raise existing window if it -is run again. This is very useful when Rapid Photo Downloader is set to run -automatically upon insertion of a memory card. - -Add "exit at end of successful download" automation feature. - -When an image's download is skipped, the thumbnail is now lightened. - -Show a missing image icon if the thumbnail cannot be displayed for some reason. -(See bug #369640 for why thumbnail images from certain RAW files are not -displayed). - -Resize main window when an image device is inserted -- it now expands to show -each device that is inserted. - -Do not proceed with download if there is an error in the image rename or -download subfolder preferences. Instead, indicate a download error. - -Allow version 0.1.1 of pyexiv2 to be used (an older version of the library code -that is used to get information on the images, found in distributions like -Ubuntu 8.04 Hardy Heron). - -In cases where image rename or download subfolder preferences are invalid, -more helpful information is printed to the console output. - -Bug fix: better handle automated shortening Canon names like 'Canon 5D Mark II'. -It is now shortened to '5DMkII' instead of merely '5D'. - -Bug fix: re-enable example of image renaming and subfolder name generation by -using first image from the first available download device. This was -inadvertently disabled in an earlier beta. - -Bug fix: make default download subfolder YYYY/YYYYMMDD again. It was -inadvertently set to DDMMYYYY/YYYYMMDD in beta 6. - -Bug fix: don't change download button label to "pause" when "Start downloading -on program startup" is set to true. - -Bug fix: implement code to warn / give error about missing backup devices. - -Bug fix: reset progress bar after completion of successful download. - -Fix bug #317404 when clearing completed downloads. - - - -Version 0.0.8 beta 7 --------------------- - -2009-04-07 - -Added serial number metadata option for select Nikon, Canon, Olympus, Fuji, -Panasonic, and Kodak cameras. - -Added shutter count metadata option for select Nikon cameras, e.g. Nikon D300, -D3 etc. - -Add owner name metadata option for select Canon cameras, e.g. 5D Mk II etc. - - -Version 0.0.8 beta 6 --------------------- - -2009-03-31 - -Add YYYY-MM-DD and YY-MM-DD options in date time renaming, suggested by -Andreas F.X. Siegert and Paul Gear. - -Fix bug #352242 where image has no metadata. - -Handle images with corrupt metadata more gracefully. - - -Version 0.0.8 beta 5 --------------------- - -2009-03-30 - -Reduce console output. - - -Version 0.0.8 beta 4 --------------------- - -2009-03-25 - -Updated Ubuntu package. - -Version 0.0.8 beta 3 --------------------- - -2009-03-25 - -Updated Ubuntu package. - - -Version 0.0.8 beta 2 --------------------- - -2009-03-25 - -First Ubuntu package. - -Rename tarball package to suit package name. - -Updated README. - -Version 0.0.8 beta 1 --------------------- - -2009-03-20 - -Make file renaming thread safe, fixing a long-standing (if difficult to -activate) bug. - -Implement add unique identifier when file name is not unique. - -Added "Report a Problem", "Get Help Online", "Make a Donation" to Help menu. - -Implemented "Clear completed downloads" menu item. - -Download images in order they were taken (checked by time they modified). - -Fixed bug where choosing text as the first item in a download subfolder caused a -crash. - -Fixed bug where date and time choices based on when image is downloaded caused a -crash. - -Initial code to show error message when image renaming preferences have an -error. - -Fixed bug where some invalid preferences were not being caught. - -Run default python, not one specified in env, as per recommendations in Debian -Python Policy. - -Remove initial period from filename extension when generating a subfolder name -(or else the folder will be hidden). - -Check to see if metadata is essential to generate image names is now more -robust. - -Remove list control from preferences, reverting to normal tabbed preferences, -as the window was becoming too wide. - -Show notifications via libnotify. - -Error and warning icons can now be clicked on to open log window. - -Finally, last but certainly not least--implemented sequence number and sequence -letter generation: - * session sequence number - * sequence letter -Coming soon: - * downloads today sequence number - * subfolder sequence number - * stored sequence number - - -Version 0.0.7 -------------- - -2009-01-13 - -Implemented option for automatic detection of Portal Storage Devices. - -Version 0.0.6 -------------- - -2009-01-11 - -Fixed extremely annoying bug where memory cards could not be unmounted. - -Made sample image selection for preferences more robust. - -Added license details to about dialog. - -Fix bug where image rename preferences entry boxes vertically expanded, looking -very ugly indeed. - -Wrap new filename in image rename preferences when it becomes too long. - -Make default download folder selection more robust. - -Remove sequence number and sequence letter from list of choices for image rename -(not yet implemented). - -Bug #314825: fix by not calling gnomevfs.get_local_path_from_uri() unless -strictly necessary. - -Version 0.0.5 -------------- - -2009-01-09 - -Implement auto download on device insertion, and auto download on program -startup. - -Increase default width of preferences dialog box. - -Add vertical scrollbar to image rename preferences. - -Fixes for bugs #313463 & #313462. - -Version 0.0.4 -------------- - -2009-01-06 - -Bug #314284: Implement backup functionality. - -Bug #314285: Insert debugging code to help determine the cause of this bug. - -Version 0.0.3 -------------- - -2009-01-03 - -Bug #313398: Fix bug where application needed to be restarted for new -preferences to take effect. - -Added setup.py installer. - -Version 0.0.2 -------------- - -Dependencies: -Pyexiv 0.1.2 - -Updated metadata code to reflect changes in pyexiv library. - -Version 0.0.1 -------------- - -Initial release. diff --git a/rapid/INSTALL b/rapid/INSTALL deleted file mode 100644 index 84ef53f..0000000 --- a/rapid/INSTALL +++ /dev/null @@ -1,52 +0,0 @@ -Rapid Photo Downloader requires the following software: - -* Python 2.7 -* Pyexiv2 0.3.0 or higher -* python-gnome2 2.28 or higher -* python-gtk2 2.17 or higher -* python-gconf 2.28 or higher -* python-notify 0.1.1 or higher -* python-imaging 2.0.0 or higher (Pillow) -* librsvg2-common 2.26 or higher -* exiftool -* exiftran - -To run Rapid Photo Downloader you will need all the software mentioned above. - -If you want to download videos, you should install: - -- python-hachoir-metadata -- python-kaa-metadata -- ffmpegthumbnailer - -hachoir metadata is required to download videos. kaa metadata is used to -extract additional metadata from videos. ffmpegthumbnailer is used only to -display thumbnail images before the download occurs. This is a useful feature, -and if you can install it, it is strongly recommended. - -hachoir metadata, kaa metadata and ffmpegthumbnailer are optional. The program -will run without them. - -To start from a fairly basic system, I suggest the following: - -1. Install the required gnome packages from your Linux distribution's package - repositories. On a recent Linux distribution, expect all to be available for - easy download and installation. If you already use gnome, most of the - packages will already be installed. Do confirm that python-gconf, - python-glade2, and gnome-python are installed, because they - may not be quite so common in a base system. - -2. Install pyexiv2. You can probably do that using your distributions's package - repositories (look for python-pyexiv2). But if not, there are instructions - here: - - http://tilloy.net/dev/pyexiv2/download.html - -3. Install this application from the tarball. You probably need to be the - super user (root) to be able to do this: - - sudo python setup.py install - - If you get an error saying 'Error while running msgfmt', you will need to - install the gettext package on your system, which contains the msgfmt - program. diff --git a/rapid/ValidatedEntry.py b/rapid/ValidatedEntry.py deleted file mode 100644 index cb453f4..0000000 --- a/rapid/ValidatedEntry.py +++ /dev/null @@ -1,383 +0,0 @@ -# Copyright (c) 2006, Daniel J. Popowich -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation files -# (the "Software"), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, -# publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -# -# Send bug reports and contributions to: -# -# dpopowich AT astro dot umass dot edu -# - -''' -ValidatedEntry.py - -Provides ValidatedEntry, a subclass of gtk.Entry which validates -input. - -Usage: create an instance of ValidatedEntry, specifying the function -to validate input. E.g.: - - : def money(text): - : "validate input to be monetary value" - : ... - : - : money_entry = ValidatedEntry(money) - -Validation functions must accept one argument, the text to be -validated, and must return one of: - - 1: the input is valid. - 0: the input is invalid and should not be displayed. - -1: the input is partially valid and will be displayed (and by - default with a different background color). - -Three module-level variables are defined for the convenience of -validation function writers: VALID (1), INVALID (0), PARTIAL (-1). - -There is one public method, isvalid(), which will return True if the -current text is valid. - -Note: care should be taken when implementing validation functions to -allow empty strings to be VALID or at least PARTIAL. An empty string -should never be INVALID. - -Note: the hooks for calling the validation function are implemented by -connecting the object to handlers for the gtk.Editable "insert-text" -and "delete-text" signals. These handlers are connected to instances -in the constructor, so will, by default, be called before other -handlers connected to the widgets for "*-text" signals. When input is -INVALID, stop_emission() is called, so later handlers for "*-text" -signals will not be called. - -See the doc string for ValidatedEntry.__init__ for more details. - -''' - -import pygtk -pygtk.require('2.0') - -import gtk -import gtk.gdk - -if gtk.gtk_version < (2, 8): - import warnings - - msg ='''This module was developed and tested with version 2.8.9 of gtk. -You are using version %d.%d.%d. Your milage may vary''' % gtk.gtk_version - warnings.warn(msg) - -# major, minor, patch -version = 1, 0, 4 - -PARTIAL, INVALID, VALID = range(-1,2) - -class ValidatedEntry(gtk.Entry): - - white = gtk.gdk.color_parse('white') - yellow = gtk.gdk.color_parse('yellow') - - def __init__(self, valid_func, - max=0, - use_bg=True, valid_bg=white, partial_bg=yellow, - error_func=None): - ''' - Create instance of validating gtk.Entry. - - valid_func: the function to validate input. See module doc - string for details. - - max: passed to gtk.Entry constructor. (default: 0) - - use_bg: if True (the default) set the base color of the - widget to indicate validity; see valid_bg and partial_bg. - - valid_bg: a gtk.gdk.Color; the base color of the widget when - the input is valid. (default: white) - - partial_bg: a gtk.gdk.Color; the base color of the widget when - the input is partially valid. (default: yellow) - - error_func: a function to call (with no arguments) when - valid_func returns INVALID. If None (the default) - the default action will be to emit a short beep. - ''' - - assert valid_func('') != INVALID, 'valid_func cannot return INVALID for an empty string' - - gtk.Entry.__init__(self, max) - - self.__valid_func = valid_func - self.__use_bg = use_bg - self.__valid_bg = valid_bg - self.__partial_bg = partial_bg - self.__error_func = (error_func or - gtk.gdk.display_get_default().beep) - - self.connect('insert-text', self.__insert_text_cb) - self.connect('delete-text', self.__delete_text_cb) - - # bootstrap with an empty string (so the box will appear with - # the partial_bg if an empty string is PARTIAL) - self.insert_text('') - - def isvalid(self): - return self.__isvalid - - def __insert_text_cb(self, entry, text, length, position): - 'callback for "insert-text" signal' - - # generate what the new text will be - text = text[:length] - pos = self.get_position() - old = self.get_text() - new = old[:pos] + text + old[pos:] - - # validate the new text - self.__validate(new, 'insert-text') - - def __delete_text_cb(self, entry, start, end): - 'callback for "delete-text" signal' - - # generate what the new text will be - old = self.get_text() - new = old[:start] + old[end:] - - # validate the new text - self.__validate(new, 'delete-text') - - def __validate(self, text, signal): - 'calls the user-provided validation function' - - # validate - r = self.__valid_func(text) - if r == VALID: - self.__isvalid = True - if self.__use_bg: - self.modify_base(gtk.STATE_NORMAL, self.__valid_bg) - elif r == PARTIAL: - self.__isvalid = False - if self.__use_bg: - self.modify_base(gtk.STATE_NORMAL, self.__partial_bg) - else: - # don't set self.__isvalid: since we're not displaying the - # new value, the validity should be whatever it was before - self.stop_emission(signal) - self.__error_func() - - -###################################################################### -# -# Sample validation functions to use with ValidatedEntry -# -###################################################################### - -import re - - -# STRING (non-empty after stripping) -def v_nonemptystring(value): - ''' - VALID: non-empty string after stripping whitespace - PARTAL: empty or all whitespace - INVALID: N/A - ''' - if value.strip(): - return VALID - return PARTIAL - -# INT -def v_int(value): - ''' - VALID: any postive or negative integer - PARTAL: empty or leading "-" - INVALID: non-numeral - ''' - v = value.strip() - if not v or v == '-': - return PARTIAL - try: - int(value) - return VALID - except: - return INVALID - -# FLOAT -def v_float(value): - ''' - VALID: any postive or negative floating point - PARTAL: empty or leading "-", "." - INVALID: non-numeral - ''' - v = value.strip() - if not v or v in ('-', '.', '-.'): - return PARTIAL - try: - float(value) - return VALID - except: - return INVALID - - -# ISBN -_isbnpartial = re.compile('[0-9]{0,9}[0-9xX]?$') -def v_isbn(v): - - '''Validate ISBN input. - - From the isbn manual, section 4.4: - - The check digit is the last digit of an ISBN. It is calculated on - a modulus 11 with weights 10-2, using X in lieu of 10 where ten - would occur as a check digit. This means that each of the first - nine digits of the ISBN -- excluding the check digit itself -- is - multiplied by a number ranging from 10 to 2 and that the resulting - sum of the products, plus the check digit, must be divisible by 11 - without a remainder.''' - - - if _isbnpartial.match(v): - # isbn is ten characters in length - if len(v) < 10: - return PARTIAL - - s = 0 - - for i, c in enumerate(v): - s += (c in 'xX' and 10 or int(c)) * (10 - i) - - if s % 11 == 0: - return VALID - - return INVALID - -# MONEY -# re for (possibly negative) money -_money_re = re.compile('-?\d*(\.\d{1,2})?$') -# validation function for money -def v_money(value): - ''' - VALID: any postive or negative floating point with at most two - digits after the decimal point. - PARTAL: empty or leading "-", "." - INVALID: non-numeral or more than two digits after the decimal - point. - ''' - if not value or value == '-' or value[-1] == '.': - return PARTIAL - - if _money_re.match(value): - return VALID - - return INVALID - -# PHONE -# the characters in a phone number -_phonechars = re.compile('[- 0-9]*$') -# valid phone number: [AC +]EXT-LINE -_phone = re.compile('([2-9][0-8][0-9]\s+)?[2-9][0-9]{2}-[0-9]{4}$') -def v_phone(value): - ''' - VALID: any phone number of the form: EXT-LINE -or- AC EXT-LINE. - PARTAL: any characters that make up a valid #. - INVALID: characters that are not used in a phone #. - ''' - if _phone.match(value): - return VALID - if _phonechars.match(value): - return PARTIAL - return INVALID - -def empty_valid(vfunc): - - ''' - empty_valid is a factory function returning a validation function. - All of the validation functions in this module return PARTIAL for - empty strings which, in effect, forces non-empty input. There may - be a case where, e.g., you want money input to be optional, but - v_money will not consider empty input VALID. Instead of writing - another validation function you can instead use empty_valid(). By - wrapping a validation function with empty_valid(), input (after - stripping), if empty, will be considered VALID. E.g.: - - ventry = ValidatedEntry(empty_valid(v_money)) - - It is recommended that all your validation functions treat empty - input as PARTIAL, for consistency across all validation functions - and for use with empty_valid(). - ''' - - def validate(value): - if not value.strip(): - return VALID - return vfunc(value) - - return validate - - -def bounded(vfunc, conv, minv=None, maxv=None): - - ''' - bounded is a factory function returning a validation function - providing bounded input. E.g., you may want an entry that accepts - integers, but within a range, say, a score on a test graded in - whole numbers from 0 to 100: - - score_entry = ValidatedEntry(bounded(v_int, int, 0, 100)) - - Arguments: - - vfunc: A validation function. - conv: A callable that accepts a string argument (the text in - the entry) and returns a value to be compared to minv - and maxv. - minv: None or a value of the same type returned by conv. If - None, there is no minimum value enforced. If a value, - it will be the minimum value considered VALID. - maxv: None or a value of the same type returned by conv. If - None, there is no maximum value enforced. If a value, - it will be the maximum value considered VALID. - - One or both of minv/maxv must be specified. - - The function returned will call vfunc on entry input and if vfunc - returns VALID, the input will be converted by conv and compared to - minv/maxv. If the converted value is within the bounds of - minv/maxv then VALID will be returned, else PARTIAL will be - returned. - - ''' - - assert minv is not None or maxv is not None, \ - 'One of minv/maxv must be specified' - - def F(value): - - r = vfunc(value) - if r == VALID: - v = conv(value) - if minv is not None and v < minv: - return PARTIAL - if maxv is not None and v > maxv: - return PARTIAL - return r - - return F - - diff --git a/rapid/__init__.py b/rapid/__init__.py deleted file mode 100644 index 792d600..0000000 --- a/rapid/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/rapid/backupfile.py b/rapid/backupfile.py deleted file mode 100644 index 0247346..0000000 --- a/rapid/backupfile.py +++ /dev/null @@ -1,264 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011 - 2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import multiprocessing -import tempfile -import os -import errno -import hashlib - -import shutil -import io - -import logging -logger = multiprocessing.get_logger() - -import rpdmultiprocessing as rpdmp -import rpdfile -import problemnotification as pn -import config - -PHOTO_BACKUP = 1 -VIDEO_BACKUP = 2 -PHOTO_VIDEO_BACKUP = 3 - -from gettext import gettext as _ -from copyfiles import copy_file_metadata - - -class BackupFiles(multiprocessing.Process): - def __init__(self, path, name, - batch_size_MB, results_pipe, terminate_queue, - run_event): - multiprocessing.Process.__init__(self) - self.results_pipe = results_pipe - self.terminate_queue = terminate_queue - self.batch_size_bytes = batch_size_MB * 1048576 # * 1024 * 1024 - self.io_buffer = 1048576 - self.path = path - self.mount_name = name - self.run_event = run_event - - - def check_termination_request(self): - """ - Check to see this process has not been requested to immediately terminate - """ - if not self.terminate_queue.empty(): - x = self.terminate_queue.get() - # terminate immediately - logger.info("Terminating file backup") - return True - return False - - - def update_progress(self, amount_downloaded, total): - self.amount_downloaded = amount_downloaded - chunk_downloaded = amount_downloaded - self.bytes_downloaded - if (chunk_downloaded > self.batch_size_bytes) or (amount_downloaded == total): - self.bytes_downloaded = amount_downloaded - self.results_pipe.send((rpdmp.CONN_PARTIAL, (rpdmp.MSG_BYTES, (self.scan_pid, self.pid, self.total_downloaded + amount_downloaded, chunk_downloaded)))) - if amount_downloaded == total: - self.bytes_downloaded = 0 - - def backup_additional_file(self, dest_dir, full_file_name): - """Backs up small files like XMP or THM files""" - dest_name = os.path.join(dest_dir, os.path.split(full_file_name)[1]) - - try: - logger.debug("Backing up additional file %s...", dest_name) - shutil.copyfile(full_file_name, dest_name) - logger.debug("...backing up additional file %s succeeded", dest_name) - except: - logger.error("Backup of %s failed", full_file_name) - - try: - copy_file_metadata(full_file_name, dest_name, logger) - except: - logger.error("Unknown error updating filesystem metadata when copying %s", full_file_name) - - def run(self): - - self.bytes_downloaded = 0 - self.total_downloaded = 0 - - while True: - - self.amount_downloaded = 0 - move_succeeded, do_backup, rpd_file, path_suffix, backup_duplicate_overwrite, verify_file, download_count = self.results_pipe.recv() - if rpd_file is None: - # this is a termination signal - return None - # pause if instructed by the caller - self.run_event.wait() - - if self.check_termination_request(): - return None - - backup_succeeded = False - self.scan_pid = rpd_file.scan_pid - - if move_succeeded and do_backup: - self.total_reached = False - - if path_suffix is None: - dest_base_dir = self.path - else: - dest_base_dir = os.path.join(self.path, path_suffix) - - - dest_dir = os.path.join(dest_base_dir, rpd_file.download_subfolder) - backup_full_file_name = os.path.join( - dest_dir, - rpd_file.download_name) - - if not os.path.isdir(dest_dir): - # create the subfolders on the backup path - try: - logger.debug("Creating subfolder %s on backup device %s...", dest_dir, self.mount_name) - os.makedirs(dest_dir) - logger.debug("...backup subfolder created") - except IOError as inst: - # There is a tiny chance directory may have been created by - # another process between the time it takes to query and - # the time it takes to create a new directory. - # Ignore such errors. - if inst.errno <> errno.EEXIST: - logger.error("Failed to create backup subfolder: %s", dest_dir) - msg = "%s %s", inst.errno, inst.strerror - logger.error(msg) - rpd_file.add_problem(None, pn.BACKUP_DIRECTORY_CREATION, self.mount_name) - rpd_file.add_extra_detail('%s%s' % (pn.BACKUP_DIRECTORY_CREATION, self.mount_name), msg) - rpd_file.error_title = _('Backing up error') - rpd_file.error_msg = \ - _("Destination directory could not be created: %(directory)s\n") % \ - {'directory': dest_dir, } + \ - _("Source: %(source)s\nDestination: %(destination)s") % \ - {'source': rpd_file.download_full_file_name, - 'destination': backup_full_file_name} + "\n" + \ - _("Error: %(inst)s") % {'inst': msg} - - - backup_already_exists = os.path.exists(backup_full_file_name) - if backup_already_exists: - if backup_duplicate_overwrite: - rpd_file.add_problem(None, pn.BACKUP_EXISTS_OVERWRITTEN, self.mount_name) - msg = _("Backup %(file_type)s overwritten") % {'file_type': rpd_file.title} - else: - rpd_file.add_problem(None, pn.BACKUP_EXISTS, self.mount_name) - msg = _("%(file_type)s not backed up") % {'file_type': rpd_file.title_capitalized} - - rpd_file.error_title = _("Backup of %(file_type)s already exists") % {'file_type': rpd_file.title} - rpd_file.error_msg = \ - _("Source: %(source)s\nDestination: %(destination)s") % \ - {'source': rpd_file.download_full_file_name, 'destination': backup_full_file_name} + "\n" + msg - - if backup_already_exists and not backup_duplicate_overwrite: - logger.warning(msg) - else: - try: - logger.debug("Backing up file %s on device %s...", download_count, self.mount_name) - - dest = io.open(backup_full_file_name, 'wb', self.io_buffer) - src = io.open(rpd_file.download_full_file_name, 'rb', self.io_buffer) - total = rpd_file.size - amount_downloaded = 0 - while True: - # first check if process is being terminated - if self.check_termination_request(): - logger.debug("Closing partially written temporary file") - dest.close() - src.close() - return None - else: - chunk = src.read(self.io_buffer) - if chunk: - dest.write(chunk) - amount_downloaded += len(chunk) - self.update_progress(amount_downloaded, total) - else: - break - dest.close() - src.close() - backup_succeeded = True - if verify_file: - md5 = hashlib.md5(open(backup_full_file_name).read()).hexdigest() - if md5 <> rpd_file.md5: - backup_succeeded = False - logger.critical("%s file verification FAILED", rpd_file.name) - logger.critical("The %s did not back up correctly!", rpd_file.title) - rpd_file.add_problem(None, pn.BACKUP_VERIFICATION_FAILED, self.mount_name) - rpd_file.error_title = rpd_file.problem.get_title() - rpd_file.error_msg = _("%(problem)s\nFile: %(file)s") % \ - {'problem': rpd_file.problem.get_problems(), - 'file': rpd_file.download_full_file_name} - - logger.debug("...backing up file %s on device %s succeeded", download_count, self.mount_name) - if backup_already_exists: - logger.warning(msg) - except (IOError, OSError) as inst: - logger.error("Backup of %s failed", backup_full_file_name) - msg = "%s %s", inst.errno, inst.strerror - rpd_file.add_problem(None, pn.BACKUP_ERROR, self.mount_name) - rpd_file.add_extra_detail('%s%s' % (pn.BACKUP_ERROR, self.mount_name), msg) - rpd_file.error_title = _('Backing up error') - rpd_file.error_msg = \ - _("Source: %(source)s\nDestination: %(destination)s") % \ - {'source': rpd_file.download_full_file_name, 'destination': backup_full_file_name} + "\n" + \ - _("Error: %(inst)s") % {'inst': msg} - logger.error("%s:\n%s", rpd_file.error_title, rpd_file.error_msg) - - if backup_succeeded: - try: - copy_file_metadata(rpd_file.download_full_file_name, backup_full_file_name, logger) - except: - logger.error("Unknown error updating filesystem metadata when copying %s", rpd_file.download_full_file_name) - - - if not backup_succeeded: - if rpd_file.status == config.STATUS_DOWNLOAD_FAILED: - rpd_file.status = config.STATUS_DOWNLOAD_AND_BACKUP_FAILED - else: - rpd_file.status = config.STATUS_BACKUP_PROBLEM - else: - # backup any THM, audio or XMP files - if rpd_file.download_thm_full_name: - self.backup_additional_file(dest_dir, - rpd_file.download_thm_full_name) - if rpd_file.download_audio_full_name: - self.backup_additional_file(dest_dir, - rpd_file.download_audio_full_name) - if rpd_file.download_xmp_full_name: - self.backup_additional_file(dest_dir, - rpd_file.download_xmp_full_name) - - self.total_downloaded += rpd_file.size - bytes_not_downloaded = rpd_file.size - self.amount_downloaded - if bytes_not_downloaded and do_backup: - self.results_pipe.send((rpdmp.CONN_PARTIAL, (rpdmp.MSG_BYTES, (self.scan_pid, self.pid, self.total_downloaded, bytes_not_downloaded)))) - - self.results_pipe.send((rpdmp.CONN_PARTIAL, (rpdmp.MSG_FILE, - (backup_succeeded, do_backup, rpd_file)))) - - - - - - diff --git a/rapid/config.py b/rapid/config.py deleted file mode 100644 index 03b86fa..0000000 --- a/rapid/config.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: latin1 -*- -### Copyright (C) 2007 - 2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -version = '0.4.11' - -GCONF_KEY="/apps/rapid-photo-downloader" - -DBUS_NAME = "net.damonlynch.RapidPhotoDownloader" - -#i18n -APP_NAME = "rapid-photo-downloader" - -MEDIA_LOCATION = "/media" - -SKIP_DOWNLOAD = "skip download" -ADD_UNIQUE_IDENTIFIER = "add unique identifier" - -# These next three values are fall back values that are used only -# if calls to xdg-user-dir fail -DEFAULT_PHOTO_LOCATIONS = ['Pictures', 'Photos'] -DEFAULT_BACKUP_LOCATION = 'Pictures' -DEFAULT_VIDEO_BACKUP_LOCATION = 'Videos' - -DEFAULT_VIDEO_LOCATIONS = ['Videos'] - -CRITICAL_ERROR = 1 -SERIOUS_ERROR = 2 -WARNING = 3 - -STATUS_DOWNLOAD_PENDING = 0 # going to try to download it -STATUS_DOWNLOADED = 1 # downloaded successfully -STATUS_DOWNLOADED_WITH_WARNING = 2 # downloaded ok but there was a warning -STATUS_BACKUP_PROBLEM = 3 # downloaded ok, but the file was not backed up, or had a problem (overwrite or duplicate) -STATUS_NOT_DOWNLOADED = 4 # has not yet been downloaded (but might be if the user chooses) -STATUS_DOWNLOAD_AND_BACKUP_FAILED = 5 # tried to download but failed, and the backup failed or had an error -STATUS_DOWNLOAD_FAILED = 6 # tried to download but failed -STATUS_WARNING = 7 # warning (shown in pre-download preview) -STATUS_CANNOT_DOWNLOAD = 8 # cannot be downloaded - -DEFAULT_WINDOW_WIDTH = 670 -DEFAULT_WINDOW_HEIGHT = 650 - - diff --git a/rapid/copyfiles.py b/rapid/copyfiles.py deleted file mode 100644 index c63e60f..0000000 --- a/rapid/copyfiles.py +++ /dev/null @@ -1,334 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import multiprocessing -import tempfile -import os -import random -import string - -import logging -logger = multiprocessing.get_logger() - -import rpdmultiprocessing as rpdmp -import rpdfile -import problemnotification as pn -import config -import thumbnail as tn -import io -import shutil -import stat -import hashlib - -from gettext import gettext as _ - -def copy_file_metadata(src, dst, logger=None): - """Copy all stat info (mode bits, atime, mtime, flags) from src to dst. - - Adapated from python's shutil.copystat. - - Necessary because with some NTFS file systems, there can be problems - with setting filesystem metadata like permissions and modification time""" - - st = os.stat(src) - mode = stat.S_IMODE(st.st_mode) - try: - os.utime(dst, (st.st_atime, st.st_mtime)) - except OSError as inst: - if logger: - logger.warning("Couldn't adjust file modification time when copying %s. %s: %s", src, inst.errno, inst.strerror) - try: - os.chmod(dst, mode) - except OSError as inst: - if logger: - logger.warning("Couldn't adjust file permissions when copying %s. %s: %s", src, inst.errno, inst.strerror) - - if hasattr(os, 'chflags') and hasattr(st, 'st_flags'): - try: - os.chflags(dst, st.st_flags) - except OSError as inst: - for err in 'EOPNOTSUPP', 'ENOTSUP': - if hasattr(errno, err) and inst.errno == getattr(errno, err): - break - else: - raise - - -class CopyFiles(multiprocessing.Process): - """ - Copies files from source to temporary directory, giving them a random name - """ - def __init__(self, photo_download_folder, video_download_folder, files, - verify_file, - modify_files_during_download, modify_pipe, - scan_pid, - batch_size_MB, results_pipe, terminate_queue, - run_event): - multiprocessing.Process.__init__(self) - self.results_pipe = results_pipe - self.terminate_queue = terminate_queue - self.batch_size_bytes = batch_size_MB * 1048576 # * 1024 * 1024 - self.io_buffer = 1048576 - self.photo_download_folder = photo_download_folder - self.video_download_folder = video_download_folder - self.files = files - self.verify_file = verify_file - self.modify_files_during_download = modify_files_during_download - self.modify_pipe = modify_pipe - self.scan_pid = scan_pid - self.no_files= len(self.files) - self.run_event = run_event - - - def check_termination_request(self): - """ - Check to see this process has not been requested to immediately terminate - """ - if not self.terminate_queue.empty(): - x = self.terminate_queue.get() - # terminate immediately - logger.info("Terminating file copying") - return True - return False - - - def update_progress(self, amount_downloaded, total): - - chunk_downloaded = amount_downloaded - self.bytes_downloaded - if (chunk_downloaded > self.batch_size_bytes) or (amount_downloaded == total): - self.bytes_downloaded = amount_downloaded - self.results_pipe.send((rpdmp.CONN_PARTIAL, (rpdmp.MSG_BYTES, (self.scan_pid, self.total_downloaded + amount_downloaded, chunk_downloaded)))) - if amount_downloaded == total: - self.bytes_downloaded = 0 - - - def run(self): - """start the actual copying of files""" - - #characters used to generate temporary filenames - filename_characters = string.letters + string.digits - - self.bytes_downloaded = 0 - self.total_downloaded = 0 - - self.create_temp_dirs() - - # Send the location of both temporary directories, so they can be - # removed once another process attempts to rename all the files in them - # and move them to generated subfolders - self.results_pipe.send((rpdmp.CONN_PARTIAL, (rpdmp.MSG_TEMP_DIRS, - (self.scan_pid, - self.photo_temp_dir, - self.video_temp_dir)))) - - if self.photo_temp_dir or self.video_temp_dir: - - self.thumbnail_maker = tn.Thumbnail() - - for i in range(self.no_files): - rpd_file = self.files[i] - - # pause if instructed by the caller - self.run_event.wait() - - if self.check_termination_request(): - return None - - #generate temporary name 5 digits long, no extension - temp_name = ''.join(random.choice(filename_characters) for i in xrange(5)) - - temp_full_file_name = os.path.join( - self._get_dest_dir(rpd_file.file_type), - temp_name) - rpd_file.temp_full_file_name = temp_full_file_name - - copy_succeeded = False - - src_bytes = '' - - try: - dest = io.open(temp_full_file_name, 'wb', self.io_buffer) - src = io.open(rpd_file.full_file_name, 'rb', self.io_buffer) - total = rpd_file.size - amount_downloaded = 0 - while True: - # first check if process is being terminated - if self.check_termination_request(): - logger.debug("Closing partially written temporary file") - dest.close() - src.close() - return None - else: - chunk = src.read(self.io_buffer) - if chunk: - dest.write(chunk) - src_bytes += chunk - amount_downloaded += len(chunk) - self.update_progress(amount_downloaded, total) - else: - break - dest.close() - src.close() - copy_succeeded = True - except (IOError, OSError) as inst: - rpd_file.add_problem(None, - pn.DOWNLOAD_COPYING_ERROR_W_NO, - {'filetype': rpd_file.title}) - rpd_file.add_extra_detail( - pn.DOWNLOAD_COPYING_ERROR_W_NO_DETAIL, - {'errorno': inst.errno, 'strerror': inst.strerror}) - - rpd_file.status = config.STATUS_DOWNLOAD_FAILED - - rpd_file.error_title = rpd_file.problem.get_title() - rpd_file.error_msg = _("%(problem)s\nFile: %(file)s") % \ - {'problem': rpd_file.problem.get_problems(), - 'file': rpd_file.full_file_name} - - logger.error("Failed to download file: %s", rpd_file.full_file_name) - logger.error(inst) - self.update_progress(rpd_file.size, rpd_file.size) - except: - rpd_file.add_problem(None, - pn.DOWNLOAD_COPYING_ERROR, - {'filetype': rpd_file.title}) - rpd_file.add_extra_detail( - pn.DOWNLOAD_COPYING_ERROR_DETAIL, - _("An unknown error occurred")) - - rpd_file.status = config.STATUS_DOWNLOAD_FAILED - - rpd_file.error_title = rpd_file.problem.get_title() - rpd_file.error_msg = _("%(problem)s\nFile: %(file)s") % \ - {'problem': rpd_file.problem.get_problems(), - 'file': rpd_file.full_file_name} - - logger.error("Failed to download file: %s", rpd_file.full_file_name) - self.update_progress(rpd_file.size, rpd_file.size) - - # increment this amount regardless of whether the copy actually - # succeeded or not. It's neccessary to keep the user informed. - self.total_downloaded += rpd_file.size - - try: - copy_file_metadata(rpd_file.full_file_name, temp_full_file_name, logger) - except: - logger.error("Unknown error updating filesystem metadata when copying %s", rpd_file.full_file_name) - - # copy THM (video thumbnail file) if there is one - if copy_succeeded and rpd_file.thm_full_name: - # reuse video's file name - temp_thm_full_name = temp_full_file_name + '__rpd__thm' - try: - shutil.copyfile(rpd_file.thm_full_name, temp_thm_full_name) - rpd_file.temp_thm_full_name = temp_thm_full_name - logger.debug("Copied video THM file %s", rpd_file.temp_thm_full_name) - except (IOError, OSError) as inst: - logger.error("Failed to download video THM file: %s", rpd_file.thm_full_name) - logger.error("%s: %s", inst.errno, inst.strerror) - try: - copy_file_metadata(rpd_file.thm_full_name, temp_thm_full_name. logger) - except: - logger.error("Unknown error updating filesystem metadata when copying %s", rpd_file.thm_full_name) - - else: - temp_thm_full_name = None - - #copy audio file if there is one - if copy_succeeded and rpd_file.audio_file_full_name: - # reuse photo's file name - temp_audio_full_name = temp_full_file_name + '__rpd__audio' - try: - shutil.copyfile(rpd_file.audio_file_full_name, temp_audio_full_name) - rpd_file.temp_audio_full_name = temp_audio_full_name - logger.debug("Copied audio file %s", rpd_file.temp_audio_full_name) - except (IOError, OSError) as inst: - logger.error("Failed to download audio file: %s", rpd_file.audio_file_full_name) - logger.error("%s: %s", inst.errno, inst.strerror) - try: - copy_file_metadata(rpd_file.audio_file_full_name, temp_audio_full_name, logger) - except: - logger.error("Unknown error updating filesystem metadata when copying %s", rpd_file.audio_file_full_name) - - - - if copy_succeeded and rpd_file.generate_thumbnail: - thumbnail, thumbnail_icon = self.thumbnail_maker.get_thumbnail( - temp_full_file_name, - temp_thm_full_name, - rpd_file.file_type, - (160, 120), (100,100)) - else: - thumbnail = None - thumbnail_icon = None - - if copy_succeeded and self.verify_file: - rpd_file.md5 = hashlib.md5(src_bytes).hexdigest() - - if rpd_file.metadata is not None: - rpd_file.metadata = None - - - download_count = i + 1 - if self.modify_files_during_download and copy_succeeded: - copy_finished = download_count == self.no_files - - self.modify_pipe.send((rpd_file, download_count, temp_full_file_name, - thumbnail_icon, thumbnail, copy_finished)) - else: - self.results_pipe.send((rpdmp.CONN_PARTIAL, (rpdmp.MSG_FILE, - (copy_succeeded, rpd_file, download_count, - temp_full_file_name, - thumbnail_icon, thumbnail)))) - - - self.results_pipe.send((rpdmp.CONN_COMPLETE, None)) - - - def _get_dest_dir(self, file_type): - if file_type == rpdfile.FILE_TYPE_PHOTO: - return self.photo_temp_dir - else: - return self.video_temp_dir - - def _create_temp_dir(self, folder): - try: - temp_dir = tempfile.mkdtemp(prefix="rpd-tmp-", dir=folder) - except OSError, (errno, strerror): - # FIXME: error reporting - logger.error("Failed to create temporary directory in %s: %s %s", - errono, - strerror, - folder) - temp_dir = None - - return temp_dir - - def create_temp_dirs(self): - self.photo_temp_dir = self.video_temp_dir = None - if self.photo_download_folder is not None: - self.photo_temp_dir = self._create_temp_dir(self.photo_download_folder) - logger.debug("Photo temporary directory: %s", self.photo_temp_dir) - if self.video_download_folder is not None: - self.video_temp_dir = self._create_temp_dir(self.video_download_folder) - logger.debug("Video temporary directory: %s", self.video_temp_dir) - - - diff --git a/rapid/device.py b/rapid/device.py deleted file mode 100644 index 77b03ac..0000000 --- a/rapid/device.py +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import os -import gtk, gio - -import multiprocessing -import logging -logger = multiprocessing.get_logger() - -import paths -import utilities - -from gettext import gettext as _ - -class Device: - def __init__(self, mount=None, path=None): - self.mount = mount - self.path = path - - def get_path(self): - if self.mount: - return self.mount.get_root().get_path() - else: - return self.path - - def get_name(self): - if self.mount: - return self.mount.get_name() - else: - return self.path - - def get_icon(self, size=16): - if self.mount: - icon = self.mount.get_icon() - else: - folder = gio.File(self.path) - file_info = folder.query_info(gio.FILE_ATTRIBUTE_STANDARD_ICON) - icon = file_info.get_icon() - - icontheme = gtk.icon_theme_get_default() - - icon_file = None - if isinstance(icon, gio.ThemedIcon): - try: - # on some user's systems, themes do not have icons associated with them - iconinfo = icontheme.choose_icon(icon.get_names(), size, gtk.ICON_LOOKUP_USE_BUILTIN) - icon_file = iconinfo.get_filename() - return gtk.gdk.pixbuf_new_from_file_at_size(icon_file, size, size) - except: - pass - - if not icon_file: - return icontheme.load_icon('folder', size, gtk.ICON_LOOKUP_USE_BUILTIN) - - -class UseDeviceDialog(gtk.Dialog): - """ - Simple dialog window that prompt's the user whether to use a certain - device or not - """ - def __init__(self, parent_window, device, post_choice_callback): - gtk.Dialog.__init__(self, _('Device Detected'), None, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - (gtk.STOCK_NO, gtk.RESPONSE_CANCEL, - gtk.STOCK_YES, gtk.RESPONSE_OK)) - - self.post_choice_callback = post_choice_callback - - self.set_icon_from_file(paths.share_dir('glade3/rapid-photo-downloader.svg')) - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#usedeviceprompt - prompt_label = gtk.Label(_('Should this device or partition be used to download photos or videos from?')) - prompt_label.set_line_wrap(True) - prompt_hbox = gtk.HBox() - prompt_hbox.pack_start(prompt_label, False, False, padding=6) - device_label = gtk.Label() - device_label.set_markup("<b>%s</b>" % device.get_name()) - device_hbox = gtk.HBox() - device_hbox.pack_start(device_label, False, False) - path_label = gtk.Label() - path_label.set_markup("<i>%s</i>" % device.get_path()) - path_hbox = gtk.HBox() - path_hbox.pack_start(path_label, False, False) - - icon = device.get_icon(size=36) - if icon: - image = gtk.Image() - image.set_from_pixbuf(icon) - - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#usedeviceprompt - self.always_checkbutton = gtk.CheckButton(_('_Remember this choice'), True) - - if icon: - device_hbox_icon = gtk.HBox(homogeneous=False, spacing=6) - device_hbox_icon.pack_start(image, False, False, padding = 6) - device_vbox = gtk.VBox(homogeneous=True, spacing=6) - device_vbox.pack_start(device_hbox, False, False) - device_vbox.pack_start(path_hbox, False, False) - device_hbox_icon.pack_start(device_vbox, False, False) - self.vbox.pack_start(device_hbox_icon, padding = 6) - else: - self.vbox.pack_start(device_hbox, padding=6) - self.vbox.pack_start(path_hbox, padding = 6) - - self.vbox.pack_start(prompt_hbox, padding=6) - self.vbox.pack_start(self.always_checkbutton, padding=6) - - self.set_border_width(6) - self.set_has_separator(False) - - self.set_default_response(gtk.RESPONSE_OK) - - - self.set_transient_for(parent_window) - self.show_all() - self.device = device - - self.connect('response', self.on_response) - - def on_response(self, device_dialog, response): - user_selected = False - permanent_choice = self.always_checkbutton.get_active() - if response == gtk.RESPONSE_OK: - user_selected = True - logger.info("%s selected for downloading from", self.device.get_name()) - if permanent_choice: - logger.info("This device or partition will always be used to download from") - else: - logger.info("%s rejected as a download device", self.device.get_name()) - if permanent_choice: - logger.info("This device or partition will never be used to download from") - - self.post_choice_callback(self, user_selected, permanent_choice, - self.device) - - -def is_DCIM_device(path): - """ Returns true if directory specifies media with photos on it""" - - test_path = os.path.join(path, "DCIM") - return utilities.is_directory(test_path) - - - diff --git a/rapid/downloadtracker.py b/rapid/downloadtracker.py deleted file mode 100644 index e489903..0000000 --- a/rapid/downloadtracker.py +++ /dev/null @@ -1,357 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import time - -import multiprocessing -import logging -logger = multiprocessing.get_logger() - -from rpdfile import FILE_TYPE_PHOTO, FILE_TYPE_VIDEO -from config import STATUS_DOWNLOAD_FAILED, STATUS_DOWNLOADED_WITH_WARNING, \ - STATUS_DOWNLOAD_AND_BACKUP_FAILED, STATUS_BACKUP_PROBLEM - -from gettext import gettext as _ - -class DownloadTracker: - """ - Track file downloads - their size, number, and any problems - """ - def __init__(self): - self.file_types_present_by_scan_pid = dict() - self._refresh_values() - - def _refresh_values(self): - """ these values are reset when a download is completed""" - self.size_of_download_in_bytes_by_scan_pid = dict() - self.total_bytes_backed_up_by_scan_pid = dict() - self.size_of_photo_backup_in_bytes_by_scan_pid = dict() - self.size_of_video_backup_in_bytes_by_scan_pid = dict() - self.raw_size_of_download_in_bytes_by_scan_pid = dict() - self.total_bytes_copied_by_scan_pid = dict() - self.total_bytes_video_backed_up_by_scan_pid = dict() - self.no_files_in_download_by_scan_pid = dict() - self.no_photos_in_download_by_scan_pid = dict() - self.no_videos_in_download_by_scan_pid = dict() - - - # 'Download count' tracks the index of the file being downloaded - # into the list of files that need to be downloaded -- much like - # a counter in a for loop, e.g. 'for i in list', where i is the counter - self.download_count_for_file_by_unique_id = dict() - self.download_count_by_scan_pid = dict() - self.rename_chunk = dict() - self.files_downloaded = dict() - self.photos_downloaded = dict() - self.videos_downloaded = dict() - self.photo_failures = dict() - self.video_failures = dict() - self.warnings = dict() - self.total_photos_downloaded = 0 - self.total_photo_failures = 0 - self.total_videos_downloaded = 0 - self.total_video_failures = 0 - self.total_warnings = 0 - self.total_bytes_to_download = 0 - self.backups_performed_by_unique_id = dict() - self.auto_delete = dict() - - def set_no_backup_devices(self, no_photo_backup_devices, no_video_backup_devices): - self.no_photo_backup_devices = no_photo_backup_devices - self.no_video_backup_devices = no_video_backup_devices - #~ self.no_backup_devices = no_photo_backup_devices + no_video_backup_devices -#~ - #~ def get_no_backup_devices(self): - #~ """ - #~ Returns how many devices are being used to backup files of each type - #~ Return value is an integer tuple: photo and video - #~ """ - #~ return (self.no_photo_backup_devices, self.no_video_backup_devices) - - def init_stats(self, scan_pid, photo_size_in_bytes, video_size_in_bytes, no_photos_to_download, no_videos_to_download): - no_files = no_photos_to_download + no_videos_to_download - self.no_files_in_download_by_scan_pid[scan_pid] = no_files - self.no_photos_in_download_by_scan_pid[scan_pid] = no_photos_to_download - self.no_videos_in_download_by_scan_pid[scan_pid] = no_videos_to_download - self.size_of_photo_backup_in_bytes_by_scan_pid[scan_pid] = photo_size_in_bytes * self.no_photo_backup_devices - self.size_of_video_backup_in_bytes_by_scan_pid[scan_pid] = video_size_in_bytes * self.no_video_backup_devices - bytes = photo_size_in_bytes + video_size_in_bytes - # rename_chunk is used to account for the time it takes to rename a file - # it is arbitrarily set to 10% of the time it takes to copy it - # this makes a difference to the user when they're downloading from a - # a high speed source - self.rename_chunk[scan_pid] = bytes / 10 / no_files - self.size_of_download_in_bytes_by_scan_pid[scan_pid] = bytes + self.rename_chunk[scan_pid] * no_files - self.raw_size_of_download_in_bytes_by_scan_pid[scan_pid] = bytes - self.total_bytes_to_download += self.size_of_download_in_bytes_by_scan_pid[scan_pid] - self.files_downloaded[scan_pid] = 0 - self.photos_downloaded[scan_pid] = 0 - self.videos_downloaded[scan_pid] = 0 - self.photo_failures[scan_pid] = 0 - self.video_failures[scan_pid] = 0 - self.warnings[scan_pid] = 0 - self.total_bytes_backed_up_by_scan_pid[scan_pid] = 0 - - def get_no_files_in_download(self, scan_pid): - return self.no_files_in_download_by_scan_pid[scan_pid] - - def get_no_files_downloaded(self, scan_pid, file_type): - if file_type == FILE_TYPE_PHOTO: - return self.photos_downloaded.get(scan_pid, 0) - else: - return self.videos_downloaded.get(scan_pid, 0) - - def get_no_files_failed(self, scan_pid, file_type): - if file_type == FILE_TYPE_PHOTO: - return self.photo_failures.get(scan_pid, 0) - else: - return self.video_failures.get(scan_pid, 0) - - def get_no_warnings(self, scan_pid): - return self.warnings.get(scan_pid, 0) - - def add_to_auto_delete(self, rpd_file): - if rpd_file.scan_pid in self.auto_delete: - self.auto_delete[rpd_file.scan_pid].append(rpd_file.full_file_name) - else: - self.auto_delete[rpd_file.scan_pid] = [rpd_file.full_file_name,] - - def get_files_to_auto_delete(self, scan_pid): - return self.auto_delete[scan_pid] - - def clear_auto_delete(self, scan_pid): - if scan_pid in self.auto_delete: - del self.auto_delete[scan_pid] - - def file_backed_up(self, unique_id): - self.backups_performed_by_unique_id[unique_id] = \ - self.backups_performed_by_unique_id.get(unique_id, 0) + 1 - - def all_files_backed_up(self, unique_id, file_type): - if unique_id in self.backups_performed_by_unique_id: - if file_type == FILE_TYPE_PHOTO: - return self.backups_performed_by_unique_id[unique_id] == self.no_photo_backup_devices - else: - return self.backups_performed_by_unique_id[unique_id] == self.no_video_backup_devices - else: - logger.critical("Unexpected unique_id in self.backups_performed_by_unique_id") - return True - - - def file_downloaded_increment(self, scan_pid, file_type, status): - self.files_downloaded[scan_pid] += 1 - - if status <> STATUS_DOWNLOAD_FAILED and status <> STATUS_DOWNLOAD_AND_BACKUP_FAILED: - if file_type == FILE_TYPE_PHOTO: - self.photos_downloaded[scan_pid] += 1 - self.total_photos_downloaded += 1 - else: - self.videos_downloaded[scan_pid] += 1 - self.total_videos_downloaded += 1 - - if status == STATUS_DOWNLOADED_WITH_WARNING or status == STATUS_BACKUP_PROBLEM: - self.warnings[scan_pid] += 1 - self.total_warnings += 1 - else: - if file_type == FILE_TYPE_PHOTO: - self.photo_failures[scan_pid] += 1 - self.total_photo_failures += 1 - else: - self.video_failures[scan_pid] += 1 - self.total_video_failures += 1 - - def get_percent_complete(self, scan_pid): - """ - Returns a float representing how much of the download - has been completed - """ - - # when calculating the percentage, there are three components: - # copy (download), rename ('rename_chunk'), and backup - percent_complete = (((float( - self.total_bytes_copied_by_scan_pid[scan_pid]) - + self.rename_chunk[scan_pid] * self.files_downloaded[scan_pid]) - + self.total_bytes_backed_up_by_scan_pid[scan_pid]) - / (self.size_of_download_in_bytes_by_scan_pid[scan_pid] + - self.size_of_photo_backup_in_bytes_by_scan_pid[scan_pid] + - self.size_of_video_backup_in_bytes_by_scan_pid[scan_pid] - )) * 100 - - return percent_complete - - def get_overall_percent_complete(self): - total = 0 - for scan_pid in self.total_bytes_copied_by_scan_pid: - total += (self.total_bytes_copied_by_scan_pid[scan_pid] + - (self.rename_chunk[scan_pid] * - self.files_downloaded[scan_pid])) - - percent_complete = float(total) / self.total_bytes_to_download - return percent_complete - - def set_total_bytes_copied(self, scan_pid, total_bytes): - self.total_bytes_copied_by_scan_pid[scan_pid] = total_bytes - - def increment_bytes_backed_up(self, scan_pid, chunk_downloaded): - self.total_bytes_backed_up_by_scan_pid[scan_pid] += chunk_downloaded - - def set_download_count_for_file(self, unique_id, download_count): - self.download_count_for_file_by_unique_id[unique_id] = download_count - - def get_download_count_for_file(self, unique_id): - return self.download_count_for_file_by_unique_id[unique_id] - - def set_download_count(self, scan_pid, download_count): - self.download_count_by_scan_pid[scan_pid] = download_count - - def get_file_types_present(self, scan_pid): - return self.file_types_present_by_scan_pid[scan_pid] - - def set_file_types_present(self, scan_pid, file_types_present): - self.file_types_present_by_scan_pid[scan_pid] = file_types_present - - def no_errors_or_warnings(self): - """ - Return True if there were no errors or warnings in the download - else return False - """ - return (self.total_warnings == 0 and - self.total_photo_failures == 0 and - self.total_video_failures == 0) - - def purge(self, scan_pid): - del self.no_files_in_download_by_scan_pid[scan_pid] - del self.size_of_download_in_bytes_by_scan_pid[scan_pid] - del self.raw_size_of_download_in_bytes_by_scan_pid[scan_pid] - del self.photos_downloaded[scan_pid] - del self.videos_downloaded[scan_pid] - del self.files_downloaded[scan_pid] - del self.photo_failures[scan_pid] - del self.video_failures[scan_pid] - del self.warnings[scan_pid] - - def purge_all(self): - self._refresh_values() - - - -class TimeCheck: - """ - Record times downloads commmence and pause - used in calculating time - remaining. - - Also tracks and reports download speed. - - Note: This is completely independent of the file / subfolder naming - preference "download start time" - """ - - def __init__(self): - # set the number of seconds gap with which to measure download time remaing - self.download_time_gap = 3 - - self.reset() - - def reset(self): - self.mark_set = False - self.total_downloaded_so_far = 0 - self.total_download_size = 0 - self.size_mark = 0 - - def increment(self, bytes_downloaded): - self.total_downloaded_so_far += bytes_downloaded - - def set_download_mark(self): - if not self.mark_set: - self.mark_set = True - - self.time_mark = time.time() - - def pause(self): - self.mark_set = False - - def check_for_update(self): - now = time.time() - update = now > (self.download_time_gap + self.time_mark) - - if update: - amt_time = now - self.time_mark - self.time_mark = now - amt_downloaded = self.total_downloaded_so_far - self.size_mark - self.size_mark = self.total_downloaded_so_far - download_speed = "%1.1f" % (amt_downloaded / 1048576 / amt_time) +_("MB/s") - else: - download_speed = None - - return (update, download_speed) - -class TimeForDownload: - # used to store variables, see below - pass - -class TimeRemaining: - """ - Calculate how much time is remaining to finish a download - """ - gap = 3 - def __init__(self): - self.clear() - - def set(self, scan_pid, size): - t = TimeForDownload() - t.time_remaining = None - t.size = size - t.downloaded = 0 - t.size_mark = 0 - t.time_mark = time.time() - self.times[scan_pid] = t - - def update(self, scan_pid, bytes_downloaded): - if scan_pid in self.times: - self.times[scan_pid].downloaded += bytes_downloaded - now = time.time() - tm = self.times[scan_pid].time_mark - amt_time = now - tm - if amt_time > self.gap: - self.times[scan_pid].time_mark = now - amt_downloaded = self.times[scan_pid].downloaded - self.times[scan_pid].size_mark - self.times[scan_pid].size_mark = self.times[scan_pid].downloaded - timefraction = amt_downloaded / float(amt_time) - amt_to_download = float(self.times[scan_pid].size) - self.times[scan_pid].downloaded - if timefraction: - self.times[scan_pid].time_remaining = amt_to_download / timefraction - - def _time_estimates(self): - for t in self.times: - yield self.times[t].time_remaining - - def time_remaining(self): - return max(self._time_estimates()) - - def set_time_mark(self, scan_pid): - if scan_pid in self.times: - self.times[scan_pid].time_mark = time.time() - - def clear(self): - self.times = {} - - def remove(self, scan_pid): - if scan_pid in self.times: - del self.times[scan_pid] diff --git a/rapid/errorlog.py b/rapid/errorlog.py deleted file mode 100644 index d391b89..0000000 --- a/rapid/errorlog.py +++ /dev/null @@ -1,94 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import gtk - -import pango -import config -import paths - -class ErrorLog(): - """ - Displays a log of errors, warnings or other information to the user - """ - - def __init__(self, rapidapp): - """ - Initialize values for log dialog, but do not display. - """ - - self.builder = gtk.Builder() - self.builder.set_translation_domain(config.APP_NAME) - self.builder.add_from_file(paths.share_dir("glade3/errorlog.ui")) - self.builder.connect_signals(self) - self.widget = self.builder.get_object("errorlog") - self.log_textview = self.builder.get_object("log_textview") - self.log_scrolledwindow = self.builder.get_object("log_scrolledwindow") - - self.widget.connect("delete-event", self.hide_window) - - self.rapidapp = rapidapp - #~ self.log_textview.set_cursor_visible(False) - self.textbuffer = self.log_textview.get_buffer() - - self.error_tag = self.textbuffer.create_tag(weight=pango.WEIGHT_BOLD, foreground="red") - self.warning_tag = self.textbuffer.create_tag(weight=pango.WEIGHT_BOLD) - self.extra_detail_tag = self.textbuffer.create_tag(style=pango.STYLE_ITALIC) - - def add_message(self, severity, problem, details, extra_detail): - if severity in [config.CRITICAL_ERROR, config.SERIOUS_ERROR]: - self.rapidapp.error_image.show() - elif severity == config.WARNING: - self.rapidapp.warning_image.show() - self.rapidapp.warning_vseparator.show() - - iter = self.textbuffer.get_end_iter() - if severity in [config.CRITICAL_ERROR, config.SERIOUS_ERROR]: - self.textbuffer.insert_with_tags(iter, problem +"\n", self.error_tag) - else: - self.textbuffer.insert_with_tags(iter, problem +"\n", self.warning_tag) - if details: - iter = self.textbuffer.get_end_iter() - self.textbuffer.insert(iter, details + "\n") - if extra_detail: - iter = self.textbuffer.get_end_iter() - self.textbuffer.insert_with_tags(iter, extra_detail +"\n", self.extra_detail_tag) - - iter = self.textbuffer.get_end_iter() - self.textbuffer.insert(iter, "\n") - - # move viewport to display the latest message - adjustment = self.log_scrolledwindow.get_vadjustment() - adjustment.set_value(adjustment.upper) - - - def on_errorlog_response(self, dialog, arg): - if arg == gtk.RESPONSE_CLOSE: - pass - self.rapidapp.error_image.hide() - self.rapidapp.warning_image.hide() - self.rapidapp.warning_vseparator.hide() - self.rapidapp.prefs.show_log_dialog = False - self.widget.hide() - return True - - def hide_window(self, window, event): - window.hide() - return True diff --git a/rapid/filemodify.py b/rapid/filemodify.py deleted file mode 100644 index 49a4d24..0000000 --- a/rapid/filemodify.py +++ /dev/null @@ -1,180 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import os.path, fractions -import subprocess -import hashlib -import multiprocessing -import logging -logger = multiprocessing.get_logger() - -import rpdmultiprocessing as rpdmp -import rpdfile -import metadataxmp as mxmp -import subfolderfile -import config -import problemnotification as pn - -from gettext import gettext as _ - -WRITE_XMP_INPLACE = rpdfile.NON_RAW_IMAGE_EXTENSIONS + ['dng'] - - -def lossless_rotate(jpeg): - """using exiftran, performs a lossless, inplace translation of a jpeg, preserving time stamps""" - try: - logger.debug("Auto rotating %s", jpeg) - proc = subprocess.Popen(['exiftran', '-a', '-i', '-p', jpeg], stdout=subprocess.PIPE) - v = proc.communicate()[0].strip() - except OSError: - v = None - return v - -class FileModify(multiprocessing.Process): - def __init__(self, auto_rotate_jpeg, focal_length, verify_file, - refresh_md5_on_file_change, results_pipe, terminate_queue, - run_event): - multiprocessing.Process.__init__(self) - self.results_pipe = results_pipe - self.terminate_queue = terminate_queue - self.run_event = run_event - - self.auto_rotate_jpeg = auto_rotate_jpeg - self.focal_length = focal_length - self.verify_file = verify_file - self.refresh_md5_on_file_change = refresh_md5_on_file_change - - def check_termination_request(self): - """ - Check to see this process has not been requested to immediately terminate - """ - if not self.terminate_queue.empty(): - x = self.terminate_queue.get() - # terminate immediately - return True - return False - - def create_rational(self, value): - return '%s/%s' % (value.numerator, value.denominator) - - def run(self): - - download_count = 0 - copy_finished = False - while not copy_finished: - logger.debug("Finished %s. Getting next task.", download_count) - - data = self.results_pipe.recv() - if len(data) > 2: - rpd_file, download_count, temp_full_file_name, thumbnail_icon, thumbnail, copy_finished = data - else: - rpd_file, copy_finished = data - if rpd_file is None: - # this is a termination signal - logger.info("Terminating file modify via pipe") - return None - # pause if instructed by the caller - self.run_event.wait() - - if self.check_termination_request(): - return None - - copy_succeeded = True - redo_md5 = False - - if self.verify_file: - logger.debug("Verifying file %s....", rpd_file.name) - md5 = hashlib.md5(open(temp_full_file_name).read()).hexdigest() - if md5 <> rpd_file.md5: - logger.critical("%s file verification FAILED", rpd_file.name) - logger.critical("The %s did not download correctly!", rpd_file.title) - - rpd_file.status = config.STATUS_DOWNLOAD_FAILED - - rpd_file.add_problem(None, pn.FILE_VERIFICATION_FAILED, - {'filetype': rpd_file.title}) - rpd_file.error_title = rpd_file.problem.get_title() - rpd_file.error_msg = _("%(problem)s\nFile: %(file)s") % \ - {'problem': rpd_file.problem.get_problems(), - 'file': rpd_file.full_file_name} - copy_succeeded = False - else: - logger.debug("....file %s verified", rpd_file.name) - - - if copy_succeeded: - if self.auto_rotate_jpeg and rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO: - if rpd_file.extension in rpdfile.JPEG_EXTENSIONS: - lossless_rotate(rpd_file.temp_full_file_name) - redo_md5 = True - - xmp_sidecar = None - # check to see if focal length and aperture data should be manipulated - if self.focal_length is not None and rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO: - if subfolderfile.load_metadata(rpd_file, temp_file=True): - a = rpd_file.metadata.aperture() - if a == '0.0': - logger.info("Adjusting focal length and aperture for %s (%s)", rpd_file.temp_full_file_name, rpd_file.name) - - new_focal_length = fractions.Fraction(self.focal_length,1) - new_aperture = fractions.Fraction(8,1) - if rpd_file.extension in WRITE_XMP_INPLACE: - try: - rpd_file.metadata["Exif.Photo.FocalLength"] = new_focal_length - rpd_file.metadata["Exif.Photo.FNumber"] = new_aperture - rpd_file.metadata.write(preserve_timestamps=True) - redo_md5 = True - logger.debug("Wrote new focal length and aperture to %s (%s)", rpd_file.temp_full_file_name, rpd_file.name) - except: - logger.error("failed to write new focal length and aperture to %s (%s)!", rpd_file.temp_full_file_name, rpd_file.name) - else: - # write to xmp sidecar - xmp_sidecar = mxmp.XmpMetadataSidecar(rpd_file.temp_full_file_name) - xmp_sidecar.set_exif_value('FocalLength', self.create_rational(new_focal_length)) - xmp_sidecar.set_exif_value('FNumber', self.create_rational(new_aperture)) - # store values in rpd_file, so they can be used in the subfolderfile process - rpd_file.new_focal_length = new_focal_length - rpd_file.new_aperture = new_aperture - - - - if xmp_sidecar is not None: - # need to write out xmp sidecar - o = xmp_sidecar.write_xmp_sidecar() - logger.debug("Wrote XMP sidecar file") - logger.debug("exiv2 output: %s", o) - rpd_file.temp_xmp_full_name = rpd_file.temp_full_file_name + '.xmp' - - if self.refresh_md5_on_file_change and redo_md5: - rpd_file.md5 = hashlib.md5(open(temp_full_file_name).read()).hexdigest() - - - - rpd_file.metadata = None #purge metadata, as it cannot be pickled - - - self.results_pipe.send((rpdmp.CONN_PARTIAL, - (copy_succeeded, rpd_file, download_count, - temp_full_file_name, - thumbnail_icon, thumbnail))) - - self.results_pipe.send((rpdmp.CONN_COMPLETE, None)) - - diff --git a/rapid/filmstrip.py b/rapid/filmstrip.py deleted file mode 100755 index 916cb39..0000000 --- a/rapid/filmstrip.py +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2010-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -""" -Adds a filmstrip to the left and right of a pixbuf -""" - -import gtk - - -xpm_data = [ -"12 10 27 1", -" c #000000", -". c #232323", -"+ c #7A7A7A", -"@ c #838383", -"# c #8C8C8C", -"$ c #909090", -"% c #8E8E8E", -"& c #525252", -"* c #6E6E6E", -"= c #939393", -"- c #A3A3A3", -"; c #ABABAB", -"> c #A8A8A8", -", c #9B9B9B", -"' c #727272", -") c #A4A4A4", -"! c #BBBBBB", -"~ c #C4C4C4", -"{ c #C1C1C1", -"] c #AFAFAF", -"^ c #3E3E3E", -"/ c #A6A6A6", -"( c #BEBEBE", -"_ c #C8C8C8", -": c #070707", -"< c #090909", -"[ c #0A0A0A", -" ", -" ", -" ", -" .+@#$%& ", -" *@=-;>, ", -" '%)!~{] ", -" ^$/(_~% ", -" :<[[[ ", -" ", -" "] - - -def add_filmstrip(pixbuf): - """ - Adds a filmstrip to the left and right of a pixbuf - - Returns a pixbuf - - """ - filmstrip = gtk.gdk.pixbuf_new_from_xpm_data(xpm_data) - filmstrip_width = filmstrip.get_width() - filmstrip_height = filmstrip.get_height() - filmstrip_right = filmstrip.flip(True) - - - original = pixbuf - original_height = original.get_height() - thumbnail_width = original.get_width() + filmstrip_width * 2 - thumbnail_right_col = original.get_width() + filmstrip_width - - thumbnail = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, thumbnail_width, original.get_height()) - - #add filmstrips to left and right - for i in range(original_height / filmstrip_height): - filmstrip.copy_area(0, 0, filmstrip_width, filmstrip_height, thumbnail, 0, i * filmstrip_height) - filmstrip_right.copy_area(0, 0, filmstrip_width, filmstrip_height, thumbnail, thumbnail_right_col, i * filmstrip_height) - - #now do the remainder, at the bottom - remaining_height = original_height % filmstrip_height - if remaining_height: - filmstrip.copy_area(0, 0, filmstrip_width, remaining_height, thumbnail, 0, original_height-remaining_height) - filmstrip_right.copy_area(0, 0, filmstrip_width, remaining_height, thumbnail, thumbnail_right_col, original_height-remaining_height) - - if original.get_has_alpha(): - thumbnail = thumbnail.add_alpha(False, 0,0,0) - #copy in the original image - original.copy_area(0, 0, original.get_width(), original_height, thumbnail, filmstrip_width, 0) - - return thumbnail - - -if __name__ == '__main__': - import sys - - - if (len(sys.argv) != 2): - print 'Usage: ' + sys.argv[0] + ' path/to/photo/image' - - else: - p = gtk.gdk.pixbuf_new_from_file(sys.argv[1]) - p2 = add_filmstrip(p) - p2.save('testing.png', 'png') diff --git a/rapid/generatename.py b/rapid/generatename.py deleted file mode 100644 index 446aaf8..0000000 --- a/rapid/generatename.py +++ /dev/null @@ -1,546 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2007-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import os, re, datetime, string, collections - -import multiprocessing -import logging -logger = multiprocessing.get_logger() - -import problemnotification as pn - -from generatenameconfig import * - -from gettext import gettext as _ - - -def convert_date_for_strftime(datetime_user_choice): - try: - return DATE_TIME_CONVERT[LIST_DATE_TIME_L2.index(datetime_user_choice)] - except: - raise PrefValueInvalidError(datetime_user_choice) - - -class PhotoName: - """ - Generate the name of a photo. Used as a base class for generating names - of videos, as well as subfolder names for both file types - """ - - def __init__(self, pref_list): - self.pref_list = pref_list - - - # Some of the next values are overwritten in derived classes - self.strip_initial_period_from_extension = False - self.strip_forward_slash = True - self.L1_date_check = IMAGE_DATE #used in _get_date_component() - self.component = pn.FILENAME_COMPONENT #used in error reporting - - - def _get_values_from_pref_list(self): - for i in range(0, len(self.pref_list), 3): - yield (self.pref_list[i], self.pref_list[i+1], self.pref_list[i+2]) - - def _get_date_component(self): - """ - Returns portion of new file / subfolder name based on date time. - If the date is missing, will attempt to use the fallback date. - """ - - # step 1: get the correct value from metadata - if self.L1 == self.L1_date_check: - if self.L2 == SUBSECONDS: - d = self.rpd_file.metadata.sub_seconds(missing=None) - if d is None: - self.rpd_file.problem.add_problem(self.component, pn.MISSING_METADATA, _(self.L2)) - return '' - else: - return d - else: - d = self.rpd_file.metadata.date_time(missing=None) - - elif self.L1 == TODAY: - d = datetime.datetime.now() - elif self.L1 == YESTERDAY: - delta = datetime.timedelta(days = 1) - d = datetime.datetime.now() - delta - elif self.L1 == DOWNLOAD_TIME: - d = self.rpd_file.download_start_time - else: - raise("Date options invalid") - - # step 2: if have a value, try to convert it to string format - if d: - try: - return d.strftime(convert_date_for_strftime(self.L2)) - except: - logger.warning("Exif date time value appears invalid for file %s", self.rpd_file.full_file_name) - - # step 3: handle a missing value using file modification time - if self.rpd_file.modification_time: - try: - d = datetime.datetime.fromtimestamp(self.rpd_file.modification_time) - except: - self.rpd_file.add_problem(self.component, pn.INVALID_DATE_TIME, '') - logger.error("Both file modification time and metadata date & time are invalid for file %s", self.rpd_file.full_file_name) - return '' - else: - self.rpd_file.add_problem(self.component, pn.MISSING_METADATA, _(self.L1)) - return '' - - try: - return d.strftime(convert_date_for_strftime(self.L2)) - except: - self.rpd_file.add_problem(self.component, pn.INVALID_DATE_TIME, d) - logger.error("Both file modification time and metadata date & time are invalid for file %s", self.rpd_file.full_file_name) - return '' - - def _get_associated_file_extension(self, associate_file): - """ - Generates extensions with correct capitalization for files like - thumbnail or audio files. - """ - if associate_file: - extension = os.path.splitext(associate_file)[1] - if self.L2 == UPPERCASE: - extension = extension.upper() - elif self.L2 == LOWERCASE: - extension = extension.lower() - else: - extension = None - return extension - - - def _get_thm_extension(self): - """ - Generates THM extension with correct capitalization, if needed - """ - self.rpd_file.thm_extension = self._get_associated_file_extension(self.rpd_file.thm_full_name) - - def _get_audio_extension(self): - """ - Generates audio extension with correct capitalization, if needed - e.g. WAV or wav - """ - self.rpd_file.audio_extension = self._get_associated_file_extension(self.rpd_file.audio_file_full_name) - - def _get_xmp_extension(self, extension): - """ - Generates XMP extension with correct capitalization, if needed. - """ - if self.rpd_file.temp_xmp_full_name: - if self.L2 == UPPERCASE: - self.rpd_file.xmp_extension = '.XMP' - elif self.L2 == LOWERCASE: - self.rpd_file.xmp_extension = '.xmp' - else: - # mimic capitalization of extension - if extension.isupper(): - self.rpd_file.xmp_extension = '.XMP' - else: - self.rpd_file.xmp_extension = '.xmp' - else: - self.rpd_file.xmp_extension = None - - - def _get_filename_component(self): - """ - Returns portion of new file / subfolder name based on the file name - """ - - name, extension = os.path.splitext(self.rpd_file.name) - - if self.L1 == NAME_EXTENSION: - filename = self.rpd_file.name - self._get_thm_extension() - self._get_audio_extension() - self._get_xmp_extension(extension) - elif self.L1 == NAME: - filename = name - elif self.L1 == EXTENSION: - self._get_thm_extension() - self._get_audio_extension() - self._get_xmp_extension(extension) - if extension: - if not self.strip_initial_period_from_extension: - # keep the period / dot of the extension, so the user does not - # need to manually specify it - filename = extension - else: - # having the period when this is used as a part of a subfolder name - # is a bad idea when it is at the start! - filename = extension[1:] - else: - self.rpd_file.add_problem(self.component, pn.MISSING_FILE_EXTENSION) - return "" - elif self.L1 == IMAGE_NUMBER or self.L1 == VIDEO_NUMBER: - n = re.search("(?P<image_number>[0-9]+$)", name) - if not n: - self.rpd_file.add_problem(self.component, pn.MISSING_IMAGE_NUMBER) - return '' - else: - image_number = n.group("image_number") - - if self.L2 == IMAGE_NUMBER_ALL: - filename = image_number - elif self.L2 == IMAGE_NUMBER_1: - filename = image_number[-1] - elif self.L2 == IMAGE_NUMBER_2: - filename = image_number[-2:] - elif self.L2 == IMAGE_NUMBER_3: - filename = image_number[-3:] - elif self.L2 == IMAGE_NUMBER_4: - filename = image_number[-4:] - else: - raise TypeError("Incorrect filename option") - - if self.L2 == UPPERCASE: - filename = filename.upper() - elif self.L2 == LOWERCASE: - filename = filename.lower() - - return filename - - def _get_metadata_component(self): - """ - Returns portion of new image / subfolder name based on the metadata - - Note: date time metadata found in _getDateComponent() - """ - - if self.L1 == APERTURE: - v = self.rpd_file.metadata.aperture() - elif self.L1 == ISO: - v = self.rpd_file.metadata.iso() - elif self.L1 == EXPOSURE_TIME: - v = self.rpd_file.metadata.exposure_time(alternativeFormat=True) - elif self.L1 == FOCAL_LENGTH: - v = self.rpd_file.metadata.focal_length() - elif self.L1 == CAMERA_MAKE: - v = self.rpd_file.metadata.camera_make() - elif self.L1 == CAMERA_MODEL: - v = self.rpd_file.metadata.camera_model() - elif self.L1 == SHORT_CAMERA_MODEL: - v = self.rpd_file.metadata.short_camera_model() - elif self.L1 == SHORT_CAMERA_MODEL_HYPHEN: - v = self.rpd_file.metadata.short_camera_model(includeCharacters = "\-") - elif self.L1 == SERIAL_NUMBER: - v = self.rpd_file.metadata.camera_serial() - elif self.L1 == SHUTTER_COUNT: - v = self.rpd_file.metadata.shutter_count() - if v: - v = int(v) - padding = LIST_SHUTTER_COUNT_L2.index(self.L2) + 3 - formatter = '%0' + str(padding) + "i" - v = formatter % v - elif self.L1 == FILE_NUMBER: - v = self.rpd_file.metadata.file_number() - if v and self.L2 == FILE_NUMBER_FOLDER: - v = v[:3] - elif self.L1 == OWNER_NAME: - v = self.rpd_file.metadata.owner_name() - elif self.L1 == ARTIST: - v = self.rpd_file.metadata.artist() - elif self.L1 == COPYRIGHT: - v = self.rpd_file.metadata.copyright() - else: - raise TypeError("Invalid metadata option specified") - if self.L1 in [CAMERA_MAKE, CAMERA_MODEL, SHORT_CAMERA_MODEL, - SHORT_CAMERA_MODEL_HYPHEN, OWNER_NAME, ARTIST, - COPYRIGHT]: - if self.L2 == UPPERCASE: - v = v.upper() - elif self.L2 == LOWERCASE: - v = v.lower() - if not v: - self.rpd_file.add_problem(self.component, pn.MISSING_METADATA, _(self.L1)) - return v - - def _calculate_letter_sequence(self, sequence): - - def _letters(x): - """ - Adapted from algorithm at http://en.wikipedia.org/wiki/Hexavigesimal - """ - v = '' - while x > 25: - r = x % 26 - x= x / 26 - 1 - v = string.lowercase[r] + v - v = string.lowercase[x] + v - - return v - - - v = _letters(sequence) - if self.L2 == UPPERCASE: - v = v.upper() - - return v - - def _format_sequence_no(self, value, amountToPad): - padding = LIST_SEQUENCE_NUMBERS_L2.index(amountToPad) + 1 - formatter = '%0' + str(padding) + "i" - return formatter % value - - def _get_downloads_today(self): - return self._format_sequence_no(self.rpd_file.sequences.get_downloads_today(), self.L2) - - def _get_session_sequence_no(self): - return self._format_sequence_no(self.rpd_file.sequences.get_session_sequence_no(), self.L2) - - def _get_stored_sequence_no(self): - return self._format_sequence_no(self.rpd_file.sequences.get_stored_sequence_no(), self.L2) - - def _get_sequence_letter(self): - return self._calculate_letter_sequence(self.rpd_file.sequences.get_sequence_letter()) - - def _get_sequences_component(self): - if self.L1 == DOWNLOAD_SEQ_NUMBER: - return self._get_downloads_today() - elif self.L1 == SESSION_SEQ_NUMBER: - return self._get_session_sequence_no() - elif self.L1 == STORED_SEQ_NUMBER: - return self._get_stored_sequence_no() - elif self.L1 == SEQUENCE_LETTER: - return self._get_sequence_letter() - - - #~ elif self.L1 == SUBFOLDER_SEQ_NUMBER: - #~ return self._getSubfolderSequenceNo() - - - - def _get_component(self): - try: - if self.L0 == DATE_TIME: - return self._get_date_component() - elif self.L0 == TEXT: - return self.L1 - elif self.L0 == FILENAME: - return self._get_filename_component() - elif self.L0 == METADATA: - return self._get_metadata_component() - elif self.L0 == SEQUENCES: - return self._get_sequences_component() - elif self.L0 == JOB_CODE: - return self.rpd_file.job_code - elif self.L0 == SEPARATOR: - return os.sep - except: - self.rpd_file.add_problem(self.component, pn.ERROR_IN_GENERATION, _(self.L0)) - return '' - - - def generate_name(self, rpd_file): - self.rpd_file = rpd_file - - name = '' - - for self.L0, self.L1, self.L2 in self._get_values_from_pref_list(): - v = self._get_component() - if v: - name += v - - # remove any null characters - they are bad news in filenames - name = name.replace('\x00', '') - - if self.rpd_file.strip_characters: - for c in r'\:*?"<>|': - name = name.replace(c, '') - - if self.strip_forward_slash: - name = name.replace('/', '') - - name = name.strip() - - return name - - - - -class VideoName(PhotoName): - def __init__(self, pref_list): - PhotoName.__init__(self, pref_list) - self.L1_date_check = VIDEO_DATE #used in _get_date_component() - - def _get_metadata_component(self): - """ - Returns portion of video / subfolder name based on the metadata - - Note: date time metadata found in _getDateComponent() - """ - return get_video_metadata_component(self) - -class PhotoSubfolder(PhotoName): - """ - Generate subfolder names for photo files - """ - - def __init__(self, pref_list): - self.pref_list = pref_list - - self.strip_extraneous_white_space = re.compile(r'\s*%s\s*' % os.sep) - self.strip_initial_period_from_extension = True - self.strip_forward_slash = False - self.L1_date_check = IMAGE_DATE #used in _get_date_component() - self.component = pn.SUBFOLDER_COMPONENT #used in error reporting - - def generate_name(self, rpd_file): - - subfolders = PhotoName.generate_name(self, rpd_file) - - # subfolder value must never start with a separator, or else any - # os.path.join function call will fail to join a subfolder to its - # parent folder - if subfolders: - if subfolders[0] == os.sep: - subfolders = subfolders[1:] - - # remove any spaces before and after a directory name - if subfolders and self.rpd_file.strip_characters: - subfolders = self.strip_extraneous_white_space.sub(os.sep, subfolders) - - return subfolders - - - - -class VideoSubfolder(PhotoSubfolder): - """ - Generate subfolder names for video files - """ - - def __init__(self, pref_list): - PhotoSubfolder.__init__(self, pref_list) - self.L1_date_check = VIDEO_DATE #used in _get_date_component() - - - def _get_metadata_component(self): - """ - Returns portion of video / subfolder name based on the metadata - - Note: date time metadata found in _getDateComponent() - """ - return get_video_metadata_component(self) - -def get_video_metadata_component(video): - """ - Returns portion of video / subfolder name based on the metadata - - This is outside of a class definition because of the inheritence - hierarchy. - """ - - problem = None - if video.L1 == CODEC: - v = video.rpd_file.metadata.codec() - elif video.L1 == WIDTH: - v = video.rpd_file.metadata.width() - elif video.L1 == HEIGHT: - v = video.rpd_file.metadata.height() - elif video.L1 == FPS: - v = video.rpd_file.metadata.frames_per_second() - elif video.L1 == LENGTH: - v = video.rpd_file.metadata.length() - else: - raise TypeError("Invalid metadata option specified") - if video.L1 in [CODEC]: - if video.L2 == UPPERCASE: - v = v.upper() - elif video.L2 == LOWERCASE: - v = v.lower() - if not v: - video.rpd_file.add_problem(video.component, pn.MISSING_METADATA, _(video.L1)) - return v - -class Sequences: - """ - Holds sequence numbers and letters used in generating filenames. - """ - def __init__(self, downloads_today_tracker, stored_sequence_no): - self.session_sequence_no = 0 - self.sequence_letter = -1 - self.downloads_today_tracker = downloads_today_tracker - self.stored_sequence_no = stored_sequence_no - self.matched_sequences = None - - def set_matched_sequence_value(self, matched_sequences): - self.matched_sequences = matched_sequences - - def get_session_sequence_no(self): - if self.matched_sequences is not None: - return self.matched_sequences.session_sequence_no - else: - return self._get_session_sequence_no() - - def _get_session_sequence_no(self): - return self.session_sequence_no + 1 - - def get_sequence_letter(self): - if self.matched_sequences is not None: - return self.matched_sequences.sequence_letter - else: - return self._get_sequence_letter() - - def _get_sequence_letter(self): - return self.sequence_letter + 1 - - def increment(self, uses_session_sequece_no, uses_sequence_letter): - if uses_session_sequece_no: - self.session_sequence_no += 1 - if uses_sequence_letter: - self.sequence_letter += 1 - - def get_downloads_today(self): - if self.matched_sequences is not None: - return self.matched_sequences.downloads_today - else: - return self._get_downloads_today() - - def _get_downloads_today(self): - v = self.downloads_today_tracker.get_downloads_today() - if v == -1: - return 1 - else: - return v + 1 - - def get_stored_sequence_no(self): - if self.matched_sequences is not None: - return self.matched_sequences.stored_sequence_no - else: - return self._get_stored_sequence_no() - - def _get_stored_sequence_no(self): - # Must add 1 to the value, for historic reasons (that is how it used - # to work) - return self.stored_sequence_no + 1 - - def create_matched_sequences(self): - sequences = collections.namedtuple( - 'AssignedSequences', - 'session_sequence_no sequence_letter downloads_today stored_sequence_no' - ) - sequences.session_sequence_no = self._get_session_sequence_no() - sequences.sequence_letter = self._get_sequence_letter() - sequences.downloads_today = self._get_downloads_today() - sequences.stored_sequence_no = self._get_stored_sequence_no() - return sequences diff --git a/rapid/generatenameconfig.py b/rapid/generatenameconfig.py deleted file mode 100644 index 23e58f6..0000000 --- a/rapid/generatenameconfig.py +++ /dev/null @@ -1,512 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2007-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -# Special key in each dictionary which specifies the order of elements. -# It is very important to have a consistent and rational order when displaying -# these prefs to the user, and dictionaries are unsorted. - -import os - -from gettext import gettext as _ - -ORDER_KEY = "__order__" - -# PLEASE NOTE: these values are duplicated in a dummy class whose function -# is to have them put into the translation template. If you change the values below -# then you MUST change the value in class i18TranslateMeThanks as well!! - -# *** Level 0, i.e. first column of values presented to user -DATE_TIME = 'Date time' -TEXT = 'Text' -FILENAME = 'Filename' -METADATA = 'Metadata' -SEQUENCES = 'Sequences' -JOB_CODE = 'Job code' - -SEPARATOR = os.sep - -# *** Level 1, i.e. second column of values presented to user - -# Date time -IMAGE_DATE = 'Image date' -TODAY = 'Today' -YESTERDAY = 'Yesterday' -VIDEO_DATE = 'Video date' -DOWNLOAD_TIME = 'Download time' - -# File name -NAME_EXTENSION = 'Name + extension' -NAME = 'Name' -EXTENSION = 'Extension' -IMAGE_NUMBER = 'Image number' -VIDEO_NUMBER = 'Video number' - -# Metadata -APERTURE = 'Aperture' -ISO = 'ISO' -EXPOSURE_TIME = 'Exposure time' -FOCAL_LENGTH = 'Focal length' -CAMERA_MAKE = 'Camera make' -CAMERA_MODEL = 'Camera model' -SHORT_CAMERA_MODEL = 'Short camera model' -SHORT_CAMERA_MODEL_HYPHEN = 'Hyphenated short camera model' -SERIAL_NUMBER = 'Serial number' -SHUTTER_COUNT = 'Shutter count' -#Currently the only file number is Exif.CanonFi.FileNumber, -#which is in the format xxx-yyyy, where xxx is the folder and yyyy the image -FILE_NUMBER = 'File number' -OWNER_NAME = 'Owner name' -COPYRIGHT = 'Copyright' -ARTIST = 'Artist' - -# Video metadata -CODEC = 'Codec' -WIDTH = 'Width' -HEIGHT = 'Height' -FPS = 'Frames Per Second' -LENGTH = 'Length' - -#Image sequences -DOWNLOAD_SEQ_NUMBER = 'Downloads today' -SESSION_SEQ_NUMBER = 'Session number' -SUBFOLDER_SEQ_NUMBER = 'Subfolder number' -STORED_SEQ_NUMBER = 'Stored number' -SEQUENCE_LETTER = 'Sequence letter' - - - -# *** Level 2, i.e. third and final column of values presented to user - -# Image number -IMAGE_NUMBER_ALL = 'All digits' -IMAGE_NUMBER_1 = 'Last digit' -IMAGE_NUMBER_2 = 'Last 2 digits' -IMAGE_NUMBER_3 = 'Last 3 digits' -IMAGE_NUMBER_4 = 'Last 4 digits' - - -# Case -ORIGINAL_CASE = "Original Case" -UPPERCASE = "UPPERCASE" -LOWERCASE = "lowercase" - -# Sequence number -SEQUENCE_NUMBER_1 = "One digit" -SEQUENCE_NUMBER_2 = "Two digits" -SEQUENCE_NUMBER_3 = "Three digits" -SEQUENCE_NUMBER_4 = "Four digits" -SEQUENCE_NUMBER_5 = "Five digits" -SEQUENCE_NUMBER_6 = "Six digits" -SEQUENCE_NUMBER_7 = "Seven digits" - -#File number -FILE_NUMBER_FOLDER = "Folder only" -FILE_NUMBER_ALL = "Folder and file" - -# Now, define dictionaries and lists of valid combinations of preferences. - -# Level 2 - -# Date - -SUBSECONDS = 'Subseconds' - -# ****** NOTE 1: if changing LIST_DATE_TIME_L2, you MUST update the default subfolder preference below ***** -# ****** NOTE 2: if changing LIST_DATE_TIME_L2, you MUST update DATE_TIME_CONVERT below ***** -LIST_DATE_TIME_L2 = ['YYYYMMDD', 'YYYY-MM-DD','YYMMDD', 'YY-MM-DD', - 'MMDDYYYY', 'MMDDYY', 'MMDD', - 'DDMMYYYY', 'DDMMYY', 'YYYY', 'YY', - 'MM', 'DD', 'Month (full)', 'Month (abbreviated)', - 'HHMMSS', 'HHMM', 'HH-MM-SS', 'HH-MM', 'HH', 'MM (minutes)', 'SS'] - - -LIST_IMAGE_DATE_TIME_L2 = LIST_DATE_TIME_L2 + [SUBSECONDS] - -DEFAULT_SUBFOLDER_PREFS = [DATE_TIME, IMAGE_DATE, LIST_DATE_TIME_L2[9], '/', '', '', DATE_TIME, IMAGE_DATE, LIST_DATE_TIME_L2[0]] -DEFAULT_VIDEO_SUBFOLDER_PREFS = [DATE_TIME, VIDEO_DATE, LIST_DATE_TIME_L2[9], '/', '', '', DATE_TIME, VIDEO_DATE, LIST_DATE_TIME_L2[0]] - -class i18TranslateMeThanks: - """ this class is never used in actual running code - Its purpose is to have these values inserted into the program's i18n template file - - """ - def __init__(self): - _('Date time') - _('Text') - _('Filename') - _('Metadata') - _('Sequences') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#jobcode - _('Job code') - _('Image date') - _('Video date') - _('Today') - _('Yesterday') - # Translators: Download time is the time and date that the download started (when the user clicked the Download button) - _('Download time') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename - _('Name + extension') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename - _('Name') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename - _('Extension') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename - _('Image number') - _('Video number') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Aperture') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('ISO') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Exposure time') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Focal length') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Camera make') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Camera model') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Short camera model') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Hyphenated short camera model') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Serial number') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Shutter count') - #File number currently refers to the Exif value Exif.Canon.FileNumber - _('File number') - #Only the folder component of the Exif.Canon.FileNumber value - _('Folder only') - #The folder and file component of the Exif.Canon.FileNumber value - _('Folder and file') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamemetadata - _('Owner name') - _('Codec') - _('Width') - _('Height') - _('Length') - _('Frames Per Second') - _('Artist') - _('Copyright') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#sequencenumbers - _('Downloads today') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#sequencenumbers - _('Session number') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#sequencenumbers - _('Subfolder number') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#sequencenumbers - _('Stored number') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#sequenceletters - _('Sequence letter') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename - _('All digits') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename - _('Last digit') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename - _('Last 2 digits') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename - _('Last 3 digits') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamefilename - _('Last 4 digits') - # Translators: please not the capitalization of this text, and keep it the same if your language features capitalization - _("Original Case") - # Translators: please not the capitalization of this text, and keep it the same if your language features capitalization - _("UPPERCASE") - # Translators: please not the capitalization of this text, and keep it the same if your language features capitalization - _("lowercase") - _("One digit") - _("Two digits") - _("Three digits") - _("Four digits") - _("Five digits") - _("Six digits") - _("Seven digits") - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('Subseconds') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('YYYYMMDD') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('YYYY-MM-DD') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('YYMMDD') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('YY-MM-DD') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('MMDDYYYY') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('MMDDYY') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('MMDD') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('DDMMYYYY') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('DDMMYY') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('YYYY') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('YY') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('MM') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('DD') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('Month (full)'), - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('Month (abbreviated)'), - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('HHMMSS') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('HHMM') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('HH-MM-SS') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('HH-MM') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('HH') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('MM (minutes)') - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#renamedateandtime - _('SS') - - -# Convenience values for python datetime conversion using values in -# LIST_DATE_TIME_L2. Obviously the two must remain synchronized. - -DATE_TIME_CONVERT = ['%Y%m%d', '%Y-%m-%d','%y%m%d', '%y-%m-%d', - '%m%d%Y', '%m%d%y', '%m%d', - '%d%m%Y', '%d%m%y', '%Y', '%y', - '%m', '%d', '%B', '%b', - '%H%M%S', '%H%M', '%H-%M-%S', '%H-%M', - '%H', '%M', '%S'] - - -LIST_IMAGE_NUMBER_L2 = [IMAGE_NUMBER_ALL, IMAGE_NUMBER_1, IMAGE_NUMBER_2, - IMAGE_NUMBER_3, IMAGE_NUMBER_4] - - -LIST_CASE_L2 = [ORIGINAL_CASE, UPPERCASE, LOWERCASE] - -LIST_SEQUENCE_LETTER_L2 = [ - UPPERCASE, - LOWERCASE - ] - - - -LIST_SEQUENCE_NUMBERS_L2 = [ - SEQUENCE_NUMBER_1, - SEQUENCE_NUMBER_2, - SEQUENCE_NUMBER_3, - SEQUENCE_NUMBER_4, - SEQUENCE_NUMBER_5, - SEQUENCE_NUMBER_6, - SEQUENCE_NUMBER_7, - ] - - - -LIST_SHUTTER_COUNT_L2 = [ - SEQUENCE_NUMBER_3, - SEQUENCE_NUMBER_4, - SEQUENCE_NUMBER_5, - SEQUENCE_NUMBER_6, - ] -FILE_NUMBER_L2 = [ - FILE_NUMBER_FOLDER, - FILE_NUMBER_ALL - ] - -# Level 1 -LIST_DATE_TIME_L1 = [IMAGE_DATE, TODAY, YESTERDAY, DOWNLOAD_TIME] -LIST_VIDEO_DATE_TIME_L1 = [VIDEO_DATE, TODAY, YESTERDAY, DOWNLOAD_TIME] - -DICT_DATE_TIME_L1 = { - IMAGE_DATE: LIST_IMAGE_DATE_TIME_L2, - TODAY: LIST_DATE_TIME_L2, - YESTERDAY: LIST_DATE_TIME_L2, - DOWNLOAD_TIME: LIST_DATE_TIME_L2, - ORDER_KEY: LIST_DATE_TIME_L1 - } - -VIDEO_DICT_DATE_TIME_L1 = { - VIDEO_DATE: LIST_IMAGE_DATE_TIME_L2, - TODAY: LIST_DATE_TIME_L2, - YESTERDAY: LIST_DATE_TIME_L2, - DOWNLOAD_TIME: LIST_DATE_TIME_L2, - ORDER_KEY: LIST_VIDEO_DATE_TIME_L1 - } - - -LIST_FILENAME_L1 = [NAME_EXTENSION, NAME, EXTENSION, IMAGE_NUMBER] - -DICT_FILENAME_L1 = { - NAME_EXTENSION: LIST_CASE_L2, - NAME: LIST_CASE_L2, - EXTENSION: LIST_CASE_L2, - IMAGE_NUMBER: LIST_IMAGE_NUMBER_L2, - ORDER_KEY: LIST_FILENAME_L1 - } - -LIST_VIDEO_FILENAME_L1 = [NAME_EXTENSION, NAME, EXTENSION, VIDEO_NUMBER] - -DICT_VIDEO_FILENAME_L1 = { - NAME_EXTENSION: LIST_CASE_L2, - NAME: LIST_CASE_L2, - EXTENSION: LIST_CASE_L2, - VIDEO_NUMBER: LIST_IMAGE_NUMBER_L2, - ORDER_KEY: LIST_VIDEO_FILENAME_L1 - } - - -LIST_SUBFOLDER_FILENAME_L1 = [EXTENSION] - -DICT_SUBFOLDER_FILENAME_L1 = { - EXTENSION: LIST_CASE_L2, - ORDER_KEY: LIST_SUBFOLDER_FILENAME_L1 -} - -LIST_METADATA_L1 = [APERTURE, ISO, EXPOSURE_TIME, FOCAL_LENGTH, - CAMERA_MAKE, CAMERA_MODEL, - SHORT_CAMERA_MODEL, - SHORT_CAMERA_MODEL_HYPHEN, - SERIAL_NUMBER, - SHUTTER_COUNT, - FILE_NUMBER, - OWNER_NAME, - ARTIST, - COPYRIGHT] - -LIST_VIDEO_METADATA_L1 = [CODEC, WIDTH, HEIGHT, LENGTH, FPS] - -DICT_METADATA_L1 = { - APERTURE: None, - ISO: None, - EXPOSURE_TIME: None, - FOCAL_LENGTH: None, - CAMERA_MAKE: LIST_CASE_L2, - CAMERA_MODEL: LIST_CASE_L2, - SHORT_CAMERA_MODEL: LIST_CASE_L2, - SHORT_CAMERA_MODEL_HYPHEN: LIST_CASE_L2, - SERIAL_NUMBER: None, - SHUTTER_COUNT: LIST_SHUTTER_COUNT_L2, - FILE_NUMBER: FILE_NUMBER_L2, - OWNER_NAME: LIST_CASE_L2, - ARTIST: LIST_CASE_L2, - COPYRIGHT: LIST_CASE_L2, - ORDER_KEY: LIST_METADATA_L1 - } - -DICT_VIDEO_METADATA_L1 = { - CODEC: LIST_CASE_L2, - WIDTH: None, - HEIGHT: None, - LENGTH: None, - FPS: None, - ORDER_KEY: LIST_VIDEO_METADATA_L1 - } - -LIST_SEQUENCE_L1 = [ - DOWNLOAD_SEQ_NUMBER, - STORED_SEQ_NUMBER, - SESSION_SEQ_NUMBER, - SEQUENCE_LETTER - ] - -DICT_SEQUENCE_L1 = { - DOWNLOAD_SEQ_NUMBER: LIST_SEQUENCE_NUMBERS_L2, - STORED_SEQ_NUMBER: LIST_SEQUENCE_NUMBERS_L2, - SESSION_SEQ_NUMBER: LIST_SEQUENCE_NUMBERS_L2, - SEQUENCE_LETTER: LIST_SEQUENCE_LETTER_L2, - ORDER_KEY: LIST_SEQUENCE_L1 - } - - -# Level 0 - - -LIST_IMAGE_RENAME_L0 = [DATE_TIME, TEXT, FILENAME, METADATA, - SEQUENCES, JOB_CODE] - -LIST_VIDEO_RENAME_L0 = LIST_IMAGE_RENAME_L0 - - -DICT_IMAGE_RENAME_L0 = { - DATE_TIME: DICT_DATE_TIME_L1, - TEXT: None, - FILENAME: DICT_FILENAME_L1, - METADATA: DICT_METADATA_L1, - SEQUENCES: DICT_SEQUENCE_L1, - JOB_CODE: None, - ORDER_KEY: LIST_IMAGE_RENAME_L0 - } - -DICT_VIDEO_RENAME_L0 = { - DATE_TIME: VIDEO_DICT_DATE_TIME_L1, - TEXT: None, - FILENAME: DICT_VIDEO_FILENAME_L1, - METADATA: DICT_VIDEO_METADATA_L1, - SEQUENCES: DICT_SEQUENCE_L1, - JOB_CODE: None, - ORDER_KEY: LIST_VIDEO_RENAME_L0 - } - -LIST_SUBFOLDER_L0 = [DATE_TIME, TEXT, FILENAME, METADATA, JOB_CODE, SEPARATOR] - -DICT_SUBFOLDER_L0 = { - DATE_TIME: DICT_DATE_TIME_L1, - TEXT: None, - FILENAME: DICT_SUBFOLDER_FILENAME_L1, - METADATA: DICT_METADATA_L1, - JOB_CODE: None, - SEPARATOR: None, - ORDER_KEY: LIST_SUBFOLDER_L0 - } - -LIST_VIDEO_SUBFOLDER_L0 = [DATE_TIME, TEXT, FILENAME, METADATA, JOB_CODE, SEPARATOR] - -DICT_VIDEO_SUBFOLDER_L0 = { - DATE_TIME: VIDEO_DICT_DATE_TIME_L1, - TEXT: None, - FILENAME: DICT_SUBFOLDER_FILENAME_L1, - METADATA: DICT_VIDEO_METADATA_L1, - JOB_CODE: None, - SEPARATOR: None, - ORDER_KEY: LIST_VIDEO_SUBFOLDER_L0 - } - -# preference elements that require metadata -# note there is no need to specify lower level elements if a higher level -# element is necessary for them to be present to begin with -METADATA_ELEMENTS = [METADATA, IMAGE_DATE] - -# preference elements that are sequence numbers or letters -SEQUENCE_ELEMENTS = [ - DOWNLOAD_SEQ_NUMBER, - SESSION_SEQ_NUMBER, - SUBFOLDER_SEQ_NUMBER, - STORED_SEQ_NUMBER, - SEQUENCE_LETTER] - -# preference elements that do not require metadata and are not fixed -# as above, there is no need to specify lower level elements if a higher level -# element is necessary for them to be present to begin with -DYNAMIC_NON_METADATA_ELEMENTS = [ - TODAY, YESTERDAY, - FILENAME] + SEQUENCE_ELEMENTS diff --git a/rapid/glade3/about.ui b/rapid/glade3/about.ui deleted file mode 100644 index c9a775a..0000000 --- a/rapid/glade3/about.ui +++ /dev/null @@ -1,84 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <requires lib="gtk+" version="2.20"/> - <!-- interface-naming-policy toplevel-contextual --> - <object class="GtkAboutDialog" id="about"> - <property name="can_focus">False</property> - <property name="border_width">5</property> - <property name="destroy_with_parent">True</property> - <property name="icon">rapid-photo-downloader.svg</property> - <property name="type_hint">dialog</property> - <property name="program_name">Rapid Photo Downloader</property> - <property name="copyright">Copyright Damon Lynch 2007-14</property> - <property name="comments" translatable="yes">Import your photos and videos efficiently and reliably</property> - <property name="website">http://www.damonlynch.net/rapid</property> - <property name="license">Rapid Photo Downloader is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. - -Rapid Photo Downloader is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License along with Rapid Photo Downloader; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.</property> - <property name="authors">Damon Lynch <damonlynch@gmail.com></property> - <property name="translator_credits">Anton Alyab'ev <subeditor@dolgopa.org> -Lőrincz András <level.andrasnak@gmail.com> -Michel Ange <michelange@wanadoo.fr> -Adolfo Jayme Barrientos <fitoschido@gmail.com> -Alain J. Baudrez <a.baudrez@gmail.com> -Kevin Brubeck Unhammer <unhammer@fsfe.org> -Bert <crinbert@yahoo.com> -Martin Dahl Moe -Marco de Freitas <marcodefreitas@gmail.com> -Martin Egger <martin.egger@gmx.net> -Tauno Erik <tauno.erik@gmail.com> -Sergiy Gavrylov <sergiovana@bigmir.net> -Emanuele Grande <caccolangrifata@gmail.com> -Torben Gundtofte-Bruun <torben@g-b.dk> -Joachim Johansson <joachim.j@gmail.com> -Miroslav Matejaš <silverspace@ubuntu-hr.org> -Nicolás M. Zahlut <nzahlut@live.com> -Erik M -Toni Lähdekorpi <toni@lygon.net> -Jose Luis Navarro <jlnavarro111@gmail.com> -Tomas Novak <kuvaly@seznam.cz> -Abel O'Rian <abel.orian@gmail.com> -Balazs Oveges <ovegesb@freemail.hu> -Daniel Paessler <daniel@paessler.org> -Miloš Popović <gpopac@gmail.com> -Michal Predotka <mpredotka@googlemail.com> -Ye Qing <allen19920930@gmail.com> -Luca Reverberi <thereve@gmail.com> -Mikko Ruohola <polarfox@polarfox.net> -Ahmed Shubbar <ahmed.shubbar@gmail.com> -Sergei Sedov <sedov@webmail.perm.ru> -Marco Solari <marcosolari@gmail.com> -Ilya Tsimokhin <ilya@tsimokhin.com> -Ulf Urdén <ulf.urden@purplescout.com> -Julien Valroff <julien@kirya.net> -Aron Xu <happyaron.xu@gmail.com> -梁其学 <yalongbay@gmail.com></property> - <property name="logo">rapid-photo-downloader.svg</property> - <property name="wrap_license">True</property> - <child internal-child="vbox"> - <object class="GtkVBox" id="dialog-vbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">2</property> - <child internal-child="action_area"> - <object class="GtkHButtonBox" id="dialog-action_area1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="layout_style">end</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <placeholder/> - </child> - </object> - </child> - </object> -</interface> diff --git a/rapid/glade3/errorlog.ui b/rapid/glade3/errorlog.ui deleted file mode 100644 index 43c8bdd..0000000 --- a/rapid/glade3/errorlog.ui +++ /dev/null @@ -1,90 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <requires lib="gtk+" version="2.20"/> - <!-- interface-naming-policy project-wide --> - <object class="GtkDialog" id="errorlog"> - <property name="can_focus">False</property> - <property name="border_width">5</property> - <property name="title" translatable="yes">Error Log</property> - <property name="default_width">650</property> - <property name="default_height">400</property> - <property name="destroy_with_parent">True</property> - <property name="icon">rapid-photo-downloader.svg</property> - <property name="type_hint">dialog</property> - <signal name="response" handler="on_errorlog_response" swapped="no"/> - <child internal-child="vbox"> - <object class="GtkVBox" id="dialog-vbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">2</property> - <child internal-child="action_area"> - <object class="GtkHButtonBox" id="dialog-action_area1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="layout_style">end</property> - <child> - <placeholder/> - </child> - <child> - <object class="GtkButton" id="button1"> - <property name="label">gtk-close</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_action_appearance">False</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="log_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <object class="GtkViewport" id="viewport1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkTextView" id="log_textview"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="editable">False</property> - <property name="cursor_visible">False</property> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - </object> - </child> - <action-widgets> - <action-widget response="0">button1</action-widget> - </action-widgets> - </object> -</interface> diff --git a/rapid/glade3/media-eject.png b/rapid/glade3/media-eject.png Binary files differdeleted file mode 100644 index 0ff107e..0000000 --- a/rapid/glade3/media-eject.png +++ /dev/null diff --git a/rapid/glade3/photo.svg b/rapid/glade3/photo.svg deleted file mode 100644 index 95c57d0..0000000 --- a/rapid/glade3/photo.svg +++ /dev/null @@ -1,1208 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.0" - width="96" - height="96" - id="svg2408" - inkscape:version="0.48.1 r9760" - sodipodi:docname="camera-photo.svg" - inkscape:export-filename="/home/damon/rapid/branding/camera-photo.png" - inkscape:export-xdpi="69.209999" - inkscape:export-ydpi="69.209999"> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1920" - inkscape:window-height="1176" - id="namedview245" - showgrid="false" - inkscape:zoom="7.719249" - inkscape:cx="32.454446" - inkscape:cy="48" - inkscape:window-x="0" - inkscape:window-y="24" - inkscape:window-maximized="1" - inkscape:current-layer="svg2408" /> - <defs - id="defs2410"> - <linearGradient - id="linearGradient3921"> - <stop - id="stop3923" - style="stop-color:#5fd3bc;stop-opacity:0" - offset="0" /> - <stop - id="stop3929" - style="stop-color:#5fd3bc;stop-opacity:1" - offset="0.5" /> - <stop - id="stop3925" - style="stop-color:#5fd3bc;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3911"> - <stop - id="stop3913" - style="stop-color:#2a7fff;stop-opacity:0" - offset="0" /> - <stop - id="stop3919" - style="stop-color:#2a7fff;stop-opacity:1" - offset="0.5" /> - <stop - id="stop3915" - style="stop-color:#2a7fff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3901"> - <stop - id="stop3903" - style="stop-color:#6600ff;stop-opacity:0" - offset="0" /> - <stop - id="stop3909" - style="stop-color:#6600ff;stop-opacity:1" - offset="0.5" /> - <stop - id="stop3905" - style="stop-color:#6600ff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3891"> - <stop - id="stop3893" - style="stop-color:#d42aff;stop-opacity:0" - offset="0" /> - <stop - id="stop3899" - style="stop-color:#d42aff;stop-opacity:1" - offset="0.5" /> - <stop - id="stop3895" - style="stop-color:#d42aff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3881"> - <stop - id="stop3883" - style="stop-color:#d40000;stop-opacity:0" - offset="0" /> - <stop - id="stop3889" - style="stop-color:#d40000;stop-opacity:1" - offset="0.5" /> - <stop - id="stop3885" - style="stop-color:#d40000;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3871"> - <stop - id="stop3873" - style="stop-color:#ff6600;stop-opacity:0" - offset="0" /> - <stop - id="stop3879" - style="stop-color:#ff6600;stop-opacity:1" - offset="0.5" /> - <stop - id="stop3875" - style="stop-color:#ff6600;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3861"> - <stop - id="stop3863" - style="stop-color:#ffcc00;stop-opacity:0" - offset="0" /> - <stop - id="stop3869" - style="stop-color:#ffcc00;stop-opacity:1" - offset="0.5" /> - <stop - id="stop3865" - style="stop-color:#ffcc00;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3851"> - <stop - id="stop3853" - style="stop-color:#55d400;stop-opacity:0" - offset="0" /> - <stop - id="stop3859" - style="stop-color:#55d400;stop-opacity:1" - offset="0.5" /> - <stop - id="stop3855" - style="stop-color:#55d400;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3797"> - <stop - id="stop3799" - style="stop-color:#ffffff;stop-opacity:1" - offset="0" /> - <stop - id="stop3805" - style="stop-color:#ffffff;stop-opacity:1" - offset="0.5" /> - <stop - id="stop3801" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3761"> - <stop - id="stop3763" - style="stop-color:#ffffff;stop-opacity:1" - offset="0" /> - <stop - id="stop3765" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3727"> - <stop - id="stop3729" - style="stop-color:#ffffff;stop-opacity:1" - offset="0" /> - <stop - id="stop3731" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3681"> - <stop - id="stop3683" - style="stop-color:#ffffff;stop-opacity:0.18548387" - offset="0" /> - <stop - id="stop3689" - style="stop-color:#ffffff;stop-opacity:0.10887097" - offset="0.3086735" /> - <stop - id="stop3691" - style="stop-color:#000000;stop-opacity:0" - offset="0.43622452" /> - <stop - id="stop3693" - style="stop-color:#000000;stop-opacity:0" - offset="0.54464293" /> - <stop - id="stop3695" - style="stop-color:#ffffff;stop-opacity:0" - offset="0.58290821" /> - <stop - id="stop3697" - style="stop-color:#000000;stop-opacity:0.1814516" - offset="0.61479598" /> - <stop - id="stop3699" - style="stop-color:#000000;stop-opacity:0.43145162" - offset="0.69132656" /> - <stop - id="stop3701" - style="stop-color:#ffffff;stop-opacity:0.08064516" - offset="0.78061229" /> - <stop - id="stop3703" - style="stop-color:#000000;stop-opacity:0" - offset="0.87627554" /> - <stop - id="stop3685" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3746"> - <stop - id="stop3748" - style="stop-color:#5d5d5d;stop-opacity:1" - offset="0" /> - <stop - id="stop3753" - style="stop-color:#0a0a0a;stop-opacity:1" - offset="0.60000002" /> - <stop - id="stop3751" - style="stop-color:#4e4e4e;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3732"> - <stop - id="stop3734" - style="stop-color:#000000;stop-opacity:0" - offset="0" /> - <stop - id="stop3740" - style="stop-color:#000000;stop-opacity:0" - offset="0.94999999" /> - <stop - id="stop3736" - style="stop-color:#000000;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3719"> - <stop - id="stop3721" - style="stop-color:#4b4b4b;stop-opacity:1" - offset="0" /> - <stop - id="stop3723" - style="stop-color:#232323;stop-opacity:1" - offset="0.37373093" /> - <stop - id="stop3725" - style="stop-color:#5c5c5c;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3664"> - <stop - id="stop3666" - style="stop-color:#000000;stop-opacity:0.68992245" - offset="0" /> - <stop - id="stop3804" - style="stop-color:#7f7f7f;stop-opacity:0" - offset="0.5" /> - <stop - id="stop3668" - style="stop-color:#ffffff;stop-opacity:0.21705426" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3650"> - <stop - id="stop3709" - style="stop-color:#141414;stop-opacity:1" - offset="0" /> - <stop - id="stop3654" - style="stop-color:#616161;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3641"> - <stop - id="stop3643" - style="stop-color:#0c0c0c;stop-opacity:1" - offset="0" /> - <stop - id="stop3645" - style="stop-color:#373737;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - x1="45.447727" - y1="92.539597" - x2="45.447727" - y2="7.0165396" - id="ButtonShadow" - gradientUnits="userSpaceOnUse" - gradientTransform="scale(1.0058652,0.994169)"> - <stop - id="stop3750" - style="stop-color:#000000;stop-opacity:1" - offset="0" /> - <stop - id="stop3752" - style="stop-color:#000000;stop-opacity:0.58823532" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3737"> - <stop - id="stop3739" - style="stop-color:#ffffff;stop-opacity:1" - offset="0" /> - <stop - id="stop3741" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <filter - color-interpolation-filters="sRGB" - id="filter3174"> - <feGaussianBlur - id="feGaussianBlur3176" - stdDeviation="1.71" /> - </filter> - <linearGradient - x1="36.357143" - y1="6" - x2="36.357143" - y2="63.893143" - id="linearGradient3188" - xlink:href="#linearGradient3737" - gradientUnits="userSpaceOnUse" /> - <filter - x="-0.192" - y="-0.192" - width="1.3839999" - height="1.3839999" - color-interpolation-filters="sRGB" - id="filter3794"> - <feGaussianBlur - id="feGaussianBlur3796" - stdDeviation="5.28" /> - </filter> - <linearGradient - x1="48" - y1="20.220806" - x2="48" - y2="138.66119" - id="linearGradient3613" - xlink:href="#linearGradient3737" - gradientUnits="userSpaceOnUse" /> - <radialGradient - cx="48" - cy="90.171875" - r="42" - fx="48" - fy="90.171875" - id="radialGradient3619" - xlink:href="#linearGradient3737" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.1573129,0,0,0.99590774,-7.5510206,0.19713193)" /> - <clipPath - id="clipPath3613"> - <rect - width="84" - height="84" - rx="6" - ry="6" - x="6" - y="6" - id="rect3615" - style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /> - </clipPath> - <linearGradient - x1="48" - y1="90" - x2="48" - y2="5.9877172" - id="linearGradient3617" - xlink:href="#linearGradient3641" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="16.162441" - y1="52.098946" - x2="76.838295" - y2="52.098946" - id="linearGradient3670" - xlink:href="#linearGradient3664" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="16.162441" - y1="44.48222" - x2="76.771591" - y2="44.48222" - id="linearGradient3674" - xlink:href="#linearGradient3719" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="16.162441" - y1="44.48222" - x2="76.771591" - y2="44.48222" - id="linearGradient3707" - xlink:href="#linearGradient3650" - gradientUnits="userSpaceOnUse" /> - <radialGradient - cx="48" - cy="48.000935" - r="27.001867" - fx="48" - fy="48.000935" - id="radialGradient3738" - xlink:href="#linearGradient3732" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0369653,0,0,1.0369653,-1.774336,-1.7753043)" /> - <linearGradient - x1="16.162441" - y1="44.48222" - x2="76.771591" - y2="44.48222" - id="linearGradient3744" - xlink:href="#linearGradient3746" - gradientUnits="userSpaceOnUse" /> - <clipPath - id="clipPath3798"> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 1 1 -27.274118,0 13.637059,13.637059 0 1 1 27.274118,0 z" - transform="matrix(1.0266143,0,0,1.0266143,-0.999999,0.778362)" - id="path3800" - style="fill:#ff00ff;fill-opacity:1;stroke:none" /> - </clipPath> - <filter - x="-0.23999999" - y="-0.24000001" - width="1.48" - height="1.48" - color-interpolation-filters="sRGB" - id="filter3833"> - <feGaussianBlur - id="feGaussianBlur3835" - stdDeviation="2.8000002" /> - </filter> - <radialGradient - cx="47.729706" - cy="45.997448" - r="13.637059" - fx="47.729706" - fy="45.997448" - id="radialGradient3687" - xlink:href="#linearGradient3681" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="33.199345" - y1="45.997448" - x2="67.957489" - y2="45.997448" - id="linearGradient3733" - xlink:href="#linearGradient3727" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="40.068184" - y1="58.17598" - x2="31.793724" - y2="49.665066" - id="linearGradient3767" - xlink:href="#linearGradient3761" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="40.068184" - y1="58.17598" - x2="40.068184" - y2="41.697067" - id="linearGradient3771" - xlink:href="#linearGradient3761" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="40.068184" - y1="58.17598" - x2="40.068184" - y2="41.697067" - id="linearGradient3775" - xlink:href="#linearGradient3761" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="40.068184" - y1="58.17598" - x2="40.068184" - y2="41.697067" - id="linearGradient3779" - xlink:href="#linearGradient3761" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="40.068184" - y1="58.17598" - x2="40.068184" - y2="41.697067" - id="linearGradient3791" - xlink:href="#linearGradient3761" - gradientUnits="userSpaceOnUse" /> - <radialGradient - cx="41" - cy="52" - r="9.7082043" - fx="41" - fy="52" - id="radialGradient3803" - xlink:href="#linearGradient3797" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1,0,0,0.61803397,0,19.862234)" /> - <filter - color-interpolation-filters="sRGB" - id="filter3774"> - <feGaussianBlur - id="feGaussianBlur3776" - stdDeviation="0.27274118" /> - </filter> - <linearGradient - x1="53.510502" - y1="59.608364" - x2="61.49099" - y2="39.92907" - id="linearGradient3967" - xlink:href="#linearGradient3881" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0266143,0,0,1.0266143,-0.9999987,50.778362)" /> - <linearGradient - x1="53.730709" - y1="60.139374" - x2="62.58559" - y2="38.831467" - id="linearGradient3969" - xlink:href="#linearGradient3891" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.72592593,0.72592593,-0.72592593,0.72592593,46.742509,29.961028)" /> - <linearGradient - x1="53.387505" - y1="60.34634" - x2="61.982975" - y2="39.437084" - id="linearGradient3971" - xlink:href="#linearGradient3901" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,1.0266143,-1.0266143,0,95.221638,49.000001)" /> - <linearGradient - x1="53.295853" - y1="59.182693" - x2="61.88982" - y2="40.223007" - id="linearGradient3973" - xlink:href="#linearGradient3911" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-0.72592593,0.72592593,-0.72592593,-0.72592593,116.03897,96.742509)" /> - <linearGradient - x1="53.87949" - y1="61.330303" - x2="60.999012" - y2="39.683075" - id="linearGradient3975" - xlink:href="#linearGradient3921" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-1.0266143,0,0,-1.0266143,96.999997,145.22164)" /> - <linearGradient - x1="53.469795" - y1="59.878456" - x2="60.324341" - y2="39.70118" - id="linearGradient3977" - xlink:href="#linearGradient3851" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-0.72592593,-0.72592593,0.72592593,-0.72592593,49.25749,166.03897)" /> - <linearGradient - x1="61.628212" - y1="39.683075" - x2="52.881298" - y2="60.715324" - id="linearGradient3979" - xlink:href="#linearGradient3861" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,-1.0266143,1.0266143,0,0.77836254,147)" /> - <linearGradient - x1="53.034939" - y1="61.183025" - x2="61.802849" - y2="40.483921" - id="linearGradient3981" - xlink:href="#linearGradient3871" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.72592593,-0.72592593,0.72592593,0.72592593,-20.03897,99.25749)" /> - <filter - color-interpolation-filters="sRGB" - id="filter3987"> - <feGaussianBlur - id="feGaussianBlur3989" - stdDeviation="0.42020394" /> - </filter> - <linearGradient - x1="64.072342" - y1="64.036171" - x2="46.604744" - y2="46.568573" - id="linearGradient3817" - xlink:href="#linearGradient3761" - gradientUnits="userSpaceOnUse" /> - <linearGradient - x1="45.447727" - y1="92.539597" - x2="45.447727" - y2="7.0165396" - id="ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0058652,0,0,0.994169,100,0)"> - <stop - id="stop3750-8" - style="stop-color:#000000;stop-opacity:1" - offset="0" /> - <stop - id="stop3752-5" - style="stop-color:#000000;stop-opacity:0.58823532" - offset="1" /> - </linearGradient> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient3780" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0238095,0,0,1.0119048,-1.1428571,-98.071429)" /> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient3772" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0238095,0,0,1.0119048,-1.1428571,-98.071429)" /> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient3725" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0238095,0,0,1.0119048,-1.1428571,-98.071429)" /> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient3721" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(0,-97)" /> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient2918" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0238095,0,0,1.0119048,-1.1428571,-98.071429)" /> - <linearGradient - x1="15.999999" - y1="85.0625" - x2="20.178572" - y2="85.0625" - id="linearGradient3556" - xlink:href="#linearGradient3376" - gradientUnits="userSpaceOnUse" /> - <linearGradient - id="linearGradient3376"> - <stop - id="stop3378" - style="stop-color:#767676;stop-opacity:1" - offset="0" /> - <stop - id="stop3380" - style="stop-color:#fdfdfd;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3733-2"> - <stop - id="stop3735" - style="stop-color:#ffbf67;stop-opacity:1" - offset="0" /> - <stop - id="stop3737" - style="stop-color:#c70000;stop-opacity:1" - offset="1" /> - </linearGradient> - <radialGradient - cx="18.089285" - cy="85.0625" - r="2.0892856" - fx="18.089285" - fy="85.0625" - id="radialGradient3029" - xlink:href="#linearGradient3733-2" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1,0,0,1.0042736,0,-0.3635233)" /> - </defs> - <metadata - id="metadata2413"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title /> - </cc:Work> - </rdf:RDF> - </metadata> - <g - id="layer2" - style="display:none"> - <rect - width="86" - height="85" - rx="6" - ry="6" - x="5" - y="7" - id="rect3745" - style="opacity:0.9;fill:url(#ButtonShadow);fill-opacity:1;fill-rule:nonzero;stroke:none;filter:url(#filter3174)" /> - </g> - <g - id="layer1" - style="display:inline" - inkscape:export-xdpi="70.709999" - inkscape:export-ydpi="70.709999" - inkscape:export-filename="/home/damon/rapid/branding/photo66.png"> - <rect - width="84" - height="84" - rx="6" - ry="6" - x="6" - y="6" - id="rect2419" - style="fill:url(#linearGradient3617);fill-opacity:1;fill-rule:nonzero;stroke:none" /> - <path - d="M 12,6 C 8.676,6 6,8.676 6,12 l 0,2 0,68 0,2 c 0,0.334721 0.04135,0.6507 0.09375,0.96875 0.0487,0.295596 0.09704,0.596915 0.1875,0.875 0.00988,0.03038 0.020892,0.0636 0.03125,0.09375 0.098865,0.287771 0.2348802,0.547452 0.375,0.8125 0.1445918,0.273507 0.3156161,0.535615 0.5,0.78125 0.1843839,0.245635 0.3737765,0.473472 0.59375,0.6875 0.439947,0.428056 0.94291,0.814526 1.5,1.09375 0.278545,0.139612 0.5734731,0.246947 0.875,0.34375 -0.2562018,-0.100222 -0.4867109,-0.236272 -0.71875,-0.375 -0.00741,-0.0044 -0.023866,0.0045 -0.03125,0 -0.031933,-0.0193 -0.062293,-0.04251 -0.09375,-0.0625 -0.120395,-0.0767 -0.2310226,-0.163513 -0.34375,-0.25 -0.1061728,-0.0808 -0.2132809,-0.161112 -0.3125,-0.25 C 8.4783201,88.557317 8.3087904,88.373362 8.15625,88.1875 8.0486711,88.057245 7.9378561,87.922215 7.84375,87.78125 7.818661,87.74287 7.805304,87.69538 7.78125,87.65625 7.716487,87.553218 7.6510225,87.451733 7.59375,87.34375 7.4927417,87.149044 7.3880752,86.928049 7.3125,86.71875 7.30454,86.69694 7.288911,86.6782 7.28125,86.65625 7.2494249,86.5643 7.2454455,86.469419 7.21875,86.375 7.1884177,86.268382 7.1483606,86.171969 7.125,86.0625 7.0521214,85.720988 7,85.364295 7,85 L 7,83 7,15 7,13 C 7,10.218152 9.2181517,8 12,8 l 2,0 68,0 2,0 c 2.781848,0 5,2.218152 5,5 l 0,2 0,68 0,2 c 0,0.364295 -0.05212,0.720988 -0.125,1.0625 -0.04415,0.206893 -0.08838,0.397658 -0.15625,0.59375 -0.0077,0.02195 -0.0233,0.04069 -0.03125,0.0625 -0.06274,0.173739 -0.138383,0.367449 -0.21875,0.53125 -0.04158,0.0828 -0.07904,0.169954 -0.125,0.25 -0.0546,0.09721 -0.126774,0.18835 -0.1875,0.28125 -0.09411,0.140965 -0.204921,0.275995 -0.3125,0.40625 -0.143174,0.17445 -0.303141,0.346998 -0.46875,0.5 -0.01117,0.0102 -0.01998,0.02115 -0.03125,0.03125 -0.138386,0.125556 -0.285091,0.234436 -0.4375,0.34375 -0.102571,0.07315 -0.204318,0.153364 -0.3125,0.21875 -0.0074,0.0045 -0.02384,-0.0044 -0.03125,0 -0.232039,0.138728 -0.462548,0.274778 -0.71875,0.375 0.301527,-0.0968 0.596455,-0.204138 0.875,-0.34375 0.55709,-0.279224 1.060053,-0.665694 1.5,-1.09375 0.219973,-0.214028 0.409366,-0.441865 0.59375,-0.6875 0.184384,-0.245635 0.355408,-0.507743 0.5,-0.78125 0.14012,-0.265048 0.276135,-0.524729 0.375,-0.8125 0.01041,-0.03078 0.02133,-0.06274 0.03125,-0.09375 0.09046,-0.278085 0.1388,-0.579404 0.1875,-0.875 C 89.95865,84.6507 90,84.334721 90,84 l 0,-2 0,-68 0,-2 C 90,8.676 87.324,6 84,6 L 12,6 z" - inkscape:connector-curvature="0" - id="rect3728" - style="opacity:0.25;fill:url(#linearGradient3188);fill-opacity:1;fill-rule:nonzero;stroke:none" /> - <path - d="M 12,90 C 8.676,90 6,87.324 6,84 L 6,82 6,14 6,12 c 0,-0.334721 0.04135,-0.6507 0.09375,-0.96875 0.0487,-0.295596 0.09704,-0.596915 0.1875,-0.875 C 6.29113,10.12587 6.302142,10.09265 6.3125,10.0625 6.411365,9.774729 6.5473802,9.515048 6.6875,9.25 6.8320918,8.976493 7.0031161,8.714385 7.1875,8.46875 7.3718839,8.223115 7.5612765,7.995278 7.78125,7.78125 8.221197,7.353194 8.72416,6.966724 9.28125,6.6875 9.559795,6.547888 9.8547231,6.440553 10.15625,6.34375 9.9000482,6.443972 9.6695391,6.580022 9.4375,6.71875 c -0.00741,0.0044 -0.023866,-0.0045 -0.03125,0 -0.031933,0.0193 -0.062293,0.04251 -0.09375,0.0625 -0.120395,0.0767 -0.2310226,0.163513 -0.34375,0.25 -0.1061728,0.0808 -0.2132809,0.161112 -0.3125,0.25 C 8.4783201,7.442683 8.3087904,7.626638 8.15625,7.8125 8.0486711,7.942755 7.9378561,8.077785 7.84375,8.21875 7.818661,8.25713 7.805304,8.30462 7.78125,8.34375 7.716487,8.446782 7.6510225,8.548267 7.59375,8.65625 7.4927417,8.850956 7.3880752,9.071951 7.3125,9.28125 7.30454,9.30306 7.288911,9.3218 7.28125,9.34375 7.2494249,9.4357 7.2454455,9.530581 7.21875,9.625 7.1884177,9.731618 7.1483606,9.828031 7.125,9.9375 7.0521214,10.279012 7,10.635705 7,11 l 0,2 0,68 0,2 c 0,2.781848 2.2181517,5 5,5 l 2,0 68,0 2,0 c 2.781848,0 5,-2.218152 5,-5 l 0,-2 0,-68 0,-2 C 89,10.635705 88.94788,10.279012 88.875,9.9375 88.83085,9.730607 88.78662,9.539842 88.71875,9.34375 88.71105,9.3218 88.69545,9.30306 88.6875,9.28125 88.62476,9.107511 88.549117,8.913801 88.46875,8.75 88.42717,8.6672 88.38971,8.580046 88.34375,8.5 88.28915,8.40279 88.216976,8.31165 88.15625,8.21875 88.06214,8.077785 87.951329,7.942755 87.84375,7.8125 87.700576,7.63805 87.540609,7.465502 87.375,7.3125 87.36383,7.3023 87.35502,7.29135 87.34375,7.28125 87.205364,7.155694 87.058659,7.046814 86.90625,6.9375 86.803679,6.86435 86.701932,6.784136 86.59375,6.71875 c -0.0074,-0.0045 -0.02384,0.0044 -0.03125,0 -0.232039,-0.138728 -0.462548,-0.274778 -0.71875,-0.375 0.301527,0.0968 0.596455,0.204138 0.875,0.34375 0.55709,0.279224 1.060053,0.665694 1.5,1.09375 0.219973,0.214028 0.409366,0.441865 0.59375,0.6875 0.184384,0.245635 0.355408,0.507743 0.5,0.78125 0.14012,0.265048 0.276135,0.524729 0.375,0.8125 0.01041,0.03078 0.02133,0.06274 0.03125,0.09375 0.09046,0.278085 0.1388,0.579404 0.1875,0.875 C 89.95865,11.3493 90,11.665279 90,12 l 0,2 0,68 0,2 c 0,3.324 -2.676,6 -6,6 l -72,0 z" - inkscape:connector-curvature="0" - id="path3615" - style="opacity:0.15;fill:url(#radialGradient3619);fill-opacity:1;fill-rule:nonzero;stroke:none" /> - </g> - <g - id="layer5" - style="display:none"> - <rect - width="66" - height="66" - rx="12" - ry="12" - x="15" - y="15" - clip-path="url(#clipPath3613)" - id="rect3171" - style="opacity:0.5;fill:url(#linearGradient3613);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3794)" /> - </g> - <g - id="layer3"> - <path - d="m 76.771595,44.48222 a 30.304577,30.304577 0 1 1 -60.609154,0 30.304577,30.304577 0 1 1 60.609154,0 z" - transform="matrix(0,1.1879394,-1.1879394,0,100.84218,-7.2000033)" - id="path3660" - style="fill:url(#linearGradient3670);fill-opacity:1;stroke:none" /> - <path - d="m 76.771595,44.48222 a 30.304577,30.304577 0 1 1 -60.609154,0 30.304577,30.304577 0 1 1 60.609154,0 z" - transform="matrix(0.79333332,0.79333332,-0.79333332,0.79333332,46.425393,-24.15306)" - id="path3648" - style="fill:url(#linearGradient3674);fill-opacity:1;stroke:none" /> - <path - d="m 76.771595,44.48222 a 30.304577,30.304577 0 1 1 -60.609154,0 30.304577,30.304577 0 1 1 60.609154,0 z" - transform="matrix(0,1.1136932,-1.1136932,0,97.53954,-3.75)" - id="path3672" - style="fill:none;stroke:#000000;stroke-width:0.44895676;stroke-miterlimit:4;stroke-dasharray:none" /> - <path - d="m 76.771595,44.48222 a 30.304577,30.304577 0 1 1 -60.609154,0 30.304577,30.304577 0 1 1 60.609154,0 z" - transform="matrix(0,0.92395284,-0.92395284,0,89.099473,5.0666664)" - id="path3697" - style="fill:url(#linearGradient3707);fill-opacity:1;stroke:none" /> - <g - transform="matrix(0.92592592,0,0,0.92592592,3.555556,3.5555557)" - id="g3691" - style="opacity:0.2"> - <path - d="m 76.771595,44.48222 a 30.304577,30.304577 0 1 1 -60.609154,0 30.304577,30.304577 0 1 1 60.609154,0 z" - transform="matrix(0,0.88270495,-0.88270495,0,87.264675,6.9833334)" - id="path3676" - style="fill:none;stroke:#000000;stroke-width:0.61175603;stroke-miterlimit:4;stroke-dasharray:none" /> - <path - d="m 76.771595,44.48222 a 30.304577,30.304577 0 1 1 -60.609154,0 30.304577,30.304577 0 1 1 60.609154,0 z" - transform="matrix(0,0.81670832,-0.81670832,0,84.328998,10.05)" - id="path3678" - style="fill:none;stroke:#000000;stroke-width:0.66119081;stroke-miterlimit:4;stroke-dasharray:none" /> - <path - d="m 76.771595,44.48222 a 30.304577,30.304577 0 1 1 -60.609154,0 30.304577,30.304577 0 1 1 60.609154,0 z" - transform="matrix(0,0.84970663,-0.84970663,0,85.796836,8.5166669)" - id="path3680" - style="fill:none;stroke:#000000;stroke-width:0.63551354;stroke-miterlimit:4;stroke-dasharray:none" /> - <path - d="m 76.771595,44.48222 a 30.304577,30.304577 0 1 1 -60.609154,0 30.304577,30.304577 0 1 1 60.609154,0 z" - transform="matrix(0,0.78371,-0.78371,0,82.861159,11.583334)" - id="path3682" - style="fill:none;stroke:#000000;stroke-width:0.68903041;stroke-miterlimit:4;stroke-dasharray:none" /> - </g> - <path - d="m 48,20 c -7.16533,0 -14.332595,2.731639 -19.799557,8.19879 -10.933924,10.934302 -10.933924,28.666181 0,39.600483 10.933924,10.934303 28.66519,10.934303 39.599114,0 10.933924,-10.934302 10.933924,-28.666181 0,-39.600483 C 62.332595,22.731639 55.16533,20 48,20 z m 0,2.592503 c 6.501873,0 12.9917,2.492512 17.952462,7.453446 9.921524,9.921866 9.921524,25.9843 0,35.906166 -9.921523,9.921867 -25.983401,9.921867 -35.904924,0 -9.921524,-9.921866 -9.921524,-25.9843 0,-35.906166 C 35.0083,25.085015 41.498127,22.592503 48,22.592503 z" - inkscape:connector-curvature="0" - id="path3727" - style="opacity:0.6;fill:url(#radialGradient3738);fill-opacity:1;stroke:none" /> - <path - d="m 76.771595,44.48222 a 30.304577,30.304577 0 1 1 -60.609154,0 30.304577,30.304577 0 1 1 60.609154,0 z" - transform="matrix(0,0.64907743,-0.64907743,0,76.872405,17.839307)" - id="path3742" - style="fill:url(#linearGradient3744);fill-opacity:1;stroke:#1a1a1a;stroke-width:1.54064822;stroke-miterlimit:4;stroke-dasharray:none" /> - <path - d="m 20.178571,85.0625 a 2.0892856,2.0982144 0 1 1 -4.178572,0 2.0892856,2.0982144 0 1 1 4.178572,0 z" - transform="matrix(0,1.6709402,1.6638297,0,-126.03844,-14.717185)" - id="path3374" - style="opacity:0.4025974;fill:url(#linearGradient3556);fill-opacity:1;fill-rule:nonzero;stroke:none" /> - <path - d="m 20.178571,85.0625 a 2.0892856,2.0982144 0 1 1 -4.178572,0 2.0892856,2.0982144 0 1 1 4.178572,0 z" - transform="matrix(1.1923077,0,0,1.1872339,-6.0769275,-85.480158)" - id="path3364" - style="fill:url(#radialGradient3029);fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:0.84049988;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.53181823;stroke-dasharray:none;stroke-dashoffset:0" /> - <g - transform="matrix(-0.5,0.8660254,-0.8660254,-0.5,113.57328,30.437809)" - id="text3845" - style="font-size:4px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:0.70995669;fill:#b3b3b3;fill-opacity:1;stroke:none;font-family:Liberation Sans;-inkscape-font-specification:Liberation Sans"> - <path - d="m 45.682364,77.430433 2.189583,0.08937 -0.01139,0.279065 -1.7628,2.097825 1.531928,0.06253 -0.01243,0.304434 -1.955403,-0.07981 0.01107,-0.271258 1.763118,-2.105631 -1.766108,-0.07208 0.01243,-0.304434" - inkscape:connector-curvature="0" - id="path3853" /> - <path - d="m 42.472517,78.425885 c 0.02879,-0.212908 0.08593,-0.402272 0.171412,-0.568093 0.0842,-0.165997 0.19274,-0.303081 0.325617,-0.411252 0.132881,-0.108172 0.287729,-0.184466 0.464543,-0.228884 0.176819,-0.04442 0.37168,-0.05223 0.584585,-0.02345 0.224521,0.03036 0.416587,0.0918 0.5762,0.184334 0.159441,0.09382 0.286829,0.210906 0.382165,0.351247 0.09516,0.141631 0.159443,0.302738 0.19284,0.483321 0.0334,0.180581 0.03623,0.373454 0.0085,0.57862 -0.02879,0.212905 -0.08446,0.401153 -0.167018,0.564745 -0.08256,0.163589 -0.189371,0.297621 -0.320437,0.402098 -0.131239,0.105763 -0.285092,0.179564 -0.461561,0.221404 -0.177931,0.04295 -0.375931,0.04968 -0.594001,0.0202 -0.216775,-0.02931 -0.404587,-0.08821 -0.563437,-0.176695 -0.160134,-0.08866 -0.288778,-0.201318 -0.385932,-0.33796 -0.09844,-0.136822 -0.165735,-0.295051 -0.20189,-0.47469 -0.03615,-0.179643 -0.04001,-0.374626 -0.01157,-0.58495 m 0.377425,0.05103 c -0.02215,0.163872 -0.02101,0.315785 0.0034,0.455739 0.02315,0.139777 0.06956,0.262991 0.139229,0.369644 0.06838,0.106475 0.159731,0.193719 0.274042,0.261734 0.114316,0.06801 0.250829,0.112747 0.409539,0.134208 0.162585,0.02198 0.308657,0.01545 0.438217,-0.01959 0.129562,-0.03504 0.242088,-0.09472 0.337579,-0.179039 0.0942,-0.0845 0.171021,-0.19105 0.230455,-0.319661 0.05815,-0.128789 0.0983,-0.275119 0.120452,-0.438991 0.02216,-0.163875 0.02184,-0.316991 -9.62e-4,-0.459349 -0.02426,-0.141244 -0.07032,-0.267039 -0.138182,-0.377386 -0.06932,-0.109232 -0.160878,-0.19979 -0.274669,-0.271674 -0.115254,-0.07077 -0.252238,-0.116881 -0.410951,-0.13834 -0.170324,-0.02303 -0.320529,-0.01509 -0.450615,0.02382 -0.131372,0.03874 -0.243045,0.101816 -0.335017,0.18924 -0.09343,0.08854 -0.168106,0.198668 -0.224021,0.330386 -0.0572,0.131541 -0.09671,0.277959 -0.118517,0.439253" - inkscape:connector-curvature="0" - id="path3855" /> - <path - d="m 39.301436,77.678216 c 0.05112,-0.208676 0.127935,-0.390946 0.230457,-0.546811 0.101263,-0.156174 0.223674,-0.281027 0.367232,-0.374559 0.143564,-0.09353 0.305604,-0.153043 0.48612,-0.178537 0.180521,-0.02549 0.375118,-0.01268 0.583792,0.03843 0.220058,0.0539 0.404561,0.135289 0.553507,0.244163 0.148639,0.110139 0.262948,0.240021 0.342928,0.389647 0.07967,0.150891 0.126575,0.317886 0.140711,0.500986 0.01414,0.183099 -0.0034,0.375192 -0.05268,0.57628 -0.05111,0.208673 -0.126357,0.389988 -0.225734,0.543945 -0.09938,0.153953 -0.219747,0.275954 -0.361115,0.366003 -0.141676,0.09131 -0.302464,0.148447 -0.482364,0.171414 -0.181473,0.02392 -0.379076,0.0097 -0.592812,-0.04265 -0.212467,-0.05205 -0.393008,-0.130451 -0.541622,-0.235219 -0.149874,-0.105082 -0.2659,-0.230693 -0.348077,-0.376833 -0.08344,-0.146453 -0.133644,-0.310906 -0.150623,-0.493359 -0.01697,-0.182456 -2.14e-4,-0.376756 0.05028,-0.5829 m 0.369924,0.09061 c -0.03934,0.160615 -0.05425,0.311799 -0.04474,0.453551 0.0083,0.14144 0.04139,0.268868 0.09941,0.382282 0.05676,0.113102 0.138377,0.209507 0.244865,0.289216 0.106493,0.0797 0.237517,0.138609 0.393072,0.176713 0.159354,0.03903 0.305298,0.04797 0.437834,0.02681 0.132539,-0.02116 0.250739,-0.06862 0.354602,-0.142382 0.1026,-0.07407 0.190243,-0.171917 0.262929,-0.293531 0.07142,-0.121927 0.126805,-0.263198 0.166146,-0.423814 0.03934,-0.160617 0.0552,-0.312911 0.04756,-0.456881 -0.0092,-0.143016 -0.04172,-0.272973 -0.09755,-0.38987 -0.0574,-0.115943 -0.138876,-0.215664 -0.244439,-0.299165 -0.107134,-0.08255 -0.238481,-0.14287 -0.39404,-0.180972 -0.166938,-0.04089 -0.317142,-0.04886 -0.45061,-0.02391 -0.134729,0.02464 -0.25244,0.07558 -0.353131,0.152797 -0.102262,0.07818 -0.18815,0.1798 -0.257665,0.304875 -0.07077,0.124764 -0.125524,0.26619 -0.164248,0.424278" - inkscape:connector-curvature="0" - id="path3857" /> - <path - d="m 36.93709,75.37391 -0.630016,1.724455 c -0.02278,0.06237 -0.04663,0.125747 -0.07154,0.190121 -0.0249,0.06437 -0.04841,0.123018 -0.07052,0.175938 -0.02523,0.06148 -0.05041,0.120902 -0.07552,0.178268 0.03991,-0.05612 0.08021,-0.111404 0.120894,-0.165851 0.03512,-0.04678 0.0734,-0.09656 0.114863,-0.14934 0.03979,-0.052 0.07758,-0.0985 0.113345,-0.139497 l 1.311189,-1.517182 0.245826,0.08981 0.03352,2.00846 c 6.37e-4,0.02103 7.68e-4,0.04533 3.91e-4,0.07292 -0.0016,0.02714 -0.0024,0.05595 -0.0025,0.08643 -0.0017,0.03126 -0.0028,0.06274 -0.0033,0.09444 -0.0022,0.03248 -0.0041,0.06435 -0.0058,0.09561 -0.0029,0.07241 -0.0073,0.147052 -0.01319,0.223915 0.0238,-0.06894 0.04676,-0.137485 0.06889,-0.205643 0.01856,-0.05838 0.03884,-0.119587 0.06085,-0.183629 0.02157,-0.06282 0.04106,-0.118081 0.05849,-0.165777 l 0.630016,-1.724456 0.31187,0.113939 -0.944354,2.584849 -0.460466,-0.168228 -0.03313,-2.043669 c 3.78e-4,-0.02759 0.0016,-0.06319 0.0037,-0.106789 0.0016,-0.04238 0.0043,-0.08576 0.008,-0.130144 0.0037,-0.04438 0.008,-0.08648 0.01287,-0.126299 0.0032,-0.03904 0.0065,-0.06902 0.01,-0.08993 -0.01082,0.01823 -0.02782,0.0439 -0.05101,0.07702 -0.02319,0.03312 -0.04827,0.06762 -0.07525,0.103511 -0.0282,0.03545 -0.05534,0.06989 -0.08142,0.103335 -0.02608,0.03345 -0.04848,0.06061 -0.0672,0.0815 l -1.32992,1.545688 -0.449459,-0.164206 0.944354,-2.584848 0.315538,0.115279" - inkscape:connector-curvature="0" - id="path3859" /> - <path - d="m 35.016377,74.508208 -1.277834,2.43729 -0.330392,-0.173219 1.136356,-2.167442 -1.231619,-0.645719 0.141478,-0.269849 1.562011,0.818939" - inkscape:connector-curvature="0" - id="path3861" /> - <path - d="m 33.052264,73.45796 -1.475751,2.322801 -1.762295,-1.119644 0.16339,-0.257173 1.447423,0.919595 0.473414,-0.745142 -1.348511,-0.856753 0.161296,-0.253876 1.34851,0.856752 0.514261,-0.809436 -1.515013,-0.962537 0.16339,-0.257173 1.829886,1.162586" - inkscape:connector-curvature="0" - id="path3863" /> - <path - d="m 29.39996,70.908274 -0.273684,2.754447 c 0.03706,-0.052 0.07321,-0.103884 0.108446,-0.155651 0.03068,-0.04377 0.06386,-0.08971 0.09956,-0.137827 0.03387,-0.04788 0.06478,-0.08983 0.09272,-0.125828 l 1.120895,-1.444173 0.262296,0.203582 -1.687331,2.173973 -0.342528,-0.265853 0.270922,-2.776371 c -0.03603,0.0528 -0.07207,0.105595 -0.108102,0.158391 -0.03147,0.0448 -0.06574,0.0932 -0.102804,0.145196 -0.03786,0.05303 -0.07475,0.102686 -0.11068,0.148973 l -1.106525,1.425657 -0.265382,-0.205977 1.687331,-2.173973 0.354871,0.275434" - inkscape:connector-curvature="0" - id="path3865" /> - <path - d="m 26.456345,69.279934 c 0.07932,-0.08449 0.169006,-0.146739 0.269069,-0.186749 0.09912,-0.0409 0.207358,-0.05537 0.324723,-0.04342 0.116476,0.0129 0.24074,0.05365 0.372792,0.122251 0.130213,0.06866 0.266519,0.169826 0.408917,0.303501 0.249675,0.23438 0.403294,0.46074 0.460857,0.67908 0.05756,0.218341 0.0278,0.427927 -0.08929,0.628758 L 27.890512,70.58874 c 0.03572,-0.06469 0.05987,-0.131317 0.07244,-0.199881 0.01257,-0.06856 0.01031,-0.140343 -0.0068,-0.215339 -0.01895,-0.07494 -0.05505,-0.153477 -0.108314,-0.235621 -0.05326,-0.08214 -0.128304,-0.168665 -0.225137,-0.259566 -0.08069,-0.07575 -0.160978,-0.138615 -0.240858,-0.188599 -0.08077,-0.04903 -0.158729,-0.08203 -0.233882,-0.09901 -0.07515,-0.01697 -0.146958,-0.01562 -0.215426,0.004 -0.07031,0.01972 -0.134868,0.06091 -0.193687,0.12357 -0.06149,0.06551 -0.09621,0.132926 -0.10415,0.202264 -0.0089,0.06845 0.0012,0.141361 0.0304,0.218739 0.02916,0.07738 0.07277,0.160287 0.130836,0.248727 0.05807,0.08844 0.122864,0.185879 0.194392,0.292317 0.04443,0.06492 0.08843,0.131236 0.132026,0.198947 0.04175,0.06777 0.07938,0.136128 0.112875,0.205079 0.03166,0.06901 0.05735,0.138666 0.07707,0.208973 0.01973,0.07031 0.02838,0.140042 0.02595,0.209207 -0.0033,0.07011 -0.01909,0.139239 -0.04734,0.207378 -0.02914,0.06909 -0.0749,0.136856 -0.137279,0.20331 -0.08912,0.09493 -0.184298,0.158276 -0.285546,0.190033 -0.103085,0.03181 -0.209301,0.03747 -0.318649,0.01698 -0.109344,-0.0205 -0.219891,-0.06444 -0.331643,-0.131842 -0.111746,-0.0674 -0.221732,-0.151897 -0.329959,-0.25349 -0.124359,-0.116745 -0.221327,-0.226524 -0.290903,-0.329339 -0.07046,-0.101866 -0.117802,-0.200778 -0.142025,-0.296735 -0.02421,-0.09596 -0.02718,-0.189824 -0.0089,-0.281597 0.01645,-0.09172 0.0494,-0.185796 0.09886,-0.282239 l 0.311825,0.204319 c -0.03216,0.06089 -0.05324,0.122356 -0.06326,0.184391 -0.01096,0.06114 -0.0081,0.123692 0.0087,0.187645 0.01676,0.06395 0.04833,0.130199 0.09471,0.198743 0.04544,0.06765 0.107549,0.138462 0.186341,0.21243 0.09304,0.08733 0.179377,0.152312 0.259021,0.194934 0.07781,0.04268 0.14954,0.06805 0.215195,0.07611 0.06471,0.0072 0.123878,-6.92e-4 0.177502,-0.02357 0.05179,-0.02282 0.09862,-0.05654 0.140506,-0.101162 0.05615,-0.05981 0.08777,-0.122988 0.09488,-0.189536 0.0053,-0.06649 -0.0057,-0.136614 -0.0329,-0.210369 -0.0272,-0.07376 -0.06648,-0.150814 -0.117845,-0.231175 -0.05136,-0.08036 -0.106992,-0.164733 -0.1669,-0.253115 -0.04834,-0.07217 -0.09576,-0.144362 -0.142256,-0.216586 -0.04834,-0.07217 -0.09071,-0.144982 -0.127115,-0.218447 -0.03729,-0.07251 -0.06723,-0.145262 -0.0898,-0.218243 -0.02352,-0.07387 -0.0346,-0.147675 -0.03325,-0.221412 0.0014,-0.07374 0.01747,-0.147018 0.04832,-0.219845 0.02991,-0.07372 0.07918,-0.147125 0.147798,-0.220224" - inkscape:connector-curvature="0" - id="path3867" /> - <path - d="m 18.630854,41.246205 0.610015,0.126766 -0.06756,0.325086 -0.610014,-0.126766 -0.263863,1.269749 -0.267718,-0.05563 -1.560346,-1.610929 0.07511,-0.36142 1.812833,0.37672 0.07868,-0.37863 0.271543,0.05643 -0.07868,0.37863 m -1.76374,-0.02739 c 0.0058,0.0039 0.01727,0.0129 0.03427,0.02707 0.01573,0.01391 0.03374,0.02962 0.05404,0.04714 0.0203,0.01752 0.0411,0.03581 0.06241,0.05486 0.0203,0.01752 0.03704,0.03297 0.05021,0.04634 l 0.873873,0.901738 c 0.0089,0.0085 0.02027,0.02084 0.0342,0.03703 0.01419,0.01492 0.02888,0.03061 0.04408,0.04706 0.01393,0.01619 0.02785,0.03239 0.04177,0.04858 0.01392,0.01619 0.02455,0.02904 0.03189,0.03854 l 0.197897,-0.952311 -1.424642,-0.296051" - inkscape:connector-curvature="0" - id="path3869" /> - <path - d="m 19.399774,40.683288 -0.238326,-0.06876 c -0.127964,-0.106035 -0.234444,-0.220778 -0.319439,-0.344229 -0.08588,-0.125063 -0.159246,-0.251257 -0.220086,-0.378581 -0.06209,-0.127684 -0.117659,-0.252132 -0.166711,-0.373345 -0.04869,-0.122462 -0.100265,-0.234917 -0.154731,-0.337365 -0.05446,-0.102444 -0.117193,-0.190335 -0.188189,-0.263672 -0.07063,-0.07458 -0.159744,-0.127399 -0.267336,-0.158443 -0.07506,-0.02166 -0.143991,-0.02935 -0.206787,-0.02307 -0.06404,0.0059 -0.121594,0.02455 -0.172651,0.05589 -0.05105,0.03135 -0.09463,0.07434 -0.130719,0.128974 -0.03698,0.05303 -0.06594,0.115819 -0.08687,0.188378 -0.01949,0.06756 -0.02852,0.13407 -0.02708,0.199533 0.0018,0.06421 0.01479,0.124881 0.03897,0.181997 0.02418,0.05712 0.06008,0.108811 0.107717,0.155078 0.04675,0.04466 0.105762,0.08201 0.177045,0.112064 l -0.131524,0.336087 c -0.09183,-0.03869 -0.173699,-0.09077 -0.245611,-0.15624 -0.07191,-0.06547 -0.12867,-0.143506 -0.17029,-0.234115 -0.04287,-0.09097 -0.06782,-0.192355 -0.07487,-0.304159 -0.0067,-0.113052 0.0093,-0.236511 0.04791,-0.370375 0.0379,-0.131359 0.08729,-0.243821 0.148161,-0.337385 0.06088,-0.09356 0.13207,-0.166528 0.213583,-0.218903 0.08188,-0.05362 0.173268,-0.0862 0.27417,-0.09775 0.100906,-0.01154 0.210158,-3.48e-4 0.327755,0.03358 0.08883,0.02563 0.16858,0.06626 0.239262,0.121883 0.07105,0.05438 0.135087,0.118933 0.192126,0.193661 0.0574,0.07348 0.109775,0.154997 0.157118,0.244545 0.04734,0.08955 0.09253,0.181869 0.135571,0.276951 0.04179,0.09472 0.08339,0.190072 0.124816,0.286044 0.04018,0.09561 0.08224,0.187029 0.126188,0.274244 0.04431,0.08597 0.09186,0.165412 0.142647,0.238337 0.04953,0.07256 0.105625,0.134147 0.168272,0.184746 l 0.397946,-1.379288 0.287117,0.08284 -0.505148,1.750851" - inkscape:connector-curvature="0" - id="path3871" /> - <path - d="m 20.097629,38.396989 -0.403551,-0.141786 0.126248,-0.359327 0.403551,0.141787 -0.126248,0.359326" - inkscape:connector-curvature="0" - id="path3873" /> - <path - d="m 20.293152,35.445622 c 0.127403,0.05309 0.235975,0.1209 0.325717,0.203432 0.08974,0.08254 0.157297,0.17769 0.202668,0.285461 0.04417,0.107275 0.06375,0.226165 0.05874,0.356672 -0.0045,0.129308 -0.03757,0.267879 -0.09917,0.415713 -0.05559,0.133413 -0.119797,0.243488 -0.192615,0.330225 -0.07232,0.08554 -0.149793,0.151289 -0.232426,0.19726 -0.08384,0.04547 -0.170925,0.07336 -0.26127,0.08368 -0.08984,0.0091 -0.180338,0.0046 -0.271484,-0.01368 l 0.09886,-0.343898 c 0.05359,0.0068 0.108224,0.0077 0.163913,0.0027 0.05449,-0.0055 0.108273,-0.02118 0.161359,-0.04702 0.05238,-0.02754 0.101914,-0.06685 0.14859,-0.117936 0.04597,-0.05278 0.08699,-0.122443 0.123051,-0.208981 0.03506,-0.08413 0.05424,-0.165713 0.05755,-0.244741 0.0021,-0.07953 -0.01207,-0.153845 -0.04251,-0.222957 -0.02995,-0.07031 -0.07607,-0.133965 -0.138367,-0.190959 -0.0623,-0.05699 -0.140923,-0.10527 -0.235874,-0.144837 -0.07812,-0.03255 -0.154897,-0.04973 -0.230318,-0.05154 -0.07662,-0.0023 -0.148136,0.01022 -0.214544,0.03755 -0.06761,0.02684 -0.128907,0.06901 -0.183897,0.126499 -0.05449,0.05629 -0.09976,0.127704 -0.135822,0.21424 -0.02254,0.05409 -0.03851,0.10597 -0.04793,0.155648 -0.0094,0.04968 -0.01397,0.09786 -0.01367,0.144531 8.02e-4,0.04547 0.0059,0.08919 0.01517,0.13116 0.0086,0.04027 0.01918,0.07923 0.0317,0.116888 l -0.132218,0.317305 -1.331266,-0.654175 0.601743,-1.444098 0.275839,0.11494 -0.47854,1.148427 0.785599,0.384482 c -0.02043,-0.07622 -0.02824,-0.164112 -0.02343,-0.263673 0.0036,-0.100058 0.0302,-0.209583 0.07979,-0.328574 0.05259,-0.126199 0.119146,-0.231766 0.199676,-0.316702 0.08053,-0.08493 0.171027,-0.148081 0.271488,-0.189447 0.09926,-0.04186 0.205581,-0.06174 0.318962,-0.05964 0.113381,0.0021 0.228365,0.02745 0.34495,0.07603" - inkscape:connector-curvature="0" - id="path3875" /> - <path - d="M 21.868617,34.296638 20.68612,33.666653 c -0.09078,-0.04836 -0.169691,-0.08303 -0.23672,-0.103985 -0.06703,-0.02095 -0.126275,-0.02744 -0.177743,-0.01945 -0.052,0.0062 -0.09711,0.0272 -0.135312,0.06291 -0.03935,0.0351 -0.07587,0.08425 -0.109539,0.147452 -0.0349,0.06551 -0.05321,0.131728 -0.05494,0.198669 -0.0029,0.06633 0.01028,0.131622 0.0395,0.19587 0.02867,0.06249 0.0737,0.123359 0.135077,0.182614 0.06084,0.05749 0.137803,0.111038 0.230885,0.160627 l 1.080796,0.575804 -0.164385,0.308552 -1.466916,-0.781513 c -0.03907,-0.02081 -0.08017,-0.04197 -0.123305,-0.06348 -0.04367,-0.02326 -0.08477,-0.04442 -0.123305,-0.06348 -0.03968,-0.01966 -0.07362,-0.03627 -0.101815,-0.04982 -0.02819,-0.01354 -0.04662,-0.02188 -0.05527,-0.02502 l 0.156119,-0.293038 c 0.0064,0.0019 0.02248,0.009 0.04838,0.02135 0.02589,0.01232 0.05581,0.02678 0.08975,0.04339 0.0334,0.01485 0.06765,0.03088 0.102733,0.04809 0.03394,0.01661 0.0624,0.03103 0.08538,0.04328 l 0.0028,-0.0052 c -0.03932,-0.05635 -0.07224,-0.112252 -0.09877,-0.167695 -0.02652,-0.05544 -0.0434,-0.11238 -0.05063,-0.170819 -0.0078,-0.0602 -0.005,-0.122162 0.0083,-0.185895 0.01276,-0.06549 0.03874,-0.135011 0.07792,-0.20856 0.07531,-0.141345 0.162847,-0.237815 0.262623,-0.289409 0.100391,-0.05274 0.220638,-0.06244 0.360741,-0.02911 l 0.0028,-0.0052 c -0.03932,-0.05635 -0.07163,-0.1134 -0.09693,-0.171142 -0.0253,-0.05774 -0.04065,-0.11755 -0.04604,-0.179438 -0.0065,-0.06249 -0.0025,-0.126757 0.01198,-0.19279 0.01337,-0.06664 0.03965,-0.136733 0.07884,-0.210283 0.0502,-0.09423 0.106006,-0.168511 0.167409,-0.222846 0.06202,-0.05548 0.131087,-0.09097 0.207214,-0.106481 0.07613,-0.0155 0.161645,-0.01125 0.256554,0.01275 0.09376,0.0234 0.198099,0.06571 0.313016,0.126928 l 1.242829,0.662128 -0.163466,0.306829 -1.182497,-0.629986 c -0.09078,-0.04836 -0.169691,-0.08303 -0.236719,-0.103984 -0.06703,-0.02095 -0.126276,-0.02744 -0.177743,-0.01945 -0.052,0.0062 -0.09711,0.0272 -0.135312,0.06291 -0.03935,0.0351 -0.07587,0.08425 -0.109539,0.147452 -0.0349,0.06551 -0.05379,0.131423 -0.05667,0.197751 -0.004,0.06572 0.008,0.130399 0.03605,0.194033 0.02867,0.06249 0.0737,0.123361 0.135077,0.182614 0.06199,0.05811 0.140676,0.112569 0.236057,0.163382 l 1.080795,0.575804 -0.163465,0.306828" - inkscape:connector-curvature="0" - id="path3877" /> - <path - d="M 23.584114,31.437572 22.48027,30.678158 c -0.08475,-0.0583 -0.159236,-0.101646 -0.223471,-0.130035 -0.06424,-0.02839 -0.122372,-0.04151 -0.174413,-0.03939 -0.05237,3.21e-4 -0.09956,0.01607 -0.141546,0.04723 -0.04306,0.03043 -0.08489,0.07515 -0.12548,0.134149 -0.04207,0.06115 -0.06774,0.12488 -0.07701,0.191199 -0.01035,0.06558 -0.0046,0.131942 0.01714,0.199075 0.02144,0.06533 0.05931,0.130889 0.113605,0.196692 0.05396,0.06399 0.124392,0.12588 0.211282,0.185658 l 1.008907,0.6941 -0.198156,0.288029 -1.369346,-0.942072 c -0.03647,-0.02509 -0.07492,-0.05075 -0.115353,-0.07699 -0.04076,-0.02804 -0.07921,-0.05371 -0.115353,-0.07699 -0.03721,-0.02402 -0.06906,-0.04435 -0.09554,-0.06099 -0.02648,-0.01664 -0.04385,-0.027 -0.0521,-0.0311 l 0.188193,-0.273547 c 0.0061,0.0026 0.02132,0.01151 0.04566,0.02667 0.02434,0.01516 0.05243,0.03291 0.08428,0.05324 0.03151,0.01852 0.06373,0.03831 0.09665,0.05938 0.03185,0.02033 0.0585,0.03788 0.07995,0.05263 l 0.0033,-0.0048 c -0.0327,-0.06043 -0.05911,-0.119687 -0.07921,-0.17777 -0.0201,-0.05808 -0.03044,-0.11656 -0.03103,-0.175441 -9.19e-4,-0.06069 0.0088,-0.121945 0.02923,-0.18377 0.02007,-0.06363 0.05373,-0.129777 0.100956,-0.198434 0.09078,-0.131944 0.188646,-0.217918 0.293608,-0.257922 0.105701,-0.04107 0.226275,-0.03714 0.361722,0.01178 l 0.0033,-0.0048 c -0.03271,-0.06043 -0.05837,-0.120759 -0.077,-0.180988 -0.01862,-0.06022 -0.02712,-0.121386 -0.02549,-0.183486 5.57e-4,-0.06283 0.01178,-0.126235 0.03366,-0.190207 0.02081,-0.0647 0.05483,-0.131385 0.102063,-0.200043 0.06052,-0.08796 0.124348,-0.15547 0.19149,-0.202529 0.06788,-0.04812 0.140517,-0.0756 0.217907,-0.08242 0.07739,-0.0068 0.161882,0.0071 0.253476,0.04163 0.09052,0.03383 0.189418,0.08765 0.296692,0.161443 L 24.473049,30.145463 24.276,30.431884 23.172156,29.67247 c -0.08475,-0.0583 -0.159237,-0.101645 -0.223471,-0.130036 -0.06424,-0.02838 -0.122373,-0.04151 -0.174413,-0.03939 -0.05237,3.22e-4 -0.09956,0.01607 -0.141546,0.04723 -0.04306,0.03043 -0.08489,0.07515 -0.125481,0.134148 -0.04207,0.06115 -0.06827,0.124513 -0.07862,0.190092 -0.01142,0.06485 -0.0068,0.130467 0.01392,0.196861 0.02144,0.06533 0.05931,0.13089 0.113605,0.196692 0.05504,0.06473 0.127072,0.127726 0.216109,0.188979 l 1.008907,0.6941 -0.197049,0.28642" - inkscape:connector-curvature="0" - id="path3879" /> - <path - d="m 24.787912,28.352718 -0.23516,-0.205805 0.643142,-0.734876 0.235161,0.205805 -0.643143,0.734876" - inkscape:connector-curvature="0" - id="path3881" /> - <path - d="m 27.872659,25.20214 c 0.09453,0.100568 0.165856,0.206864 0.213976,0.318889 0.04812,0.112029 0.07083,0.226493 0.06814,0.343395 -0.0036,0.115955 -0.03437,0.23245 -0.09233,0.349485 -0.05702,0.116144 -0.143878,0.229062 -0.260572,0.338753 -0.105311,0.09899 -0.208932,0.173163 -0.310863,0.222515 -0.100984,0.04846 -0.19858,0.07676 -0.292789,0.0849 -0.0951,0.0072 -0.185981,-0.003 -0.272638,-0.03054 -0.08571,-0.02844 -0.166417,-0.06963 -0.242126,-0.123555 l 0.230914,-0.273348 c 0.04611,0.02814 0.09559,0.05132 0.148458,0.06953 0.05197,0.01727 0.107462,0.02497 0.166474,0.02311 0.05907,-0.0037 0.120346,-0.01931 0.183836,-0.04682 0.06355,-0.02935 0.129474,-0.07613 0.197784,-0.140346 0.06641,-0.06243 0.117291,-0.129016 0.152642,-0.199774 0.03446,-0.0717 0.05193,-0.145316 0.05243,-0.220836 0.0014,-0.07641 -0.0146,-0.153362 -0.04813,-0.230856 -0.03353,-0.07749 -0.08552,-0.153712 -0.15597,-0.228665 -0.05797,-0.06167 -0.120992,-0.108752 -0.189073,-0.141257 -0.06897,-0.03345 -0.13935,-0.05129 -0.211131,-0.05351 -0.07267,-0.0032 -0.145858,0.01023 -0.219556,0.04019 -0.07275,0.02907 -0.143278,0.07571 -0.211587,0.139918 -0.04269,0.04013 -0.0785,0.08094 -0.107417,0.122415 -0.02892,0.04148 -0.05279,0.08357 -0.07161,0.126285 -0.01787,0.04182 -0.03115,0.08378 -0.03982,0.125888 -0.0086,0.04027 -0.01491,0.08014 -0.0189,0.119626 l -0.250467,0.235436 -0.947096,-1.141588 1.139909,-1.071504 0.204669,0.217736 -0.906519,0.85212 0.559531,0.672247 c 0.01254,-0.07791 0.04138,-0.161302 0.0865,-0.250181 0.04423,-0.08982 0.113309,-0.178881 0.207234,-0.267172 0.09962,-0.09364 0.203542,-0.162733 0.311774,-0.207287 0.108231,-0.04455 0.216643,-0.06515 0.325236,-0.06179 0.107699,0.0024 0.212847,0.02777 0.315444,0.07608 0.102595,0.04831 0.197145,0.118481 0.283651,0.210507" - inkscape:connector-curvature="0" - id="path3883" /> - <path - d="m 29.355132,23.367427 c 0.166083,0.20563 0.286234,0.394797 0.360453,0.567502 0.07441,0.170877 0.112865,0.327279 0.115359,0.469207 0.0027,0.140099 -0.02642,0.265704 -0.08731,0.376815 -0.06089,0.111113 -0.143002,0.208395 -0.246321,0.291845 -0.104334,0.08427 -0.216712,0.144073 -0.337135,0.179409 -0.120424,0.03534 -0.248834,0.03695 -0.38523,0.0049 -0.135384,-0.03292 -0.278774,-0.103728 -0.430171,-0.212425 -0.151201,-0.110527 -0.310252,-0.269111 -0.477155,-0.475752 -0.174265,-0.215756 -0.299519,-0.40917 -0.375764,-0.580242 -0.07605,-0.1729 -0.114598,-0.328386 -0.115652,-0.466461 -0.001,-0.138069 0.03051,-0.260635 0.09467,-0.367698 0.06335,-0.108071 0.148709,-0.20547 0.25608,-0.292196 0.102308,-0.08263 0.21194,-0.142725 0.328895,-0.180285 0.117152,-0.03939 0.243107,-0.04404 0.377867,-0.01397 0.134761,0.03008 0.278657,0.100479 0.43169,0.211198 0.152214,0.109712 0.315454,0.272446 0.48972,0.4882 m -0.278053,0.224582 c -0.137449,-0.170173 -0.26171,-0.303297 -0.372783,-0.399373 -0.111892,-0.09708 -0.214588,-0.1631 -0.308091,-0.19805 -0.09432,-0.03596 -0.181395,-0.04429 -0.261223,-0.02501 -0.07963,0.01746 -0.155915,0.05564 -0.228847,0.114541 -0.07698,0.06218 -0.133396,0.131178 -0.16924,0.206991 -0.03584,0.07582 -0.04661,0.164021 -0.03232,0.264612 0.01449,0.09876 0.05743,0.213052 0.1288,0.342862 0.07056,0.128802 0.174149,0.277782 0.310779,0.446942 0.132541,0.164098 0.254347,0.294183 0.36542,0.390256 0.112087,0.09526 0.215699,0.16137 0.310838,0.198342 0.09533,0.03514 0.184141,0.04459 0.266424,0.02834 0.08146,-0.01726 0.159168,-0.05575 0.233113,-0.115476 0.07192,-0.05809 0.124884,-0.125137 0.158896,-0.201148 0.03319,-0.07702 0.04213,-0.16542 0.02682,-0.265196 -0.01512,-0.101605 -0.05745,-0.217217 -0.126988,-0.346836 -0.06853,-0.130434 -0.169065,-0.277701 -0.301605,-0.441799" - inkscape:connector-curvature="0" - id="path3885" /> - <path - d="M 31.579813,23.488032 30.839466,22.37131 c -0.05684,-0.08573 -0.109752,-0.153761 -0.158736,-0.204087 -0.04899,-0.05032 -0.09801,-0.08421 -0.147082,-0.101676 -0.0487,-0.01926 -0.09835,-0.02228 -0.148945,-0.009 -0.05131,0.01215 -0.106812,0.03801 -0.166501,0.07758 -0.06186,0.04101 -0.109475,0.09055 -0.142849,0.148601 -0.03409,0.05697 -0.05358,0.120664 -0.05845,0.191071 -0.0045,0.06861 0.0061,0.143568 0.03193,0.224888 0.02616,0.07952 0.06838,0.163229 0.126655,0.251133 l 0.676673,1.020677 -0.29139,0.193181 -0.918418,-1.385321 c -0.02446,-0.0369 -0.05055,-0.07506 -0.07825,-0.1145 -0.02734,-0.04124 -0.05342,-0.07941 -0.07825,-0.1145 -0.02555,-0.03618 -0.0475,-0.06693 -0.06585,-0.09226 -0.01835,-0.02532 -0.03059,-0.04143 -0.03671,-0.04831 l 0.276739,-0.183468 c 0.0047,0.0047 0.01548,0.01864 0.03239,0.04179 0.01692,0.02316 0.03635,0.05011 0.0583,0.08086 0.02232,0.02895 0.04481,0.05934 0.06748,0.09118 0.02195,0.03076 0.04012,0.05698 0.05451,0.07869 l 0.0049,-0.0032 c -0.0078,-0.06827 -0.01013,-0.133102 -0.0071,-0.194489 0.003,-0.06138 0.01529,-0.119494 0.03674,-0.174333 0.02181,-0.05664 0.05373,-0.109825 0.09575,-0.159556 0.04238,-0.05153 0.0983,-0.100321 0.167761,-0.14637 0.133486,-0.08849 0.256382,-0.131695 0.36869,-0.129604 0.113392,0.0014 0.223774,0.05005 0.331146,0.146026 l 0.0049,-0.0032 c -0.0078,-0.06827 -0.009,-0.133821 -0.0038,-0.196648 0.0052,-0.06282 0.02018,-0.122731 0.04488,-0.179729 0.02398,-0.05808 0.05807,-0.112702 0.10226,-0.163873 0.04347,-0.05225 0.09993,-0.1014 0.16939,-0.14745 0.08899,-0.05899 0.173414,-0.09778 0.253273,-0.116357 0.08094,-0.01929 0.158582,-0.01765 0.232919,0.0049 0.07433,0.02258 0.14753,0.06701 0.219588,0.133281 0.07134,0.06519 0.142978,0.152046 0.214928,0.260568 l 0.77812,1.173697 -0.289762,0.192102 -0.740347,-1.116722 c -0.05684,-0.08573 -0.109752,-0.153761 -0.158736,-0.204087 -0.04899,-0.05032 -0.09801,-0.08421 -0.147081,-0.101676 -0.0487,-0.01926 -0.09835,-0.02228 -0.148945,-0.009 -0.05131,0.01215 -0.106814,0.03801 -0.166501,0.07758 -0.06186,0.04101 -0.109836,0.09 -0.143929,0.146973 -0.03481,0.05589 -0.05502,0.118494 -0.06061,0.187815 -0.0045,0.06861 0.0061,0.143569 0.03193,0.224888 0.02688,0.0806 0.07017,0.165943 0.129893,0.256016 l 0.676672,1.020678 -0.289761,0.192101" - inkscape:connector-curvature="0" - id="path3887" /> - <path - d="m 34.448904,21.789158 -0.609488,-1.193191 c -0.04679,-0.0916 -0.09168,-0.165173 -0.134671,-0.220709 -0.04299,-0.05553 -0.08787,-0.09474 -0.134657,-0.117637 -0.04622,-0.02464 -0.09521,-0.03325 -0.14697,-0.02581 -0.05236,0.0063 -0.110422,0.0257 -0.174197,0.05828 -0.0661,0.03376 -0.119002,0.0776 -0.158718,0.131517 -0.04031,0.05276 -0.06686,0.113841 -0.07966,0.183247 -0.01223,0.06766 -0.01012,0.143342 0.0063,0.227054 0.01701,0.08196 0.0495,0.169907 0.09748,0.263831 l 0.557069,1.09057 -0.311343,0.159035 -0.756085,-1.480183 c -0.02014,-0.03942 -0.04174,-0.08029 -0.06482,-0.122605 -0.02251,-0.04406 -0.04411,-0.08493 -0.06482,-0.122605 -0.0213,-0.03883 -0.03963,-0.07187 -0.05501,-0.09911 -0.01537,-0.02723 -0.02572,-0.04462 -0.03102,-0.05214 l 0.295689,-0.15104 c 0.0041,0.0052 0.01328,0.02027 0.02747,0.04519 0.01419,0.02492 0.03045,0.0539 0.04879,0.08693 0.0189,0.03129 0.03782,0.06402 0.05675,0.09822 0.01834,0.03304 0.03343,0.06115 0.04527,0.08434 l 0.0052,-0.0027 c -9e-6,-0.06871 0.005,-0.133394 0.01493,-0.194045 0.01,-0.06065 0.02869,-0.117002 0.0562,-0.169068 0.02807,-0.05381 0.06579,-0.103054 0.113156,-0.147721 0.04793,-0.04642 0.109007,-0.08858 0.183219,-0.126487 0.142627,-0.07285 0.269616,-0.101897 0.380968,-0.08713 0.112512,0.01417 0.21669,0.07501 0.312536,0.182492 l 0.0052,-0.0027 c -1e-5,-0.06871 0.0061,-0.133986 0.01841,-0.195821 0.01228,-0.06183 0.03391,-0.119667 0.06489,-0.173511 0.03039,-0.055 0.07043,-0.105423 0.120114,-0.151275 0.04909,-0.04701 0.110745,-0.08947 0.184959,-0.127376 0.09508,-0.04857 0.183347,-0.07757 0.264794,-0.08701 0.0826,-0.01002 0.15956,3.77e-4 0.230871,0.03121 0.07131,0.03083 0.139019,0.08324 0.203131,0.157228 0.06352,0.07283 0.124892,0.167221 0.184125,0.283175 l 0.640584,1.254068 -0.309603,0.158147 -0.609489,-1.193191 c -0.04679,-0.0916 -0.09168,-0.165173 -0.134671,-0.220709 -0.04299,-0.05553 -0.08788,-0.09474 -0.134657,-0.117636 -0.04622,-0.02464 -0.09521,-0.03325 -0.14697,-0.02581 -0.05236,0.0063 -0.110423,0.0257 -0.174197,0.05828 -0.0661,0.03376 -0.119299,0.07702 -0.159607,0.129777 -0.0409,0.0516 -0.06805,0.111522 -0.08143,0.179768 -0.01223,0.06766 -0.01012,0.143343 0.0063,0.227055 0.0176,0.08312 0.05098,0.172806 0.100147,0.269048 l 0.557069,1.09057 -0.309604,0.158147" - inkscape:connector-curvature="0" - id="path3889" /> - <path - d="m 42.744275,18.955546 -0.04478,-0.295454 0.693255,-0.105066 -0.317246,-2.093284 -0.547647,0.531421 -0.04975,-0.328283 0.576028,-0.539672 0.320559,-0.04858 0.367584,2.425428 0.662358,-0.100383 0.04478,0.295454 -1.705138,0.258421" - inkscape:connector-curvature="0" - id="path3891" /> - <path - d="m 44.849923,16.947576 -0.03792,-0.402515 0.379181,-0.03572 0.03792,0.402515 -0.379181,0.03572 m 0.16027,1.701453 -0.03791,-0.402515 0.379181,-0.03572 0.03791,0.402515 -0.379181,0.03572" - inkscape:connector-curvature="0" - id="path3893" /> - <path - d="m 46.054835,18.552163 -0.01133,-0.298614 0.700668,-0.02658 -0.08025,-2.115666 -0.603843,0.466582 -0.01259,-0.331793 0.632971,-0.471596 0.323985,-0.01229 0.09298,2.451363 0.66944,-0.02539 0.01133,0.298613 -1.72337,0.06537" - inkscape:connector-curvature="0" - id="path3895" /> - <path - d="m 48.342118,18.501344 0.0076,-0.427668 0.3808,0.0067 -0.0076,0.427668 -0.380799,-0.0067" - inkscape:connector-curvature="0" - id="path3897" /> - <path - d="m 50.84888,18.003867 -0.04578,0.621362 -0.331133,-0.0244 0.04578,-0.621362 -1.293369,-0.09529 0.02009,-0.272698 1.392697,-1.757888 0.368143,0.02712 -0.13605,1.846558 0.385673,0.02841 -0.02038,0.276594 -0.385673,-0.02841 M 50.64504,16.251727 c -0.0031,0.0063 -0.01053,0.01881 -0.02235,0.03752 -0.01173,0.01742 -0.02495,0.03733 -0.03965,0.05975 -0.01471,0.02242 -0.03011,0.04544 -0.04621,0.06906 -0.01471,0.02242 -0.02783,0.04104 -0.03937,0.05585 l -0.779521,0.984446 c -0.0073,0.0099 -0.018,0.02283 -0.03223,0.03875 -0.01293,0.01602 -0.02656,0.03264 -0.04088,0.04987 -0.01423,0.01592 -0.02846,0.03185 -0.04269,0.04777 -0.01423,0.01593 -0.02557,0.02815 -0.03404,0.03666 l 0.970027,0.07147 0.106917,-1.451144" - inkscape:connector-curvature="0" - id="path3899" /> - <path - d="m 77.021288,37.280268 c -0.21155,0.08812 -0.411725,0.134119 -0.600525,0.138007 -0.187603,0.0034 -0.359123,-0.02744 -0.514564,-0.09245 -0.154241,-0.06552 -0.291149,-0.162243 -0.410726,-0.290158 -0.117876,-0.127218 -0.213112,-0.277969 -0.285709,-0.452254 l -0.395782,-0.950163 2.540378,-1.058172 0.34997,0.840182 c 0.08161,0.195922 0.132067,0.386482 0.151381,0.571681 0.02101,0.185899 0.0052,0.359625 -0.04738,0.521179 -0.0514,0.161051 -0.141462,0.307175 -0.270179,0.438372 -0.128222,0.132397 -0.30051,0.243656 -0.516863,0.333779 m -0.144194,-0.346169 c 0.171881,-0.0716 0.308258,-0.15732 0.409132,-0.257168 0.102072,-0.100351 0.173648,-0.211271 0.21473,-0.332759 0.04107,-0.121491 0.05386,-0.251648 0.03835,-0.390471 -0.01431,-0.139325 -0.05226,-0.282909 -0.113836,-0.430751 l -0.203524,-0.488604 -1.98867,0.828363 0.235817,0.566131 c 0.05557,0.133418 0.128528,0.247609 0.218863,0.342571 0.09083,0.09616 0.196195,0.167939 0.316083,0.215329 0.119885,0.04739 0.253296,0.06798 0.400234,0.06179 0.146934,-0.0062 0.30454,-0.04434 0.472818,-0.114432" - inkscape:connector-curvature="0" - id="path3901" /> - <path - d="m 76.885764,38.221869 c -0.105493,0.03348 -0.199569,0.07495 -0.28223,0.124408 -0.08103,0.05031 -0.146718,0.108039 -0.197074,0.1732 -0.04872,0.06601 -0.07985,0.140094 -0.09339,0.222259 -0.0123,0.08177 -0.0027,0.172297 0.02882,0.271585 0.04608,0.145204 0.112078,0.251989 0.197982,0.320352 0.0863,0.0696 0.179046,0.105738 0.278252,0.108406 l 0.0096,0.320723 c -0.06255,-0.0034 -0.128468,-0.01524 -0.197767,-0.03559 -0.06767,-0.01951 -0.134502,-0.05362 -0.20051,-0.102342 -0.06477,-0.04912 -0.127209,-0.116729 -0.187324,-0.202837 -0.05848,-0.08526 -0.108991,-0.194913 -0.15153,-0.328948 -0.09453,-0.297859 -0.07788,-0.553139 0.04997,-0.76584 0.128238,-0.211461 0.367348,-0.37273 0.717332,-0.483807 0.188643,-0.05987 0.355605,-0.0869 0.500887,-0.0811 0.146521,0.0054 0.273735,0.03607 0.381645,0.09198 0.107906,0.05591 0.197186,0.132768 0.267842,0.23056 0.07189,0.0974 0.127534,0.208151 0.166925,0.332259 0.05357,0.168785 0.07097,0.318313 0.05221,0.448583 -0.01837,0.131509 -0.06513,0.246755 -0.140265,0.345739 -0.07351,0.09983 -0.170886,0.184695 -0.292138,0.2546 -0.120861,0.07114 -0.256997,0.130742 -0.408406,0.178798 l -0.04468,0.01418 -0.456124,-1.437167 m 0.603131,1.009371 c 0.206314,-0.08597 0.343694,-0.188998 0.41214,-0.309078 0.06968,-0.120476 0.07794,-0.264486 0.02477,-0.43203 -0.01773,-0.05585 -0.04574,-0.111846 -0.08405,-0.167991 -0.03668,-0.0553 -0.08625,-0.101724 -0.148708,-0.139275 -0.06247,-0.03755 -0.139122,-0.06172 -0.229972,-0.0725 -0.08961,-0.01118 -0.196018,5.4e-5 -0.319218,0.03369 l 0.345047,1.087183" - inkscape:connector-curvature="0" - id="path3903" /> - <path - d="m 77.456336,41.797791 c -0.09624,0.02305 -0.186892,0.02469 -0.271967,0.0049 -0.08351,-0.01882 -0.159712,-0.05747 -0.228616,-0.115944 -0.06764,-0.05878 -0.127824,-0.136745 -0.180556,-0.233902 -0.05116,-0.0962 -0.09267,-0.210774 -0.124523,-0.343729 -0.02852,-0.119029 -0.0448,-0.228935 -0.04887,-0.32972 -0.005,-0.09922 0.0041,-0.189766 0.02731,-0.27165 0.02323,-0.08188 0.06185,-0.155405 0.115865,-0.220563 0.05558,-0.0642 0.128763,-0.119217 0.219538,-0.165065 l 0.131231,0.287896 c -0.103187,0.05551 -0.169818,0.135077 -0.199892,0.238685 -0.02881,0.103304 -0.0238,0.235996 0.01503,0.398077 0.0176,0.07344 0.03874,0.13934 0.06343,0.197694 0.02499,0.05962 0.05483,0.108706 0.08951,0.147262 0.03594,0.03825 0.07706,0.06455 0.12334,0.0789 0.04785,0.01531 0.102169,0.01569 0.16295,0.0011 0.06205,-0.01487 0.109207,-0.04156 0.141485,-0.08009 0.03354,-0.03883 0.05739,-0.08739 0.07155,-0.145677 0.01415,-0.05829 0.02178,-0.127061 0.02287,-0.206319 0.0027,-0.0783 0.0054,-0.164646 0.0082,-0.259047 0.0018,-0.08746 0.0063,-0.174901 0.01348,-0.262311 0.0072,-0.08741 0.0244,-0.169196 0.05168,-0.245355 0.02885,-0.0752 0.07071,-0.14079 0.125578,-0.196781 0.05487,-0.05599 0.131057,-0.09567 0.22856,-0.119025 0.187403,-0.0449 0.345783,-0.01255 0.475139,0.09705 0.130921,0.110561 0.227022,0.293733 0.288302,0.549516 0.0543,0.226659 0.05749,0.416022 0.0096,0.568091 -0.04762,0.153333 -0.155187,0.268811 -0.3227,0.346434 l -0.111703,-0.298599 c 0.05045,-0.02414 0.08976,-0.05565 0.11794,-0.09453 0.02944,-0.03919 0.04976,-0.08289 0.06098,-0.131095 0.01248,-0.04851 0.01663,-0.101057 0.01245,-0.157628 -0.0026,-0.05561 -0.0109,-0.112539 -0.02485,-0.170786 -0.03701,-0.154483 -0.08774,-0.262834 -0.152183,-0.325052 -0.06445,-0.06222 -0.144155,-0.08195 -0.239122,-0.0592 -0.05572,0.01335 -0.09875,0.03771 -0.129099,0.0731 -0.02878,0.03635 -0.05007,0.08162 -0.06387,0.135805 -0.01223,0.05515 -0.01965,0.119185 -0.02226,0.192113 -0.0013,0.07262 -0.0023,0.152516 -0.0028,0.239677 -8.86e-4,0.05779 -0.0027,0.117141 -0.0055,0.178066 -0.0015,0.06062 -0.0073,0.120253 -0.01739,0.178897 -0.0085,0.0596 -0.02231,0.116474 -0.04147,0.170607 -0.0176,0.05509 -0.04304,0.105374 -0.07633,0.150841 -0.03329,0.04546 -0.07537,0.085 -0.12623,0.11861 -0.05056,0.03487 -0.11256,0.06111 -0.186001,0.0787" - inkscape:connector-curvature="0" - id="path3905" /> - <path - d="m 79.476583,41.866986 0.330152,-0.06208 0.06496,0.345508 -0.330152,0.06208 -0.06496,-0.345509 m -2.518371,0.473514 2.076888,-0.390504 0.06496,0.345508 -2.076888,0.390504 -0.06496,-0.345508" - inkscape:connector-curvature="0" - id="path3907" /> - <path - d="m 76.409909,44.121315 c -0.01605,-0.120027 -0.01802,-0.228141 -0.0059,-0.324341 0.0121,-0.0962 0.03698,-0.18032 0.07464,-0.252352 0.03637,-0.07186 0.08342,-0.132668 0.141132,-0.182422 0.05772,-0.04976 0.124086,-0.08884 0.199106,-0.117265 l 0.09718,0.343668 c -0.09369,0.03617 -0.161082,0.09707 -0.20218,0.182702 -0.04221,0.08709 -0.05469,0.195167 -0.03744,0.324228 0.01053,0.07872 0.03107,0.148887 0.06164,0.210484 0.03057,0.0616 0.07339,0.1117 0.128476,0.150315 0.05379,0.03878 0.121045,0.06526 0.20176,0.07943 0.08071,0.01417 0.176567,0.01383 0.287559,-10e-4 l 0.336846,-0.04504 -5.18e-4,-0.0039 c -0.05637,-0.01874 -0.111659,-0.04419 -0.165878,-0.07635 -0.05276,-0.03104 -0.101558,-0.07181 -0.146406,-0.122301 -0.04467,-0.0492 -0.08346,-0.108386 -0.116353,-0.177552 -0.0316,-0.06934 -0.05362,-0.150473 -0.06604,-0.243395 -0.01777,-0.132932 -0.01111,-0.250084 0.02001,-0.351454 0.03257,-0.100254 0.08933,-0.186663 0.170272,-0.259228 0.08111,-0.07127 0.185592,-0.129908 0.313437,-0.175902 0.129307,-0.04488 0.281077,-0.07896 0.455308,-0.102258 0.167777,-0.02243 0.320589,-0.02907 0.458436,-0.01991 0.138017,0.01045 0.257629,0.04043 0.358837,0.08996 0.102667,0.05064 0.185166,0.122372 0.247498,0.215191 0.06379,0.09394 0.105265,0.212533 0.124421,0.35579 0.01984,0.148418 0.0028,0.281405 -0.05109,0.398962 -0.05245,0.118673 -0.137292,0.216063 -0.254541,0.29217 l 5.18e-4,0.0039 c 0.03226,-0.0043 0.06849,-0.0085 0.108669,-0.01256 0.04035,-0.0028 0.07804,-0.0058 0.113059,-0.0092 0.03648,-0.0023 0.0678,-0.0038 0.09396,-0.0047 0.02616,-8.71e-4 0.04134,-2.75e-4 0.04556,0.0018 l 0.04426,0.331039 c -0.01179,2.6e-4 -0.03197,0.0016 -0.06053,0.0042 -0.02728,0.0023 -0.06036,0.0054 -0.09925,0.0093 -0.03889,0.0039 -0.0823,0.0084 -0.130223,0.01347 -0.04646,0.0062 -0.09486,0.01268 -0.145192,0.01941 l -1.600987,0.214061 c -0.292966,0.03917 -0.522676,-0.0037 -0.689131,-0.128556 -0.167573,-0.123414 -0.271204,-0.333538 -0.310893,-0.630374 m 1.940999,0.280395 c 0.145836,-0.0195 0.268222,-0.05229 0.367157,-0.09835 0.100395,-0.04495 0.180053,-0.09764 0.238973,-0.158065 0.05892,-0.06042 0.09938,-0.126264 0.121379,-0.197515 0.02217,-0.06996 0.0286,-0.13979 0.01928,-0.209482 -0.01191,-0.08905 -0.03766,-0.163771 -0.07726,-0.224159 -0.03943,-0.0591 -0.09555,-0.105456 -0.168353,-0.139072 -0.07134,-0.0325 -0.160665,-0.05208 -0.267963,-0.05876 -0.105837,-0.0056 -0.231029,0.0013 -0.375575,0.02066 -0.151,0.02019 -0.27683,0.04687 -0.377489,0.08003 -0.0992,0.03428 -0.177653,0.0763 -0.23537,0.126055 -0.05772,0.04975 -0.0959,0.108062 -0.114547,0.174926 -0.01865,0.06686 -0.0221,0.144174 -0.01037,0.231936 0.0093,0.06969 0.03322,0.135463 0.0717,0.197316 0.03866,0.06314 0.09379,0.116855 0.165392,0.161143 0.0716,0.04428 0.160542,0.07574 0.266814,0.09438 0.106271,0.01863 0.23168,0.01828 0.376228,-0.001" - inkscape:connector-curvature="0" - id="path3909" /> - <path - d="m 77.478883,46.76703 1.337606,-0.07741 c 0.102691,-0.0059 0.188034,-0.01871 0.256028,-0.03829 0.06806,-0.01829 0.12192,-0.04553 0.161563,-0.08174 0.04094,-0.03628 0.06824,-0.08286 0.08191,-0.139731 0.01504,-0.05565 0.02019,-0.124422 0.01545,-0.206315 -0.0048,-0.0832 -0.02413,-0.157725 -0.05794,-0.223589 -0.03244,-0.06464 -0.07799,-0.119392 -0.136671,-0.164253 -0.05731,-0.04364 -0.127658,-0.07609 -0.211055,-0.09734 -0.0821,-0.02134 -0.175796,-0.02895 -0.281087,-0.02286 l -1.222564,0.07075 -0.02031,-0.350976 1.659333,-0.09603 c 0.0442,-0.0026 0.0903,-0.0059 0.138327,-0.01 0.0494,-0.0029 0.0955,-0.0062 0.138327,-0.01 0.04412,-0.0039 0.08174,-0.0073 0.112867,-0.01044 0.03112,-0.0031 0.05119,-0.0056 0.06022,-0.0074 l 0.01918,0.331477 c -0.0064,0.0017 -0.0239,0.004 -0.05242,0.0069 -0.02852,0.003 -0.0616,0.0062 -0.09922,0.0097 -0.03625,0.0047 -0.07383,0.0088 -0.112753,0.01239 -0.03762,0.0035 -0.06943,0.006 -0.09543,0.0075 l 3.39e-4,0.0058 c 0.06305,0.03026 0.12044,0.06411 0.17217,0.101551 0.05173,0.03744 0.09603,0.08183 0.132916,0.133168 0.03818,0.05126 0.06836,0.110818 0.09055,0.178661 0.02348,0.06777 0.03786,0.147145 0.04313,0.23814 0.0068,0.116991 -0.0017,0.218561 -0.02541,0.304711 -0.02371,0.08615 -0.06388,0.158251 -0.120517,0.216309 -0.05664,0.05806 -0.131688,0.102178 -0.22515,0.13237 -0.09209,0.03141 -0.20313,0.05088 -0.333119,0.05841 l -1.405851,0.08136 -0.02043,-0.352926" - inkscape:connector-curvature="0" - id="path3911" /> - <path - d="m 78.487682,47.938431 c -0.110662,-0.0018 -0.213045,0.0076 -0.307147,0.02813 -0.09282,0.02191 -0.17347,0.05574 -0.241943,0.101487 -0.06719,0.04707 -0.12028,0.107402 -0.159261,0.180988 -0.03768,0.07361 -0.05738,0.162485 -0.0591,0.266639 -0.0025,0.152321 0.02607,0.274555 0.08575,0.3667 0.05966,0.09345 0.136094,0.157216 0.229294,0.191313 l -0.09298,0.3071 c -0.05822,-0.0231 -0.116941,-0.05533 -0.176161,-0.09667 -0.05794,-0.04003 -0.110448,-0.09364 -0.157522,-0.160827 -0.04577,-0.06717 -0.08345,-0.15114 -0.113043,-0.251901 -0.02831,-0.09944 -0.0413,-0.219463 -0.03898,-0.360068 0.0052,-0.312458 0.102183,-0.549169 0.291065,-0.710134 0.18886,-0.159663 0.466859,-0.236462 0.833997,-0.230398 0.197888,0.0033 0.364774,0.03077 0.500659,0.0825 0.137183,0.05175 0.248028,0.1213 0.332537,0.208645 0.0845,0.08734 0.144689,0.188613 0.180554,0.303805 0.03716,0.115212 0.05467,0.237914 0.05252,0.368105 -0.0029,0.177058 -0.03401,0.324351 -0.09325,0.44188 -0.05926,0.118827 -0.140261,0.213205 -0.242991,0.283135 -0.101452,0.07125 -0.220775,0.120717 -0.357968,0.148405 -0.137218,0.02898 -0.285242,0.04217 -0.444073,0.03955 l -0.04687,-7.74e-4 0.0249,-1.507607 m 0.250592,1.148827 c 0.222947,-0.01585 0.38597,-0.06981 0.489069,-0.161867 0.104397,-0.09204 0.158048,-0.225938 0.160953,-0.401694 9.65e-4,-0.05859 -0.0078,-0.120588 -0.02623,-0.186005 -0.01717,-0.06409 -0.04939,-0.12388 -0.09666,-0.179355 -0.04727,-0.05548 -0.112252,-0.10278 -0.194949,-0.141911 -0.0814,-0.03911 -0.185846,-0.06232 -0.313346,-0.06964 l -0.01884,1.14047" - inkscape:connector-curvature="0" - id="path3913" /> - <path - d="m 77.670156,51.238318 c -0.128928,-0.07716 -0.217647,-0.172223 -0.266159,-0.285179 -0.04863,-0.111663 -0.06572,-0.246591 -0.05125,-0.404785 0.0243,-0.265819 0.131669,-0.453438 0.322108,-0.562857 0.19032,-0.108124 0.467015,-0.14559 0.830085,-0.1124 0.733918,0.06709 1.077288,0.358674 1.030113,0.874752 -0.01458,0.15949 -0.05591,0.289732 -0.123975,0.390727 -0.06807,0.100992 -0.168607,0.178097 -0.301607,0.231316 l -3.55e-4,0.0039 c 0.01297,0.0012 0.03312,0.0024 0.06047,0.0036 0.02853,0.0026 0.05776,0.0046 0.0877,0.0061 0.03112,0.0028 0.06029,0.0055 0.08753,0.008 0.02723,0.0025 0.04668,0.0043 0.05835,0.0053 l 0.814961,0.0745 -0.032,0.350103 -2.452664,-0.224209 c -0.05057,-0.0046 -0.0992,-0.0091 -0.145876,-0.01333 -0.0468,-0.003 -0.08971,-0.0056 -0.128726,-0.0078 -0.03902,-0.0023 -0.07285,-0.004 -0.101497,-0.0054 -0.02735,-0.0012 -0.04692,-0.0017 -0.05871,-0.0014 l 0.03058,-0.334542 c 0.0132,-0.0014 0.03159,-0.0023 0.05517,-0.0028 0.02475,9.54e-4 0.05281,0.0016 0.08417,0.0018 0.03124,0.0015 0.06377,0.0032 0.09761,0.005 0.03513,0.0019 0.06955,0.0044 0.103264,0.0075 l 7.11e-4,-0.0078 m 0.812846,-0.996547 c -0.145229,-0.01328 -0.270539,-0.01558 -0.375932,-0.0069 -0.105395,0.0087 -0.193642,0.03002 -0.264743,0.06406 -0.06981,0.03415 -0.123113,0.08093 -0.159923,0.140321 -0.03681,0.0594 -0.0593,0.133828 -0.06748,0.2233 -0.0084,0.09206 -9.13e-4,0.174468 0.02251,0.247216 0.0233,0.07404 0.06589,0.137427 0.127751,0.190154 0.06304,0.05414 0.147248,0.09845 0.25262,0.132924 0.105371,0.03447 0.235858,0.05882 0.39146,0.07305 0.149117,0.01363 0.27602,0.01281 0.380711,-0.0025 0.105985,-0.01516 0.192939,-0.04382 0.260864,-0.08599 0.06792,-0.04217 0.118698,-0.09702 0.152331,-0.16455 0.03481,-0.06612 0.05636,-0.144559 0.06466,-0.235325 0.0078,-0.08558 -3.34e-4,-0.160856 -0.02447,-0.225821 -0.02414,-0.06497 -0.06737,-0.121221 -0.129707,-0.168759 -0.06234,-0.04754 -0.145723,-0.08654 -0.250152,-0.117008 -0.103254,-0.02905 -0.230088,-0.05045 -0.380501,-0.0642" - inkscape:connector-curvature="0" - id="path3915" /> - <path - d="m 78.791939,53.959052 -1.825621,-0.342233 0.06478,-0.345543 1.825621,0.342232 0.0547,-0.291792 0.251479,0.04714 -0.0547,0.291792 0.234202,0.0439 c 0.07551,0.01415 0.143934,0.03427 0.205287,0.06034 0.06263,0.02631 0.11414,0.06246 0.154538,0.108456 0.04143,0.04751 0.06947,0.106418 0.08411,0.176727 0.01592,0.07055 0.01416,0.157651 -0.0053,0.261315 -0.0077,0.04095 -0.01752,0.08282 -0.02951,0.12562 -0.01224,0.04407 -0.02532,0.08203 -0.03923,0.113862 l -0.262997,-0.0493 c 0.0079,-0.02104 0.01591,-0.04603 0.02399,-0.07499 0.0091,-0.02744 0.01583,-0.05267 0.02015,-0.07571 0.0096,-0.05119 0.01047,-0.09475 0.0026,-0.130657 -0.0081,-0.03464 -0.02376,-0.06407 -0.04704,-0.0883 -0.02224,-0.02272 -0.05183,-0.04151 -0.08879,-0.05639 -0.0372,-0.0136 -0.08075,-0.02507 -0.130659,-0.03443 l -0.18621,-0.03491 -0.07593,0.405054 -0.251478,-0.04714 0.07593,-0.405054" - inkscape:connector-curvature="0" - id="path3917" /> - <path - d="m 77.441728,56.26852 c -0.358999,-0.08869 -0.606835,-0.233741 -0.743508,-0.435155 -0.136672,-0.201419 -0.167847,-0.452553 -0.09353,-0.753403 0.03529,-0.142842 0.08832,-0.265206 0.159089,-0.367091 0.07077,-0.101886 0.160081,-0.181086 0.267928,-0.237599 0.107534,-0.05525 0.232818,-0.08734 0.375852,-0.09626 0.143985,-0.0073 0.30636,0.0113 0.487124,0.05596 0.707885,0.174875 0.9861,0.568852 0.834647,1.181933 -0.03935,0.159273 -0.09535,0.290961 -0.168011,0.395064 -0.07297,0.105364 -0.162759,0.183776 -0.269355,0.235235 -0.106911,0.05272 -0.231103,0.08038 -0.372574,0.08299 -0.141473,0.0026 -0.300695,-0.01795 -0.477666,-0.06167 m 0.08853,-0.358367 c 0.159274,0.03934 0.293861,0.05918 0.403762,0.05951 0.109587,0.0016 0.201284,-0.01399 0.275092,-0.04672 0.07349,-0.03147 0.13068,-0.07837 0.171562,-0.140695 0.04214,-0.06202 0.07383,-0.136003 0.09507,-0.221959 0.02155,-0.08722 0.02779,-0.169507 0.01873,-0.246852 -0.0081,-0.07577 -0.03767,-0.146113 -0.0887,-0.211025 -0.04976,-0.0646 -0.123192,-0.122978 -0.220288,-0.17513 -0.09741,-0.05089 -0.223855,-0.09554 -0.379336,-0.133948 -0.159276,-0.03935 -0.294807,-0.05808 -0.406595,-0.05618 -0.110526,0.0022 -0.202691,0.01967 -0.276498,0.05241 -0.07412,0.034 -0.131938,0.08074 -0.173458,0.140226 -0.04183,0.06075 -0.07243,0.130312 -0.09179,0.208686 -0.02155,0.08722 -0.02906,0.169192 -0.02253,0.245915 0.0078,0.07703 0.03657,0.14785 0.08633,0.212453 0.04976,0.0646 0.123822,0.123132 0.222183,0.175598 0.09836,0.05246 0.227178,0.09837 0.386453,0.137718" - inkscape:connector-curvature="0" - id="path3919" /> - <path - d="m 76.277382,56.441011 1.547231,0.483758 c 0.04225,0.01321 0.08532,0.02599 0.129208,0.03835 0.04474,0.01399 0.08781,0.02677 0.129208,0.03835 0.0414,0.01158 0.08031,0.02238 0.116742,0.03241 0.03643,0.01002 0.06913,0.01888 0.0981,0.02658 l -0.09908,0.316902 c -0.02897,-0.0077 -0.0623,-0.01675 -0.09997,-0.02716 -0.03682,-0.0088 -0.07468,-0.01858 -0.113597,-0.02938 -0.0393,-0.0096 -0.07697,-0.01998 -0.113013,-0.03124 -0.03519,-0.0096 -0.06521,-0.01834 -0.09006,-0.02611 l -0.0023,0.0075 c 0.06967,0.0477 0.128383,0.09334 0.176149,0.13693 0.04862,0.04521 0.08531,0.09147 0.110085,0.138783 0.02601,0.0477 0.03995,0.09912 0.04181,0.154271 0.0031,0.05554 -0.0064,0.118724 -0.02857,0.189563 -0.0085,0.02734 -0.01919,0.05266 -0.03194,0.07596 -0.0115,0.02369 -0.02125,0.04178 -0.02925,0.05429 l -0.307582,-0.09617 c 0.01321,-0.02043 0.02572,-0.04517 0.03753,-0.07421 0.01305,-0.02866 0.02541,-0.06163 0.03707,-0.09891 0.02409,-0.07705 0.02728,-0.148358 0.0096,-0.213917 -0.01809,-0.06432 -0.05276,-0.123591 -0.104021,-0.177815 -0.0504,-0.0526 -0.115956,-0.100376 -0.196654,-0.143341 -0.07946,-0.04258 -0.169517,-0.0796 -0.270179,-0.111077 l -1.051371,-0.328722 0.104911,-0.335544" - inkscape:connector-curvature="0" - id="path3921" /> - <path - d="m 77.57706,60.103407 -0.941955,-0.400182 -0.600272,1.412932 -0.284025,-0.120665 0.600273,-1.412932 -1.026443,-0.436076 0.145868,-0.343346 2.532851,1.076061 -0.764469,1.799421 -0.28043,-0.119138 0.618602,-1.456075" - inkscape:connector-curvature="0" - id="path3923" /> - <path - d="m 73.672921,62.572089 0.858474,0.09708 0.58473,-1.109222 -0.564288,-0.655144 0.180337,-0.342096 1.910706,2.27677 -0.197642,0.374923 -2.949922,-0.305396 0.177605,-0.336913 m 2.136088,0.245098 c 0.05579,0.0059 0.111848,0.01259 0.168183,0.02021 0.05572,0.0088 0.105932,0.01684 0.150624,0.02421 0.04584,0.008 0.08386,0.01403 0.114059,0.01818 0.03074,0.0059 0.0493,0.0098 0.05566,0.01168 -0.0045,-0.0053 -0.01794,-0.01903 -0.04018,-0.04105 -0.02224,-0.02203 -0.0493,-0.05028 -0.08118,-0.08474 -0.03248,-0.03332 -0.06779,-0.07106 -0.105902,-0.113231 -0.03873,-0.04102 -0.07657,-0.08232 -0.113535,-0.123879 l -0.63601,-0.732695 -0.479988,0.91053 0.968262,0.110794" - inkscape:connector-curvature="0" - id="path3925" /> - <path - d="m 73.310171,63.196101 2.308254,1.498402 -1.136829,1.751259 -0.255563,-0.165899 0.93371,-1.438358 -0.740476,-0.48068 -0.869903,1.340065 -0.252286,-0.163771 0.869903,-1.340065 -0.804367,-0.522155 -0.977311,1.505526 -0.255563,-0.165899 1.180431,-1.818425" - inkscape:connector-curvature="0" - id="path3927" /> - <path - d="m 70.724956,66.823317 2.751648,0.30052 c -0.05164,-0.03757 -0.103165,-0.07422 -0.154586,-0.109958 -0.04347,-0.0311 -0.08909,-0.06473 -0.13685,-0.100896 -0.04755,-0.03434 -0.08919,-0.06565 -0.124918,-0.09394 l -1.433179,-1.13492 0.206129,-0.260299 2.157423,1.708441 -0.26918,0.339921 -2.773597,-0.297971 c 0.05244,0.03655 0.104887,0.07309 0.15733,0.10964 0.04449,0.03191 0.09255,0.06664 0.144187,0.104215 0.05266,0.03837 0.101951,0.07575 0.147886,0.112127 l 1.414804,1.120369 -0.208553,0.263361 -2.157424,-1.708441 0.27888,-0.35217" - inkscape:connector-curvature="0" - id="path3929" /> - <path - d="m 68.636859,69.108388 1.509272,-1.588823 0.202497,0.192358 0.417682,2.708113 1.055952,-1.11161 0.220906,0.209845 -1.347853,1.418896 -0.196833,-0.186978 -0.423345,-2.713493 -1.217372,1.281538 -0.220906,-0.209846" - inkscape:connector-curvature="0" - id="path3931" /> - <path - d="m 66.845397,70.716316 0.76893,0.39389 0.938663,-0.831379 -0.296674,-0.81217 0.289494,-0.256407 0.983929,2.804704 -0.317274,0.281012 -2.652176,-1.327128 0.285108,-0.252522 m 1.912028,0.983409 c 0.05013,0.02517 0.100201,0.05127 0.150218,0.07828 0.04904,0.02788 0.09317,0.05315 0.132381,0.07582 0.04008,0.02364 0.07351,0.04273 0.100299,0.05727 0.02668,0.01637 0.04266,0.02657 0.04796,0.03058 -0.0024,-0.0066 -0.01007,-0.02413 -0.0231,-0.05259 -0.01303,-0.02846 -0.02838,-0.06445 -0.04603,-0.107944 -0.01863,-0.04264 -0.03833,-0.09041 -0.05911,-0.143327 -0.02175,-0.05205 -0.04258,-0.104047 -0.06249,-0.155984 l -0.336401,-0.910045 -0.770522,0.682456 0.866806,0.445481" - inkscape:connector-curvature="0" - id="path3933" /> - </g> - </g> - <g - id="layer4" - style="display:inline"> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 1 1 -27.274118,0 13.637059,13.637059 0 1 1 27.274118,0 z" - transform="matrix(1.0266143,0,0,1.0266143,-0.999999,0.7783622)" - id="path3757" - style="fill:#000000;fill-opacity:1;stroke:none" /> - <path - d="M 47.5,33 C 39.491871,33 33,39.491871 33,47.5 33,55.508129 39.491871,62 47.5,62 c 0.08504,0 0.165309,0.0015 0.25,0 C 40.133878,61.866031 34,55.648102 34,48 c 0,-7.731986 6.268014,-14 14,-14 7.648102,0 13.866031,6.133878 14,13.75 0.0015,-0.08469 0,-0.164961 0,-0.25 C 62,39.491871 55.508129,33 47.5,33 z" - inkscape:connector-curvature="0" - id="path3760" - style="opacity:0.6;fill:#000000;fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 1 1 -27.274118,0 13.637059,13.637059 0 1 1 27.274118,0 z" - transform="matrix(1.0266143,0,0,1.0266143,-0.999999,0.778362)" - id="path2907" - style="fill:url(#radialGradient3687);fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 1 1 -27.274118,0 13.637059,13.637059 0 1 1 27.274118,0 z" - transform="matrix(0,0.97161711,-0.97161711,0,92.69191,1.6250011)" - id="path3705" - style="fill:none;stroke:url(#linearGradient3733);stroke-width:0.514606;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;filter:url(#filter3774)" /> - <path - d="m 41.34375,47.375 c 0.07082,-0.767299 0.285421,-1.486362 0.59375,-2.15625 L 37.34375,42.8125 c -0.603997,1.243084 -1.018186,2.621977 -1.15625,4.0625 l 5.15625,0.5 z m 1.375,-3.4375 c 1.071033,-1.389071 2.667628,-2.34897 4.5,-2.5625 L 46.625,36.21875 c -3.598468,0.417087 -6.654118,2.435762 -8.53125,5.3125 l 4.625,2.40625 z" - inkscape:connector-curvature="0" - id="path3735" - style="opacity:0.6;fill:#ffffff;fill-opacity:1;stroke:none" /> - <path - d="m 50.708204,52 a 9.7082043,6 0 1 1 -19.416408,0 9.7082043,6 0 1 1 19.416408,0 z" - transform="matrix(0.58268801,-0.58268801,0.58268801,0.58268801,-0.74355667,46.935862)" - id="path3759" - style="opacity:0.4314516;fill:url(#linearGradient3767);fill-opacity:1;stroke:none" /> - <path - d="m 50.708204,52 a 9.7082043,6 0 1 1 -19.416408,0 9.7082043,6 0 1 1 19.416408,0 z" - transform="matrix(0.15076894,-0.15076894,0.15076894,0.15076894,37.387737,49.649792)" - id="path3769" - style="opacity:0.3;fill:url(#linearGradient3771);fill-opacity:1;stroke:none" /> - <path - d="m 50.708204,52 a 9.7082043,6 0 1 1 -19.416408,0 9.7082043,6 0 1 1 19.416408,0 z" - transform="matrix(-0.10757707,0.10757707,-0.10757707,-0.10757707,55.999137,47.076819)" - id="path3773" - style="opacity:0.3;fill:url(#linearGradient3775);fill-opacity:1;stroke:none" /> - <path - d="m 50.708204,52 a 9.7082043,6 0 1 1 -19.416408,0 9.7082043,6 0 1 1 19.416408,0 z" - transform="matrix(-0.19358861,0.19358861,-0.19358861,-0.19358861,59.194255,43.21899)" - id="path3777" - style="opacity:0.4314516;fill:url(#linearGradient3779);fill-opacity:1;stroke:none" /> - <path - d="m 50.708204,52 a 9.7082043,6 0 1 1 -19.416408,0 9.7082043,6 0 1 1 19.416408,0 z" - transform="matrix(0.08968638,-0.08968638,0.08968638,0.08968638,40.497472,47.750757)" - id="path3789" - style="opacity:0.6;fill:url(#linearGradient3791);fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 1 1 -27.274118,0 13.637059,13.637059 0 1 1 27.274118,0 z" - transform="matrix(1.0266143,0,0,1.0266143,69.000001,0.7783622)" - id="path3779" - style="opacity:0.6;fill:#ff00ff;fill-opacity:1;stroke:none;display:inline" /> - <path - d="m 48,32 c -8.836556,0 -16,7.163444 -16,16 0,8.836556 7.163444,16 16,16 8.836556,0 16,-7.163444 16,-16 0,-8.836556 -7.163444,-16 -16,-16 z m 0,1.5 c 8.008129,0 14.5,6.491871 14.5,14.5 0,8.008129 -6.491871,14.5 -14.5,14.5 -8.008129,0 -14.5,-6.491871 -14.5,-14.5 0,-8.008129 6.491871,-14.5 14.5,-14.5 z" - inkscape:connector-curvature="0" - id="path3806" - style="opacity:0.7;fill:url(#linearGradient3817);fill-opacity:1;stroke:none;display:inline" /> - </g> - <g - id="layer6" - style="display:inline"> - <g - clip-path="url(#clipPath3798)" - id="g3786" - style="opacity:0.4;display:inline;filter:url(#filter3833)"> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 0 1 -3.994202,9.642857 l -9.642857,-9.642857 z" - transform="matrix(1.0266143,0,0,1.0266143,-0.9999987,0.7783622)" - id="path3756" - style="fill:#d40000;fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 0 1 -3.994202,9.642857 l -9.642857,-9.642857 z" - transform="matrix(0.72592593,0.72592593,-0.72592593,0.72592593,46.742509,-20.038972)" - id="path3772" - style="fill:#d42aff;fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 0 1 -3.994202,9.642857 l -9.642857,-9.642857 z" - transform="matrix(0,1.0266143,-1.0266143,0,95.221638,-0.99999877)" - id="path3774" - style="fill:#6600ff;fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 0 1 -3.994202,9.642857 l -9.642857,-9.642857 z" - transform="matrix(-0.72592593,0.72592593,-0.72592593,-0.72592593,116.03897,46.742509)" - id="path3776" - style="fill:#2a7fff;fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 0 1 -3.994202,9.642857 l -9.642857,-9.642857 z" - transform="matrix(-1.0266143,0,0,-1.0266143,96.999997,95.221637)" - id="path3778" - style="fill:#5fd3bc;fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 0 1 -3.994202,9.642857 l -9.642857,-9.642857 z" - transform="matrix(-0.72592593,-0.72592593,0.72592593,-0.72592593,49.25749,116.03897)" - id="path3780" - style="fill:#55d400;fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 0 1 -3.994202,9.642857 l -9.642857,-9.642857 z" - transform="matrix(0,-1.0266143,1.0266143,0,0.77836254,96.999997)" - id="path3782" - style="fill:#ffcc00;fill-opacity:1;stroke:none" /> - <path - d="m 61.366765,45.997448 a 13.637059,13.637059 0 0 1 -3.994202,9.642857 l -9.642857,-9.642857 z" - transform="matrix(0.72592593,-0.72592593,0.72592593,0.72592593,-20.03897,49.25749)" - id="path3784" - style="fill:#ff6600;fill-opacity:1;stroke:none" /> - </g> - <g - transform="translate(0,-50)" - id="g3957" - style="opacity:0.8;filter:url(#filter3987)"> - <path - d="m 61.53125,94.375 -0.96875,0.25 C 60.849921,95.699342 61,96.835019 61,98 c 0,4.80784 -2.619633,9.00086 -6.5,11.25 l 0.5,0.875 c 5.487553,-3.16824 8.171252,-9.62943 6.53125,-15.75 z" - inkscape:connector-curvature="0" - id="path3726" - style="opacity:0.8;fill:url(#linearGradient3967);fill-opacity:1;stroke:none" /> - <path - d="m 59.25,104.5 c -2.249137,3.88037 -6.442164,6.5 -11.25,6.5 -1.164981,0 -2.300658,-0.15008 -3.375,-0.4375 l -0.25,0.96875 c 6.120569,1.64 12.58176,-1.0437 15.75,-6.53125 l -0.875,-0.5 z" - inkscape:connector-curvature="0" - id="path3728" - style="opacity:0.8;fill:url(#linearGradient3969);fill-opacity:1;stroke:none" /> - <path - d="m 36.75,104.5 -0.875,0.5 c 3.16824,5.48755 9.629431,8.17125 15.75,6.53125 l -0.25,-0.96875 C 50.300658,110.84992 49.164981,111 48,111 c -4.807836,0 -9.000863,-2.61963 -11.25,-6.5 z" - inkscape:connector-curvature="0" - id="path3730" - style="fill:url(#linearGradient3971);fill-opacity:1;stroke:none" /> - <path - d="m 34.46875,94.375 c -1.640002,6.12057 1.043697,12.58176 6.53125,15.75 l 0.5,-0.875 C 37.619633,107.00086 35,102.80784 35,98 c 0,-1.164981 0.150079,-2.300658 0.4375,-3.375 l -0.96875,-0.25 z" - inkscape:connector-curvature="0" - id="path3732" - style="opacity:0.9;fill:url(#linearGradient3973);fill-opacity:1;stroke:none" /> - <path - d="m 41,85.875 c -5.487553,3.16824 -8.171252,9.629431 -6.53125,15.75 l 0.96875,-0.25 C 35.150079,100.30066 35,99.164981 35,98 35,93.192164 37.619633,88.999137 41.5,86.75 L 41,85.875 z" - inkscape:connector-curvature="0" - id="path3734" - style="fill:url(#linearGradient3975);fill-opacity:1;stroke:none" /> - <path - d="M 48.15625,84 C 43.183834,83.93941 38.449195,86.541364 35.875,91 l 0.875,0.5 C 38.999137,87.619633 43.192164,85 48,85 c 1.164981,0 2.300658,0.150079 3.375,0.4375 l 0.25,-0.96875 C 50.477393,84.16125 49.303731,84.013982 48.15625,84 z" - inkscape:connector-curvature="0" - id="path3736" - style="fill:url(#linearGradient3977);fill-opacity:1;stroke:none" /> - <path - d="m 47.84375,84 c -1.147481,0.01398 -2.321143,0.16125 -3.46875,0.46875 l 0.25,0.96875 C 45.699342,85.150079 46.835019,85 48,85 c 4.807836,0 9.000863,2.619633 11.25,6.5 L 60.125,91 C 57.550805,86.541363 52.816166,83.939411 47.84375,84 z" - inkscape:connector-curvature="0" - id="path3738" - style="fill:url(#linearGradient3979);fill-opacity:1;stroke:none" /> - <path - d="m 55,85.875 -0.5,0.875 c 3.880367,2.249137 6.5,6.442164 6.5,11.25 0,1.164981 -0.150079,2.30066 -0.4375,3.375 l 0.96875,0.25 C 63.171252,95.504431 60.487553,89.04324 55,85.875 z" - inkscape:connector-curvature="0" - id="path3740" - style="opacity:0.9;fill:url(#linearGradient3981);fill-opacity:1;stroke:none" /> - </g> - <path - d="m 50.708204,52 a 9.7082043,6 0 1 1 -19.416408,0 9.7082043,6 0 1 1 19.416408,0 z" - transform="matrix(0.23287504,-0.23287504,0.23287504,0.23287504,33.266783,52.13527)" - id="path3793" - style="fill:url(#radialGradient3803);fill-opacity:1;stroke:none;display:inline" /> - </g> -</svg> diff --git a/rapid/glade3/photo66.png b/rapid/glade3/photo66.png Binary files differdeleted file mode 100644 index 1bef29f..0000000 --- a/rapid/glade3/photo66.png +++ /dev/null diff --git a/rapid/glade3/photo_icon.png b/rapid/glade3/photo_icon.png Binary files differdeleted file mode 100644 index 52e22bc..0000000 --- a/rapid/glade3/photo_icon.png +++ /dev/null diff --git a/rapid/glade3/prefs.ui b/rapid/glade3/prefs.ui deleted file mode 100644 index d4e6229..0000000 --- a/rapid/glade3/prefs.ui +++ /dev/null @@ -1,3782 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <requires lib="gtk+" version="2.16"/> - <!-- interface-naming-policy toplevel-contextual --> - <object class="GtkAdjustment" id="hour_adjustment"> - <property name="upper">23</property> - <property name="step_increment">1</property> - <property name="page_increment">10</property> - </object> - <object class="GtkAdjustment" id="minute_adjustment"> - <property name="upper">59</property> - <property name="step_increment">1</property> - <property name="page_increment">10</property> - </object> - <object class="GtkDialog" id="preferencesdialog"> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="border_width">5</property> - <property name="title" translatable="yes">Preferences: Rapid Photo Downloader</property> - <property name="modal">True</property> - <property name="window_position">center-on-parent</property> - <property name="default_height">500</property> - <property name="icon">rapid-photo-downloader.svg</property> - <property name="type_hint">dialog</property> - <signal name="destroy" handler="on_preferencesdialog_destroy" swapped="no"/> - <signal name="response" handler="on_preferencesdialog_response" swapped="no"/> - <child internal-child="vbox"> - <object class="GtkVBox" id="dialog-vbox2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">2</property> - <child internal-child="action_area"> - <object class="GtkHButtonBox" id="dialog-action_area2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="layout_style">end</property> - <child> - <object class="GtkButton" id="help_button1"> - <property name="label">gtk-help</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - <property name="secondary">True</property> - </packing> - </child> - <child> - <object class="GtkButton" id="close_button"> - <property name="label">gtk-close</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">2</property> - <child> - <object class="GtkScrolledWindow" id="scrolled_window"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="resize_mode">queue</property> - <property name="shadow_type">in</property> - <child> - <object class="GtkTreeView" id="treeview"> - <property name="width_request">100</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="headers_visible">False</property> - <signal name="cursor-changed" handler="on_treeview_cursor_changed" swapped="no"/> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">5</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkNotebook" id="notebook"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="show_border">False</property> - <child> - <object class="GtkVBox" id="folder_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkVBox" id="vbox6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-directory</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Photo Download Folders</span></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label16"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="download_folder_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">7</property> - <property name="n_columns">3</property> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <object class="GtkLabel" id="example_photo_download_path_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><i>Example: /home/user/Pictures</i></property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Download Subfolders</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="right_attach">3</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="lblPhotos1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Download folder:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options"></property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="ypad">12</property> - <property name="label" translatable="yes">Choose the download folder. Subfolders for the downloaded photos will be automatically created in this folder using the structure specified below.</property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Download Folder</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="right_attach">3</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="photo_subfolder_warning_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="wrap">True</property> - <property name="wrap_mode">word-char</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkVBox" id="subfolder_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="y_padding">12</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label23"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="menu_label">Download Folders</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="download_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Photo Folders</property> - </object> - <packing> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="rename_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkVBox" id="vbox7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox10"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-convert</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Photo Rename</span> </property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox12"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label24"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="rename_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label42"> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Photo Rename</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="rename_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="window_placement_set">True</property> - <child> - <object class="GtkViewport" id="viewport2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="resize_mode">queue</property> - <property name="shadow_type">none</property> - <child> - <object class="GtkVBox" id="rename_table_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <placeholder/> - </child> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkTable" id="rename_example_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">3</property> - <property name="n_columns">3</property> - <child> - <object class="GtkLabel" id="label17"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"> </property> - </object> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label15"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"> </property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options"></property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="new_name_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="label">translators please ignore this</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="original_name_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label">translators please ignore this</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label21"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="label" translatable="yes"><i>New:</i></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label20"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><i>Original:</i></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label14"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Example</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="right_attach">3</property> - <property name="y_padding">12</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label25"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="menu_label">Rename</property> - <property name="position">1</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="rename_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Photo Rename</property> - </object> - <packing> - <property name="position">1</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="folder_tab1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkVBox" id="vbox12"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox11"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-directory</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label46"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Video Download Folders</span></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="folder_videos_cannot_be_downloaded_hbox"> - <property name="can_focus">False</property> - <child> - <object class="GtkLabel" id="folder_videos_cannot_be_downloaded_label"> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="ypad">10</property> - <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install either the <i>hachoir metadata</i> and <i>kaa metadata</i> packages for python, or <i>exiftool</i>.</property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="video_folders_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label57"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="video_download_folder_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">7</property> - <property name="n_columns">3</property> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <object class="GtkLabel" id="lblPhotos2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Download folder:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options"></property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label59"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="ypad">12</property> - <property name="label" translatable="yes">Choose the download folder. Subfolders for the downloaded videos will be automatically created in this folder using the structure specified below.</property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label61"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Download Folder</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="right_attach">3</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label62"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Download Subfolders</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="right_attach">3</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="example_video_download_path_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><i>Example: /home/user/Pictures</i></property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="video_subfolder_warning_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="wrap">True</property> - <property name="wrap_mode">word-char</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkVBox" id="video_subfolder_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="y_padding">12</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label63"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="position">2</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="video_download_folder"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Video Folders</property> - </object> - <packing> - <property name="position">2</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="video_rename_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkVBox" id="vbox10"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox13"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-convert</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label32"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Video Rename</span> </property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox14"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label44"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="rename_vbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="videos_cannot_be_downloaded_label"> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Sorry, video downloading functionality disabled. To download videos, please install either the <i>hachoir metadata</i> and <i>kaa metadata</i> packages for python, or <i>exiftool</i>.</property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="video_rename_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="window_placement_set">True</property> - <child> - <object class="GtkViewport" id="viewport1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="resize_mode">queue</property> - <property name="shadow_type">none</property> - <child> - <object class="GtkVBox" id="video_rename_table_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <placeholder/> - </child> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkTable" id="video_rename_example_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">3</property> - <property name="n_columns">3</property> - <child> - <object class="GtkLabel" id="label55"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"> </property> - </object> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label56"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"> </property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options"></property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="video_new_name_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="label">translators please ignore this</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="video_original_name_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label">translators please ignore this</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="new_video_filename_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="label" translatable="yes"><i>New:</i></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="original_video_filename_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><i>Original:</i></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="example_video_filename_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Example</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="right_attach">3</property> - <property name="y_padding">12</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label60"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="position">3</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="video_rename_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Video Rename</property> - </object> - <packing> - <property name="position">3</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="rename_options_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkVBox" id="vbox14"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox18"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="icon_name">input-keyboard</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label10"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Rename Options</span></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="reame_options_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkLabel" id="sequence_number_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes"><b>Sequence Numbers</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="sequence_number_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="spacer_seq_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xpad">12</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="seq_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label47"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Specify the time in 24 hour format at which the <i>Downloads today</i> sequence number should be reset.</property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox23"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkVBox" id="vbox3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkLabel" id="label49"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Day start:</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label51"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Downloads today:</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label52"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Stored number:</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label54"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xpad">6</property> - <property name="label" translatable="yes"> </property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="sequence_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkHBox" id="hbox22"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkSpinButton" id="hour_spinbutton"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="max_length">2</property> - <property name="invisible_char">•</property> - <property name="width_chars">2</property> - <property name="xalign">1</property> - <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <property name="adjustment">hour_adjustment</property> - <property name="numeric">True</property> - <signal name="value-changed" handler="on_hour_spinbutton_value_changed" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label50"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">:</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkSpinButton" id="minute_spinbutton"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="max_length">2</property> - <property name="invisible_char">•</property> - <property name="width_chars">2</property> - <property name="xalign">1</property> - <property name="truncate_multiline">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <property name="adjustment">minute_adjustment</property> - <property name="numeric">True</property> - <signal name="value-changed" handler="on_minute_spinbutton_value_changed" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label53"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"> hh:mm</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="synchronize_raw_jpg_checkbutton"> - <property name="label" translatable="yes">Synchronize RAW + JPEG sequence numbers</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_synchronize_raw_jpg_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label48"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="compatibility_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes"><b>Compatibility with Other Operating Systems</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="compatibility_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="compatibility_spacer_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xpad">12</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="compatibility_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">2</property> - <property name="n_columns">2</property> - <child> - <object class="GtkLabel" id="label9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Specify whether photo, video and folder names should have any characters removed that are not allowed by other operating systems.</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="right_attach">2</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="strip_characters_checkbutton"> - <property name="label" translatable="yes">Strip incompatible characters</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_strip_characters_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label33"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">4</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="position">4</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="rename_options_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Rename Options</property> - </object> - <packing> - <property name="position">4</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="job_codes_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkVBox" id="job_codes_header_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox188"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image77"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">rapid-photo-downloader-jobcode</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label1340"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Job Codes</span></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator44"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="job_codes_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkVBox" id="job_code_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkLabel" id="job_code_label"> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes"><b>Job Codes</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="job_code_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="job_code_spacer_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="job_code_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="shadow_type">in</property> - <child> - <object class="GtkTreeView" id="job_code_treeview"> - <property name="width_request">250</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="headers_visible">False</property> - <property name="rubber_banding">True</property> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="job_code_button_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkVButtonBox" id="job_code_vbuttonbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <property name="layout_style">start</property> - <child> - <object class="GtkButton" id="add_job_code_button"> - <property name="label" translatable="yes">_Add...</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_underline">True</property> - <signal name="clicked" handler="on_add_job_code_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="remove_job_code_button"> - <property name="label">gtk-remove</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - <signal name="clicked" handler="on_remove_job_code_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="remove_all_job_code_button"> - <property name="label" translatable="yes" comments="The underscore after the C signifies that the l is the accelerator key. This is the standard 'Clear' button, but I needed to change the accelerator from the standard 'c' to 'l' because the close button also used 'c'">R_emove All</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_underline">True</property> - <signal name="clicked" handler="on_remove_all_job_code_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="position">5</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="job_codes_tab_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Job Codes</property> - </object> - <packing> - <property name="position">5</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="device_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkVBox" id="vbox4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox15"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="icon_name">drive-removable-media</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label22"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Devices</span></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="vbox5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label41"> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes">Devices</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label18"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes">Devices are from where to download photos and videos, such as cameras, memory cards or Portable Storage Devices. - -You can download from multiple devices simultaneously, or you can specify a location on your hard drive. - -<i>Downloading directly from cameras is currently an experimental feature. If downloading directly from your camera works poorly or not at all, try setting it to PTP mode. If that is not possible, a card reader must be used.</i></property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox16"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkLabel" id="label26"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xpad">3</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="devices_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">3</property> - <property name="n_columns">2</property> - <property name="row_spacing">3</property> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <object class="GtkCheckButton" id="autodetect_psd_checkbutton"> - <property name="label" translatable="yes">Automatically detect Portable Storage Devices</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_autodetect_psd_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="autodetect_device_checkbutton"> - <property name="label" translatable="yes">Automatically detect devices</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_autodetect_device_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="right_attach">2</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="autodetect_image_devices_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="ypad">6</property> - <property name="label" translatable="yes">If you enable automatic detection of Portable Storage Devices, the entire device will be scanned. On large devices, this could take some time. - -When this option is enabled, and a potential device is detected, you will be prompted to determine if it should be scanned or not.</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label28"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox17"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkTable" id="devices2_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">2</property> - <property name="n_columns">2</property> - <child> - <placeholder/> - </child> - <child> - <object class="GtkLabel" id="device_location_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes">Location:</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_SHRINK</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="device_location_explanation_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="ypad">12</property> - <property name="label" translatable="yes">If you disable automatic detection, choose the exact location of the photos and videos.</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="right_attach">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <placeholder/> - </child> - <child> - <object class="GtkLabel" id="label30"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="position">6</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="device_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Devices</property> - </object> - <packing> - <property name="position">6</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="device_options_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="exclusions_header_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image10"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-preferences</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="exclusionlabel"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Device Options</span></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="vbox2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkVBox" id="vbox15"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="remebered_devices_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes">Remembered Paths</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="remembered_devices_explanation_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes">Remembered paths are those associated with devices that you have chosen to always scan or ignore when automatic detection of Portable Storage Devices is enabled.</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="remembered_devices_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="remembered_devices_spacer_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="remembered_devices_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <child> - <object class="GtkTreeView" id="remembered_devices_treeview"> - <property name="width_request">250</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="headers_visible">False</property> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="remembered_devices_button_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkVButtonBox" id="remembered_devices_vbuttonbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <property name="layout_style">start</property> - <child> - <object class="GtkButton" id="remove_remembered_device_button"> - <property name="label">gtk-remove</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - <signal name="clicked" handler="on_remove_remembered_device_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="remove_all_remembered_device_button"> - <property name="label" translatable="yes" comments="The underscore after the C signifies that the l is the accelerator key. This is the standard 'Clear' button, but I needed to change the accelerator from the standard 'c' to 'l' because the close button also used 'c'">R_emove All</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_underline">True</property> - <signal name="clicked" handler="on_remove_all_remembered_device_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="vbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="ignored_paths_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes">Ignored Paths</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="ignored_paths_explanation_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes">Specify the ending portion of any paths you want ignored when scanning devices for photos or videos. Any path ending with the values below will not be scanned.</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="ignored_paths_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="ignored_paths_spacer_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="ignored_paths_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <child> - <object class="GtkTreeView" id="ignored_paths_treeview"> - <property name="width_request">250</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="headers_visible">False</property> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="ignored_paths_button_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkVButtonBox" id="ignored_paths_vbuttonbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <property name="layout_style">start</property> - <child> - <object class="GtkButton" id="add_ignored_path_button"> - <property name="label" translatable="yes">_Add...</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_underline">True</property> - <signal name="clicked" handler="on_add_ignored_path_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="remove_ignored_path_button"> - <property name="label">gtk-remove</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_stock">True</property> - <signal name="clicked" handler="on_remove_ignored_path_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="remove_all_ignored_paths_button"> - <property name="label" translatable="yes" comments="The underscore after the C signifies that the l is the accelerator key. This is the standard 'Clear' button, but I needed to change the accelerator from the standard 'c' to 'l' because the close button also used 'c'">Re_move All</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_underline">True</property> - <signal name="clicked" handler="on_remove_all_ignored_paths_button_clicked" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkCheckButton" id="ignored_paths_use_re_checkbutton"> - <property name="label" translatable="yes">Use _python-style regular expressions</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_ignored_paths_use_re_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="position">7</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="device_options_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Device Options</property> - </object> - <packing> - <property name="position">7</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="backup_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkVBox" id="vbox8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox19"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="icon_name">drive-removable-media</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label27"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Backup</span> </property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="vbox9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkLabel" id="label43"> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes"><b>Backup</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox20"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label38"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="backup_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">10</property> - <property name="n_columns">4</property> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <object class="GtkLabel" id="backup_location_explanation_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="xalign">0</property> - <property name="ypad">12</property> - <property name="label" translatable="yes">If you disable automatic detection, choose the exact backup locations.</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">4</property> - <property name="top_attach">7</property> - <property name="bottom_attach">8</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="auto_detect_backup_checkbutton"> - <property name="label" translatable="yes">Automatically detect backup devices</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_auto_detect_backup_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">4</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label11"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="ypad">12</property> - <property name="label" translatable="yes">You can have your photos and videos backed up to multiple locations as they are downloaded, e.g. external hard drives.</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="right_attach">4</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="backup_checkbutton"> - <property name="label" translatable="yes">Back up photos and videos when downloading</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_backup_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="right_attach">4</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="backup_identifier_explanation_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="ypad">6</property> - <property name="label" translatable="yes">Specify the folder in which backups are stored on the device. - -<i>Note: this will also be used to determine whether or not the device is used for backups. For each device you wish to use for backing up to, create a folder in it with one of these names.</i></property> - <property name="use_markup">True</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">4</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="backup_location_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Photo backup location:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">8</property> - <property name="bottom_attach">9</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="backup_identifier_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Photo backup folder name:</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">6</property> - <property name="bottom_attach">5</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - <property name="y_padding">6</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="backup_example_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="ypad">6</property> - <property name="label" translatable="yes"><i>Example:</i></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="x_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="example_backup_path_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="ypad">6</property> - <property name="label" translatable="yes"><i>/media/externaldrive/Photos</i></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="left_attach">3</property> - <property name="right_attach">4</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="backup_identifier_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <signal name="changed" handler="on_backup_identifier_entry_changed" swapped="no"/> - </object> - <packing> - <property name="left_attach">3</property> - <property name="right_attach">4</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="y_options"></property> - <property name="y_padding">6</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="video_backup_identifier_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Video backup folder name:</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - <property name="y_padding">6</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="video_backup_identifier_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - <signal name="changed" handler="on_video_backup_identifier_entry_changed" swapped="no"/> - </object> - <packing> - <property name="left_attach">3</property> - <property name="right_attach">4</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="y_options"></property> - <property name="y_padding">6</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="backup_video_location_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Video backup location:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">3</property> - <property name="top_attach">9</property> - <property name="bottom_attach">10</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label39"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="position">8</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="backup_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Backup</property> - </object> - <packing> - <property name="position">8</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="automation_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkVBox" id="vbox11"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox21"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-execute</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label31"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Miscellaneous</span></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="vbox13"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkLabel" id="label45"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes"><b>Program Automation</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox24"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkLabel" id="label34"> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="automation_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">7</property> - <property name="n_columns">3</property> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <object class="GtkCheckButton" id="auto_unmount_checkbutton"> - <property name="label" translatable="yes">Unmount ("eject") device upon download completion</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="xalign">0</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_auto_unmount_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="right_attach">3</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="auto_startup_checkbutton"> - <property name="label" translatable="yes">Start downloading at program startup</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="xalign">0</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_auto_startup_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="right_attach">3</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="auto_insertion_checkbutton"> - <property name="label" translatable="yes">Start downloading upon device insertion</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="xalign">0</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_auto_insertion_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="right_attach">3</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="auto_exit_checkbutton"> - <property name="label" translatable="yes">Exit program when download completes</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="xalign">0</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_auto_exit_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="right_attach">3</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="auto_exit_force_checkbutton"> - <property name="label" translatable="yes">Exit program even if download had warnings or errors</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="xalign">0</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_auto_exit_force_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="auto_rotate_checkbutton"> - <property name="label" translatable="yes">Automatically rotate JPEG images</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="xalign">0</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_auto_rotate_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="right_attach">3</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - </packing> - </child> - <child> - <object class="GtkCheckButton" id="verify_file_checkbutton"> - <property name="label" translatable="yes" comments="Checks files using MD5 to ensure they have downloaded and been saved correctly">Verify files as they are downloaded</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_verify_file_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="right_attach">3</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">24</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label35"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="xpad">12</property> - <property name="label" translatable="yes">Performance</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox25"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkLabel" id="label19"> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="table1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_columns">3</property> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <object class="GtkCheckButton" id="generate_thumbnails_checkbutton"> - <property name="label" translatable="yes">Generate thumbnails (slower)</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_generate_thumbnails_checkbutton_toggled" swapped="no"/> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">24</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label29"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">3</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="position">9</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="miscillaneous_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="label" translatable="yes">Miscellaneous</property> - </object> - <packing> - <property name="position">9</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="error_tab"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkVBox" id="vbox16"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="hbox26"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkImage" id="image9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="stock">gtk-dialog-error</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label40"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes"><span weight="bold" size="x-large">Error Handling</span></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHSeparator" id="hseparator10"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox27"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">12</property> - <child> - <object class="GtkLabel" id="label36"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkTable" id="error_table"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">14</property> - <property name="n_columns">2</property> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <object class="GtkLabel" id="label13"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="label" translatable="yes"> </property> - </object> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label37"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes"><b>Photo and Video Name Conflicts</b></property> - <property name="use_markup">True</property> - </object> - <packing> - <property name="right_attach">2</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="add_identifier_radiobutton"> - <property name="label" translatable="yes">Add unique identifier</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="active">True</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_add_identifier_radiobutton_toggled" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="skip_download_radiobutton"> - <property name="label" translatable="yes">Skip download</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <property name="group">add_identifier_radiobutton</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label58"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="ypad">12</property> - <property name="label" translatable="yes">When a photo or video of the same name has already been downloaded, choose whether to skip downloading the file, or to add a unique indentifier.</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label64"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="ypad">12</property> - <property name="label" translatable="yes">When backing up, choose whether to overwrite a file on the backup device that has the same name, or skip backing it up.</property> - <property name="wrap">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="backup_duplicate_overwrite_radiobutton"> - <property name="label" translatable="yes">Overwrite</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="draw_indicator">True</property> - <property name="group">backup_duplicate_skip_radiobutton</property> - <signal name="toggled" handler="on_backup_duplicate_overwrite_radiobutton_toggled" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - </packing> - </child> - <child> - <object class="GtkRadioButton" id="backup_duplicate_skip_radiobutton"> - <property name="label" translatable="yes">Skip</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="active">True</property> - <property name="draw_indicator">True</property> - <signal name="toggled" handler="on_backup_duplicate_skip_radiobutton_toggled" swapped="no"/> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label65"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">12</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="position">10</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="error_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="label" translatable="yes">Error Handling</property> - </object> - <packing> - <property name="position">10</property> - <property name="tab_fill">False</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">6</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">5</property> - <property name="position">1</property> - </packing> - </child> - <child> - <placeholder/> - </child> - </object> - </child> - <action-widgets> - <action-widget response="-11">help_button1</action-widget> - <action-widget response="-7">close_button</action-widget> - </action-widgets> - </object> -</interface> diff --git a/rapid/glade3/rapid-photo-downloader-download-pending.png b/rapid/glade3/rapid-photo-downloader-download-pending.png Binary files differdeleted file mode 100644 index e12cc69..0000000 --- a/rapid/glade3/rapid-photo-downloader-download-pending.png +++ /dev/null diff --git a/rapid/glade3/rapid-photo-downloader-downloaded-with-error.svg b/rapid/glade3/rapid-photo-downloader-downloaded-with-error.svg deleted file mode 100644 index 74f13e3..0000000 --- a/rapid/glade3/rapid-photo-downloader-downloaded-with-error.svg +++ /dev/null @@ -1,350 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.0" - width="16" - height="16" - id="svg2" - inkscape:version="0.47 r22583" - sodipodi:docname="downloaded-with-error.svg"> - <metadata - id="metadata64"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - </cc:Work> - </rdf:RDF> - </metadata> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1920" - inkscape:window-height="1089" - id="namedview62" - showgrid="false" - inkscape:zoom="51.5" - inkscape:cx="4.0485437" - inkscape:cy="8" - inkscape:window-x="0" - inkscape:window-y="24" - inkscape:window-maximized="1" - inkscape:current-layer="svg2" /> - <defs - id="defs4"> - <inkscape:perspective - sodipodi:type="inkscape:persp3d" - inkscape:vp_x="0 : 8 : 1" - inkscape:vp_y="0 : 1000 : 0" - inkscape:vp_z="16 : 8 : 1" - inkscape:persp3d-origin="8 : 5.3333333 : 1" - id="perspective66" /> - <linearGradient - id="linearGradient4168"> - <stop - id="stop3652" - style="stop-color:#ee8b68;stop-opacity:1;" - offset="0" /> - <stop - id="stop4172" - style="stop-color:#a71d3a;stop-opacity:1;" - offset="1" /> - </linearGradient> - <linearGradient - x1="28.314739" - y1="20.709501" - x2="0.81572169" - y2="20.709501" - id="linearGradient3374" - xlink:href="#linearGradient4168" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,-0.319447,-0.313352,0,15.561474,13.08587)" /> - <linearGradient - x1="53.99139" - y1="87.89592" - x2="53.99139" - y2="104.28131" - id="linearGradient8226" - xlink:href="#linearGradient2215-9-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.1242128,0,0,0.1863981,0.233129,-3.9907482)" /> - <linearGradient - x1="7.0625" - y1="35.28125" - x2="24.6875" - y2="35.28125" - id="linearGradient8223" - xlink:href="#linearGradient6309-1" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.5545779,0,0,0.35955055,-1.6911436,1.3146057)" /> - <linearGradient - x1="12.277412" - y1="37.205811" - x2="12.221823" - y2="33.758667" - id="linearGradient8220" - xlink:href="#linearGradient4236-0-1" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.5545779,0,0,0.35955055,-2.132306,1.7913054)" /> - <linearGradient - x1="4.6104097" - y1="11.320717" - x2="2.2246289" - y2="6.8646717" - id="linearGradient8217" - xlink:href="#linearGradient3484-2-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.6451613,0,0,0.9797594,0.258064,-6.7257265)" /> - <radialGradient - cx="4.1993008" - cy="2.3117516" - r="7.9999995" - fx="4.1993008" - fy="2.3117516" - id="radialGradient8215" - xlink:href="#linearGradient7056-0-9" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.1767008,1.0376968,-0.76927742,0.87232541,1.0363585,-3.2771526)" /> - <radialGradient - cx="141.74666" - cy="206.42612" - r="78.728165" - fx="141.74666" - fy="206.42612" - id="radialGradient8211" - xlink:href="#linearGradient4035-5" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.12708936,-0.00212891,9.4059438e-4,0.12249323,-10.298148,-14.500064)" /> - <radialGradient - cx="142.62215" - cy="191.85428" - r="78.728165" - fx="142.62215" - fy="191.85428" - id="radialGradient8208" - xlink:href="#linearGradient4035-5" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.06165082,0,0,-0.0653716,-1.0370648,17.524179)" /> - <radialGradient - cx="24" - cy="42" - r="21" - fx="24" - fy="42" - id="radialGradient8201" - xlink:href="#linearGradient6310-8-2" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.33333332,0,0,0.14285711,6.5e-7,8.0000015)" /> - <radialGradient - cx="127.31733" - cy="143.82751" - r="78.728165" - fx="127.31733" - fy="143.82751" - id="radialGradient8198" - xlink:href="#linearGradient8105" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.04179653,-0.01388393,0.00338688,0.03797545,-0.54190607,-0.79595187)" /> - <linearGradient - id="linearGradient6310-8-2"> - <stop - id="stop6312-6-2" - style="stop-color:#ffffff;stop-opacity:1" - offset="0" /> - <stop - id="stop6314-6-0" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3484-2-0"> - <stop - id="stop3486-2-4" - style="stop-color:#969696;stop-opacity:1" - offset="0" /> - <stop - id="stop3488-0-2" - style="stop-color:#b4b4b4;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient7056-0-9"> - <stop - id="stop7064-4-6" - style="stop-color:#e6e6e6;stop-opacity:1" - offset="0" /> - <stop - id="stop7060-2-1" - style="stop-color:#c8c8c8;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient2215-9-0"> - <stop - id="stop2223-6-4" - style="stop-color:#7a7a7a;stop-opacity:1" - offset="0" /> - <stop - id="stop2219-1-2" - style="stop-color:#474747;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient4035-5"> - <stop - id="stop4037-8" - style="stop-color:#f5f5f5;stop-opacity:1" - offset="0" /> - <stop - id="stop4039-7" - style="stop-color:#e7e7e7;stop-opacity:1" - offset="0.47025558" /> - <stop - id="stop4041-0" - style="stop-color:#8c8c8c;stop-opacity:1" - offset="0.69348532" /> - <stop - id="stop4043-4" - style="stop-color:#dddddd;stop-opacity:1" - offset="0.83542866" /> - <stop - id="stop4045-8" - style="stop-color:#a8a8a8;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient6309-1"> - <stop - id="stop6311-0" - style="stop-color:#000000;stop-opacity:1" - offset="0" /> - <stop - id="stop6313-7" - style="stop-color:#bbbbbb;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient4236-0-1"> - <stop - id="stop4238-4-4" - style="stop-color:#eeeeee;stop-opacity:1" - offset="0" /> - <stop - id="stop4240-3-9" - style="stop-color:#eeeeee;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient8105"> - <stop - id="stop8107" - style="stop-color:#f5f5f5;stop-opacity:1" - offset="0" /> - <stop - id="stop8109" - style="stop-color:#e7e7e7;stop-opacity:1" - offset="0.25027597" /> - <stop - id="stop8111" - style="stop-color:#8c8c8c;stop-opacity:1" - offset="0.69348532" /> - <stop - id="stop8113" - style="stop-color:#dddddd;stop-opacity:1" - offset="0.83542866" /> - <stop - id="stop8115" - style="stop-color:#a8a8a8;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4168" - id="linearGradient3656" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,-0.319447,-0.313352,0,15.561474,13.08587)" - x1="28.314739" - y1="20.709501" - x2="0.81572169" - y2="20.709501" /> - </defs> - <g - id="g3119"> - <path - d="m 15.49723,12.5 c -0.304165,2.535038 -0.22639,2.983925 -0.777167,2.983925 -0.21573,0.03617 -8.698317,0 -13.470165,0 -0.598135,0 -0.447005,0.04932 -0.749898,-2.983925 4.8141597,0.139771 10.032192,-0.370561 14.99723,0 z" - id="rect2992-5" - style="fill:url(#linearGradient8226);fill-opacity:1;fill-rule:nonzero;stroke:#353537;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - <path - d="m 2,13 10,0 0,2 L 2.2255647,15 2,13 z" - id="rect9146-5" - style="fill:url(#linearGradient8223);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.40899992;marker:none;visibility:visible;display:inline;overflow:visible" /> - <path - d="m 2.2255639,14.999999 c 0,0 -0.1503759,-1.442332 -0.1503759,-1.442332 1.0179428,1.143093 4.7514263,1.442332 7.3252263,1.442332 0,0 -7.1748504,0 -7.1748504,0 z" - id="path9148-2" - style="opacity:0.81142853;fill:url(#linearGradient8220);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <path - d="M 1.4897093,1.4657632 0.514749,12.488885 C 0.505,12.488885 0.5,12.491788 0.5,12.499999 l 14.999999,0 c 0,-0.0082 -0.005,-0.01111 -0.01475,-0.01111 L 14.479867,1.2478485 c 0,0 -0.187746,-0.74340135 -0.909635,-0.74340135 -0.688148,0 -10.5350353,-0.004447 -11.2600103,-0.004447 -0.75868,0 -0.8205124,0.96576325 -0.8205124,0.96576325 z" - id="rect2990-9" - style="fill:url(#radialGradient8215);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient8217);stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - <path - d="m 1.4762913,12.5 13.0474177,0" - id="path2215-0" - style="opacity:0.7;fill:none;stroke:#ffffff;stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> - <path - d="M 9.875,1.03125 C 9.5425325,1.03125 9.2896361,1.1933561 9.28125,1.4375 7.5462296,4.2547045 3.7159646,3.3310272 2.1875,6.6875 2.107281,7.5502069 2.3133647,8.6701052 3.09375,9.375 4.23596,10.40677 6.1379859,11.041207 8.15625,11 12.321615,10.914959 15.990924,8.1997572 12.8125,4.65625 12.794904,4.4114056 12.5625,1.4375 12.5625,1.4375 12.545264,1.1967161 12.267601,1.03125 11.9375,1.03125 l -2.0625,0 z m -0.03125,0.4375 2.0625,0 c 0.119904,0 0.214005,0.027064 0.21875,0.09375 L 11.6875,4.65625 C 15.160987,7.4613605 11.945518,9.7796633 8.25,9.90625 4.8918537,10.021279 2.9699492,8.847871 3.15625,6.84375 4.3344434,3.466723 8.863656,5.0817755 9.625,1.5625 9.6054609,1.4997867 9.7265155,1.46875 9.84375,1.46875 z" - id="path8117" - style="fill:url(#radialGradient8211);fill-opacity:1" /> - <path - d="M 7.9999249,8.9999497 C 7.3338259,9.003979 6.6178906,8.7647964 6.2182574,8.2045172 5.8878761,7.7376942 5.9413392,7.042109 6.3494314,6.63805 6.8327879,6.1198432 7.5866528,5.9505056 8.2718727,6.0129013 8.8789744,6.0691126 9.5104356,6.3443475 9.8353698,6.8829643 10.086844,7.3170303 10.047208,7.9041627 9.7164741,8.2868807 9.3098043,8.7947936 8.6299115,8.9998014 7.9999249,8.9999497 z m 0.033529,-2.05954 C 7.5676631,6.950996 7.0685483,6.9372219 6.6531228,7.1810992 6.3786983,7.3719617 6.3550571,7.7834456 6.5399472,8.0446579 6.791609,8.4367198 7.2662569,8.6084808 7.7086272,8.6682148 8.2791121,8.7399813 8.9061994,8.6113728 9.334753,8.2097584 9.5762055,7.9697526 9.6865747,7.5362831 9.4461096,7.2585712 9.2078802,7.0082999 8.8377224,6.985091 8.5141086,6.9541818 8.3541203,6.9436237 8.1937276,6.9420228 8.0334539,6.9404097 z" - id="path9400" - style="fill:url(#radialGradient8208);fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - <g - transform="matrix(-0.04685826,0,0,0.07750161,4.3055193,-2.991086)" - id="g9436"> - <path - d="m 37.925296,187.15033 c 0.31446,3.45855 5.02375,6.27902 10.51419,6.27902 5.48794,0 9.6432,-2.82047 9.27634,-6.27902 -0.36437,-3.44025 -5.07117,-6.21806 -10.5067,-6.21806 -5.43803,0.002 -9.59329,2.77781 -9.28383,6.21806 l 0,0 z" - id="path9438" - style="fill:#535353;fill-opacity:1" /> - </g> - <g - transform="matrix(-0.04685826,0,0,0.07750161,16.205519,-2.991086)" - id="g9496"> - <path - d="m 37.925296,187.15033 c 0.31446,3.45855 5.02375,6.27902 10.51419,6.27902 5.48794,0 9.6432,-2.82047 9.27634,-6.27902 -0.36437,-3.44025 -5.07117,-6.21806 -10.5067,-6.21806 -5.43803,0.002 -9.59329,2.77781 -9.28383,6.21806 l 0,0 z" - id="path9498" - style="fill:#535353;fill-opacity:1" /> - </g> - <rect - width="14" - height="2" - x="1" - y="13" - id="rect6300-3-2" - style="opacity:0.2;fill:url(#radialGradient8201);fill-opacity:1;stroke:none" /> - <path - d="M 3.5490052,1.2794204 C 3.5821222,1.4638603 3.5033392,1.668064 3.4765882,1.8577602 3.3642225,2.4379547 3.248083,3.0174209 3.1387788,3.5982061 3.0364568,3.976483 3.4034434,3.822574 3.4487925,3.8610185 3.9285086,3.6778743 4.4037673,3.4775049 4.8813544,3.2862237 5.3796101,3.0823738 5.8809457,2.8849464 6.3772748,2.677079 6.6902572,2.4909307 6.7820665,2.323762 6.8635898,2.0428768 6.9084532,1.7990145 6.9138614,1.5262921 6.7919595,1.3034032 6.7139126,1.1493881 6.5435087,1.0841579 6.379809,1.1073783 c -0.8675476,0 -1.7350953,0 -2.6026429,0 L 3.5490052,1.2794204 z m 0.588988,0.4439693 c 0.5921265,0 1.184253,-1e-7 1.7763795,-1e-7 0.1871044,0.070513 0.054625,0.2194806 -0.083295,0.2325189 C 5.229708,2.1997992 4.6283387,2.4436898 4.0269694,2.6875804 3.8824333,2.7420385 3.8720481,2.5203138 3.9157358,2.4262942 c 0.037079,-0.2075185 0.074157,-0.415037 0.1112359,-0.6225555 0.041217,-0.021125 0.065659,-0.065363 0.1110215,-0.080349 z" - id="path8125" - style="fill:url(#radialGradient8198);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - </g> - <path - style="fill:url(#linearGradient3656);fill-opacity:1;fill-rule:nonzero;stroke:#ac272e;stroke-width:0.99800000000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" - d="M 12.5,5.4999998 8.0596782,10.5 3.5,5.4999998 l 2,0 L 5.5,0.5 l 5,0 0,4.9999998 2,0 z" - id="path3288" /> -</svg> diff --git a/rapid/glade3/rapid-photo-downloader-downloaded-with-warning.svg b/rapid/glade3/rapid-photo-downloader-downloaded-with-warning.svg deleted file mode 100644 index 8065ac2..0000000 --- a/rapid/glade3/rapid-photo-downloader-downloaded-with-warning.svg +++ /dev/null @@ -1,351 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.0" - width="16" - height="16" - id="svg2" - inkscape:version="0.47 r22583" - sodipodi:docname="document-save.svg"> - <metadata - id="metadata64"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <sodipodi:namedview - pagecolor="#ffffff" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="0" - inkscape:pageshadow="2" - inkscape:window-width="1920" - inkscape:window-height="1089" - id="namedview62" - showgrid="false" - inkscape:zoom="51.5" - inkscape:cx="4.0485437" - inkscape:cy="8" - inkscape:window-x="0" - inkscape:window-y="24" - inkscape:window-maximized="1" - inkscape:current-layer="svg2" /> - <defs - id="defs4"> - <inkscape:perspective - sodipodi:type="inkscape:persp3d" - inkscape:vp_x="0 : 8 : 1" - inkscape:vp_y="0 : 1000 : 0" - inkscape:vp_z="16 : 8 : 1" - inkscape:persp3d-origin="8 : 5.3333333 : 1" - id="perspective66" /> - <linearGradient - id="linearGradient4168"> - <stop - id="stop3652" - style="stop-color:#edd563;stop-opacity:1;" - offset="0" /> - <stop - id="stop4172" - style="stop-color:#e08223;stop-opacity:1;" - offset="1" /> - </linearGradient> - <linearGradient - x1="28.314739" - y1="20.709501" - x2="0.81572169" - y2="20.709501" - id="linearGradient3374" - xlink:href="#linearGradient4168" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,-0.319447,-0.313352,0,15.561474,13.08587)" /> - <linearGradient - x1="53.99139" - y1="87.89592" - x2="53.99139" - y2="104.28131" - id="linearGradient8226" - xlink:href="#linearGradient2215-9-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.1242128,0,0,0.1863981,0.233129,-3.9907482)" /> - <linearGradient - x1="7.0625" - y1="35.28125" - x2="24.6875" - y2="35.28125" - id="linearGradient8223" - xlink:href="#linearGradient6309-1" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.5545779,0,0,0.35955055,-1.6911436,1.3146057)" /> - <linearGradient - x1="12.277412" - y1="37.205811" - x2="12.221823" - y2="33.758667" - id="linearGradient8220" - xlink:href="#linearGradient4236-0-1" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.5545779,0,0,0.35955055,-2.132306,1.7913054)" /> - <linearGradient - x1="4.6104097" - y1="11.320717" - x2="2.2246289" - y2="6.8646717" - id="linearGradient8217" - xlink:href="#linearGradient3484-2-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.6451613,0,0,0.9797594,0.258064,-6.7257265)" /> - <radialGradient - cx="4.1993008" - cy="2.3117516" - r="7.9999995" - fx="4.1993008" - fy="2.3117516" - id="radialGradient8215" - xlink:href="#linearGradient7056-0-9" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.1767008,1.0376968,-0.76927742,0.87232541,1.0363585,-3.2771526)" /> - <radialGradient - cx="141.74666" - cy="206.42612" - r="78.728165" - fx="141.74666" - fy="206.42612" - id="radialGradient8211" - xlink:href="#linearGradient4035-5" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.12708936,-0.00212891,9.4059438e-4,0.12249323,-10.298148,-14.500064)" /> - <radialGradient - cx="142.62215" - cy="191.85428" - r="78.728165" - fx="142.62215" - fy="191.85428" - id="radialGradient8208" - xlink:href="#linearGradient4035-5" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.06165082,0,0,-0.0653716,-1.0370648,17.524179)" /> - <radialGradient - cx="24" - cy="42" - r="21" - fx="24" - fy="42" - id="radialGradient8201" - xlink:href="#linearGradient6310-8-2" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.33333332,0,0,0.14285711,6.5e-7,8.0000015)" /> - <radialGradient - cx="127.31733" - cy="143.82751" - r="78.728165" - fx="127.31733" - fy="143.82751" - id="radialGradient8198" - xlink:href="#linearGradient8105" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.04179653,-0.01388393,0.00338688,0.03797545,-0.54190607,-0.79595187)" /> - <linearGradient - id="linearGradient6310-8-2"> - <stop - id="stop6312-6-2" - style="stop-color:#ffffff;stop-opacity:1" - offset="0" /> - <stop - id="stop6314-6-0" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3484-2-0"> - <stop - id="stop3486-2-4" - style="stop-color:#969696;stop-opacity:1" - offset="0" /> - <stop - id="stop3488-0-2" - style="stop-color:#b4b4b4;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient7056-0-9"> - <stop - id="stop7064-4-6" - style="stop-color:#e6e6e6;stop-opacity:1" - offset="0" /> - <stop - id="stop7060-2-1" - style="stop-color:#c8c8c8;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient2215-9-0"> - <stop - id="stop2223-6-4" - style="stop-color:#7a7a7a;stop-opacity:1" - offset="0" /> - <stop - id="stop2219-1-2" - style="stop-color:#474747;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient4035-5"> - <stop - id="stop4037-8" - style="stop-color:#f5f5f5;stop-opacity:1" - offset="0" /> - <stop - id="stop4039-7" - style="stop-color:#e7e7e7;stop-opacity:1" - offset="0.47025558" /> - <stop - id="stop4041-0" - style="stop-color:#8c8c8c;stop-opacity:1" - offset="0.69348532" /> - <stop - id="stop4043-4" - style="stop-color:#dddddd;stop-opacity:1" - offset="0.83542866" /> - <stop - id="stop4045-8" - style="stop-color:#a8a8a8;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient6309-1"> - <stop - id="stop6311-0" - style="stop-color:#000000;stop-opacity:1" - offset="0" /> - <stop - id="stop6313-7" - style="stop-color:#bbbbbb;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient4236-0-1"> - <stop - id="stop4238-4-4" - style="stop-color:#eeeeee;stop-opacity:1" - offset="0" /> - <stop - id="stop4240-3-9" - style="stop-color:#eeeeee;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient8105"> - <stop - id="stop8107" - style="stop-color:#f5f5f5;stop-opacity:1" - offset="0" /> - <stop - id="stop8109" - style="stop-color:#e7e7e7;stop-opacity:1" - offset="0.25027597" /> - <stop - id="stop8111" - style="stop-color:#8c8c8c;stop-opacity:1" - offset="0.69348532" /> - <stop - id="stop8113" - style="stop-color:#dddddd;stop-opacity:1" - offset="0.83542866" /> - <stop - id="stop8115" - style="stop-color:#a8a8a8;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4168" - id="linearGradient3656" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,-0.319447,-0.313352,0,15.561474,13.08587)" - x1="28.314739" - y1="20.709501" - x2="0.81572169" - y2="20.709501" /> - </defs> - <g - id="g3119"> - <path - d="m 15.49723,12.5 c -0.304165,2.535038 -0.22639,2.983925 -0.777167,2.983925 -0.21573,0.03617 -8.698317,0 -13.470165,0 -0.598135,0 -0.447005,0.04932 -0.749898,-2.983925 4.8141597,0.139771 10.032192,-0.370561 14.99723,0 z" - id="rect2992-5" - style="fill:url(#linearGradient8226);fill-opacity:1;fill-rule:nonzero;stroke:#353537;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - <path - d="m 2,13 10,0 0,2 L 2.2255647,15 2,13 z" - id="rect9146-5" - style="fill:url(#linearGradient8223);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.40899992;marker:none;visibility:visible;display:inline;overflow:visible" /> - <path - d="m 2.2255639,14.999999 c 0,0 -0.1503759,-1.442332 -0.1503759,-1.442332 1.0179428,1.143093 4.7514263,1.442332 7.3252263,1.442332 0,0 -7.1748504,0 -7.1748504,0 z" - id="path9148-2" - style="opacity:0.81142853;fill:url(#linearGradient8220);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <path - d="M 1.4897093,1.4657632 0.514749,12.488885 C 0.505,12.488885 0.5,12.491788 0.5,12.499999 l 14.999999,0 c 0,-0.0082 -0.005,-0.01111 -0.01475,-0.01111 L 14.479867,1.2478485 c 0,0 -0.187746,-0.74340135 -0.909635,-0.74340135 -0.688148,0 -10.5350353,-0.004447 -11.2600103,-0.004447 -0.75868,0 -0.8205124,0.96576325 -0.8205124,0.96576325 z" - id="rect2990-9" - style="fill:url(#radialGradient8215);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient8217);stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - <path - d="m 1.4762913,12.5 13.0474177,0" - id="path2215-0" - style="opacity:0.7;fill:none;stroke:#ffffff;stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> - <path - d="M 9.875,1.03125 C 9.5425325,1.03125 9.2896361,1.1933561 9.28125,1.4375 7.5462296,4.2547045 3.7159646,3.3310272 2.1875,6.6875 2.107281,7.5502069 2.3133647,8.6701052 3.09375,9.375 4.23596,10.40677 6.1379859,11.041207 8.15625,11 12.321615,10.914959 15.990924,8.1997572 12.8125,4.65625 12.794904,4.4114056 12.5625,1.4375 12.5625,1.4375 12.545264,1.1967161 12.267601,1.03125 11.9375,1.03125 l -2.0625,0 z m -0.03125,0.4375 2.0625,0 c 0.119904,0 0.214005,0.027064 0.21875,0.09375 L 11.6875,4.65625 C 15.160987,7.4613605 11.945518,9.7796633 8.25,9.90625 4.8918537,10.021279 2.9699492,8.847871 3.15625,6.84375 4.3344434,3.466723 8.863656,5.0817755 9.625,1.5625 9.6054609,1.4997867 9.7265155,1.46875 9.84375,1.46875 z" - id="path8117" - style="fill:url(#radialGradient8211);fill-opacity:1" /> - <path - d="M 7.9999249,8.9999497 C 7.3338259,9.003979 6.6178906,8.7647964 6.2182574,8.2045172 5.8878761,7.7376942 5.9413392,7.042109 6.3494314,6.63805 6.8327879,6.1198432 7.5866528,5.9505056 8.2718727,6.0129013 8.8789744,6.0691126 9.5104356,6.3443475 9.8353698,6.8829643 10.086844,7.3170303 10.047208,7.9041627 9.7164741,8.2868807 9.3098043,8.7947936 8.6299115,8.9998014 7.9999249,8.9999497 z m 0.033529,-2.05954 C 7.5676631,6.950996 7.0685483,6.9372219 6.6531228,7.1810992 6.3786983,7.3719617 6.3550571,7.7834456 6.5399472,8.0446579 6.791609,8.4367198 7.2662569,8.6084808 7.7086272,8.6682148 8.2791121,8.7399813 8.9061994,8.6113728 9.334753,8.2097584 9.5762055,7.9697526 9.6865747,7.5362831 9.4461096,7.2585712 9.2078802,7.0082999 8.8377224,6.985091 8.5141086,6.9541818 8.3541203,6.9436237 8.1937276,6.9420228 8.0334539,6.9404097 z" - id="path9400" - style="fill:url(#radialGradient8208);fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - <g - transform="matrix(-0.04685826,0,0,0.07750161,4.3055193,-2.991086)" - id="g9436"> - <path - d="m 37.925296,187.15033 c 0.31446,3.45855 5.02375,6.27902 10.51419,6.27902 5.48794,0 9.6432,-2.82047 9.27634,-6.27902 -0.36437,-3.44025 -5.07117,-6.21806 -10.5067,-6.21806 -5.43803,0.002 -9.59329,2.77781 -9.28383,6.21806 l 0,0 z" - id="path9438" - style="fill:#535353;fill-opacity:1" /> - </g> - <g - transform="matrix(-0.04685826,0,0,0.07750161,16.205519,-2.991086)" - id="g9496"> - <path - d="m 37.925296,187.15033 c 0.31446,3.45855 5.02375,6.27902 10.51419,6.27902 5.48794,0 9.6432,-2.82047 9.27634,-6.27902 -0.36437,-3.44025 -5.07117,-6.21806 -10.5067,-6.21806 -5.43803,0.002 -9.59329,2.77781 -9.28383,6.21806 l 0,0 z" - id="path9498" - style="fill:#535353;fill-opacity:1" /> - </g> - <rect - width="14" - height="2" - x="1" - y="13" - id="rect6300-3-2" - style="opacity:0.2;fill:url(#radialGradient8201);fill-opacity:1;stroke:none" /> - <path - d="M 3.5490052,1.2794204 C 3.5821222,1.4638603 3.5033392,1.668064 3.4765882,1.8577602 3.3642225,2.4379547 3.248083,3.0174209 3.1387788,3.5982061 3.0364568,3.976483 3.4034434,3.822574 3.4487925,3.8610185 3.9285086,3.6778743 4.4037673,3.4775049 4.8813544,3.2862237 5.3796101,3.0823738 5.8809457,2.8849464 6.3772748,2.677079 6.6902572,2.4909307 6.7820665,2.323762 6.8635898,2.0428768 6.9084532,1.7990145 6.9138614,1.5262921 6.7919595,1.3034032 6.7139126,1.1493881 6.5435087,1.0841579 6.379809,1.1073783 c -0.8675476,0 -1.7350953,0 -2.6026429,0 L 3.5490052,1.2794204 z m 0.588988,0.4439693 c 0.5921265,0 1.184253,-1e-7 1.7763795,-1e-7 0.1871044,0.070513 0.054625,0.2194806 -0.083295,0.2325189 C 5.229708,2.1997992 4.6283387,2.4436898 4.0269694,2.6875804 3.8824333,2.7420385 3.8720481,2.5203138 3.9157358,2.4262942 c 0.037079,-0.2075185 0.074157,-0.415037 0.1112359,-0.6225555 0.041217,-0.021125 0.065659,-0.065363 0.1110215,-0.080349 z" - id="path8125" - style="fill:url(#radialGradient8198);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - </g> - <path - style="fill:url(#linearGradient3656);fill-opacity:1;fill-rule:nonzero;stroke:#bb5e12;stroke-width:0.99800000000000000;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" - d="M 12.5,5.4999998 8.0596782,10.5 3.5,5.4999998 l 2,0 L 5.5,0.5 l 5,0 0,4.9999998 2,0 z" - id="path3288" /> -</svg> diff --git a/rapid/glade3/rapid-photo-downloader-downloaded.svg b/rapid/glade3/rapid-photo-downloader-downloaded.svg deleted file mode 100644 index 378927e..0000000 --- a/rapid/glade3/rapid-photo-downloader-downloaded.svg +++ /dev/null @@ -1,295 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - version="1.0" - width="16" - height="16" - id="svg2"> - <defs - id="defs4"> - <linearGradient - id="linearGradient4168"> - <stop - id="stop3652" - style="stop-color:#d0e08f;stop-opacity:1" - offset="0" /> - <stop - id="stop4172" - style="stop-color:#9fba48;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - x1="28.314739" - y1="20.709501" - x2="0.81572169" - y2="20.709501" - id="linearGradient3374" - xlink:href="#linearGradient4168" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0,-0.319447,-0.313352,0,15.561474,13.08587)" /> - <linearGradient - x1="53.99139" - y1="87.89592" - x2="53.99139" - y2="104.28131" - id="linearGradient8226" - xlink:href="#linearGradient2215-9-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.1242128,0,0,0.1863981,0.233129,-3.9907482)" /> - <linearGradient - x1="7.0625" - y1="35.28125" - x2="24.6875" - y2="35.28125" - id="linearGradient8223" - xlink:href="#linearGradient6309-1" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.5545779,0,0,0.35955055,-1.6911436,1.3146057)" /> - <linearGradient - x1="12.277412" - y1="37.205811" - x2="12.221823" - y2="33.758667" - id="linearGradient8220" - xlink:href="#linearGradient4236-0-1" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.5545779,0,0,0.35955055,-2.132306,1.7913054)" /> - <linearGradient - x1="4.6104097" - y1="11.320717" - x2="2.2246289" - y2="6.8646717" - id="linearGradient8217" - xlink:href="#linearGradient3484-2-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.6451613,0,0,0.9797594,0.258064,-6.7257265)" /> - <radialGradient - cx="4.1993008" - cy="2.3117516" - r="7.9999995" - fx="4.1993008" - fy="2.3117516" - id="radialGradient8215" - xlink:href="#linearGradient7056-0-9" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.1767008,1.0376968,-0.76927742,0.87232541,1.0363585,-3.2771526)" /> - <radialGradient - cx="141.74666" - cy="206.42612" - r="78.728165" - fx="141.74666" - fy="206.42612" - id="radialGradient8211" - xlink:href="#linearGradient4035-5" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.12708936,-0.00212891,9.4059438e-4,0.12249323,-10.298148,-14.500064)" /> - <radialGradient - cx="142.62215" - cy="191.85428" - r="78.728165" - fx="142.62215" - fy="191.85428" - id="radialGradient8208" - xlink:href="#linearGradient4035-5" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.06165082,0,0,-0.0653716,-1.0370648,17.524179)" /> - <radialGradient - cx="24" - cy="42" - r="21" - fx="24" - fy="42" - id="radialGradient8201" - xlink:href="#linearGradient6310-8-2" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.33333332,0,0,0.14285711,6.5e-7,8.0000015)" /> - <radialGradient - cx="127.31733" - cy="143.82751" - r="78.728165" - fx="127.31733" - fy="143.82751" - id="radialGradient8198" - xlink:href="#linearGradient8105" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.04179653,-0.01388393,0.00338688,0.03797545,-0.54190607,-0.79595187)" /> - <linearGradient - id="linearGradient6310-8-2"> - <stop - id="stop6312-6-2" - style="stop-color:#ffffff;stop-opacity:1" - offset="0" /> - <stop - id="stop6314-6-0" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3484-2-0"> - <stop - id="stop3486-2-4" - style="stop-color:#969696;stop-opacity:1" - offset="0" /> - <stop - id="stop3488-0-2" - style="stop-color:#b4b4b4;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient7056-0-9"> - <stop - id="stop7064-4-6" - style="stop-color:#e6e6e6;stop-opacity:1" - offset="0" /> - <stop - id="stop7060-2-1" - style="stop-color:#c8c8c8;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient2215-9-0"> - <stop - id="stop2223-6-4" - style="stop-color:#7a7a7a;stop-opacity:1" - offset="0" /> - <stop - id="stop2219-1-2" - style="stop-color:#474747;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient4035-5"> - <stop - id="stop4037-8" - style="stop-color:#f5f5f5;stop-opacity:1" - offset="0" /> - <stop - id="stop4039-7" - style="stop-color:#e7e7e7;stop-opacity:1" - offset="0.47025558" /> - <stop - id="stop4041-0" - style="stop-color:#8c8c8c;stop-opacity:1" - offset="0.69348532" /> - <stop - id="stop4043-4" - style="stop-color:#dddddd;stop-opacity:1" - offset="0.83542866" /> - <stop - id="stop4045-8" - style="stop-color:#a8a8a8;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient6309-1"> - <stop - id="stop6311-0" - style="stop-color:#000000;stop-opacity:1" - offset="0" /> - <stop - id="stop6313-7" - style="stop-color:#bbbbbb;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient4236-0-1"> - <stop - id="stop4238-4-4" - style="stop-color:#eeeeee;stop-opacity:1" - offset="0" /> - <stop - id="stop4240-3-9" - style="stop-color:#eeeeee;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient8105"> - <stop - id="stop8107" - style="stop-color:#f5f5f5;stop-opacity:1" - offset="0" /> - <stop - id="stop8109" - style="stop-color:#e7e7e7;stop-opacity:1" - offset="0.25027597" /> - <stop - id="stop8111" - style="stop-color:#8c8c8c;stop-opacity:1" - offset="0.69348532" /> - <stop - id="stop8113" - style="stop-color:#dddddd;stop-opacity:1" - offset="0.83542866" /> - <stop - id="stop8115" - style="stop-color:#a8a8a8;stop-opacity:1" - offset="1" /> - </linearGradient> - </defs> - <g - id="g3119"> - <path - d="m 15.49723,12.5 c -0.304165,2.535038 -0.22639,2.983925 -0.777167,2.983925 -0.21573,0.03617 -8.698317,0 -13.470165,0 -0.598135,0 -0.447005,0.04932 -0.749898,-2.983925 4.8141597,0.139771 10.032192,-0.370561 14.99723,0 z" - id="rect2992-5" - style="fill:url(#linearGradient8226);fill-opacity:1;fill-rule:nonzero;stroke:#353537;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - <path - d="m 2,13 10,0 0,2 L 2.2255647,15 2,13 z" - id="rect9146-5" - style="fill:url(#linearGradient8223);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:2.40899992;marker:none;visibility:visible;display:inline;overflow:visible" /> - <path - d="m 2.2255639,14.999999 c 0,0 -0.1503759,-1.442332 -0.1503759,-1.442332 1.0179428,1.143093 4.7514263,1.442332 7.3252263,1.442332 0,0 -7.1748504,0 -7.1748504,0 z" - id="path9148-2" - style="opacity:0.81142853;fill:url(#linearGradient8220);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <path - d="M 1.4897093,1.4657632 0.514749,12.488885 C 0.505,12.488885 0.5,12.491788 0.5,12.499999 l 14.999999,0 c 0,-0.0082 -0.005,-0.01111 -0.01475,-0.01111 L 14.479867,1.2478485 c 0,0 -0.187746,-0.74340135 -0.909635,-0.74340135 -0.688148,0 -10.5350353,-0.004447 -11.2600103,-0.004447 -0.75868,0 -0.8205124,0.96576325 -0.8205124,0.96576325 z" - id="rect2990-9" - style="fill:url(#radialGradient8215);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient8217);stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - <path - d="m 1.4762913,12.5 13.0474177,0" - id="path2215-0" - style="opacity:0.7;fill:none;stroke:#ffffff;stroke-width:0.99999988;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;display:inline" /> - <path - d="M 9.875,1.03125 C 9.5425325,1.03125 9.2896361,1.1933561 9.28125,1.4375 7.5462296,4.2547045 3.7159646,3.3310272 2.1875,6.6875 2.107281,7.5502069 2.3133647,8.6701052 3.09375,9.375 4.23596,10.40677 6.1379859,11.041207 8.15625,11 12.321615,10.914959 15.990924,8.1997572 12.8125,4.65625 12.794904,4.4114056 12.5625,1.4375 12.5625,1.4375 12.545264,1.1967161 12.267601,1.03125 11.9375,1.03125 l -2.0625,0 z m -0.03125,0.4375 2.0625,0 c 0.119904,0 0.214005,0.027064 0.21875,0.09375 L 11.6875,4.65625 C 15.160987,7.4613605 11.945518,9.7796633 8.25,9.90625 4.8918537,10.021279 2.9699492,8.847871 3.15625,6.84375 4.3344434,3.466723 8.863656,5.0817755 9.625,1.5625 9.6054609,1.4997867 9.7265155,1.46875 9.84375,1.46875 z" - id="path8117" - style="fill:url(#radialGradient8211);fill-opacity:1" /> - <path - d="M 7.9999249,8.9999497 C 7.3338259,9.003979 6.6178906,8.7647964 6.2182574,8.2045172 5.8878761,7.7376942 5.9413392,7.042109 6.3494314,6.63805 6.8327879,6.1198432 7.5866528,5.9505056 8.2718727,6.0129013 8.8789744,6.0691126 9.5104356,6.3443475 9.8353698,6.8829643 10.086844,7.3170303 10.047208,7.9041627 9.7164741,8.2868807 9.3098043,8.7947936 8.6299115,8.9998014 7.9999249,8.9999497 z m 0.033529,-2.05954 C 7.5676631,6.950996 7.0685483,6.9372219 6.6531228,7.1810992 6.3786983,7.3719617 6.3550571,7.7834456 6.5399472,8.0446579 6.791609,8.4367198 7.2662569,8.6084808 7.7086272,8.6682148 8.2791121,8.7399813 8.9061994,8.6113728 9.334753,8.2097584 9.5762055,7.9697526 9.6865747,7.5362831 9.4461096,7.2585712 9.2078802,7.0082999 8.8377224,6.985091 8.5141086,6.9541818 8.3541203,6.9436237 8.1937276,6.9420228 8.0334539,6.9404097 z" - id="path9400" - style="fill:url(#radialGradient8208);fill-opacity:1;fill-rule:nonzero;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - <g - transform="matrix(-0.04685826,0,0,0.07750161,4.3055193,-2.991086)" - id="g9436"> - <path - d="m 37.925296,187.15033 c 0.31446,3.45855 5.02375,6.27902 10.51419,6.27902 5.48794,0 9.6432,-2.82047 9.27634,-6.27902 -0.36437,-3.44025 -5.07117,-6.21806 -10.5067,-6.21806 -5.43803,0.002 -9.59329,2.77781 -9.28383,6.21806 l 0,0 z" - id="path9438" - style="fill:#535353;fill-opacity:1" /> - </g> - <g - transform="matrix(-0.04685826,0,0,0.07750161,16.205519,-2.991086)" - id="g9496"> - <path - d="m 37.925296,187.15033 c 0.31446,3.45855 5.02375,6.27902 10.51419,6.27902 5.48794,0 9.6432,-2.82047 9.27634,-6.27902 -0.36437,-3.44025 -5.07117,-6.21806 -10.5067,-6.21806 -5.43803,0.002 -9.59329,2.77781 -9.28383,6.21806 l 0,0 z" - id="path9498" - style="fill:#535353;fill-opacity:1" /> - </g> - <rect - width="14" - height="2" - x="1" - y="13" - id="rect6300-3-2" - style="opacity:0.2;fill:url(#radialGradient8201);fill-opacity:1;stroke:none" /> - <path - d="M 3.5490052,1.2794204 C 3.5821222,1.4638603 3.5033392,1.668064 3.4765882,1.8577602 3.3642225,2.4379547 3.248083,3.0174209 3.1387788,3.5982061 3.0364568,3.976483 3.4034434,3.822574 3.4487925,3.8610185 3.9285086,3.6778743 4.4037673,3.4775049 4.8813544,3.2862237 5.3796101,3.0823738 5.8809457,2.8849464 6.3772748,2.677079 6.6902572,2.4909307 6.7820665,2.323762 6.8635898,2.0428768 6.9084532,1.7990145 6.9138614,1.5262921 6.7919595,1.3034032 6.7139126,1.1493881 6.5435087,1.0841579 6.379809,1.1073783 c -0.8675476,0 -1.7350953,0 -2.6026429,0 L 3.5490052,1.2794204 z m 0.588988,0.4439693 c 0.5921265,0 1.184253,-1e-7 1.7763795,-1e-7 0.1871044,0.070513 0.054625,0.2194806 -0.083295,0.2325189 C 5.229708,2.1997992 4.6283387,2.4436898 4.0269694,2.6875804 3.8824333,2.7420385 3.8720481,2.5203138 3.9157358,2.4262942 c 0.037079,-0.2075185 0.074157,-0.415037 0.1112359,-0.6225555 0.041217,-0.021125 0.065659,-0.065363 0.1110215,-0.080349 z" - id="path8125" - style="fill:url(#radialGradient8198);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - </g> - <path - d="M 12.5,5.4999998 8.0596782,10.5 3.5,5.4999998 l 2,0 L 5.5,0.5 l 5,0 0,4.9999998 2,0 z" - id="path3288" - style="fill:url(#linearGradient3374);fill-opacity:1;fill-rule:nonzero;stroke:#699536;stroke-width:0.99829447;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" /> -</svg> diff --git a/rapid/glade3/rapid-photo-downloader-jobcode.svg b/rapid/glade3/rapid-photo-downloader-jobcode.svg deleted file mode 100644 index 3096ee2..0000000 --- a/rapid/glade3/rapid-photo-downloader-jobcode.svg +++ /dev/null @@ -1,265 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://web.resource.org/cc/" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="48" - height="48" - id="svg1306" - sodipodi:version="0.32" - inkscape:version="0.45" - sodipodi:docbase="/home/dobey/Projects/gnome-icon-theme/scalable/actions" - sodipodi:docname="insert-text.svg" - inkscape:export-filename="/home/lapo/Desktop/insert-link-16.png" - inkscape:export-xdpi="90.000031" - inkscape:export-ydpi="90.000031" - version="1.0" - inkscape:output_extension="org.inkscape.output.svg.inkscape"> - <defs - id="defs1308"> - <linearGradient - id="linearGradient3384"> - <stop - style="stop-color:white;stop-opacity:1" - offset="0" - id="stop3386" /> - <stop - style="stop-color:#eeeeec;stop-opacity:1" - offset="1" - id="stop3388" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3284"> - <stop - style="stop-color:#ce5c00;stop-opacity:1;" - offset="0" - id="stop3286" /> - <stop - style="stop-color:#f57900;stop-opacity:1" - offset="1" - id="stop3288" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3276"> - <stop - style="stop-color:white;stop-opacity:1;" - offset="0" - id="stop3278" /> - <stop - style="stop-color:white;stop-opacity:0;" - offset="1" - id="stop3280" /> - </linearGradient> - <linearGradient - id="linearGradient3269"> - <stop - id="stop3271" - offset="0" - style="stop-color:#fecb81;stop-opacity:1" /> - <stop - id="stop3273" - offset="1" - style="stop-color:#fcaf3e;stop-opacity:1" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient3188"> - <stop - style="stop-color:black;stop-opacity:1;" - offset="0" - id="stop3190" /> - <stop - style="stop-color:black;stop-opacity:0;" - offset="1" - id="stop3192" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3188" - id="radialGradient3368" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1,0,0,0.194656,0,34.62977)" - cx="23.8125" - cy="43" - fx="23.8125" - fy="43" - r="16.375" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3269" - id="linearGradient3370" - gradientUnits="userSpaceOnUse" - x1="22.750002" - y1="40.000755" - x2="22.750002" - y2="37.409679" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3284" - id="linearGradient3372" - gradientUnits="userSpaceOnUse" - x1="17.25" - y1="41.147343" - x2="17.25" - y2="36.103439" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3276" - id="linearGradient3374" - gradientUnits="userSpaceOnUse" - x1="18.875" - y1="35.183414" - x2="18.875" - y2="43.978611" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3384" - id="radialGradient3390" - cx="24.842901" - cy="30.77803" - fx="24.842901" - fy="30.77803" - r="22.999998" - gradientTransform="matrix(1.48996,0,0,0.657586,-13.01493,5.2238)" - gradientUnits="userSpaceOnUse" /> - </defs> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#666" - borderopacity="1" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="1.4142136" - inkscape:cx="-71.544452" - inkscape:cy="31.751968" - inkscape:current-layer="layer1" - showgrid="true" - inkscape:grid-bbox="true" - inkscape:document-units="px" - fill="#fcaf3e" - stroke="#555753" - showguides="true" - inkscape:guide-bbox="true" - inkscape:window-width="1085" - inkscape:window-height="760" - inkscape:window-x="214" - inkscape:window-y="175" - inkscape:showpageshadow="false" - showborder="true" - gridspacingx="0.5px" - gridspacingy="0.5px" - gridempspacing="2" - inkscape:grid-points="true" - width="48px" - height="48px" /> - <metadata - id="metadata1311"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title>Insert Text</dc:title> - <dc:creator> - <cc:Agent> - <dc:title>Lapo Calamandrei</dc:title> - </cc:Agent> - </dc:creator> - <dc:source>http://www.gnome.org</dc:source> - <dc:contributor> - <cc:Agent> - <dc:title /> - </cc:Agent> - </dc:contributor> - <cc:license - rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" /> - <dc:subject> - <rdf:Bag> - <rdf:li>insert</rdf:li> - <rdf:li>text</rdf:li> - <rdf:li>generic</rdf:li> - </rdf:Bag> - </dc:subject> - </cc:Work> - <cc:License - rdf:about="http://creativecommons.org/licenses/GPL/2.0/"> - <cc:permits - rdf:resource="http://web.resource.org/cc/Reproduction" /> - <cc:permits - rdf:resource="http://web.resource.org/cc/Distribution" /> - <cc:requires - rdf:resource="http://web.resource.org/cc/Notice" /> - <cc:permits - rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> - <cc:requires - rdf:resource="http://web.resource.org/cc/ShareAlike" /> - <cc:requires - rdf:resource="http://web.resource.org/cc/SourceCode" /> - </cc:License> - </rdf:RDF> - </metadata> - <g - id="layer1" - inkscape:label="Layer 1" - inkscape:groupmode="layer"> - <path - sodipodi:type="arc" - style="opacity:0.2;color:black;fill:url(#radialGradient3368);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" - id="path3360" - sodipodi:cx="23.8125" - sodipodi:cy="43" - sodipodi:rx="16.375" - sodipodi:ry="3.1875" - d="M 40.1875 43 A 16.375 3.1875 0 1 1 7.4375,43 A 16.375 3.1875 0 1 1 40.1875 43 z" - transform="matrix(1.068703,0,0,1.411765,-2.9485,-18.2059)" /> - <g - id="g3362"> - <path - sodipodi:nodetypes="cccc" - id="path3364" - d="M 15.500002,35.500004 L 32.500012,35.500004 L 24.000007,44.499996 L 15.500002,35.500004 z " - style="color:black;fill:url(#linearGradient3370);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3372);stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dashoffset:1.4;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" /> - <path - transform="translate(0,2)" - d="M 17.8125,34.5 L 24,41.0625 L 30.1875,34.5 L 17.8125,34.5 z " - id="path3366" - style="opacity:0.7;color:black;fill:none;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3374);stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;marker:none;marker-start:none;marker-mid:none;marker-end:none;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1;visibility:visible;display:inline;overflow:visible" - inkscape:original="M 15.5 33.5 L 24 42.5 L 32.5 33.5 L 15.5 33.5 z " - inkscape:radius="-1" - sodipodi:type="inkscape:offset" /> - </g> - <rect - style="opacity:1;fill:url(#radialGradient3390);fill-opacity:1;stroke:#888a85;stroke-width:0.99999994;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1" - id="rect3376" - width="44.999996" - height="24.000004" - x="1.5" - y="6.5" - rx="1.03125" - ry="1.03125" /> - <path - style="font-size:17.50059891px;font-style:normal;font-weight:bold;fill:#2e3436;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Bitstream Vera Sans" - d="M 15.64186,22.622828 L 10.612,22.622828 L 9.81825,24.999972 L 6.58477,24.999972 L 11.20522,11.951901 L 15.04028,11.951901 L 19.66073,24.999972 L 16.42725,24.999972 L 15.64186,22.622828 M 11.4141,19.983237 L 14.8314,19.983237 L 13.12692,15.010725 L 11.4141,19.983237 M 26.15275,22.981148 C 26.79332,22.98115 27.2807,22.736444 27.61492,22.24703 C 27.95469,21.757621 28.12458,21.046809 28.12459,20.114592 C 28.12458,19.182385 27.95469,18.471573 27.61492,17.982154 C 27.2807,17.49275 26.79332,17.248045 26.15275,17.248037 C 25.51218,17.248045 24.83172,17.495663 24.48638,17.990894 C 24.14659,18.480313 23.9767,19.188211 23.97671,20.114592 C 23.9767,21.040983 24.14659,21.751795 24.48638,22.24703 C 24.83172,22.736444 25.51218,22.98115 26.15275,22.981148 M 23.97671,16.645011 C 24.38889,16.07404 25.03315,15.654544 25.53447,15.386523 C 26.03577,15.112696 26.61228,14.975777 27.264,14.975767 C 28.41702,14.975777 29.36394,15.456449 30.10479,16.417785 C 30.84561,17.373311 31.21602,18.605579 31.21603,20.114592 C 31.21602,21.623615 30.84561,22.858797 30.10479,23.820139 C 29.36394,24.775658 28.41702,25.253417 27.264,25.253417 C 26.61228,25.253417 26.03577,25.119412 25.53447,24.851401 C 25.03315,24.577563 24.38889,24.155155 23.97671,23.584172 L 23.97671,24.999972 L 20.98553,24.999972 L 20.98553,12.088812 L 23.97671,12.088812 L 23.97671,16.645011 M 41.00002,15.517616 L 41.00002,18.069549 C 40.59339,17.77824 40.18399,17.562666 39.7718,17.422826 C 39.36517,17.283002 38.94184,17.213087 38.50181,17.213078 C 37.66628,17.213087 37.01457,17.469445 36.54668,17.982154 C 36.08435,18.489052 35.85319,19.199864 35.85319,20.114592 C 35.85319,21.02933 36.08435,21.743055 36.54668,22.255769 C 37.01457,22.762662 37.66628,23.016107 38.50181,23.016105 C 38.96969,23.016107 39.41252,22.943278 39.83029,22.797618 C 40.25361,22.651962 40.64353,22.436388 41.00002,22.150895 L 41.00002,24.711568 C 40.53212,24.892184 40.05587,25.02619 39.57128,25.113585 C 39.09224,25.206807 38.61042,25.253417 38.12582,25.253417 C 36.43806,25.253417 35.11793,24.801877 34.16543,23.898795 C 33.21294,22.989889 32.73669,21.72849 32.73669,20.114592 C 32.73669,18.500704 33.21294,17.242218 34.16543,16.339129 C 35.11793,15.430231 36.43806,14.975777 38.12582,14.975767 C 38.61599,14.975777 39.09781,15.022388 39.57128,15.115599 C 40.0503,15.203003 40.52655,15.337009 41.00002,15.517616" - id="text3378" - sodipodi:nodetypes="ccccccccccccccsssssssccsssssssccccccccsssssssccsssssssc" /> - <rect - style="opacity:1;fill:none;fill-opacity:1;stroke:white;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:1.4;stroke-opacity:1" - id="rect3382" - width="42.97773" - height="22.000011" - x="2.5" - y="7.5" - rx="0" - ry="0" /> - </g> -</svg> diff --git a/rapid/glade3/rapid-photo-downloader.svg b/rapid/glade3/rapid-photo-downloader.svg deleted file mode 100644 index a1e9885..0000000 --- a/rapid/glade3/rapid-photo-downloader.svg +++ /dev/null @@ -1,2673 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - width="128" - height="128" - id="svg7854" - sodipodi:version="0.32" - inkscape:version="0.47 r22583" - version="1.0" - sodipodi:docname="rapid-photo-downloader.svg" - inkscape:output_extension="org.inkscape.output.svg.inkscape" - inkscape:export-filename="/home/damon/rapid/data/icons/16x16/apps/rapid-photo-downloader.png" - inkscape:export-xdpi="30" - inkscape:export-ydpi="30" - sodipodi:modified="TRUE"> - <sodipodi:namedview - id="base" - pagecolor="#ffffff" - bordercolor="#e0e0e0" - borderopacity="1" - gridtolerance="10000" - guidetolerance="10" - objecttolerance="10" - inkscape:pageopacity="0.0" - inkscape:pageshadow="2" - inkscape:zoom="7.7132076" - inkscape:cx="62.076472" - inkscape:cy="102.31383" - inkscape:document-units="px" - inkscape:current-layer="layer2" - width="48px" - height="48px" - inkscape:showpageshadow="false" - inkscape:window-width="1920" - inkscape:window-height="1089" - inkscape:window-x="0" - inkscape:window-y="24" - showgrid="false" - inkscape:window-maximized="1" /> - <defs - id="defs7856"> - <inkscape:perspective - sodipodi:type="inkscape:persp3d" - inkscape:vp_x="0 : 24 : 1" - inkscape:vp_y="0 : 1000 : 0" - inkscape:vp_z="48 : 24 : 1" - inkscape:persp3d-origin="24 : 16 : 1" - id="perspective3324" /> - <linearGradient - inkscape:collect="always" - id="linearGradient8954"> - <stop - offset="0" - id="stop8956" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop8958" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient8940"> - <stop - offset="0" - id="stop8942" - style="stop-color:#72776c;stop-opacity:1;" /> - <stop - offset="0.5" - id="stop8944" - style="stop-color:#e0e0e0;stop-opacity:1;" /> - <stop - offset="0.75" - id="stop8946" - style="stop-color:#8f9587;stop-opacity:1;" /> - <stop - offset="1" - id="stop8948" - style="stop-color:#c1c1c1;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8928"> - <stop - offset="0" - id="stop8930" - style="stop-color:#ffffff;stop-opacity:1;" /> - <stop - offset="1" - id="stop8932" - style="stop-color:#cccccc;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8895"> - <stop - offset="0" - id="stop8899" - style="stop-color:#204a87;stop-opacity:0;" /> - <stop - offset="0.5" - id="stop8905" - style="stop-color:#204a87;stop-opacity:0;" /> - <stop - offset="1" - id="stop8897" - style="stop-color:#204a87;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8735"> - <stop - offset="0" - id="stop8737" - style="stop-color:#ffffff;stop-opacity:1;" /> - <stop - offset="1" - id="stop8739" - style="stop-color:#8b8b8b;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8700"> - <stop - offset="0" - id="stop8702" - style="stop-color:#babdb6;stop-opacity:1;" /> - <stop - offset="0.5" - id="stop8704" - style="stop-color:#e0e0e0;stop-opacity:1;" /> - <stop - offset="0.75" - id="stop8706" - style="stop-color:#c0c3bb;stop-opacity:1;" /> - <stop - offset="1" - id="stop8708" - style="stop-color:#dbdbdb;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8680"> - <stop - offset="0" - id="stop8682" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="0.5" - id="stop8688" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop8684" - style="stop-color:#afafaf;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8670"> - <stop - offset="0" - id="stop8672" - style="stop-color:#ffffff;stop-opacity:1;" /> - <stop - offset="0.32142857" - id="stop8950" - style="stop-color:#ebebeb;stop-opacity:1;" /> - <stop - offset="0.5" - id="stop8678" - style="stop-color:#909090;stop-opacity:1;" /> - <stop - offset="1" - id="stop8674" - style="stop-color:#fafafa;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8658"> - <stop - offset="0" - id="stop8660" - style="stop-color:#ffffff;stop-opacity:1;" /> - <stop - offset="1" - id="stop8662" - style="stop-color:#a5a5a5;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8644"> - <stop - offset="0" - id="stop8646" - style="stop-color:#717171;stop-opacity:1;" /> - <stop - offset="1" - id="stop8648" - style="stop-color:#000000;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient8632"> - <stop - offset="0" - id="stop8634" - style="stop-color:#2e3436;stop-opacity:1;" /> - <stop - offset="0.5" - id="stop8640" - style="stop-color:#6b797d;stop-opacity:1;" /> - <stop - offset="1" - id="stop8636" - style="stop-color:#2e3436;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient2230"> - <stop - offset="0" - id="stop2232" - style="stop-color:#eeeeee;stop-opacity:1;" /> - <stop - offset="1.0000000" - id="stop2234" - style="stop-color:#a2a2a2;stop-opacity:1.0000000;" /> - </linearGradient> - <linearGradient - id="linearGradient3301"> - <stop - offset="0.0000000" - id="stop3303" - style="stop-color:#ffffff;stop-opacity:1.0000000;" /> - <stop - offset="1.0000000" - id="stop3305" - style="stop-color:#cbcbcb;stop-opacity:1.0000000;" /> - </linearGradient> - <linearGradient - id="linearGradient5895"> - <stop - offset="0" - id="stop5897" - style="stop-color:#888a85;stop-opacity:1;" /> - <stop - offset="1" - id="stop5899" - style="stop-color:#ffffff;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient5819"> - <stop - offset="0" - id="stop5821" - style="stop-color:#ffffff;stop-opacity:1;" /> - <stop - offset="1" - id="stop5823" - style="stop-color:#a5a5a5;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient5776"> - <stop - offset="0" - id="stop5778" - style="stop-color:#babdb6;stop-opacity:1;" /> - <stop - offset="0.5" - id="stop5784" - style="stop-color:#ffffff;stop-opacity:1;" /> - <stop - offset="0.75" - id="stop5786" - style="stop-color:#c0c3bb;stop-opacity:1;" /> - <stop - offset="1" - id="stop5780" - style="stop-color:#ffffff;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient5759"> - <stop - offset="0" - id="stop5761" - style="stop-color:#898b86;stop-opacity:1;" /> - <stop - offset="0.16071428" - id="stop5767" - style="stop-color:#b3b3b0;stop-opacity:1;" /> - <stop - offset="0.56037414" - id="stop5769" - style="stop-color:#71746f;stop-opacity:1;" /> - <stop - offset="1" - id="stop5763" - style="stop-color:#555753;stop-opacity:1;" /> - </linearGradient> - <clipPath - id="clipPath8924" - clipPathUnits="userSpaceOnUse"> - <rect - id="rect8926" - x="28.475426" - y="29" - width="8.17838" - height="5" - ry="1.1324512" - rx="1.1324512" - style="color:#000000;fill:none;stroke:#ffffff;stroke-width:0.1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:10;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> - </clipPath> - <radialGradient - inkscape:collect="always" - id="radialGradient8960" - r="11.5" - gradientTransform="matrix(1,0,0,0.5,0,17.9375)" - cx="7.375" - cy="35.875" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient8954" - fy="35.875" - fx="7.375" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4140" - x1="19.864109" - x2="37.166458" - y1="17.906694" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4134" - y2="41.594772" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4131" - x1="19.500004" - x2="19.500004" - y1="10.687498" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4125" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4123" - x1="19.500004" - x2="19.500004" - y1="10.687498" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4117" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4107" - x1="25.044359" - x2="25.791954" - gradientTransform="matrix(0.919614,0,0,0.919598,1.768501,2.487437)" - y1="20.624216" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4109" - y2="28.863333" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4103" - x1="22.937496" - x2="22.937496" - y1="29.93535" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4097" - y2="40.689152" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4085" - x1="9.1648445" - x2="9.125" - gradientTransform="translate(20,0)" - y1="4.4145103" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4081" - x1="9.1648445" - x2="9.125" - gradientTransform="translate(16,0)" - y1="4.4145103" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4077" - x1="9.1648445" - x2="9.125" - gradientTransform="translate(12,0)" - y1="4.4145103" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4073" - x1="9.1648445" - x2="9.125" - gradientTransform="translate(8,0)" - y1="4.4145103" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4069" - x1="9.1648445" - x2="9.125" - gradientTransform="translate(4,0)" - y1="4.4145103" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4065" - x1="9.1648445" - x2="9.125" - y1="4.4145103" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4057" - x1="22.509596" - x2="31.603951" - gradientTransform="translate(-2.2e-6,0)" - y1="1.9792649" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4051" - y2="58.909889" /> - <linearGradient - inkscape:collect="always" - id="linearGradient5027" - x1="302.85715" - x2="302.85715" - gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" - y1="366.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5048" - y2="609.50507" /> - <linearGradient - id="linearGradient5048"> - <stop - offset="0" - id="stop5050" - style="stop-color:black;stop-opacity:0;" /> - <stop - offset="0.5" - id="stop5056" - style="stop-color:black;stop-opacity:1;" /> - <stop - offset="1" - id="stop5052" - style="stop-color:black;stop-opacity:0;" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - id="radialGradient5029" - r="117.14286" - gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <linearGradient - inkscape:collect="always" - id="linearGradient5060"> - <stop - offset="0" - id="stop5062" - style="stop-color:black;stop-opacity:1;" /> - <stop - offset="1" - id="stop5064" - style="stop-color:black;stop-opacity:0;" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - id="radialGradient5031" - r="117.14286" - gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4051"> - <stop - offset="0" - id="stop4053" - style="stop-color:white;stop-opacity:1;" /> - <stop - offset="1" - id="stop4055" - style="stop-color:white;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4059"> - <stop - offset="0" - id="stop4061" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop4063" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4097"> - <stop - offset="0" - id="stop4099" - style="stop-color:black;stop-opacity:1;" /> - <stop - offset="1" - id="stop4101" - style="stop-color:black;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4109"> - <stop - offset="0" - id="stop4111" - style="stop-color:white;stop-opacity:1;" /> - <stop - offset="1" - id="stop4113" - style="stop-color:white;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient4117"> - <stop - offset="0" - id="stop4119" - style="stop-color:white;stop-opacity:1;" /> - <stop - offset="1" - id="stop4121" - style="stop-color:white;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient4125"> - <stop - offset="0" - id="stop4127" - style="stop-color:black;stop-opacity:1;" /> - <stop - offset="1" - id="stop4129" - style="stop-color:black;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4134"> - <stop - offset="0" - id="stop4136" - style="stop-color:#3465a4;stop-opacity:1;" /> - <stop - offset="1" - id="stop4138" - style="stop-color:#2d578e;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13444" - x1="302.85715" - y1="366.64789" - gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" - x2="302.85715" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5048" - y2="609.50507" /> - <radialGradient - inkscape:collect="always" - id="radialGradient13446" - r="117.14286" - gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <radialGradient - inkscape:collect="always" - id="radialGradient13448" - r="117.14286" - gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13450" - x1="19.864109" - y1="17.906694" - x2="37.166458" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4134" - y2="41.594772" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13452" - x1="19.500004" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4125" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13454" - x1="19.500004" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4117" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13456" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13458" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(4,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13460" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(8,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13462" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(12,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13464" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(16,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13466" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(20,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13468" - x1="22.509596" - y1="1.9792649" - gradientTransform="translate(-2.2e-6,0)" - x2="31.603951" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4051" - y2="58.909889" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13470" - x1="22.937496" - y1="29.93535" - x2="22.937496" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4097" - y2="40.689152" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13472" - x1="25.044359" - y1="20.624216" - gradientTransform="matrix(0.919614,0,0,0.919598,1.768501,2.487437)" - x2="25.791954" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4109" - y2="28.863333" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13512" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(16,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13556" - x1="302.85715" - y1="366.64789" - gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" - x2="302.85715" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5048" - y2="609.50507" /> - <radialGradient - inkscape:collect="always" - id="radialGradient13558" - r="117.14286" - gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <radialGradient - inkscape:collect="always" - id="radialGradient13560" - r="117.14286" - gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13562" - x1="19.864109" - y1="17.906694" - x2="37.166458" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4134" - y2="41.594772" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13564" - x1="19.500004" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4125" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13566" - x1="19.500004" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4117" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13568" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13570" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(4,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13572" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(8,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13574" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(12,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13576" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(16,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13578" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(20,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13580" - x1="22.509596" - y1="1.9792649" - gradientTransform="translate(-2.2e-6,0)" - x2="31.603951" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4051" - y2="58.909889" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13582" - x1="22.937496" - y1="29.93535" - x2="22.937496" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4097" - y2="40.689152" /> - <linearGradient - inkscape:collect="always" - id="linearGradient13584" - x1="25.044359" - y1="20.624216" - gradientTransform="matrix(0.919614,0,0,0.919598,1.768501,2.487437)" - x2="25.791954" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4109" - y2="28.863333" /> - <linearGradient - id="linearGradient5048-553"> - <stop - offset="0" - id="stop22302" - style="stop-color:#000000;stop-opacity:0;" /> - <stop - offset="0.5" - id="stop22304" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop22306" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4134-983"> - <stop - offset="0" - id="stop22322" - style="stop-color:#346500;stop-opacity:1;" /> - <stop - offset="1" - id="stop22324" - style="stop-color:#2d5700;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-435"> - <stop - offset="0" - id="stop22340" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop22342" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-801"> - <stop - offset="0" - id="stop22346" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop22348" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-394"> - <stop - offset="0" - id="stop22352" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop22354" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-756"> - <stop - offset="0" - id="stop22358" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop22360" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-158"> - <stop - offset="0" - id="stop22364" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop22366" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-607"> - <stop - offset="0" - id="stop22370" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop22372" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4097-506"> - <stop - offset="0" - id="stop22382" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop22384" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4109-629"> - <stop - offset="0" - id="stop22388" - style="stop-color:#ffff00;stop-opacity:1;" /> - <stop - offset="1" - id="stop22390" - style="stop-color:#ffff00;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13444-386" - y2="609.50507" - y1="366.64789" - gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" - x2="302.85715" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5048-640" - x1="302.85715" /> - <linearGradient - id="linearGradient5048-640"> - <stop - offset="0" - id="stop22977" - style="stop-color:#000000;stop-opacity:0;" /> - <stop - offset="0.5" - id="stop22979" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop22981" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - id="radialGradient13446-878" - r="117.14286" - gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060-189" - fy="486.64789" - fx="605.71429" /> - <linearGradient - inkscape:collect="always" - id="linearGradient5060-189"> - <stop - offset="0" - id="stop22985" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop22987" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - id="radialGradient13448-893" - r="117.14286" - gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060-614" - fy="486.64789" - fx="605.71429" /> - <linearGradient - inkscape:collect="always" - id="linearGradient5060-614"> - <stop - offset="0" - id="stop22991" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop22993" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13450-291" - y2="41.594772" - y1="17.906694" - x2="37.166458" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4134-263" - x1="19.864109" /> - <linearGradient - id="linearGradient4134-263"> - <stop - offset="0" - id="stop22997" - style="stop-color:#346500;stop-opacity:1;" /> - <stop - offset="1" - id="stop22999" - style="stop-color:#2d5700;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13452-630" - y2="2.1249981" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4125-44" - x1="19.500004" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4125-44"> - <stop - offset="0" - id="stop23003" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23005" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13454-799" - y2="2.1249981" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4117-937" - x1="19.500004" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4117-937"> - <stop - offset="0" - id="stop23009" - style="stop-color:#ffff00;stop-opacity:1;" /> - <stop - offset="1" - id="stop23011" - style="stop-color:#ffff00;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13456-439" - y2="11.352011" - y1="4.4145103" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059-765" - x1="9.1648445" /> - <linearGradient - id="linearGradient4059-765"> - <stop - offset="0" - id="stop23015" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop23017" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13458-70" - y2="11.352011" - y1="4.4145103" - gradientTransform="translate(4,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059-23" - x1="9.1648445" /> - <linearGradient - id="linearGradient4059-23"> - <stop - offset="0" - id="stop23021" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop23023" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13460-46" - y2="11.352011" - y1="4.4145103" - gradientTransform="translate(8,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059-602" - x1="9.1648445" /> - <linearGradient - id="linearGradient4059-602"> - <stop - offset="0" - id="stop23027" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop23029" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13462-772" - y2="11.352011" - y1="4.4145103" - gradientTransform="translate(12,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059-683" - x1="9.1648445" /> - <linearGradient - id="linearGradient4059-683"> - <stop - offset="0" - id="stop23033" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop23035" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13464-861" - y2="11.352011" - y1="4.4145103" - gradientTransform="translate(16,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059-588" - x1="9.1648445" /> - <linearGradient - id="linearGradient4059-588"> - <stop - offset="0" - id="stop23039" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop23041" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13466-479" - y2="11.352011" - y1="4.4145103" - gradientTransform="translate(20,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059-524" - x1="9.1648445" /> - <linearGradient - id="linearGradient4059-524"> - <stop - offset="0" - id="stop23045" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop23047" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13468-669" - y2="58.909889" - y1="1.9792649" - gradientTransform="translate(-2.2e-6,0)" - x2="31.603951" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4051-546" - x1="22.509596" /> - <linearGradient - inkscape:collect="always" - id="linearGradient4051-546"> - <stop - offset="0" - id="stop23051" - style="stop-color:#ffff00;stop-opacity:1;" /> - <stop - offset="1" - id="stop23053" - style="stop-color:#ffff00;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13470-480" - y2="40.689152" - y1="29.93535" - x2="22.937496" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4097-418" - x1="22.937496" /> - <linearGradient - id="linearGradient4097-418"> - <stop - offset="0" - id="stop23057" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23059" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient13472-992" - y2="28.863333" - y1="20.624216" - gradientTransform="matrix(0.919614,0,0,0.919598,1.768501,2.487437)" - x2="25.791954" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4109-10" - x1="25.044359" /> - <linearGradient - id="linearGradient4109-10"> - <stop - offset="0" - id="stop23063" - style="stop-color:#ffff00;stop-opacity:1;" /> - <stop - offset="1" - id="stop23065" - style="stop-color:#ffff00;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient5048-640-390"> - <stop - offset="0" - id="stop23773" - style="stop-color:#000000;stop-opacity:0;" /> - <stop - offset="0.5" - id="stop23775" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23777" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4134-263-738"> - <stop - offset="0" - id="stop23793" - style="stop-color:#340000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23795" - style="stop-color:#2d0000;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-765-0"> - <stop - offset="0" - id="stop23811" - style="stop-color:#ed0000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23813" - style="stop-color:#b10000;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-23-198"> - <stop - offset="0" - id="stop23817" - style="stop-color:#ed0000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23819" - style="stop-color:#b10000;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-602-642"> - <stop - offset="0" - id="stop23823" - style="stop-color:#ed0000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23825" - style="stop-color:#b10000;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-683-70"> - <stop - offset="0" - id="stop23829" - style="stop-color:#ed0000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23831" - style="stop-color:#b10000;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-588-551"> - <stop - offset="0" - id="stop23835" - style="stop-color:#ed0000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23837" - style="stop-color:#b10000;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-524-838"> - <stop - offset="0" - id="stop23841" - style="stop-color:#ed0000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23843" - style="stop-color:#b10000;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4097-418-303"> - <stop - offset="0" - id="stop23853" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23855" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4109-10-161"> - <stop - offset="0" - id="stop23859" - style="stop-color:#ff0000;stop-opacity:1;" /> - <stop - offset="1" - id="stop23861" - style="stop-color:#ff0000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient24431" - x1="302.85715" - y1="366.64789" - gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" - x2="302.85715" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5048" - y2="609.50507" /> - <radialGradient - inkscape:collect="always" - id="radialGradient24433" - r="117.14286" - gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <radialGradient - inkscape:collect="always" - id="radialGradient24435" - r="117.14286" - gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24437" - x1="19.864109" - y1="17.906694" - x2="37.166458" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4134" - y2="41.594772" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24439" - x1="19.500004" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4125" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24441" - x1="19.500004" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4117" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24443" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24445" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(4,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24447" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(8,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24449" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(12,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24451" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(16,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24453" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(20,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24455" - x1="22.509596" - y1="1.9792649" - gradientTransform="translate(-2.2e-6,0)" - x2="31.603951" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4051" - y2="58.909889" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24457" - x1="22.937496" - y1="29.93535" - x2="22.937496" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4097" - y2="40.689152" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24459" - x1="25.044359" - y1="20.624216" - gradientTransform="matrix(0.919614,0,0,0.919598,1.768501,2.487437)" - x2="25.791954" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4109" - y2="28.863333" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24559" - x1="302.85715" - y1="366.64789" - gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" - x2="302.85715" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5048" - y2="609.50507" /> - <radialGradient - inkscape:collect="always" - id="radialGradient24561" - r="117.14286" - gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <radialGradient - inkscape:collect="always" - id="radialGradient24563" - r="117.14286" - gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" - cx="605.71429" - cy="486.64789" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient5060" - fy="486.64789" - fx="605.71429" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24565" - x1="19.864109" - y1="17.906694" - x2="37.166458" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4134" - y2="41.594772" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24567" - x1="19.500004" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4125" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24569" - x1="19.500004" - y1="10.687498" - x2="19.500004" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4117" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24571" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24573" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(4,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24575" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(8,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24577" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(12,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24579" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(16,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24581" - x1="9.1648445" - y1="4.4145103" - gradientTransform="translate(20,0)" - x2="9.125" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4059" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24583" - x1="22.509596" - y1="1.9792649" - gradientTransform="translate(-2.2e-6,0)" - x2="31.603951" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4051" - y2="58.909889" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24585" - x1="22.937496" - y1="29.93535" - x2="22.937496" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4097" - y2="40.689152" /> - <linearGradient - inkscape:collect="always" - id="linearGradient24587" - x1="25.044359" - y1="20.624216" - gradientTransform="matrix(0.919614,0,0,0.919598,1.768501,2.487437)" - x2="25.791954" - gradientUnits="userSpaceOnUse" - xlink:href="#linearGradient4109" - y2="28.863333" /> - <linearGradient - id="linearGradient5048-597"> - <stop - offset="0" - id="stop24888" - style="stop-color:#000000;stop-opacity:0;" /> - <stop - offset="0.5" - id="stop24890" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop24892" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient5060-848"> - <stop - offset="0" - id="stop24896" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop24898" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient5060-139"> - <stop - offset="0" - id="stop24902" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop24904" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4134-168"> - <stop - offset="0" - id="stop24908" - style="stop-color:#346500;stop-opacity:1;" /> - <stop - offset="1" - id="stop24910" - style="stop-color:#2d5700;stop-opacity:1;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient4125-691"> - <stop - offset="0" - id="stop24914" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop24916" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - id="linearGradient4117-994"> - <stop - offset="0" - id="stop24920" - style="stop-color:#ffff00;stop-opacity:1;" /> - <stop - offset="1" - id="stop24922" - style="stop-color:#ffff00;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-309"> - <stop - offset="0" - id="stop24926" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop24928" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-684"> - <stop - offset="0" - id="stop24932" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop24934" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-264"> - <stop - offset="0" - id="stop24938" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop24940" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-61"> - <stop - offset="0" - id="stop24944" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop24946" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-351"> - <stop - offset="0" - id="stop24950" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop24952" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4059-254"> - <stop - offset="0" - id="stop24956" - style="stop-color:#edd400;stop-opacity:1;" /> - <stop - offset="1" - id="stop24958" - style="stop-color:#b19e00;stop-opacity:1;" /> - </linearGradient> - <linearGradient - id="linearGradient4051-764"> - <stop - offset="0" - id="stop24962" - style="stop-color:#9a9a93;stop-opacity:1;" /> - <stop - offset="1" - id="stop24964" - style="stop-color:#ffff00;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4097-965"> - <stop - offset="0" - id="stop24968" - style="stop-color:#000000;stop-opacity:1;" /> - <stop - offset="1" - id="stop24970" - style="stop-color:#000000;stop-opacity:0;" /> - </linearGradient> - <linearGradient - id="linearGradient4109-735"> - <stop - offset="0" - id="stop24974" - style="stop-color:#ffff00;stop-opacity:1;" /> - <stop - offset="1" - id="stop24976" - style="stop-color:#ffff00;stop-opacity:0;" /> - </linearGradient> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient5048" - id="linearGradient25463" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" - x1="302.85715" - y1="366.64789" - x2="302.85715" - y2="609.50507" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient5060" - id="radialGradient25465" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" - cx="605.71429" - cy="486.64789" - fx="605.71429" - fy="486.64789" - r="117.14286" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient5060" - id="radialGradient25467" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" - cx="605.71429" - cy="486.64789" - fx="605.71429" - fy="486.64789" - r="117.14286" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4134" - id="linearGradient25469" - gradientUnits="userSpaceOnUse" - x1="19.864109" - y1="17.906694" - x2="37.166458" - y2="41.594772" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4125" - id="linearGradient25471" - gradientUnits="userSpaceOnUse" - x1="19.500004" - y1="10.687498" - x2="19.500004" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117" - id="linearGradient25473" - gradientUnits="userSpaceOnUse" - x1="19.500004" - y1="10.687498" - x2="19.500004" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25475" - gradientUnits="userSpaceOnUse" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25477" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(4,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25479" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(8,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25481" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(12,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25483" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(16,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25485" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(20,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4051" - id="linearGradient25487" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(-2.2e-6,0)" - x1="22.509596" - y1="1.9792649" - x2="31.603951" - y2="58.909889" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4097" - id="linearGradient25489" - gradientUnits="userSpaceOnUse" - x1="22.937496" - y1="29.93535" - x2="22.937496" - y2="40.689152" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4109" - id="linearGradient25491" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.919614,0,0,0.919598,1.768501,2.487437)" - x1="25.044359" - y1="20.624216" - x2="25.791954" - y2="28.863333" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient5048" - id="linearGradient25739" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" - x1="302.85715" - y1="366.64789" - x2="302.85715" - y2="609.50507" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient5060" - id="radialGradient25741" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" - cx="605.71429" - cy="486.64789" - fx="605.71429" - fy="486.64789" - r="117.14286" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient5060" - id="radialGradient25743" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" - cx="605.71429" - cy="486.64789" - fx="605.71429" - fy="486.64789" - r="117.14286" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4125" - id="linearGradient25747" - gradientUnits="userSpaceOnUse" - x1="19.500004" - y1="10.687498" - x2="19.500004" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117" - id="linearGradient25749" - gradientUnits="userSpaceOnUse" - x1="19.500004" - y1="10.687498" - x2="19.500004" - y2="2.1249981" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25751" - gradientUnits="userSpaceOnUse" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25753" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(4,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25755" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(8,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25757" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(12,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25759" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(16,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059" - id="linearGradient25761" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(20,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4051" - id="linearGradient25763" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(-2.2e-6,0)" - x1="22.509596" - y1="1.9792649" - x2="31.603951" - y2="58.909889" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4097" - id="linearGradient25765" - gradientUnits="userSpaceOnUse" - x1="22.937496" - y1="29.93535" - x2="22.937496" - y2="40.689152" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4109" - id="linearGradient25767" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.919614,0,0,0.919598,1.768501,2.487437)" - x1="25.044359" - y1="20.624216" - x2="25.791954" - y2="28.863333" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient5048-597" - id="linearGradient26827" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.774389,0,0,1.969706,-1892.179,-872.8854)" - x1="302.85715" - y1="366.64789" - x2="302.85715" - y2="609.50507" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient5060-848" - id="radialGradient26829" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(2.774389,0,0,1.969706,-1891.633,-872.8854)" - cx="605.71429" - cy="486.64789" - fx="605.71429" - fy="486.64789" - r="117.14286" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient5060-139" - id="radialGradient26831" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(-2.774389,0,0,1.969706,112.7623,-872.8854)" - cx="605.71429" - cy="486.64789" - fx="605.71429" - fy="486.64789" - r="117.14286" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059-309" - id="linearGradient26839" - gradientUnits="userSpaceOnUse" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059-684" - id="linearGradient26841" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(4,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059-264" - id="linearGradient26843" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(8,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059-61" - id="linearGradient26845" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(12,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059-351" - id="linearGradient26847" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(16,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4059-254" - id="linearGradient26849" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(20,0)" - x1="9.1648445" - y1="4.4145103" - x2="9.125" - y2="11.352011" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4109-735" - id="linearGradient26858" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.6203552,0,0,0.6494232,9.344411,8.7449428)" - x1="25.044359" - y1="20.624216" - x2="25.791954" - y2="28.863333" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4097-965" - id="linearGradient26861" - gradientUnits="userSpaceOnUse" - x1="22.937496" - y1="29.93535" - x2="22.937496" - y2="40.689152" - gradientTransform="matrix(0.6745822,0,0,0.7062034,8.1514117,6.9883063)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4051-764" - id="linearGradient26864" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.6745822,0,0,0.7062034,8.1514102,6.9883063)" - x1="22.509596" - y1="1.9792649" - x2="31.603951" - y2="58.909889" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4125-691" - id="linearGradient26874" - gradientUnits="userSpaceOnUse" - x1="19.500004" - y1="10.687498" - x2="19.500004" - y2="2.1249981" - gradientTransform="matrix(0.6745822,0,0,0.7062034,8.1514117,6.9883063)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient4117-994" - id="linearGradient26876" - gradientUnits="userSpaceOnUse" - x1="19.500004" - y1="10.687498" - x2="19.500004" - y2="2.1249981" - gradientTransform="matrix(0.6745822,0,0,0.7062034,8.1514117,6.9883063)" /> - </defs> - <metadata - id="metadata7859"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:creator> - <cc:Agent> - <dc:title>Jakub Steiner</dc:title> - </cc:Agent> - </dc:creator> - <dc:source>http://jimmac.musichall.cz</dc:source> - <cc:license - rdf:resource="http://creativecommons.org/licenses/GPL/2.0/" /> - <dc:title /> - <dc:subject> - <rdf:Bag> - <rdf:li>snapshot</rdf:li> - <rdf:li>camera</rdf:li> - <rdf:li>photo</rdf:li> - <rdf:li>compact</rdf:li> - <rdf:li>snap</rdf:li> - </rdf:Bag> - </dc:subject> - </cc:Work> - <cc:License - rdf:about="http://creativecommons.org/licenses/GPL/2.0/"> - <cc:permits - rdf:resource="http://web.resource.org/cc/Reproduction" /> - <cc:permits - rdf:resource="http://web.resource.org/cc/Distribution" /> - <cc:requires - rdf:resource="http://web.resource.org/cc/Notice" /> - <cc:permits - rdf:resource="http://web.resource.org/cc/DerivativeWorks" /> - <cc:requires - rdf:resource="http://web.resource.org/cc/ShareAlike" /> - <cc:requires - rdf:resource="http://web.resource.org/cc/SourceCode" /> - </cc:License> - </rdf:RDF> - </metadata> - <g - inkscape:groupmode="layer" - id="layer2" - inkscape:label="bottom" - style="display:inline" - transform="translate(0,80)"> - <g - id="g25705" - transform="matrix(1.8666694,0,0,1.95417,40.803984,-77.285467)" - inkscape:label="Layer 1"> - <g - transform="matrix(0.02268614,0,0,0.01568626,43.42786,42.48706)" - id="g25707" - style="opacity:0.58730164;display:inline"> - <rect - id="rect25709" - x="-1559.2523" - y="-150.69685" - width="1339.6335" - height="478.35718" - style="opacity:0.40206185;color:#000000;fill:url(#linearGradient25739);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /> - <path - d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z" - id="path25711" - style="opacity:0.40206185;color:#000000;fill:url(#radialGradient25741);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="cccc" /> - <path - id="path25713" - d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z" - style="opacity:0.40206185;color:#000000;fill:url(#radialGradient25743);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="cccc" /> - </g> - <path - id="path25715" - d="m 39.494883,43.500017 0,-27.009746 -1.191582,0 0,-2.828281 1.191582,0 0,-7.7357849 -5.468964,-4.4204484 -28.4801477,0 -0.030906,41.9942603 3.047634,0 0.9375,-0.9375 1.1250007,0.9375 22.874999,0 0.875,-0.9375 1.125,0.9375 3.994884,0 z" - style="fill:#d40000;fill-opacity:1;fill-rule:evenodd;stroke:#c32934;stroke-width:0.99999988px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - sodipodi:nodetypes="cccccccccccccccc" /> - <path - id="path25717" - d="m 32.500005,2.5937499 0,8.6874971 c 0,1.229187 -0.989563,2.218749 -2.21875,2.218749 l -21.5625038,0 c -1.2291876,0 -2.2187502,-0.989562 -2.2187502,-2.218749 l 0,-8.6874971 c 0,0 26.000004,0 26.000004,0 z" - style="opacity:0.18592966;color:#000000;fill:url(#linearGradient25747);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient25749);stroke-width:1.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccccccc" /> - <g - id="g25719"> - <path - d="m 11,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.8199629,0 -1.4800775,-0.660115 -1.4800775,-1.480078 l 0,-8.5415156 c 0,0 2.9601555,0 2.9601555,0 z" - id="path25721" - style="color:#000000;fill:url(#linearGradient25751);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - id="path25723" - d="m 15,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - style="color:#000000;fill:url(#linearGradient25753);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - d="m 19,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - id="path25725" - style="color:#000000;fill:url(#linearGradient25755);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - id="path25727" - d="m 23,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - style="color:#000000;fill:url(#linearGradient25757);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - d="m 27,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - id="path25729" - style="color:#000000;fill:url(#linearGradient25759);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - id="path25731" - d="m 31,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - style="color:#000000;fill:url(#linearGradient25761);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - </g> - <path - id="path25733" - d="m 38.528573,42.499985 0,-25.062124 -1.050532,0 0,-4.730225 1.050532,0 -0.07295,-6.2869785 -4.864604,-3.941393 -27.0712672,0 -0.029139,40.0207205 1.6411132,0 1.3258248,-1.237437 1.6793792,1.237437 21.831922,0 1.325826,-1.149049 1.59099,1.149049 2.642903,0 z" - style="fill:none;stroke:url(#linearGradient25763);stroke-width:0.99999934px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.453125" - sodipodi:nodetypes="cccccccccccccccc" /> - <rect - id="rect25735" - x="9.4999981" - y="18.500008" - width="25" - height="24.875011" - ry="1.4800782" - rx="1.4800774" - style="opacity:0.18592966;color:#000000;fill:none;stroke:url(#linearGradient25765);stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.453125;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" /> - <rect - id="rect25737" - x="10.504832" - y="19.500008" - width="22.990351" - height="22.875011" - ry="0.46875" - rx="0.46875" - style="opacity:0.18592966;color:#000000;fill:none;stroke:url(#linearGradient25767);stroke-width:1.00000072;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.453125;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" /> - </g> - </g> - <g - inkscape:groupmode="layer" - id="layer3" - inkscape:label="middle" - style="display:none" - transform="translate(0,80)"> - <g - style="opacity:0.58730164;display:inline" - id="g24399" - transform="matrix(0.01530367,0,0,0.01107769,37.447073,36.992813)"> - <rect - style="opacity:0.40206185;color:#000000;fill:url(#linearGradient26827);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" - height="478.35718" - width="1339.6335" - y="-150.69685" - x="-1559.2523" - id="rect24401" /> - <path - sodipodi:nodetypes="cccc" - style="opacity:0.40206185;color:#000000;fill:url(#radialGradient26829);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" - id="path24403" - d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z" /> - <path - sodipodi:nodetypes="cccc" - style="opacity:0.40206185;color:#000000;fill:url(#radialGradient26831);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" - d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z" - id="path24405" /> - </g> - <path - sodipodi:nodetypes="cccccccccccccccc" - style="fill:#ac9d93;fill-opacity:1;fill-rule:evenodd;stroke:#a9a9ac;stroke-width:0.69021165px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - d="m 34.793957,37.708166 0,-19.074374 -0.80382,0 0,-1.997342 0.80382,0 0,-5.463038 -3.689266,-3.1217352 -19.212201,0 -0.02085,29.6564892 2.05588,0 0.63242,-0.662065 0.758906,0.662065 15.431067,0 0.590259,-0.662065 0.758905,0.662065 2.694878,0 z" - id="path24407" /> - <path - sodipodi:nodetypes="ccccccc" - style="opacity:0.18592966;color:#000000;fill:url(#linearGradient26874);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient26876);stroke-width:0.69021189;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" - d="m 30.075337,8.8200213 0,6.1351397 c 0,0.868056 -0.667542,1.566888 -1.49673,1.566888 l -14.545681,0 c -0.829188,0 -1.496729,-0.698832 -1.496729,-1.566888 l 0,-6.1351397 c 0,0 17.53914,0 17.53914,0 z" - id="path24409" /> - <g - id="g24411" - transform="matrix(0.6745822,0,0,0.7062034,8.1514117,6.9883063)"> - <path - sodipodi:nodetypes="ccsccc" - style="color:#000000;fill:url(#linearGradient26839);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - id="path24413" - d="m 11,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.8199629,0 -1.4800775,-0.660115 -1.4800775,-1.480078 l 0,-8.5415156 c 0,0 2.9601555,0 2.9601555,0 z" /> - <path - sodipodi:nodetypes="ccsccc" - style="color:#000000;fill:url(#linearGradient26841);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - d="m 15,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - id="path24415" /> - <path - sodipodi:nodetypes="ccsccc" - style="color:#000000;fill:url(#linearGradient26843);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - id="path24417" - d="m 19,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" /> - <path - sodipodi:nodetypes="ccsccc" - style="color:#000000;fill:url(#linearGradient26845);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - d="m 23,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - id="path24419" /> - <path - sodipodi:nodetypes="ccsccc" - style="color:#000000;fill:url(#linearGradient26847);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - id="path24421" - d="m 27,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" /> - <path - sodipodi:nodetypes="ccsccc" - style="color:#000000;fill:url(#linearGradient26849);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - d="m 31,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - id="path24423" /> - </g> - <path - sodipodi:nodetypes="cccccccccccccccc" - style="fill:none;stroke:url(#linearGradient26864);stroke-width:0.6902113px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.453125" - d="m 34.142101,37.00194 0,-17.698957 -0.70867,0 0,-3.340501 0.70867,0 -0.04921,-4.439886 -3.281575,-2.7834247 -18.261795,0 -0.01966,28.2627687 1.107066,0 0.894377,-0.873882 1.13288,0.873882 14.727426,0 0.894378,-0.811462 1.073254,0.811462 1.782855,0 z" - id="path24425" /> - <rect - style="opacity:0.18592966;color:#000000;fill:none;stroke:url(#linearGradient26861);stroke-width:0.69021207;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.453125;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" - rx="0.99843389" - ry="1.0452362" - height="17.566818" - width="16.864555" - y="20.053074" - x="14.559941" - id="rect24427" /> - <rect - style="opacity:0.18592966;color:#000000;fill:none;stroke:url(#linearGradient26858);stroke-width:0.69021225;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.453125;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" - rx="0.31621042" - ry="0.33103284" - height="16.154411" - width="15.508882" - y="20.759277" - x="15.237784" - id="rect24429" /> - </g> - <g - inkscape:groupmode="layer" - id="layer1" - inkscape:label="Layer 1" - style="display:inline" - transform="translate(0,80)"> - <path - id="path8952" - sodipodi:cx="7.375" - sodipodi:cy="35.875" - transform="matrix(2.541,0,0,2.541,11.285653,-76.240557)" - d="m 18.875,35.875 a 11.5,5.75 0 1 1 -23,0 11.5,5.75 0 1 1 23,0 z" - sodipodi:type="arc" - style="opacity:0.24117647;color:#000000;fill:url(#radialGradient8960);fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.1;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" - sodipodi:ry="5.75" - sodipodi:rx="11.5" /> - <g - id="g2655" - transform="matrix(1.8666694,0,0,1.95417,2.2680351,-49.772991)" - inkscape:label="Layer 1"> - <g - transform="matrix(0.02268614,0,0,0.01568626,43.42786,42.48706)" - id="g5022" - style="opacity:0.58730164;display:inline"> - <rect - id="rect4173" - x="-1559.2523" - y="-150.69685" - width="1339.6335" - height="478.35718" - style="opacity:0.40206185;color:#000000;fill:url(#linearGradient5027);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" /> - <path - d="m -219.61876,-150.68038 c 0,0 0,478.33079 0,478.33079 142.874166,0.90045 345.40022,-107.16966 345.40014,-239.196175 0,-132.026537 -159.436816,-239.134595 -345.40014,-239.134615 z" - id="path5058" - style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5029);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="cccc" /> - <path - id="path5018" - d="m -1559.2523,-150.68038 c 0,0 0,478.33079 0,478.33079 -142.8742,0.90045 -345.4002,-107.16966 -345.4002,-239.196175 0,-132.026537 159.4368,-239.134595 345.4002,-239.134615 z" - style="opacity:0.40206185;color:#000000;fill:url(#radialGradient5031);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="cccc" /> - </g> - <path - id="path1333" - d="m 39.494883,43.500017 0,-27.009746 -1.191582,0 0,-2.828281 1.191582,0 0,-7.7357849 -5.468964,-4.4204484 -28.4801477,0 -0.030906,41.9942603 3.047634,0 0.9375,-0.9375 1.1250007,0.9375 22.874999,0 0.875,-0.9375 1.125,0.9375 3.994884,0 z" - style="fill:url(#linearGradient4140);fill-opacity:1;fill-rule:evenodd;stroke:#204a87;stroke-width:0.99999988px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" - sodipodi:nodetypes="cccccccccccccccc" /> - <path - id="rect4115" - d="m 32.500005,2.5937499 0,8.6874971 c 0,1.229187 -0.989563,2.218749 -2.21875,2.218749 l -21.5625038,0 c -1.2291876,0 -2.2187502,-0.989562 -2.2187502,-2.218749 l 0,-8.6874971 c 0,0 26.000004,0 26.000004,0 z" - style="opacity:0.18592966;color:#000000;fill:url(#linearGradient4131);fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient4123);stroke-width:1.00000024;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccccccc" /> - <g - id="g4087"> - <path - d="m 11,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.8199629,0 -1.4800775,-0.660115 -1.4800775,-1.480078 l 0,-8.5415156 c 0,0 2.9601555,0 2.9601555,0 z" - id="rect3163" - style="color:#000000;fill:url(#linearGradient4065);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - id="path4067" - d="m 15,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - style="color:#000000;fill:url(#linearGradient4069);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - d="m 19,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - id="path4071" - style="color:#000000;fill:url(#linearGradient4073);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - id="path4075" - d="m 23,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - style="color:#000000;fill:url(#linearGradient4077);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - d="m 27,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - id="path4079" - style="color:#000000;fill:url(#linearGradient4081);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - <path - id="path4083" - d="m 31,1.9662134 0,8.5415156 c 0,0.819963 -0.660115,1.480078 -1.480078,1.480078 -0.819963,0 -1.480078,-0.660115 -1.480078,-1.480078 l 0,-8.5415156 c 0,0 2.960156,0 2.960156,0 z" - style="color:#000000;fill:url(#linearGradient4085);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1.00000036;marker:none;visibility:visible;display:inline;overflow:visible" - sodipodi:nodetypes="ccsccc" /> - </g> - <path - id="path2208" - d="m 38.528573,42.499985 0,-25.062124 -1.050532,0 0,-4.730225 1.050532,0 -0.07295,-6.2869785 -4.864604,-3.941393 -27.0712672,0 -0.029139,40.0207205 1.6411132,0 1.3258248,-1.237437 1.6793792,1.237437 21.831922,0 1.325826,-1.149049 1.59099,1.149049 2.642903,0 z" - style="fill:none;stroke:url(#linearGradient4057);stroke-width:0.99999934px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.453125" - sodipodi:nodetypes="cccccccccccccccc" /> - <rect - id="rect4095" - x="9.4999981" - y="18.500008" - width="25" - height="24.875011" - ry="1.4800782" - rx="1.4800774" - style="opacity:0.18592966;color:#000000;fill:none;stroke:url(#linearGradient4103);stroke-width:1.00000048;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.453125;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" /> - <rect - id="rect4105" - x="10.504832" - y="19.500008" - width="22.990351" - height="22.875011" - ry="0.46875" - rx="0.46875" - style="opacity:0.18592966;color:#000000;fill:none;stroke:url(#linearGradient4107);stroke-width:1.00000072;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:0.453125;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible" /> - </g> - </g> -</svg> diff --git a/rapid/glade3/rapid.ui b/rapid/glade3/rapid.ui deleted file mode 100644 index 75f145e..0000000 --- a/rapid/glade3/rapid.ui +++ /dev/null @@ -1,1044 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <requires lib="gtk+" version="2.20"/> - <!-- interface-naming-policy toplevel-contextual --> - <object class="GtkAction" id="about_action"> - <property name="label" translatable="yes">About...</property> - <signal name="activate" handler="on_about_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="check_all_action"> - <property name="label" translatable="yes">_Check All</property> - <signal name="activate" handler="on_check_all_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="check_all_photos_action"> - <property name="label" translatable="yes">Check All Photos</property> - <signal name="activate" handler="on_check_all_photos_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="check_all_videos_action"> - <property name="label" translatable="yes">Check All Videos</property> - <signal name="activate" handler="on_check_all_videos_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="donate_action"> - <property name="label" translatable="yes">_Make a Donation...</property> - <signal name="activate" handler="on_donate_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="download_action"> - <property name="label" translatable="yes">Download</property> - <property name="icon_name">system-run</property> - <property name="sensitive">False</property> - <signal name="activate" handler="on_download_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="get_help_action"> - <property name="label" translatable="yes">_Get Help Online...</property> - <signal name="activate" handler="on_get_help_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="help_action"> - <property name="label" translatable="yes">Help</property> - <signal name="activate" handler="on_help_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="next_image_action"> - <property name="tooltip">Next file</property> - <signal name="activate" handler="on_next_image_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="preferences_action"> - <property name="label" translatable="yes">Preferences</property> - <signal name="activate" handler="on_preferences_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="prev_image_action"> - <property name="tooltip">Previous file</property> - <signal name="activate" handler="on_prev_image_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="quit_action"> - <property name="label" translatable="yes">Quit</property> - <signal name="activate" handler="on_quit_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="refresh_action"> - <property name="label" translatable="yes">Refresh</property> - <signal name="activate" handler="on_refresh_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="report_problem_action"> - <property name="label" translatable="yes">_Report a Problem...</property> - <property name="stock_id">gtk-dialog-warning</property> - <signal name="activate" handler="on_report_problem_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="show_image_action"> - <property name="tooltip">Show image</property> - <signal name="activate" handler="on_show_image_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="show_thumbnails_action"> - <property name="tooltip">Show thumbnails</property> - <signal name="activate" handler="on_show_thumbnails_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="translate_action"> - <property name="label" translatable="yes">Translate this Application...</property> - <signal name="activate" handler="on_translate_action_activate" swapped="no"/> - </object> - <object class="GtkAction" id="uncheck_all_action"> - <property name="label" translatable="yes">_Uncheck All</property> - <signal name="activate" handler="on_uncheck_all_action_activate" swapped="no"/> - </object> - <object class="GtkWindow" id="rapidapp"> - <property name="can_focus">False</property> - <property name="title" translatable="yes">Rapid Photo Downloader</property> - <property name="icon">rapid-photo-downloader.svg</property> - <signal name="destroy" handler="on_rapidapp_destroy" swapped="no"/> - <signal name="window-state-event" handler="on_rapidapp_window_state_event" swapped="no"/> - <child> - <object class="GtkVBox" id="rapidapp_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkMenuBar" id="menubar3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <child> - <object class="GtkMenuItem" id="menuitem7"> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_File</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="menuitem7_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkImageMenuItem" id="menu_download_pause"> - <property name="label" translatable="yes">Download / Pause</property> - <property name="related_action">download_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_stock">False</property> - <accelerator key="Return" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="menu_refresh"> - <property name="related_action">refresh_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="r" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="menu_preferences"> - <property name="label">gtk-preferences</property> - <property name="related_action">preferences_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="p" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="menu_quit"> - <property name="label">gtk-quit</property> - <property name="related_action">quit_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="q" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuItem" id="select_menuitem"> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Select</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="select_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkMenuItem" id="menu_check_all"> - <property name="related_action">check_all_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <accelerator key="a" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menu_check_all_photos"> - <property name="related_action">check_all_photos_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <accelerator key="t" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menu_check_all_videos"> - <property name="related_action">check_all_videos_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <accelerator key="d" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menu_uncheck_all"> - <property name="related_action">uncheck_all_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <accelerator key="l" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="menu_select_sep"> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menu_select_all_without_job_code"> - <property name="use_action_appearance">False</property> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">Select All Without _Job Code</property> - <property name="use_underline">True</property> - <accelerator key="j" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menu_select_all_with_job_code"> - <property name="use_action_appearance">False</property> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - <property name="label" translatable="yes">Select All Wit_h Job Code</property> - <property name="use_underline">True</property> - <accelerator key="h" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menuitem10"> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_View</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="menuitem10_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkImageMenuItem" id="menu_zoom_in"> - <property name="label">gtk-zoom-in</property> - <property name="use_action_appearance">False</property> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="equal" signal="activate" modifiers="GDK_CONTROL_MASK"/> - <accelerator key="plus" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="menu_zoom_out"> - <property name="label">gtk-zoom-out</property> - <property name="use_action_appearance">False</property> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="minus" signal="activate" modifiers="GDK_CONTROL_MASK"/> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="seperator20"> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - </object> - </child> - <child> - <object class="GtkCheckMenuItem" id="menu_log_window"> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Error Log</property> - <property name="use_underline">True</property> - <signal name="toggled" handler="on_menu_log_window_toggled" swapped="no"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menu_clear"> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Clear Completed Downloads</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="separator5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="previous_image_menuitem"> - <property name="use_action_appearance">False</property> - <property name="related_action">prev_image_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Previous File</property> - <accelerator key="bracketleft" signal="activate"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="next_file_menuitem"> - <property name="use_action_appearance">False</property> - <property name="related_action">next_image_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Next File</property> - <accelerator key="bracketright" signal="activate"/> - </object> - </child> - </object> - </child> - </object> - </child> - <child> - <object class="GtkMenuItem" id="help_menuitem"> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Help</property> - <property name="use_underline">True</property> - <child type="submenu"> - <object class="GtkMenu" id="help_menu"> - <property name="can_focus">False</property> - <child> - <object class="GtkImageMenuItem" id="menu_get_help_online"> - <property name="related_action">get_help_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_underline">True</property> - <property name="use_stock">True</property> - <accelerator key="F1" signal="activate"/> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menu_report_problem"> - <property name="related_action">report_problem_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menu_donate"> - <property name="related_action">donate_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Make a Donation...</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkMenuItem" id="menu_translate"> - <property name="related_action">translate_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">_Translate this Application...</property> - <property name="use_underline">True</property> - </object> - </child> - <child> - <object class="GtkSeparatorMenuItem" id="separator1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - </child> - <child> - <object class="GtkImageMenuItem" id="menu_about"> - <property name="related_action">about_action</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="use_stock">True</property> - </object> - </child> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="toobar_box"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="homogeneous">True</property> - <child> - <object class="GtkToolbar" id="from_toolbar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <property name="show_arrow">False</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkToolbar" id="copy_toolbar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <property name="show_arrow">False</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkToolbar" id="dest_toolbar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - <property name="show_arrow">False</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkToolbar" id="menu_toolbar"> - <property name="can_focus">False</property> - <property name="orientation">vertical</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="pack_type">end</property> - <property name="position">3</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkVPaned" id="main_vpaned"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="position">1</property> - <property name="position_set">True</property> - <child> - <object class="GtkHBox" id="devices_box"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkScrolledWindow" id="device_collection_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="hscrollbar_policy">never</property> - <property name="shadow_type">etched-out</property> - <child> - <object class="GtkViewport" id="device_collection_viewport"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="resize_mode">queue</property> - <property name="shadow_type">none</property> - <child> - <placeholder/> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">6</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="resize">False</property> - <property name="shrink">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="thumbnail_pane_vbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkNotebook" id="main_notebook"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="show_tabs">False</property> - <property name="show_border">False</property> - <child> - <object class="GtkVBox" id="vbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkScrolledWindow" id="thumbnails_scrolledwindow"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="thumbnail_control_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <placeholder/> - </child> - <child> - <object class="GtkButton" id="preview_button"> - <property name="related_action">show_image_action</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="relief">none</property> - <property name="focus_on_click">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="check_all_button"> - <property name="related_action">check_all_action</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="relief">none</property> - <property name="use_underline">True</property> - <property name="focus_on_click">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkButton" id="uncheck_all_button"> - <property name="label" translatable="yes">_Uncheck All</property> - <property name="related_action">uncheck_all_action</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="relief">none</property> - <property name="use_underline">True</property> - <property name="focus_on_click">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">3</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">4</property> - <property name="position">0</property> - </packing> - </child> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - </child> - <child type="tab"> - <object class="GtkLabel" id="label1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label">page 1</property> - </object> - <packing> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="vbox2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkEventBox" id="preview_eventbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="no_show_all">True</property> - <signal name="button-press-event" handler="on_preview_eventbox_button_press_event" swapped="no"/> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="homogeneous">True</property> - <child> - <object class="GtkHBox" id="image_controls_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">6</property> - <child> - <object class="GtkButton" id="next_image_button"> - <property name="related_action">next_image_action</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="relief">none</property> - <property name="focus_on_click">False</property> - <accelerator key="bracketright" signal="activate"/> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="prev_image_button"> - <property name="related_action">prev_image_action</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="relief">none</property> - <property name="focus_on_click">False</property> - <accelerator key="bracketleft" signal="activate"/> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">6</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkButton" id="thumbnails_button"> - <property name="related_action">show_thumbnails_action</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="relief">none</property> - <property name="use_underline">True</property> - <property name="focus_on_click">False</property> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="padding">4</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkCheckButton" id="download_this_checkbutton"> - <property name="label" translatable="yes">_Include in download</property> - <property name="use_action_appearance">False</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">False</property> - <property name="use_underline">True</property> - <property name="draw_indicator">True</property> - <accelerator key="i" signal="activate"/> - <signal name="toggled" handler="on_download_this_checkbutton_toggled" swapped="no"/> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="position">1</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="label2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label">page 2</property> - </object> - <packing> - <property name="position">1</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <placeholder/> - </child> - <child type="tab"> - <object class="GtkLabel" id="label3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label">page 3</property> - </object> - <packing> - <property name="position">2</property> - <property name="tab_fill">False</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="resize">True</property> - <property name="shrink">True</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="buttons_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHButtonBox" id="main_buttonbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkButton" id="help_button"> - <property name="label">_Help</property> - <property name="related_action">help_action</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_underline">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkButton" id="download_button"> - <property name="label" translatable="yes">_Download</property> - <property name="related_action">download_action</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="can_default">True</property> - <property name="has_default">True</property> - <property name="receives_default">True</property> - <property name="use_underline">True</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="padding">6</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="padding">6</property> - <property name="position">3</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="statusbar_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHBox" id="download_progressbar_hbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkProgressBar" id="download_progressbar"> - <property name="width_request">0</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkStatusbar" id="rapid_statusbar"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">2</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkVSeparator" id="vseparator1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - <child> - <object class="GtkVBox" id="vbox15"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkHSeparator" id="hseparator3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkLabel" id="speed_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label"> </property> - <property name="width_chars">9</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkEventBox" id="error_eventbox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <signal name="button-press-event" handler="on_error_eventbox_button_press_event" swapped="no"/> - <child> - <object class="GtkHBox" id="hbox6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkVSeparator" id="warning_vseparator"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkImage" id="error_image"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xpad">3</property> - <property name="stock">gtk-dialog-error</property> - <property name="icon-size">1</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkImage" id="warning_image"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> - <property name="xpad">3</property> - <property name="stock">gtk-dialog-warning</property> - <property name="icon-size">1</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">2</property> - </packing> - </child> - </object> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">3</property> - </packing> - </child> - <child> - <object class="GtkVSeparator" id="vseparator2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">4</property> - </packing> - </child> - <child> - <object class="GtkStatusbar" id="statusbar1"> - <property name="width_request">15</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">2</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="position">5</property> - </packing> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">False</property> - <property name="pack_type">end</property> - <property name="position">4</property> - </packing> - </child> - </object> - </child> - </object> -</interface> diff --git a/rapid/glade3/thumbnails_icon.png b/rapid/glade3/thumbnails_icon.png Binary files differdeleted file mode 100644 index d76d954..0000000 --- a/rapid/glade3/thumbnails_icon.png +++ /dev/null diff --git a/rapid/glade3/video.svg b/rapid/glade3/video.svg deleted file mode 100644 index 0817d62..0000000 --- a/rapid/glade3/video.svg +++ /dev/null @@ -1,956 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="no"?> -<!-- Created with Inkscape (http://www.inkscape.org/) --> - -<svg - xmlns:dc="http://purl.org/dc/elements/1.1/" - xmlns:cc="http://creativecommons.org/ns#" - xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" - xmlns:svg="http://www.w3.org/2000/svg" - xmlns="http://www.w3.org/2000/svg" - xmlns:xlink="http://www.w3.org/1999/xlink" - xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" - version="1.0" - width="96" - height="96" - id="svg2408" - inkscape:version="0.48.1 r9760" - sodipodi:docname="video.svg" - inkscape:export-filename="/home/damon/rapid/rapid/glade3/video66.png" - inkscape:export-xdpi="61.880001" - inkscape:export-ydpi="61.880001"> - <sodipodi:namedview - pagecolor="#444444" - bordercolor="#666666" - borderopacity="1" - objecttolerance="10" - gridtolerance="10" - guidetolerance="10" - inkscape:pageopacity="1" - inkscape:pageshadow="2" - inkscape:window-width="950" - inkscape:window-height="950" - id="namedview3275" - showgrid="false" - inkscape:zoom="3.0104167" - inkscape:cx="48" - inkscape:cy="48" - inkscape:window-x="93" - inkscape:window-y="58" - inkscape:window-maximized="0" - inkscape:current-layer="svg2408" /> - <defs - id="defs2410"> - <linearGradient - id="linearGradient4314"> - <stop - id="stop4316" - style="stop-color:#ffffff;stop-opacity:1" - offset="0" /> - <stop - id="stop4322" - style="stop-color:#ffffff;stop-opacity:0.49803922" - offset="0.30000001" /> - <stop - id="stop4318" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - x1="45.447727" - y1="92.539597" - x2="45.447727" - y2="7.0165396" - id="ButtonShadow" - gradientUnits="userSpaceOnUse" - gradientTransform="scale(1.0058652,0.994169)"> - <stop - id="stop3750" - style="stop-color:#000000;stop-opacity:1" - offset="0" /> - <stop - id="stop3752" - style="stop-color:#000000;stop-opacity:0.58823532" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3737"> - <stop - id="stop3739" - style="stop-color:#ffffff;stop-opacity:1" - offset="0" /> - <stop - id="stop3741" - style="stop-color:#ffffff;stop-opacity:0" - offset="1" /> - </linearGradient> - <filter - color-interpolation-filters="sRGB" - id="filter3174"> - <feGaussianBlur - id="feGaussianBlur3176" - stdDeviation="1.71" /> - </filter> - <linearGradient - x1="36.357143" - y1="6" - x2="36.357143" - y2="63.893143" - id="linearGradient3188" - xlink:href="#linearGradient3737" - gradientUnits="userSpaceOnUse" /> - <filter - x="-0.192" - y="-0.192" - width="1.3839999" - height="1.3839999" - color-interpolation-filters="sRGB" - id="filter3794"> - <feGaussianBlur - id="feGaussianBlur3796" - stdDeviation="5.28" /> - </filter> - <linearGradient - x1="48" - y1="20.220806" - x2="48" - y2="138.66119" - id="linearGradient3613" - xlink:href="#linearGradient3737" - gradientUnits="userSpaceOnUse" /> - <radialGradient - cx="48" - cy="90.171875" - r="42" - fx="48" - fy="90.171875" - id="radialGradient3619" - xlink:href="#linearGradient3737" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.1573129,0,0,0.99590774,-7.5510206,0.19713193)" /> - <clipPath - id="clipPath3613"> - <rect - width="84" - height="84" - rx="6" - ry="6" - x="6" - y="6" - id="rect3615" - style="fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none" /> - </clipPath> - <linearGradient - x1="48" - y1="90" - x2="48" - y2="5.9877172" - id="linearGradient3617" - xlink:href="#linearGradient3187" - gradientUnits="userSpaceOnUse" /> - <linearGradient - id="linearGradient3187"> - <stop - id="stop3189" - style="stop-color:#282828;stop-opacity:1" - offset="0" /> - <stop - id="stop3191" - style="stop-color:#5a5a5a;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3386"> - <stop - id="stop3388" - style="stop-color:#eeeeec;stop-opacity:1" - offset="0" /> - <stop - id="stop3390" - style="stop-color:#eeeeec;stop-opacity:0" - offset="1" /> - </linearGradient> - <radialGradient - cx="53.419292" - cy="66.597893" - r="47.724609" - fx="53.419292" - fy="66.597893" - id="radialGradient3904" - xlink:href="#linearGradient3386" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0164118,0,0,1.2415079,-19.337623,-31.113008)" /> - <radialGradient - cx="53.419292" - cy="66.597893" - r="47.724609" - fx="53.419292" - fy="66.597893" - id="radialGradient3929" - xlink:href="#linearGradient3386" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0220726,0,0,1.2415079,-19.896446,-47.863009)" /> - <radialGradient - cx="53.419292" - cy="66.597893" - r="47.724609" - fx="53.419292" - fy="66.597893" - id="radialGradient3933" - xlink:href="#linearGradient3386" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.4750367,0,0,0.847859,-55.736923,-9.8942437)" /> - <radialGradient - cx="53.419292" - cy="66.597893" - r="47.724609" - fx="53.419292" - fy="66.597893" - id="radialGradient3937" - xlink:href="#linearGradient3386" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.4750367,0,0,0.847859,-77.736923,-9.8942437)" /> - <radialGradient - cx="53.419292" - cy="66.597893" - r="47.724609" - fx="53.419292" - fy="66.597893" - id="radialGradient4114" - xlink:href="#linearGradient3386" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0199392,0,0,0.85846172,-19.873341,-11.926904)" /> - <radialGradient - cx="53.419292" - cy="66.597893" - r="47.724609" - fx="53.419292" - fy="66.597893" - id="radialGradient4116" - xlink:href="#linearGradient3386" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0199392,0,0,0.85846172,-19.685841,-9.268213)" /> - <linearGradient - id="linearGradient3299"> - <stop - id="stop3301" - style="stop-color:#dfdfdf;stop-opacity:1" - offset="0" /> - <stop - id="stop3303" - style="stop-color:#434343;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3273"> - <stop - id="stop3275" - style="stop-color:#5d5d5d;stop-opacity:1" - offset="0" /> - <stop - id="stop3277" - style="stop-color:#171717;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3289"> - <stop - id="stop3291" - style="stop-color:#919191;stop-opacity:1" - offset="0" /> - <stop - id="stop3293" - style="stop-color:#232323;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3260" - gradientUnits="userSpaceOnUse"> - <stop - id="stop3262" - style="stop-color:#000000;stop-opacity:1" - offset="0" /> - <stop - id="stop3266" - style="stop-color:#000000;stop-opacity:0.3548387" - offset="0.2" /> - <stop - id="stop3268" - style="stop-color:#000000;stop-opacity:0" - offset="0.55254942" /> - <stop - id="stop3264" - style="stop-color:#000000;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - x1="43.476658" - y1="26" - x2="43.476658" - y2="34.001266" - id="linearGradient4262" - xlink:href="#linearGradient3260" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.72413795,0,0,0.75,1.6551725,4.5)" /> - <linearGradient - x1="48" - y1="16" - x2="48" - y2="11.924657" - id="linearGradient4310" - xlink:href="#linearGradient3260" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1,0,0,1.25,0,-4.5982122)" /> - <linearGradient - x1="50.464287" - y1="16.035713" - x2="50.464287" - y2="20.329885" - id="linearGradient4320" - xlink:href="#linearGradient4314" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(0,-0.67856979)" /> - <linearGradient - x1="48" - y1="16" - x2="48" - y2="11.924657" - id="linearGradient4326" - xlink:href="#linearGradient3260" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1,0,0,1.25,0,4.4017878)" /> - <linearGradient - x1="11" - y1="10.441942" - x2="16" - y2="24" - id="linearGradient4371" - xlink:href="#linearGradient3273" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.81330712,0,0,0.81132563,3.9935431,2.0281849)" /> - <linearGradient - x1="13.125" - y1="9.9419422" - x2="19.25" - y2="24.5" - id="linearGradient4373" - xlink:href="#linearGradient3289" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.81330712,0,0,0.81132563,3.9935431,2.0281849)" /> - <radialGradient - cx="11.3125" - cy="14.9375" - r="1.4375" - fx="11.3125" - fy="14.9375" - id="radialGradient4375" - xlink:href="#linearGradient3299" - gradientUnits="userSpaceOnUse" /> - <radialGradient - cx="11.3125" - cy="14.9375" - r="1.4375" - fx="11.3125" - fy="14.9375" - id="radialGradient4377" - xlink:href="#linearGradient3299" - gradientUnits="userSpaceOnUse" /> - <radialGradient - cx="11.3125" - cy="14.9375" - r="1.4375" - fx="11.3125" - fy="14.9375" - id="radialGradient4379" - xlink:href="#linearGradient3299" - gradientUnits="userSpaceOnUse" /> - <linearGradient - id="linearGradient3277"> - <stop - id="stop3279" - style="stop-color:#3d9119;stop-opacity:1" - offset="0" /> - <stop - id="stop3281" - style="stop-color:#76e62b;stop-opacity:1" - offset="1" /> - </linearGradient> - <linearGradient - id="linearGradient3266"> - <stop - id="stop3269" - style="stop-color:#87ff2b;stop-opacity:1" - offset="0" /> - <stop - id="stop3271" - style="stop-color:#87ff2b;stop-opacity:0" - offset="1" /> - </linearGradient> - <linearGradient - x1="70.782135" - y1="83.017471" - x2="75.404488" - y2="22.02951" - id="linearGradient4520" - xlink:href="#linearGradient3277" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0513134,0,0,1.0513134,-120.2285,31.416155)" /> - <radialGradient - cx="40.846539" - cy="75.263962" - r="14.672466" - fx="40.846539" - fy="75.263962" - id="radialGradient4522" - xlink:href="#linearGradient3266" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.9847961,0.02597927,-0.01823822,0.6913564,1.9937062,24.820256)" /> - <radialGradient - cx="40.846539" - cy="75.263962" - r="14.672466" - fx="40.846539" - fy="75.263962" - id="radialGradient4524" - xlink:href="#linearGradient3266" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.9847961,0.02597927,-0.01823822,0.6913564,1.9937062,24.820256)" /> - <radialGradient - cx="54.75" - cy="25.072803" - r="13" - fx="54.75" - fy="25.072803" - id="radialGradient4526" - xlink:href="#linearGradient3266" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.543369,0.28142051,-0.32722971,1.7945958,-138.17549,-6.0460513)" /> - <radialGradient - cx="56.496048" - cy="31.701607" - r="13" - fx="56.496048" - fy="31.701607" - id="radialGradient4528" - xlink:href="#linearGradient3266" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.88267832,0,-2.2566243e-8,1.115007,-69.080446,51.097006)" /> - <clipPath - id="clipPath3754"> - <rect - width="96" - height="96" - x="-100" - y="-3.469447e-15" - id="rect3756" - style="fill:#ffffff;fill-opacity:1;stroke:none" /> - </clipPath> - <linearGradient - x1="45.447727" - y1="92.539597" - x2="45.447727" - y2="7.0165396" - id="ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0058652,0,0,0.994169,100,0)"> - <stop - id="stop3750-8" - style="stop-color:#000000;stop-opacity:1" - offset="0" /> - <stop - id="stop3752-5" - style="stop-color:#000000;stop-opacity:0.58823532" - offset="1" /> - </linearGradient> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient3780" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0238095,0,0,1.0119048,-1.1428571,-98.071429)" /> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient3772" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0238095,0,0,1.0119048,-1.1428571,-98.071429)" /> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient3725" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0238095,0,0,1.0119048,-1.1428571,-98.071429)" /> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient3721" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="translate(0,-97)" /> - <linearGradient - x1="32.251034" - y1="6.1317081" - x2="32.251034" - y2="90.238609" - id="linearGradient3811" - xlink:href="#ButtonShadow-0" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0238095,0,0,1.0119048,-1.1428571,-98.071429)" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3277" - id="linearGradient3157" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0513134,0,0,1.0513134,-120.2285,31.416155)" - x1="70.782135" - y1="83.017471" - x2="75.404488" - y2="22.02951" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3266" - id="radialGradient3159" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.9847961,0.02597927,-0.01823822,0.6913564,1.9937062,24.820256)" - cx="40.846539" - cy="75.263962" - fx="40.846539" - fy="75.263962" - r="14.672466" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3266" - id="radialGradient3161" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.9847961,0.02597927,-0.01823822,0.6913564,1.9937062,24.820256)" - cx="40.846539" - cy="75.263962" - fx="40.846539" - fy="75.263962" - r="14.672466" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3266" - id="radialGradient3163" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.543369,0.28142051,-0.32722971,1.7945958,-138.17549,-6.0460513)" - cx="54.75" - cy="25.072803" - fx="54.75" - fy="25.072803" - r="13" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3266" - id="radialGradient3165" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.88267832,0,-2.2566243e-8,1.115007,-69.080446,51.097006)" - cx="56.496048" - cy="31.701607" - fx="56.496048" - fy="31.701607" - r="13" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3277-7" - id="linearGradient3157-9" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0513134,0,0,1.0513134,-120.2285,31.416155)" - x1="70.782135" - y1="83.017471" - x2="75.404488" - y2="22.02951" /> - <linearGradient - id="linearGradient3277-7"> - <stop - id="stop3279-5" - style="stop-color:#3d9119;stop-opacity:1" - offset="0" /> - <stop - id="stop3281-9" - style="stop-color:#76e62b;stop-opacity:1" - offset="1" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3266-8" - id="radialGradient3159-7" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.9847961,0.02597927,-0.01823822,0.6913564,1.9937062,24.820256)" - cx="40.846539" - cy="75.263962" - fx="40.846539" - fy="75.263962" - r="14.672466" /> - <linearGradient - id="linearGradient3266-8"> - <stop - id="stop3269-5" - style="stop-color:#87ff2b;stop-opacity:1" - offset="0" /> - <stop - id="stop3271-3" - style="stop-color:#87ff2b;stop-opacity:0" - offset="1" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3266-8" - id="radialGradient3161-3" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.9847961,0.02597927,-0.01823822,0.6913564,1.9937062,24.820256)" - cx="40.846539" - cy="75.263962" - fx="40.846539" - fy="75.263962" - r="14.672466" /> - <linearGradient - id="linearGradient3196"> - <stop - id="stop3198" - style="stop-color:#87ff2b;stop-opacity:1" - offset="0" /> - <stop - id="stop3200" - style="stop-color:#87ff2b;stop-opacity:0" - offset="1" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3266-8" - id="radialGradient3163-8" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.543369,0.28142051,-0.32722971,1.7945958,-138.17549,-6.0460513)" - cx="54.75" - cy="25.072803" - fx="54.75" - fy="25.072803" - r="13" /> - <linearGradient - id="linearGradient3203"> - <stop - id="stop3205" - style="stop-color:#87ff2b;stop-opacity:1" - offset="0" /> - <stop - id="stop3207" - style="stop-color:#87ff2b;stop-opacity:0" - offset="1" /> - </linearGradient> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3266-8" - id="radialGradient3165-3" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(0.88267832,0,-2.2566243e-8,1.115007,-69.080446,51.097006)" - cx="56.496048" - cy="31.701607" - fx="56.496048" - fy="31.701607" - r="13" /> - <linearGradient - id="linearGradient3210"> - <stop - id="stop3212" - style="stop-color:#87ff2b;stop-opacity:1" - offset="0" /> - <stop - id="stop3214" - style="stop-color:#87ff2b;stop-opacity:0" - offset="1" /> - </linearGradient> - <radialGradient - r="13" - fy="31.701607" - fx="56.496048" - cy="31.701607" - cx="56.496048" - gradientTransform="matrix(0.88267832,0,-2.2566243e-8,1.115007,-69.080446,51.097006)" - gradientUnits="userSpaceOnUse" - id="radialGradient3222" - xlink:href="#linearGradient3266-8" - inkscape:collect="always" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3386" - id="radialGradient3284" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.4750367,0,0,0.847859,-77.736923,-9.8942437)" - cx="53.419292" - cy="66.597893" - fx="53.419292" - fy="66.597893" - r="47.724609" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3386" - id="radialGradient3287" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0199392,0,0,0.85846172,-19.873341,-11.926904)" - cx="53.419292" - cy="66.597893" - fx="53.419292" - fy="66.597893" - r="47.724609" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3386" - id="radialGradient3290" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.4750367,0,0,0.847859,-55.736923,-9.8942437)" - cx="53.419292" - cy="66.597893" - fx="53.419292" - fy="66.597893" - r="47.724609" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3386" - id="radialGradient3293" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0220726,0,0,1.2415079,-19.896446,-47.863009)" - cx="53.419292" - cy="66.597893" - fx="53.419292" - fy="66.597893" - r="47.724609" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3386" - id="radialGradient3296" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0164118,0,0,1.2415079,-19.337623,-31.113008)" - cx="53.419292" - cy="66.597893" - fx="53.419292" - fy="66.597893" - r="47.724609" /> - <radialGradient - inkscape:collect="always" - xlink:href="#linearGradient3386" - id="radialGradient3299" - gradientUnits="userSpaceOnUse" - gradientTransform="matrix(1.0199392,0,0,0.85846172,-19.685841,-9.268213)" - cx="53.419292" - cy="66.597893" - fx="53.419292" - fy="66.597893" - r="47.724609" /> - <linearGradient - inkscape:collect="always" - xlink:href="#linearGradient3187" - id="linearGradient3302" - gradientUnits="userSpaceOnUse" - x1="48" - y1="90" - x2="48" - y2="5.9877172" /> - </defs> - <metadata - id="metadata2413"> - <rdf:RDF> - <cc:Work - rdf:about=""> - <dc:format>image/svg+xml</dc:format> - <dc:type - rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> - <dc:title></dc:title> - </cc:Work> - </rdf:RDF> - </metadata> - <g - id="layer2" - style="display:none"> - <rect - width="86" - height="85" - rx="6" - ry="6" - x="5" - y="7" - id="rect3745" - style="opacity:0.9;fill:url(#ButtonShadow);fill-opacity:1;fill-rule:nonzero;stroke:none;filter:url(#filter3174)" /> - </g> - <rect - style="fill:url(#linearGradient3302);fill-opacity:1;fill-rule:nonzero;stroke:none" - id="rect2419" - y="6" - x="6" - ry="6" - rx="6" - height="84" - width="84" /> - <path - style="font-size:6px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:0.8;fill:url(#radialGradient3299);fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans Bold" - id="path3890" - inkscape:connector-curvature="0" - d="m 36.5625,32.0625 c -0.319303,3e-6 -0.56595,0.04413 -0.75,0.125 -0.182658,0.07948 -0.31423,0.171675 -0.40625,0.3125 -0.09063,0.139436 -0.15625,0.311193 -0.15625,0.46875 0,0.239827 0.102775,0.407731 0.28125,0.5625 0.177079,0.154772 0.459488,0.28158 0.875,0.375 0.253766,0.05577 0.43028,0.12476 0.5,0.1875 C 36.97597,34.1565 37,34.23302 37,34.3125 c -2e-6,0.08366 -0.01985,0.15461 -0.09375,0.21875 -0.07251,0.06275 -0.177251,0.09375 -0.3125,0.09375 -0.181264,10e-7 -0.3399,-0.0634 -0.4375,-0.1875 -0.05996,-0.07669 -0.10548,-0.197344 -0.125,-0.34375 l -0.875,0.0625 c 0.02649,0.309543 0.111716,0.580466 0.3125,0.78125 0.200783,0.200784 0.572269,0.28125 1.09375,0.28125 0.296991,0 0.554791,-0.03995 0.75,-0.125 0.195204,-0.08645 0.32874,-0.209074 0.4375,-0.375 0.108757,-0.165925 0.187497,-0.334648 0.1875,-0.53125 -3e-6,-0.167318 -0.04274,-0.333498 -0.125,-0.46875 -0.08087,-0.135248 -0.226384,-0.25312 -0.40625,-0.34375 -0.178477,-0.09202 -0.460885,-0.16076 -0.875,-0.25 C 36.363929,33.09015 36.26337,33.04044 36.21875,33 36.17274,32.96097 36.125,32.9238 36.125,32.875 c 0,-0.06693 0.03798,-0.14148 0.09375,-0.1875 0.05577,-0.0474 0.139846,-0.0625 0.25,-0.0625 0.133854,0 0.2372,0.03101 0.3125,0.09375 0.07669,0.06275 0.13254,0.174464 0.15625,0.3125 l 0.875,-0.0625 C 37.77346,32.650845 37.675108,32.426263 37.46875,32.28125 37.263781,32.134852 36.954306,32.062503 36.5625,32.0625 z m 14.96875,0 c -0.497778,3e-6 -0.877385,0.127386 -1.15625,0.40625 -0.278867,0.278869 -0.4375,0.686937 -0.4375,1.1875 0,0.358345 0.07792,0.63657 0.21875,0.875 0.140827,0.238431 0.336618,0.421098 0.5625,0.53125 0.227275,0.110148 0.497954,0.15625 0.84375,0.15625 0.340215,0 0.616471,-0.06062 0.84375,-0.1875 0.228667,-0.128278 0.411335,-0.302579 0.53125,-0.53125 0.121301,-0.230064 0.187496,-0.516655 0.1875,-0.875 -4e-6,-0.493591 -0.130175,-0.882958 -0.40625,-1.15625 -0.27608,-0.27468 -0.677177,-0.406247 -1.1875,-0.40625 z m 3.375,0 c -0.319304,3e-6 -0.597199,0.04413 -0.78125,0.125 -0.182658,0.07948 -0.31422,0.171675 -0.40625,0.3125 -0.09063,0.139436 -0.125,0.311193 -0.125,0.46875 0,0.239827 0.07153,0.407731 0.25,0.5625 0.17708,0.154772 0.490738,0.28158 0.90625,0.375 0.253767,0.05577 0.39903,0.12476 0.46875,0.1875 0.06972,0.06275 0.124998,0.13927 0.125,0.21875 -2e-6,0.08366 -0.0511,0.15461 -0.125,0.21875 C 55.14624,34.594 55.041498,34.625 54.90625,34.625 54.724985,34.625001 54.5976,34.5616 54.5,34.4375 54.44004,34.36081 54.39452,34.240156 54.375,34.09375 l -0.90625,0.0625 c 0.02649,0.309543 0.142966,0.580466 0.34375,0.78125 0.200783,0.200784 0.572269,0.28125 1.09375,0.28125 0.296991,0 0.523541,-0.03995 0.71875,-0.125 0.195204,-0.08645 0.359989,-0.209074 0.46875,-0.375 0.108756,-0.165925 0.156247,-0.334648 0.15625,-0.53125 -3e-6,-0.167318 -0.04274,-0.333498 -0.125,-0.46875 C 56.04413,33.583502 55.929866,33.46563 55.75,33.375 55.571523,33.28298 55.257865,33.21424 54.84375,33.125 54.676429,33.09015 54.57587,33.04044 54.53125,33 c -0.04602,-0.03903 -0.0625,-0.0762 -0.0625,-0.125 0,-0.06693 0.03798,-0.14148 0.09375,-0.1875 0.05577,-0.0474 0.139847,-0.0625 0.25,-0.0625 0.133855,0 0.2372,0.03101 0.3125,0.09375 0.07669,0.06275 0.10129,0.174464 0.125,0.3125 l 0.90625,-0.0625 c -0.03905,-0.317905 -0.168642,-0.542487 -0.375,-0.6875 -0.204969,-0.146398 -0.483195,-0.218747 -0.875,-0.21875 z M 15,32.125 l 0,3.0625 0.9375,0 0,-1.25 1.1875,0 0,-0.625 -1.1875,0 0,-0.53125 1.40625,0 0,-0.65625 -2.34375,0 z m 3.3125,0 -1.15625,3.0625 0.96875,0 0.125,-0.53125 1.09375,0 0.15625,0.53125 0.96875,0 -1.125,-3.0625 -1.03125,0 z m 2.46875,0 0,3.0625 2.59375,0 0,-0.71875 -1.65625,0 0,-0.59375 1.5,0 0,-0.625 -1.5,0 0,-0.5 1.59375,0 0,-0.625 -2.53125,0 z m 3.09375,0 0,3.0625 0.90625,0 0,-1.6875 1.15625,1.6875 0.875,0 0,-3.0625 -0.875,0 0,1.6875 -1.15625,-1.6875 -0.90625,0 z m 3.5,0 0,0.625 1.53125,0 -1.6875,1.78125 0,0.65625 2.90625,0 0,-0.65625 -1.78125,0 1.71875,-1.8125 0,-0.59375 -2.6875,0 z m 4.03125,0 -1.15625,3.0625 0.96875,0 0.125,-0.53125 1.09375,0 0.15625,0.53125 0.96875,0 -1.125,-3.0625 -1.03125,0 z m 6.78125,0 0,0.75 0.96875,0 0,2.3125 0.9375,0 0,-2.3125 0.96875,0 0,-0.75 -2.875,0 z m 3.3125,0 0,1.8125 c 0,0.150589 0.03519,0.336044 0.09375,0.53125 0.03625,0.121307 0.0899,0.229415 0.1875,0.34375 0.099,0.114335 0.225231,0.18726 0.34375,0.25 0.118517,0.06135 0.261813,0.10409 0.4375,0.125 0.177079,0.02091 0.350804,0.03125 0.5,0.03125 0.257949,0 0.472195,-0.02543 0.65625,-0.09375 0.132459,-0.0488 0.255084,-0.130087 0.375,-0.25 0.121304,-0.121306 0.19422,-0.277152 0.25,-0.4375 0.05716,-0.161742 0.09375,-0.318736 0.09375,-0.5 l 0,-1.8125 -0.9375,0 0,1.84375 c -2e-6,0.170109 -0.06283,0.31283 -0.15625,0.40625 -0.09203,0.09203 -0.211865,0.125 -0.375,0.125 -0.164533,0 -0.28158,-0.03158 -0.375,-0.125 -0.09203,-0.09481 -0.156251,-0.23893 -0.15625,-0.40625 l 0,-1.84375 -0.9375,0 z m 3.59375,0 0,3.0625 1.40625,0 c 0.168713,0 0.354742,-0.03798 0.5625,-0.09375 0.15198,-0.04044 0.273786,-0.128693 0.40625,-0.25 0.132459,-0.1227 0.2386,-0.25763 0.3125,-0.4375 0.07529,-0.181262 0.124997,-0.45358 0.125,-0.78125 0,-0.209148 -0.01231,-0.406908 -0.0625,-0.59375 -0.0502,-0.186838 -0.138456,-0.35917 -0.25,-0.5 -0.111549,-0.140825 -0.266,-0.2372 -0.4375,-0.3125 C 46.986139,32.143459 46.77747,32.125003 46.5,32.125 l -1.40625,0 z m 3.34375,0 0,3.0625 0.9375,0 0,-3.0625 -0.9375,0 z m 3.09375,0.65625 c 0.199387,3e-6 0.353018,0.05365 0.46875,0.1875 0.117126,0.132464 0.187498,0.336376 0.1875,0.625 -2e-6,0.343007 -0.07596,0.58629 -0.1875,0.71875 -0.111549,0.132463 -0.26518,0.21875 -0.46875,0.21875 -0.197997,0 -0.353022,-0.0835 -0.46875,-0.21875 -0.114333,-0.135249 -0.156251,-0.346707 -0.15625,-0.65625 -10e-7,-0.312328 0.04052,-0.552247 0.15625,-0.6875 0.115728,-0.135245 0.277725,-0.187497 0.46875,-0.1875 z m -5.5,0.03125 0.25,0 c 0.245401,3e-6 0.394028,0.03634 0.5,0.15625 0.105963,0.119915 0.187498,0.362622 0.1875,0.6875 0,0.245404 -0.04634,0.421099 -0.09375,0.53125 -0.04741,0.108759 -0.10384,0.17413 -0.1875,0.21875 -0.08366,0.04323 -0.239507,0.0625 -0.4375,0.0625 l -0.21875,0 0,-1.65625 z M 18.8125,32.90625 19.15625,34 l -0.6875,0 0.34375,-1.09375 z m 13.09375,0 L 32.25,34 l -0.6875,0 0.34375,-1.09375 z m -8.875,4.96875 c -0.364643,2e-6 -0.649275,0.05669 -0.84375,0.21875 -0.194472,0.162065 -0.3125,0.417587 -0.3125,0.71875 0,0.240394 0.07964,0.420698 0.21875,0.5625 0.140453,0.141807 0.353732,0.24767 0.65625,0.3125 l 0.3125,0.0625 c 0.183669,0.04051 0.31423,0.07369 0.375,0.125 0.06213,0.05132 0.09375,0.12421 0.09375,0.21875 -2e-6,0.105341 -0.06576,0.19328 -0.15625,0.25 -0.09049,0.05671 -0.204836,0.09375 -0.375,0.09375 -0.167465,0 -0.350282,-0.04243 -0.53125,-0.09375 -0.179621,-0.05267 -0.369375,-0.116109 -0.5625,-0.21875 l 0,0.65625 C 22.099375,40.85283 22.306875,40.90104 22.5,40.9375 22.693123,40.97397 22.870724,41 23.0625,41 c 0.406505,0 0.70907,-0.08794 0.90625,-0.25 0.19852,-0.163412 0.312497,-0.41642 0.3125,-0.75 -3e-6,-0.253896 -0.07695,-0.424746 -0.21875,-0.5625 -0.141807,-0.137752 -0.382472,-0.24497 -0.71875,-0.3125 L 23,39.0625 c -0.158012,-0.03241 -0.25173,-0.08043 -0.3125,-0.125 -0.05942,-0.04592 -0.09375,-0.10647 -0.09375,-0.1875 0,-0.10804 0.03586,-0.19869 0.125,-0.25 0.08913,-0.05131 0.219877,-0.0625 0.40625,-0.0625 0.140453,0 0.310736,-0.0065 0.46875,0.03125 0.158008,0.03782 0.302633,0.11322 0.46875,0.1875 l 0,-0.65625 c -0.187725,-0.04997 -0.355685,-0.06943 -0.53125,-0.09375 -0.17557,-0.02566 -0.333888,-0.03125 -0.5,-0.03125 z m 2.78125,0 c -0.364642,3e-6 -0.649275,0.05669 -0.84375,0.21875 -0.194472,0.162065 -0.281251,0.417587 -0.28125,0.71875 -1e-6,0.240395 0.07965,0.420697 0.21875,0.5625 0.140453,0.141806 0.353733,0.24767 0.65625,0.3125 L 25.875,39.75 c 0.18367,0.04051 0.28298,0.07368 0.34375,0.125 0.06212,0.05132 0.09375,0.12421 0.09375,0.21875 -1e-6,0.105342 -0.03451,0.19329 -0.125,0.25 -0.09049,0.05671 -0.204836,0.09375 -0.375,0.09375 -0.167467,0 -0.350282,-0.04243 -0.53125,-0.09375 -0.17962,-0.05267 -0.369374,-0.11611 -0.5625,-0.21875 l 0,0.65625 c 0.193126,0.07158 0.369375,0.11979 0.5625,0.15625 C 25.474373,40.97397 25.683224,41 25.875,41 c 0.406505,0 0.709071,-0.08794 0.90625,-0.25 0.19852,-0.163412 0.281248,-0.416421 0.28125,-0.75 -2e-6,-0.253897 -0.0457,-0.424746 -0.1875,-0.5625 -0.141807,-0.137752 -0.382472,-0.24497 -0.71875,-0.3125 L 25.8125,39.0625 C 25.654489,39.03009 25.56077,38.98207 25.5,38.9375 25.44058,38.89158 25.40625,38.83103 25.40625,38.75 c -2e-6,-0.10804 0.03586,-0.19869 0.125,-0.25 0.08913,-0.05131 0.219876,-0.0625 0.40625,-0.0625 0.140453,0 0.310737,-0.0066 0.46875,0.03125 0.158009,0.03782 0.302634,0.11322 0.46875,0.1875 L 26.875,38 C 26.687275,37.950031 26.519315,37.93057 26.34375,37.90625 26.168181,37.88059 25.978613,37.875003 25.8125,37.875 z m 6.28125,0 c -0.349448,0.05242 -0.618889,0.196585 -0.84375,0.40625 -0.299816,0.279562 -0.4375,0.668714 -0.4375,1.15625 0,0.482137 0.143086,0.873992 0.4375,1.15625 C 31.544413,40.874658 31.933755,41 32.4375,41 c 0.222834,0 0.446916,-0.01524 0.65625,-0.0625 0.209328,-0.04727 0.399272,-0.12421 0.59375,-0.21875 l 0,-1.46875 -1.21875,0 0,0.53125 0.46875,0 0,0.59375 c -0.05537,0.0216 -0.11727,0.02045 -0.1875,0.03125 -0.06888,0.0095 -0.13772,0.03125 -0.21875,0.03125 -0.298467,10e-7 -0.525439,-0.07578 -0.6875,-0.25 -0.162064,-0.174215 -0.250001,-0.429925 -0.25,-0.75 -1e-6,-0.322772 0.08254,-0.577132 0.25,-0.75 0.168814,-0.174216 0.408129,-0.249998 0.71875,-0.25 0.167463,0 0.333884,0.01524 0.5,0.0625 0.167462,0.04727 0.362433,0.12286 0.53125,0.21875 l 0,-0.625 C 33.430335,38.01947 33.247518,37.94406 33.0625,37.90625 32.878826,37.86844 32.669977,37.875 32.46875,37.875 c -0.130326,10e-7 -0.258517,-0.01747 -0.375,0 z M 15,37.90625 l 0,3.03125 0.75,0 0,-2.21875 0.6875,1.65625 0.5,0 0.6875,-1.65625 0,2.21875 0.75,0 0,-3.03125 -1,0 -0.6875,1.625 -0.6875,-1.625 -1,0 z m 4.125,0 0,3.03125 2.15625,0 0,-0.59375 -1.375,0 0,-0.6875 1.25,0 0,-0.59375 -1.25,0 0,-0.5625 1.34375,0 0,-0.59375 -2.125,0 z m 15.25,0 0,3.03125 2.15625,0 0,-0.59375 -1.375,0 0,-0.6875 1.25,0 0,-0.59375 -1.25,0 0,-0.5625 1.3125,0 0,-0.59375 -2.09375,0 z m -5.84375,0.03125 -1.125,3.03125 0.78125,0 0.1875,-0.5625 1.21875,0 0.1875,0.5625 0.78125,0 -1.125,-3.03125 -0.90625,0 z M 29,38.625 l 0.40625,1.21875 -0.84375,0 L 29,38.625 z" /> - <path - style="font-size:6px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:0.8;fill:url(#radialGradient3296);fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans Bold" - id="path3842" - inkscape:connector-curvature="0" - d="m 40,39 41,0 0,1 -41,0 0,-1 z" /> - <path - style="font-size:6px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:0.8;fill:url(#radialGradient3293);fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans Bold" - id="path3840" - inkscape:connector-curvature="0" - d="m 15,69 66,0 0,1 -66,0 0,-1 z" /> - <path - style="font-size:6px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:0.8;fill:url(#radialGradient3290);fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans Bold" - id="text3348" - inkscape:connector-curvature="0" - d="m 59,70 0,14 -1,0 0,-14 1,0 z" /> - <path - style="font-size:6px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:0.8;fill:url(#radialGradient3287);fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans Bold" - id="path3870" - inkscape:connector-curvature="0" - d="m 41.78125,62.875 c -0.364641,3e-6 -0.649276,0.05669 -0.84375,0.21875 -0.194473,0.162065 -0.28125,0.417586 -0.28125,0.71875 0,0.240395 0.07964,0.420696 0.21875,0.5625 0.140454,0.141805 0.353733,0.24767 0.65625,0.3125 l 0.3125,0.0625 c 0.183669,0.04051 0.28298,0.07368 0.34375,0.125 0.06212,0.05132 0.09375,0.12421 0.09375,0.21875 -2e-6,0.105341 -0.03452,0.19328 -0.125,0.25 -0.09049,0.05671 -0.204835,0.09375 -0.375,0.09375 -0.167466,0 -0.350281,-0.04243 -0.53125,-0.09375 -0.17962,-0.05267 -0.369376,-0.11611 -0.5625,-0.21875 l 0,0.65625 c 0.193124,0.07157 0.369376,0.11979 0.5625,0.15625 0.193125,0.03647 0.401974,0.0625 0.59375,0.0625 0.406504,0 0.709071,-0.08794 0.90625,-0.25 0.19852,-0.163413 0.281247,-0.41642 0.28125,-0.75 -3e-6,-0.253896 -0.0457,-0.424745 -0.1875,-0.5625 C 42.701942,64.299749 42.461277,64.19253 42.125,64.125 L 41.78125,64.0625 C 41.623237,64.03009 41.52952,63.98207 41.46875,63.9375 41.40933,63.891583 41.375,63.83103 41.375,63.75 c 0,-0.10804 0.03586,-0.19868 0.125,-0.25 0.08913,-0.05131 0.219875,-0.0625 0.40625,-0.0625 0.140451,0 0.310736,-0.0066 0.46875,0.03125 0.158008,0.03782 0.302634,0.11322 0.46875,0.1875 l 0,-0.65625 C 42.656025,62.950032 42.488067,62.93057 42.3125,62.90625 42.136931,62.88059 41.947363,62.875003 41.78125,62.875 z m 3.0625,0 C 44.505191,62.92793 44.220809,63.069558 44,63.28125 c -0.294414,0.28091 -0.4375,0.672764 -0.4375,1.15625 0,0.482137 0.143086,0.873991 0.4375,1.15625 C 44.294412,65.874658 44.683754,66 45.1875,66 c 0.168813,0 0.344688,-0.02468 0.5,-0.0625 0.155308,-0.03781 0.294342,-0.08197 0.4375,-0.15625 l 0,-0.625 c -0.144508,0.09859 -0.265799,0.17283 -0.40625,0.21875 -0.140457,0.04592 -0.313442,0.0625 -0.46875,0.0625 -0.278209,10e-7 -0.49689,-0.102981 -0.65625,-0.28125 -0.159363,-0.178269 -0.218751,-0.406778 -0.21875,-0.71875 -10e-7,-0.313321 0.05939,-0.57173 0.21875,-0.75 0.15936,-0.178264 0.378041,-0.249998 0.65625,-0.25 0.155308,0 0.328293,0.01659 0.46875,0.0625 0.140451,0.04592 0.261742,0.12016 0.40625,0.21875 l 0,-0.625 c -0.143158,-0.07428 -0.282192,-0.14969 -0.4375,-0.1875 -0.155312,-0.0378 -0.331187,-0.03125 -0.5,-0.03125 -0.125937,10e-7 -0.230897,-0.01764 -0.34375,0 z m -18.875,0.03125 0,0.59375 1,0 0,2.4375 0.78125,0 0,-2.4375 1,0 0,-0.59375 -2.78125,0 z m 3.1875,0 0,3.03125 2.15625,0 0,-0.59375 -1.375,0 0,-0.6875 1.25,0 0,-0.59375 -1.25,0 0,-0.5625 1.3125,0 0,-0.59375 -2.09375,0 z m 17.625,0 0,3.03125 2.15625,0 0,-0.59375 -1.375,0 0,-0.6875 1.25,0 0,-0.59375 -1.25,0 0,-0.5625 1.3125,0 0,-0.59375 -2.09375,0 z m 2.84375,0 0,3.03125 0.71875,0 0,-2.0625 1.125,2.0625 0.84375,0 0,-3.03125 -0.71875,0 0,2.09375 -1.09375,-2.09375 -0.875,0 z m 3.46875,0 0,3.03125 2.15625,0 0,-0.59375 -1.375,0 0,-0.6875 1.25,0 0,-0.59375 -1.25,0 0,-0.5625 1.3125,0 0,-0.59375 -2.09375,0 z m 11.65625,0 0,0.59375 1,0 0,2.4375 0.78125,0 0,-2.4375 1,0 0,-0.59375 -2.78125,0 z m 6.09375,0 0,3.03125 0.78125,0 0,-1.1875 1.1875,1.1875 0.96875,0 -1.59375,-1.59375 1.4375,-1.4375 -0.90625,0 -1.09375,1.125 0,-1.125 -0.78125,0 z m 3.1875,0 0,3.03125 2.15625,0 0,-0.59375 -1.375,0 0,-0.6875 1.25,0 0,-0.59375 -1.25,0 0,-0.5625 1.34375,0 0,-0.59375 -2.125,0 z M 20,62.9375 l 0,3.03125 0.8125,0 c 0.453773,0 0.80436,-0.06017 1.03125,-0.125 0.226885,-0.06618 0.431684,-0.15584 0.59375,-0.3125 0.141802,-0.136402 0.24362,-0.290479 0.3125,-0.46875 0.06887,-0.179617 0.09375,-0.39676 0.09375,-0.625 -2e-6,-0.225535 -0.02487,-0.415479 -0.09375,-0.59375 C 22.68112,63.665484 22.579302,63.511405 22.4375,63.375 22.276784,63.218343 22.069286,63.09608 21.84375,63.03125 21.619563,62.96508 21.271675,62.9375 20.8125,62.9375 l -0.8125,0 z m 4.1875,0 -1.09375,3.03125 0.78125,0 0.1875,-0.5625 1.21875,0 0.1875,0.5625 0.78125,0 -1.125,-3.03125 -0.9375,0 z m 44.1875,0 -1.125,3.03125 0.78125,0 0.21875,-0.5625 1.21875,0 0.1875,0.5625 0.78125,0 -1.125,-3.03125 -0.9375,0 z m -47.59375,0.59375 0.28125,0 c 0.320071,2e-6 0.551284,0.06209 0.71875,0.21875 0.167457,0.156663 0.249997,0.387686 0.25,0.6875 -3e-6,0.301167 -0.08119,0.52949 -0.25,0.6875 -0.167467,0.158017 -0.400029,0.25 -0.71875,0.25 l -0.28125,0 0,-1.84375 z m 3.875,0.09375 0.4375,1.21875 -0.84375,0 0.40625,-1.21875 z m 44.1875,0 0.40625,1.21875 -0.8125,0 0.40625,-1.21875 z" /> - <path - style="font-size:6px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;opacity:0.8;fill:url(#radialGradient3284);fill-opacity:1;stroke:none;display:inline;font-family:DejaVu Sans;-inkscape-font-specification:DejaVu Sans Bold" - id="path3935" - inkscape:connector-curvature="0" - d="m 37,70 0,14 -1,0 0,-14 1,0 z" /> - <g - id="layer5"> - <path - d="M 12,90 C 8.676,90 6,87.324 6,84 L 6,82 6,14 6,12 c 0,-0.334721 0.04135,-0.6507 0.09375,-0.96875 0.0487,-0.295596 0.09704,-0.596915 0.1875,-0.875 C 6.29113,10.12587 6.302142,10.09265 6.3125,10.0625 6.411365,9.774729 6.5473802,9.515048 6.6875,9.25 6.8320918,8.976493 7.0031161,8.714385 7.1875,8.46875 7.3718839,8.223115 7.5612765,7.995278 7.78125,7.78125 8.221197,7.353194 8.72416,6.966724 9.28125,6.6875 9.559795,6.547888 9.8547231,6.440553 10.15625,6.34375 9.9000482,6.443972 9.6695391,6.580022 9.4375,6.71875 c -0.00741,0.0044 -0.023866,-0.0045 -0.03125,0 -0.031933,0.0193 -0.062293,0.04251 -0.09375,0.0625 -0.120395,0.0767 -0.2310226,0.163513 -0.34375,0.25 -0.1061728,0.0808 -0.2132809,0.161112 -0.3125,0.25 C 8.4783201,7.442683 8.3087904,7.626638 8.15625,7.8125 8.0486711,7.942755 7.9378561,8.077785 7.84375,8.21875 7.818661,8.25713 7.805304,8.30462 7.78125,8.34375 7.716487,8.446782 7.6510225,8.548267 7.59375,8.65625 7.4927417,8.850956 7.3880752,9.071951 7.3125,9.28125 7.30454,9.30306 7.288911,9.3218 7.28125,9.34375 7.2494249,9.4357 7.2454455,9.530581 7.21875,9.625 7.1884177,9.731618 7.1483606,9.828031 7.125,9.9375 7.0521214,10.279012 7,10.635705 7,11 l 0,2 0,68 0,2 c 0,2.781848 2.2181517,5 5,5 l 2,0 68,0 2,0 c 2.781848,0 5,-2.218152 5,-5 l 0,-2 0,-68 0,-2 C 89,10.635705 88.94788,10.279012 88.875,9.9375 88.83085,9.730607 88.78662,9.539842 88.71875,9.34375 88.71105,9.3218 88.69545,9.30306 88.6875,9.28125 88.62476,9.107511 88.549117,8.913801 88.46875,8.75 88.42717,8.6672 88.38971,8.580046 88.34375,8.5 88.28915,8.40279 88.216976,8.31165 88.15625,8.21875 88.06214,8.077785 87.951329,7.942755 87.84375,7.8125 87.700576,7.63805 87.540609,7.465502 87.375,7.3125 87.36383,7.3023 87.35502,7.29135 87.34375,7.28125 87.205364,7.155694 87.058659,7.046814 86.90625,6.9375 86.803679,6.86435 86.701932,6.784136 86.59375,6.71875 c -0.0074,-0.0045 -0.02384,0.0044 -0.03125,0 -0.232039,-0.138728 -0.462548,-0.274778 -0.71875,-0.375 0.301527,0.0968 0.596455,0.204138 0.875,0.34375 0.55709,0.279224 1.060053,0.665694 1.5,1.09375 0.219973,0.214028 0.409366,0.441865 0.59375,0.6875 0.184384,0.245635 0.355408,0.507743 0.5,0.78125 0.14012,0.265048 0.276135,0.524729 0.375,0.8125 0.01041,0.03078 0.02133,0.06274 0.03125,0.09375 0.09046,0.278085 0.1388,0.579404 0.1875,0.875 C 89.95865,11.3493 90,11.665279 90,12 l 0,2 0,68 0,2 c 0,3.324 -2.676,6 -6,6 l -72,0 z" - inkscape:connector-curvature="0" - id="path3615" - style="opacity:0.2;fill:url(#radialGradient3619);fill-opacity:1;fill-rule:nonzero;stroke:none" /> - <rect - width="66" - height="66" - rx="12" - ry="12" - x="15" - y="15" - clip-path="url(#clipPath3613)" - id="rect3171" - style="opacity:0.1;fill:url(#linearGradient3613);fill-opacity:1;fill-rule:nonzero;stroke:#ffffff;stroke-width:0.5;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;filter:url(#filter3794)" /> - </g> - <g - id="layer3"> - <path - d="M 12,6 C 8.676,6 6,8.676 6,12 l 0,12 84,0 0,-12 C 90,8.676 87.324,6 84,6 L 12,6 z" - inkscape:connector-curvature="0" - id="rect4237" - style="fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none" /> - <rect - width="84" - height="6" - x="6" - y="24" - id="rect3250" - style="opacity:0.8;fill:url(#linearGradient4262);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <rect - width="84" - height="5" - x="6" - y="15" - id="rect4289" - style="opacity:0.3;fill:url(#linearGradient4320);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <g - transform="translate(1,0)" - id="g4299"> - <path - d="M 28.333333,6 23,15 28.333333,24 39,24 33.666667,15 39,6 28.333333,6 z" - inkscape:connector-curvature="0" - id="path4293" - style="fill:#f0f0f0;fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <path - d="M 50.333333,6 45,15 50.333333,24 61,24 55.666667,15 61,6 50.333333,6 z" - inkscape:connector-curvature="0" - id="path4295" - style="fill:#f0f0f0;fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <path - d="M 72.333333,6 67,15 72.333333,24 83,24 77.666667,15 83,6 72.333333,6 z" - inkscape:connector-curvature="0" - id="path4297" - style="fill:#f0f0f0;fill-opacity:1;fill-rule:evenodd;stroke:none" /> - </g> - <rect - width="84" - height="5" - x="6" - y="10" - id="rect4291" - style="opacity:0.7;fill:url(#linearGradient4310);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <rect - width="84" - height="5" - x="6" - y="19" - id="rect4324" - style="opacity:0.7;fill:url(#linearGradient4326);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <path - d="m 12.005391,10.5 c 1.109513,0 1.770155,0.983693 6.03003,4.302704 4.304387,3.353689 5.477493,3.726472 5.477493,5.074645 0,1.348423 -1.088205,1.622651 -2.439921,1.622651 l -8.133072,0 C 11.588205,21.5 10.5,20.414446 10.5,19.066023 l 0,-6.490605 C 10.5,11.226995 10.967611,10.5 12.005391,10.5 z" - inkscape:connector-curvature="0" - id="path4333" - style="fill:url(#linearGradient4371);fill-opacity:1;fill-rule:evenodd;stroke:url(#linearGradient4373);stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" /> - <path - d="m 12.75,14.9375 a 1.4375,1.4375 0 1 1 -2.875,0 1.4375,1.4375 0 1 1 2.875,0 z" - transform="matrix(0.81330712,0,0,0.81132563,3.5868895,1.0746776)" - id="path4335" - style="fill:url(#radialGradient4379);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <path - d="m 12.75,14.9375 a 1.4375,1.4375 0 1 1 -2.875,0 1.4375,1.4375 0 1 1 2.875,0 z" - transform="matrix(0.81330712,0,0,0.81132563,3.5868895,7.09897)" - id="path4337" - style="fill:url(#radialGradient4377);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - <path - d="m 12.75,14.9375 a 1.4375,1.4375 0 1 1 -2.875,0 1.4375,1.4375 0 1 1 2.875,0 z" - transform="matrix(0.81330712,0,0,0.81132563,11.618297,7.09897)" - id="path4339" - style="fill:url(#radialGradient4375);fill-opacity:1;fill-rule:evenodd;stroke:none" /> - </g> - <g - id="layer4"> - <path - d="M 12,6 C 8.676,6 6,8.676 6,12 l 0,2 0,68 0,2 c 0,0.334721 0.04135,0.6507 0.09375,0.96875 0.0487,0.295596 0.09704,0.596915 0.1875,0.875 0.00988,0.03038 0.020892,0.0636 0.03125,0.09375 0.098865,0.287771 0.2348802,0.547452 0.375,0.8125 0.1445918,0.273507 0.3156161,0.535615 0.5,0.78125 0.1843839,0.245635 0.3737765,0.473472 0.59375,0.6875 0.439947,0.428056 0.94291,0.814526 1.5,1.09375 0.278545,0.139612 0.5734731,0.246947 0.875,0.34375 -0.2562018,-0.100222 -0.4867109,-0.236272 -0.71875,-0.375 -0.00741,-0.0044 -0.023866,0.0045 -0.03125,0 -0.031933,-0.0193 -0.062293,-0.04251 -0.09375,-0.0625 -0.120395,-0.0767 -0.2310226,-0.163513 -0.34375,-0.25 -0.1061728,-0.0808 -0.2132809,-0.161112 -0.3125,-0.25 C 8.4783201,88.557317 8.3087904,88.373362 8.15625,88.1875 8.0486711,88.057245 7.9378561,87.922215 7.84375,87.78125 7.818661,87.74287 7.805304,87.69538 7.78125,87.65625 7.716487,87.553218 7.6510225,87.451733 7.59375,87.34375 7.4927417,87.149044 7.3880752,86.928049 7.3125,86.71875 7.30454,86.69694 7.288911,86.6782 7.28125,86.65625 7.2494249,86.5643 7.2454455,86.469419 7.21875,86.375 7.1884177,86.268382 7.1483606,86.171969 7.125,86.0625 7.0521214,85.720988 7,85.364295 7,85 L 7,83 7,15 7,13 C 7,10.218152 9.2181517,8 12,8 l 2,0 68,0 2,0 c 2.781848,0 5,2.218152 5,5 l 0,2 0,68 0,2 c 0,0.364295 -0.05212,0.720988 -0.125,1.0625 -0.04415,0.206893 -0.08838,0.397658 -0.15625,0.59375 -0.0077,0.02195 -0.0233,0.04069 -0.03125,0.0625 -0.06274,0.173739 -0.138383,0.367449 -0.21875,0.53125 -0.04158,0.0828 -0.07904,0.169954 -0.125,0.25 -0.0546,0.09721 -0.126774,0.18835 -0.1875,0.28125 -0.09411,0.140965 -0.204921,0.275995 -0.3125,0.40625 -0.143174,0.17445 -0.303141,0.346998 -0.46875,0.5 -0.01117,0.0102 -0.01998,0.02115 -0.03125,0.03125 -0.138386,0.125556 -0.285091,0.234436 -0.4375,0.34375 -0.102571,0.07315 -0.204318,0.153364 -0.3125,0.21875 -0.0074,0.0045 -0.02384,-0.0044 -0.03125,0 -0.232039,0.138728 -0.462548,0.274778 -0.71875,0.375 0.301527,-0.0968 0.596455,-0.204138 0.875,-0.34375 0.55709,-0.279224 1.060053,-0.665694 1.5,-1.09375 0.219973,-0.214028 0.409366,-0.441865 0.59375,-0.6875 0.184384,-0.245635 0.355408,-0.507743 0.5,-0.78125 0.14012,-0.265048 0.276135,-0.524729 0.375,-0.8125 0.01041,-0.03078 0.02133,-0.06274 0.03125,-0.09375 0.09046,-0.278085 0.1388,-0.579404 0.1875,-0.875 C 89.95865,84.6507 90,84.334721 90,84 l 0,-2 0,-68 0,-2 C 90,8.676 87.324,6 84,6 L 12,6 z" - inkscape:connector-curvature="0" - id="rect3728" - style="opacity:0.3;fill:url(#linearGradient3188);fill-opacity:1;fill-rule:nonzero;stroke:none" /> - </g> - <g - id="g4513" - transform="matrix(0.6394564,0,0,0.6394564,84.664912,2.0456889)"> - <path - style="fill:url(#linearGradient3157-9);fill-opacity:1;stroke:#34870e;stroke-width:2.34574246;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - id="path3261" - inkscape:connector-curvature="0" - d="m -65.015175,50.278074 0,54.903746 c -3.855793,-1.50998 -8.894464,-1.76801 -14.09417,-0.36139 -9.717741,2.62883 -16.233352,10.00877 -14.521267,16.45962 1.712085,6.45086 10.979991,9.56093 20.697733,6.9321 8.256133,-2.23343 14.170779,-7.8963 14.718388,-13.50281 l 0.06571,0 0,-40.377001 35.186145,4.369521 0,39.09572 c -3.855791,-1.50998 -8.894464,-1.76801 -14.09417,-0.36139 -9.717739,2.62883 -16.233352,10.00876 -14.521267,16.45962 1.712087,6.45086 10.979991,9.56093 20.697733,6.9321 8.256135,-2.23343 14.170779,-7.8963 14.718388,-13.50281 l 0.06571,0 0,-70.542025 -48.918927,-6.505001 z m 6.866391,13.212595 35.186145,4.960885 0,4.435229 -35.186145,-4.369522 0,-5.026592 z" /> - <path - inkscape:connector-curvature="0" - style="fill:url(#radialGradient3159-7);fill-opacity:1;stroke:none" - id="path2494" - transform="matrix(1.0233043,-0.24105639,0.24105639,1.0233043,-138.19892,40.809487)" - d="m 56.391766,82.480003 a 14.672467,9.2807773 0 1 1 -29.344933,0 14.672467,9.2807773 0 1 1 29.344933,0 z" /> - <path - inkscape:connector-curvature="0" - style="fill:url(#radialGradient3161-3);fill-opacity:1;stroke:none" - id="path3278" - transform="matrix(1.0233043,-0.24105639,0.24105639,1.0233043,-96.754882,53.447131)" - d="m 56.391766,82.480003 a 14.672467,9.2807773 0 1 1 -29.344933,0 14.672467,9.2807773 0 1 1 29.344933,0 z" /> - <path - style="fill:none;stroke:url(#radialGradient3163-8);stroke-width:3.12765646;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - id="path3282" - inkscape:connector-curvature="0" - d="m -61.35495,88.196416 0,-33.510614 24.18021,3.942425" /> - <path - style="fill:none;stroke:url(#radialGradient3222);stroke-width:4.93222046;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" - id="path3292" - inkscape:connector-curvature="0" - d="m -19.212614,107.89627 0,-42.617148" /> - </g> -</svg> diff --git a/rapid/glade3/video66.png b/rapid/glade3/video66.png Binary files differdeleted file mode 100644 index 5502609..0000000 --- a/rapid/glade3/video66.png +++ /dev/null diff --git a/rapid/glade3/xmp.ui b/rapid/glade3/xmp.ui deleted file mode 100644 index c0fd03b..0000000 --- a/rapid/glade3/xmp.ui +++ /dev/null @@ -1,1376 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<interface> - <requires lib="gtk+" version="2.24"/> - <!-- interface-naming-policy project-wide --> - <object class="GtkDialog" id="dialog1"> - <property name="can_focus">False</property> - <property name="border_width">5</property> - <property name="type_hint">dialog</property> - <child internal-child="vbox"> - <object class="GtkVBox" id="dialog-vbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="spacing">2</property> - <child internal-child="action_area"> - <object class="GtkHButtonBox" id="dialog-action_area1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="layout_style">end</property> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="pack_type">end</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkHBox" id="hbox1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <placeholder/> - </child> - <child> - <object class="GtkNotebook" id="notebook"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <child> - <object class="GtkTable" id="table1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">10</property> - <property name="n_columns">5</property> - <child> - <object class="GtkLabel" id="label14"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="label" translatable="yes" comments="The Description field, often referred to as "Caption," should report the who, what and why of what the photograph depicts. Source: http://www.photometadata.org/META-Resources-Field-Guide-to-Metadata#Description">Description:</property> - </object> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <object class="GtkViewport" id="viewport1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkTextView" id="textview1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label15"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="A headline is a brief, publishable synopsis or summary of the contents of the photograph. Source: http://www.photometadata.org/META-Resources-Field-Guide-to-Metadata#Headline">Headline:</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry1"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label16"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="label" translatable="yes" comments="Keyword terms or phrases describe the subject of content in the photograph. Source: http://www.photometadata.org/META-Resources-Field-Guide-to-Metadata#Keywords">Keywords:</property> - </object> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow3"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <object class="GtkViewport" id="viewport3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkTextView" id="textview2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label17"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="The name of the person writing, editing or correcting the description of the photograph. Source: http://www.photometadata.org/META-Resources-Field-Guide-to-Metadata#Description%20writer">Description writer:</property> - </object> - <packing> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label18"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="The Copyright Notice for the photo.">Copyright:</property> - </object> - <packing> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry3"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label19"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="The location of a web page describing the owner and/or rights statement for this photo.">Copyright URL:</property> - </object> - <packing> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry4"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label20"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="label" translatable="yes" comments="Name or names of a person or the people shown in the photo.">Person(s) shown:</property> - </object> - <packing> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow4"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <object class="GtkViewport" id="viewport4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkTextView" id="textview3"> - <property name="visible">True</property> - <property name="can_focus">True</property> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - </packing> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - </object> - </child> - <child type="tab"> - <object class="GtkLabel" id="label1"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Description</property> - </object> - <packing> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkTable" id="table3"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">16</property> - <property name="n_columns">5</property> - <child> - <object class="GtkLabel" id="label21"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Location Created</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="right_attach">5</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label22"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Sublocation:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label23"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">City:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label24"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Province/state:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label25"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Country name:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label26"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Country ISO-code:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label27"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">World region:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry5"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry6"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry7"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry8"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry9"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry10"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label28"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Location Shown</property> - <attributes> - <attribute name="weight" value="bold"/> - </attributes> - </object> - <packing> - <property name="right_attach">5</property> - <property name="top_attach">8</property> - <property name="bottom_attach">9</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label29"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Sublocation:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">9</property> - <property name="bottom_attach">10</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label30"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">City:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">10</property> - <property name="bottom_attach">11</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label31"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Province/state:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">11</property> - <property name="bottom_attach">12</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label32"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Country name:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">12</property> - <property name="bottom_attach">13</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label33"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">Country ISO-code:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">13</property> - <property name="bottom_attach">14</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label34"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">World region:</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">14</property> - <property name="bottom_attach">15</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry11"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">9</property> - <property name="bottom_attach">10</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry12"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">10</property> - <property name="bottom_attach">11</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry13"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">11</property> - <property name="bottom_attach">12</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry14"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">12</property> - <property name="bottom_attach">13</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry15"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">13</property> - <property name="bottom_attach">14</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkEntry" id="entry16"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="invisible_char">•</property> - <property name="invisible_char_set">True</property> - <property name="primary_icon_activatable">False</property> - <property name="secondary_icon_activatable">False</property> - <property name="primary_icon_sensitive">True</property> - <property name="secondary_icon_sensitive">True</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">14</property> - <property name="bottom_attach">15</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <placeholder/> - </child> - <child> - <object class="GtkButton" id="copy_location_button"> - <property name="label" translatable="yes">Copy to Location Shown</property> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="use_action_appearance">False</property> - </object> - <packing> - <property name="left_attach">2</property> - <property name="right_attach">3</property> - <property name="top_attach">7</property> - <property name="bottom_attach">8</property> - </packing> - </child> - </object> - <packing> - <property name="position">1</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="label35"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes">Location</property> - </object> - <packing> - <property name="position">1</property> - <property name="tab_fill">False</property> - </packing> - </child> - <child> - <object class="GtkTable" id="table2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="n_rows">10</property> - <property name="n_columns">2</property> - <property name="column_spacing">12</property> - <property name="row_spacing">6</property> - <child> - <object class="GtkLabel" id="label4"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="Person who created the photograph">Creator:</property> - </object> - <packing> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label5"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="Job title of the person who created the photograph">Creator's job title:</property> - </object> - <packing> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label6"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="yalign">0</property> - <property name="label" translatable="yes" comments="Address of the person who created the photograph">Address:</property> - </object> - <packing> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options">GTK_FILL</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label7"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes">City:</property> - </object> - <packing> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label8"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="State or province of the person who created the photograph">State/province:</property> - </object> - <packing> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label9"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="Postal code of the person who created the photograph">Postal code:</property> - </object> - <packing> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label10"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="Country of the person who created the photograph">Country: </property> - </object> - <packing> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label11"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="Phone numbers / number of the person who created the photograph">Phone(s):</property> - </object> - <packing> - <property name="top_attach">7</property> - <property name="bottom_attach">8</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label12"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="Email address / addresses of the person who created the photograph">Email(s):</property> - </object> - <packing> - <property name="top_attach">8</property> - <property name="bottom_attach">9</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkLabel" id="label13"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="xalign">0</property> - <property name="label" translatable="yes" comments="Websites / website of the person who created the photograph">Website(s):</property> - </object> - <packing> - <property name="top_attach">9</property> - <property name="bottom_attach">10</property> - <property name="x_options">GTK_FILL</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="creator_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="has_entry">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="job_title_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="has_entry">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">1</property> - <property name="bottom_attach">2</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="city_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="has_entry">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">3</property> - <property name="bottom_attach">4</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="state_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="has_entry">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">4</property> - <property name="bottom_attach">5</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="postal_code_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="has_entry">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">5</property> - <property name="bottom_attach">6</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="country_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="has_entry">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">6</property> - <property name="bottom_attach">7</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="phone_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="has_entry">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">7</property> - <property name="bottom_attach">8</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="email_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="has_entry">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">8</property> - <property name="bottom_attach">9</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkComboBox" id="website_combobox"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="has_entry">True</property> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">9</property> - <property name="bottom_attach">10</property> - <property name="y_options"></property> - </packing> - </child> - <child> - <object class="GtkScrolledWindow" id="scrolledwindow2"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="hscrollbar_policy">automatic</property> - <property name="vscrollbar_policy">automatic</property> - <child> - <object class="GtkViewport" id="viewport2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <child> - <object class="GtkTextView" id="address_textview"> - <property name="visible">True</property> - <property name="can_focus">True</property> - </object> - </child> - </object> - </child> - </object> - <packing> - <property name="left_attach">1</property> - <property name="right_attach">2</property> - <property name="top_attach">2</property> - <property name="bottom_attach">3</property> - </packing> - </child> - </object> - <packing> - <property name="position">2</property> - </packing> - </child> - <child type="tab"> - <object class="GtkLabel" id="label2"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="label" translatable="yes" comments="Contact information for the photographer">Photographer</property> - </object> - <packing> - <property name="position">2</property> - <property name="tab_fill">False</property> - </packing> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <placeholder/> - </child> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <placeholder/> - </child> - </object> - </child> - </object> -</interface> diff --git a/rapid/glade3/zoom-in.png b/rapid/glade3/zoom-in.png Binary files differdeleted file mode 100644 index af84614..0000000 --- a/rapid/glade3/zoom-in.png +++ /dev/null diff --git a/rapid/glade3/zoom-out.png b/rapid/glade3/zoom-out.png Binary files differdeleted file mode 100644 index 6c674df..0000000 --- a/rapid/glade3/zoom-out.png +++ /dev/null diff --git a/rapid/higdefaults.py b/rapid/higdefaults.py deleted file mode 100644 index 9dfd7e5..0000000 --- a/rapid/higdefaults.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: latin1 -*- -VERTICAL_CONTROL_LABEL_SPACE = CONTROL_LABEL_SPACE = 12 -CONTROL_IN_TABLE_SPACE = 6 -WINDOW_BORDER_SPACE = 12 -NESTED_CONTROLS_SPACE = 24 -VERTICAL_CONTROL_SPACE = 18 -VERTICAL_GROUP_SPACE = 24 -HORIZONTAL_GROUP_SPACE = 24 diff --git a/rapid/metadataexiftool.py b/rapid/metadataexiftool.py deleted file mode 100755 index 2524da1..0000000 --- a/rapid/metadataexiftool.py +++ /dev/null @@ -1,226 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import subprocess -import json -import datetime, time -import string - -import multiprocessing -import logging -logger = multiprocessing.get_logger() - -def version_info(): - """returns the version of exiftool being used""" - try: - # unfortunately subprocess.check_output does not exist on python 2.6 - proc = subprocess.Popen(['exiftool', '-ver'], stdout=subprocess.PIPE) - v = proc.communicate()[0].strip() - except OSError: - v = None - return v - -EXIFTOOL_VERSION = version_info() - -class ExifToolMetaData: - """ - Class to use when a python based metadata parser fails to correctly load - necessary metadata. Calls exiftool as a subprocess. It is therefore slow, - but in contrast to exiv2 or kaa metadata, exiftool somtimes gives better - output. - """ - def __init__(self, filename): - self.filename = filename - self.metadata = None - self.metadata_string_format = None - self.exiftool_error = "Error encountered using exiftool with file %s" - self.exiftool_output = "Unexpected output from exiftool with file %s" - - def _get(self, key, missing): - - if key == "VideoStreamType" or "FileNumber": - # special case: want exiftool's string formatting - if self.metadata_string_format is None: - try: - proc = subprocess.Popen(['exiftool', '-j', self.filename], stdout=subprocess.PIPE) - s = proc.communicate()[0] - except: - logger.error(self.exiftool_error, self.filename) - return missing - try: - self.metadata_string_format = json.loads(s) - except: - logger.error(self.exiftool_output, self.filename) - return missing - - try: - v = self.metadata_string_format[0][key] - except: - return missing - return v - - elif self.metadata is None: - # note: exiftool's string formatting is OFF (-n switch) - try: - proc = subprocess.Popen(['exiftool', '-j', '-n', self.filename], stdout=subprocess.PIPE) - s = proc.communicate()[0] - except: - logger.error(self.exiftool_error, self.filename) - return missing - try: - self.metadata = json.loads(s) - except: - logger.error(self.exiftool_output, self.filename) - return missing - - try: - v = self.metadata[0][key] - except: - return missing - return v - - - def date_time(self, missing=''): - """ - Returns in python datetime format the date and time the image was - recorded. - - Trys to get value from key "DateTimeOriginal" - If that fails, tries "CreateDate" - - Returns missing either metadata value is not present. - """ - d = self._get('DateTimeOriginal', None) - if d is None: - d = self._get('CreateDate', None) - if d is None: - d = self._get('FileModifyDate', None) - if d is not None: - try: - # returned value may or may not have a time offset - # strip it if need be - dt = d[:19] - dt = datetime.datetime.strptime(dt, "%Y:%m:%d %H:%M:%S") - except: - logger.error("Error reading date metadata with file %s", self.filename) - return missing - - return dt - else: - return missing - - def time_stamp(self, missing=''): - """ - Returns a float value representing the time stamp, if it exists - """ - dt = self.date_time(missing=None) - if dt: - # convert it to a time stamp (not optimal, but better than nothing!) - v = time.mktime(dt.timetuple()) - else: - v = missing - return v - - def file_number(self, missing=''): - v = self._get("FileNumber", None) - if v is not None: - return str(v) - else: - return missing - - def width(self, missing=''): - v = self._get('ImageWidth', None) - if v is not None: - return str(v) - else: - return missing - - def height(self, missing=''): - v = self._get('ImageHeight', None) - if v is not None: - return str(v) - else: - return missing - - def length(self, missing=''): - """ - return the duration (length) of the video, rounded to the nearest second, in string format - """ - v = self._get("Duration", None) - if v is not None: - try: - v = float(v) - v = "%0.f" % v - except: - return missing - return v - else: - return missing - - def frames_per_second(self, stream=0, missing=''): - """ - value stream is ignored (kept for compatibilty with code calling kaa) - """ - v = self._get("FrameRate", None) - if v is None: - v = self._get("VideoFrameRate", None) - - if v is None: - return missing - try: - v = '%.0f' % v - except: - return missing - return v - - def codec(self, stream=0, missing=''): - """ - value stream is ignored (kept for compatibilty with code calling kaa) - """ - v = self._get("VideoStreamType", None) - if v is None: - v = self._get("VideoCodec", None) - if v is not None: - return v - else: - return missing - - def fourcc(self, stream=0, missing=''): - """ - value stream is ignored (kept for compatibilty with code calling kaa) - """ - return self._get("CompressorID", missing) - -if __name__ == '__main__': - import sys - - - if (len(sys.argv) != 2): - print 'Usage: ' + sys.argv[0] + ' path/to/video/containing/metadata' - sys.exit(0) - - else: - m = ExifToolMetaData(sys.argv[1]) - dt = m.date_time() - print dt - print "%sx%s" % (m.width(), m.height()) - print m.length() - print m.frames_per_second() - print m.codec() diff --git a/rapid/metadataphoto.py b/rapid/metadataphoto.py deleted file mode 100644 index 4e88994..0000000 --- a/rapid/metadataphoto.py +++ /dev/null @@ -1,463 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2007-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import re -import datetime -import sys -import config -import types -import time - -try: - import pyexiv2 -except ImportError: - sys.stderr.write("You need to install pyexiv2, the python binding for exiv2, to run this program.\n" ) - sys.exit(1) - -import metadataexiftool - -def __version_info(version): - if not version: - return '' - else: - v = '' - for i in version: - v += '.%s' % i - return v[1:] - -def pyexiv2_version_info(): - return __version_info(pyexiv2.version_info) - -def exiv2_version_info(): - return __version_info(pyexiv2.exiv2_version_info) - - -class MetaData(pyexiv2.metadata.ImageMetadata): - """ - Class providing human readable access to image metadata - - """ - - def __init__(self, full_file_name): - pyexiv2.metadata.ImageMetadata.__init__(self, full_file_name) - self.rpd_metadata_exiftool = None - self.rpd_full_file_name = full_file_name - - def _load_exiftool(self): - if self.rpd_metadata_exiftool is None: - self.rpd_metadata_exiftool = metadataexiftool.ExifToolMetaData(self.rpd_full_file_name) - - - def aperture(self, missing=''): - """ - Returns in string format the floating point value of the image's aperture. - - Returns missing if the metadata value is not present. - """ - - try: - - a = self["Exif.Photo.FNumber"].value - - a = float(a.numerator) / float(a.denominator) - return "%.1f" % a - except: - return missing - - def iso(self, missing=''): - """ - Returns in string format the integer value of the image's ISO. - - Returns missing if the metadata value is not present. - """ - try: - return self["Exif.Photo.ISOSpeedRatings"].human_value - except: - return missing - - def exposure_time(self, alternativeFormat=False, missing=''): - """ - Returns in string format the exposure time of the image. - - Returns missing if the metadata value is not present. - - alternativeFormat is useful if the value is going to be used in a - purpose where / is an invalid character, e.g. file system names. - - alternativeFormat is False: - For exposures less than one second, the result is formatted as a - fraction e.g. 1/125 - For exposures greater than or equal to one second, the value is - formatted as an integer e.g. 30 - - alternativeFormat is True: - For exposures less than one second, the result is formatted as an - integer e.g. 125 - For exposures less than one second but more than or equal to - one tenth of a second, the result is formatted as an integer - e.g. 3 representing 3/10 of a second - For exposures greater than or equal to one second, the value is - formatted as an integer with a trailing s e.g. 30s - """ - - try: - - e = self["Exif.Photo.ExposureTime"].value - - e0 = int(e.numerator) - e1 = int(e.denominator) - - if e1 > e0: - if alternativeFormat: - if e0 == 1: - return str(e1) - else: - return str(e0) - else: - return "%s/%s" % (e0,e1) - elif e0 > e1: - e = float(e0) / e1 - if alternativeFormat: - return "%.0fs" % e - else: - return "%.0f" % e - else: - return "1s" - except: - return missing - - def focal_length(self, missing=''): - """ - Returns in string format the focal length of the lens used to record the image. - - Returns missing if the metadata value is not present. - """ - try: - f = self["Exif.Photo.FocalLength"].value - f0 = float(f.numerator) - f1 = float(f.denominator) - - return "%.0f" % (f0 / f1) - except: - return missing - - - def camera_make(self, missing=''): - """ - Returns in string format the camera make (manufacturer) used to record the image. - - Returns missing if the metadata value is not present. - """ - try: - return self["Exif.Image.Make"].value.strip() - except: - return missing - - def camera_model(self, missing=''): - """ - Returns in string format the camera model used to record the image. - - Returns missing if the metadata value is not present. - """ - try: - return self["Exif.Image.Model"].value.strip() - except: - return missing - - def camera_serial(self, missing=''): - try: - keys = self.exif_keys - if 'Exif.Canon.SerialNumber' in keys: - v = self['Exif.Canon.SerialNumber'].raw_value - elif 'Exif.Nikon3.SerialNumber' in keys: - v = self['Exif.Nikon3.SerialNumber'].raw_value - elif 'Exif.OlympusEq.SerialNumber' in keys: - v = self['Exif.OlympusEq.SerialNumber'].raw_value - elif 'Exif.Olympus.SerialNumber' in keys: - v = self['Exif.Olympus.SerialNumber'].raw_value - elif 'Exif.Olympus.SerialNumber2' in keys: - v = self['Exif.Olympus.SerialNumber2'].raw_value - elif 'Exif.Panasonic.SerialNumber' in keys: - v = self['Exif.Panasonic.SerialNumber'].raw_value - elif 'Exif.Fujifilm.SerialNumber' in keys: - v = self['Exif.Fujifilm.SerialNumber'].raw_value - elif 'Exif.Image.CameraSerialNumber' in keys: - v = self['Exif.Image.CameraSerialNumber'].raw_value - else: - return missing - v = str(v) # probably not necessary, but just in case - return v.strip() - except: - return missing - - def shutter_count(self, missing=''): - try: - keys = self.exif_keys - if 'Exif.Nikon3.ShutterCount' in keys: - v = self['Exif.Nikon3.ShutterCount'].raw_value - elif 'Exif.Canon.FileNumber' in keys: - v = self['Exif.Canon.FileNumber'].raw_value - elif 'Exif.Canon.ImageNumber' in keys: - v = self['Exif.Canon.ImageNumber'].raw_value - else: - return missing - return str(v) - except: - return missing - - def file_number(self, missing=''): - """returns Exif.CanonFi.FileNumber, not to be confused with - Exif.Canon.FileNumber""" - try: - if 'Exif.CanonFi.FileNumber' in self.exif_keys: - self._load_exiftool() - return self.rpd_metadata_exiftool.file_number(missing) - else: - return missing - except: - return missing - - - def owner_name(self, missing=''): - """ returns camera name recorded by select Canon cameras""" - try: - return self['Exif.Canon.OwnerName'].value.strip() - except: - return missing - - def copyright(self, missing=''): - """returns copyright exif information""" - try: - return self['Exif.Image.Copyright'].value.strip() - except: - return missing - - def artist(self, missing=''): - """returns exif artis information""" - try: - return self['Exif.Image.Artist'].value.strip() - except: - return missing - - def short_camera_model(self, includeCharacters = '', missing=''): - """ - Returns in shorterned string format the camera model used to record the image. - - Returns missing if the metadata value is not present. - - The short format is determined by the first occurrence of a digit in the - camera model, including all alphaNumeric characters before and after - that digit up till a non-alphanumeric character, but with these interventions: - - 1. Canon "Mark" designations are shortened prior to conversion. - 2. Names like "Canon EOS DIGITAL REBEL XSi" do not have a number and must - and treated differently (see below) - - Examples: - Canon EOS 300D DIGITAL -> 300D - Canon EOS 5D -> 5D - Canon EOS 5D Mark II -> 5DMkII - NIKON D2X -> D2X - NIKON D70 -> D70 - X100,D540Z,C310Z -> X100 - Canon EOS DIGITAL REBEL XSi -> XSi - Canon EOS Digital Rebel XS -> XS - Canon EOS Digital Rebel XTi -> XTi - Canon EOS Kiss Digital X -> Digital - Canon EOS Digital Rebel XT -> XT - EOS Kiss Digital -> Digital - Canon Digital IXUS Wireless -> Wireless - Canon Digital IXUS i zoom -> zoom - Canon EOS Kiss Digital N -> N - Canon Digital IXUS IIs -> IIs - IXY Digital L -> L - Digital IXUS i -> i - IXY Digital -> Digital - Digital IXUS -> IXUS - - The optional includeCharacters allows additional characters to appear - before and after the digits. - Note: special includeCharacters MUST be escaped as per syntax of a - regular expressions (see documentation for module re) - - Examples: - - includeCharacters = '': - DSC-P92 -> P92 - includeCharacters = '\-': - DSC-P92 -> DSC-P92 - - If a digit is not found in the camera model, the last word is returned. - - Note: assume exif values are in ENGLISH, regardless of current platform - """ - m = self.camera_model() - m = m.replace(' Mark ', 'Mk') - if m: - s = r"(?:[^a-zA-Z0-9%s]?)(?P<model>[a-zA-Z0-9%s]*\d+[a-zA-Z0-9%s]*)"\ - % (includeCharacters, includeCharacters, includeCharacters) - r = re.search(s, m) - if r: - return r.group("model") - else: - head, space, model = m.strip().rpartition(' ') - return model - else: - return missing - - - def date_time(self, missing=''): - """ - Returns in python datetime format the date and time the image was - recorded. - - Trys to get value from exif key "Exif.Photo.DateTimeOriginal". - If that does not exist, trys key "Exif.Image.DateTime" - - Returns missing either metadata value is not present. - """ - try: - if "Exif.Photo.DateTimeOriginal" in self.exif_keys: - v = self["Exif.Photo.DateTimeOriginal"].value - else: - v = self["Exif.Image.DateTime"].value - - return v - except: - return missing - - def time_stamp(self, missing=''): - dt = self.date_time(missing=None) - if not dt is None: - try: - t = dt.timetuple() - ts = time.mktime(t) - except: - ts = missing - else: - ts = missing - return ts - - def sub_seconds(self, missing='00'): - """ returns the subsecond the image was taken, as recorded by the camera""" - try: - return str(self["Exif.Photo.SubSecTimeOriginal"].value) - except: - return missing - - def orientation(self, missing=''): - """ - Returns the orientation of the image, as recorded by the camera - Return type int - """ - try: - v = self['Exif.Image.Orientation'].value - if isinstance(v, types.StringType): - v = int(v) - return v - except: - return missing - - -class DummyMetaData(MetaData): - """ - Class which gives metadata values for an imaginary image. - - Useful for displaying in preference examples etc. when no image is ready to - be downloaded. - - See MetaData class for documentation of class methods. - """ - - def __init__(self): - pass - - def readMetadata(self): - pass - - def aperture(self, missing=''): - return "2.0" - - def iso(self, missing=''): - return "100" - - def exposure_time(self, alternativeFormat=False, missing=''): - if alternativeFormat: - return "4000" - else: - return "1/4000" - - def focal_length(self, missing=''): - return "135" - - def camera_make(self, missing=''): - return "Canon" - - def camera_model(self, missing=''): - return "Canon EOS 5D" - - def short_camera_model(self, includeCharacters = '', missing=''): - return "5D" - - def camera_serial(self, missing=''): - return '730402168' - - def shutter_count(self, missing=''): - return '387' - - def owner_name(self, missing=''): - return 'Photographer Name' - - def date_time(self, missing=''): - return datetime.datetime.now() - - def subSeconds(self, missing='00'): - return '57' - - def orientation(self, missing=''): - return 1 - -if __name__ == '__main__': - import sys - - - if (len(sys.argv) != 2): - print 'Usage: ' + sys.argv[0] + ' path/to/photo/containing/metadata' - m = DummyMetaData() - - else: - m = MetaData(sys.argv[1]) - m.read() - - print "f"+ m.aperture('missing ') - print "ISO " + m.iso('missing ') - print m.exposure_time(missing='missing ') + " sec" - print m.exposure_time(alternativeFormat=True, missing='missing ') - print m.focal_length('missing ') + "mm" - print m.camera_make() - print m.camera_model() - print m.short_camera_model() - print m.short_camera_model(includeCharacters = "\-") - print m.date_time() - print m.orientation() - print 'Serial number:', m.camera_serial() - print 'Shutter count:', m.shutter_count() - print 'Subseconds:', m.sub_seconds() - diff --git a/rapid/metadatavideo.py b/rapid/metadatavideo.py deleted file mode 100644 index f5a11a4..0000000 --- a/rapid/metadatavideo.py +++ /dev/null @@ -1,239 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2007-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -HAVE_HACHOIR = True -DOWNLOAD_VIDEO = True - -import os -import datetime -import time -import subprocess - -import multiprocessing -import logging -logger = multiprocessing.get_logger() - -import gtk -import paths - -import rpdfile -import metadataexiftool - -from gettext import gettext as _ - -try: - from hachoir_core.cmd_line import unicodeFilename - from hachoir_parser import createParser - from hachoir_metadata import extractMetadata -except ImportError: - HAVE_HACHOIR = False - -if not HAVE_HACHOIR: - v = metadataexiftool.version_info() - if v is None: - DOWNLOAD_VIDEO = False - -def file_types_to_download(): - """Returns a string with the types of file to download, to display to the user""" - if DOWNLOAD_VIDEO: - return _("photos and videos") - else: - return _("photos") - -if HAVE_HACHOIR: - - def version_info(): - from hachoir_metadata.version import VERSION - return VERSION - - def get_video_THM_file(full_filename): - """ - Checks to see if a thumbnail file (THM) is in the same directory as the - file. Expects a full path to be part of the file name. - - Returns the filename, including path, if found, else returns None. - """ - - f = None - name, ext = os.path.splitext(full_filename) - for e in rpdfile.VIDEO_THUMBNAIL_FILE_EXTENSIONS: - if os.path.exists(name + '.' + e): - f = name + '.' + e - break - if os.path.exists(name + '.' + e.upper()): - f = name + '.' + e.upper() - break - - return f - - class VideoMetaData(): - def __init__(self, filename): - """ - Initialize by loading metadata using hachoir - """ - - self.filename = filename - self.u_filename = unicodeFilename(filename) - self.metadata = None - - def _kaa_get(self, key, missing, stream=None): - if not hasattr(self, 'info'): - try: - from kaa.metadata import parse - except ImportError: - msg = """The package Kaa metadata does not exist. -It is needed to access FPS and codec video file metadata.""" - logger.error(msg) - self.info = None - else: - self.info = parse(self.filename) - if self.info: - if stream != None: - v = self.info['video'][stream][key] - else: - v = self.info[key] - else: - v = None - if v: - return str(v) - else: - return missing - - def _load_hachoir_metadata_parser(self): - self.parser = createParser(self.u_filename, self.filename) - self.metadata = extractMetadata(self.parser) - - def _get(self, key, missing): - if self.metadata is None: - self._load_hachoir_metadata_parser() - - try: - v = self.metadata.get(key) - except: - v = missing - return v - - def date_time(self, missing=''): - return self._get('creation_date', missing) - - def time_stamp(self, missing=''): - """ - Returns a float value representing the time stamp, if it exists - """ - dt = self.date_time(missing=None) - if dt: - # convert it to a time stamp (not optimal, but better than nothing!) - v = time.mktime(dt.timetuple()) - else: - v = missing - return v - - def codec(self, stream=0, missing=''): - return self._kaa_get('codec', missing, stream) - - def length(self, missing=''): - """ - return the duration (length) of the video, rounded to the nearest second, in string format - """ - delta = self.metadata.get('duration') - l = '%.0f' % (86400 * delta.days + delta.seconds + float('.%s' % delta.microseconds)) - return l - - - def width(self, missing=''): - v = self._get('width', missing) - if v != None: - return str(v) - else: - return None - - def height(self, missing=''): - v = self._get('height', missing) - if v != None: - return str(v) - else: - return None - - def frames_per_second(self, stream=0, missing=''): - fps = self._kaa_get('fps', missing, stream) - try: - fps = '%.0f' % float(fps) - except: - pass - return fps - - def fourcc(self, stream=0, missing=''): - return self._kaa_get('fourcc', missing, stream) - - - -class DummyMetaData(): - """ - Class which gives metadata values for an imaginary video. - - Useful for displaying in preference examples etc. when no video is ready to - be downloaded. - - See VideoMetaData class for documentation of class methods. - """ - def __init__(self, filename): - pass - - def date_time(self, missing=''): - return datetime.datetime.now() - - def codec(self, stream=0, missing=''): - return 'H.264 AVC' - - def length(self, missing=''): - return '57' - - def width(self, stream=0, missing=''): - return '1920' - - def height(self, stream=0, missing=''): - return '1080' - - def frames_per_second(self, stream=0, missing=''): - return '24' - - def fourcc(self, stream=0, missing=''): - return 'AVC1' - - -if __name__ == '__main__': - import sys - - - if (len(sys.argv) != 2): - print 'Usage: ' + sys.argv[0] + ' path/to/video/containing/metadata' - sys.exit(0) - - else: - m = VideoMetaData(sys.argv[1]) - dt = m.date_time() - if dt: - print dt.strftime('%Y%m%d-%H:%M:%S') - print "codec: %s" % m.codec() - print "%s seconds" % m.length() - print "%sx%s" % (m.width(), m.height()) - print "%s fps" % m.frames_per_second() - print "Fourcc: %s" % (m.fourcc()) - diff --git a/rapid/metadataxmp.py b/rapid/metadataxmp.py deleted file mode 100755 index b42b587..0000000 --- a/rapid/metadataxmp.py +++ /dev/null @@ -1,199 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import subprocess - -import multiprocessing, logging -logger = multiprocessing.get_logger() - -class XmpMetadataSidecar: - - def __init__(self, filename): - self.filename = filename - self.keys = [] - - def _add_pair(self, key_value_pair): - self.keys.append(key_value_pair) - logger.debug(key_value_pair) - - def _generate_exiv2_command_line(self): - # -f option: overwrites any existing xmp file - return ['exiv2', '-f'] + self.keys + ['-exX', self.filename] - - def _generate_exiv2_contact_info(self, key, value): - return "-M set Xmp.iptc.CreatorContactInfo/Iptc4xmpCore:%s %s" % (key, value) - - def _generate_exiv2_dc(self, key, value): - return "-M set Xmp.dc.%s %s" % (key, value) - - def _generate_exiv2_photoshop(self, key, value): - return "-M set Xmp.photoshop.%s %s" % (key, value) - - def _generate_exiv2_rights(self, key, value): - return "-M set Xmp.xmpRights.%s %s" % (key, value) - - def _generate_exiv2_iptc(self, key, value): - return "-M set Xmp.iptc.%s %s" % (key, value) - - def _generate_exiv2_exif(self, key, value): - return "-M set Xmp.exif.%s %s" % (key, value) - - def set_location(self, location): - self._add_pair(self._generate_exiv2_iptc('Location', location)) - - def set_city(self, city): - self._add_pair(self._generate_exiv2_photoshop('City', city)) - - def set_state_province(self, state): - self._add_pair(self._generate_exiv2_photoshop('State', state)) - - def set_country(self, country): - self._add_pair(self._generate_exiv2_photoshop('Country', country)) - - def set_country_code(self, country_code): - self._add_pair(self._generate_exiv2_iptc('CountryCode', country_code)) - - def set_headline(self, headline): - self._add_pair(self._generate_exiv2_photoshop('Headline', headline)) - - def set_description_writer(self, writer): - """ - Synonym: Caption writer - """ - self._add_pair(self._generate_exiv2_photoshop('CaptionWriter', writer)) - - def set_description(self, description): - """A synonym for this in some older programs is 'Caption'""" - self._add_pair(self._generate_exiv2_dc('description', description)) - - def set_subject(self, subject): - """ - You can call this more than once, to add multiple subjects - - A synonym is 'Keywords' - """ - self._add_pair(self._generate_exiv2_dc('subject', subject)) - - def set_creator(self, creator): - """ - Sets the author (creator) field. Photo Mechanic calls this 'Photographer'. - """ - self._add_pair(self._generate_exiv2_dc('creator', creator)) - - def set_creator_job_title(self, job_title): - self._add_pair(self._generate_exiv2_photoshop('AuthorsPosition', job_title)) - - def set_credit_line(self, credit_line): - self._add_pair(self._generate_exiv2_photoshop('Credit', credit_line)) - - def set_source(self, source): - """ - original owner or copyright holder of the photograph - """ - self._add_pair(self._generate_exiv2_photoshop('Source', source)) - - def set_copyright(self, copyright): - self._add_pair(self._generate_exiv2_dc('rights', copyright)) - - def set_copyright_url(self, copyright_url): - self._add_pair(self._generate_exiv2_rights('WebStatement', copyright_url)) - - def set_contact_city(self, city): - self._add_pair(self._generate_exiv2_contact_info('CiAdrCity', city)) - - def set_contact_country(self, country): - self._add_pair(self._generate_exiv2_contact_info('CiAdrCtry', country)) - - def set_contact_address(self, address): - """The contact information address part. - Comprises an optional company name and all required information - to locate the building or postbox to which mail should be sent.""" - self._add_pair(self._generate_exiv2_contact_info('CiAdrExtadr', address)) - - def set_contact_postal_code(self, postal_code): - self._add_pair(self._generate_exiv2_contact_info('CiAdrPcode', postal_code)) - - def set_contact_region(self, region): - """State/Province""" - self._add_pair(self._generate_exiv2_contact_info('CiAdrRegion', region)) - - def set_contact_email(self, email): - """Multiple email addresses can be given, separated by a comma.""" - self._add_pair(self._generate_exiv2_contact_info('CiEmailWork', email)) - - def set_contact_telephone(self, telephone): - """Multiple numbers can be given, separated by a comma.""" - self._add_pair(self._generate_exiv2_contact_info('CiTelWork', telephone)) - - def set_contact_url(self, url): - """Multiple URLs can be given, separated by a comma.""" - self._add_pair(self._generate_exiv2_contact_info('CiUrlWork', url)) - - def set_exif_value(self, key, value): - self._add_pair(self._generate_exiv2_exif(key, value)) - - def write_xmp_sidecar(self): - cmd = self._generate_exiv2_command_line() - if logger.getEffectiveLevel() == logging.DEBUG: - cmd_line = '' - for c in cmd: - cmd_line += c + ' ' - cmd_line = cmd_line.strip() - logger.debug("XMP write command: %s", cmd_line) - proc = subprocess.Popen(cmd, stdout=subprocess.PIPE) - return proc.communicate()[0].strip() - - -if __name__ == '__main__': - import sys - - - if (len(sys.argv) != 2): - print 'Usage: ' + sys.argv[0] + ' path/to/photo/containing/metadata' - - else: - x = XmpMetadataSidecar(sys.argv[1]) - x.set_description("This is image is just a sample and is nothing serious. I used to test out writing XMP files in Rapid Photo Downloader.") - x.set_description_writer("Damon Lynch wrote caption") - x.set_headline("Sample image to test XMP") - x.set_subject("Keyword 1") - x.set_subject("Keyword 2") - x.set_city("Minneapolis") - x.set_location("University of Minnesota") - x.set_state_province("Minnesota") - x.set_country("United States of America") - x.set_country_code("USA") - x.set_creator("Damon Lynch") - x.set_creator_job_title("Photographer") - x.set_credit_line("Contact Damon for permission") - x.set_source("Damon Lynch is the original photographer") - x.set_copyright("© 2011 Damon Lynch, all rights reserved.") - x.set_copyright_url("http://www.damonlynch.net/license") - x.set_contact_address("Contact house number, street, apartment") - x.set_contact_city('Contact City') - x.set_contact_region('Contact State') - x.set_contact_postal_code('Contact Post code') - x.set_contact_telephone('+1 111 111 1111') - x.set_contact_country('Contact Country') - x.set_contact_address('Address\nApartment') - x.set_contact_url('http://www.sample.net') - x.set_contact_email('name@email1.com, name@email2.com') - - x.write_xmp_sidecar() diff --git a/rapid/misc.py b/rapid/misc.py deleted file mode 100644 index a2c6d08..0000000 --- a/rapid/misc.py +++ /dev/null @@ -1,60 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2002-2006 Stephen Kennedy <stevek@gnome.org> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -# modified by Damon Lynch 2009 to remove default bold formatting and alignment -# modified by Damon Lynch 2103 to add function to get folder chosen by user in file chooser button - -"""Module of commonly used helper classes and functions - -""" - -import gtk - -def run_dialog( text, secondarytext=None, parent=None, messagetype=gtk.MESSAGE_WARNING, buttonstype=gtk.BUTTONS_OK, extrabuttons=[]): - """Run a dialog with text 'text'. - Extra buttons are passed as tuples of (button label, response id). - """ - d = gtk.MessageDialog(None, - gtk.DIALOG_DESTROY_WITH_PARENT, - messagetype, - buttonstype, - text - ) - if parent: - d.set_transient_for(parent.get_toplevel()) - for b,rid in extrabuttons: - d.add_button(b,rid) - d.vbox.set_spacing(12) - d.format_secondary_text(secondarytext) - ret = d.run() - d.destroy() - return ret - -def get_folder_selection(filechooserbutton): - """ - Returns the path (folder) the user has chosen in a filechooserbutton - """ - # this no longer works on Ubuntu 13.10: - # path = filechooserbutton.get_current_folder() - # but this works on Ubuntu 13.10: - path = filechooserbutton.get_filenames() #returns a list - if path: - path = path[0] - return path diff --git a/rapid/paths.py b/rapid/paths.py deleted file mode 100644 index b110512..0000000 --- a/rapid/paths.py +++ /dev/null @@ -1,41 +0,0 @@ -### Copyright (C) 2002-2006 Stephen Kennedy <stevek@gnome.org> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import os - -_locale_dir = ( #LOCALEDIR# -) -_help_dir = ( #HELPDIR# -) -_share_dir = ( #SHAREDIR# -) - -appdir = os.path.dirname(__file__) - -if not _locale_dir: _locale_dir = os.path.join(appdir,"po") -if not _help_dir: _help_dir = os.path.join(appdir,"help") -if not _share_dir: _share_dir = appdir - -def locale_dir(*args): # i18n files - return os.path.join(_locale_dir, *args) - -def help_dir(*args): # help - return os.path.join(_help_dir, *args) - -def share_dir(*args): # glade + pixmaps - return os.path.join(_share_dir, *args) - diff --git a/rapid/preferencesdialog.py b/rapid/preferencesdialog.py deleted file mode 100644 index a0767af..0000000 --- a/rapid/preferencesdialog.py +++ /dev/null @@ -1,1963 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2007-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - - -import datetime, re - -import gtk - -import datetime -import multiprocessing -import logging -logger = multiprocessing.get_logger() - -import webbrowser - -import ValidatedEntry -import misc - -import config -import paths -import rpdfile -import higdefaults as hd -import metadataphoto -import metadatavideo - -import tableplusminus as tpm - -import utilities - -import generatename as gn -from generatenameconfig import * -import problemnotification as pn - -from prefsrapid import format_pref_list_for_pretty_print, DownloadsTodayTracker - -from gettext import gettext as _ - -class PrefError(Exception): - """ base class """ - def unpackList(self, l): - """ - Make the preferences presentable to the user - """ - - s = '' - for i in l: - if i <> ORDER_KEY: - s += "'" + i + "', " - return s[:-2] - - def __str__(self): - return self.msg - -class PrefKeyError(PrefError): - def __init__(self, error): - value = error[0] - expectedValues = self.unpackList(error[1]) - self.msg = "Preference key '%(key)s' is invalid.\nExpected one of %(value)s" % { - 'key': value, 'value': expectedValues} - - -class PrefValueInvalidError(PrefKeyError): - def __init__(self, error): - value = error[0] - self.msg = "Preference value '%(value)s' is invalid" % {'value': value} - -class PrefLengthError(PrefError): - def __init__(self, error): - self.msg = "These preferences are not well formed:" + "\n %s" % self.unpackList(error) - -class PrefValueKeyComboError(PrefError): - def __init__(self, error): - self.msg = error - - -def check_pref_valid(pref_defn, prefs, modulo=3): - """ - Checks to see if prefs are valid according to definition. - - prefs is a list of preferences. - pref_defn is a Dict specifying what is valid. - modulo is how many list elements are equivalent to one line of preferences. - - Returns True if prefs match with pref_defn, - else raises appropriate error. - """ - - if (len(prefs) % modulo <> 0) or not prefs: - raise PrefLengthError(prefs) - else: - for i in range(0, len(prefs), modulo): - _check_pref_valid(pref_defn, prefs[i:i+modulo]) - - return True - -def _check_pref_valid(pref_defn, prefs): - - key = prefs[0] - value = prefs[1] - - - if pref_defn.has_key(key): - - next_pref_defn = pref_defn[key] - - if value == None: - # value should never be None, at any time - raise PrefValueInvalidError((None, next_pref_defn)) - - if next_pref_defn and not value: - raise gn.PrefValueInvalidError((value, next_pref_defn)) - - if type(next_pref_defn) == type({}): - return _check_pref_valid(next_pref_defn, prefs[1:]) - else: - if type(next_pref_defn) == type([]): - result = value in next_pref_defn - if not result: - raise gn.PrefValueInvalidError((value, next_pref_defn)) - return True - elif not next_pref_defn: - return True - else: - result = next_pref_defn == value - if not result: - raise gn.PrefKeyValue((value, next_pref_defn)) - return True - else: - raise PrefKeyError((key, pref_defn[ORDER_KEY])) - - -def filter_subfolder_prefs(pref_list): - """ - Filters out extraneous preference choices - """ - prefs_changed = False - continue_check = True - while continue_check and pref_list: - continue_check = False - if pref_list[0] == SEPARATOR: - # subfolder preferences should not start with a / - pref_list = pref_list[3:] - prefs_changed = True - continue_check = True - elif pref_list[-3] == SEPARATOR: - # subfolder preferences should not end with a / - pref_list = pref_list[:-3] - continue_check = True - prefs_changed = True - else: - for i in range(0, len(pref_list) - 3, 3): - if pref_list[i] == SEPARATOR and pref_list[i+3] == SEPARATOR: - # subfolder preferences should not contain two /s side by side - continue_check = True - prefs_changed = True - # note we are messing with the contents of the pref list, - # must exit loop and try again - pref_list = pref_list[:i] + pref_list[i+3:] - break - - return (prefs_changed, pref_list) - -class Comboi18n(gtk.ComboBox): - """ very simple i18n version of the venerable combo box - with one column displayed to the user. - - This combo box has two columns: - 1. the first contains the actual value and is invisible - 2. the second contains the translation of the first column, and this is what - the users sees - """ - def __init__(self): - liststore = gtk.ListStore(str, str) - gtk.ComboBox.__init__(self, liststore) - cell = gtk.CellRendererText() - self.pack_start(cell, True) - self.add_attribute(cell, 'text', 1) - # must name the combo box on pygtk used in Ubuntu 11.04, Fedora 15, etc. - self.set_name('GtkComboBox') - - def append_text(self, text): - model = self.get_model() - model.append((text, _(text))) - - def get_active_text(self): - model = self.get_model() - active = self.get_active() - if active < 0: - return None - return model[active][0] - -class PreferenceWidgets: - - def __init__(self, default_row, default_prefs, pref_defn_L0, pref_list): - self.default_row = default_row - self.default_prefs = default_prefs - self.pref_defn_L0 = pref_defn_L0 - self.pref_list = pref_list - - def _create_combo(self, choices): - combobox = Comboi18n() - for text in choices: - combobox.append_text(text) - return combobox - - def get_default_row(self): - """ - returns a list of default widgets - """ - return self.get_widgets_based_on_user_selection(self.default_row) - - def _get_pref_widgets(self, pref_definition, prefs, widgets): - key = prefs[0] - value = prefs[1] - - # supply a default value if the user has not yet chosen a value! - if not key: - key = pref_definition[ORDER_KEY][0] - - if not key in pref_definition: - raise gn.PrefKeyError((key, pref_definition.keys())) - - - list0 = pref_definition[ORDER_KEY] - - # the first widget will always be a combo box - widget0 = self._create_combo(list0) - widget0.set_active(list0.index(key)) - - widgets.append(widget0) - - if key == TEXT: - widget1 = gtk.Entry() - widget1.set_text(value) - - widgets.append(widget1) - widgets.append(None) - return - elif key in [SEPARATOR, JOB_CODE]: - widgets.append(None) - widgets.append(None) - return - else: - next_pref_definition = pref_definition[key] - if type(next_pref_definition) == type({}): - return self._get_pref_widgets(next_pref_definition, - prefs[1:], - widgets) - else: - if type(next_pref_definition) == type([]): - widget1 = self._create_combo(next_pref_definition) - if not value: - value = next_pref_definition[0] - try: - widget1.set_active(next_pref_definition.index(value)) - except: - raise gn.PrefValueInvalidError((value, next_pref_definition)) - - widgets.append(widget1) - else: - widgets.append(None) - - def _get_values_from_list(self): - for i in range(0, len(self.pref_list), 3): - yield (self.pref_list[i], self.pref_list[i+1], self.pref_list[i+2]) - - def get_widgets_based_on_prefs(self): - """ - Yields a list of widgets and their callbacks based on the users preferences. - - This list is equivalent to one row of preferences when presented to the - user in the Plus Minus Table. - """ - - for L0, L1, L2 in self._get_values_from_list(): - prefs = [L0, L1, L2] - widgets = [] - self._get_pref_widgets(self.pref_defn_L0, prefs, widgets) - yield widgets - - - def get_widgets_based_on_user_selection(self, selection): - """ - Returns a list of widgets and their callbacks based on what the user has selected. - - Selection is the values the user has chosen thus far in comboboxes. - It determines the contents of the widgets returned. - It should be a list of three values, with None for values not chosen. - For values which are None, the first value in the preferences - definition is chosen. - - """ - widgets = [] - - self._get_pref_widgets(self.pref_defn_L0, selection, widgets) - return widgets - - def check_prefs_for_validity(self): - """ - Checks preferences validity - """ - - return check_pref_valid(self.pref_defn_L0, self.pref_list) - -class PhotoNamePrefs(PreferenceWidgets): - def __init__(self, pref_list): - PreferenceWidgets.__init__(self, - default_row = [FILENAME, NAME_EXTENSION, ORIGINAL_CASE], - default_prefs = [FILENAME, NAME_EXTENSION, ORIGINAL_CASE], - pref_defn_L0 = DICT_IMAGE_RENAME_L0, - pref_list = pref_list) - -class VideoNamePrefs(PreferenceWidgets): - def __init__(self, pref_list): - PreferenceWidgets.__init__(self, - default_row = [FILENAME, NAME_EXTENSION, ORIGINAL_CASE], - default_prefs = [FILENAME, NAME_EXTENSION, ORIGINAL_CASE], - pref_defn_L0 = DICT_VIDEO_RENAME_L0, - pref_list = pref_list) - - -class PhotoSubfolderPrefs(PreferenceWidgets): - def __init__(self, pref_list): - - PreferenceWidgets.__init__(self, - default_row = [DATE_TIME, IMAGE_DATE, LIST_DATE_TIME_L2[0]], - default_prefs = DEFAULT_SUBFOLDER_PREFS, - pref_defn_L0 = DICT_SUBFOLDER_L0, - pref_list = pref_list) - - def filter_preferences(self): - filtered, pref_list = filter_subfolder_prefs(self.pref_list) - if filtered: - self.pref_list = pref_list - - def check_prefs_for_validity(self): - """ - Checks subfolder preferences validity above and beyond image name checks. - - See parent method for full description. - - Subfolders have additional requirments to that of file names. - """ - v = PreferenceWidgets.check_prefs_for_validity(self) - if v: - # peform additional checks: - # 1. do not start with a separator - # 2. do not end with a separator - # 3. do not have two separators in a row - # these three rules will ensure something else other than a - # separator is specified - L1s = [] - for i in range(0, len(self.pref_list), 3): - L1s.append(self.pref_list[i]) - - if L1s[0] == SEPARATOR: - raise PrefValueKeyComboError(_("Subfolder preferences should not start with a %s") % os.sep) - elif L1s[-1] == SEPARATOR: - raise PrefValueKeyComboError(_("Subfolder preferences should not end with a %s") % os.sep) - else: - for i in range(len(L1s) - 1): - if L1s[i] == SEPARATOR and L1s[i+1] == SEPARATOR: - raise PrefValueKeyComboError(_("Subfolder preferences should not contain two %s one after the other") % os.sep) - return v - -class VideoSubfolderPrefs(PhotoSubfolderPrefs): - def __init__(self, pref_list): - PreferenceWidgets.__init__(self, - default_row = [DATE_TIME, VIDEO_DATE, LIST_DATE_TIME_L2[0]], - default_prefs = DEFAULT_VIDEO_SUBFOLDER_PREFS, - pref_defn_L0 = DICT_VIDEO_SUBFOLDER_L0, - pref_list = pref_list) - -class QuestionDialog(gtk.Dialog): - def __init__(self, parent_window, title, question, use_markup=False, - default_to_yes=True, post_choice_callback=None): - gtk.Dialog.__init__(self, title, None, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - (gtk.STOCK_NO, gtk.RESPONSE_CANCEL, - gtk.STOCK_YES, gtk.RESPONSE_OK)) - - self.post_choice_callback = post_choice_callback - self.set_icon_from_file(paths.share_dir('glade3/rapid-photo-downloader.svg')) - - prompt_hbox = gtk.HBox() - - icontheme = gtk.icon_theme_get_default() - icon = icontheme.load_icon('dialog-question', 36, gtk.ICON_LOOKUP_USE_BUILTIN) - if icon: - image = gtk.Image() - image.set_from_pixbuf(icon) - prompt_hbox.pack_start(image, False, False, padding = 6) - - prompt_label = gtk.Label(question) - prompt_label.set_use_markup(use_markup) - prompt_label.set_line_wrap(True) - prompt_hbox.pack_start(prompt_label, False, False, padding=6) - - self.vbox.pack_start(prompt_hbox, padding=6) - - self.set_border_width(6) - self.set_has_separator(False) - - if default_to_yes: - self.set_default_response(gtk.RESPONSE_OK) - else: - self.set_default_response(gtk.RESPONSE_CANCEL) - - self.set_transient_for(parent_window) - self.show_all() - - if post_choice_callback: - self.connect('response', self.on_response) - - def on_response(self, device_dialog, response): - user_selected = response == gtk.RESPONSE_OK - self.post_choice_callback(self, user_selected) - -class RemoveAllJobCodeDialog(QuestionDialog): - def __init__(self, parent_window, post_choice_callback): - QuestionDialog.__init__(self, parent_window, - _('Remove all Job Codes?'), - _('Should all Job Codes be removed?'), - post_choice_callback=post_choice_callback) - -class RemoveAllRemeberedDevicesDialog(QuestionDialog): - def __init__(self, parent_window, post_choice_callback): - QuestionDialog.__init__(self, parent_window, - _('Remove all Remembered Paths?'), - _('Should all remembered paths be removed?'), - post_choice_callback=post_choice_callback) - -class RemoveAllIgnoredPathsDialog(QuestionDialog): - def __init__(self, parent_window, post_choice_callback): - QuestionDialog.__init__(self, parent_window, - _('Remove all Ignored Paths?'), - _('Should all ignored paths be removed?'), - post_choice_callback=post_choice_callback) - -class PhotoRenameTable(tpm.TablePlusMinus): - - def __init__(self, preferencesdialog, adjust_scroll_window): - - tpm.TablePlusMinus.__init__(self, 1, 3) - self.preferencesdialog = preferencesdialog - self.adjust_scroll_window = adjust_scroll_window - if not hasattr(self, "error_title"): - self.error_title = _("Error in Photo Rename preferences") - - self.table_type = self.error_title[len("Error in "):] - self.i = 0 - - if adjust_scroll_window: - self.scroll_bar = self.adjust_scroll_window.get_vscrollbar() - #this next line does not work on early versions of pygtk :( - self.scroll_bar.connect('visibility-notify-event', self.scrollbar_visibility_change) - self.connect("size-request", self.size_adjustment) - self.connect("add", self.size_adjustment) - self.connect("remove", self.size_adjustment) - - self.bump = 16 - self.have_vertical_scrollbar = False - - - self.get_preferencesdialog_prefs() - self.setup_prefs_factory() - - try: - self.prefs_factory.check_prefs_for_validity() - - except (PrefValueInvalidError, PrefLengthError, - PrefValueKeyComboError, PrefKeyError), e: - - logger.error(self.error_title) - logger.error("Sorry, these preferences contain an error:") - logger.error(format_pref_list_for_pretty_print(self.prefs_factory.pref_list)) - - # the preferences were invalid - # reset them to their default - - self.pref_list = self.prefs_factory.default_prefs - self.setup_prefs_factory() - self.update_parentapp_prefs() - - msg = "%s.\n" % e - msg += "Resetting to default values." - logger.error(msg) - - - misc.run_dialog(self.error_title, msg, - preferencesdialog, - gtk.MESSAGE_ERROR) - - for row in self.prefs_factory.get_widgets_based_on_prefs(): - self.append(row) - - def update_preferences(self): - pref_list = [] - for row in self.pm_rows: - for col in range(self.pm_no_columns): - widget = row[col] - if widget: - name = widget.get_name() - if name == 'GtkComboBox': - value = widget.get_active_text() - elif name == 'GtkEntry': - value = widget.get_text() - else: - logger.critical("Program error: Unknown preference widget!") - value = '' - else: - value = '' - pref_list.append(value) - - self.pref_list = pref_list - self.update_parentapp_prefs() - self.prefs_factory.pref_list = pref_list - self.update_example() - - - def scrollbar_visibility_change(self, widget, event): - if event.state == gtk.gdk.VISIBILITY_UNOBSCURED: - self.have_vertical_scrollbar = True - self.adjust_scroll_window.set_size_request(self.adjust_scroll_window.allocation.width + self.bump, -1) - - - def size_adjustment(self, widget, arg2): - """ - Adjust scrolledwindow width in preferences dialog to reflect width of image rename table - - The algorithm is complicated by the need to take into account the presence of a vertical scrollbar, - which might be added as the user adds more rows - - The pygtk code behaves inconsistently depending on the pygtk version - """ - - if self.adjust_scroll_window: - self.have_vertical_scrollbar = self.scroll_bar.allocation.width > 1 or self.have_vertical_scrollbar - if not self.have_vertical_scrollbar: - if self.allocation.width > self.adjust_scroll_window.allocation.width: - self.adjust_scroll_window.set_size_request(self.allocation.width, -1) - else: - if self.allocation.width > self.adjust_scroll_window.allocation.width - self.bump: - self.adjust_scroll_window.set_size_request(self.allocation.width + self.bump, -1) - self.bump = 0 - - def get_preferencesdialog_prefs(self): - self.pref_list = self.preferencesdialog.prefs.image_rename - - - def setup_prefs_factory(self): - self.prefs_factory = PhotoNamePrefs(self.pref_list) - - def update_parentapp_prefs(self): - self.preferencesdialog.prefs.image_rename = self.pref_list - - def update_example_job_code(self): - job_code = self.preferencesdialog.prefs.get_sample_job_code() - if not job_code: - job_code = _('Job code') - #~ self.prefs_factory.setJobCode(job_code) - - def update_example(self): - self.preferencesdialog.update_photo_rename_example() - - def get_default_row(self): - return self.prefs_factory.get_default_row() - - def on_combobox_changed(self, widget, row_position): - - for col in range(self.pm_no_columns): - if self.pm_rows[row_position][col] == widget: - break - selection = [] - for i in range(col + 1): - # ensure it is a combo box we are getting the value from - w = self.pm_rows[row_position][i] - name = w.get_name() - if name == 'GtkComboBox': - selection.append(w.get_active_text()) - else: - selection.append(w.get_text()) - - for i in range(col + 1, self.pm_no_columns): - selection.append('') - - if col <> (self.pm_no_columns - 1): - widgets = self.prefs_factory.get_widgets_based_on_user_selection(selection) - - for i in range(col + 1, self.pm_no_columns): - old_widget = self.pm_rows[row_position][i] - if old_widget: - self.remove(old_widget) - if old_widget in self.pm_callbacks: - del self.pm_callbacks[old_widget] - new_widget = widgets[i] - self.pm_rows[row_position][i] = new_widget - if new_widget: - self._create_callback(new_widget, row_position) - self.attach(new_widget, i, i+1, row_position, row_position + 1) - new_widget.show() - self.update_preferences() - - - def on_entry_changed(self, widget, row_position): - self.update_preferences() - - def on_row_added(self, row_position): - """ - Update preferences, as a row has been added - """ - self.update_preferences() - - # if this was the last row or 2nd to last row, and another has just been added, move vertical scrollbar down - if row_position in range(self.pm_no_rows - 3, self.pm_no_rows - 2): - adjustment = self.preferencesdialog.rename_scrolledwindow.get_vadjustment() - adjustment.set_value(adjustment.upper) - - - def on_row_deleted(self, row_position): - """ - Update preferences, as a row has been deleted - """ - self.update_preferences() - -class VideoRenameTable(PhotoRenameTable): - def __init__(self, preferencesdialog, adjust_scroll_window): - self.error_title = _("Error in Video Rename preferences") - PhotoRenameTable.__init__(self, preferencesdialog, adjust_scroll_window) - - def get_preferencesdialog_prefs(self): - self.pref_list = self.preferencesdialog.prefs.video_rename - - def setup_prefs_factory(self): - self.prefs_factory = VideoNamePrefs(self.pref_list) - - def update_parentapp_prefs(self): - self.preferencesdialog.prefs.video_rename = self.pref_list - - def update_example(self): - self.preferencesdialog.update_video_rename_example() - -class SubfolderTable(PhotoRenameTable): - """ - Table to display photo download subfolder preferences as part of preferences - dialog window. - """ - def __init__(self, preferencesdialog, adjust_scroll_window): - self.error_title = _("Error in Photo Download Subfolders preferences") - PhotoRenameTable.__init__(self, preferencesdialog, adjust_scroll_window) - - def get_preferencesdialog_prefs(self): - self.pref_list = self.preferencesdialog.prefs.subfolder - - def setup_prefs_factory(self): - self.prefs_factory = PhotoSubfolderPrefs(self.pref_list) - - def update_parentapp_prefs(self): - self.preferencesdialog.prefs.subfolder = self.pref_list - - def update_example(self): - self.preferencesdialog.update_photo_download_folder_example() - -class VideoSubfolderTable(PhotoRenameTable): - def __init__(self, preferencesdialog, adjust_scroll_window): - self.error_title = _("Error in Video Download Subfolders preferences") - PhotoRenameTable.__init__(self, preferencesdialog, adjust_scroll_window) - - def get_preferencesdialog_prefs(self): - self.pref_list = self.preferencesdialog.prefs.video_subfolder - - def setup_prefs_factory(self): - self.prefs_factory = VideoSubfolderPrefs(self.pref_list) - - def update_parentapp_prefs(self): - self.preferencesdialog.prefs.video_subfolder = self.pref_list - - def update_example(self): - self.preferencesdialog.update_video_download_folder_example() - -class RemoveAllJobCodeDialog(gtk.Dialog): - def __init__(self, parent_window, post_choice_callback): - gtk.Dialog.__init__(self, _('Remove all Job Codes?'), None, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - (gtk.STOCK_NO, gtk.RESPONSE_CANCEL, - gtk.STOCK_YES, gtk.RESPONSE_OK)) - - self.post_choice_callback = post_choice_callback - self.set_icon_from_file(paths.share_dir('glade3/rapid-photo-downloader.svg')) - - prompt_hbox = gtk.HBox() - - icontheme = gtk.icon_theme_get_default() - icon = icontheme.load_icon('dialog-question', 36, gtk.ICON_LOOKUP_USE_BUILTIN) - if icon: - image = gtk.Image() - image.set_from_pixbuf(icon) - prompt_hbox.pack_start(image, False, False, padding = 6) - - prompt_label = gtk.Label(_('Should all Job Codes be removed?')) - prompt_label.set_line_wrap(True) - prompt_hbox.pack_start(prompt_label, False, False, padding=6) - - self.vbox.pack_start(prompt_hbox, padding=6) - - self.set_border_width(6) - self.set_has_separator(False) - - self.set_default_response(gtk.RESPONSE_OK) - - self.set_transient_for(parent_window) - self.show_all() - - self.connect('response', self.on_response) - - def on_response(self, device_dialog, response): - user_selected = response == gtk.RESPONSE_OK - self.post_choice_callback(self, user_selected) - -class JobCodeDialog(gtk.Dialog): - """ Dialog prompting for a job code""" - - def __init__(self, parent_window, job_codes, default_job_code, post_job_code_entry_callback, entry_only): - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#jobcode - gtk.Dialog.__init__(self, _('Enter a Job Code'), None, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, - gtk.STOCK_OK, gtk.RESPONSE_OK)) - - self.set_icon_from_file(paths.share_dir('glade3/rapid-photo-downloader.svg')) - self.post_job_code_entry_callback = post_job_code_entry_callback - - self.combobox = gtk.combo_box_entry_new_text() - for text in job_codes: - self.combobox.append_text(text) - - self.job_code_hbox = gtk.HBox(homogeneous = False) - - if len(job_codes) and not entry_only: - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#jobcode - task_label = gtk.Label(_('Enter a new Job Code, or select a previous one')) - else: - # Translators: for an explanation of what this means, see http://damonlynch.net/rapid/documentation/index.html#jobcode - task_label = gtk.Label(_('Enter a new Job Code')) - task_label.set_line_wrap(True) - task_hbox = gtk.HBox() - task_hbox.pack_start(task_label, False, False, padding=6) - - label = gtk.Label(_('Job Code:')) - self.job_code_hbox.pack_start(label, False, False, padding=6) - self.job_code_hbox.pack_start(self.combobox, True, True, padding=6) - - self.set_border_width(6) - self.set_has_separator(False) - - # make entry box have entry completion - self.entry = self.combobox.child - - completion = gtk.EntryCompletion() - completion.set_match_func(self.match_func) - completion.connect("match-selected", - self.on_completion_match) - completion.set_model(self.combobox.get_model()) - completion.set_text_column(0) - self.entry.set_completion(completion) - - # when user hits enter, close the dialog window - self.set_default_response(gtk.RESPONSE_OK) - self.entry.set_activates_default(True) - - if default_job_code: - self.entry.set_text(default_job_code) - - self.vbox.pack_start(task_hbox, False, False, padding = 6) - self.vbox.pack_start(self.job_code_hbox, False, False, padding=12) - - self.set_transient_for(parent_window) - self.show_all() - self.connect('response', self.on_job_code_resp) - - def match_func(self, completion, key, iter): - model = completion.get_model() - return model[iter][0].lower().startswith(self.entry.get_text().lower()) - - def on_completion_match(self, completion, model, iter): - self.entry.set_text(model[iter][0]) - self.entry.set_position(-1) - - def get_job_code(self): - return self.combobox.child.get_text() - - def on_job_code_resp(self, jc_dialog, response): - user_chose_code = False - if response == gtk.RESPONSE_OK: - user_chose_code = True - logger.debug("Job Code entered") - else: - logger.debug("Job Code not entered") - self.post_job_code_entry_callback(self, user_chose_code, self.get_job_code()) - -class IgnorePathDialog(gtk.Dialog): - """ Dialog prompting for a path to ignore when scanning devices""" - - def __init__(self, parent_window, post_entry_callback): - gtk.Dialog.__init__(self, _('Enter a Path to Ignore'), None, - gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, - (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, - gtk.STOCK_OK, gtk.RESPONSE_OK)) - - self.set_icon_from_file(paths.share_dir('glade3/rapid-photo-downloader.svg')) - self.post_entry_callback = post_entry_callback - - self.path_entry = gtk.Entry(max=0) - - self.ignored_path_hbox = gtk.HBox(homogeneous = False) - - task_label = gtk.Label(_('Specify a path that will never be scanned for photos or videos')) - task_label.set_line_wrap(True) - task_hbox = gtk.HBox() - task_hbox.pack_start(task_label, False, False, padding=6) - - label = gtk.Label(_('Path:')) - self.ignored_path_hbox.pack_start(label, False, False, padding=6) - self.ignored_path_hbox.pack_start(self.path_entry, True, True, padding=6) - - self.set_border_width(6) - self.set_has_separator(False) - - # when user hits enter, close the dialog window - self.set_default_response(gtk.RESPONSE_OK) - self.path_entry.set_activates_default(True) - - self.vbox.pack_start(task_hbox, False, False, padding = 6) - self.vbox.pack_start(self.ignored_path_hbox, False, False, padding=12) - - self.set_transient_for(parent_window) - self.show_all() - self.connect('response', self.on_ignored_path_resp) - - def on_ignored_path_resp(self, ignored_path_dialog, response): - user_chose_path = False - if response == gtk.RESPONSE_OK: - user_chose_path = True - logger.debug("Ignored Path entered") - else: - logger.debug("Ignored Path not entered") - self.post_entry_callback(self, user_chose_path, self.path_entry.get_text()) - - -class PreferencesDialog(): - """ - Dialog window to show Rapid Photo Downloader preferences. - - Is tightly integrated into main Rapid Photo Downloader window, i.e. - directly access members in class RapidApp. - """ - - def __init__(self, rapidapp): - - self.builder = gtk.Builder() - self.builder.set_translation_domain(config.APP_NAME) - self.builder.add_from_file(paths.share_dir("glade3/prefs.ui")) - self.builder.connect_signals(self) - - self.dialog = self.preferencesdialog - self.widget = self.dialog - self.dialog.set_transient_for(rapidapp.rapidapp) - self.prefs = rapidapp.prefs - - rapidapp.preferences_dialog_displayed = True - - self.pref_dialog_startup = True - - self.rapidapp = rapidapp - - self._setup_tab_selector() - - self._setup_control_spacing() - - self.file_types = metadatavideo.file_types_to_download() - - self._setup_sample_names() - - # setup tabs - self._setup_photo_download_folder_tab() - self._setup_image_rename_tab() - self._setup_video_download_folder_tab() - self._setup_video_rename_tab() - self._setup_rename_options_tab() - self._setup_job_code_tab() - self._setup_device_tab() - self._setup_device_options_tab() - self._setup_backup_tab() - self._setup_miscellaneous_tab() - self._setup_error_tab() - - if not metadatavideo.DOWNLOAD_VIDEO: - self.disable_video_controls() - - self.dialog.realize() - - #set the width of the left column for selecting values - #note: this must be called after self.dialog.realize(), or else the width calculation will fail - width_of_widest_sel_row = self.treeview.get_background_area(1, self.treeview_column)[2] - self.scrolled_window.set_size_request(width_of_widest_sel_row + 2, -1) - - #set the minimum width of the scolled window holding the photo rename table - if self.rename_scrolledwindow.get_vscrollbar(): - extra = self.rename_scrolledwindow.get_vscrollbar().allocation.width + 10 - else: - extra = 10 - self.rename_scrolledwindow.set_size_request(self.rename_table.allocation.width + extra, -1) - - self.dialog.show() - - self.pref_dialog_startup = False - - def __getattr__(self, key): - """Allow builder widgets to be accessed as self.widgetname - """ - widget = self.builder.get_object(key) - if widget: # cache lookups - setattr(self, key, widget) - return widget - raise AttributeError(key) - - def on_preferencesdialog_destroy(self, widget): - """ Delete variables from memory that cause a file descriptor to be created on a mounted media""" - logger.debug("Preference window closing") - - def _setup_tab_selector(self): - self.notebook.set_show_tabs(0) - self.model = gtk.ListStore(type("")) - column = gtk.TreeViewColumn() - rentext = gtk.CellRendererText() - column.pack_start(rentext, expand=0) - column.set_attributes(rentext, text=0) - self.treeview_column = column - self.treeview.append_column(column) - self.treeview.props.model = self.model - for c in self.notebook.get_children(): - label = self.notebook.get_tab_label(c).get_text() - if not label.startswith("_"): - self.model.append( (label,) ) - - - # select the first value in the list store - self.treeview.set_cursor(0,column) - - def on_download_folder_filechooser_button_selection_changed(self, widget): - path = misc.get_folder_selection(widget) - if path: - self.prefs.download_folder = path - self.update_photo_download_folder_example() - - def on_video_download_folder_filechooser_button_selection_changed(self, widget): - path = misc.get_folder_selection(widget) - if path: - self.prefs.video_download_folder = path - self.update_video_download_folder_example() - - def on_backup_folder_filechooser_button_selection_changed(self, widget): - path = misc.get_folder_selection(widget) - if path: - self.prefs.backup_location = path - self.update_backup_example() - - def on_backup_video_folder_filechooser_button_selection_changed(self, widget): - path = misc.get_folder_selection(widget) - if path: - self.prefs.backup_video_location = path - self.update_backup_example() - - def on_device_location_filechooser_button_selection_changed(self, widget): - path = misc.get_folder_selection(widget) - if path: - self.prefs.device_location = path - - def on_add_ignored_path_button_clicked(self, widget): - i = IgnorePathDialog(parent_window = self.dialog, - post_entry_callback = self.add_ignored_path) - - def add_ignored_path(self, dialog, user_chose_path, path): - dialog.destroy() - if user_chose_path: - if path and path not in self.prefs.ignored_paths: - self.ignored_paths_liststore.prepend((path, )) - self.update_ignored_paths() - selection = self.ignored_paths_treeview.get_selection() - selection.unselect_all() - selection.select_path((0, )) - #scroll to the top - adjustment = self.ignored_paths_scrolledwindow.get_vadjustment() - adjustment.set_value(adjustment.lower) - - def on_ignored_paths_use_re_checkbutton_toggled(self, checkbutton): - self.prefs.use_re_ignored_paths = checkbutton.get_active() - if self.prefs.use_re_ignored_paths and not self.pref_dialog_startup: - # check for invalid regular expressions - self.update_ignored_paths() - - def on_remove_ignored_path_button_clicked(self, button): - self._remove_from_treeview(self.ignored_paths_treeview) - self.update_ignored_paths() - - def on_remove_all_ignored_paths_button_clicked(self, button): - i = RemoveAllIgnoredPathsDialog(self.dialog, self.remove_all_ignored_paths) - - def remove_all_ignored_paths(self, dialog, user_selected): - dialog.destroy() - if user_selected: - self.ignored_paths_liststore.clear() - self.update_ignored_paths() - - def on_remove_remembered_device_button_clicked(self, button): - """ - uses remembered devices treeview to delete any removed items from the - device_whitelist and device_blacklist prefs - """ - blacklist = [i for i in self.prefs.device_blacklist if i] - whitelist = [i for i in self.prefs.device_whitelist if i] - selection = self.remembered_devices_treeview.get_selection() - model, selected = selection.get_selected_rows() - iters = [model.get_iter(path) for path in selected] - # only delete if a value is selected - if iters: - no = len(iters) - path = None - for i in range(0, no): - iter = iters[i] - if i == no - 1: - path = model.get_path(iter) - v = self.remembered_devices_liststore.get_value(iter, 0) - if v in blacklist: - blacklist.remove(v) - elif v in whitelist: - whitelist.remove(v) - else: - logger.debug("Unknown remembered device %s", v) - model.remove(iter) - - # now that we removed the selection, play nice with - # the user and select the next item - selection.select_path(path) - - # if there was no selection that meant the user - # removed the last entry, so we try to select the - # last item - if not selection.path_is_selected(path): - row = path[0]-1 - # test case for empty lists - if row >= 0: - selection.select_path((row,)) - - self.prefs.device_blacklist = blacklist - self.prefs.device_whitelist = whitelist - - def on_remove_all_remembered_device_button_clicked(self, button): - r = RemoveAllRemeberedDevicesDialog(self.dialog, self.remove_all_remembered_devices) - - def remove_all_remembered_devices(self, dialog, user_selected): - dialog.destroy() - if user_selected: - self.remembered_devices_liststore.clear() - self.prefs.device_blacklist = [] - self.prefs.device_whitelist = [] - - def _setup_sample_names(self, use_dummy_data = False): - """ - If use_dummy_data is True, then samples will not attempt to get - data from actual download files - """ - job_code = self.prefs.most_recent_job_code() - if job_code is None: - job_code = _("Job Code") - self.downloads_today_tracker = DownloadsTodayTracker( - day_start = self.prefs.day_start, - downloads_today = self.prefs.downloads_today[1], - downloads_today_date = self.prefs.downloads_today[0]) - self.sequences = gn.Sequences(self.downloads_today_tracker, - self.prefs.stored_sequence_no) - - # get example photo and video data - if use_dummy_data: - self.sample_photo = None - else: - self.sample_photo = self.rapidapp.thumbnails.get_sample_file(rpdfile.FILE_TYPE_PHOTO) - if self.sample_photo is not None: - # try to load metadata from the file returned - # if it fails, give up with this sample file - if not self.sample_photo.load_metadata(): - self.sample_photo = None - else: - self.sample_photo.sequences = self.sequences - self.sample_photo.download_start_time = datetime.datetime.now() - - if self.sample_photo is None: - self.sample_photo = rpdfile.SamplePhoto(sequences=self.sequences) - - self.sample_photo.job_code = job_code - - self.sample_video = None - if metadatavideo.DOWNLOAD_VIDEO: - if not use_dummy_data: - self.sample_video = self.rapidapp.thumbnails.get_sample_file(rpdfile.FILE_TYPE_VIDEO) - if self.sample_video is not None: - self.sample_video.load_metadata() - self.sample_video.sequences = self.sequences - self.sample_video.download_start_time = datetime.datetime.now() - if self.sample_video is None: - self.sample_video = rpdfile.SampleVideo(sequences=self.sequences) - self.sample_video.job_code = job_code - - - - def _setup_control_spacing(self): - """ - set spacing of some but not all controls - """ - - self._setup_table_spacing(self.download_folder_table) - self._setup_table_spacing(self.video_download_folder_table) - self.download_folder_table.set_row_spacing(2, - hd.VERTICAL_CONTROL_SPACE) - self.video_download_folder_table.set_row_spacing(2, - hd.VERTICAL_CONTROL_SPACE) - self._setup_table_spacing(self.rename_example_table) - self._setup_table_spacing(self.video_rename_example_table) - self.devices_table.set_col_spacing(0, hd.NESTED_CONTROLS_SPACE) - self.automation_table.set_col_spacing(0, hd.NESTED_CONTROLS_SPACE) - - self._setup_table_spacing(self.backup_table) - self.backup_table.set_col_spacing(1, hd.NESTED_CONTROLS_SPACE) - self.backup_table.set_col_spacing(2, hd.CONTROL_LABEL_SPACE) - self._setup_table_spacing(self.compatibility_table) - self.compatibility_table.set_row_spacing(0, - hd.VERTICAL_CONTROL_LABEL_SPACE) - self._setup_table_spacing(self.error_table) - - - def _setup_table_spacing(self, table): - table.set_col_spacing(0, hd.NESTED_CONTROLS_SPACE) - table.set_col_spacing(1, hd.CONTROL_LABEL_SPACE) - - def _setup_subfolder_table(self): - self.subfolder_table = SubfolderTable(self, None) - self.subfolder_vbox.pack_start(self.subfolder_table) - self.subfolder_table.show_all() - - def _setup_video_subfolder_table(self): - self.video_subfolder_table = VideoSubfolderTable(self, None) - self.video_subfolder_vbox.pack_start(self.video_subfolder_table) - self.video_subfolder_table.show_all() - - def _setup_photo_download_folder_tab(self): - self.download_folder_filechooser_button = gtk.FileChooserButton( - _("Select a folder to download photos to")) - self.download_folder_filechooser_button.set_current_folder( - self.prefs.download_folder) - self.download_folder_filechooser_button.set_action( - gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) - self.download_folder_filechooser_button.connect("selection-changed", - self.on_download_folder_filechooser_button_selection_changed) - - self.download_folder_table.attach( - self.download_folder_filechooser_button, - 2, 3, 2, 3, yoptions = gtk.SHRINK) - self.download_folder_filechooser_button.show() - - self._setup_subfolder_table() - self.update_photo_download_folder_example() - - def _setup_video_download_folder_tab(self): - self.video_download_folder_filechooser_button = gtk.FileChooserButton( - _("Select a folder to download videos to")) - self.video_download_folder_filechooser_button.set_current_folder( - self.prefs.video_download_folder) - self.video_download_folder_filechooser_button.set_action( - gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) - self.video_download_folder_filechooser_button.connect("selection-changed", - self.on_video_download_folder_filechooser_button_selection_changed) - - self.video_download_folder_table.attach( - self.video_download_folder_filechooser_button, - 2, 3, 2, 3, yoptions = gtk.SHRINK) - self.video_download_folder_filechooser_button.show() - self._setup_video_subfolder_table() - self.update_video_download_folder_example() - - def _setup_image_rename_tab(self): - - self.rename_table = PhotoRenameTable(self, self.rename_scrolledwindow) - self.rename_table_vbox.pack_start(self.rename_table) - self.rename_table.show_all() - self._setup_photo_original_name() - self.update_photo_rename_example() - - def _setup_photo_original_name(self): - self.original_name_label.set_markup("<i>%s</i>" % self.sample_photo.display_name) - - def _setup_video_rename_tab(self): - - self.video_rename_table = VideoRenameTable(self, self.video_rename_scrolledwindow) - self.video_rename_table_vbox.pack_start(self.video_rename_table) - self.video_rename_table.show_all() - self._setup_video_original_name() - self.update_video_rename_example() - - def _setup_video_original_name(self): - if self.sample_video is not None: - self.video_original_name_label.set_markup("<i>%s</i>" % self.sample_video.display_name) - else: - self.video_original_name_label.set_markup("") - - def _setup_rename_options_tab(self): - - # sequence numbers - self.downloads_today_entry = ValidatedEntry.ValidatedEntry(ValidatedEntry.bounded(ValidatedEntry.v_int, int, 0)) - self.stored_number_entry = ValidatedEntry.ValidatedEntry(ValidatedEntry.bounded(ValidatedEntry.v_int, int, 1)) - self.downloads_today_entry.connect('changed', self.on_downloads_today_entry_changed) - self.stored_number_entry.connect('changed', self.on_stored_number_entry_changed) - v = self.rapidapp.downloads_today_tracker.get_and_maybe_reset_downloads_today() - self.downloads_today_entry.set_text(str(v)) - # make the displayed value of stored sequence no 1 more than actual value - # so as not to confuse the user - self.stored_number_entry.set_text(str(self.prefs.stored_sequence_no+1)) - self.sequence_vbox.pack_start(self.downloads_today_entry, expand=True, fill=True) - self.sequence_vbox.pack_start(self.stored_number_entry, expand=False) - self.downloads_today_entry.show() - self.stored_number_entry.show() - hour, minute = self.rapidapp.downloads_today_tracker.get_day_start() - self.hour_spinbutton.set_value(float(hour)) - self.minute_spinbutton.set_value(float(minute)) - - self.synchronize_raw_jpg_checkbutton.set_active( - self.prefs.synchronize_raw_jpg) - - #compatibility - self.strip_characters_checkbutton.set_active( - self.prefs.strip_characters) - - def _setup_job_code_tab(self): - self.job_code_liststore = gtk.ListStore(str) - column = gtk.TreeViewColumn() - rentext = gtk.CellRendererText() - rentext.connect('edited', self.on_job_code_edited) - rentext.set_property('editable', True) - - column.pack_start(rentext, expand=0) - column.set_attributes(rentext, text=0) - self.job_code_treeview_column = column - self.job_code_treeview.append_column(column) - self.job_code_treeview.props.model = self.job_code_liststore - for code in self.prefs.job_codes: - self.job_code_liststore.append((code, )) - - # set multiple selections - self.job_code_treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) - - self.remove_all_job_code_button.set_image(gtk.image_new_from_stock( - gtk.STOCK_CLEAR, - gtk.ICON_SIZE_BUTTON)) - def _setup_device_options_tab(self): - """ - Setup ignored paths and remembered devices tab in prefs dialog - """ - - self.ignored_paths_use_re_checkbutton.set_active( - self.prefs.use_re_ignored_paths) - - self.ignored_paths_liststore = gtk.ListStore(str) - column = gtk.TreeViewColumn() - rentext = gtk.CellRendererText() - rentext.connect('edited', self.on_ignored_path_edited) - rentext.set_property('editable', True) - - column.pack_start(rentext, expand=0) - column.set_attributes(rentext, text=0) - self.ignored_paths_treeview_column = column - self.ignored_paths_treeview.append_column(column) - self.ignored_paths_treeview.props.model = self.ignored_paths_liststore - for path in self.prefs.ignored_paths: - self.ignored_paths_liststore.append((path, )) - - self.ignored_paths_treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) - self.remove_all_ignored_paths_button.set_image(gtk.image_new_from_stock( - gtk.STOCK_CLEAR, - gtk.ICON_SIZE_BUTTON)) - - # Remembered devices are a little different in that they cannot be - # edited, and they can only added when the user is prompted by the - # program. Moreover, the list the user sees is a combination of two - # lists: device_whitelist and device_blacklist - - self.remembered_devices_liststore = gtk.ListStore(str) - column = gtk.TreeViewColumn() - rentext = gtk.CellRendererText() - rentext.set_property('editable', False) - - column.pack_start(rentext, expand=0) - column.set_attributes(rentext, text=0) - self.remembered_devices_treeview_column = column - self.remembered_devices_treeview.append_column(column) - self.remembered_devices_treeview.props.model = self.remembered_devices_liststore - for device in self.prefs.device_whitelist: - if device: - self.remembered_devices_liststore.append((device, )) - for device in self.prefs.device_blacklist: - if device: - self.remembered_devices_liststore.append((device, )) - - self.remembered_devices_treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) - self.remove_all_remembered_device_button.set_image(gtk.image_new_from_stock( - gtk.STOCK_CLEAR, - gtk.ICON_SIZE_BUTTON)) - - def _setup_device_tab(self): - - self.device_location_filechooser_button = gtk.FileChooserButton( - _("Select a folder containing %(file_types)s") % {'file_types':self.file_types}) - self.device_location_filechooser_button.set_current_folder( - self.prefs.device_location) - self.device_location_filechooser_button.set_action( - gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) - - self.device_location_filechooser_button.connect("selection-changed", - self.on_device_location_filechooser_button_selection_changed) - - self.devices2_table.attach(self.device_location_filechooser_button, - 1, 2, 1, 2, xoptions = gtk.EXPAND|gtk.FILL, yoptions = gtk.SHRINK) - self.device_location_filechooser_button.show() - self.autodetect_device_checkbutton.set_active( - self.prefs.device_autodetection) - self.autodetect_psd_checkbutton.set_active( - self.prefs.device_autodetection_psd) - - self.update_device_controls() - - - def _setup_backup_tab(self): - """ - Setup and configure backup tab - """ - #Manual backup location for photos file chooser - self.backup_folder_filechooser_button = gtk.FileChooserButton( - _("Select a folder in which to backup photos")) - self.backup_folder_filechooser_button.set_current_folder( - self.prefs.backup_location) - self.backup_folder_filechooser_button.set_action( - gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) - self.backup_folder_filechooser_button.connect("selection-changed", - self.on_backup_folder_filechooser_button_selection_changed) - self.backup_table.attach(self.backup_folder_filechooser_button, - 3, 4, 8, 9, yoptions = gtk.SHRINK) - self.backup_folder_filechooser_button.show() - - #Manual backup location for videos file chooser - self.backup_video_folder_filechooser_button = gtk.FileChooserButton( - _("Select a folder in which to backup videos")) - self.backup_video_folder_filechooser_button.set_current_folder( - self.prefs.backup_video_location) - self.backup_video_folder_filechooser_button.set_action( - gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) - self.backup_video_folder_filechooser_button.connect("selection-changed", - self.on_backup_video_folder_filechooser_button_selection_changed) - self.backup_table.attach(self.backup_video_folder_filechooser_button, - 3, 4, 9, 10, yoptions = gtk.SHRINK) - self.backup_video_folder_filechooser_button.show() - - self.backup_identifier_entry.set_text(self.prefs.backup_identifier) - self.video_backup_identifier_entry.set_text(self.prefs.video_backup_identifier) - - #setup controls for manipulating sensitivity - self._backup_controls0 = [self.auto_detect_backup_checkbutton] - self._backup_controls1 = [self.backup_identifier_explanation_label, - self.backup_identifier_label, - self.backup_identifier_entry, - self.example_backup_path_label, - self.backup_example_label,] - self._backup_controls2 = [self.backup_location_label, - self.backup_folder_filechooser_button, - self.backup_location_explanation_label] - - if metadatavideo.DOWNLOAD_VIDEO: - self._backup_controls2 += [self.backup_video_folder_filechooser_button, - self.backup_video_location_label] - else: - self.backup_video_folder_filechooser_button.set_sensitive(False) - self.backup_video_location_label.set_sensitive(False) - - self._backup_controls = self._backup_controls0 + self._backup_controls1 + \ - self._backup_controls2 - - self._backup_video_controls = [self.video_backup_identifier_label, - self.video_backup_identifier_entry] - - #assign values to checkbuttons only when other controls - #have been setup, because their toggle signal is activated - #when a value is assigned - - self.backup_checkbutton.set_active(self.prefs.backup_images) - self.auto_detect_backup_checkbutton.set_active( - self.prefs.backup_device_autodetection) - self.update_backup_controls() - self.update_backup_example() - - def _setup_miscellaneous_tab(self): - self.auto_startup_checkbutton.set_active( - self.prefs.auto_download_at_startup) - self.auto_insertion_checkbutton.set_active( - self.prefs.auto_download_upon_device_insertion) - self.auto_unmount_checkbutton.set_active( - self.prefs.auto_unmount) - self.auto_exit_checkbutton.set_active( - self.prefs.auto_exit) - self.auto_exit_force_checkbutton.set_active( - self.prefs.auto_exit_force) - self.generate_thumbnails_checkbutton.set_active( - self.prefs.generate_thumbnails) - self.auto_rotate_checkbutton.set_active( - self.prefs.auto_rotate_jpeg) - self.verify_file_checkbutton.set_active( - self.prefs.verify_file) - - self.update_misc_controls() - - - def _setup_error_tab(self): - if self.prefs.download_conflict_resolution == config.SKIP_DOWNLOAD: - self.skip_download_radiobutton.set_active(True) - else: - self.add_identifier_radiobutton.set_active(True) - - if self.prefs.backup_duplicate_overwrite: - self.backup_duplicate_overwrite_radiobutton.set_active(True) - else: - self.backup_duplicate_skip_radiobutton.set_active(True) - - - def update_example_file_name(self, display_table, rename_table, sample_rpd_file, generator, example_label): - if hasattr(self, display_table) and sample_rpd_file is not None: - sample_rpd_file.download_folder = self.prefs.get_download_folder_for_file_type(sample_rpd_file.file_type) - sample_rpd_file.strip_characters = self.prefs.strip_characters - sample_rpd_file.initialize_problem() - name = generator.generate_name(sample_rpd_file) - else: - name = '' - - # since this is markup, escape it - text = "<i>%s</i>" % utilities.escape(name) - - if sample_rpd_file is not None: - if sample_rpd_file.has_problem(): - text += "\n" - # Translators: please do not modify or leave out html formatting tags like <i> and <b>. These are used to format the text the users sees - text += _("<i><b>Warning:</b> There is insufficient metadata to fully generate the name. Please use other renaming options.</i>") - - example_label.set_markup(text) - - def update_photo_rename_example(self): - """ - Displays example image name to the user - """ - generator = gn.PhotoName(self.prefs.image_rename) - self.update_example_file_name('rename_table', self.rename_table, - self.sample_photo, generator, - self.new_name_label) - - - def update_video_rename_example(self): - """ - Displays example video name to the user - """ - if self.sample_video is not None: - generator = gn.VideoName(self.prefs.video_rename) - else: - generator = None - self.update_example_file_name('video_rename_table', - self.video_rename_table, - self.sample_video, generator, - self.video_new_name_label) - - def update_download_folder_example(self, display_table, subfolder_table, - download_folder, sample_rpd_file, - generator, - example_download_path_label, - subfolder_warning_label): - """ - Displays example subfolder name(s) to the user - """ - - if hasattr(self, display_table) and sample_rpd_file is not None: - #~ subfolder_table.update_example_job_code() - sample_rpd_file.strip_characters = self.prefs.strip_characters - sample_rpd_file.initialize_problem() - path = generator.generate_name(sample_rpd_file) - else: - path = '' - - text = os.path.join(download_folder, path) - # since this is markup, escape it - path = utilities.escape(text) - - warning = "" - if sample_rpd_file is not None: - if sample_rpd_file.has_problem(): - warning = _("<i><b>Warning:</b> There is insufficient metadata to fully generate subfolders. Please use other subfolder naming options.</i>" ) - - # Translators: you should not modify or leave out the %s. This is a code used by the programming language python to insert a value that thes user will see - example_download_path_label.set_markup(_("<i>Example: %s</i>") % text) - subfolder_warning_label.set_markup(warning) - - def update_photo_download_folder_example(self): - if hasattr(self, 'subfolder_table'): - generator = gn.PhotoSubfolder(self.prefs.subfolder) - self.update_download_folder_example('subfolder_table', - self.subfolder_table, self.prefs.download_folder, - self.sample_photo, generator, - self.example_photo_download_path_label, - self.photo_subfolder_warning_label) - - def update_video_download_folder_example(self): - if hasattr(self, 'video_subfolder_table'): - if self.sample_video is not None: - generator = gn.VideoSubfolder(self.prefs.video_subfolder) - else: - generator = None - self.update_download_folder_example('video_subfolder_table', - self.video_subfolder_table, - self.prefs.video_download_folder, - self.sample_video, generator, - self.example_video_download_path_label, - self.video_subfolder_warning_label) - - def on_hour_spinbutton_value_changed(self, spinbutton): - hour = spinbutton.get_value_as_int() - minute = self.minute_spinbutton.get_value_as_int() - self.rapidapp.downloads_today_tracker.set_day_start(hour, minute) - self.on_downloads_today_entry_changed(self.downloads_today_entry) - - def on_minute_spinbutton_value_changed(self, spinbutton): - hour = self.hour_spinbutton.get_value_as_int() - minute = spinbutton.get_value_as_int() - self.rapidapp.downloads_today_tracker.set_day_start(hour, minute) - self.on_downloads_today_entry_changed(self.downloads_today_entry) - - def on_downloads_today_entry_changed(self, entry): - # do not update value if a download is occurring - it will mess it up! - if self.rapidapp.download_is_occurring(): - logger.info("Downloads today value not updated, as a download is currently occurring") - else: - v = entry.get_text() - try: - v = int(v) - except: - v = 0 - if v < 0: - v = 0 - self.rapidapp.downloads_today_tracker.reset_downloads_today(v) - self.rapidapp.refresh_downloads_today = True - self.update_photo_rename_example() - - def on_stored_number_entry_changed(self, entry): - # do not update value if a download is occurring - it will mess it up! - if self.rapidapp.download_is_occurring(): - logger.info("Stored number value not updated, as a download is currently occurring") - else: - v = entry.get_text() - try: - # the displayed value of stored sequence no 1 more than actual value - # so as not to confuse the user - v = int(v) - 1 - except: - v = 0 - if v < 0: - v = 0 - self.prefs.stored_sequence_no = v - self.update_photo_rename_example() - - def _update_subfolder_pref_on_error(self, new_pref_list): - self.prefs.subfolder = new_pref_list - - def _update_video_subfolder_pref_on_error(self, new_pref_list): - self.prefs.video_subfolder = new_pref_list - - - def check_subfolder_values_valid_on_exit(self, users_pref_list, update_pref_function, filetype, default_pref_list): - """ - Checks that the user has not entered in any inappropriate values - - If they have, filters out bad values and warns the user - """ - filtered, pref_list = filter_subfolder_prefs(users_pref_list) - if filtered: - logger.info("The %(filetype)s subfolder preferences had some unnecessary values removed.", {'filetype': filetype}) - if pref_list: - update_pref_function(pref_list) - else: - #Preferences list is now empty - msg = _("The %(filetype)s subfolder preferences entered are invalid and cannot be used.\nThey will be reset to their default values.") % {'filetype': filetype} - sys.stderr.write(msg + "\n") - misc.run_dialog(PROGRAM_NAME, msg) - update_pref_function(self.prefs.get_default(default_pref_list)) - - def on_preferencesdialog_response(self, dialog, arg): - if arg == gtk.RESPONSE_HELP: - webbrowser.open("http://www.damonlynch.net/rapid/documentation") - else: - # arg==gtk.RESPONSE_CLOSE, or the user hit the 'x' to close the window - self.prefs.backup_identifier = self.backup_identifier_entry.get_property("text") - self.prefs.video_backup_identifier = self.video_backup_identifier_entry.get_property("text") - - #check subfolder preferences for bad values - self.check_subfolder_values_valid_on_exit(self.prefs.subfolder, self._update_subfolder_pref_on_error, _("photo"), "subfolder") - self.check_subfolder_values_valid_on_exit(self.prefs.video_subfolder, self._update_video_subfolder_pref_on_error, _("video"), "video_subfolder") - - self.dialog.destroy() - self.rapidapp.preferences_dialog_displayed = False - self.rapidapp.post_preference_change() - - - - - def on_add_job_code_button_clicked(self, button): - j = JobCodeDialog(parent_window = self.dialog, - job_codes = self.prefs.job_codes, - default_job_code = None, - post_job_code_entry_callback=self.add_job_code, - entry_only = True) - - def add_job_code(self, dialog, user_chose_code, job_code): - dialog.destroy() - if user_chose_code: - if job_code and job_code not in self.prefs.job_codes: - self.job_code_liststore.prepend((job_code, )) - self.update_job_codes() - selection = self.job_code_treeview.get_selection() - selection.unselect_all() - selection.select_path((0, )) - #scroll to the top - adjustment = self.job_code_scrolledwindow.get_vadjustment() - adjustment.set_value(adjustment.lower) - - def _remove_from_treeview(self, treeview): - """ - Removes selected items from a treeview, allowing multiple selections - """ - selection = treeview.get_selection() - model, selected = selection.get_selected_rows() - iters = [model.get_iter(path) for path in selected] - # only delete if a value is selected - if iters: - no = len(iters) - path = None - for i in range(0, no): - iter = iters[i] - if i == no - 1: - path = model.get_path(iter) - model.remove(iter) - - # now that we removed the selection, play nice with - # the user and select the next item - selection.select_path(path) - - # if there was no selection that meant the user - # removed the last entry, so we try to select the - # last item - if not selection.path_is_selected(path): - row = path[0]-1 - # test case for empty lists - if row >= 0: - selection.select_path((row,)) - - - def on_remove_job_code_button_clicked(self, button): - """ remove selected job codes (can be multiple selection)""" - - self._remove_from_treeview(self.job_code_treeview) - self.update_job_codes() - self.update_photo_rename_example() - self.update_video_rename_example() - self.update_photo_download_folder_example() - self.update_video_download_folder_example() - - def on_remove_all_job_code_button_clicked(self, button): - j = RemoveAllJobCodeDialog(self.dialog, self.remove_all_job_code) - - def remove_all_job_code(self, dialog, user_selected): - dialog.destroy() - if user_selected: - self.job_code_liststore.clear() - self.update_job_codes() - self.update_photo_rename_example() - self.update_video_rename_example() - self.update_photo_download_folder_example() - self.update_video_download_folder_example() - - def on_job_code_edited(self, widget, path, new_text): - iter = self.job_code_liststore.get_iter(path) - self.job_code_liststore.set_value(iter, 0, new_text) - self.update_job_codes() - self.update_photo_rename_example() - self.update_video_rename_example() - self.update_photo_download_folder_example() - self.update_video_download_folder_example() - - def _update_prefs_list(self, liststore): - replacement_list = [] - for row in liststore: - replacement_list.append(row[0]) - return replacement_list - - def update_job_codes(self): - """ update preferences with list of job codes""" - self.prefs.job_codes = self._update_prefs_list(self.job_code_liststore) - - def on_ignored_path_edited(self, widget, path, new_text): - iter = self.ignored_paths_liststore.get_iter(path) - self.ignored_paths_liststore.set_value(iter, 0, new_text) - self.update_ignored_paths() - - def update_ignored_paths(self): - ignored_paths = self._update_prefs_list(self.ignored_paths_liststore) - - # remove any trailing slashes - ignored_paths = [path.rstrip('/') for path in ignored_paths if path] - # remove any blank values from ignored_paths - ignored_paths = [path for path in ignored_paths if path] - - if self.prefs.use_re_ignored_paths: - ip = [] - bad_paths = '' - for path in ignored_paths: - # check for validity - try: - re.match(path, '') - ip.append(path) - except: - logger.error("Ignoring invalid regular expression: %s", path) - bad_paths += path + '\n' - ignored_paths = ip - if bad_paths: - bad_paths = bad_paths[:-1] - if bad_paths.find('\n') >= 0: - msg = _("The following regular expressions are invalid, and will be removed unless you correct them:\n %s") % bad_paths - else: - msg = _("This regular expression is invalid, and will be removed unless you correct it:\n %s") % bad_paths - misc.run_dialog(_("Invalid regular expression"), msg, self) - - self.prefs.ignored_paths = ignored_paths - - def on_auto_startup_checkbutton_toggled(self, checkbutton): - self.prefs.auto_download_at_startup = checkbutton.get_active() - - def on_auto_insertion_checkbutton_toggled(self, checkbutton): - self.prefs.auto_download_upon_device_insertion = checkbutton.get_active() - - def on_auto_unmount_checkbutton_toggled(self, checkbutton): - self.prefs.auto_unmount = checkbutton.get_active() - - def on_auto_rotate_checkbutton_toggled(self, checkbutton): - self.prefs.auto_rotate_jpeg = checkbutton.get_active() - - def on_verify_file_checkbutton_toggled(self, checkbutton): - self.prefs.verify_file = checkbutton.get_active() - - def on_auto_exit_checkbutton_toggled(self, checkbutton): - active = checkbutton.get_active() - self.prefs.auto_exit = active - if not active: - self.prefs.auto_exit_force = False - self.auto_exit_force_checkbutton.set_active(False) - self.update_misc_controls() - - def on_auto_exit_force_checkbutton_toggled(self, checkbutton): - self.prefs.auto_exit_force = checkbutton.get_active() - - def on_autodetect_device_checkbutton_toggled(self, checkbutton): - self.prefs.device_autodetection = checkbutton.get_active() - self.update_device_controls() - - def on_autodetect_psd_checkbutton_toggled(self, checkbutton): - self.prefs.device_autodetection_psd = checkbutton.get_active() - - def on_generate_thumbnails_checkbutton_toggled(self, checkbutton): - self.prefs.generate_thumbnails = checkbutton.get_active() - - def on_backup_duplicate_overwrite_radiobutton_toggled(self, widget): - self.prefs.backup_duplicate_overwrite = widget.get_active() - - def on_backup_duplicate_skip_radiobutton_toggled(self, widget): - self.prefs.backup_duplicate_overwrite = not widget.get_active() - - def on_treeview_cursor_changed(self, tree): - path, column = tree.get_cursor() - self.notebook.set_current_page(path[0]) - - def on_synchronize_raw_jpg_checkbutton_toggled(self, check_button): - self.prefs.synchronize_raw_jpg = check_button.get_active() - - def on_strip_characters_checkbutton_toggled(self, check_button): - self.prefs.strip_characters = check_button.get_active() - self.update_photo_rename_example() - self.update_photo_download_folder_example() - self.update_video_download_folder_example() - - def on_add_identifier_radiobutton_toggled(self, widget): - if widget.get_active(): - self.prefs.download_conflict_resolution = config.ADD_UNIQUE_IDENTIFIER - else: - self.prefs.download_conflict_resolution = config.SKIP_DOWNLOAD - - - def update_device_controls(self): - """ - Sets sensitivity of image device controls - """ - - controls = [self.device_location_explanation_label, - self.device_location_label, - self.device_location_filechooser_button] - - if self.prefs.device_autodetection: - for c in controls: - c.set_sensitive(False) - self.autodetect_psd_checkbutton.set_sensitive(True) - self.autodetect_image_devices_label.set_sensitive(True) - else: - for c in controls: - c.set_sensitive(True) - self.autodetect_psd_checkbutton.set_sensitive(False) - self.autodetect_image_devices_label.set_sensitive(False) - - if not self.pref_dialog_startup: - logger.debug("Resetting sample file photo and video files") - self._setup_sample_names(use_dummy_data = True) - self._setup_photo_original_name() - self.update_photo_download_folder_example() - self.update_photo_rename_example() - self.update_video_download_folder_example() - self._setup_video_original_name() - self.update_video_rename_example() - - def update_misc_controls(self): - """ - Sets sensitivity of miscillaneous controls - """ - - self.auto_exit_force_checkbutton.set_sensitive(self.prefs.auto_exit) - - - def update_backup_controls(self): - """ - Sets sensitivity of backup related widgets - """ - - if not self.backup_checkbutton.get_active(): - for c in self._backup_controls + self._backup_video_controls: - c.set_sensitive(False) - - else: - for c in self._backup_controls0: - c.set_sensitive(True) - self.update_backup_controls_auto() - - def update_backup_controls_auto(self): - """ - Sets sensitivity of subset of backup related widgets - """ - - if self.auto_detect_backup_checkbutton.get_active(): - for c in self._backup_controls1: - c.set_sensitive(True) - for c in self._backup_controls2: - c.set_sensitive(False) - for c in self._backup_video_controls: - c.set_sensitive(False) - if metadatavideo.DOWNLOAD_VIDEO: - for c in self._backup_video_controls: - c.set_sensitive(True) - else: - for c in self._backup_controls1: - c.set_sensitive(False) - for c in self._backup_controls2: - c.set_sensitive(True) - if metadatavideo.DOWNLOAD_VIDEO: - for c in self._backup_video_controls: - c.set_sensitive(False) - - def disable_video_controls(self): - """ - Disables video preferences if video downloading is disabled - (probably because the appropriate libraries to enable - video metadata extraction are not installed) - """ - controls = [self.example_video_filename_label, - self.original_video_filename_label, - self.new_video_filename_label, - self.video_new_name_label, - self.video_original_name_label, - self.video_rename_scrolledwindow, - self.video_folders_hbox, - self.video_backup_identifier_label, - self.video_backup_identifier_entry - ] - for c in controls: - c.set_sensitive(False) - - self.videos_cannot_be_downloaded_label.show() - self.folder_videos_cannot_be_downloaded_label.show() - self.folder_videos_cannot_be_downloaded_hbox.show() - - def on_auto_detect_backup_checkbutton_toggled(self, widget): - self.prefs.backup_device_autodetection = widget.get_active() - self.update_backup_controls_auto() - - def on_backup_checkbutton_toggled(self, widget): - self.prefs.backup_images = self.backup_checkbutton.get_active() - self.update_backup_controls() - - def on_backup_identifier_entry_changed(self, widget): - self.update_backup_example() - - def on_video_backup_identifier_entry_changed(self, widget): - self.update_backup_example() - - def on_backup_scan_folder_on_entry_changed(self, widget): - self.update_backup_example() - - def update_backup_example(self): - # Translators: this value is used as an example device when automatic backup device detection is enabled. You should translate this. - drive1 = os.path.join(config.MEDIA_LOCATION, _("externaldrive1")) - # Translators: this value is used as an example device when automatic backup device detection is enabled. You should translate this. - drive2 = os.path.join(config.MEDIA_LOCATION, _("externaldrive2")) - - path = os.path.join(drive1, self.backup_identifier_entry.get_text()) - path2 = os.path.join(drive2, self.backup_identifier_entry.get_text()) - path3 = os.path.join(drive2, self.video_backup_identifier_entry.get_text()) - path = utilities.escape(path) - path2 = utilities.escape(path2) - path3 = utilities.escape(path3) - if metadatavideo.DOWNLOAD_VIDEO: - example = "<i>%s</i>\n<i>%s</i>\n<i>%s</i>" % (path, path2, path3) - else: - example = "<i>%s</i>\n<i>%s</i>" % (path, path2) - self.example_backup_path_label.set_markup(example) diff --git a/rapid/prefs.py b/rapid/prefs.py deleted file mode 100644 index cc89c96..0000000 --- a/rapid/prefs.py +++ /dev/null @@ -1,193 +0,0 @@ -### Copyright (C) 2002-2006 Stephen Kennedy <stevek@gnome.org> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -### Modified August 2007 by Damon Lynch to allow use of list value preferences -### Modified May 2010 by Damon Lynch to allow preferences to be reset - -"""Module to help implement 'instant-apply' preferences. - -Usage: - -import prefs -defaults = { - "colour" : prefs.Value(prefs.STRING, "red") - "size" : prefs.Value(prefs.INT, 10) -} - -p = prefs.Preferences("/apps/myapp", defaults) -# use variables as if they were normal attributes. -draw(p.colour, p.size) -# settings are persistent. (saved in gconf) -p.color = "blue" - -""" - -class Value(object): - """Represents a settable preference. - """ - - __slots__ = ["type", "default", "current"] - - def __init__(self, t, d): - """Create a value. - - t : a string : one of ("bool", "int", "string", "list") - d : the default value, also the initial value - """ - self.type = t - self.default = d - self.current = d - - def setfunc(self, gconf, rootkey, attr): - setfunc = getattr(gconf, "set_%s" % self.type) - setfunc("%s/%s" % (rootkey, attr), self.current) - - def getfunc(self, gconf, rootkey, attr): - getfunc = getattr(gconf, "get_%s" % self.type) - return getfunc("%s/%s" % (rootkey, attr)) - - -class ListValue(Value): - """ - Represents a list type settable preference. - """ - - __slots__ = Value.__slots__ + ["list_type"] - def __init__(self, list_type, d): - """ - Create a list value. - - d : the default value, also the initial value - list_type: the type of elements the list contains - """ - Value.__init__(self, LIST, d) - self.list_type = list_type - - def setfunc(self, gconf, rootkey, attr): - setfunc = getattr(gconf, "set_list") - setfunc("%s/%s" % (rootkey, attr), self.list_type, self.current) - - def getfunc(self, gconf, rootkey, attr): - getfunc = getattr(gconf, "get_list") - return getfunc("%s/%s" % (rootkey, attr), self.list_type) - - -# maybe fall back to ConfigParser if gconf is unavailable. -import gconf - -# types of values allowed -BOOL = "bool" -INT = "int" -STRING = "string" -FLOAT = "float" -LIST = "list" -# PAIR = "pair" -STRING_LIST = gconf.VALUE_STRING -INT_LIST = gconf.VALUE_INT -BOOL_LIST = gconf.VALUE_BOOL -FLOAT_LIST = gconf.VALUE_FLOAT -## - -class Preferences(object): - """Persistent preferences object. - - Example: - import prefs - defaults = {"spacing": prefs.Value(prefs.INT, 4), - "font": prefs.Value(prefs.STRING, "monospace") } - p = prefs.Prefs("myapp", defaults) - print p.font - p.font = "sans" # written to gconf too - p2 = prefs.Prefs("myapp", defaults) - print p.font # prints "sans" - """ - - def __init__(self, rootkey, initial): - """Create a preferences object. - - Settings are initialised with 'initial' and then overriden - from values in the gconf database if available. - - rootkey : the root gconf key where the values will be stored - initial : a dictionary of string to Value objects. - """ - self.__dict__["_gconf"] = gconf.client_get_default() - self.__dict__["_listeners"] = [] - self.__dict__["_rootkey"] = rootkey - self.__dict__["_prefs"] = initial - self._gconf.add_dir(rootkey, gconf.CLIENT_PRELOAD_NONE) - self._gconf.notify_add(rootkey, self._on_preference_changed) - for key, value in self._prefs.items(): - gval = self._gconf.get_without_default("%s/%s" % (rootkey, key) ) - if gval != None: - value.current = value.getfunc(self._gconf, rootkey, key) - - def __getattr__(self, attr): - return self._prefs[attr].current - - def get_default(self, attr): - return self._prefs[attr].default - - def __setattr__(self, attr, val): - value = self._prefs[attr] - - if value.current != val: - value.current = val - value.setfunc(self._gconf, self._rootkey, attr) - - try: - for l in self._listeners: - l(attr,val) - except StopIteration: - pass - - def _on_preference_changed(self, client, timestamp, entry, extra): - attr = entry.key[ entry.key.rindex("/")+1 : ] - try: - valuestruct = self._prefs[attr] - except KeyError: # unknown key, we don't care about it - pass - else: - if entry.value != None: # value has changed - newval = valuestruct.getfunc(self._gconf, self._rootkey, attr) - setattr( self, attr, newval) - else: # value has been deleted - setattr( self, attr, valuestruct.default ) - - def notify_add(self, callback): - """Register a callback to be called when a preference changes. - - callback : a callable object which take two parameters, 'attr' the - name of the attribute changed and 'val' the new value. - """ - self._listeners.append(callback) - - def dump(self): - """Print all preferences. - """ - for k,v in self._prefs.items(): - print k, v.type, v.current - - def reset(self): - """ - reset all preferences to defaults - """ - - for key in self._prefs: - self.__setattr__(key, self.get_default(key)) - - diff --git a/rapid/prefsrapid.py b/rapid/prefsrapid.py deleted file mode 100644 index 05b012a..0000000 --- a/rapid/prefsrapid.py +++ /dev/null @@ -1,451 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2007-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import subprocess, os, datetime, re - -import prefs - -import preferencesdialog as pd -from generatenameconfig import * -import rpdfile - -import utilities -import config -__version__ = config.version - -import multiprocessing -import logging -logger = multiprocessing.get_logger() - -from gettext import gettext as _ - -def _get_default_location_legacy(options, ignore_missing_dir=False): - if ignore_missing_dir: - return utilities.get_full_path(options[0]) - for default in options: - path = utilities.get_full_path(default) - if os.path.isdir(path): - return path - return utilities.get_full_path('') - -def _get_default_location_XDG(dir_type): - proc = subprocess.Popen(['xdg-user-dir', dir_type], stdout=subprocess.PIPE) - output = proc.communicate()[0].strip() - return output - -def get_default_photo_location(ignore_missing_dir=False): - try: - return _get_default_location_XDG('PICTURES') - except: - return _get_default_location_legacy(config.DEFAULT_PHOTO_LOCATIONS, ignore_missing_dir) - -def get_default_video_location(ignore_missing_dir=False): - try: - return _get_default_location_XDG('VIDEOS') - except: - return _get_default_location_legacy(config.DEFAULT_VIDEO_LOCATIONS, ignore_missing_dir) - -def get_default_backup_photo_identifier(): - return os.path.split(get_default_photo_location(ignore_missing_dir = True))[1] - -def get_default_backup_video_identifier(): - return os.path.split(get_default_video_location(ignore_missing_dir = True))[1] - -def today(): - return datetime.date.today().strftime('%Y-%m-%d') - -class RapidPreferences(prefs.Preferences): - - defaults = { - "program_version": prefs.Value(prefs.STRING, ""), - "download_folder": prefs.Value(prefs.STRING, - get_default_photo_location()), - "video_download_folder": prefs.Value(prefs.STRING, - get_default_video_location()), - "subfolder": prefs.ListValue(prefs.STRING_LIST, DEFAULT_SUBFOLDER_PREFS), - "video_subfolder": prefs.ListValue(prefs.STRING_LIST, DEFAULT_VIDEO_SUBFOLDER_PREFS), - "image_rename": prefs.ListValue(prefs.STRING_LIST, [FILENAME, - NAME_EXTENSION, - ORIGINAL_CASE]), - "video_rename": prefs.ListValue(prefs.STRING_LIST, [FILENAME, - NAME_EXTENSION, - ORIGINAL_CASE]), - "device_autodetection": prefs.Value(prefs.BOOL, True), - "device_location": prefs.Value(prefs.STRING, os.path.expanduser('~')), - "device_autodetection_psd": prefs.Value(prefs.BOOL, False), - "device_whitelist": prefs.ListValue(prefs.STRING_LIST, ['']), - "device_blacklist": prefs.ListValue(prefs.STRING_LIST, ['']), - "ignored_paths": prefs.ListValue(prefs.STRING_LIST, ['.Trash', - '.thumbnails']), - "use_re_ignored_paths": prefs.Value(prefs.BOOL, False), - "backup_images": prefs.Value(prefs.BOOL, False), - "backup_device_autodetection": prefs.Value(prefs.BOOL, True), - "backup_identifier": prefs.Value(prefs.STRING, - get_default_backup_photo_identifier()), - "video_backup_identifier": prefs.Value(prefs.STRING, - get_default_backup_video_identifier()), - "backup_location": prefs.Value(prefs.STRING, os.path.expanduser('~')), - "backup_video_location": prefs.Value(prefs.STRING, os.path.expanduser('~')), - "strip_characters": prefs.Value(prefs.BOOL, True), - "auto_download_at_startup": prefs.Value(prefs.BOOL, False), - "auto_download_upon_device_insertion": prefs.Value(prefs.BOOL, False), - "auto_unmount": prefs.Value(prefs.BOOL, False), - "auto_exit": prefs.Value(prefs.BOOL, False), - "auto_exit_force": prefs.Value(prefs.BOOL, False), - "auto_delete": prefs.Value(prefs.BOOL, False), - "download_conflict_resolution": prefs.Value(prefs.STRING, - config.SKIP_DOWNLOAD), - "backup_duplicate_overwrite": prefs.Value(prefs.BOOL, False), - "display_selection": prefs.Value(prefs.BOOL, True), - "display_size_column": prefs.Value(prefs.BOOL, True), - "display_filename_column": prefs.Value(prefs.BOOL, False), - "display_type_column": prefs.Value(prefs.BOOL, True), - "display_path_column": prefs.Value(prefs.BOOL, False), - "display_device_column": prefs.Value(prefs.BOOL, False), - "display_preview_folders": prefs.Value(prefs.BOOL, True), - "show_log_dialog": prefs.Value(prefs.BOOL, False), - "day_start": prefs.Value(prefs.STRING, "03:00"), - "downloads_today": prefs.ListValue(prefs.STRING_LIST, [today(), '0']), - "stored_sequence_no": prefs.Value(prefs.INT, 0), - "job_codes": prefs.ListValue(prefs.STRING_LIST, [_('New York'), - _('Manila'), _('Prague'), _('Helsinki'), _('Wellington'), - _('Tehran'), _('Kampala'), _('Paris'), _('Berlin'), _('Sydney'), - _('Budapest'), _('Rome'), _('Moscow'), _('Delhi'), _('Warsaw'), - _('Jakarta'), _('Madrid'), _('Stockholm')]), - "synchronize_raw_jpg": prefs.Value(prefs.BOOL, False), - "vpaned_pos": prefs.Value(prefs.INT, 0), - "main_window_size_x": prefs.Value(prefs.INT, 0), - "main_window_size_y": prefs.Value(prefs.INT, 0), - "main_window_maximized": prefs.Value(prefs.INT, 0), - "show_warning_downloading_from_camera": prefs.Value(prefs.BOOL, True), - #~ "preview_zoom": prefs.Value(prefs.INT, zoom), - "generate_thumbnails": prefs.Value(prefs.BOOL, True), - "auto_rotate_jpeg": prefs.Value(prefs.BOOL, True), - "verify_file": prefs.Value(prefs.BOOL, False), - } - - def __init__(self): - prefs.Preferences.__init__(self, config.GCONF_KEY, self.defaults) - - - def get_downloads_today_tracker(self): - return DownloadsTodayTracker(downloads_today_date = self.downloads_today[0], - downloads_today = self.downloads_today[1], - day_start = self.day_start - ) - - def set_downloads_today_from_tracker(self, downloads_today_tracker): - self.downloads_today = downloads_today_tracker.downloads_today - self.day_start = downloads_today_tracker.day_start - - def get_sample_job_code(self): - if self.job_codes: - return self.job_codes[0] - else: - return '' - - def _get_pref_lists(self): - return (self.image_rename, self.subfolder, self.video_rename, - self.video_subfolder) - - def _pref_list_uses_component(self, pref_list, pref_component, offset): - for i in range(0, len(pref_list), 3): - if pref_list[i+offset] == pref_component: - return True - return False - - def must_synchronize_raw_jpg(self): - """Returns True if synchronize_raw_jpg is True and photo renaming - uses sequence values""" - if self.synchronize_raw_jpg: - for s in LIST_SEQUENCE_L1: - if self._pref_list_uses_component(self.image_rename, s, 1): - return True - return False - - def any_pref_uses_stored_sequence_no(self): - """Returns True if any of the pref lists contain a stored sequence no""" - for pref_list in self._get_pref_lists(): - if self._pref_list_uses_component(pref_list, STORED_SEQ_NUMBER, 1): - return True - return False - - def any_pref_uses_session_sequece_no(self): - """Returns True if any of the pref lists contain a session sequence no""" - for pref_list in self._get_pref_lists(): - if self._pref_list_uses_component(pref_list, SESSION_SEQ_NUMBER, 1): - return True - return False - - def any_pref_uses_sequence_letter_value(self): - """Returns True if any of the pref lists contain a sequence letter""" - for pref_list in self._get_pref_lists(): - if self._pref_list_uses_component(pref_list, SEQUENCE_LETTER, 1): - return True - return False - - def reset(self): - """ - resets all preferences to default values - """ - - prefs.Preferences.reset(self) - self.program_version = __version__ - - - def pref_uses_job_code(self, pref_list): - """ Returns True if the particular preferences contains a job code""" - for i in range(0, len(pref_list), 3): - if pref_list[i] == JOB_CODE: - return True - return False - - def any_pref_uses_job_code(self): - """ Returns True if any of the preferences contain a job code""" - for pref_list in self._get_pref_lists(): - if self.pref_uses_job_code(pref_list): - return True - return False - - def most_recent_job_code(self): - if len(self.job_codes) > 0: - return self.job_codes[0] - else: - return None - - def get_pref_lists_by_file_type(self, file_type): - """ - Returns tuple of subfolder and file rename pref lists for the given - file type - """ - if file_type == rpdfile.FILE_TYPE_PHOTO: - return (self.subfolder, self.image_rename) - else: - return (self.video_subfolder, self.video_rename) - - def get_download_folder_for_file_type(self, file_type): - """ - Returns the download folder for the given file type - """ - if file_type == rpdfile.FILE_TYPE_PHOTO: - return self.download_folder - else: - return self.video_download_folder - - -class DownloadsTodayTracker: - """ - Handles tracking the number of downloads undertaken on any one day. - - When a day starts is flexible. See http://damonlynch.net/rapid/documentation/#renameoptions - """ - def __init__(self, downloads_today_date, downloads_today, day_start): - self.day_start = day_start # string - self.downloads_today = [downloads_today_date, str(downloads_today)] # two strings - - def get_and_maybe_reset_downloads_today(self): - v = self.get_downloads_today() - if v <= 0: - self.reset_downloads_today() - return v - - def get_downloads_today(self): - """Returns the preference value for the number of downloads performed today - - If value is less than zero, that means the date has changed""" - - hour, minute = self.get_day_start() - try: - adjusted_today = datetime.datetime.strptime("%s %s:%s" % (self.downloads_today[0], hour, minute), "%Y-%m-%d %H:%M") - except: - logger.critical("Failed to calculate date adjustment. Download today values appear to be corrupted: %s %s:%s", - self.downloads_today[0], hour, minute) - adjusted_today = None - - now = datetime.datetime.today() - - if adjusted_today is None: - return -1 - - if now < adjusted_today : - try: - return int(self.downloads_today[1]) - except ValueError: - logger.error("Invalid Downloads Today value. Resetting value to zero.") - self.get_downloads_today(self.downloads_today[0] , 0) - return 0 - else: - return -1 - - def get_raw_downloads_today(self): - """ - Gets value without changing it in any way, except to check for type convesion error. - If there is an error, then the value is reset - """ - try: - return int(self.downloads_today[1]) - except ValueError: - logger.critical("Downloads today value is corrupted: %s", self.downloads_today[1]) - self.downloads_today[1] = '0' - return 0 - - def set_raw_downloads_today_from_int(self, downloads_today): - self.downloads_today[1] = str(downloads_today) - - def set_raw_downloads_today_date(self, downloads_today_date): - self.downloads_today[0] = downloads_today_date - - def get_raw_downloads_today_date(self): - return self.downloads_today[0] - - def get_raw_day_start(self): - """ - Gets value without changing it in any way - """ - return self.day_start - - def get_day_start(self): - try: - t1, t2 = self.day_start.split(":") - return (int(t1), int(t2)) - except ValueError: - logger.error("'Start of day' preference value %s is corrupted. Resetting to midnight", self.day_start) - self.day_start = "0:0" - return 0, 0 - - def increment_downloads_today(self): - """ returns true if day changed """ - v = self.get_downloads_today() - if v >= 0: - self.set_downloads_today(self.downloads_today[0], v + 1) - return False - else: - self.reset_downloads_today(1) - return True - - def reset_downloads_today(self, value=0): - now = datetime.datetime.today() - hour, minute = self.get_day_start() - t = datetime.time(hour, minute) - if now.time() < t: - date = today() - else: - d = datetime.datetime.today() + datetime.timedelta(days=1) - date = d.strftime(('%Y-%m-%d')) - - self.set_downloads_today(date, value) - - def set_downloads_today(self, date, value=0): - self.downloads_today = [date, str(value)] - - def set_day_start(self, hour, minute): - self.day_start = "%s:%s" % (hour, minute) - - def log_vals(self): - logger.info("Date %s Value %s Day start %s", self.downloads_today[0], self.downloads_today[1], self.day_start) - - - -def check_prefs_for_validity(prefs): - """ - Checks preferences for validity (called at program startup) - - Returns tuple with two values: - 1. true if the passed in preferences are valid, else returns False - 2. message if prefs are invalid - """ - - - msg = '' - valid = True - tests = ((prefs.image_rename, pd.PhotoNamePrefs), - (prefs.subfolder, pd.PhotoSubfolderPrefs), - (prefs.video_rename, pd.VideoNamePrefs), - (prefs.video_subfolder, pd.VideoSubfolderPrefs)) - for pref, pref_widgets in tests: - p = pref_widgets(pref) - try: - p.check_prefs_for_validity() - except pd.PrefError as e: - valid = False - msg += e.msg + "\n" - - return (valid, msg) - -def insert_pref_lists(prefs, rpd_file): - """ - Convenience function to insert subfolder and file rename pref_lists for - the given file type. - - Returns the modified rpd_file - """ - subfolder_pref_list, name_pref_list = prefs.get_pref_lists_by_file_type(rpd_file.file_type) - rpd_file.subfolder_pref_list = subfolder_pref_list - rpd_file.name_pref_list = name_pref_list - return rpd_file - -def format_pref_list_for_pretty_print(pref_list): - """ returns a string useful for printing the preferences""" - - v = '' - - for i in range(0, len(pref_list), 3): - if (pref_list[i+1] or pref_list[i+2]): - c = ':' - else: - c = '' - s = "%s%s " % (pref_list[i], c) - - if pref_list[i+1]: - s = "%s%s" % (s, pref_list[i+1]) - if pref_list[i+2]: - s = "%s (%s)" % (s, pref_list[i+2]) - v += s + "\n" - return v - - -def check_and_compile_re(ignored_paths): - """ - configure regular expression to search for, checking to see it is valid - - returns compiled RE, or None if invalid - """ - - pattern = '' - for path in ignored_paths: - # check for validity - try: - re.match(path, '') - pattern += '.*%s$|' % path - except: - logger.error("Ignoring invalid regular expression: %s", path) - if pattern: - pattern = pattern[:-1] - - logger.debug("Ignored paths regular expression pattern: %s", pattern) - if not pattern: - logger.warning("No regular expression is specified") - return None - else: - try: - return re.compile(pattern) - except: - logger.error('This regular expression is invalid: %s', pattern) - return None diff --git a/rapid/problemnotification.py b/rapid/problemnotification.py deleted file mode 100755 index 5982a6d..0000000 --- a/rapid/problemnotification.py +++ /dev/null @@ -1,452 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2010-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import sys -import types -from gettext import gettext as _ - - -# components -SUBFOLDER_COMPONENT = _('subfolder') -FILENAME_COMPONENT = _('filename') - -# problem categories -METADATA_PROBLEM = 1 -FILE_PROBLEM = 2 -GENERATION_PROBLEM = 3 -DOWNLOAD_PROBLEM = 4 -DOWNLOAD_PROBLEM_W_NO = 5 -DIFFERENT_EXIF = 6 -FILE_ALREADY_EXISTS = 7 -UNIQUE_IDENTIFIER_CAT = 8 -BACKUP_PROBLEM = 9 -BACKUP_OK = 10 -FILE_ALREADY_DOWN_CAT = 11 -VERIFICATION_PROBLEM = 12 - -# problem text -MISSING_METADATA = 1 -INVALID_DATE_TIME = 2 -MISSING_FILE_EXTENSION = 3 -MISSING_IMAGE_NUMBER = 4 -ERROR_IN_GENERATION = 5 - -CANNOT_DOWNLOAD_BAD_METADATA = 6 - -ERROR_IN_NAME_GENERATION = 7 - -DOWNLOAD_COPYING_ERROR = 8 -DOWNLOAD_COPYING_ERROR_W_NO = 9 - -FILE_ALREADY_EXISTS_NO_DOWNLOAD = 10 -UNIQUE_IDENTIFIER_ADDED = 11 -BACKUP_EXISTS = 12 -BACKUP_EXISTS_OVERWRITTEN = 13 -NO_BACKUP_PERFORMED = 14 -BACKUP_ERROR = 15 -BACKUP_DIRECTORY_CREATION = 16 - -SAME_FILE_DIFFERENT_EXIF = 17 -NO_DOWNLOAD_WAS_BACKED_UP = 18 -FILE_ALREADY_DOWNLOADED = 19 - -FILE_VERIFICATION_FAILED = 20 -BACKUP_VERIFICATION_FAILED = 21 - -#extra details -UNIQUE_IDENTIFIER = '__1' -EXISTING_FILE = '__2' -NO_DATA_TO_NAME = '__3' -DOWNLOAD_COPYING_ERROR_DETAIL = '__4' -DOWNLOAD_COPYING_ERROR_W_NO_DETAIL = '__5' -BACKUP_OK_TYPE = '__6' - -# category, text, duplicates allowed -problem_definitions = { - - MISSING_METADATA: (METADATA_PROBLEM, "%s", True), - INVALID_DATE_TIME: (METADATA_PROBLEM, _('Date time value %s appears invalid.'), False), - MISSING_FILE_EXTENSION: (METADATA_PROBLEM, _("Filename does not have an extension."), False), - # a number component is something like the 8346 in IMG_8346.JPG - MISSING_IMAGE_NUMBER: (METADATA_PROBLEM, _("Filename does not have a number component."), False), - ERROR_IN_GENERATION: (METADATA_PROBLEM, _("Error generating component %s."), False), # a generic problem - - CANNOT_DOWNLOAD_BAD_METADATA: (FILE_PROBLEM, _("%(filetype)s metadata cannot be read"), False), - - ERROR_IN_NAME_GENERATION: (GENERATION_PROBLEM, _("%(filetype)s %(area)s could not be generated"), False), - - DOWNLOAD_COPYING_ERROR: (DOWNLOAD_PROBLEM, _("An error occurred when copying the %(filetype)s"), False), - DOWNLOAD_COPYING_ERROR_W_NO: (DOWNLOAD_PROBLEM_W_NO, _("An error occurred when copying the %(filetype)s"), False), - - FILE_VERIFICATION_FAILED: (VERIFICATION_PROBLEM, _("The %(filetype)s did not download correctly"), False), - BACKUP_VERIFICATION_FAILED: (BACKUP_PROBLEM, "%s", True), - - FILE_ALREADY_EXISTS_NO_DOWNLOAD:(FILE_ALREADY_EXISTS, _("%(filetype)s already exists"), False), - UNIQUE_IDENTIFIER_ADDED: (UNIQUE_IDENTIFIER_CAT, _("%(filetype)s already exists"), False), - BACKUP_EXISTS: (BACKUP_PROBLEM, "%s", True), - BACKUP_EXISTS_OVERWRITTEN: (BACKUP_PROBLEM, "%s", True), - NO_BACKUP_PERFORMED: (BACKUP_PROBLEM, _("%(filetype)s could not be backed up because no suitable backup locations were found."), False), - BACKUP_ERROR: (BACKUP_PROBLEM, "%s", True), - BACKUP_DIRECTORY_CREATION: (BACKUP_PROBLEM, "%s", True), - NO_DOWNLOAD_WAS_BACKED_UP: (BACKUP_OK, "%s", True), - - SAME_FILE_DIFFERENT_EXIF: (DIFFERENT_EXIF, _("%(image1)s was taken on %(image1_date)s at %(image1_time)s, and %(image2)s on %(image2_date)s at %(image2_time)s."), False), - FILE_ALREADY_DOWNLOADED: (FILE_ALREADY_DOWN_CAT, _('%(filetype)s was already downloaded'), False), -} - -extra_detail_definitions = { - UNIQUE_IDENTIFIER: _("The existing %(filetype)s was last modified on %(date)s at %(time)s. Unique identifier '%(identifier)s' added."), - EXISTING_FILE: _("The existing %(filetype)s was last modified on %(date)s at %(time)s."), - NO_DATA_TO_NAME: _("There is no data with which to name the %(filetype)s."), - DOWNLOAD_COPYING_ERROR_DETAIL: "%s", - DOWNLOAD_COPYING_ERROR_W_NO_DETAIL: _("Error: %(errorno)s %(strerror)s"), - BACKUP_OK_TYPE: "%s", -} - -class Problem: - """ - Collect problems with subfolder and filename generation, download errors, and so forth - - Problems are human readable - """ - - def __init__(self): - self.problems = {} - self.categories = {} - self.components = [] - self.extra_detail = {} - - def add_problem(self, component, problem_definition, *args): - added = True - if problem_definition not in problem_definitions: - sys.stderr.write("FIXME: unknown problem definition!\n") - else: - category, problem, duplicates_ok = problem_definitions[problem_definition] - - if args: - # check for special case of named arguments in a dictionary - if isinstance(args[0], types.DictType): - problem_details = problem % args[0] - else: - problem_details = problem % args - else: - problem_details = problem - - if not duplicates_ok: - self.problems[problem_definition] = [problem_details] - else: - if problem_definition in self.problems: - if problem_details not in self.problems[problem_definition]: - self.problems[problem_definition].append(problem_details) - else: - added = False - else: - self.problems[problem_definition] = [problem_details] - - if category not in self.categories or not added: - self.categories[category] = 1 - else: - self.categories[category] += 1 - - if (component is not None) and (component not in self.components): - self.components.append(component) - - def add_extra_detail(self, extra_detail, *args): - if extra_detail not in extra_detail_definitions: - self.extra_detail[extra_detail] = args[0] - else: - detail = extra_detail_definitions[extra_detail] - - if args: - if isinstance(args[0], types.DictType): - extra_details = detail % args[0] - else: - extra_details = detail % args - else: - extra_details = detail - - self.extra_detail[extra_detail] = extra_details - - - def has_problem(self): - return len(self.problems) > 0 - - def get_problems(self): - """ - Returns a string with the problems encountered in downloading the file. - """ - - def get_backup_error_inst(volume): - if ('%s%s' % (BACKUP_ERROR, volume)) in self.extra_detail: - return self.extra_detail['%s%s' % (BACKUP_ERROR, volume)] - else: - return '' - - def get_dir_creation_inst(volume): - return self.extra_detail['%s%s' % (BACKUP_DIRECTORY_CREATION, volume)] - - v = '' - - # special cases - - if VERIFICATION_PROBLEM in self.categories: - return _("File verification failed. The downloaded version is different from the original.") - - if FILE_PROBLEM in self.categories: - return _("The metadata might be corrupt.") - - if FILE_ALREADY_DOWN_CAT in self.categories: - return _("The filename, extension and Exif information indicate it has already been downloaded.") - - if FILE_ALREADY_EXISTS in self.categories: - if EXISTING_FILE in self.extra_detail: - v = self.extra_detail[EXISTING_FILE] - - - if UNIQUE_IDENTIFIER_CAT in self.categories: - v = self.extra_detail[UNIQUE_IDENTIFIER] - - if DOWNLOAD_PROBLEM in self.categories: - v = self.extra_detail[DOWNLOAD_COPYING_ERROR_DETAIL] - - if DOWNLOAD_PROBLEM_W_NO in self.categories: - v = self.extra_detail[DOWNLOAD_COPYING_ERROR_W_NO_DETAIL] - - if BACKUP_OK in self.categories: - details = self.problems[NO_DOWNLOAD_WAS_BACKED_UP] - if len(self.problems[NO_DOWNLOAD_WAS_BACKED_UP]) == 1: - vv = _(' It was backed up to %(volume)s') % {'volume': details[0]} - else: - vv = _(" It was backed up to these devices: ") - for d in details[:-1]: - vv += _("%s, ") % d - vv = _("%(volumes)s and %(final_volume)s.") % \ - {'volumes': vv[:-2], - 'final_volume': details[-1]} \ - + ' ' - v += vv - - if GENERATION_PROBLEM in self.categories: - v = self.extra_detail[NO_DATA_TO_NAME] - - if DIFFERENT_EXIF in self.categories: - v = self.problems[SAME_FILE_DIFFERENT_EXIF][0] - if METADATA_PROBLEM in self.categories: - v = _('Photos detected with the same filenames, but taken at different times: %(details)s' ) % {'details':v} - - # Problems backing up - if BACKUP_PROBLEM in self.categories: - vv = '' - for p in self.problems: - details = self.problems[p] - - if p == NO_BACKUP_PERFORMED: - vv = details[0] - - elif p == BACKUP_ERROR: - - if len(details) == 1: - volume = details[0] - inst = get_backup_error_inst(volume) - if inst: - vv += _("An error occurred when backing up on %(volume)s: %(inst)s.") % {'volume': volume, 'inst': inst} + ' ' - else: - vv += _("An error occurred when backing up on %(volume)s.") % {'volume': volume} + ' ' - else: - vv += _("Errors occurred when backing up on the following backup devices: ") - for volume in details[:-1]: - inst = get_backup_error_inst(volume) - if inst: - vv += _("%(volume)s (%(inst)s), ") % {'volume': volume, 'inst': inst} - else: - vv += _("%(volume)s, ") % {'volume': volume} - volume = details[-1] - inst = get_backup_error_inst(volume) - if inst: - vv = _("%(volumes)s and %(volume)s (%(inst)s).") % \ - {'volumes': vv[:-2], - 'volume': volume, - 'inst': get_inst(volume)} - else: - vv = _("%(volumes)s and %(volume)s.") % \ - {'volumes': vv[:-2], - 'volume': volume} \ - + ' ' - - - elif p == BACKUP_EXISTS: - if len(details) == 1: - vv += _("Backup already exists on %(volume)s.") % {'volume': details[0]} + ' ' - else: - vv += _("Backups already exist in these locations: ") - for d in details[:-1]: - vv += _("%s, ") % d - vv = _("%(volumes)s and %(final_volume)s.") % \ - {'volumes': vv[:-2], - 'final_volume': details[-1]} \ - + ' ' - - elif p == BACKUP_EXISTS_OVERWRITTEN: - if len(details) == 1: - vv += _("Backup overwritten on %(volume)s.") % {'volume': details[0]} + ' ' - else: - vv += _("Backups overwritten on these devices: ") - for d in details[:-1]: - vv += _("%s, ") % d - vv = _("%(volumes)s and %(final_volume)s.") % \ - {'volumes': vv[:-2], - 'final_volume': details[-1]} \ - + ' ' - - elif p == BACKUP_DIRECTORY_CREATION: - if len(details) == 1: - volume = details[0] - vv += _("An error occurred when creating directories on %(volume)s: %(inst)s.") % {'volume': volume, 'inst': get_dir_creation_inst(volume)} + ' ' - else: - vv += _("Errors occurred when creating directories on the following backup devices: ") - for volume in details[:-1]: - vv += _("%(volume)s (%(inst)s), ") % {'volume': volume, 'inst': get_dir_creation_inst(volume)} - volume = details[-1] - vv = _("%(volumes)s and %(volume)s (%(inst)s).") % \ - {'volumes': vv[:-2], - 'volume': volume, - 'inst': get_dir_creation_inst(volume)} \ - + ' ' - - elif p == BACKUP_VERIFICATION_FAILED: - if len(details) == 1: - vv += _("File verification failed on %(volume)s. The backed up version is different from the downloaded version.") % {'volume': details[0]} + ' ' - else: - vv += _("File verification failed on these devices: ") - for d in details[:-1]: - vv += _("%s, ") % d - vv = _("%(volumes)s and %(final_volume)s.") % \ - {'volumes': vv[:-2], - 'final_volume': details[-1]} \ - + ' ' - - - if v: - v = _('%(previousproblem)s Additionally, %(newproblem)s') % {'previousproblem': v, 'newproblem': vv[0].lower() + vv[1:]} - else: - v = vv - - - if v and METADATA_PROBLEM in self.categories: - vv = self._get_generation_title() - if self.categories[METADATA_PROBLEM] > 1: - v += _(' Furthermore, there were %(problems)s.') % {'problems': vv[0].lower() + vv[1:]} - else: - v += _(' Furthermore, there was a %(problem)s.') % {'problem': vv[0].lower() + vv[1:]} - - # Problems generating file / subfolder names - if METADATA_PROBLEM in self.categories: - for p in self.problems: - vv = '' - details = self.problems[p] - if p == MISSING_METADATA: - if len(details) == 1: - vv = _("The %(type)s metadata is missing.") % {'type': details[0]} - else: - vv = _("The following metadata is missing: ") - for d in details[:-1]: - vv += ("%s, ") % d - vv = _("%(missing_metadata_elements)s and %(final_missing_metadata_element)s.") % \ - {'missing_metadata_elements': vv[:-2], - 'final_missing_metadata_element': details[-1]} - - - elif p in [MISSING_IMAGE_NUMBER, ERROR_IN_GENERATION, INVALID_DATE_TIME]: - vv = details[0] - - v += ' ' + vv - - v = v.strip() - return v - - def _get_generation_title(self): - if self.components: - if len(self.components) > 1: - if self.categories[METADATA_PROBLEM] > 1: - return _('Problems in subfolder and filename generation') - else: - return _('Problem in subfolder and filename generation') - else: - if self.categories[METADATA_PROBLEM] > 1: - return _('Problems in %s generation') % self.components[0] - else: - return _('Problem in %s generation') % self.components[0] - return '' - - - def get_title(self): - v = '' - - if BACKUP_OK in self.categories: - if FILE_ALREADY_EXISTS in self.categories: - v = _('%(filetype)s already exists, but it was backed up') % {'filetype': self.extra_detail[BACKUP_OK_TYPE]} - else: - v = _('An error occurred when copying the %(filetype)s, but it was backed up') % {'filetype': self.extra_detail[BACKUP_OK_TYPE]} - - # High priority problems - elif VERIFICATION_PROBLEM in self.categories: - v = self.problems[FILE_VERIFICATION_FAILED][0] - elif FILE_ALREADY_DOWN_CAT in self.categories: - v = self.problems[FILE_ALREADY_DOWNLOADED][0] - elif DOWNLOAD_PROBLEM in self.categories: - v = self.problems[DOWNLOAD_COPYING_ERROR][0] - elif DOWNLOAD_PROBLEM_W_NO in self.categories: - v = self.problems[DOWNLOAD_COPYING_ERROR_W_NO][0] - elif GENERATION_PROBLEM in self.categories: - v = self.problems[ERROR_IN_NAME_GENERATION][0] - elif FILE_ALREADY_EXISTS in self.categories: - v = self.problems[FILE_ALREADY_EXISTS_NO_DOWNLOAD][0] - elif UNIQUE_IDENTIFIER_CAT in self.categories: - v = self.problems[UNIQUE_IDENTIFIER_ADDED][0] - elif FILE_PROBLEM in self.categories: - v = self.problems[CANNOT_DOWNLOAD_BAD_METADATA][0] - - # Lesser priority - elif len(self.categories) > 1: - v = _('Multiple problems were encountered') - elif DIFFERENT_EXIF in self.categories: - v = _('Photos detected with the same filenames, but taken at different times') - elif METADATA_PROBLEM in self.categories: - v = self._get_generation_title() - - if BACKUP_PROBLEM in self.categories: - if self.categories[BACKUP_PROBLEM] >1: - vp = _("there were errors backing up") - vv = _("There were errors backing up") - else: - vp = _("there was an error backing up") - vv = _("There was an error backing up") - if v: - # e.g. - v = _("%(previousproblem)s, and %(backinguperror)s") % {'previousproblem': v, 'backinguperror':vp} - else: - v = vv - - return v - - - -if __name__ == '__main__': - pass diff --git a/rapid/rapid.py b/rapid/rapid.py deleted file mode 100755 index 303e1f7..0000000 --- a/rapid/rapid.py +++ /dev/null @@ -1,4177 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -use_pynotify = True - -import tempfile - -import dbus -import dbus.bus -import dbus.service -from dbus.mainloop.glib import DBusGMainLoop -DBusGMainLoop(set_as_default=True) - -from optparse import OptionParser - -import gtk -import gtk.gdk as gdk - -from gobject.constants import G_MAXINT - -import webbrowser - -import sys, time, types, os, datetime - -import gobject, pango, cairo, array, pangocairo, gio - -if use_pynotify: - import pynotify - -from multiprocessing import Process, Pipe, Queue, Event, Value, Array, current_process, log_to_stderr -from ctypes import c_int, c_bool, c_char - -import logging -logger = log_to_stderr() - -# Rapid Photo Downloader modules - -import rpdfile - -from misc import get_folder_selection - -import problemnotification as pn -import thumbnail as tn -import rpdmultiprocessing as rpdmp - -import preferencesdialog -import prefsrapid - -import tableplusminus as tpm -import generatename as gn - -import downloadtracker - -import filemodify - -from metadatavideo import DOWNLOAD_VIDEO, file_types_to_download -import metadataphoto -import metadatavideo -import metadataexiftool - -import scan as scan_process -import copyfiles -import subfolderfile -import backupfile -from backupfile import PHOTO_BACKUP, VIDEO_BACKUP, PHOTO_VIDEO_BACKUP - -import errorlog - -import device as dv -import utilities - -import config -__version__ = config.version - -import paths - -import gettext -gettext.bindtextdomain(config.APP_NAME) -gettext.textdomain(config.APP_NAME) - -_ = gettext.gettext - - -from utilities import format_size_for_user -from utilities import register_iconsets - - -from config import STATUS_CANNOT_DOWNLOAD, STATUS_DOWNLOADED, \ - STATUS_DOWNLOADED_WITH_WARNING, \ - STATUS_DOWNLOAD_FAILED, \ - STATUS_DOWNLOAD_PENDING, \ - STATUS_BACKUP_PROBLEM, \ - STATUS_NOT_DOWNLOADED, \ - STATUS_DOWNLOAD_AND_BACKUP_FAILED, \ - STATUS_WARNING - -DOWNLOADED = [STATUS_DOWNLOADED, STATUS_DOWNLOADED_WITH_WARNING, STATUS_BACKUP_PROBLEM] - -#Translators: if neccessary, for guidance in how to translate this program, you may see http://damonlynch.net/translate.html -PROGRAM_NAME = _('Rapid Photo Downloader') -__version__ = config.version - -def date_time_human_readable(date, with_line_break=True): - if with_line_break: - return _("%(date)s\n%(time)s") % {'date':date.strftime("%x"), 'time':date.strftime("%X")} - else: - return _("%(date)s %(time)s") % {'date':date.strftime("%x"), 'time':date.strftime("%X")} - -def date_time_subseconds_human_readable(date, subseconds): - return _("%(date)s %(hour)s:%(minute)s:%(second)s:%(subsecond)s") % \ - {'date':date.strftime("%x"), - 'hour':date.strftime("%H"), - 'minute':date.strftime("%M"), - 'second':date.strftime("%S"), - 'subsecond': subseconds} - - -class DeviceCollection(gtk.TreeView): - """ - TreeView display of devices and how many files have been copied, shown - immediately under the menu in the main application window. - """ - def __init__(self, parent_app): - - self.parent_app = parent_app - # device icon & name, size of images on the device (human readable), - # copy progress (%), copy text, eject button (None if irrelevant), - # process id, pulse - self.liststore = gtk.ListStore(gtk.gdk.Pixbuf, str, str, float, str, - gtk.gdk.Pixbuf, int, int) - self.map_process_to_row = {} - self.devices_by_scan_pid = {} - - gtk.TreeView.__init__(self, self.liststore) - - self.props.enable_search = False - # make it impossible to select a row - selection = self.get_selection() - selection.set_mode(gtk.SELECTION_NONE) - self.set_headers_visible(False) - - - # Device refers to a thing like a camera, memory card in its reader, - # external hard drive, Portable Storage Device, etc. - column0 = gtk.TreeViewColumn(_("Device")) - pixbuf_renderer = gtk.CellRendererPixbuf() - pixbuf_renderer.set_padding(2, 0) - text_renderer = gtk.CellRendererText() - text_renderer.props.ellipsize = pango.ELLIPSIZE_MIDDLE - text_renderer.set_fixed_size(160, -1) - eject_renderer = gtk.CellRendererPixbuf() - column0.pack_start(pixbuf_renderer, expand=False) - column0.pack_start(text_renderer, expand=True) - column0.pack_end(eject_renderer, expand=False) - column0.add_attribute(pixbuf_renderer, 'pixbuf', 0) - column0.add_attribute(text_renderer, 'text', 1) - column0.add_attribute(eject_renderer, 'pixbuf', 5) - self.append_column(column0) - - - # Size refers to the total size of images on the device, typically in - # MB or GB - column1 = gtk.TreeViewColumn(_("Size"), gtk.CellRendererText(), text=2) - self.append_column(column1) - - column2 = gtk.TreeViewColumn(_("Download Progress"), - gtk.CellRendererProgress(), - value=3, - text=4, - pulse=7) - self.append_column(column2) - self.show_all() - - icontheme = gtk.icon_theme_get_default() - try: - self.eject_pixbuf = icontheme.load_icon('media-eject', 16, - gtk.ICON_LOOKUP_USE_BUILTIN) - except: - self.eject_pixbuf = gtk.gdk.pixbuf_new_from_file( - paths.share_dir('glade3/media-eject.png')) - - self.add_events(gtk.gdk.BUTTON_PRESS_MASK) - self.connect('button-press-event', self.button_clicked) - - - def add_device(self, process_id, device, progress_bar_text = ''): - - # add the row, and get a temporary pointer to the row - size_files = '' - progress = 0.0 - - if device.mount is None: - eject = None - else: - eject = self.eject_pixbuf - - self.devices_by_scan_pid[process_id] = device - - iter = self.liststore.append((device.get_icon(), - device.get_name(), - size_files, - progress, - progress_bar_text, - eject, - process_id, - -1)) - - self._set_process_map(process_id, iter) - - # adjust scrolled window height, based on row height and number of ready to start downloads - - # please note, at program startup, self.row_height() will be less than it will be when already running - # e.g. when starting with 3 cards, it could be 18, but when adding 2 cards to the already running program - # (with one card at startup), it could be 21 - # must account for header row at the top - row_height = self.get_background_area(0, self.get_column(0))[3] + 1 - height = max(((len(self.map_process_to_row) + 1) * row_height), 24) - self.parent_app.device_collection_scrolledwindow.set_size_request(-1, height) - - - def update_device(self, process_id, total_size_files): - """ - Updates the size of the photos and videos on the device, displayed to the user - """ - if process_id in self.map_process_to_row: - iter = self._get_process_map(process_id) - self.liststore.set_value(iter, 2, total_size_files) - else: - logger.critical("This device is unknown") - - def get_device(self, process_id): - return self.devices_by_scan_pid.get(process_id) - - def remove_device(self, process_id): - if process_id in self.map_process_to_row: - iter = self._get_process_map(process_id) - self.liststore.remove(iter) - del self.map_process_to_row[process_id] - del self.devices_by_scan_pid[process_id] - - def get_all_displayed_processes(self): - """ - returns a list of the processes currently being displayed to the user - """ - return self.map_process_to_row.keys() - - - def _set_process_map(self, process_id, iter): - """ - convert the temporary iter into a tree reference, which is - permanent - """ - - path = self.liststore.get_path(iter) - treerowref = gtk.TreeRowReference(self.liststore, path) - self.map_process_to_row[process_id] = treerowref - - def _get_process_map(self, process_id): - """ - return the tree iter for this process - """ - - if process_id in self.map_process_to_row: - treerowref = self.map_process_to_row[process_id] - path = treerowref.get_path() - iter = self.liststore.get_iter(path) - return iter - else: - return None - - def update_progress(self, scan_pid, percent_complete, progress_bar_text, bytes_downloaded, pulse=None): - - iter = self._get_process_map(scan_pid) - if iter: - if percent_complete: - self.liststore.set_value(iter, 3, percent_complete) - if progress_bar_text: - self.liststore.set_value(iter, 4, progress_bar_text) - - if pulse is not None: - if pulse: - # Make the bar pulse - self.liststore.set_value(iter, 7, self.liststore.get_value(iter, 7) + 1) - else: - # Set to finished state - self.liststore.set_value(iter, 7, G_MAXINT) - else: - # Reset to allow fraction to be set - self.liststore.set_value(iter, 7, -1) - - - - def button_clicked(self, widget, event): - """ - Look for left single click on eject button - """ - if event.button == 1: - if len (self.liststore): - x = int(event.x) - y = int(event.y) - path, column, cell_x, cell_y = self.get_path_at_pos(x, y) - if path is not None: - if column == self.get_column(0): - if cell_x >= column.get_width() - self.eject_pixbuf.get_width(): - iter = self.liststore.get_iter(path) - if self.liststore.get_value(iter, 5) is not None: - self.unmount(process_id = self.liststore.get_value(iter, 6)) - - def unmount(self, process_id): - device = self.devices_by_scan_pid[process_id] - if device.mount is not None: - logger.debug("Unmounting device with scan pid %s", process_id) - device.mount.unmount(self.unmount_callback) - - - def unmount_callback(self, mount, result): - name = mount.get_name() - - try: - mount.unmount_finish(result) - logger.debug("%s successfully unmounted" % name) - except gio.Error, inst: - logger.error("%s did not unmount: %s", name, inst) - - if use_pynotify: - title = _("%(device)s did not unmount") % {'device': name} - message = '%s' % inst - - n = pynotify.Notification(title, message) - n.set_icon_from_pixbuf(self.parent_app.application_icon) - try: - n.show() - except: - logger.error("Unable to display message using notification system") - - -def create_cairo_image_surface(pil_image, image_width, image_height): - imgd = pil_image.tobytes("raw","BGRA", 0, 1) - data = array.array('B',imgd) - stride = image_width * 4 - image = cairo.ImageSurface.create_for_data(data, cairo.FORMAT_ARGB32, - image_width, image_height, stride) - return image - -class ThumbnailCellRenderer(gtk.CellRenderer): - __gproperties__ = { - "image": (gobject.TYPE_PYOBJECT, "Image", - "Image", gobject.PARAM_READWRITE), - - "filename": (gobject.TYPE_STRING, "Filename", - "Filename", '', gobject.PARAM_READWRITE), - - "status": (gtk.gdk.Pixbuf, "Status", - "Status", gobject.PARAM_READWRITE), - } - - def __init__(self, checkbutton_height): - gtk.CellRenderer.__init__(self) - self.image = None - - self.image_area_size = 100 - self.text_area_size = 30 - self.padding = 6 - self.checkbutton_height = checkbutton_height - self.icon_width = 20 - - def do_set_property(self, pspec, value): - setattr(self, pspec.name, value) - - def do_get_property(self, pspec): - return getattr(self, pspec.name) - - def do_render(self, window, widget, background_area, cell_area, expose_area, flags): - - cairo_context = window.cairo_create() - - x = cell_area.x - y = cell_area.y + self.checkbutton_height - 8 - w = cell_area.width - h = cell_area.height - - #constrain operations to cell area, allowing for a 1 pixel border - #either side - #~ cairo_context.rectangle(x-1, y-1, w+2, h+2) - #~ cairo_context.clip() - - #fill in the background with dark grey - #this ensures that a selected cell's fill does not make - #the text impossible to read - #~ cairo_context.rectangle(x, y, w, h) - #~ cairo_context.set_source_rgb(0.267, 0.267, 0.267) - #~ cairo_context.fill() - - #image width and height - image_w = self.image.size[0] - image_h = self.image.size[1] - - #center the image horizontally - #bottom align vertically - #top left and right corners for the image: - image_x = x + ((w - image_w) / 2) - image_y = y + self.image_area_size - image_h - - #convert PIL image to format suitable for cairo - image = create_cairo_image_surface(self.image, image_w, image_h) - - # draw a light grey border of 1px around the image - cairo_context.set_source_rgb(0.66, 0.66, 0.66) #light grey, #a9a9a9 - cairo_context.set_line_width(1) - cairo_context.rectangle(image_x-.5, image_y-.5, image_w+1, image_h+1) - cairo_context.stroke() - - # draw a thin border around each cell - #~ cairo_context.set_source_rgb(0.33,0.33,0.33) - #~ cairo_context.rectangle(x, y, w, h) - #~ cairo_context.stroke() - - #place the image - cairo_context.set_source_surface(image, image_x, image_y) - cairo_context.paint() - - #text - context = pangocairo.CairoContext(cairo_context) - - text_y = y + self.image_area_size + 10 - text_w = w - self.icon_width - text_x = x + self.icon_width - #~ context.rectangle(text_x, text_y, text_w, 15) - #~ context.clip() - - layout = context.create_layout() - - width = text_w * pango.SCALE - layout.set_width(width) - - layout.set_alignment(pango.ALIGN_CENTER) - layout.set_ellipsize(pango.ELLIPSIZE_END) - - #font color and size - fg_color = pango.AttrForeground(65535, 65535, 65535, 0, -1) - font_size = pango.AttrSize(8192, 0, -1) # 8 * 1024 = 8192 - font_family = pango.AttrFamily('sans', 0, -1) - attr = pango.AttrList() - attr.insert(fg_color) - attr.insert(font_size) - attr.insert(font_family) - layout.set_attributes(attr) - - layout.set_text(self.filename) - - context.move_to(text_x, text_y) - context.show_layout(layout) - - #status - cairo_context.set_source_pixbuf(self.status, x, y + self.image_area_size + 10) - cairo_context.paint() - - def do_get_size(self, widget, cell_area): - return (0, 0, self.image_area_size, self.image_area_size + self.text_area_size - self.checkbutton_height + 4) - - -gobject.type_register(ThumbnailCellRenderer) - - -class ThumbnailDisplay(gtk.IconView): - def __init__(self, parent_app): - gtk.IconView.__init__(self) - self.set_spacing(0) - self.set_row_spacing(5) - self.set_margin(25) - - self.set_selection_mode(gtk.SELECTION_MULTIPLE) - self.connect('selection-changed', self.on_selection_changed) - self._selected_items = [] - - self.rapid_app = parent_app - - self.batch_size = 10 - - self.thumbnail_manager = ThumbnailManager(self.thumbnail_results, self.batch_size) - self.preview_manager = PreviewManager(self.preview_results) - - self.treerow_index = {} - self.process_index = {} - - self.rpd_files = {} - - self.total_thumbs_to_generate = 0 - self.thumbnails_generated = 0 - - # dict of scan_pids that are having thumbnails generated - # value is the thumbnail process id - # this is needed when terminating thumbnailing early such as when - # user clicks download before the thumbnailing is finished - self.generating_thumbnails = {} - - self.thumbnails = {} - self.previews = {} - self.previews_being_fetched = set() - - self.stock_photo_thumbnails = tn.PhotoIcons() - self.stock_video_thumbnails = tn.VideoIcons() - - self.SELECTED_COL = 1 - self.UNIQUE_ID_COL = 2 - self.TIMESTAMP_COL = 4 - self.FILETYPE_COL = 5 - self.CHECKBUTTON_VISIBLE_COL = 6 - self.DOWNLOAD_STATUS_COL = 7 - self.STATUS_ICON_COL = 8 - - self._create_liststore() - - self.clear() - - checkbutton = gtk.CellRendererToggle() - checkbutton.set_radio(False) - checkbutton.props.activatable = True - checkbutton.props.xalign = 0.0 - checkbutton.connect('toggled', self.on_checkbutton_toggled) - self.pack_end(checkbutton, expand=False) - - self.add_attribute(checkbutton, "active", 1) - self.add_attribute(checkbutton, "visible", 6) - - checkbutton_size = checkbutton.get_size(self, None) - checkbutton_height = checkbutton_size[3] - checkbutton_width = checkbutton_size[2] - - image = ThumbnailCellRenderer(checkbutton_height) - self.pack_start(image, expand=True) - self.add_attribute(image, "image", 0) - self.add_attribute(image, "filename", 3) - self.add_attribute(image, "status", 8) - - #set the background color to a darkish grey - self.modify_base(gtk.STATE_NORMAL, gtk.gdk.Color('#444444')) - - self.show_all() - self._setup_icons() - - self.connect('item-activated', self.on_item_activated) - - def _create_liststore(self): - """ - Creates the default list store to hold the icons - """ - self.liststore = gtk.ListStore( - gobject.TYPE_PYOBJECT, # 0 PIL thumbnail - gobject.TYPE_BOOLEAN, # 1 selected or not - str, # 2 unique id - str, # 3 file name - int, # 4 timestamp for sorting, converted float - int, # 5 file type i.e. photo or video - gobject.TYPE_BOOLEAN, # 6 visibility of checkbutton - int, # 7 status of download - gtk.gdk.Pixbuf, # 8 status icon - ) - - def _setup_icons(self): - # icons to be displayed in status column - - size = 16 - # standard icons - failed = self.render_icon(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU) - self.download_failed_icon = failed.scale_simple(size, size, gtk.gdk.INTERP_HYPER) - error = self.render_icon(gtk.STOCK_DIALOG_ERROR, gtk.ICON_SIZE_MENU) - self.error_icon = error.scale_simple(size, size, gtk.gdk.INTERP_HYPER) - warning = self.render_icon(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_MENU) - self.warning_icon = warning.scale_simple(size, size, gtk.gdk.INTERP_HYPER) - - # Rapid Photo Downloader specific icons - self.downloaded_icon = gtk.gdk.pixbuf_new_from_file_at_size( - paths.share_dir('glade3/rapid-photo-downloader-downloaded.svg'), - size, size) - self.download_pending_icon = gtk.gdk.pixbuf_new_from_file_at_size( - paths.share_dir('glade3/rapid-photo-downloader-download-pending.png'), - size, size) - self.downloaded_with_warning_icon = gtk.gdk.pixbuf_new_from_file_at_size( - paths.share_dir('glade3/rapid-photo-downloader-downloaded-with-warning.svg'), - size, size) - self.downloaded_with_error_icon = gtk.gdk.pixbuf_new_from_file_at_size( - paths.share_dir('glade3/rapid-photo-downloader-downloaded-with-error.svg'), - size, size) - - # make the not yet downloaded icon a transparent square - self.not_downloaded_icon = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB, False, 8, 16, 16) - self.not_downloaded_icon.fill(0xffffffff) - self.not_downloaded_icon = self.not_downloaded_icon.add_alpha(True, chr(255), chr(255), chr(255)) - - def get_status_icon(self, status): - """ - Returns the correct icon, based on the status - """ - if status == STATUS_WARNING: - status_icon = self.warning_icon - elif status == STATUS_CANNOT_DOWNLOAD: - status_icon = self.error_icon - elif status == STATUS_DOWNLOADED: - status_icon = self.downloaded_icon - elif status == STATUS_NOT_DOWNLOADED: - status_icon = self.not_downloaded_icon - elif status in [STATUS_DOWNLOADED_WITH_WARNING, STATUS_BACKUP_PROBLEM]: - status_icon = self.downloaded_with_warning_icon - elif status in [STATUS_DOWNLOAD_FAILED, STATUS_DOWNLOAD_AND_BACKUP_FAILED]: - status_icon = self.downloaded_with_error_icon - elif status == STATUS_DOWNLOAD_PENDING: - status_icon = self.download_pending_icon - else: - logger.critical("FIXME: unknown status: %s", status) - status_icon = self.not_downloaded_icon - return status_icon - - def sort_by_timestamp(self): - self.liststore.set_sort_column_id(self.TIMESTAMP_COL, gtk.SORT_ASCENDING) - - def on_selection_changed(self, iconview): - """ - Allow selections by row (and not GTK default by square) when user is - dragging the mouse or using the keyboard to select - """ - selections = self.get_selected_items() - if len(selections) > 1: - previous_sel = selections[0][0] + 1 - # seleted items list always starts with the highest selected item - for selection in selections: - current_sel = selection[0] - if current_sel <> (previous_sel-1): - for i in range(previous_sel-1, current_sel, -1): - self.select_path(i) - previous_sel = current_sel - self._selected_items = self.get_selected_items() - - def on_checkbutton_toggled(self, cellrenderertoggle, path): - paths = [p[0] for p in self._selected_items] - if int(path) not in paths: - self._selected_items = [path,] - - for path in self._selected_items: - iter = self.liststore.get_iter(path) - status = self.liststore.get_value(iter, self.DOWNLOAD_STATUS_COL) - if status == STATUS_NOT_DOWNLOADED: - self.liststore.set_value(iter, self.SELECTED_COL, not cellrenderertoggle.get_active()) - self.select_path(path) - - self.rapid_app.set_download_action_sensitivity() - - - def set_selected(self, unique_id, value): - iter = self.get_iter_from_unique_id(unique_id) - self.liststore.set_value(iter, self.SELECTED_COL, value) - - def add_file(self, rpd_file, generate_thumbnail): - - thumbnail_icon = self.get_stock_icon(rpd_file.file_type) - unique_id = rpd_file.unique_id - scan_pid = rpd_file.scan_pid - - timestamp = int(rpd_file.modification_time) - - iter = self.liststore.append((thumbnail_icon, - True, - unique_id, - rpd_file.display_name, - timestamp, - rpd_file.file_type, - True, - STATUS_NOT_DOWNLOADED, - self.not_downloaded_icon - )) - - path = self.liststore.get_path(iter) - treerowref = gtk.TreeRowReference(self.liststore, path) - - if scan_pid in self.process_index: - self.process_index[scan_pid].append(unique_id) - else: - self.process_index[scan_pid] = [unique_id,] - - self.treerow_index[unique_id] = treerowref - self.rpd_files[unique_id] = rpd_file - - if generate_thumbnail: - self.total_thumbs_to_generate += 1 - - def get_sample_file(self, file_type): - """Returns an rpd_file for of a given file type, or None if it does - not exist""" - for unique_id, rpd_file in self.rpd_files.iteritems(): - if rpd_file.file_type == file_type: - if rpd_file.status <> STATUS_CANNOT_DOWNLOAD: - return rpd_file - - return None - - def get_unique_id_from_iter(self, iter): - return self.liststore.get_value(iter, 2) - - def get_iter_from_unique_id(self, unique_id): - treerowref = self.treerow_index[unique_id] - path = treerowref.get_path() - return self.liststore.get_iter(path) - - def on_item_activated(self, iconview, path): - """ - """ - iter = self.liststore.get_iter(path) - self.show_preview(iter=iter) - self.advance_get_preview_image(iter) - - - def _get_preview(self, unique_id, rpd_file): - if unique_id not in self.previews_being_fetched: - #check if preview should be from a downloaded file, or the source - if rpd_file.status in DOWNLOADED: - file_location = rpd_file.download_full_file_name - thm_file_name = rpd_file.download_thm_full_name - else: - file_location = rpd_file.full_file_name - thm_file_name = rpd_file.thm_full_name - - self.preview_manager.get_preview(unique_id, file_location, - thm_file_name, - rpd_file.file_type, size_max=None,) - - self.previews_being_fetched.add(unique_id) - - def show_preview(self, unique_id=None, iter=None): - if len(self.liststore): - if unique_id is not None: - iter = self.get_iter_from_unique_id(unique_id) - elif iter is not None: - unique_id = self.get_unique_id_from_iter(iter) - else: - # neither an iter or a unique_id were passed - # use iter from first selected file - # if none is selected, choose the first file - selected = self.get_selected_items() - if selected: - path = selected[0] - else: - path = 0 - iter = self.liststore.get_iter(path) - unique_id = self.get_unique_id_from_iter(iter) - - - rpd_file = self.rpd_files[unique_id] - - if unique_id in self.previews: - preview_image = self.previews[unique_id] - else: - # request daemon process to get a full size thumbnail - self._get_preview(unique_id, rpd_file) - if unique_id in self.thumbnails: - preview_image = self.thumbnails[unique_id] - else: - preview_image = self.get_stock_icon(rpd_file.file_type) - - checked = self.liststore.get_value(iter, self.SELECTED_COL) - include_checkbutton_visible = rpd_file.status == STATUS_NOT_DOWNLOADED - self.rapid_app.show_preview_image(unique_id, preview_image, - include_checkbutton_visible, checked) - - def _get_next_iter(self, iter): - iter = self.liststore.iter_next(iter) - if iter is None: - iter = self.liststore.get_iter_first() - return iter - - def _get_prev_iter(self, iter): - row = self.liststore.get_path(iter)[0] - if row == 0: - row = len(self.liststore)-1 - else: - row -= 1 - iter = self.liststore.get_iter(row) - return iter - - def show_next_image(self, unique_id): - iter = self.get_iter_from_unique_id(unique_id) - iter = self._get_next_iter(iter) - - if iter is not None: - self.show_preview(iter=iter) - - # cache next image - self.advance_get_preview_image(iter, prev=False, next=True) - - def show_prev_image(self, unique_id): - iter = self.get_iter_from_unique_id(unique_id) - iter = self._get_prev_iter(iter) - - if iter is not None: - self.show_preview(iter=iter) - - # cache next image - self.advance_get_preview_image(iter, prev=True, next=False) - - - def advance_get_preview_image(self, iter, prev=True, next=True): - unique_ids = [] - if next: - next_iter = self._get_next_iter(iter) - unique_ids.append(self.get_unique_id_from_iter(next_iter)) - - if prev: - prev_iter = self._get_prev_iter(iter) - unique_ids.append(self.get_unique_id_from_iter(prev_iter)) - - for unique_id in unique_ids: - if not unique_id in self.previews: - rpd_file = self.rpd_files[unique_id] - self._get_preview(unique_id, rpd_file) - - def check_all(self, check_all, file_type=None): - for row in self.liststore: - if row[self.CHECKBUTTON_VISIBLE_COL]: - if file_type is not None: - if row[self.FILETYPE_COL] == file_type: - row[self.SELECTED_COL] = check_all - else: - row[self.SELECTED_COL] = check_all - self.rapid_app.set_download_action_sensitivity() - - def files_are_checked_to_download(self): - """ - Returns True if there is any file that the user has indicated they - intend to download, else returns False. - """ - for row in self.liststore: - if row[self.SELECTED_COL]: - rpd_file = self.rpd_files[row[self.UNIQUE_ID_COL]] - if rpd_file.status not in DOWNLOADED: - return True - return False - - def get_files_checked_for_download(self, scan_pid): - """ - Returns a dict of scan ids and associated files the user has indicated - they want to download - - If scan_pid is not None, then returns only those files from that scan_pid - """ - files = dict() - if scan_pid is None: - for row in self.liststore: - if row[self.SELECTED_COL]: - rpd_file = self.rpd_files[row[self.UNIQUE_ID_COL]] - if rpd_file.status not in DOWNLOADED: - scan_pid = rpd_file.scan_pid - if scan_pid in files: - files[scan_pid].append(rpd_file) - else: - files[scan_pid] = [rpd_file,] - else: - files[scan_pid] = [] - for unique_id in self.process_index[scan_pid]: - rpd_file = self.rpd_files[unique_id] - if rpd_file.status not in DOWNLOADED: - iter = self.get_iter_from_unique_id(unique_id) - if self.liststore.get_value(iter, self.SELECTED_COL): - files[scan_pid].append(rpd_file) - return files - - def get_no_files_remaining(self, scan_pid): - """ - Returns the number of files that have not yet been downloaded for the - scan_pid - """ - i = 0 - for unique_id in self.process_index[scan_pid]: - rpd_file = self.rpd_files[unique_id] - if rpd_file.status == STATUS_NOT_DOWNLOADED: - i += 1 - return i - - def files_remain_to_download(self): - """ - Returns True if any files remain that are not downloaded, else returns - False - """ - for row in self.liststore: - if row[self.DOWNLOAD_STATUS_COL] == STATUS_NOT_DOWNLOADED: - return True - return False - - - def mark_download_pending(self, files_by_scan_pid): - """ - Sets status to download pending and updates thumbnails display - """ - for scan_pid in files_by_scan_pid: - for rpd_file in files_by_scan_pid[scan_pid]: - unique_id = rpd_file.unique_id - self.rpd_files[unique_id].status = STATUS_DOWNLOAD_PENDING - iter = self.get_iter_from_unique_id(unique_id) - if not self.rapid_app.auto_start_is_on: - # don't make the checkbox invisible immediately when on auto start - # otherwise the box can be rendred at the wrong size, as it is - # realized after the checkbox has already been made invisible - self.liststore.set_value(iter, self.CHECKBUTTON_VISIBLE_COL, False) - self.liststore.set_value(iter, self.SELECTED_COL, False) - self.liststore.set_value(iter, self.DOWNLOAD_STATUS_COL, STATUS_DOWNLOAD_PENDING) - icon = self.get_status_icon(STATUS_DOWNLOAD_PENDING) - self.liststore.set_value(iter, self.STATUS_ICON_COL, icon) - - def select_image(self, unique_id): - iter = self.get_iter_from_unique_id(unique_id) - path = self.liststore.get_path(iter) - self.select_path(path) - self.scroll_to_path(path, use_align=False, row_align=0.5, col_align=0.5) - - def get_stock_icon(self, file_type): - if file_type == rpdfile.FILE_TYPE_PHOTO: - return self.stock_photo_thumbnails.stock_thumbnail_image_icon - else: - return self.stock_video_thumbnails.stock_thumbnail_image_icon - - def update_status_post_download(self, rpd_file): - iter = self.get_iter_from_unique_id(rpd_file.unique_id) - self.liststore.set_value(iter, self.DOWNLOAD_STATUS_COL, rpd_file.status) - icon = self.get_status_icon(rpd_file.status) - self.liststore.set_value(iter, self.STATUS_ICON_COL, icon) - self.liststore.set_value(iter, self.CHECKBUTTON_VISIBLE_COL, False) - self.rpd_files[rpd_file.unique_id] = rpd_file - - def generate_thumbnails(self, scan_pid): - """Initiate thumbnail generation for files scanned in one process - """ - if scan_pid in self.process_index: - rpd_files = [self.rpd_files[unique_id] for unique_id in self.process_index[scan_pid]] - thumbnail_pid = self.thumbnail_manager.add_task((scan_pid, rpd_files)) - self.generating_thumbnails[scan_pid] = thumbnail_pid - - def _set_thumbnail(self, unique_id, icon): - treerowref = self.treerow_index[unique_id] - path = treerowref.get_path() - iter = self.liststore.get_iter(path) - self.liststore.set(iter, 0, icon) - - def update_thumbnail(self, thumbnail_data): - """ - Takes the generated thumbnail and updates the display - - If the thumbnail_data includes a second image, that is used to - update the thumbnail list using the unique_id - """ - unique_id = thumbnail_data[0] - thumbnail_icon = thumbnail_data[1] - - if thumbnail_icon is not None: - # get the thumbnail icon in PIL format - thumbnail_icon = thumbnail_icon.get_image() - - if thumbnail_icon: - self._set_thumbnail(unique_id, thumbnail_icon) - - if len(thumbnail_data) > 2: - # get the 2nd image in PIL format - self.thumbnails[unique_id] = thumbnail_data[2].get_image() - - def terminate_thumbnail_generation(self, scan_pid): - """ - Terminates thumbnail generation if thumbnails are currently - being generated for this scan_pid - """ - - if scan_pid in self.generating_thumbnails: - terminated = True - self.thumbnail_manager.terminate_process( - self.generating_thumbnails[scan_pid]) - del self.generating_thumbnails[scan_pid] - - if len(self.generating_thumbnails) == 0: - self._reset_thumbnail_tracking_and_display() - else: - terminated = False - - return terminated - - def mark_thumbnails_needed(self, rpd_files): - for rpd_file in rpd_files: - if rpd_file.unique_id not in self.thumbnails: - rpd_file.generate_thumbnail = True - - def _reset_thumbnail_tracking_and_display(self): - self.rapid_app.download_progressbar.set_fraction(0.0) - self.rapid_app.download_progressbar.set_text('') - self.thumbnails_generated = 0 - self.total_thumbs_to_generate = 0 - - def thumbnail_results(self, source, condition): - connection = self.thumbnail_manager.get_pipe(source) - - conn_type, data = connection.recv() - - if conn_type == rpdmp.CONN_COMPLETE: - scan_pid = data - del self.generating_thumbnails[scan_pid] - connection.close() - return False - else: - - for thumbnail_data in data: - self.update_thumbnail(thumbnail_data) - - self.thumbnails_generated += len(data) - - # clear progress bar information if all thumbnails have been - # extracted - if self.thumbnails_generated == self.total_thumbs_to_generate: - self._reset_thumbnail_tracking_and_display() - else: - if self.total_thumbs_to_generate: - self.rapid_app.download_progressbar.set_fraction( - float(self.thumbnails_generated) / self.total_thumbs_to_generate) - - - return True - - def preview_results(self, unique_id, preview_full_size, preview_small): - """ - Receive a full size preview image and update - """ - self.previews_being_fetched.remove(unique_id) - if preview_full_size: - preview_image = preview_full_size.get_image() - self.previews[unique_id] = preview_image - self.rapid_app.update_preview_image(unique_id, preview_image) - - # user can turn off option for thumbnail generation after a scan - if unique_id not in self.thumbnails and preview_small is not None: - self._set_thumbnail(unique_id, preview_small.get_image()) - - - def clear_all(self, scan_pid=None, keep_downloaded_files=False): - """ - Removes files from display and internal tracking. - - If scan_pid is not None, then only files matching that scan_pid will - be removed. Otherwise, everything will be removed. - - If keep_downloaded_files is True, files will not be removed if they - have been downloaded. - """ - if scan_pid is None and not keep_downloaded_files: - - # Here it is critically important to create a brand new liststore, - # because the old one is set to be sorted, which is extremely slow. - logger.debug("Creating new thumbnails model") - self.set_model(None) - self._create_liststore() - - self.treerow_index = {} - self.process_index = {} - - self.rpd_files = {} - else: - if scan_pid in self.process_index: - for unique_id in self.process_index[scan_pid]: - rpd_file = self.rpd_files[unique_id] - if not keep_downloaded_files or not rpd_file.status in DOWNLOADED: - treerowref = self.treerow_index[rpd_file.unique_id] - path = treerowref.get_path() - iter = self.liststore.get_iter(path) - self.liststore.remove(iter) - del self.treerow_index[rpd_file.unique_id] - del self.rpd_files[rpd_file.unique_id] - if not keep_downloaded_files or not len(self.process_index[scan_pid]): - del self.process_index[scan_pid] - - def display_thumbnails(self): - self.set_model(self.liststore) - -class TaskManager: - def __init__(self, results_callback, batch_size): - self.results_callback = results_callback - - # List of actual process, it's terminate_queue, and it's run_event - self._processes = [] - - self._pipes = {} - self.batch_size = batch_size - - self.paused = False - self.no_tasks = 0 - - - def add_task(self, task): - pid = self._setup_task(task) - logger.debug("TaskManager PID: %s", pid) - self.no_tasks += 1 - return pid - - - def _setup_task(self, task): - task_results_conn, task_process_conn = self._setup_pipe() - - source = task_results_conn.fileno() - self._pipes[source] = task_results_conn - gobject.io_add_watch(source, gobject.IO_IN, self.results_callback) - - terminate_queue = Queue() - run_event = Event() - run_event.set() - - return self._initiate_task(task, task_results_conn, task_process_conn, - terminate_queue, run_event) - - def _setup_pipe(self): - return Pipe(duplex=False) - - def _initiate_task(self, task, task_process_conn, terminate_queue, run_event): - logger.error("Implement child class method!") - - - def processes(self): - for i in range(len(self._processes)): - yield self._processes[i] - - def start(self): - self.paused = False - for scan in self.processes(): - run_event = scan[2] - if not run_event.is_set(): - run_event.set() - - def pause(self): - self.paused = True - for scan in self.processes(): - run_event = scan[2] - if run_event.is_set(): - run_event.clear() - - def _terminate_process(self, p): - self._send_termination_msg(p) - # The process might be paused: let it run - run_event = p[2] - if not run_event.is_set(): - run_event.set() - - def _send_termination_msg(self, p): - p[1].put(None) - - def terminate_process(self, process_id): - """ - Send a signal to process with matching process_id that it should - immediately terminate - """ - for p in self.processes(): - if p[0].pid == process_id: - if p[0].is_alive(): - self._terminate_process(p) - - def request_termination(self): - """ - Send a signal to processes that they should immediately terminate - """ - requested = False - for p in self.processes(): - if p[0].is_alive(): - requested = True - self._terminate_process(p) - - return requested - - def terminate_forcefully(self): - """ - Forcefully terminates any running processes. Use with great caution. - No cleanup action is performed. - - As python essential reference (4th edition) says, if the process - 'holds a lock or is involved with interprocess communication, - terminating it might cause a deadlock or corrupted I/O.' - """ - - for p in self.processes(): - if p[0].is_alive(): - logger.info("Forcefully terminating %s in %s" , p[0].name, - self.__class__.__name__) - p[0].terminate() - - - def get_pipe(self, source): - return self._pipes[source] - - def get_no_active_processes(self): - """ - Returns how many processes are currently active, i.e. running - """ - i = 0 - for p in self.processes(): - if p[0].is_alive(): - i += 1 - return i - - -class ScanManager(TaskManager): - - def __init__(self, results_callback, batch_size, - add_device_function): - TaskManager.__init__(self, results_callback, batch_size) - self.add_device_function = add_device_function - - def _initiate_task(self, task, task_results_conn, task_process_conn, - terminate_queue, run_event): - - device = task[0] - ignored_paths = task[1] - use_re_ignored_paths = task[2] - - scan = scan_process.Scan(device.get_path(), - ignored_paths, - use_re_ignored_paths, - self.batch_size, - task_process_conn, terminate_queue, run_event) - scan.start() - self._processes.append((scan, terminate_queue, run_event)) - self.add_device_function(scan.pid, device, - # This refers to when a device like a hard drive is having its contents scanned, - # looking for photos or videos. It is visible initially in the progress bar for each device - # (which normally holds "x photos and videos"). - # It maybe displayed only briefly if the contents of the device being scanned is small. - progress_bar_text=_('scanning...')) - - return scan.pid - -class CopyFilesManager(TaskManager): - - def _initiate_task(self, task, task_results_conn, - task_process_conn, terminate_queue, run_event): - photo_download_folder = task[0] - video_download_folder = task[1] - scan_pid = task[2] - files = task[3] - verify_files = task[4] - modify_files_during_download = task[5] - modify_pipe = task[6] - - copy_files = copyfiles.CopyFiles(photo_download_folder, - video_download_folder, - files, - verify_files, - modify_files_during_download, - modify_pipe, - scan_pid, self.batch_size, - task_process_conn, terminate_queue, run_event) - copy_files.start() - self._processes.append((copy_files, terminate_queue, run_event)) - return copy_files.pid - -class ThumbnailManager(TaskManager): - def _initiate_task(self, task, task_results_conn, - task_process_conn, terminate_queue, run_event): - scan_pid = task[0] - files = task[1] - generator = tn.GenerateThumbnails(scan_pid, files, self.batch_size, - task_process_conn, terminate_queue, - run_event) - generator.start() - self._processes.append((generator, terminate_queue, run_event)) - return generator.pid - -class FileModifyManager(TaskManager): - """Handles the modification or verification of of downloaded files before they are renamed - Duplex, multiprocess, similar to BackupFilesManager - """ - def __init__(self, results_callback): - TaskManager.__init__(self, results_callback=results_callback, - batch_size=0) - self.file_modify_by_scan_pid = {} - - def _initiate_task(self, task, task_results_conn, task_process_conn, - terminate_queue, run_event): - scan_pid = task[0] - auto_rotate_jpeg = task[1] - focal_length = task[2] - verify_file = task[3] - refresh_md5_on_file_change = task[4] - - file_modify = filemodify.FileModify(auto_rotate_jpeg, focal_length, - verify_file, refresh_md5_on_file_change, - task_process_conn, terminate_queue, - run_event) - file_modify.start() - self._processes.append((file_modify, terminate_queue, run_event, - task_results_conn)) - - self.file_modify_by_scan_pid[scan_pid] = (task_results_conn, file_modify.pid) - - return file_modify.pid - - def _setup_pipe(self): - return Pipe(duplex=True) - - def _send_termination_msg(self, p): - p[1].put(None) - p[3].send((None, None)) - - def get_modify_pipe(self, scan_pid): - return self.file_modify_by_scan_pid[scan_pid][0] - - -class BackupFilesManager(TaskManager): - """ - Handles backup processes. This is a little different from some other Task - Manager classes in that its pipe is Duplex, and the work done by it - is not pre-assigned when the process is started. - - Duplex, multiprocess. - """ - def __init__(self, results_callback, batch_size): - TaskManager.__init__(self, results_callback, batch_size) - self.backup_devices_by_path = {} - - def _setup_pipe(self): - return Pipe(duplex=True) - - def _send_termination_msg(self, p): - p[1].put(None) - p[3].send((None, None, None, None, None, None, None)) - - def _initiate_task(self, task, task_results_conn, task_process_conn, - terminate_queue, run_event): - path = task[0] - name = task[1] - backup_type = task[2] - backup_files = backupfile.BackupFiles(path, name, self.batch_size, - task_process_conn, terminate_queue, - run_event) - backup_files.start() - self._processes.append((backup_files, terminate_queue, run_event, - task_results_conn)) - - self.backup_devices_by_path[path] = (task_results_conn, backup_files.pid, - backup_type) - - return backup_files.pid - - def backup_file(self, move_succeeded, rpd_file, path_suffix, - backup_duplicate_overwrite, - verify_file, - download_count): - - if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO: - logger.debug("Backing up photo %s", rpd_file.download_name) - else: - logger.debug("Backing up video %s", rpd_file.download_name) - - for path in self.backup_devices_by_path: - backup_type = self.backup_devices_by_path[path][2] - do_backup = ((backup_type == PHOTO_VIDEO_BACKUP) or - (rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO and backup_type == PHOTO_BACKUP) or - (rpd_file.file_type == rpdfile.FILE_TYPE_VIDEO and backup_type == VIDEO_BACKUP)) - if do_backup: - logger.debug("Backing up to %s", path) - else: - logger.debug("Not backing up to %s", path) - # Even if not going to backup to this device, need to send it anyway so - # progress bar can be updated. Not this most efficient but the - # code is much more simple - task_results_conn = self.backup_devices_by_path[path][0] - task_results_conn.send((move_succeeded, do_backup, rpd_file, - path_suffix, - backup_duplicate_overwrite, - verify_file, download_count)) - - def add_device(self, path, name, backup_type): - """ - Convenience function to setup adding a backup device - """ - return self.add_task((path, name, backup_type)) - - def remove_device(self, path): - pid = self.backup_devices_by_path[path][1] - self.terminate_process(pid) - del self.backup_devices_by_path[path] - - -class SingleInstanceTaskManager: - """ - Base class to manage single instance processes. Examples are daemon - processes, but also a non-daemon process that has one simple task. - - Core (infrastructure) functionality is implemented in this class. - Derived classes should implemented functionality to actually implement - specific tasks. - """ - def __init__(self, results_callback): - self.results_callback = results_callback - - self.task_results_conn, self.task_process_conn = Pipe(duplex=True) - - source = self.task_results_conn.fileno() - gobject.io_add_watch(source, gobject.IO_IN, self.task_results) - - -class PreviewManager(SingleInstanceTaskManager): - def __init__(self, results_callback): - SingleInstanceTaskManager.__init__(self, results_callback) - self._get_preview = tn.GetPreviewImage(self.task_process_conn) - self._get_preview.start() - - def get_preview(self, unique_id, full_file_name, thm_file_name, file_type, size_max): - self.task_results_conn.send((unique_id, full_file_name, thm_file_name, file_type, size_max)) - - def task_results(self, source, condition): - unique_id, preview_full_size, preview_small = self.task_results_conn.recv() - self.results_callback(unique_id, preview_full_size, preview_small) - return True - -class SubfolderFileManager(SingleInstanceTaskManager): - """ - Manages the daemon process that renames files and creates subfolders - """ - def __init__(self, results_callback, sequence_values): - SingleInstanceTaskManager.__init__(self, results_callback) - self._subfolder_file = subfolderfile.SubfolderFile(self.task_process_conn, - sequence_values) - self._subfolder_file.start() - logger.debug("SubfolderFile PID: %s", self._subfolder_file.pid) - - def rename_file_and_move_to_subfolder(self, download_succeeded, - download_count, rpd_file): - - logger.debug("Sending file for rename: %s.", download_count) - self.task_results_conn.send((download_succeeded, download_count, - rpd_file)) - - - def task_results(self, source, condition): - move_succeeded, rpd_file, download_count = self.task_results_conn.recv() - self.results_callback(move_succeeded, rpd_file, download_count) - return True - -class ResizblePilImage(gtk.DrawingArea): - def __init__(self, bg_color=None): - gtk.DrawingArea.__init__(self) - self.base_image = None - self.bg_color = bg_color - self.connect('expose_event', self.expose) - - def set_image(self, image): - self.base_image = image - - #set up sizes and ratio used for drawing the derived image - self.base_image_w = self.base_image.size[0] - self.base_image_h = self.base_image.size[1] - self.base_image_aspect = float(self.base_image_w) / self.base_image_h - - self.queue_draw() - - def expose(self, widget, event): - - cairo_context = self.window.cairo_create() - - x = event.area.x - y = event.area.y - w = event.area.width - h = event.area.height - - #constrain operations to event area - cairo_context.rectangle(x, y, w, h) - cairo_context.clip_preserve() - - #set background color, if needed - if self.bg_color: - cairo_context.set_source_rgb(*self.bg_color) - cairo_context.fill_preserve() - - if not self.base_image: - return False - - frame_aspect = float(w) / h - - if frame_aspect > self.base_image_aspect: - # Frame is wider than image - height = h - width = int(height * self.base_image_aspect) - else: - # Frame is taller than image - width = w - height = int(width / self.base_image_aspect) - - #resize image - pil_image = self.base_image.copy() - if self.base_image_w < width or self.base_image_h < height: - logger.debug("Upsizing image") - pil_image = tn.upsize_pil(pil_image, (width, height)) - else: - logger.debug("Downsizing image") - tn.downsize_pil(pil_image, (width, height)) - - #image width and height - image_w = pil_image.size[0] - image_h = pil_image.size[1] - - #center the image horizontally and vertically - #top left and right corners for the image: - image_x = x + ((w - image_w) / 2) - image_y = y + ((h - image_h) / 2) - - image = create_cairo_image_surface(pil_image, image_w, image_h) - cairo_context.set_source_surface(image, image_x, image_y) - cairo_context.paint() - - return False - - - -class PreviewImage: - - def __init__(self, parent_app, builder): - #set background color to equivalent of '#444444 - self.preview_image = ResizblePilImage(bg_color=(0.267, 0.267, 0.267)) - self.preview_image_eventbox = builder.get_object("preview_eventbox") - self.preview_image_eventbox.add(self.preview_image) - self.preview_image.show() - self.download_this_checkbutton = builder.get_object("download_this_checkbutton") - self.rapid_app = parent_app - - self.base_preview_image = None # large size image used to scale down from - self.current_preview_size = (0,0) - self.preview_image_size_limit = (0,0) - - self.unique_id = None - - def set_preview_image(self, unique_id, pil_image, include_checkbutton_visible=None, - checked=None): - """ - """ - self.preview_image.set_image(pil_image) - self.unique_id = unique_id - if checked is not None: - self.download_this_checkbutton.set_active(checked) - self.download_this_checkbutton.grab_focus() - - if include_checkbutton_visible is not None: - self.download_this_checkbutton.props.visible = include_checkbutton_visible - - def update_preview_image(self, unique_id, pil_image): - if unique_id == self.unique_id: - self.set_preview_image(unique_id, pil_image) - - -class RapidApp(dbus.service.Object): - """ - The main Rapid Photo Downloader application class. - - Contains functionality for main program window, and directs all other - processes. - """ - - def __init__(self, bus, path, name, taskserver=None, focal_length=None, - auto_detect=None, device_location=None): - - dbus.service.Object.__init__ (self, bus, path, name) - self.running = False - - self.taskserver = taskserver - - self.focal_length = focal_length - - # Setup program preferences, and set callback for when they change - self._init_prefs(auto_detect, device_location) - - # Initialize widgets in the main window, and variables that point to them - self._init_widgets() - - if use_pynotify: - self._init_pynotify() - - # Initialize job code handling - self._init_job_code() - - # Remember the window size from the last time the program was run, or - # set a default size - self._set_window_size() - - # Setup various widgets - self._setup_buttons() - self._setup_error_icons() - self._setup_icons() - - # Show the main window - self.rapidapp.show() - - # Check program preferences - don't allow auto start if there is a problem - prefs_valid, msg = prefsrapid.check_prefs_for_validity(self.prefs) - if not prefs_valid: - self.notify_prefs_are_invalid(details=msg) - - # Initialize variables with which to track important downloads results - self._init_download_tracking() - - # Set up process managers. - # A task such as scanning a device or copying files is handled in its - # own process. - self._start_process_managers() - - # Setup devices from which to download from and backup to - self.setup_devices(on_startup=True, on_preference_change=False, - block_auto_start=not prefs_valid) - - # Ensure the device collection scrolled window is not too small - self._set_device_collection_size() - - def on_rapidapp_destroy(self, widget, data=None): - - self._terminate_processes(terminate_file_copies = True) - - # save window and component sizes - self.prefs.vpaned_pos = self.main_vpaned.get_position() - - x, y, width, height = self.rapidapp.get_allocation() - self.prefs.main_window_size_x = width - self.prefs.main_window_size_y = height - - self.prefs.set_downloads_today_from_tracker(self.downloads_today_tracker) - - gtk.main_quit() - - def _terminate_processes(self, terminate_file_copies=False): - - if terminate_file_copies: - logger.info("Terminating all processes...") - - scan_termination_requested = self.scan_manager.request_termination() - thumbnails_termination_requested = self.thumbnails.thumbnail_manager.request_termination() - backup_termination_requested = self.backup_manager.request_termination() - file_modify_termination_requested = self.file_modify_manager.request_termination() - - if terminate_file_copies: - copy_files_termination_requested = self.copy_files_manager.request_termination() - else: - copy_files_termination_requested = False - - if (scan_termination_requested or thumbnails_termination_requested or - backup_termination_requested or file_modify_termination_requested): - time.sleep(1) - if (self.scan_manager.get_no_active_processes() > 0 or - self.thumbnails.thumbnail_manager.get_no_active_processes() > 0 or - self.backup_manager.get_no_active_processes() > 0 or - self.file_modify_manager.get_no_active_processes() > 0): - time.sleep(1) - # must try again, just in case a new scan has meanwhile started! - self.scan_manager.request_termination() - self.thumbnails.thumbnail_manager.terminate_forcefully() - self.scan_manager.terminate_forcefully() - self.backup_manager.terminate_forcefully() - self.file_modify_manager.terminate_forcefully() - - if terminate_file_copies and copy_files_termination_requested: - time.sleep(1) - self.copy_files_manager.terminate_forcefully() - - if terminate_file_copies: - self._clean_all_temp_dirs() - - # # # - # Events and tasks related to displaying preview images and thumbnails - # # # - - def on_download_this_checkbutton_toggled(self, checkbutton): - value = checkbutton.get_active() - self.thumbnails.set_selected(self.preview_image.unique_id, value) - self.set_download_action_sensitivity() - - def on_preview_eventbox_button_press_event(self, widget, event): - - if event.type == gtk.gdk._2BUTTON_PRESS and event.button == 1: - self.show_thumbnails() - - def on_show_thumbnails_action_activate(self, action): - logger.debug("on_show_thumbnails_action_activate") - self.show_thumbnails() - - def on_show_image_action_activate(self, action): - logger.debug("on_show_image_action_activate") - self.thumbnails.show_preview() - - def on_check_all_action_activate(self, action): - self.thumbnails.check_all(check_all=True) - - def on_uncheck_all_action_activate(self, action): - self.thumbnails.check_all(check_all=False) - - def on_check_all_photos_action_activate(self, action): - self.thumbnails.check_all(check_all=True, - file_type=rpdfile.FILE_TYPE_PHOTO) - - def on_check_all_videos_action_activate(self, action): - self.thumbnails.check_all(check_all=True, - file_type=rpdfile.FILE_TYPE_VIDEO) - - def on_quit_action_activate(self, action): - self.on_rapidapp_destroy(widget=self.rapidapp, data=None) - - def on_refresh_action_activate(self, action): - self.thumbnails.clear_all() - self.setup_devices(on_startup=False, on_preference_change=False, - block_auto_start=True) - - def on_get_help_action_activate(self, action): - webbrowser.open("http://www.damonlynch.net/rapid/help.html") - - def on_about_action_activate(self, action): - self.about.set_property("name", PROGRAM_NAME) - self.about.set_property("version", utilities.human_readable_version( - __version__)) - self.about.run() - self.about.hide() - - def on_report_problem_action_activate(self, action): - webbrowser.open("https://bugs.launchpad.net/rapid") - - def on_translate_action_activate(self, action): - webbrowser.open("http://www.damonlynch.net/rapid/translate.html") - - def on_donate_action_activate(self, action): - webbrowser.open("http://www.damonlynch.net/rapid/donate.html") - - def show_preview_image(self, unique_id, image, include_checkbutton_visible, checked): - if self.main_notebook.get_current_page() == 0: # thumbnails - logger.debug("Switching to preview image display") - self.main_notebook.set_current_page(1) - self.preview_image.set_preview_image(unique_id, image, include_checkbutton_visible, checked) - self.next_image_action.set_sensitive(True) - self.prev_image_action.set_sensitive(True) - - def update_preview_image(self, unique_id, image): - self.preview_image.update_preview_image(unique_id, image) - - def show_thumbnails(self): - logger.debug("Switching to thumbnails display") - self.main_notebook.set_current_page(0) - self.thumbnails.select_image(self.preview_image.unique_id) - self.next_image_action.set_sensitive(False) - self.prev_image_action.set_sensitive(False) - - - def on_next_image_action_activate(self, action): - if self.preview_image.unique_id is not None: - self.thumbnails.show_next_image(self.preview_image.unique_id) - - def on_prev_image_action_activate(self, action): - if self.preview_image.unique_id is not None: - self.thumbnails.show_prev_image(self.preview_image.unique_id) - - def display_scan_thumbnails(self): - """ - If all the scans are complete, sets the sort order and displays - thumbnails in the icon view - """ - if self.scan_manager.no_tasks == 0: - self.thumbnails.sort_by_timestamp() - self.thumbnails.display_thumbnails() - - - # # # - # Volume management - # # # - - def start_volume_monitor(self): - if not self.vmonitor: - self.vmonitor = gio.volume_monitor_get() - self.vmonitor.connect("mount-added", self.on_mount_added) - self.vmonitor.connect("mount-removed", self.on_mount_removed) - - - def _backup_device_name(self, path): - if self.backup_devices[path][0] is None: - name = path - else: - name = self.backup_devices[path][0].get_name() - return name - - def start_device_scan(self, device): - """ - Commences the scanning of a device using the preference values for - any paths to ignore while scanning - """ - logger.debug("Starting a device scan for device %s", device.get_name()) - return self.scan_manager.add_task([device, - self.prefs.ignored_paths, - self.prefs.use_re_ignored_paths]) - - def confirm_manual_location(self): - """ - Queries the user to ask if they really want to download from locations - that could take a very long time to scan. They can choose yes or no. - - Returns True if yes or there was no need to ask the user, False if the - user said no. - """ - l = self.prefs.device_location - if l in ['/media', '/run', os.path.expanduser('~'), '/']: - logger.info("Prompting whether to download from %s", l) - if l == '/': - #this location is a human readable explanation for /, and is inserted into Downloading from %(location)s - l = _('the root of the file system') - c = preferencesdialog.QuestionDialog(parent_window=self.rapidapp, - title=_('Rapid Photo Downloader'), - #message in dialog box which asks the user if they really want to be downloading from this location - question="<b>" + _("Downloading from %(location)s.") % {'location': l} + "</b>\n\n" + - _("Do you really want to download from here? On some systems, scanning this location can take a very long time."), - default_to_yes=False, - use_markup=True) - response = c.run() - user_confirmed = response == gtk.RESPONSE_OK - c.destroy() - if not user_confirmed: - return False - return True - - def setup_devices(self, on_startup, on_preference_change, block_auto_start): - """ - - Setup devices from which to download from and backup to - - Sets up volumes for downloading from and backing up to - - on_startup should be True if the program is still starting, - i.e. this is being called from the program's initialization. - - on_preference_change should be True if this is being called as the - result of a preference being changed - - block_auto_start should be True if automation options to automatically - start a download should be ignored - - Removes any image media that are currently not downloaded, - or finished downloading - """ - - if self.using_volume_monitor(): - self.start_volume_monitor() - - self.clear_non_running_downloads() - if not self.prefs.device_autodetection: - if not self.confirm_manual_location(): - return - - mounts = [] - self.backup_devices = {} - - if self.using_volume_monitor(): - # either using automatically detected backup devices - # or download devices - for mount in self.vmonitor.get_mounts(): - if not mount.is_shadowed(): - path = mount.get_root().get_path() - if path: - if (path in self.prefs.device_blacklist and - self.search_for_PSD()): - logger.info("blacklisted device %s ignored", mount.get_name()) - else: - logger.info("Detected %s", mount.get_name()) - is_backup_mount, backup_file_type = self.check_if_backup_mount(path) - if is_backup_mount: - self.backup_devices[path] = (mount, backup_file_type) - elif (self.prefs.device_autodetection and - (dv.is_DCIM_device(path) or - self.search_for_PSD())): - logger.debug("Appending %s", mount.get_name()) - mounts.append((path, mount)) - else: - logger.debug("Ignoring %s", mount.get_name()) - - - if not self.prefs.device_autodetection: - # user manually specified the path from which to download - path = self.prefs.device_location - if path: - logger.info("Using manually specified path %s", path) - if utilities.is_directory(path): - mounts.append((path, None)) - else: - logger.error("Download path does not exist: %s", path) - - if self.prefs.backup_images: - if not self.prefs.backup_device_autodetection: - self._setup_manual_backup() - self._add_backup_devices() - - self.update_no_backup_devices() - - # Display amount of free space in a status bar message - self.display_free_space() - - if block_auto_start: - self.auto_start_is_on = False - else: - self.auto_start_is_on = ((not on_preference_change) and - ((self.prefs.auto_download_at_startup and - on_startup) or - (self.prefs.auto_download_upon_device_insertion and - not on_startup))) - - logger.debug("Working with %s devices", len(mounts)) - for m in mounts: - path, mount = m - device = dv.Device(path=path, mount=mount) - - - if not self._device_already_detected(device): - if (self.search_for_PSD() and - path not in self.prefs.device_whitelist): - # prompt user to see if device should be used or not - self.get_use_device(device) - else: - scan_pid = self.start_device_scan(device) - if mount is not None: - self.mounts_by_path[path] = scan_pid - if not mounts: - self.set_download_action_sensitivity() - - def _device_already_detected(self, device): - path = device.get_path() - if path in self.mounts_by_path: - logger.debug("Ignoring device %s as already have path %s", device.get_name(), path) - return True - else: - return False - - def _setup_manual_backup(self): - """ - Setup backup devices that the user has manually specified. - Depending on the folder the user has chosen, the paths for photo and - video backup will either be the same or they will differ. - """ - # user manually specified backup locations - # will backup to these paths, but don't need any volume info - # associated with them - self.backup_devices[self.prefs.backup_location] = (None, PHOTO_BACKUP) - if DOWNLOAD_VIDEO: - if self.prefs.backup_location <> self.prefs.backup_video_location: - self.backup_devices[self.prefs.backup_video_location] = (None, VIDEO_BACKUP) - logger.info("Backing up photos to %s", self.prefs.backup_location) - logger.info("Backing up videos to %s", self.prefs.backup_video_location) - else: - # videos and photos are being backed up to the same location - self.backup_devices[self.prefs.backup_location] = (None, PHOTO_VIDEO_BACKUP) - logger.info("Backing up photos and videos to %s", self.prefs.backup_location) - else: - logger.info("Backing up photos to %s", self.prefs.backup_location) - - def _add_backup_devices(self): - """ - Add each backup devices / path to backup manager - """ - for path in self.backup_devices: - name = self._backup_device_name(path) - backup_type = self.backup_devices[path][1] - self.backup_manager.add_device(path, name, backup_type) - - - def get_use_device(self, device): - """ Prompt user whether or not to download from this device """ - - logger.info("Prompting whether to use %s", device.get_name()) - - # On some systems, e.g. Ubuntu 12.10, the GTK/Gnome environment - # unexpectedly results in a device being added twice and not once. - # The hack on the next line ensures the user is not prompted twice - # for the same device. - - self.mounts_by_path[device.get_path()] = "PROMPTING" - - d = dv.UseDeviceDialog(self.rapidapp, device, self.got_use_device) - - def got_use_device(self, dialog, user_selected, permanent_choice, device): - """ User has chosen whether or not to use a device to download from """ - dialog.destroy() - - path = device.get_path() - - if user_selected: - if permanent_choice and path not in self.prefs.device_whitelist: - # do NOT do a list append operation here without the assignment, - # or the actual preferences will not be updated!! - if len(self.prefs.device_whitelist): - self.prefs.device_whitelist = self.prefs.device_whitelist + [path] - else: - self.prefs.device_whitelist = [path] - scan_pid = self.start_device_scan(device) - self.mounts_by_path[path] = scan_pid - - elif permanent_choice and path not in self.prefs.device_blacklist: - # do not do a list append operation here without the assignment, or the preferences will not be updated! - if len(self.prefs.device_blacklist): - self.prefs.device_blacklist = self.prefs.device_blacklist + [path] - else: - self.prefs.device_blacklist = [path] - - def search_for_PSD(self): - """ - Check to see if user preferences are to automatically search for - Portable Storage Devices or not - """ - return self.prefs.device_autodetection_psd and self.prefs.device_autodetection - - def check_if_backup_mount(self, path): - """ - Checks to see if backups are enabled and path represents a valid backup - location. It must be writeable. - - Checks against user preferences. - - Returns a tuple: - (True, <backup-type> (one of PHOTO_VIDEO_BACKUP, PHOTO_BACKUP, or VIDEO_BACKUP)) or - (False, None) - """ - if self.prefs.backup_images: - if self.prefs.backup_device_autodetection: - # Determine if the auto-detected backup device is - # to be used to backup only photos, or videos, or both. - # Use the presence of a corresponding directory to - # determine this. - # The directory must be writable. - photo_path = os.path.join(path, self.prefs.backup_identifier) - p_backup = os.path.isdir(photo_path) and os.access(photo_path, os.W_OK) - if DOWNLOAD_VIDEO: - video_path = os.path.join(path, self.prefs.video_backup_identifier) - v_backup = os.path.isdir(video_path) and os.access(video_path, os.W_OK) - else: - v_backup = False - if p_backup and v_backup: - logger.info("Photos and videos will be backed up to %s", path) - return (True, PHOTO_VIDEO_BACKUP) - elif p_backup: - logger.info("Photos will be backed up to %s", path) - return (True, PHOTO_BACKUP) - elif v_backup: - logger.info("Videos will be backed up to %s", path) - return (True, VIDEO_BACKUP) - elif path == self.prefs.backup_location: - # user manually specified the path - if os.access(self.prefs.backup_location, os.W_OK): - return (True, PHOTO_BACKUP) - elif path == self.prefs.backup_video_location: - # user manually specified the path - if os.access(self.prefs.backup_video_location, os.W_OK): - return (True, VIDEO_BACKUP) - return (False, None) - - def update_no_backup_devices(self): - self.no_photo_backup_devices = 0 - self.no_video_backup_devices = 0 - for path, value in self.backup_devices.iteritems(): - backup_type = value[1] - if backup_type == PHOTO_BACKUP: - self.no_photo_backup_devices += 1 - elif backup_type == VIDEO_BACKUP: - self.no_video_backup_devices += 1 - else: - #both videos and photos are backed up to this device / path - self.no_photo_backup_devices += 1 - self.no_video_backup_devices += 1 - logger.info("# photo backup devices: %s; # video backup devices: %s", - self.no_photo_backup_devices, self.no_video_backup_devices) - self.download_tracker.set_no_backup_devices(self.no_photo_backup_devices, - self.no_video_backup_devices) - - def refresh_backup_media(self): - """ - Setup the backup media - - Assumptions: this is being called after the user has changed their - preferences AND download media has already been setup - """ - - # terminate any running backup processes - self.backup_manager.request_termination() - - self.backup_devices = {} - if self.prefs.backup_images: - if not self.prefs.backup_device_autodetection: - self._setup_manual_backup() - else: - for mount in self.vmonitor.get_mounts(): - if not mount.is_shadowed(): - path = mount.get_root().get_path() - if path: - is_backup_mount, backup_file_type = self.check_if_backup_mount(path) - if is_backup_mount: - # is a backup volume - if path not in self.backup_devices: - self.backup_devices[path] = (mount, backup_file_type) - - self._add_backup_devices() - - self.update_no_backup_devices() - self.display_free_space() - - def using_volume_monitor(self): - """ - Returns True if programs needs to use gio volume monitor - """ - - return (self.prefs.device_autodetection or - (self.prefs.backup_images and - self.prefs.backup_device_autodetection - )) - - def on_mount_added(self, vmonitor, mount): - """ - callback run when gio indicates a new volume - has been mounted - """ - - - if mount.is_shadowed(): - # ignore this type of mount - return - - path = mount.get_root().get_path() - if path is not None: - - if path in self.prefs.device_blacklist and self.search_for_PSD(): - logger.info("Device %(device)s (%(path)s) ignored" % { - 'device': mount.get_name(), 'path': path}) - else: - is_backup_mount, backup_file_type = self.check_if_backup_mount(path) - - if is_backup_mount: - if path not in self.backup_devices: - self.backup_devices[path] = mount - name = self._backup_device_name(path) - self.backup_manager.add_device(path, name, backup_file_type) - self.update_no_backup_devices() - self.display_free_space() - - elif self.prefs.device_autodetection and (dv.is_DCIM_device(path) or - self.search_for_PSD()): - - self.auto_start_is_on = self.prefs.auto_download_upon_device_insertion - device = dv.Device(path=path, mount=mount) - - if not self._device_already_detected(device): - if self.search_for_PSD() and path not in self.prefs.device_whitelist: - # prompt user if device should be used or not - self.get_use_device(device) - else: - scan_pid = self.start_device_scan(device) - self.mounts_by_path[path] = scan_pid - - def on_mount_removed(self, vmonitor, mount): - """ - callback run when gio indicates a new volume - has been mounted - """ - - path = mount.get_root().get_path() - - # three scenarios - - # the mount has been scanned but downloading has not yet started - # files are being downloaded from mount (it must be a messy unmount) - # files have finished downloading from mount - - if path in self.mounts_by_path: - scan_pid = self.mounts_by_path[path] - del self.mounts_by_path[path] - # temp directory should be cleaned by finishing of process - - self.thumbnails.clear_all(scan_pid = scan_pid, - keep_downloaded_files = True) - self.device_collection.remove_device(scan_pid) - - - - # remove backup volumes - elif path in self.backup_devices: - del self.backup_devices[path] - self.display_free_space() - self.backup_manager.remove_device(path) - self.update_no_backup_devices() - - # may need to disable download button and menu - self.set_download_action_sensitivity() - - def clear_non_running_downloads(self): - """ - Clears the display of downloads that are currently not running - """ - - # Stop any processes currently scanning or creating thumbnails - self._terminate_processes(terminate_file_copies=False) - - # Remove them from the user interface - for scan_pid in self.device_collection.get_all_displayed_processes(): - if scan_pid not in self.download_active_by_scan_pid: - self.device_collection.remove_device(scan_pid) - self.thumbnails.clear_all(scan_pid=scan_pid) - - - - - # # # - # Download and help buttons, and menu items - # # # - - def on_download_action_activate(self, action): - """ - Called when a download is activated - """ - - if self.copy_files_manager.paused: - logger.debug("Download resumed") - self.resume_download() - else: - logger.debug("Download activated") - - if self.download_action_is_download: - if self.need_job_code_for_naming and not self.prompting_for_job_code: - self.get_job_code() - else: - self.start_download() - else: - self.pause_download() - - - def on_help_action_activate(self, action): - webbrowser.open("http://www.damonlynch.net/rapid/documentation") - - def on_preferences_action_activate(self, action): - - preferencesdialog.PreferencesDialog(self) - - def set_download_action_sensitivity(self): - """ - Sets sensitivity of Download action to enable or disable it - - Affects download button and menu item - """ - if not self.download_is_occurring(): - sensitivity = False - if self.scan_manager.no_tasks == 0: - if self.thumbnails.files_are_checked_to_download(): - sensitivity = True - - self.download_action.set_sensitive(sensitivity) - - def set_download_action_label(self, is_download): - """ - Toggles label betwen pause and download - """ - - if is_download: - self.download_action.set_label(_("Download")) - self.download_action_is_download = True - else: - self.download_action.set_label(_("Pause")) - self.download_action_is_download = False - - # # # - # Job codes - # # # - - - def _init_job_code(self): - self.job_code = self.last_chosen_job_code = '' - self.need_job_code_for_naming = self.prefs.any_pref_uses_job_code() - self.prompting_for_job_code = False - - def assign_job_code(self, code): - """ assign job code (which may be empty) to member variable and update user preferences - - Update preferences only if code is not empty. Do not duplicate job code. - """ - - self.job_code = code - - if code: - #add this value to job codes preferences - #delete any existing value which is the same - #(this way it comes to the front, which is where it should be) - #never modify self.prefs.job_codes in place! (or prefs become screwed up) - - jcs = self.prefs.job_codes - while code in jcs: - jcs.remove(code) - - self.prefs.job_codes = [code] + jcs - - def _get_job_code(self, post_job_code_entry_callback): - """ prompt for a job code """ - - if not self.prompting_for_job_code: - logger.debug("Prompting for Job Code") - self.prompting_for_job_code = True - j = preferencesdialog.JobCodeDialog(parent_window = self.rapidapp, - job_codes = self.prefs.job_codes, - default_job_code = self.last_chosen_job_code, - post_job_code_entry_callback=post_job_code_entry_callback, - entry_only = False) - else: - logger.debug("Already prompting for Job Code, do not prompt again") - - def get_job_code(self): - self._get_job_code(self.got_job_code) - - def got_job_code(self, dialog, user_chose_code, code): - dialog.destroy() - self.prompting_for_job_code = False - - if user_chose_code: - if code is None: - code = '' - self.assign_job_code(code) - self.last_chosen_job_code = code - logger.debug("Job Code %s entered", self.job_code) - self.start_download() - - else: - # user cancelled - logger.debug("No Job Code entered") - self.job_code = '' - self.auto_start_is_on = False - - - # # # - # Download - # # # - - def _init_download_tracking(self): - """ - Initialize variables to track downloads - """ - # Track download sizes and other values for each device. - # (Scan id acts as an index to each device. A device could be scanned - # more than once). - self.download_tracker = downloadtracker.DownloadTracker() - - # Track which temporary directories are created when downloading files - self.temp_dirs_by_scan_pid = dict() - - # Track which downloads are running - self.download_active_by_scan_pid = [] - - def modify_files_during_download(self): - """ Returns True if there is a need to modify or verify files during download""" - return self.prefs.auto_rotate_jpeg or (self.focal_length is not None) or self.prefs.verify_file - - - def start_download(self, scan_pid=None): - """ - Start download, renaming and backup of files. - - If scan_pid is specified, only files matching it will be downloaded - """ - - files_by_scan_pid = self.thumbnails.get_files_checked_for_download(scan_pid) - self.check_file_types_to_be_downloaded(files_by_scan_pid) - folders_valid, invalid_dirs = self.check_download_folder_validity(files_by_scan_pid) - - if not folders_valid: - if len(invalid_dirs) > 1: - msg = _("These download folders are invalid:\n%(folder1)s\n%(folder2)s") % { - 'folder1': invalid_dirs[0], 'folder2': invalid_dirs[1]} - else: - msg = _("This download folder is invalid:\n%s") % invalid_dirs[0] - self.log_error(config.CRITICAL_ERROR, _("Download cannot proceed"), - msg) - else: - missing_destinations = self.backup_destinations_missing() - if missing_destinations is not None: - # Warn user that they have specified that they want to backup a file type, but no such folder exists on backup devices - if not missing_destinations[0]: - logger.warning("No backup device contains a valid folder for backing up photos") - msg = _("No backup device contains a valid folder for backing up %(filetype)s") % {'filetype': _('photos')} - else: - logger.warning("No backup device contains a valid folder for backing up videos") - msg = _("No backup device contains a valid folder for backing up %(filetype)s") % {'filetype': _('videos')} - - self.log_error(config.WARNING, _("Backup problem"), msg) - - # set time download is starting if it is not already set - # it is unset when all downloads are completed - if self.download_start_time is None: - self.download_start_time = datetime.datetime.now() - - # Set status to download pending - self.thumbnails.mark_download_pending(files_by_scan_pid) - - # disable refresh and preferences change while download is occurring - self.enable_prefs_and_refresh(enabled=False) - - for scan_pid in files_by_scan_pid: - files = files_by_scan_pid[scan_pid] - # if generating thumbnails for this scan_pid, stop it - if self.thumbnails.terminate_thumbnail_generation(scan_pid): - self.thumbnails.mark_thumbnails_needed(files) - - self.download_files(files, scan_pid) - - self.set_download_action_label(is_download = False) - - def pause_download(self): - - self.copy_files_manager.pause() - - # set action to display Download - if not self.download_action_is_download: - self.set_download_action_label(is_download = True) - - self.time_check.pause() - - def resume_download(self): - for scan_pid in self.download_active_by_scan_pid: - self.time_remaining.set_time_mark(scan_pid) - - self.time_check.set_download_mark() - - self.copy_files_manager.start() - - def download_files(self, files, scan_pid): - """ - Initiate downloading and renaming of files - """ - # Check which file types will be downloaded for this particular process - no_photos_to_download = self.files_of_type_present(files, - rpdfile.FILE_TYPE_PHOTO, - return_file_count=True) - if no_photos_to_download: - photo_download_folder = self.prefs.download_folder - else: - photo_download_folder = None - - if DOWNLOAD_VIDEO: - no_videos_to_download = self.files_of_type_present(files, - rpdfile.FILE_TYPE_VIDEO, - return_file_count=True) - if no_videos_to_download: - video_download_folder = self.prefs.video_download_folder - else: - video_download_folder = None - else: - video_download_folder = None - no_videos_to_download = 0 - - photo_download_size, video_download_size = self.size_files_to_be_downloaded(files) - self.download_tracker.init_stats(scan_pid=scan_pid, - photo_size_in_bytes=photo_download_size, - video_size_in_bytes=video_download_size, - no_photos_to_download=no_photos_to_download, - no_videos_to_download=no_videos_to_download) - - - download_size = photo_download_size + video_download_size - - if self.prefs.backup_images: - download_size = download_size + ((self.no_photo_backup_devices * photo_download_size) + - (self.no_video_backup_devices * video_download_size)) - - self.time_remaining.set(scan_pid, download_size) - self.time_check.set_download_mark() - - self.download_active_by_scan_pid.append(scan_pid) - - - if len(self.download_active_by_scan_pid) > 1: - self.display_summary_notification = True - - if self.auto_start_is_on and self.prefs.generate_thumbnails: - for rpd_file in files: - rpd_file.generate_thumbnail = True - - verify_file = self.prefs.verify_file - if verify_file: - # since a file might be modified in the file modify process, - # if it will be backed up, need to refresh the md5 once it has - # been modified - refresh_md5_on_file_change = self.prefs.backup_images - else: - refresh_md5_on_file_change = False - modify_files_during_download = self.modify_files_during_download() - if modify_files_during_download: - self.file_modify_manager.add_task((scan_pid, self.prefs.auto_rotate_jpeg, self.focal_length, verify_file, refresh_md5_on_file_change)) - modify_pipe = self.file_modify_manager.get_modify_pipe(scan_pid) - else: - modify_pipe = None - - - # Initiate copy files process - self.copy_files_manager.add_task((photo_download_folder, - video_download_folder, scan_pid, - files, verify_file, - modify_files_during_download, - modify_pipe)) - - def copy_files_results(self, source, condition): - """ - Handle results from copy files process - """ - #FIXME: must handle early termination / pause of copy files process - connection = self.copy_files_manager.get_pipe(source) - conn_type, msg_data = connection.recv() - if conn_type == rpdmp.CONN_PARTIAL: - msg_type, data = msg_data - - if msg_type == rpdmp.MSG_TEMP_DIRS: - scan_pid, photo_temp_dir, video_temp_dir = data - self.temp_dirs_by_scan_pid[scan_pid] = (photo_temp_dir, video_temp_dir) - - # Report which temporary directories are being used for this - # download - if photo_temp_dir and video_temp_dir: - logger.debug("Using temp dirs %s (photos) & %s (videos)", - photo_temp_dir, video_temp_dir) - elif photo_temp_dir: - logger.debug("Using temp dir %s (photos)", - photo_temp_dir) - else: - logger.debug("Using temp dir %s (videos)", - video_temp_dir) - elif msg_type == rpdmp.MSG_BYTES: - scan_pid, total_downloaded, chunk_downloaded = data - self.download_tracker.set_total_bytes_copied(scan_pid, - total_downloaded) - self.time_check.increment(bytes_downloaded=chunk_downloaded) - percent_complete = self.download_tracker.get_percent_complete(scan_pid) - self.device_collection.update_progress(scan_pid, percent_complete, - None, None) - self.time_remaining.update(scan_pid, bytes_downloaded=chunk_downloaded) - elif msg_type == rpdmp.MSG_FILE: - self.copy_file_results_single_file(data) - - return True - else: - # Process is complete, i.e. conn_type == rpdmp.CONN_COMPLETE - connection.close() - return False - - - def copy_file_results_single_file(self, data): - """ - Handles results from one of two processes: - 1. copy_files - 2. file_modify - - Operates after a single file has been copied from the download device - to the local folder. - - Calls the process to rename files and create subfolders (subfolderfile) - """ - - download_succeeded, rpd_file, download_count, temp_full_file_name, thumbnail_icon, thumbnail = data - - if thumbnail is not None or thumbnail_icon is not None: - self.thumbnails.update_thumbnail((rpd_file.unique_id, - thumbnail_icon, - thumbnail)) - - self.download_tracker.set_download_count_for_file( - rpd_file.unique_id, download_count) - self.download_tracker.set_download_count( - rpd_file.scan_pid, download_count) - rpd_file.download_start_time = self.download_start_time - - if download_succeeded: - # Insert preference values needed for name generation - rpd_file = prefsrapid.insert_pref_lists(self.prefs, rpd_file) - rpd_file.strip_characters = self.prefs.strip_characters - rpd_file.download_folder = self.prefs.get_download_folder_for_file_type(rpd_file.file_type) - rpd_file.download_conflict_resolution = self.prefs.download_conflict_resolution - rpd_file.synchronize_raw_jpg = self.prefs.must_synchronize_raw_jpg() - rpd_file.job_code = self.job_code - - # Call this even if download did not succeed e.g. file verification error - self.subfolder_file_manager.rename_file_and_move_to_subfolder( - download_succeeded, - download_count, - rpd_file - ) - - def file_modify_results(self, source, condition): - """ - 'file modify' is a process that runs immediately after 'copy files', - meaning there can be more than one at one time. - - It runs before the renaming process. - """ - connection = self.file_modify_manager.get_pipe(source) - - conn_type, data = connection.recv() - if conn_type == rpdmp.CONN_PARTIAL: - self.copy_file_results_single_file(data) - return True - else: - # Process is complete, i.e. conn_type == rpdmp.CONN_COMPLETE - connection.close() - return False - - - def download_is_occurring(self): - """Returns True if a file is currently being downloaded, renamed or - backed up - """ - return not len(self.download_active_by_scan_pid) == 0 - - # # # - # Create folder and file names for downloaded files - # # # - - def subfolder_file_results(self, move_succeeded, rpd_file, download_count): - """ - Handle results of subfolder creation and file renaming - """ - - scan_pid = rpd_file.scan_pid - unique_id = rpd_file.unique_id - - if rpd_file.status == config.STATUS_DOWNLOADED_WITH_WARNING: - self.log_error(config.WARNING, rpd_file.error_title, - rpd_file.error_msg, rpd_file.error_extra_detail) - - if self.prefs.backup_images: - if self.backup_possible(rpd_file.file_type): - if self.prefs.backup_device_autodetection: - if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO: - path_suffix = self.prefs.backup_identifier - else: - path_suffix = self.prefs.video_backup_identifier - else: - path_suffix = None - - self.backup_manager.backup_file(move_succeeded, rpd_file, - path_suffix, - self.prefs.backup_duplicate_overwrite, - self.prefs.verify_file, - download_count) - else: - if rpd_file.status == config.STATUS_DOWNLOAD_FAILED: - rpd_file.status = config.STATUS_DOWNLOAD_AND_BACKUP_FAILED - else: - rpd_file.status = config.STATUS_BACKUP_PROBLEM - - self.file_download_finished(move_succeeded, rpd_file) - else: - self.file_download_finished(move_succeeded, rpd_file) - - def backup_possible(self, file_type): - if file_type == rpdfile.FILE_TYPE_PHOTO: - return self.no_photo_backup_devices > 0 - elif file_type == rpdfile.FILE_TYPE_VIDEO: - return self.no_video_backup_devices > 0 - else: - logger.critical("Unrecognized file type when determining if backup is possible") - - - def multiple_backup_devices(self, file_type): - """Returns true if more than one backup device is being used for that - file type - """ - return ((file_type == rpdfile.FILE_TYPE_PHOTO and - self.no_photo_backup_devices > 1) or - (file_type == rpdfile.FILE_TYPE_VIDEO and - self.no_video_backup_devices > 1)) - - def backup_results(self, source, condition): - """ - Handle results sent from backup processes - """ - connection = self.backup_manager.get_pipe(source) - conn_type, msg_data = connection.recv() - if conn_type == rpdmp.CONN_PARTIAL: - msg_type, data = msg_data - - if msg_type == rpdmp.MSG_BYTES: - scan_pid, backup_pid, total_downloaded, chunk_downloaded = data - self.download_tracker.increment_bytes_backed_up(scan_pid, - chunk_downloaded) - self.time_check.increment(bytes_downloaded=chunk_downloaded) - percent_complete = self.download_tracker.get_percent_complete(scan_pid) - self.device_collection.update_progress(scan_pid, percent_complete, - None, None) - self.time_remaining.update(scan_pid, bytes_downloaded=chunk_downloaded) - - elif msg_type == rpdmp.MSG_FILE: - backup_succeeded, do_backup, rpd_file = data - - # Only show an error message if there is more than one device - # backing up files of this type - if that is the case, - # do not want to rely on showing an error message in the - # function file_download_finished, as it is only called once, - # when all files have been backed up - if not backup_succeeded and self.multiple_backup_devices(rpd_file.file_type) and do_backup: - self.log_error(config.SERIOUS_ERROR, - rpd_file.error_title, - rpd_file.error_msg, rpd_file.error_extra_detail) - - if do_backup: - self.download_tracker.file_backed_up(rpd_file.unique_id) - if self.download_tracker.all_files_backed_up(rpd_file.unique_id, - rpd_file.file_type): - logger.debug("File %s will not be backed up to any more locations", rpd_file.download_name) - self.file_download_finished(backup_succeeded or not do_backup, rpd_file) - return True - else: - return False - - - def file_download_finished(self, succeeded, rpd_file): - """ - Called when a file has been downloaded i.e. copied, renamed, and backed up - """ - scan_pid = rpd_file.scan_pid - unique_id = rpd_file.unique_id - # Update error log window if neccessary - if not succeeded and not self.multiple_backup_devices(rpd_file.file_type): - self.log_error(config.SERIOUS_ERROR, rpd_file.error_title, - rpd_file.error_msg, rpd_file.error_extra_detail) - elif self.prefs.auto_delete: - # record which files to automatically delete when download - # completes - self.download_tracker.add_to_auto_delete(rpd_file) - - self.thumbnails.update_status_post_download(rpd_file) - self.download_tracker.file_downloaded_increment(scan_pid, - rpd_file.file_type, - rpd_file.status) - - completed, files_remaining = self._update_file_download_device_progress(scan_pid, unique_id, rpd_file.file_type) - - if self.download_is_occurring(): - self.update_time_remaining() - - if completed: - # Last file for this scan pid has been downloaded, so clean temp directory - logger.debug("Purging temp directories") - self._clean_temp_dirs_for_scan_pid(scan_pid) - if self.prefs.auto_delete: - logger.debug("Auto deleting files") - self.auto_delete(scan_pid) - self.download_tracker.clear_auto_delete(scan_pid) - self.download_active_by_scan_pid.remove(scan_pid) - self.time_remaining.remove(scan_pid) - self.notify_downloaded_from_device(scan_pid) - if files_remaining == 0 and self.prefs.auto_unmount: - self.device_collection.unmount(scan_pid) - - - if not self.download_is_occurring(): - logger.debug("Download completed") - self.enable_prefs_and_refresh(enabled=True) - self.notify_download_complete() - self.download_progressbar.set_fraction(0.0) - - self.prefs.stored_sequence_no = self.stored_sequence_value.value - self.downloads_today_tracker.set_raw_downloads_today_from_int(self.downloads_today_value.value) - self.downloads_today_tracker.set_raw_downloads_today_date(self.downloads_today_date_value.value) - self.prefs.set_downloads_today_from_tracker(self.downloads_today_tracker) - - if ((self.prefs.auto_exit and self.download_tracker.no_errors_or_warnings()) - or self.prefs.auto_exit_force): - if not self.thumbnails.files_remain_to_download(): - self._terminate_processes() - gtk.main_quit() - - self.download_tracker.purge_all() - self.speed_label.set_label(" ") - - self.display_free_space() - - self.set_download_action_label(is_download=True) - self.set_download_action_sensitivity() - - self.job_code = '' - self.download_start_time = None - - - def update_time_remaining(self): - update, download_speed = self.time_check.check_for_update() - if update: - self.speed_label.set_text(download_speed) - - time_remaining = self.time_remaining.time_remaining() - if time_remaining: - secs = int(time_remaining) - - if secs == 0: - message = "" - elif secs == 1: - message = _("About 1 second remaining") - elif secs < 60: - message = _("About %i seconds remaining") % secs - elif secs == 60: - message = _("About 1 minute remaining") - else: - # Translators: in the text '%(minutes)i:%(seconds)02i', only the : should be translated, if needed. - # '%(minutes)i' and '%(seconds)02i' should not be modified or left out. They are used to format and display the amount - # of time the download has remainging, e.g. 'About 5:36 minutes remaining' - message = _("About %(minutes)i:%(seconds)02i minutes remaining") % {'minutes': secs / 60, 'seconds': secs % 60} - - self.rapid_statusbar.pop(self.statusbar_context_id) - self.rapid_statusbar.push(self.statusbar_context_id, message) - - def auto_delete(self, scan_pid): - """Delete files from download device at completion of download""" - for file in self.download_tracker.get_files_to_auto_delete(scan_pid): - f = gio.File(file) - try: - f.delete(cancellable=None) - except gio.Error, inst: - logger.error("Failure deleting file %s", file) - logger.error(inst) - - def file_types_by_number(self, no_photos, no_videos): - """ - returns a string to be displayed to the user that can be used - to show if a value refers to photos or videos or both, or just one - of each - """ - if (no_videos > 0) and (no_photos > 0): - v = _('photos and videos') - elif (no_videos == 0) and (no_photos == 0): - v = _('photos or videos') - elif no_videos > 0: - if no_videos > 1: - v = _('videos') - else: - v = _('video') - else: - if no_photos > 1: - v = _('photos') - else: - v = _('photo') - return v - - def notify_downloaded_from_device(self, scan_pid): - device = self.device_collection.get_device(scan_pid) - - if device.mount is None: - notification_name = PROGRAM_NAME - icon = self.application_icon - else: - notification_name = device.get_name() - icon = device.get_icon(self.notification_icon_size) - - no_photos_downloaded = self.download_tracker.get_no_files_downloaded( - scan_pid, rpdfile.FILE_TYPE_PHOTO) - no_videos_downloaded = self.download_tracker.get_no_files_downloaded( - scan_pid, rpdfile.FILE_TYPE_VIDEO) - no_photos_failed = self.download_tracker.get_no_files_failed( - scan_pid, rpdfile.FILE_TYPE_PHOTO) - no_videos_failed = self.download_tracker.get_no_files_failed( - scan_pid, rpdfile.FILE_TYPE_VIDEO) - no_files_downloaded = no_photos_downloaded + no_videos_downloaded - no_files_failed = no_photos_failed + no_videos_failed - no_warnings = self.download_tracker.get_no_warnings(scan_pid) - - file_types = self.file_types_by_number(no_photos_downloaded, no_videos_downloaded) - file_types_failed = self.file_types_by_number(no_photos_failed, no_videos_failed) - message = _("%(noFiles)s %(filetypes)s downloaded") % \ - {'noFiles':no_files_downloaded, 'filetypes': file_types} - - if no_files_failed: - message += "\n" + _("%(noFiles)s %(filetypes)s failed to download") % {'noFiles':no_files_failed, 'filetypes':file_types_failed} - - if no_warnings: - message = "%s\n%s " % (message, no_warnings) + _("warnings") - - if use_pynotify: - n = pynotify.Notification(notification_name, message) - n.set_icon_from_pixbuf(icon) - - try: - n.show() - except: - logger.error("Unable to display message using notification system") - - def notify_download_complete(self): - if self.display_summary_notification: - message = _("All downloads complete") - - # photo downloads - photo_downloads = self.download_tracker.total_photos_downloaded - if photo_downloads: - filetype = self.file_types_by_number(photo_downloads, 0) - message += "\n" + _("%(number)s %(numberdownloaded)s") % \ - {'number': photo_downloads, - 'numberdownloaded': _("%(filetype)s downloaded") % \ - {'filetype': filetype}} - - # photo failures - photo_failures = self.download_tracker.total_photo_failures - if photo_failures: - filetype = self.file_types_by_number(photo_failures, 0) - message += "\n" + _("%(number)s %(numberdownloaded)s") % \ - {'number': photo_failures, - 'numberdownloaded': _("%(filetype)s failed to download") % \ - {'filetype': filetype}} - - # video downloads - video_downloads = self.download_tracker.total_videos_downloaded - if video_downloads: - filetype = self.file_types_by_number(0, video_downloads) - message += "\n" + _("%(number)s %(numberdownloaded)s") % \ - {'number': video_downloads, - 'numberdownloaded': _("%(filetype)s downloaded") % \ - {'filetype': filetype}} - - # video failures - video_failures = self.download_tracker.total_video_failures - if video_failures: - filetype = self.file_types_by_number(0, video_failures) - message += "\n" + _("%(number)s %(numberdownloaded)s") % \ - {'number': video_failures, - 'numberdownloaded': _("%(filetype)s failed to download") % \ - {'filetype': filetype}} - - # warnings - warnings = self.download_tracker.total_warnings - if warnings: - message += "\n" + _("%(number)s %(numberdownloaded)s") % \ - {'number': warnings, - 'numberdownloaded': _("warnings")} - - if use_pynotify: - n = pynotify.Notification(PROGRAM_NAME, message) - n.set_icon_from_pixbuf(self.application_icon) - try: - n.show() - except: - logger.error("Unable to display message using notification system") - self.display_summary_notification = False # don't show it again unless needed - - - def _update_file_download_device_progress(self, scan_pid, unique_id, file_type): - """ - Increments the progress bar for an individual device - - Returns if the download is completed for that scan_pid - It also returns the number of files remaining for the scan_pid, BUT - this value is valid ONLY if the download is completed - """ - - files_downloaded = self.download_tracker.get_download_count_for_file(unique_id) - files_to_download = self.download_tracker.get_no_files_in_download(scan_pid) - file_types = self.download_tracker.get_file_types_present(scan_pid) - completed = files_downloaded == files_to_download - if completed and (self.prefs.backup_images and self.backup_possible(file_type)): - completed = self.download_tracker.all_files_backed_up(unique_id, file_type) - - if completed: - files_remaining = self.thumbnails.get_no_files_remaining(scan_pid) - else: - files_remaining = 0 - - if completed and files_remaining: - # e.g.: 3 of 205 photos and videos (202 remaining) - progress_bar_text = _("%(number)s of %(total)s %(filetypes)s (%(remaining)s remaining)") % { - 'number': files_downloaded, - 'total': files_to_download, - 'filetypes': file_types, - 'remaining': files_remaining} - else: - # e.g.: 205 of 205 photos and videos - progress_bar_text = _("%(number)s of %(total)s %(filetypes)s") % \ - {'number': files_downloaded, - 'total': files_to_download, - 'filetypes': file_types} - percent_complete = self.download_tracker.get_percent_complete(scan_pid) - self.device_collection.update_progress(scan_pid=scan_pid, - percent_complete=percent_complete, - progress_bar_text=progress_bar_text, - bytes_downloaded=None) - - percent_complete = self.download_tracker.get_overall_percent_complete() - self.download_progressbar.set_fraction(percent_complete) - - return (completed, files_remaining) - - - def _clean_all_temp_dirs(self): - """ - Cleans all temp dirs if they exist - """ - for scan_pid in self.temp_dirs_by_scan_pid: - for temp_dir in self.temp_dirs_by_scan_pid[scan_pid]: - self._purge_dir(temp_dir) - - self.temp_dirs_by_scan_pid = {} - - - def _clean_temp_dirs_for_scan_pid(self, scan_pid): - """ - Deletes temp files and folders used in download - """ - for temp_dir in self.temp_dirs_by_scan_pid[scan_pid]: - self._purge_dir(temp_dir) - del self.temp_dirs_by_scan_pid[scan_pid] - - def _purge_dir(self, directory): - """ - Deletes all files in the directory, and the directory itself. - - Does not recursively traverse any subfolders in the directory. - """ - - if directory: - try: - path = gio.File(directory) - # first delete any files in the temp directory - # assume there are no directories in the temp directory - file_attributes = "standard::name,standard::type" - children = path.enumerate_children(file_attributes) - for child in children: - f = path.get_child(child.get_name()) - f.delete(cancellable=None) - path.delete(cancellable=None) - logger.debug("Deleted directory %s", directory) - except gio.Error, inst: - logger.error("Failure deleting temporary folder %s", directory) - logger.error(inst) - - - # # # - # Preferences - # # # - - - def _init_prefs(self, auto_detect, device_location): - self.prefs = prefsrapid.RapidPreferences() - - # handle device preferences set from the command line - # do this before preference changes are handled with notify_add - if auto_detect: - self.prefs.device_autodetection = True - elif device_location: - self.prefs.device_location = device_location - self.prefs.device_autodetection = False - - self.prefs.notify_add(self.on_preference_changed) - - # flag to indicate whether the user changed some preferences that - # indicate the image and backup devices should be setup again - self.rerun_setup_available_image_and_video_media = False - self.rerun_setup_available_backup_media = False - - # flag to indicate that the preferences dialog window is being - # displayed to the user - self.preferences_dialog_displayed = False - - # flag to indicate that the user has modified the download today - # related values in the preferences dialog window - self.refresh_downloads_today = False - - # these values are used to track the number of backup devices / - # locations for each file type - self.no_photo_backup_devices = 0 - self.no_video_backup_devices = 0 - - self.downloads_today_tracker = self.prefs.get_downloads_today_tracker() - - downloads_today = self.downloads_today_tracker.get_and_maybe_reset_downloads_today() - if downloads_today > 0: - logger.info("Downloads that have occurred so far today: %s", downloads_today) - else: - logger.info("No downloads have occurred so far today") - - self.downloads_today_value = Value(c_int, - self.downloads_today_tracker.get_raw_downloads_today()) - self.downloads_today_date_value = Array(c_char, - self.downloads_today_tracker.get_raw_downloads_today_date()) - self.day_start_value = Array(c_char, - self.downloads_today_tracker.get_raw_day_start()) - self.refresh_downloads_today_value = Value(c_bool, False) - self.stored_sequence_value = Value(c_int, self.prefs.stored_sequence_no) - self.uses_stored_sequence_no_value = Value(c_bool, self.prefs.any_pref_uses_stored_sequence_no()) - self.uses_session_sequece_no_value = Value(c_bool, self.prefs.any_pref_uses_session_sequece_no()) - self.uses_sequence_letter_value = Value(c_bool, self.prefs.any_pref_uses_sequence_letter_value()) - - self.check_prefs_upgrade(__version__) - self.prefs.program_version = __version__ - - def _check_for_sequence_value_use(self): - self.uses_stored_sequence_no_value.value = self.prefs.any_pref_uses_stored_sequence_no() - self.uses_session_sequece_no_value.value = self.prefs.any_pref_uses_session_sequece_no() - self.uses_sequence_letter_value.value = self.prefs.any_pref_uses_sequence_letter_value() - - def check_prefs_upgrade(self, running_version): - """ - Checks if the running version of the program is different from the - version recorded in the preferences. - - If the version is different, the preferences are checked to see - whether they should be upgraded or not. - """ - previous_version = self.prefs.program_version - if len(previous_version) > 0: - # the program has been run previously for this user - - pv = utilities.pythonify_version(previous_version) - rv = utilities.pythonify_version(running_version) - - if pv <> rv: - # 0.4.1 and below had only one manual backup location - # 0.4.2 introduced a distinct video back up location that can be manually set - # Therefore must duplicate the previous photo & video manual backup location into the - # new video field, unless it has already been changed already. - - if pv < utilities.pythonify_version('0.4.2'): - if self.prefs.backup_video_location == os.path.expanduser('~'): - self.prefs.backup_video_location = self.prefs.backup_location - logger.info("Migrated manual backup location preference to videos: %s", - self.prefs.backup_video_location) - - def on_preference_changed(self, key, value): - """ - Called when user changes the program's preferences - """ - logger.debug("Preference change detected: %s", key) - - if key == 'show_log_dialog': - self.menu_log_window.set_active(value) - elif key in ['device_autodetection', 'device_autodetection_psd', - 'device_location', 'ignored_paths', - 'use_re_ignored_paths', 'device_blacklist']: - self.rerun_setup_available_image_and_video_media = True - self._set_from_toolbar_state() - if not self.preferences_dialog_displayed: - self.post_preference_change() - - - elif key in ['backup_images', 'backup_device_autodetection', - 'backup_location', 'backup_video_location', - 'backup_identifier', 'video_backup_identifier']: - self.rerun_setup_available_backup_media = True - if not self.preferences_dialog_displayed: - self.post_preference_change() - - # Downloads today and stored sequence numbers are kept in shared memory, - # so that the subfolderfile daemon process can access and modify them - - # Note, totally ignore any changes in downloads today, as it - # is modified in a special manner via a tracking class - - elif key == 'stored_sequence_no': - if type(value) <> types.IntType: - logger.critical("Stored sequence number value is malformed") - else: - self.stored_sequence_value.value = value - - elif key in ['image_rename', 'subfolder', 'video_rename', 'video_subfolder']: - self.need_job_code_for_naming = self.prefs.any_pref_uses_job_code() - # Check if stored sequence no is being used - self._check_for_sequence_value_use() - - elif key in ['download_folder', 'video_download_folder']: - self._set_to_toolbar_values() - self.display_free_space() - - def post_preference_change(self): - if self.rerun_setup_available_image_and_video_media: - - logger.info("Download device settings preferences were changed") - - self.thumbnails.clear_all() - self.setup_devices(on_startup = False, on_preference_change = True, block_auto_start = True) - self._set_device_collection_size() - - if self.main_notebook.get_current_page() == 1: # preview of file - self.main_notebook.set_current_page(0) - - self.rerun_setup_available_image_and_video_media = False - - if self.rerun_setup_available_backup_media: - if self.using_volume_monitor(): - self.start_volume_monitor() - logger.info("Backup preferences were changed.") - - self.refresh_backup_media() - - self.rerun_setup_available_backup_media = False - - if self.refresh_downloads_today: - self.downloads_today_value.value = self.downloads_today_tracker.get_raw_downloads_today() - self.downloads_today_date_value.value = self.downloads_today_tracker.get_raw_downloads_today_date() - self.day_start_value.value = self.downloads_today_tracker.get_raw_day_start() - self.refresh_downloads_today_value.value = True - self.prefs.set_downloads_today_from_tracker(self.downloads_today_tracker) - - - - # # # - # Main app window management and setup - # # # - - def _init_pynotify(self): - """ - Initialize system notification messages - """ - - if not pynotify.init("TestCaps"): - logger.warning("There might be problems using pynotify.") - - do_not_size_icon = False - self.notification_icon_size = 48 - try: - info = pynotify.get_server_info() - except: - logger.warning("Desktop environment notification server is incorrectly configured.") - else: - try: - if info["name"] == 'notify-osd': - do_not_size_icon = True - except: - pass - - if do_not_size_icon: - self.application_icon = gtk.gdk.pixbuf_new_from_file( - paths.share_dir('glade3/rapid-photo-downloader.svg')) - else: - self.application_icon = gtk.gdk.pixbuf_new_from_file_at_size( - paths.share_dir('glade3/rapid-photo-downloader.svg'), - self.notification_icon_size, self.notification_icon_size) - - def _init_widgets(self): - """ - Initialize widgets in the main window, and variables that point to them - """ - builder = gtk.Builder() - builder.set_translation_domain(config.APP_NAME) - self.builder = builder - builder.add_from_file(paths.share_dir("glade3/rapid.ui")) - self.rapidapp = builder.get_object("rapidapp") - self.from_toolbar = builder.get_object("from_toolbar") - self.copy_toolbar = builder.get_object("copy_toolbar") - self.dest_toolbar = builder.get_object("dest_toolbar") - self.menu_toolbar = builder.get_object("menu_toolbar") - self.main_vpaned = builder.get_object("main_vpaned") - self.main_notebook = builder.get_object("main_notebook") - self.download_action = builder.get_object("download_action") - self.download_button = builder.get_object("download_button") - - self.download_progressbar = builder.get_object("download_progressbar") - self.rapid_statusbar = builder.get_object("rapid_statusbar") - self.statusbar_context_id = self.rapid_statusbar.get_context_id("progress") - self.device_collection_scrolledwindow = builder.get_object("device_collection_scrolledwindow") - self.next_image_action = builder.get_object("next_image_action") - self.prev_image_action = builder.get_object("prev_image_action") - self.menu_log_window = builder.get_object("menu_log_window") - self.speed_label = builder.get_object("speed_label") - self.refresh_action = builder.get_object("refresh_action") - self.preferences_action = builder.get_object("preferences_action") - - # Only enable this action when actually displaying a preview - self.next_image_action.set_sensitive(False) - self.prev_image_action.set_sensitive(False) - - self._init_toolbars() - - # About dialog - builder.add_from_file(paths.share_dir("glade3/about.ui")) - self.about = builder.get_object("about") - - builder.connect_signals(self) - - self.preview_image = PreviewImage(self, builder) - - thumbnails_scrolledwindow = builder.get_object('thumbnails_scrolledwindow') - self.thumbnails = ThumbnailDisplay(self) - thumbnails_scrolledwindow.add(self.thumbnails) - - #collection of devices from which to download - self.device_collection_viewport = builder.get_object("device_collection_viewport") - self.device_collection = DeviceCollection(self) - self.device_collection_viewport.add(self.device_collection) - - #error log window - self.error_log = errorlog.ErrorLog(self) - - # monitor to handle mounts and dismounts - self.vmonitor = None - # track scan ids for mount paths - very useful when a device is unmounted - self.mounts_by_path = {} - - # Download action state - self.download_action_is_download = True - - # Track the time a download commences - self.download_start_time = None - - # Whether a system wide notifcation message should be shown - # after a download has occurred in parallel - self.display_summary_notification = False - - # Values used to display how much longer a download will take - self.time_remaining = downloadtracker.TimeRemaining() - self.time_check = downloadtracker.TimeCheck() - - def _init_toolbars(self): - """ Setup the 3 vertical toolbars on the main screen """ - self._setup_from_toolbar() - self._setup_copy_move_toolbar() - self._setup_dest_toolbar() - - # size label widths so they are equal, or else the left border of the file chooser will not match - self.photo_dest_label.realize() - self._make_widget_widths_equal(self.photo_dest_label, self.video_dest_label) - self.photo_dest_label.set_alignment(xalign=0.0, yalign=0.5) - self.video_dest_label.set_alignment(xalign=0.0, yalign=0.5) - - # size copy / move buttons so they are equal in length, so arrows align - self._make_widget_widths_equal(self.copy_button, self.move_button) - - def _setup_from_toolbar(self): - self.from_toolbar.set_style(gtk.TOOLBAR_TEXT) - self.from_toolbar.set_border_width(5) - - from_label = gtk.Label() - from_label.set_markup("<i>" + _("From") + "</i>") - self.from_toolbar_label = gtk.ToolItem() - self.from_toolbar_label.add(from_label) - self.from_toolbar_label.set_is_important(True) - self.from_toolbar.insert(self.from_toolbar_label, 0) - - self.auto_detect_button = gtk.ToggleToolButton() - self.auto_detect_button.set_is_important(True) - self.auto_detect_button.set_label(_("Auto Detect")) - self.from_toolbar.insert(self.auto_detect_button, 1) - - self.from_filechooser_button = gtk.FileChooserButton( - _("Select a folder containing %(file_types)s") % {'file_types':file_types_to_download()}) - self.from_filechooser_button.set_action( - gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) - - self.from_filechooser = gtk.ToolItem() - self.from_filechooser.set_is_important(True) - self.from_filechooser.add(self.from_filechooser_button) - self.from_filechooser.set_expand(True) - self.from_toolbar.insert(self.from_filechooser, 2) - - self._set_from_toolbar_state() - - #set events after having initialized the values - self.auto_detect_button.connect("toggled", self.on_auto_detect_button_toggled_event) - self.from_filechooser_button.connect("selection-changed", - self.on_from_filechooser_button_selection_changed) - - self.from_toolbar.show_all() - - def _setup_copy_move_toolbar(self): - self.copy_toolbar.set_style(gtk.TOOLBAR_TEXT) - self.copy_toolbar.set_border_width(5) - - copy_move_label = gtk.Label(" ") - self.copy_move_toolbar_label = gtk.ToolItem() - self.copy_move_toolbar_label.add(copy_move_label) - self.copy_move_toolbar_label.set_is_important(True) - self.copy_toolbar.insert(self.copy_move_toolbar_label, 0) - - self.copy_hbox = gtk.HBox() - self.move_hbox = gtk.HBox() - self.forward_image = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR) - self.forward_image2 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR) - self.forward_image3 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR) - self.forward_image4 = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_SMALL_TOOLBAR) - self.forward_label = gtk.Label(" ") - self.forward_label2 = gtk.Label(" ") - self.forward_label3 = gtk.Label(" ") - self.forward_label4 = gtk.Label(" ") - - self.copy_button = gtk.RadioToolButton() - self.copy_button.set_label(_("Copy")) - self.copy_button.set_is_important(True) - - self.copy_hbox.pack_start(self.forward_label) - self.copy_hbox.pack_start(self.forward_image) - self.copy_hbox.pack_start(self.copy_button, expand=False, fill=False) - self.copy_hbox.pack_start(self.forward_image2) - self.copy_hbox.pack_start(self.forward_label2) - copy_box = gtk.ToolItem() - copy_box.add(self.copy_hbox) - self.copy_toolbar.insert(copy_box, 1) - - self.move_button = gtk.RadioToolButton(self.copy_button) - self.move_button.set_label(_("Move")) - self.move_button.set_is_important(True) - self.move_hbox.pack_start(self.forward_label3) - self.move_hbox.pack_start(self.forward_image3) - self.move_hbox.pack_start(self.move_button, expand=False, fill=False) - self.move_hbox.pack_start(self.forward_image4) - self.move_hbox.pack_start(self.forward_label4) - move_box = gtk.ToolItem() - move_box.add(self.move_hbox) - self.copy_toolbar.insert(move_box, 2) - - self.move_button.set_active(self.prefs.auto_delete) - self.copy_button.connect("toggled", self.on_copy_button_toggle_event) - - self.copy_toolbar.show_all() - self._set_copy_toolbar_active_arrows() - - def _setup_dest_toolbar(self): - #Destination Toolbar - self.dest_toolbar.set_border_width(5) - - dest_label = gtk.Label() - dest_label.set_markup("<i>" + _("To") + "</i>") - self.dest_toolbar_label = gtk.ToolItem() - self.dest_toolbar_label.add(dest_label) - self.dest_toolbar_label.set_is_important(True) - self.dest_toolbar.insert(self.dest_toolbar_label, 0) - - photo_dest_hbox = gtk.HBox() - self.photo_dest_label = gtk.Label(_("Photos:")) - - self.to_photo_filechooser_button = gtk.FileChooserButton( - _("Select a folder to download photos to")) - self.to_photo_filechooser_button.set_action( - gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) - photo_dest_hbox.pack_start(self.photo_dest_label, expand=False, fill=False, padding=6) - photo_dest_hbox.pack_start(self.to_photo_filechooser_button) - self.to_photo_filechooser = gtk.ToolItem() - self.to_photo_filechooser.set_is_important(True) - self.to_photo_filechooser.set_expand(True) - self.to_photo_filechooser.add(photo_dest_hbox) - self.dest_toolbar.insert(self.to_photo_filechooser, 1) - - video_dest_hbox = gtk.HBox() - self.video_dest_label = gtk.Label(_("Videos:")) - self.to_video_filechooser_button = gtk.FileChooserButton( - _("Select a folder to download videos to")) - self.to_video_filechooser_button.set_action( - gtk.FILE_CHOOSER_ACTION_SELECT_FOLDER) - video_dest_hbox.pack_start(self.video_dest_label, expand=False, fill=False, padding=6) - video_dest_hbox.pack_start(self.to_video_filechooser_button) - self.to_video_filechooser = gtk.ToolItem() - self.to_video_filechooser.set_is_important(True) - self.to_video_filechooser.set_expand(True) - self.to_video_filechooser.add(video_dest_hbox) - self.dest_toolbar.insert(self.to_video_filechooser, 2) - - self._set_to_toolbar_values() - self.to_photo_filechooser_button.connect("selection-changed", - self.on_to_photo_filechooser_button_selection_changed) - #~ self.to_photo_filechooser_button.connect("file-set", - #~ self.on_to_photo_filechooser_button_file_set) - #~ self.to_photo_filechooser_button.connect("current-folder-changed", - #~ self.on_to_photo_filechooser_button_current_folder_changed) - self.to_video_filechooser_button.connect("selection-changed", - self.on_to_video_filechooser_button_selection_changed) - self.dest_toolbar.show_all() - - def _make_widget_widths_equal(self, widget1, widget2): - """takes two widgets and sets a width for both equal to widest one""" - - x1, y1, w1, h1 = widget1.get_allocation() - x2, y2, w2, h2 = widget2.get_allocation() - w = max(w1, w2) - h = max(h1, h2) - widget1.set_size_request(w,h) - widget2.set_size_request(w,h) - - def _set_copy_toolbar_active_arrows(self): - if self.copy_button.get_active(): - self.forward_image.set_visible(True) - self.forward_image2.set_visible(True) - self.forward_image3.set_visible(False) - self.forward_image4.set_visible(False) - self.forward_label.set_visible(False) - self.forward_label2.set_visible(False) - self.forward_label3.set_visible(True) - self.forward_label4.set_visible(True) - else: - self.forward_image.set_visible(False) - self.forward_image2.set_visible(False) - self.forward_image3.set_visible(True) - self.forward_image4.set_visible(True) - self.forward_label.set_visible(True) - self.forward_label2.set_visible(True) - self.forward_label3.set_visible(False) - self.forward_label4.set_visible(False) - - def on_copy_button_toggle_event(self, radio_button): - self._set_copy_toolbar_active_arrows() - self.prefs.auto_delete = not self.copy_button.get_active() - - def _set_from_toolbar_state(self): - logger.debug("_set_from_toolbar_state") - self.auto_detect_button.set_active(self.prefs.device_autodetection) - if self.prefs.device_autodetection: - self.from_filechooser_button.set_sensitive(False) - self.from_filechooser_button.set_current_folder(self.prefs.device_location) - - def on_auto_detect_button_toggled_event(self, button): - logger.debug("on_auto_detect_button_toggled_event") - self.from_filechooser_button.set_sensitive(not button.get_active()) - if not self.rerun_setup_available_image_and_video_media: - self.prefs.device_autodetection = button.get_active() - - def on_from_filechooser_button_selection_changed(self, filechooserbutton): - logger.debug("on_from_filechooser_button_selection_changed") - path = get_folder_selection(filechooserbutton) - if path and not self.rerun_setup_available_image_and_video_media: - self.prefs.device_location = path - - def on_from_filechooser_button_file_set(self, button): - logger.debug("on_from_filechooser_button_file_set") - - def on_to_photo_filechooser_button_file_set(self, filechooserbutton): - logger.debug("on_to_filechooser_button_file_set") - - def on_to_photo_filechooser_button_selection_changed(self, filechooserbutton): - logger.debug("on_to_filechooser_button_selection_changed") - path = get_folder_selection(filechooserbutton) - #~ logger.debug("Path: %s", path) - if path: - self.prefs.download_folder = path - - def on_to_photo_filechooser_button_current_folder_changed(self, filechooserbutton): - logger.debug("on_to_photo_filechooser_button_current_folder_changed") - - def on_to_video_filechooser_button_selection_changed(self, filechooserbutton): - path = get_folder_selection(filechooserbutton) - if path: - self.prefs.video_download_folder = path - - def _set_to_toolbar_values(self): - self.to_photo_filechooser_button.set_current_folder(self.prefs.download_folder) - self.to_video_filechooser_button.set_current_folder(self.prefs.video_download_folder) - - def toolbar_event(self, widget, toolbar): - pass - - - - def _set_window_size(self): - """ - Remember the window size from the last time the program was run, or - set a default size - """ - - if self.prefs.main_window_maximized: - self.rapidapp.maximize() - self.rapidapp.set_default_size(config.DEFAULT_WINDOW_WIDTH, - config.DEFAULT_WINDOW_HEIGHT) - elif self.prefs.main_window_size_x > 0: - self.rapidapp.set_default_size(self.prefs.main_window_size_x, self.prefs.main_window_size_y) - else: - # set a default size - self.rapidapp.set_default_size(config.DEFAULT_WINDOW_WIDTH, - config.DEFAULT_WINDOW_HEIGHT) - - - def _set_device_collection_size(self): - """ - Set the size of the device collection scrolled window widget - """ - if self.device_collection.map_process_to_row: - height = max(self.device_collection_viewport.size_request()[1], 24) - self.device_collection_scrolledwindow.set_size_request(-1, height) - self.main_vpaned.set_position(height) - else: - # don't allow the media collection to be absolutely empty - self.device_collection_scrolledwindow.set_size_request(-1, 47) - - - def on_rapidapp_window_state_event(self, widget, event): - """ Records the window maximization state in the preferences.""" - - if event.changed_mask & gdk.WINDOW_STATE_MAXIMIZED: - self.prefs.main_window_maximized = event.new_window_state & gdk.WINDOW_STATE_MAXIMIZED - - def _setup_buttons(self): - thumbnails_button = self.builder.get_object("thumbnails_button") - image = gtk.image_new_from_file(paths.share_dir('glade3/thumbnails_icon.png')) - thumbnails_button.set_image(image) - - preview_button = self.builder.get_object("preview_button") - image = gtk.image_new_from_file(paths.share_dir('glade3/photo_icon.png')) - preview_button.set_image(image) - - next_image_button = self.builder.get_object("next_image_button") - image = gtk.image_new_from_stock(gtk.STOCK_GO_FORWARD, gtk.ICON_SIZE_BUTTON) - next_image_button.set_image(image) - - prev_image_button = self.builder.get_object("prev_image_button") - image = gtk.image_new_from_stock(gtk.STOCK_GO_BACK, gtk.ICON_SIZE_BUTTON) - prev_image_button.set_image(image) - - def _setup_icons(self): - icons = ['rapid-photo-downloader-jobcode',] - icon_list = [(icon, paths.share_dir('glade3/%s.svg' % icon)) for icon in icons] - register_iconsets(icon_list) - - def _setup_error_icons(self): - """ - hide display of warning and error symbols in the taskbar until they - are needed - """ - self.error_image = self.builder.get_object("error_image") - self.warning_image = self.builder.get_object("warning_image") - self.warning_vseparator = self.builder.get_object("warning_vseparator") - self.error_image.hide() - self.warning_image.hide() - self.warning_vseparator.hide() - - def enable_prefs_and_refresh(self, enabled): - """ - If enable is true, then the user is able to activate the preferences - or refresh command. - The intention is to be able to disable this during a download - """ - self.refresh_action.set_sensitive(enabled) - self.preferences_action.set_sensitive(enabled) - - def statusbar_message(self, msg): - self.rapid_statusbar.push(self.statusbar_context_id, msg) - - def statusbar_message_remove(self): - self.rapid_statusbar.pop(self.statusbar_context_id) - - def display_backup_mounts(self): - """ - Create a message to be displayed to the user showing which backup - mounts will be used - """ - message = '' - - paths = self.backup_devices.keys() - i = 0 - v = len(paths) - prefix = '' - for b in paths: - if v > 1: - if i < (v -1) and i > 0: - prefix = ', ' - elif i == (v - 1) : - prefix = " " + _("and") + " " - i += 1 - message = "%s%s'%s'" % (message, prefix, self.backup_devices[b][0].get_name()) - - if v > 1: - message = _("Using backup devices") + " %s" % message - elif v == 1: - message = _("Using backup device") + " %s" % message - else: - message = _("No backup devices detected") - - return message - - def display_free_space(self): - """ - Displays the amount of space free on the filesystem the files will be - downloaded to. - - Also displays backup volumes / path being used. - """ - photo_dir = self.is_valid_download_dir(path=self.prefs.download_folder, is_photo_dir=True, show_error_in_log=True) - video_dir = self.is_valid_download_dir(path=self.prefs.video_download_folder, is_photo_dir=False, show_error_in_log=True) - if photo_dir and video_dir: - same_file_system = self.same_file_system(self.prefs.download_folder, - self.prefs.video_download_folder) - else: - same_file_system = False - - dirs = [] - if photo_dir: - dirs.append((self.prefs.download_folder, _("photos"))) - if video_dir and not same_file_system: - dirs.append((self.prefs.video_download_folder, _("videos"))) - - msg = '' - if len(dirs) > 1: - msg = ' ' + _('Free space:') + ' ' - - for i in range(len(dirs)): - dir_info = dirs[i] - folder = gio.File(dir_info[0]) - file_info = folder.query_filesystem_info(gio.FILE_ATTRIBUTE_FILESYSTEM_FREE) - size = file_info.get_attribute_uint64(gio.FILE_ATTRIBUTE_FILESYSTEM_FREE) - free = format_size_for_user(bytes=size) - if len(dirs) > 1: - #(videos) or (photos) will be appended to the free space message displayed to the - #user in the status bar. - #you should only translate this if your language does not use parantheses - file_type = _("(%(file_type)s)") % {'file_type': dir_info[1]} - - #Freespace available on the filesystem for downloading to - #Displayed in status bar message on main window - msg += _("%(free)s %(file_type)s") % {'free': free, 'file_type': file_type} - if i == 0: - #Inserted in the middle of the statusbar message concerning the amount of freespace - #Used to differentiate between two different file systems - #e.g. Free space: 21.3GB (photos); 14.7GB (videos). - msg += _("; ") - elif not self.prefs.backup_images: - #Inserted at the end of the statusbar message concerning the amount of freespace - #Used to differentiate between two different file systems - #e.g. Free space: 21.3GB (photos); 14.7GB (videos). - msg += _(".") - - else: - #Freespace available on the filesystem for downloading to - #Displayed in status bar message on main window - #e.g. 14.7GB available - msg = " " + _("%(free)s free") % {'free': free} - - - if self.prefs.backup_images: - if not self.prefs.backup_device_autodetection: - if self.prefs.backup_location == self.prefs.backup_video_location: - if DOWNLOAD_VIDEO: - # user manually specified the same location for photos and video backups - msg2 = _('Backing up photos and videos to %(path)s') % {'path':self.prefs.backup_location} - else: - # user manually specified backup location - msg2 = _('Backing up to %(path)s') % {'path':self.prefs.backup_location} - else: - # user manually specified different locations for photo and video backups - msg2 = _('Backing up photos to %(path)s and videos to %(path2)s') % { - 'path':self.prefs.backup_location, - 'path2': self.prefs.backup_video_location} - else: - msg2 = self.display_backup_mounts() - - if msg: - msg = _("%(freespace)s. %(backuppaths)s.") % {'freespace': msg, 'backuppaths': msg2} - else: - msg = msg2 - - msg = msg.rstrip() - - self.statusbar_message(msg) - - def log_error(self, severity, problem, details, extra_detail=None): - """ - Display error and warning messages to user in log window - """ - self.error_log.add_message(severity, problem, details, extra_detail) - - - def on_error_eventbox_button_press_event(self, widget, event): - self.prefs.show_log_dialog = True - self.error_log.widget.show() - - - def on_menu_log_window_toggled(self, widget): - active = widget.get_active() - self.prefs.show_log_dialog = active - if active: - self.error_log.widget.show() - else: - self.error_log.widget.hide() - - def notify_prefs_are_invalid(self, details): - title = _("Program preferences are invalid") - logger.critical(title) - self.log_error(severity=config.CRITICAL_ERROR, problem=title, - details=details) - - - # # # - # Utility functions - # # # - - def files_of_type_present(self, files, file_type, return_file_count=False): - """ - Returns true if there is at least one instance of the file_type - in the list of files to be copied - - If return_file_count is True, then the number of files of that type - will be counted and returned instead of True or False - """ - i = 0 - for rpd_file in files: - if rpd_file.file_type == file_type: - if return_file_count: - i += 1 - else: - return True - if not return_file_count: - return False - else: - return i - - def size_files_to_be_downloaded(self, files): - """ - Returns the total sizes of the photos and videos to be downloaded in bytes - """ - photo_size = 0 - video_size = 0 - for rpd_file in files: - if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO: - photo_size += rpd_file.size - else: - video_size += rpd_file.size - - return (photo_size, video_size) - - def check_file_types_to_be_downloaded(self, files_by_scan_pid): - """Determines what types of files need to be downloaded, setting - self.downloading_photos and self.downloading_videos accordingly""" - self.downloading_photos = False - self.downloading_videos = False - while not self.downloading_photos and not self.downloading_videos: - for scan_pid in files_by_scan_pid: - files = files_by_scan_pid[scan_pid] - if not self.downloading_photos: - if self.files_of_type_present(files, rpdfile.FILE_TYPE_PHOTO): - self.downloading_photos = True - if not self.downloading_videos: - if self.files_of_type_present(files, rpdfile.FILE_TYPE_VIDEO): - self.downloading_videos = True - - - def check_download_folder_validity(self, files_by_scan_pid): - """ - Checks validity of download folders based on the file types the user - is attempting to download. - - If valid, returns a tuple of True and an empty list. - If invalid, returns a tuple of False and a list of the invalid directores. - """ - valid = True - invalid_dirs = [] - - if self.downloading_photos: - if not self.is_valid_download_dir(self.prefs.download_folder, - is_photo_dir=True): - valid = False - invalid_dirs.append(self.prefs.download_folder) - else: - logger.debug("Photo download folder is valid: %s", - self.prefs.download_folder) - - if self.downloading_videos: - if not self.is_valid_download_dir(self.prefs.video_download_folder, - is_photo_dir=False): - valid = False - invalid_dirs.append(self.prefs.video_download_folder) - else: - logger.debug("Video download folder is valid: %s", - self.prefs.video_download_folder) - - - return (valid, invalid_dirs) - - def backup_destinations_missing(self): - if self.prefs.backup_images and self.prefs.backup_device_autodetection: - photo_backup_ok = video_backup_ok = True - if self.downloading_photos and not self.backup_possible(rpdfile.FILE_TYPE_PHOTO): - photo_backup_ok = False - if self.downloading_videos and not self.backup_possible(rpdfile.FILE_TYPE_VIDEO): - video_backup_ok = False - if photo_backup_ok and video_backup_ok: - return None - else: - return (photo_backup_ok, video_backup_ok) - return None - - def same_file_system(self, file1, file2): - """Returns True if the files / diretories are on the same file system - """ - f1 = gio.File(file1) - f2 = gio.File(file2) - f1_info = f1.query_info(gio.FILE_ATTRIBUTE_ID_FILESYSTEM) - f1_id = f1_info.get_attribute_string(gio.FILE_ATTRIBUTE_ID_FILESYSTEM) - f2_info = f2.query_info(gio.FILE_ATTRIBUTE_ID_FILESYSTEM) - f2_id = f2_info.get_attribute_string(gio.FILE_ATTRIBUTE_ID_FILESYSTEM) - return f1_id == f2_id - - - def same_file(self, file1, file2): - """Returns True if the files / directories are the same - """ - f1 = gio.File(file1) - f2 = gio.File(file2) - - file_attributes = "id::file" - f1_info = f1.query_filesystem_info(file_attributes) - f1_id = f1_info.get_attribute_string(gio.FILE_ATTRIBUTE_ID_FILE) - f2_info = f2.query_filesystem_info(file_attributes) - f2_id = f2_info.get_attribute_string(gio.FILE_ATTRIBUTE_ID_FILE) - return f1_id == f2_id - - def is_valid_download_dir(self, path, is_photo_dir, show_error_in_log=False): - """ - Checks the following conditions: - Does the directory exist? - Is it writable? - - if show_error_in_log is True, then display warning in log window, using - is_photo_dir, which if true means the download directory is for photos, - if false, for Videos - """ - valid = False - if is_photo_dir: - download_folder_type = _("Photo") - else: - download_folder_type = _("Video") - - try: - d = gio.File(path) - if not d.query_exists(cancellable=None): - logger.error("%s download folder does not exist: %s", - download_folder_type, path) - if show_error_in_log: - severity = config.WARNING - problem = _("%(file_type)s download folder does not exist") % { - 'file_type': download_folder_type} - details = _("Folder: %s") % path - self.log_error(severity, problem, details) - else: - file_attributes = "standard::type,access::can-read,access::can-write" - file_info = d.query_filesystem_info(file_attributes) - file_type = file_info.get_file_type() - - if file_type != gio.FILE_TYPE_DIRECTORY and file_type != gio.FILE_TYPE_UNKNOWN: - logger.error("%s download folder is invalid: %s", - download_folder_type, path) - if show_error_in_log: - severity = config.WARNING - problem = _("%(file_type)s download folder is invalid") % { - 'file_type': download_folder_type} - details = _("Folder: %s") % path - self.log_error(severity, problem, details) - else: - # is the directory writable? - try: - temp_dir = tempfile.mkdtemp(prefix="rpd-tmp", dir=path) - valid = True - except: - logger.error("%s is not writable", path) - if show_error_in_log: - severity = config.WARNING - problem = _("%(file_type)s download folder is not writable") % { - 'file_type': download_folder_type} - details = _("Folder: %s") % path - self.log_error(severity, problem, details) - else: - f = gio.File(temp_dir) - f.delete(cancellable=None) - - except gio.Error, inst: - logger.error("Error checking download directory %s", path) - logger.error(inst) - - return valid - - - - # # # - # Process results and management - # # # - - - def _start_process_managers(self): - """ - Set up process managers. - - A task such as scanning a device or copying files is handled in its - own process. - """ - - self.batch_size = 10 - self.batch_size_MB = 2 - - sequence_values = (self.downloads_today_value, - self.downloads_today_date_value, - self.day_start_value, - self.refresh_downloads_today_value, - self.stored_sequence_value, - self.uses_stored_sequence_no_value, - self.uses_session_sequece_no_value, - self.uses_sequence_letter_value) - - # daemon process to rename files and create subfolders - self.subfolder_file_manager = SubfolderFileManager( - self.subfolder_file_results, - sequence_values) - - # process to scan source devices / paths - self.scan_manager = ScanManager(self.scan_results, self.batch_size, - self.device_collection.add_device) - - #process to copy files from source to destination - self.copy_files_manager = CopyFilesManager(self.copy_files_results, - self.batch_size_MB) - - #process to back files up - self.backup_manager = BackupFilesManager(self.backup_results, - self.batch_size_MB) - - #process to enhance files after they've been copied and before they're - #renamed - self.file_modify_manager = FileModifyManager(self.file_modify_results) - - - def scan_results(self, source, condition): - """ - Receive results from scan processes - """ - connection = self.scan_manager.get_pipe(source) - - conn_type, data = connection.recv() - - if conn_type == rpdmp.CONN_COMPLETE: - connection.close() - self.scan_manager.no_tasks -= 1 - size, file_type_counter, scan_pid = data - size = format_size_for_user(bytes=size) - results_summary, file_types_present = file_type_counter.summarize_file_count() - self.download_tracker.set_file_types_present(scan_pid, file_types_present) - logger.info('Found %s' % results_summary) - logger.info('Files total %s' % size) - self.device_collection.update_device(scan_pid, size) - self.device_collection.update_progress(scan_pid, 0.0, results_summary, 0, pulse=False) - self.set_download_action_sensitivity() - - if (not self.auto_start_is_on and - self.prefs.generate_thumbnails): - self.download_progressbar.set_text(_("Thumbnails")) - self.thumbnails.generate_thumbnails(scan_pid) - elif self.auto_start_is_on: - if self.need_job_code_for_naming and not self.job_code: - self.get_job_code() - else: - self.start_download(scan_pid=scan_pid) - - logger.debug("Turning on display of thumbnails") - self.display_scan_thumbnails() - self.download_button.grab_focus() - - # signal that no more data is coming, finishing io watch for this pipe - return False - else: - # partial results - if len(data) > self.batch_size: - logger.critical("incoming pipe length is unexpectedly long: %s" % len(data)) - else: - size, file_type_counter, scan_pid, rpd_files = data - size = format_size_for_user(bytes=size) - scanning_progress = file_type_counter.running_file_count() - self.device_collection.update_device(scan_pid, size) - self.device_collection.update_progress(scan_pid, 0.0, scanning_progress, 0, pulse=True) - - for rpd_file in rpd_files: - self.thumbnails.add_file(rpd_file=rpd_file, - generate_thumbnail = not self.auto_start_is_on) - - # must return True for this method to be called again - return True - - - @dbus.service.method (config.DBUS_NAME, - in_signature='', out_signature='b') - def is_running (self): - return self.running - - @dbus.service.method (config.DBUS_NAME, - in_signature='', out_signature='') - def start (self): - if self.is_running(): - self.rapidapp.present() - else: - self.running = True - gtk.main() - -def start(): - - is_beta = config.version.find('~') > 0 - - parser = OptionParser(version= "%%prog %s" % utilities.human_readable_version(config.version)) - parser.set_defaults(verbose=is_beta, extensions=False) - # Translators: this text is displayed to the user when they request information on the command line options. - # The text %default should not be modified or left out. - parser.add_option("-v", "--verbose", action="store_true", dest="verbose", help=_("display program information on the command line as the program runs (default: %default)")) - parser.add_option("-d", "--debug", action="store_true", dest="debug", help=_('display debugging information when run from the command line')) - parser.add_option("-q", "--quiet", action="store_false", dest="verbose", help=_("only output errors to the command line")) - # image file extensions are recognized RAW files plus TIFF and JPG - parser.add_option("-e", "--extensions", action="store_true", dest="extensions", help=_("list photo and video file extensions the program recognizes and exit")) - parser.add_option("--focal-length", type=int, dest="focal_length", help="If an aperture value of 0.0 is encountered, the focal length metadata will be set to the number passed, and its aperture metadata to f/8") - parser.add_option("-a", "--auto-detect", action="store_true", dest="auto_detect", help=_("automatically detect devices from which to download, overwriting existing program preferences")) - parser.add_option("-l", "--device-location", type="string", metavar="PATH", dest="device_location", help=_("manually specify the PATH of the device from which to download, overwriting existing program preferences")) - parser.add_option("--reset-settings", action="store_true", dest="reset", help=_("reset all program settings and preferences and exit")) - (options, args) = parser.parse_args() - - if options.debug: - logging_level = logging.DEBUG - elif options.verbose: - logging_level = logging.INFO - else: - logging_level = logging.ERROR - - logger.setLevel(logging_level) - - if options.auto_detect and options.device_location: - logger.info(_("Error: specify device auto-detection or manually specify a device's path from which to download, but do not do both.")) - sys.exit(1) - - if options.auto_detect: - auto_detect=True - logger.info("Device auto detection set from command line") - else: - auto_detect=None - - if options.device_location: - device_location=options.device_location - if device_location[-1]=='/': - device_location = device_location[:-1] - logger.info("Device location set from command line: %s", device_location) - else: - device_location=None - - if options.extensions: - extensions = ((rpdfile.PHOTO_EXTENSIONS, _("Photos:")), (rpdfile.VIDEO_EXTENSIONS, _("Videos:"))) - for exts, file_type in extensions: - v = '' - for e in exts[:-1]: - v += '%s, ' % e.upper() - v = file_type + " " + v[:-1] + ' '+ (_('and %s') % exts[-1].upper()) - print v - - sys.exit(0) - - if options.reset: - prefs = prefsrapid.RapidPreferences() - prefs.reset() - print _("All settings and preferences have been reset") - sys.exit(0) - - if options.focal_length: - focal_length = options.focal_length - else: - focal_length = None - - logger.info("Rapid Photo Downloader %s", utilities.human_readable_version(config.version)) - logger.info("Using pyexiv2 %s", metadataphoto.pyexiv2_version_info()) - logger.info("Using exiv2 %s", metadataphoto.exiv2_version_info()) - if metadataexiftool.EXIFTOOL_VERSION is None: - logger.info("Exiftool not detected") - else: - logger.info("Using exiftool %s", metadataexiftool.EXIFTOOL_VERSION) - if metadatavideo.HAVE_HACHOIR: - logger.info("Using hachoir %s", metadatavideo.version_info()) - - - if focal_length: - logger.info("Focal length of %s will be used when an aperture of 0.0 is encountered", focal_length) - - bus = dbus.SessionBus () - request = bus.request_name (config.DBUS_NAME, dbus.bus.NAME_FLAG_DO_NOT_QUEUE) - if request != dbus.bus.REQUEST_NAME_REPLY_EXISTS: - app = RapidApp(bus, '/', config.DBUS_NAME, focal_length=focal_length, - auto_detect=auto_detect, device_location=device_location) - else: - # this application is already running - print "Rapid Photo Downloader is already running" - object = bus.get_object (config.DBUS_NAME, "/") - app = dbus.Interface (object, config.DBUS_NAME) - - app.start() - -if __name__ == "__main__": - start() diff --git a/rapid/renamesubfolderprefstest.py b/rapid/renamesubfolderprefstest.py deleted file mode 100644 index d675c54..0000000 --- a/rapid/renamesubfolderprefstest.py +++ /dev/null @@ -1,248 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2007-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import unittest - -from renamesubfolderprefs import * - -class PreferenceTest (unittest.TestCase): - image_test = ( [TEXT, '', ''], - [DATE_TIME, IMAGE_DATE, 'YYYYMMDD'], - [METADATA, APERTURE, ''], - [FILENAME, NAME_EXTENSION, UPPERCASE], - ) - subfolder_test = ( [TEXT, '', ''], - [DATE_TIME, IMAGE_DATE, 'HHMM'], - [METADATA, SHORT_CAMERA_MODEL_HYPHEN, - LOWERCASE], - [SEPARATOR, '', ''], - [FILENAME, EXTENSION, LOWERCASE] - ) - - video_name_test= ( - [DATE_TIME, VIDEO_DATE, 'HHMMSS'], - [METADATA, CODEC, LOWERCASE], - [METADATA, FPS, ''], - ) - - video_name_test2= ( - [DATE_TIME, VIDEO_DATE, 'HHMMSS', - METADATA, CODEC, LOWERCASE, - METADATA, FPS, ''], - ) - - video_name_test3= ( - [FILENAME, VIDEO_NUMBER, IMAGE_NUMBER_4, - FILENAME, NAME_EXTENSION, LOWERCASE, - METADATA, FPS, ''], - ) - - video_subfolder_test= ( - [DATE_TIME, TODAY, 'HHMMSS', - SEPARATOR, '', '', - METADATA, WIDTH, ''], - ) - - - trueMetadataTest = ([FILENAME, EXTENSION, LOWERCASE, TEXT, '', '', METADATA, APERTURE, ''], [METADATA, APERTURE, '', TEXT, '', '', FILENAME, EXTENSION, LOWERCASE], ) - - falseMetadataTest = ([FILENAME, EXTENSION, LOWERCASE, METADATA, APERTURE, '', FILENAME, NAME, LOWERCASE], - [FILENAME, NAME, LOWERCASE, FILENAME, EXTENSION, LOWERCASE], - [FILENAME, NAME_EXTENSION, LOWERCASE, FILENAME, EXTENSION, LOWERCASE], - [FILENAME, NAME, LOWERCASE, FILENAME, METADATA, EXPOSURE_TIME, '', IMAGE_NUMBER, IMAGE_NUMBER_ALL, FILENAME, EXTENSION, LOWERCASE], ) - - sequences_test = ([SEQUENCES, SESSION_SEQ_NUMBER, SEQUENCE_NUMBER_3], - [FILENAME, NAME, LOWERCASE, SEQUENCES, DOWNLOAD_SEQ_NUMBER, SEQUENCE_NUMBER_1, - FILENAME, EXTENSION, UPPERCASE], - [METADATA, APERTURE, '', SEQUENCES, STORED_SEQ_NUMBER, SEQUENCE_NUMBER_5, - FILENAME, EXTENSION, UPPERCASE], ) - - def testPrefImageList(self): - for pref in self.image_test: - result = checkPreferenceValid(DICT_IMAGE_RENAME_L0, pref) - self.assertEqual(result, True) - - def testPrefVideoList(self): - for pref in self.video_name_test: - result = checkPreferenceValid(DICT_VIDEO_RENAME_L0, pref) - self.assertEqual(result, True) - for pref in self.video_name_test2: - result = checkPreferenceValid(DICT_VIDEO_RENAME_L0, pref) - self.assertEqual(result, True) - for pref in self.video_name_test3: - result = checkPreferenceValid(DICT_VIDEO_RENAME_L0, pref) - self.assertEqual(result, True) - - def testSequencesList(self): - for pref in self.sequences_test: - result = checkPreferenceValid(DICT_IMAGE_RENAME_L0, pref) - self.assertEqual(result, True) - - def testNeedImageMetaDataToCreateUniqueName(self): - for i in self.trueMetadataTest: - p = ImageRenamePreferences(i, None) - result = p.needImageMetaDataToCreateUniqueName() - self.assertEqual(result, True) - - for i in self.falseMetadataTest: - p = ImageRenamePreferences(i, None) - result = p.needImageMetaDataToCreateUniqueName() - self.assertEqual(result, False) - - def testLargePrefList(self): - prefList = [] - for pref in self.image_test: - for l in pref: - prefList.append(l) - - result = checkPreferenceValid(DICT_IMAGE_RENAME_L0, prefList) - self.assertEqual(result, True) - - def testPrefSubfolderList(self): - for pref in self.subfolder_test: - result = checkPreferenceValid(DICT_SUBFOLDER_L0, pref) - self.assertEqual(result, True) - - def testPrefVideoSubfolderList(self): - for pref in self.video_subfolder_test: - result = checkPreferenceValid(DICT_VIDEO_SUBFOLDER_L0, pref) - self.assertEqual(result, True) - - def testDateTimeL2Length(self): - self.assertEqual(len(LIST_DATE_TIME_L2), len(DATE_TIME_CONVERT)) - - def testDateTimeL2Conversion(self): - self.assertEqual(convertDateForStrftime('YY'), '%y') - - - -class BadPreferences(unittest.TestCase): - bad_image_key = ( [TEXT, '', '', - DATE_TIME, IMAGE_DATE, 'YYYYMMDD', - METADATA, APERTURE, '', - FILENAME, NAME_EXTENSION, UPPERCASE, - 'Filename2', NAME_EXTENSION, UPPERCASE], - ) - bad_image_value = ( [DATE_TIME, TODAY, IMAGE_NUMBER_ALL], - [METADATA, CAMERA_MAKE, IMAGE_NUMBER_4], - [DATE_TIME, IMAGE_DATE, None], - [DATE_TIME, IMAGE_DATE, ''], - [DATE_TIME, None, None], - [DATE_TIME, '', ''], - ) - - bad_image_key2 = ( - [FILENAME, VIDEO_NUMBER, IMAGE_NUMBER_4, - FILENAME, NAME_EXTENSION, LOWERCASE, - METADATA, APERTURE, ''], - ) - - bad_subfolder_key = ([FILENAME, NAME_EXTENSION, UPPERCASE],) - - bad_subfolder_key2 = ( [TEXT, '', '', - DATE_TIME, IMAGE_DATE, 'HHMM', - METADATA, SHORT_CAMERA_MODEL_HYPHEN, - LOWERCASE, - SEPARATOR, '', '', - 'Filename-bad', EXTENSION, LOWERCASE], - ) - - bad_subfolder_value = ( [FILENAME, None, None], - [FILENAME, '', ''],) - - bad_length = ([], [DATE_TIME, TODAY], [DATE_TIME]) - - bad_dt_conversion = ('HHYY', 'YYSS') - - bad_subfolder_combos = ([SEPARATOR, '', ''], - [FILENAME, EXTENSION, UPPERCASE, - SEPARATOR, '', ''], - [FILENAME, EXTENSION, UPPERCASE, - SEPARATOR, '', '', - SEPARATOR, '', '', - FILENAME, EXTENSION, UPPERCASE - ], - [SEPARATOR, '', '', - SEPARATOR, '', '', - SEPARATOR, '', '', - SEPARATOR, '', '' - ] - ) - - def testBadImageKey(self): - for pref in self.bad_image_key: - self.assertRaises(PrefKeyError, checkPreferenceValid, - DICT_IMAGE_RENAME_L0, - pref) - for pref in self.bad_image_key2: - self.assertRaises(PrefKeyError, checkPreferenceValid, - DICT_IMAGE_RENAME_L0, - pref) - - - def testBadImageValue(self): - for pref in self.bad_image_value: - self.assertRaises(PrefValueInvalidError, checkPreferenceValid, - DICT_IMAGE_RENAME_L0, - pref) - - - def testBadSubfolderKey(self): - for pref in self.bad_subfolder_key: - self.assertRaises(PrefKeyError, checkPreferenceValid, - DICT_SUBFOLDER_L0, - pref) - - for pref in self.bad_subfolder_key2: - self.assertRaises(PrefKeyError, checkPreferenceValid, - DICT_SUBFOLDER_L0, - pref) - - - def testBadSubfolderValue(self): - for pref in self.bad_subfolder_value: - self.assertRaises(PrefValueInvalidError, checkPreferenceValid, - DICT_SUBFOLDER_L0, - pref) - - def testBadLength(self): - for pref in self.bad_length: - self.assertRaises(PrefLengthError, checkPreferenceValid, - DICT_IMAGE_RENAME_L0, - pref) - def testBadDTConversion(self): - for pref in self.bad_dt_conversion: - self.assertRaises(PrefValueInvalidError, convertDateForStrftime, - pref) - - def testBadSubfolderCombo(self): - - for pref in self.bad_subfolder_combos: - s = SubfolderPreferences(pref, self) - self.assertRaises(PrefValueKeyComboError, s.checkPrefsForValidity) - - def testBadVideoSubfolderCombo(self): - - for pref in self.bad_subfolder_combos: - s = VideoSubfolderPreferences(pref, self) - self.assertRaises(PrefValueKeyComboError, s.checkPrefsForValidity) - -if __name__ == "__main__": - unittest.main() diff --git a/rapid/rpdfile.py b/rapid/rpdfile.py deleted file mode 100644 index 15c1722..0000000 --- a/rapid/rpdfile.py +++ /dev/null @@ -1,348 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import os -import gtk - -import time, datetime - -import multiprocessing, logging -logger = multiprocessing.get_logger() - -import pyexiv2 - -import paths - -from gettext import gettext as _ - -import config -import metadataphoto -import metadatavideo -import metadataexiftool - -import problemnotification as pn - -import thumbnail as tn - - -RAW_EXTENSIONS = ['arw', 'dcr', 'cr2', 'crw', 'dng', 'mos', 'mef', 'mrw', - 'nef', 'nrw', 'orf', 'pef', 'raf', 'raw', 'rw2', 'sr2', - 'srw'] - -JPEG_EXTENSIONS = ['jpg', 'jpe', 'jpeg'] - -NON_RAW_IMAGE_EXTENSIONS = JPEG_EXTENSIONS + ['tif', 'tiff', 'mpo'] - -PHOTO_EXTENSIONS = RAW_EXTENSIONS + NON_RAW_IMAGE_EXTENSIONS - -AUDIO_EXTENSIONS = ['wav', 'mp3'] - -if metadatavideo.DOWNLOAD_VIDEO: - # some distros do not include the necessary libraries that Rapid Photo Downloader - # needs to be able to download videos - VIDEO_EXTENSIONS = ['3gp', 'avi', 'm2t', 'mov', 'mp4', 'mpeg','mpg', 'mod', - 'tod'] - if metadataexiftool.EXIFTOOL_VERSION is not None: - VIDEO_EXTENSIONS += ['mts'] - VIDEO_THUMBNAIL_EXTENSIONS = ['thm'] -else: - VIDEO_EXTENSIONS = [] - VIDEO_THUMBNAIL_EXTENSIONS = [] - - -FILE_TYPE_PHOTO = 0 -FILE_TYPE_VIDEO = 1 - -def file_type(file_extension): - """ - Uses file extentsion to determine the type of file - photo or video. - - Returns True if yes, else False. - """ - if file_extension in PHOTO_EXTENSIONS: - return FILE_TYPE_PHOTO - elif file_extension in VIDEO_EXTENSIONS: - return FILE_TYPE_VIDEO - return None - -def get_rpdfile(extension, name, display_name, path, size, - file_system_modification_time, - thm_full_name, audio_file_full_name, - scan_pid, file_id, file_type): - - if file_type == FILE_TYPE_VIDEO: - return Video(name, display_name, path, size, - file_system_modification_time, thm_full_name, - audio_file_full_name, - scan_pid, file_id) - else: - # assume it's a photo - no check for performance reasons (this will be - # called many times) - return Photo(name, display_name, path, size, - file_system_modification_time, thm_full_name, - audio_file_full_name, - scan_pid, file_id) - -class FileTypeCounter: - def __init__(self): - self._counter = dict() - - def add(self, file_type): - self._counter[file_type] = self._counter.setdefault(file_type, 0) + 1 - - def no_videos(self): - """Returns the number of videos""" - return self._counter.setdefault(FILE_TYPE_VIDEO, 0) - - def no_photos(self): - """Returns the number of photos""" - return self._counter.setdefault(FILE_TYPE_PHOTO, 0) - - def file_types_present(self): - """ - returns a string to be displayed to the user that can be used - to show if a value refers to photos or videos or both, or just one - of each - """ - - no_videos = self.no_videos() - no_images = self.no_photos() - - if (no_videos > 0) and (no_images > 0): - v = _('photos and videos') - elif (no_videos == 0) and (no_images == 0): - v = _('photos or videos') - elif no_videos > 0: - if no_videos > 1: - v = _('videos') - else: - v = _('video') - else: - if no_images > 1: - v = _('photos') - else: - v = _('photo') - return v - - def count_files(self): - i = 0 - for key in self._counter: - i += self._counter[key] - return i - - def summarize_file_count(self): - """ - Summarizes the total number of photos and/or videos that can be - downloaded. Displayed after a scan is finished. - """ - #Number of files, e.g. "433 photos and videos" or "23 videos". - #Displayed in the progress bar at the top of the main application - #window. - file_types_present = self.file_types_present() - file_count_summary = _("%(number)s %(filetypes)s") % \ - {'number':self.count_files(), - 'filetypes': file_types_present} - return (file_count_summary, file_types_present) - - def running_file_count(self): - """ - Displays raw numbers of photos and videos. Displayed as a scan is - occurring. - """ - return _("scanning (found %(photos)s photos and %(videos)s videos)...") % ({'photos': self.no_photos(), - 'videos': self.no_videos()}) - -class RPDFile: - """ - Base class for photo or video file, with metadata - """ - - def __init__(self, name, display_name, path, size, - file_system_modification_time, thm_full_name, - audio_file_full_name, - scan_pid, file_id): - - self.path = path - - self.name = name - self.display_name = display_name - - self.full_file_name = os.path.join(path, name) - self.extension = os.path.splitext(name)[1][1:].lower() - - self.size = size # type int - - self.modification_time = file_system_modification_time - - #full path and name of thumbnail file that is associated with some videos - self.thm_full_name = thm_full_name - - #full path and name of audio file that is associated with some photos and maybe one day videos - #think Canon 1D series of cameras - self.audio_file_full_name = audio_file_full_name - - self.status = config.STATUS_NOT_DOWNLOADED - self.problem = None # class Problem in problemnotifcation.py - - self._assign_file_type() - - self.scan_pid = scan_pid - self.file_id = file_id - self.unique_id = str(scan_pid) + ":" + file_id - - self.problem = None - self.job_code = None - - # indicates whether to generate a thumbnail during the copy - # files process - self.generate_thumbnail = False - - # generated values - - self.temp_full_file_name = '' - self.temp_thm_full_name = '' - self.temp_audio_full_name = '' - self.temp_xmp_full_name = '' - - self.download_start_time = None - - self.download_subfolder = '' - self.download_path = '' - self.download_name = '' - self.download_full_file_name = '' #file name with path - self.download_full_base_name = '' #file name with path but no extension - self.download_thm_full_name = '' #name of THM (thumbnail) file with path - self.download_xmp_full_name = '' #name of XMP sidecar with path - self.download_audio_full_name = '' #name of the WAV or MP3 audio file with path - - self.metadata = None - - # Values that will be inserted in download process -- - # (commented out because they're not needed until then) - - #self.sequences = None - #self.download_folder - #self.subfolder_pref_list = [] - #self.name_pref_list = [] - #strip_characters = False - #self.thm_extension = '' - #self.wav_extension = '' - #self.xmp_extension = '' - - #these values are set only if they were written to an xmp sidecar - #in the filemodify process - #self.new_aperture = '' - #self.new_focal_length = '' - - - def _assign_file_type(self): - self.file_type = None - - def _load_file_for_metadata(self, temp_file): - if temp_file: - return self.temp_full_file_name - else: - return self.full_file_name - - def initialize_problem(self): - self.problem = pn.Problem() - # these next values are used to display in the error log window - # the information in them can vary from other forms of display of errors - self.error_title = self.error_msg = self.error_extra_detail = '' - - def has_problem(self): - if self.problem is None: - return False - else: - return self.problem.has_problem() - - def add_problem(self, component, problem_definition, *args): - if self.problem is None: - self.initialize_problem() - self.problem.add_problem(component, problem_definition, *args) - - def add_extra_detail(self, extra_detail, *args): - self.problem.add_extra_detail(extra_detail, *args) - -class Photo(RPDFile): - - title = _("photo") - title_capitalized = _("Photo") - - def _assign_file_type(self): - self.file_type = FILE_TYPE_PHOTO - - def load_metadata(self, temp_file=False): - self.metadata = metadataphoto.MetaData(self._load_file_for_metadata(temp_file)) - try: - self.metadata.read() - except: - logger.warning("Could not read metadata from %s" % self.full_file_name) - return False - else: - return True - - -class Video(RPDFile): - - title = _("video") - title_capitalized = _("Video") - - def _assign_file_type(self): - self.file_type = FILE_TYPE_VIDEO - - def load_metadata(self, temp_file=False): - if self.extension == 'mts' or not metadatavideo.HAVE_HACHOIR: - if metadatavideo.HAVE_HACHOIR: - logger.debug("Using ExifTool parser") - self.metadata = metadataexiftool.ExifToolMetaData(self._load_file_for_metadata(temp_file)) - else: - self.metadata = metadatavideo.VideoMetaData(self._load_file_for_metadata(temp_file)) - return True - -class SamplePhoto(Photo): - def __init__(self, sample_name='IMG_0524.CR2', sequences=None): - Photo.__init__(self, name=sample_name, - display_name=sample_name, - path='/media/EOS_DIGITAL/DCIM/100EOS5D', - size=23516764, - file_system_modification_time=time.time(), - scan_pid=2033, - file_id='9873afe', - thm_full_name=None, - audio_file_full_name=None) - self.sequences = sequences - self.metadata = metadataphoto.DummyMetaData() - self.download_start_time = datetime.datetime.now() - -class SampleVideo(Video): - def __init__(self, sample_name='MVI_1379.MOV', sequences=None): - Video.__init__(self, name=sample_name, - display_name=sample_name, - path='/media/EOS_DIGITAL/DCIM/100EOS5D', - size=823513764, - file_system_modification_time=time.time(), - scan_pid=2033, - file_id='9873qrsfe', - thm_full_name=None, - audio_file_full_name=None) - self.sequences = sequences - self.metadata = metadatavideo.DummyMetaData(filename=sample_name) - self.download_start_time = datetime.datetime.now() diff --git a/rapid/rpdmultiprocessing.py b/rapid/rpdmultiprocessing.py deleted file mode 100644 index 06d69d1..0000000 --- a/rapid/rpdmultiprocessing.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: latin1 -*- -### Copyright (C) 2007-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -CONN_PARTIAL = 0 -CONN_COMPLETE = 1 - -MSG_BYTES = 0 -MSG_FILE = 1 -MSG_TEMP_DIRS = 2 - -MSG_SEQUENCE_VALUE = 0 diff --git a/rapid/scan.py b/rapid/scan.py deleted file mode 100755 index f6b7c5e..0000000 --- a/rapid/scan.py +++ /dev/null @@ -1,258 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import os -import multiprocessing -import re - -import gio -import gtk - -import pyexiv2 - -import rpdmultiprocessing as rpdmp -import rpdfile -import prefsrapid - - -import logging -logger = multiprocessing.get_logger() - -# python whitespace is significant - don't remove the leading whitespace on -# the second line - -file_attributes = "standard::name,standard::display-name,\ -standard::type,standard::size,time::modified,access::can-read,id::file" - - -def get_associated_file(full_file_name_no_ext, extensions_to_check): - - f = None - for e in extensions_to_check: - if os.path.exists(full_file_name_no_ext + '.' + e): - f = full_file_name_no_ext + '.' + e - break - if os.path.exists(full_file_name_no_ext + '.' + e.upper()): - f = full_file_name_no_ext + '.' + e.upper() - break - return f - -def get_audio_file(full_file_name_no_ext): - """ - Checks to see if an audio file of the same name is in the same directory - as the file. Expects a full path to be part of the file name. - - Returns the filename, including path, if found, else returns None. - """ - return get_associated_file(full_file_name_no_ext, rpdfile.AUDIO_EXTENSIONS) - -def get_video_THM_file(full_file_name_no_ext): - """ - Checks to see if a thumbnail file (THM) is in the same directory as the - file. Expects a full path to be part of the file name. - - Returns the filename, including path, if found, else returns None. - """ - - return get_associated_file(full_file_name_no_ext, rpdfile.VIDEO_THUMBNAIL_EXTENSIONS) - -class Scan(multiprocessing.Process): - - """Scans the given path for files of a specified type. - - Returns results in batches, finishing with a total of the size of all the - files in bytes. - """ - - def __init__(self, path, ignored_paths, use_re_ignored_paths, - batch_size, results_pipe, - terminate_queue, run_event): - - """Setup values needed to conduct the scan. - - 'path' is a string of the path to be scanned, which is passed to gio. - - 'ignored_paths' is a list of paths that should not be scanned. Any path - ending with one of the values will be ignored. - - 'use_re_ignored_paths': if true, pytho regular expressions will be used - to determine which paths to ignore - - 'batch_size' is the number of files that should be sent back to the - calling function at one time. - - 'results_pipe' is a connection on which to send the results. - - 'terminate_queue' is a queue whose sole purpose is to notify the - process that it should terminate and not return any results. - - 'run_event' is an Event that is used to temporarily halt execution. - - """ - - multiprocessing.Process.__init__(self) - self.path = path - self.ignored_paths = ignored_paths - self.use_re_ignored_paths = use_re_ignored_paths - self.results_pipe = results_pipe - self.terminate_queue = terminate_queue - self.run_event = run_event - self.batch_size = batch_size - self.counter = 0 - self.files_scanned = 0 - self.files = [] - self.file_type_counter = rpdfile.FileTypeCounter() - - def _gio_scan(self, path, file_size_sum): - """recursive function to scan a directory and its subdirectories - for photos and possibly videos""" - - children = path.enumerate_children(file_attributes) - - for child in children: - - # pause if instructed by the caller - self.run_event.wait() - - if not self.terminate_queue.empty(): - x = self.terminate_queue.get() - # terminate immediately - logger.info("terminating scan...") - self.files = [] - return None - - # only collect files and scan in directories we can actually read - # cannot assume that users will download only from memory cards - - if child.get_attribute_boolean(gio.FILE_ATTRIBUTE_ACCESS_CAN_READ): - file_type = child.get_file_type() - name = child.get_name() - if file_type == gio.FILE_TYPE_DIRECTORY: - if not self.ignore_this_path(name): - file_size_sum = self._gio_scan(path.get_child(name), - file_size_sum) - if file_size_sum is None: - return None - - elif file_type == gio.FILE_TYPE_REGULAR: - - self.files_scanned += 1 - if self.files_scanned % 100 == 0: - logger.debug("Scanned %s files", self.files_scanned) - - base_name, ext = os.path.splitext(name) - ext = ext.lower()[1:] - - file_type = rpdfile.file_type(ext) - if file_type is not None: - file_id = child.get_attribute_string( - gio.FILE_ATTRIBUTE_ID_FILE) - if file_id is not None: - # count how many files of each type are included - # e.g. photo, video - self.file_type_counter.add(file_type) - self.counter += 1 - display_name = child.get_display_name() - size = child.get_size() - modification_time = child.get_modification_time() - path_name = path.get_path() - - # look for thumbnail file for videos - if file_type == rpdfile.FILE_TYPE_VIDEO: - thm_full_name = get_video_THM_file(os.path.join(path_name, base_name)) - else: - thm_full_name = None - - # check if an audio file is associated with the photo or video - audio_file_full_name = get_audio_file(os.path.join(path_name, base_name)) - - scanned_file = rpdfile.get_rpdfile(ext, - name, - display_name, - path_name, - size, - modification_time, - thm_full_name, - audio_file_full_name, - self.pid, - file_id, - file_type) - - self.files.append(scanned_file) - file_size_sum += size - - if self.counter == self.batch_size: - # send batch of results - self.results_pipe.send((rpdmp.CONN_PARTIAL, - (file_size_sum, - self.file_type_counter, - self.pid, - self.files))) - self.files = [] - self.counter = 0 - - return file_size_sum - - - def run(self): - """start the actual scan.""" - - if self.use_re_ignored_paths and len(self.ignored_paths): - self.re_pattern = prefsrapid.check_and_compile_re(self.ignored_paths) - - source = gio.File(self.path) - try: - if not self.ignore_this_path(self.path): - size = self._gio_scan(source, 0) - else: - size = None - except gio.Error, inst: - logger.error("Error while scanning %s: %s", self.path, inst) - size = None - - if size is not None: - if self.counter > 0: - # send any remaining results - self.results_pipe.send((rpdmp.CONN_PARTIAL, (size, - self.file_type_counter, - self.pid, - self.files))) - - self.results_pipe.send((rpdmp.CONN_COMPLETE, (size, - self.file_type_counter, self.pid))) - self.results_pipe.close() - - def ignore_this_path(self, path): - """ - determines if the path should be ignored according to the preferences - chosen by the user - """ - - if len(self.ignored_paths): - if self.use_re_ignored_paths and self.re_pattern: - # regular expressions are being used - if self.re_pattern.match(path): - return True - else: - # regular expressions are not being used - if path.endswith(tuple(self.ignored_paths)): - return True - - return False diff --git a/rapid/subfolderfile.py b/rapid/subfolderfile.py deleted file mode 100644 index c81a119..0000000 --- a/rapid/subfolderfile.py +++ /dev/null @@ -1,602 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -""" -Generates names for files and folders. - -Runs as a daemon process. -""" - -import os, datetime, collections - -#~ import shutil -import errno -import multiprocessing -import logging -logger = multiprocessing.get_logger() - - -import rpdfile -import rpdmultiprocessing as rpdmp -import generatename as gn -import problemnotification as pn -import prefsrapid -import config - -from gettext import gettext as _ - -class SyncRawJpeg: - def __init__(self): - self.photos = {} - - def add_download(self, name, extension, date_time, sub_seconds, sequence_number_used): - if name not in self.photos: - self.photos[name] = ([extension], date_time, sub_seconds, sequence_number_used) - else: - if extension not in self.photos[name][0]: - self.photos[name][0].append(extension) - - - def matching_pair(self, name, extension, date_time, sub_seconds): - """Checks to see if the image matches an image that has already been downloaded. - Image name (minus extension), exif date time, and exif subseconds are checked. - - Returns -1 and a sequence number if the name, extension, and exif values match (i.e. it has already been downloaded) - Returns 0 and a sequence number if name and exif values match, but the extension is different (i.e. a matching RAW + JPG image) - Returns -99 and a sequence number of None if photos detected with the same filenames, but taken at different times - Returns 1 and a sequence number of None if no match""" - - if name in self.photos: - if self.photos[name][1] == date_time and self.photos[name][2] == sub_seconds: - if extension in self.photos[name][0]: - return (-1, self.photos[name][3]) - else: - return (0, self.photos[name][3]) - else: - return (-99, None) - return (1, None) - - def ext_exif_date_time(self, name): - """Returns first extension, exif date time and subseconds data for the already downloaded photo""" - return (self.photos[name][0][0], self.photos[name][1], self.photos[name][2]) - -def time_subseconds_human_readable(date, subseconds): - return _("%(hour)s:%(minute)s:%(second)s:%(subsecond)s") % \ - {'hour':date.strftime("%H"), - 'minute':date.strftime("%M"), - 'second':date.strftime("%S"), - 'subsecond': subseconds} - -def load_metadata(rpd_file, temp_file=True): - """ - Loads the metadata for the file. Returns True if operation succeeded, false - otherwise - - If temp_file is true, the the metadata from the temporary file rather than - the original source file is used. This is important, because the metadata - can be modified by the filemodify process. - """ - if rpd_file.metadata is None: - if not rpd_file.load_metadata(temp_file): - # Error in reading metadata - rpd_file.add_problem(None, pn.CANNOT_DOWNLOAD_BAD_METADATA, {'filetype': rpd_file.title_capitalized}) - return False - return True - - -def _generate_name(generator, rpd_file): - - do_generation = True - if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO: - do_generation = load_metadata(rpd_file) - else: - if rpd_file.metadata is None: - rpd_file.load_metadata() - - if do_generation: - value = generator.generate_name(rpd_file) - if value is None: - value = '' - else: - value = '' - - return value - -def generate_subfolder(rpd_file): - - if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO: - generator = gn.PhotoSubfolder(rpd_file.subfolder_pref_list) - else: - generator = gn.VideoSubfolder(rpd_file.subfolder_pref_list) - - rpd_file.download_subfolder = _generate_name(generator, rpd_file) - return rpd_file - -def generate_name(rpd_file): - do_generation = True - - if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO: - generator = gn.PhotoName(rpd_file.name_pref_list) - else: - generator = gn.VideoName(rpd_file.name_pref_list) - - rpd_file.download_name = _generate_name(generator, rpd_file) - return rpd_file - - -class SubfolderFile(multiprocessing.Process): - def __init__(self, results_pipe, sequence_values): - multiprocessing.Process.__init__(self) - self.daemon = True - self.results_pipe = results_pipe - - self.downloads_today = sequence_values[0] - self.downloads_today_date = sequence_values[1] - self.day_start = sequence_values[2] - self.refresh_downloads_today = sequence_values[3] - self.stored_sequence_no = sequence_values[4] - self.uses_stored_sequence_no = sequence_values[5] - self.uses_session_sequece_no = sequence_values[6] - self.uses_sequence_letter = sequence_values[7] - - logger.debug("Start of day is set to %s", self.day_start.value) - - def progress_callback_no_update(self, amount_downloaded, total): - pass - #~ if debug_progress: - #~ logger.debug("%.1f", amount_downloaded / float(total)) - - def file_exists(self, rpd_file, identifier=None): - """ - Notify user that the download file already exists - """ - # get information on when the existing file was last modified - try: - modification_time = os.path.getmtime(rpd_file.download_full_file_name) - dt = datetime.datetime.fromtimestamp(modification_time) - date = dt.strftime("%x") - time = dt.strftime("%X") - except: - logger.warning("Could not determine the file modification time of %s", - rpd_file.download_full_file_name) - date = time = '' - - if not identifier: - rpd_file.add_problem(None, pn.FILE_ALREADY_EXISTS_NO_DOWNLOAD, - {'filetype':rpd_file.title_capitalized}) - rpd_file.add_extra_detail(pn.EXISTING_FILE, - {'filetype': rpd_file.title, - 'date': date, 'time': time}) - rpd_file.status = config.STATUS_DOWNLOAD_FAILED - rpd_file.error_extra_detail = pn.extra_detail_definitions[pn.EXISTING_FILE] % \ - {'date':date, 'time':time, 'filetype': rpd_file.title} - else: - rpd_file.add_problem(None, pn.UNIQUE_IDENTIFIER_ADDED, - {'filetype':rpd_file.title_capitalized}) - rpd_file.add_extra_detail(pn.UNIQUE_IDENTIFIER, - {'identifier': identifier, - 'filetype': rpd_file.title, - 'date': date, 'time': time}) - rpd_file.status = config.STATUS_DOWNLOADED_WITH_WARNING - rpd_file.error_extra_detail = pn.extra_detail_definitions[pn.UNIQUE_IDENTIFIER] % \ - {'identifier': identifier, 'filetype': rpd_file.title, - 'date': date, 'time': time} - rpd_file.error_title = rpd_file.problem.get_title() - rpd_file.error_msg = _("Source: %(source)s\nDestination: %(destination)s") \ - % {'source': rpd_file.full_file_name, - 'destination': rpd_file.download_full_file_name} - return rpd_file - - def download_failure_file_error(self, rpd_file, inst): - """ - Handle cases where file failed to download - """ - rpd_file.add_problem(None, pn.DOWNLOAD_COPYING_ERROR, {'filetype': rpd_file.title}) - rpd_file.add_extra_detail(pn.DOWNLOAD_COPYING_ERROR_DETAIL, inst) - rpd_file.status = config.STATUS_DOWNLOAD_FAILED - logger.error("Failed to create file %s: %s", rpd_file.download_full_file_name, inst) - - rpd_file.error_title = rpd_file.problem.get_title() - rpd_file.error_msg = _("%(problem)s\nFile: %(file)s") % \ - {'problem': rpd_file.problem.get_problems(), - 'file': rpd_file.full_file_name} - - return rpd_file - - def download_file_exists(self, rpd_file): - """ - Check how to handle a download file already existing - """ - if (rpd_file.download_conflict_resolution == - config.ADD_UNIQUE_IDENTIFIER): - add_unique_identifier = True - logger.debug("Will add unique identifier to avoid duplicate filename") - else: - rpd_file = self.file_exists(rpd_file) - add_unique_identifier = False - return (rpd_file, add_unique_identifier) - - def added_unique_identifier(self, rpd_file): - """ - Track fact that a unique identifier was added to a file name - """ - move_succeeded = True - suffix_already_used = False - rpd_file = self.file_exists(rpd_file, identifier) - logger.error("%s: %s - %s", rpd_file.full_file_name, - rpd_file.problem.get_title(), - rpd_file.problem.get_problems()) - return (rpd_file, move_succeeded, suffix_already_used) - - def same_name_different_exif(self, sync_photo_name, rpd_file): - """Notify the user that a file was already downloaded with the same name, but the exif information was different""" - i1_ext, i1_date_time, i1_subseconds = self.sync_raw_jpeg.ext_exif_date_time(sync_photo_name) - detail = {'image1': "%s%s" % (sync_photo_name, i1_ext), - 'image1_date': i1_date_time.strftime("%x"), - 'image1_time': time_subseconds_human_readable(i1_date_time, i1_subseconds), - 'image2': rpd_file.name, - 'image2_date': rpd_file.metadata.date_time().strftime("%x"), - 'image2_time': time_subseconds_human_readable( - rpd_file.metadata.date_time(), - rpd_file.metadata.sub_seconds())} - rpd_file.add_problem(None, pn.SAME_FILE_DIFFERENT_EXIF, detail) - - rpd_file.error_title = _('Photos detected with the same filenames, but taken at different times') - rpd_file.error_msg = pn.problem_definitions[pn.SAME_FILE_DIFFERENT_EXIF][1] % detail - rpd_file.status = config.STATUS_DOWNLOADED_WITH_WARNING - return rpd_file - - - def _move_associate_file(self, extension, full_base_name, temp_associate_file): - """Move (rename) the associate file using the pregenerated name - - Returns tuple of result (True if succeeded, False otherwise) and - full path and filename""" - - download_full_name = full_base_name + extension - - # move (rename) associate file - try: - # don't check to see if it already exists - os.rename(temp_associate_file, download_full_name) - success = True - except: - success = False - - return (success, download_full_name) - - def move_thm_file(self, rpd_file): - """Move (rename) the THM thumbnail file using the pregenerated name""" - ext = None - if hasattr(rpd_file, 'thm_extension'): - if rpd_file.thm_extension: - ext = rpd_file.thm_extension - if ext is None: - ext = '.THM' - - result, rpd_file.download_thm_full_name = self._move_associate_file(ext, rpd_file.download_full_base_name, rpd_file.temp_thm_full_name) - - if not result: - logger.error("Failed to move video THM file %s", rpd_file.download_thm_full_name) - - return rpd_file - - def move_audio_file(self, rpd_file): - """Move (rename) the associate audio file using the pregenerated name""" - ext = None - if hasattr(rpd_file, 'audio_extension'): - if rpd_file.audio_extension: - ext = rpd_file.audio_extension - if ext is None: - ext = '.WAV' - - result, rpd_file.download_audio_full_name = self._move_associate_file(ext, rpd_file.download_full_base_name, rpd_file.temp_audio_full_name) - - if not result: - logger.error("Failed to move file's associated audio file %s", rpd_file.download_audio_full_name) - - return rpd_file - - - def check_for_fatal_name_generation_errors(self, rpd_file): - """Returns False if either the download subfolder or filename are blank - - Else returns True""" - - if not rpd_file.download_subfolder or not rpd_file.download_name: - if not rpd_file.download_subfolder and not rpd_file.download_name: - area = _("subfolder and filename") - elif not rpd_file.download_name: - area = _("filename") - else: - area = _("subfolder") - rpd_file.add_problem(None, pn.ERROR_IN_NAME_GENERATION, {'filetype': rpd_file.title_capitalized, 'area': area}) - rpd_file.add_extra_detail(pn.NO_DATA_TO_NAME, {'filetype': area}) - rpd_file.status = config.STATUS_DOWNLOAD_FAILED - - rpd_file.error_title = rpd_file.problem.get_title() - rpd_file.error_msg = _("%(problem)s\nFile: %(file)s") % \ - {'problem': rpd_file.problem.get_problems(), - 'file': rpd_file.full_file_name} - return False - else: - return True - - def run(self): - """ - Get subfolder and name. - Attempt to move the file from it's temporary directory. - Move video THM file if there is one. - If successful, increment sequence values. - Report any success or failure. - """ - i = 0 - download_count = 0 - - duplicate_files = {} - - - # Track downloads today, using a class whose purpose is to - # take the value in the user prefs, increment, and then be used - # to update the prefs (which can only happen via the main process) - self.downloads_today_tracker = prefsrapid.DownloadsTodayTracker( - day_start = self.day_start.value, - downloads_today = self.downloads_today.value, - downloads_today_date = self.downloads_today_date.value) - - # Track sequences using shared downloads today and stored sequence number - # (shared with main process) - self.sequences = gn.Sequences(self.downloads_today_tracker, - self.stored_sequence_no.value) - - self.sync_raw_jpeg = SyncRawJpeg() - - - while True: - if download_count: - logger.debug("Finished %s. Getting next task.", download_count) - - # rename file and move to generated subfolder - download_succeeded, download_count, rpd_file = self.results_pipe.recv() - - move_succeeded = False - - - if download_succeeded: - - synchronize_raw_jpg_failed = False - if not (rpd_file.synchronize_raw_jpg and - rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO): - synchronize_raw_jpg = False - sequence_to_use = None - else: - synchronize_raw_jpg = True - sync_photo_name, sync_photo_ext = os.path.splitext(rpd_file.name) - if not load_metadata(rpd_file): - synchronize_raw_jpg_failed = True - rpd_file.status = config.STATUS_DOWNLOAD_FAILED - self.check_for_fatal_name_generation_errors(rpd_file) - else: - j, sequence_to_use = self.sync_raw_jpeg.matching_pair( - name=sync_photo_name, extension=sync_photo_ext, - date_time=rpd_file.metadata.date_time(), - sub_seconds=rpd_file.metadata.sub_seconds()) - if j == -1: - # this exact file has already been downloaded (same extension, same filename, and same exif date time subsecond info) - if (rpd_file.download_conflict_resolution <> - config.ADD_UNIQUE_IDENTIFIER): - rpd_file.add_problem(None, pn.FILE_ALREADY_DOWNLOADED, {'filetype': rpd_file.title_capitalized}) - rpd_file.error_title = _('Photo has already been downloaded') - rpd_file.error_msg = _("Source: %(source)s") % {'source': rpd_file.full_file_name} - rpd_file.status = config.STATUS_DOWNLOAD_FAILED - synchronize_raw_jpg_failed = True - else: - self.sequences.set_matched_sequence_value(sequence_to_use) - if j == -99: - rpd_file = self.same_name_different_exif(sync_photo_name, rpd_file) - - if synchronize_raw_jpg_failed: - generation_succeeded = False - else: - # Generate subfolder name and new file name - generation_succeeded = True - - if rpd_file.file_type == rpdfile.FILE_TYPE_PHOTO: - if hasattr(rpd_file, 'new_focal_length'): - # A RAW file has had its focal length and aperture adjusted. - # These have been written out to an XMP sidecar, but they won't - # be picked up by pyexiv2. So temporarily change the values inplace here, - # without saving them. - if load_metadata(rpd_file): - rpd_file.metadata["Exif.Photo.FocalLength"] = rpd_file.new_focal_length - rpd_file.metadata["Exif.Photo.FNumber"] = rpd_file.new_aperture - - rpd_file = generate_subfolder(rpd_file) - - - if rpd_file.download_subfolder: - logger.debug("Generated subfolder name %s for file %s", rpd_file.download_subfolder, rpd_file.name) - - if self.refresh_downloads_today.value: - # overwrite downloads today value tracked here, - # as user has modified their preferences - self.downloads_today_tracker.set_raw_downloads_today_from_int(self.downloads_today.value) - self.downloads_today_tracker.set_raw_downloads_today_date(self.downloads_today_date.value) - self.downloads_today_tracker.day_start = self.day_start.value - self.refresh_downloads_today.value = False - - # update whatever the stored value is - self.sequences.stored_sequence_no = self.stored_sequence_no.value - rpd_file.sequences = self.sequences - - # generate the file name - rpd_file = generate_name(rpd_file) - - if rpd_file.has_problem(): - logger.debug("Encountered a problem generating file name for file %s", rpd_file.name) - rpd_file.status = config.STATUS_DOWNLOADED_WITH_WARNING - rpd_file.error_title = rpd_file.problem.get_title() - rpd_file.error_msg = _("%(problem)s\nFile: %(file)s") % \ - {'problem': rpd_file.problem.get_problems(), - 'file': rpd_file.full_file_name} - else: - logger.debug("Generated file name %s for file %s", rpd_file.download_name, rpd_file.name) - else: - logger.debug("Failed to generate subfolder name for file: %s", rpd_file.name) - - # Check for any errors - generation_succeeded = self.check_for_fatal_name_generation_errors(rpd_file) - - - - - if generation_succeeded: - rpd_file.download_path = os.path.join(rpd_file.download_folder, rpd_file.download_subfolder) - rpd_file.download_full_file_name = os.path.join(rpd_file.download_path, rpd_file.download_name) - rpd_file.download_full_base_name = os.path.splitext(rpd_file.download_full_file_name)[0] - - logger.debug("Probing to see if subfolder already exists...") - if not os.path.isdir(rpd_file.download_path): - try: - logger.debug("...subfolder doesn't exist: creating it...") - os.makedirs(rpd_file.download_path) - logger.debug("...subfolder created") - except IOError as inst: - if inst.errno <> errno.EEXIST: - logger.error("Failed to create download subfolder: %s", rpd_file.download_path) - logger.error(inst) - rpd_file.error_title = _("Failed to create download subfolder") - rpd_file.error_msg = _("Path: %s") % rpd_file.download_path - else: - logger.debug("...subfolder already exists") - - # Move temp file to subfolder - - add_unique_identifier = False - # Use python library functions to rename file - try: - if os.path.exists(rpd_file.download_full_file_name): - raise IOError(errno.EEXIST, "File exists: %s" % rpd_file.download_full_file_name) - logger.debug("Attempting to rename file %s to %s .....", rpd_file.temp_full_file_name, rpd_file.download_full_file_name) - os.rename(rpd_file.temp_full_file_name, rpd_file.download_full_file_name) - logger.debug("....successfully renamed file") - move_succeeded = True - if rpd_file.status <> config.STATUS_DOWNLOADED_WITH_WARNING: - rpd_file.status = config.STATUS_DOWNLOADED - except (IOError, OSError) as inst: - if inst.errno == errno.EEXIST: - rpd_file, add_unique_identifier = self.download_file_exists(rpd_file) - else: - rpd_file = self.download_failure_file_error(rpd_file, inst.strerror) - except: - rpd_file = self.download_failure_file_error(rpd_file, "An unknown error occurred while renaming the file") - - if add_unique_identifier: - name = os.path.splitext(rpd_file.download_name) - full_name = rpd_file.download_full_file_name - suffix_already_used = True - while suffix_already_used: - duplicate_files[full_name] = duplicate_files.get( - full_name, 0) + 1 - identifier = '_%s' % duplicate_files[full_name] - rpd_file.download_name = name[0] + identifier + name[1] - rpd_file.download_full_file_name = os.path.join( - rpd_file.download_path, - rpd_file.download_name) - - try: - if os.path.exists(rpd_file.download_full_file_name): - raise IOError(errno.EEXIST, "File exists: %s" % rpd_file.download_full_file_name) - os.rename(rpd_file.temp_full_file_name, rpd_file.download_full_file_name) - rpd_file, move_succeeded, suffix_already_used = self.added_unique_identifier(rpd_file) - except IOError as inst: - if inst.errno <> errno.EEXIST: - rpd_file = self.download_failure_file_error(rpd_file, inst) - break - except: - rpd_file = self.download_failure_file_error(rpd_file, inst) - break - - - - - logger.debug("Finish processing file: %s", download_count) - - if move_succeeded: - if synchronize_raw_jpg: - if sequence_to_use is None: - sequence = self.sequences.create_matched_sequences() - else: - sequence = sequence_to_use - self.sync_raw_jpeg.add_download(name=sync_photo_name, - extension=sync_photo_ext, - date_time=rpd_file.metadata.date_time(), - sub_seconds=rpd_file.metadata.sub_seconds(), - sequence_number_used=sequence) - if sequence_to_use is None: - if self.uses_session_sequece_no.value or self.uses_sequence_letter.value: - self.sequences.increment( - self.uses_session_sequece_no.value, - self.uses_sequence_letter.value) - if self.uses_stored_sequence_no.value: - self.stored_sequence_no.value += 1 - self.downloads_today_tracker.increment_downloads_today() - self.downloads_today.value = self.downloads_today_tracker.get_raw_downloads_today() - self.downloads_today_date.value = self.downloads_today_tracker.get_raw_downloads_today_date() - - if rpd_file.temp_thm_full_name: - rpd_file = self.move_thm_file(rpd_file) - - if rpd_file.temp_audio_full_name: - rpd_file = self.move_audio_file(rpd_file) - - if rpd_file.temp_xmp_full_name: - # copy and rename XMP sidecar file - # generate_name() has generated xmp extension with correct capitalization - download_xmp_full_name = rpd_file.download_full_base_name + rpd_file.xmp_extension - - try: - os.rename(rpd_file.temp_xmp_full_name, download_xmp_full_name) - rpd_file.download_xmp_full_name = download_xmp_full_name - except: - logger.error("Failed to move XMP sidecar file %s", download_xmp_full_name) - - - if not move_succeeded: - logger.error("%s: %s - %s", rpd_file.full_file_name, - rpd_file.problem.get_title(), - rpd_file.problem.get_problems()) - try: - os.remove(rpd_file.temp_full_file_name) - except: - logger.error("Failed to delete temporary file %s", rpd_file.temp_full_file_name) - - - - - - rpd_file.metadata = None #purge metadata, as it cannot be pickled - rpd_file.sequences = None - self.results_pipe.send((move_succeeded, rpd_file, download_count)) - - i += 1 - - - diff --git a/rapid/tableplusminus.py b/rapid/tableplusminus.py deleted file mode 100644 index b730092..0000000 --- a/rapid/tableplusminus.py +++ /dev/null @@ -1,285 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2007-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -# import gtk.gdk as gdk -import sys -try: - import pygtk - pygtk.require("2.0") -except: - pass -try: - import gtk -except: - sys.exit(1) - -import higdefaults as hd - -class TablePlusMinus(gtk.Table): - """ - A regular gtk table which allows users to add and delete rows to the table. - - Users add and delete rows by using plus and minus buttons. - The buttons (minus first) are in the two rightmost colums. - The user can never delete a table so it has no rows. - """ - - debug = False # if True, then debugging info for the developer is displayed - def __init__(self, rows=1, columns=1, homogeneous=False): - if not self.debug: - gtk.Table.__init__(self, rows, columns + 2, homogeneous) - self.extra_cols = 2 # representing minus and plus buttons - else: - gtk.Table.__init__(self, rows, columns + 3, homogeneous) - self.extra_cols = 3 # representing minus and plus buttons, and info label - - # no of columns NOT including the + and - buttons - self.pm_no_columns = columns - # how many rows there are in the gtk.Table - self.pm_no_rows = rows - # list of widgets in the gtk.Table - self.pm_rows = [] - # dict of callback ids for minus and plus buttons - self.pm_callbacks = {} - - #spacing of controls - for i in range(columns-1): - self.set_col_spacing(i, hd.CONTROL_IN_TABLE_SPACE) - self.set_col_spacing(columns-1, hd.CONTROL_IN_TABLE_SPACE*2) - self.set_col_spacing(columns, hd.CONTROL_IN_TABLE_SPACE) - if self.debug: - self.set_col_spacing(columns+1, hd.CONTROL_IN_TABLE_SPACE) - self.set_row_spacings(hd.CONTROL_IN_TABLE_SPACE) - - def _set_minus_button_sensitivity(self): - button = self.pm_rows[0][self.pm_no_columns] - if len(self.pm_rows) == 1: - button.set_sensitive(False) - else: - button.set_sensitive(True) - - def _create_minus_plus_buttons(self, row_position): - plus_button = gtk.Button() - plus_button.set_image(gtk.image_new_from_stock(gtk.STOCK_ADD, gtk.ICON_SIZE_MENU)) - self._create_callback(plus_button, row_position, 'clicked', self.on_plus_button_clicked) - minus_button = gtk.Button() - minus_button.set_image(gtk.image_new_from_stock(gtk.STOCK_REMOVE, gtk.ICON_SIZE_MENU)) - self._create_callback(minus_button, row_position, 'clicked', self.on_minus_button_clicked) - - return minus_button, plus_button - - - - def append(self, row): - self.insert_after(len(self.pm_rows)-1, row) - - - def _get_minus_and_plus_buttons_for_row(self, row_position): - """ - Return as a tuple minus and plus buttons for the row specified by row_position - """ - return (self.pm_rows[row_position][self.pm_no_columns], self.pm_rows[row_position][self.pm_no_columns+1]) - - def remove_row(self, row_position): - # remove widgets from table - for col in range(self.pm_no_columns + self.extra_cols): - widget = self.pm_rows[row_position][col] - if widget: - self.remove(widget) - if self.pm_callbacks.has_key(widget): - widget.disconnect(self.pm_callbacks[widget]) - del self.pm_callbacks[widget] - - - # reposition existing rows in gtk.Table - self._move_rows(-1, row_position + 1) - # remove row from list of rows - del self.pm_rows[row_position] - - self._set_minus_button_sensitivity() - self.pm_no_rows -= 1 - self.resize(self.pm_no_rows, self.pm_no_columns + self.extra_cols) - self._print_debug_info() - - - def _create_callback(self, widget, row_position, callback_type = None, callbackMethod=None): - if callback_type: - self.pm_callbacks[widget] = widget.connect(callback_type, callbackMethod, row_position) - else: - name = widget.get_name() - if name == 'GtkComboBox': - self.pm_callbacks[widget] = widget.connect("changed", self.on_combobox_changed, row_position) - elif name == 'GtkEntry': - self.pm_callbacks[widget] = widget.connect("changed", self.on_entry_changed, row_position) - - - def _move_rows(self, adjustment, start_row, end_row = -1): - """ - Moves gtk.Table rows up or down according to adjustment (which MUST be -1 or 1). - - Starts at row start_row and ends at row end_row. If end_row == -1, then goes to last row in table. - Readjusts callbacks. - """ - if end_row == -1: - end_row = len(self.pm_rows) - for r in range(start_row, end_row): - if self.debug: - print "Row %s becomes row %s" % (self.pm_rows[r][self.pm_no_columns + 2].get_label(), r + adjustment) - self.pm_rows[r][self.pm_no_columns + 2].set_label(str(r + adjustment)) - - for col in range(self.pm_no_columns + self.extra_cols): - widget = self.pm_rows[r][col] - if widget: - self.remove(widget) - widget.disconnect(self.pm_callbacks[widget]) - self.attach(widget, col, col+1, r + adjustment, r + adjustment + 1) - if col == self.pm_no_columns: - self._create_callback(widget, r + adjustment, 'clicked', self.on_minus_button_clicked) - elif col == self.pm_no_columns + 1: - self._create_callback(widget, r + adjustment, 'clicked', self.on_plus_button_clicked) - else: - self._create_callback(widget, r + adjustment) - - - def _print_debug_info(self): - if self.debug: - print "\nRows in internal list: %s\nTable rows: %s" % \ - (len(self.pm_rows), self.pm_no_rows) - - if len(self.pm_rows) <> self.pm_no_rows: - print "|\n\\\n --> Unequal no. of rows" - - - - def attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions=gtk.EXPAND|gtk.FILL, - yoptions=gtk.SHRINK, xpadding=0, ypadding=0): - """ - Override base class attach method, to allow automatic shrinking of minus and plus buttons - """ - if left_attach >= self.pm_no_columns and left_attach <= self.pm_no_columns + 1: - # since we are adding plus or minus button, shrink the button - gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, gtk.SHRINK, gtk.SHRINK, xpadding, ypadding) - else: - gtk.Table.attach(self, child, left_attach, right_attach, top_attach, bottom_attach, xoptions, yoptions, xpadding, ypadding) - - - def insert_after(self, row_position, row): - """ - Inserts row into the table at row following row_position - """ - - - #is table big enough? - self.check_table_rows_and_adjust() - - #move (reattach) other widgets & readjust connect - self._move_rows(1, row_position + 1) - - # insert row - for col in range(self.pm_no_columns): - widget = row[col] - if widget: - self._create_callback(widget, row_position+1) - self.attach(widget, col, col+1, row_position+1, row_position+2) - - minus_button, plus_button = self._create_minus_plus_buttons(row_position+1) - - row.append(minus_button) - row.append(plus_button) - self.attach(minus_button, self.pm_no_columns, self.pm_no_columns+1, row_position+1, row_position+2) - self.attach(plus_button, self.pm_no_columns+1, self.pm_no_columns+2, row_position+1, row_position+2) - - if self.debug: - label = gtk.Label(str(row_position+1)) - self.attach(label, self.pm_no_columns+2, self.pm_no_columns+3, row_position+1, row_position+2) - row.append(label) - - - for widget in row: - if widget: - widget.show() - - #adjust internal reference table - - self.pm_rows.insert(row_position + 1, row) - - self._set_minus_button_sensitivity() - - self._print_debug_info() - - def check_table_rows_and_adjust(self, no_rows_to_add=1, adjust_rows=True): - no_rows_ok = True - if len(self.pm_rows) + no_rows_to_add > self.pm_no_rows: - if adjust_rows: - extra_rows_to_add = len(self.pm_rows) + no_rows_to_add - self.pm_no_rows - self.pm_no_rows += extra_rows_to_add - self.resize(self.pm_no_rows, self.pm_no_columns + self.extra_cols) - else: - no_rows_ok = False - return no_rows_ok - - def get_default_row(self): - """ - Returns a list of default widgets to insert as a row into the table. - - Expected to be implemented in derived class. - """ - - return [None] * self.pm_no_columns - - def on_combobox_changed(self, widget, row_position): - """ - Callback for combobox that is expected to be implemented in derived class - """ - pass - - def on_entry_changed(self, widget, row_position): - """ - Callback for entry that is expected to be implemented in derived class - """ - pass - - def _debug_button_pressed(self, buttonText, row_position): - if self.debug: - t = datetime.datetime.now().strftime("%H:%M:%S") - print "\n****\n%s\n\n%s clicked at %s" %(t, buttonText, row_position) - - def on_minus_button_clicked(self, widget, row_position): - self._debug_button_pressed("Minus", row_position) - self.remove_row(row_position) - self.on_row_deleted(row_position) - - def on_plus_button_clicked(self, widget, row_position): - self._debug_button_pressed("Plus", row_position) - self.insert_after(row_position, self.get_default_row()) - self.on_row_added(row_position) - - def on_row_added(self, row_position): - """ - Expected to be implemented in derived class - """ - pass - - def on_row_deleted(self, row_position): - """ - Expected to be implemented in derived class - """ - pass - diff --git a/rapid/thumbnail.py b/rapid/thumbnail.py deleted file mode 100644 index e10ae3d..0000000 --- a/rapid/thumbnail.py +++ /dev/null @@ -1,381 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2011-2014 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import multiprocessing -import types -import os - -import gtk - -import paths - -from PIL import Image -import cStringIO -import tempfile -import subprocess - -import rpdfile - -import rpdmultiprocessing as rpdmp -from utilities import image_to_pixbuf, pixbuf_to_image -import pyexiv2 - -from filmstrip import add_filmstrip - -import logging -logger = multiprocessing.get_logger() - - -def get_stock_photo_image(): - length = min(gtk.gdk.screen_width(), gtk.gdk.screen_height()) - pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(paths.share_dir('glade3/photo.svg'), length, length) - image = pixbuf_to_image(pixbuf) - return image - -def get_stock_photo_image_icon(): - image = Image.open(paths.share_dir('glade3/photo66.png')) - image = image.convert("RGBA") - return image - -def get_stock_video_image(): - length = min(gtk.gdk.screen_width(), gtk.gdk.screen_height()) - pixbuf = gtk.gdk.pixbuf_new_from_file_at_size(paths.share_dir('glade3/video.svg'), length, length) - image = pixbuf_to_image(pixbuf) - return image - -def get_stock_video_image_icon(): - image = Image.open(paths.share_dir('glade3/video66.png')) - image = image.convert("RGBA") - return image - - -class PhotoIcons(): - stock_thumbnail_image_icon = get_stock_photo_image_icon() - -class VideoIcons(): - stock_thumbnail_image_icon = get_stock_video_image_icon() - -def upsize_pil(image, size): - width_max = size[0] - height_max = size[1] - width_orig = float(image.size[0]) - height_orig = float(image.size[1]) - if (width_orig / width_max) > (height_orig / height_max): - height = int((height_orig / width_orig) * width_max) - width = width_max - else: - width = int((width_orig / height_orig) * height_max) - height=height_max - - return image.resize((width, height), Image.ANTIALIAS) - -def downsize_pil(image, box, fit=False): - """Downsample the PIL image. - image: Image - an Image-object - box: tuple(x, y) - the bounding box of the result image - fix: boolean - crop the image to fill the box - - Code adpated from example by Christian Harms - Source: http://united-coders.com/christian-harms/image-resizing-tips-every-coder-should-know - """ - #preresize image with factor 2, 4, 8 and fast algorithm - factor = 1 - logger.debug("Image size %sx%s", image.size[0], image.size[1]) - logger.debug("Box size %sx%s", box[0],box[1]) - while image.size[0]/factor > 2*box[0] and image.size[1]*2/factor > 2*box[1]: - factor *=2 - if factor > 1: - logger.debug("quick resize %sx%s", image.size[0]/factor, image.size[1]/factor) - image.thumbnail((image.size[0]/factor, image.size[1]/factor), Image.NEAREST) - logger.debug("did first thumbnail") - - #calculate the cropping box and get the cropped part - if fit: - x1 = y1 = 0 - x2, y2 = image.size - wRatio = 1.0 * x2/box[0] - hRatio = 1.0 * y2/box[1] - if hRatio > wRatio: - y1 = y2/2-box[1]*wRatio/2 - y2 = y2/2+box[1]*wRatio/2 - else: - x1 = x2/2-box[0]*hRatio/2 - x2 = x2/2+box[0]*hRatio/2 - image = image.crop((x1,y1,x2,y2)) - - #Resize the image with best quality algorithm ANTI-ALIAS - logger.debug("about to actually downsize using image.thumbnail") - image.thumbnail(box, Image.ANTIALIAS) - logger.debug("it downsized") - -class PicklablePIL: - def __init__(self, image): - self.size = image.size - self.mode = image.mode - self.image_data = image.tobytes() - - def get_image(self): - return Image.frombytes(self.mode, self.size, self.image_data) - - def get_pixbuf(self): - return image_to_pixbuf(self.get_image()) - -class Thumbnail: - - # file types from which to remove letterboxing (black bands in the thumbnail - # previews) - crop_thumbnails = ('CR2', 'DNG', 'RAF', 'ORF', 'PEF', 'ARW') - - def _ignore_embedded_160x120_thumbnail(self, max_size_needed, metadata): - return max_size_needed is None or max_size_needed[0] > 160 or max_size_needed[1] > 120 or not metadata.exif_thumbnail.data - - def _get_thumbnail_data(self, metadata, max_size_needed): - logger.debug("Getting thumbnail data %s", max_size_needed) - if self._ignore_embedded_160x120_thumbnail(max_size_needed, metadata): - logger.debug("Ignoring embedded preview") - lowrez = False - previews = metadata.previews - if not previews: - return (None, None) - else: - if max_size_needed: - for thumbnail in previews: - if thumbnail.dimensions[0] >= max_size_needed or thumbnail.dimensions[1] >= max_size_needed: - break - else: - thumbnail = previews[-1] - else: - thumbnail = metadata.exif_thumbnail - lowrez = True - return (thumbnail.data, lowrez) - - def _process_thumbnail(self, image, size_reduced): - image_ok = True - if image.mode <> "RGBA": - try: - image = image.convert("RGBA") - except: - logger.error("Image thumbnail is corrupt") - image_ok = False - - if image_ok: - thumbnail = PicklablePIL(image) - if size_reduced is not None: - thumbnail_icon = image.copy() - downsize_pil(thumbnail_icon, size_reduced, fit=False) - thumbnail_icon = PicklablePIL(thumbnail_icon) - else: - thumbnail_icon = None - else: - thumbnail = thumbnail_icon = None - return (thumbnail, thumbnail_icon) - - def _get_photo_thumbnail(self, full_file_name, size_max, size_reduced): - thumbnail = None - thumbnail_icon = None - name = os.path.basename(full_file_name) - metadata = pyexiv2.metadata.ImageMetadata(full_file_name) - try: - logger.debug("Read photo metadata...") - metadata.read() - except: - logger.warning("Could not read metadata from %s", full_file_name) - else: - logger.debug("...successfully read photo metadata") - if metadata.mime_type == "image/jpeg" and self._ignore_embedded_160x120_thumbnail(size_max, metadata): - try: - image = Image.open(full_file_name) - lowrez = False - except: - logger.warning("Could not generate thumbnail for jpeg %s ", full_file_name) - image = None - else: - thumbnail_data, lowrez = self._get_thumbnail_data(metadata, max_size_needed=size_max) - logger.debug("_get_thumbnail_data returned") - if not isinstance(thumbnail_data, types.StringType): - image = None - else: - td = cStringIO.StringIO(thumbnail_data) - logger.debug("got td") - try: - image = Image.open(td) - except: - logger.warning("Unreadable thumbnail for %s", full_file_name) - image = None - logger.debug("opened image") - if image: - try: - orientation = metadata['Exif.Image.Orientation'].value - except: - orientation = None - if lowrez: - # need to remove letterboxing / pillarboxing from some - # RAW thumbnails - if os.path.splitext(full_file_name)[1][1:].upper() in Thumbnail.crop_thumbnails: - image2 = image.crop((0, 8, 160, 112)) - image2.load() - image = image2 - if size_max is not None and (image.size[0] > size_max[0] or image.size[1] > size_max[1]): - logger.debug("downsizing") - downsize_pil(image, size_max, fit=False) - logger.debug("downsized") - if orientation == 8: - # rotate counter clockwise - image = image.rotate(90) - elif orientation == 6: - # rotate clockwise - image = image.rotate(270) - elif orientation == 3: - # rotate upside down - image = image.rotate(180) - thumbnail, thumbnail_icon = self._process_thumbnail(image, size_reduced) - - logger.debug("...got thumbnail for %s", full_file_name) - return (thumbnail, thumbnail_icon) - - def _get_video_thumbnail(self, full_file_name, thm_full_name, size_max, size_reduced): - thumbnail = None - thumbnail_icon = None - if size_max is None: - size = 0 - else: - size = max(size_max[0], size_max[1]) - image = None - if size > 0 and size <= 160: - if thm_full_name: - try: - thumbnail = gtk.gdk.pixbuf_new_from_file(thm_full_name) - except: - logger.error("Could not open THM file for %s", full_file_name) - logger.error("Thumbnail file is %s", thm_full_name) - image = None - else: - thumbnail = add_filmstrip(thumbnail) - image = pixbuf_to_image(thumbnail) - - if image is None: - try: - tmp_dir = tempfile.mkdtemp(prefix="rpd-tmp") - thm = os.path.join(tmp_dir, 'thumbnail.jpg') - subprocess.check_call(['ffmpegthumbnailer', '-i', full_file_name, '-t', '10', '-f', '-o', thm, '-s', str(size)]) - image = Image.open(thm) - image.load() - os.unlink(thm) - os.rmdir(tmp_dir) - except: - image = None - logger.error("Error generating thumbnail for %s", full_file_name) - if image: - thumbnail, thumbnail_icon = self._process_thumbnail(image, size_reduced) - - logger.debug("...got thumbnail for %s", full_file_name) - return (thumbnail, thumbnail_icon) - - def get_thumbnail(self, full_file_name, thm_full_name, file_type, size_max=None, size_reduced=None): - logger.debug("Getting thumbnail for %s...", full_file_name) - if file_type == rpdfile.FILE_TYPE_PHOTO: - logger.debug("file type is photo") - return self._get_photo_thumbnail(full_file_name, size_max, size_reduced) - else: - return self._get_video_thumbnail(full_file_name, thm_full_name, size_max, size_reduced) - - -class GetPreviewImage(multiprocessing.Process): - def __init__(self, results_pipe): - multiprocessing.Process.__init__(self) - self.daemon = True - self.results_pipe = results_pipe - self.thumbnail_maker = Thumbnail() - self.stock_photo_thumbnail_image = None - self.stock_video_thumbnail_image = None - - def get_stock_image(self, file_type): - """ - Get stock image for file type scaled to the current size of the screen - """ - if file_type == rpdfile.FILE_TYPE_PHOTO: - if self.stock_photo_thumbnail_image is None: - self.stock_photo_thumbnail_image = PicklablePIL(get_stock_photo_image()) - return self.stock_photo_thumbnail_image - else: - if self.stock_video_thumbnail_image is None: - self.stock_video_thumbnail_image = PicklablePIL(get_stock_video_image()) - return self.stock_video_thumbnail_image - - def run(self): - while True: - unique_id, full_file_name, thm_full_name, file_type, size_max = self.results_pipe.recv() - full_size_preview, reduced_size_preview = self.thumbnail_maker.get_thumbnail(full_file_name, thm_full_name, file_type, size_max=size_max, size_reduced=(100,100)) - if full_size_preview is None: - full_size_preview = self.get_stock_image(file_type) - self.results_pipe.send((unique_id, full_size_preview, reduced_size_preview)) - - -class GenerateThumbnails(multiprocessing.Process): - def __init__(self, scan_pid, files, batch_size, results_pipe, terminate_queue, - run_event): - multiprocessing.Process.__init__(self) - self.results_pipe = results_pipe - self.terminate_queue = terminate_queue - self.batch_size = batch_size - self.files = files - self.run_event = run_event - self.results = [] - - self.thumbnail_maker = Thumbnail() - - self.scan_pid = scan_pid - - - def run(self): - counter = 0 - i = 0 - for f in self.files: - - # pause if instructed by the caller - self.run_event.wait() - - if not self.terminate_queue.empty(): - x = self.terminate_queue.get() - # terminate immediately - logger.info("Terminating thumbnailing") - return None - - thumbnail, thumbnail_icon = self.thumbnail_maker.get_thumbnail( - f.full_file_name, - f.thm_full_name, - f.file_type, - #~ f.extension, - (160, 120), (100,100)) - - self.results.append((f.unique_id, thumbnail_icon, thumbnail)) - counter += 1 - if counter == self.batch_size: - self.results_pipe.send((rpdmp.CONN_PARTIAL, self.results)) - self.results = [] - counter = 0 - i += 1 - - if counter > 0: - # send any remaining results - self.results_pipe.send((rpdmp.CONN_PARTIAL, self.results)) - self.results_pipe.send((rpdmp.CONN_COMPLETE, self.scan_pid)) - self.results_pipe.close() - diff --git a/rapid/utilities.py b/rapid/utilities.py deleted file mode 100644 index f0f0569..0000000 --- a/rapid/utilities.py +++ /dev/null @@ -1,152 +0,0 @@ -#!/usr/bin/python -# -*- coding: latin1 -*- - -### Copyright (C) 2007-2012 Damon Lynch <damonlynch@gmail.com> - -### This program is free software; you can redistribute it and/or modify -### it under the terms of the GNU General Public License as published by -### the Free Software Foundation; either version 2 of the License, or -### (at your option) any later version. - -### This program is distributed in the hope that it will be useful, -### but WITHOUT ANY WARRANTY; without even the implied warranty of -### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -### GNU General Public License for more details. - -### You should have received a copy of the GNU General Public License -### along with this program; if not, write to the Free Software -### Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 -### USA - -import os -import gio -import gtk -from PIL import Image -import distutils.version - -def get_full_path(path): - """ make path relative to home directory if not an absolute path """ - if os.path.isabs(path): - return path - else: - return os.path.join(os.path.expanduser('~'), path) - -def is_directory(path): - - # for some very strange reason, doing it the GIO way fails with - # unknown type, even for directories! - return os.path.isdir(path) - - if False: - d = gio.File(path) - if d.query_exists(): - file_info = d.query_filesystem_info(attributes="standard::type") - file_type = file_info.get_file_type() - if file_type == gio.FILE_TYPE_DIRECTORY: - return True - - return False - -def format_size_for_user(bytes, zero_string="", with_decimals=True, kb_only=False): - """Format an int containing the number of bytes into a string suitable for - printing out to the user. zero_string is the string to use if bytes == 0. - source: https://develop.participatoryculture.org/trac/democracy/browser/trunk/tv/portable/util.py?rev=3993 - - """ - if bytes > (1 << 30) and not kb_only: - value = (bytes / (1024.0 * 1024.0 * 1024.0)) - if with_decimals: - format = "%1.1fGB" - else: - format = "%dGB" - elif bytes > (1 << 20) and not kb_only: - value = (bytes / (1024.0 * 1024.0)) - if with_decimals: - format = "%1.1fMB" - else: - format = "%dMB" - elif bytes > (1 << 10): - value = (bytes / 1024.0) - if with_decimals: - format = "%1.1fKB" - else: - format = "%dKB" - elif bytes > 1: - value = bytes - if with_decimals: - format = "%1.1fB" - else: - format = "%dB" - else: - return zero_string - return format % value - -def register_iconsets(icon_info): - """ - Register icons in the icon set if they're not already used - - From http://faq.pygtk.org/index.py?req=show&file=faq08.012.htp - """ - - icon_factory = gtk.IconFactory() - stock_ids = gtk.stock_list_ids() - for stock_id, file in icon_info: - # only load image files when our stock_id is not present - if stock_id not in stock_ids: - pixbuf = gtk.gdk.pixbuf_new_from_file(file) - iconset = gtk.IconSet(pixbuf) - icon_factory.add(stock_id, iconset) - icon_factory.add_default() - -def escape(s): - """ - Replace special characters by SGML entities. - """ - entities = ("&&", "<<", ">>") - for e in entities: - s = s.replace(e[0], e[1:]) - return s - -def image_to_pixbuf(image): - # convert PIL image to pixbuf - # this one handles transparency, unlike the default example in the pygtk FAQ - # this is also from the pygtk FAQ - IS_RGBA = image.mode=='RGBA' - return gtk.gdk.pixbuf_new_from_data( - image.tobytes(), # data - gtk.gdk.COLORSPACE_RGB, # color mode - IS_RGBA, # has alpha - 8, # bits - image.size[0], # width - image.size[1], # height - (IS_RGBA and 4 or 3) * image.size[0] # rowstride - ) - -def pixbuf_to_image(pb): - assert(pb.get_colorspace() == gtk.gdk.COLORSPACE_RGB) - dimensions = pb.get_width(), pb.get_height() - stride = pb.get_rowstride() - pixels = pb.get_pixels() - - mode = pb.get_has_alpha() and "RGBA" or "RGB" - image = Image.frombuffer(mode, dimensions, pixels, - "raw", mode, stride, 1) - - if mode == "RGB": - # convert to having an alpha value, so that the image can - # act as a mask in the drop shadow paste - image = image.convert("RGBA") - - return image - -def pythonify_version(v): - """ makes version number a version number in distutils sense""" - return distutils.version.StrictVersion(v.replace( '~','')) - -def human_readable_version(v): - """ returns a version in human readable form""" - v = v.replace('~a', ' alpha ') - v = v.replace('~b', ' beta ') - v = v.replace('~rc', ' RC ') - return v - |