diff --git a/ABOUT b/ABOUT new file mode 100644 index 0000000..6bd6764 --- /dev/null +++ b/ABOUT @@ -0,0 +1,111 @@ +MiniCopier 0.5 +May 2009 +Adrian Courrèges + +PROJECT PAGE : +============== +http://www.adriancourreges.com/projects/minicopier/ + + +DESCRIPTION: +============ + +MiniCopier intends to improve graphic copy/paste file operations on some popular OSes. +Its features are closed to the ones of SuperCopier (Windows environment). +It lets you create a queue of transfers, so only a file is copied at a time. +You can manage the queue by changing the position of any pending transfer. +It also handles pause and resume operations. +If a transfer fails, the bytes already copied are not erased, so MiniCopier +can resume the transfer for you later. +All the operations are done by "drag-and-drops" on the icons. +MiniCopier is published under the GNU GPL. + + +REQUIREMENTS : +============== +Unix system with: GNOME/KDE/XFCE +or Windows or Mac OS X +Java virtual machine 1.5 (or more recent) + + +INSTALLATION: +============= +Nothing to install. +(Except Sun JRE 5.0 or more recent) + + +RUN: +==== +Just launch the MiniCopier.sh script. + +/!\ If you have Beryl/Compiz enabled, you might get a blank windows. +For a fix, you can try to add the line below in MiniCopier.sh + +export AWT_TOOLKIT="MToolkit" + +just before the line: java -jar MiniCopier.jar $* + +Options: + +See configuration.ini (generated the fist time you launch MiniCopier) for +more options. + + +USE: +==== + +Drag and drop files or folder you wish to copy on the basket icon. +Once you're done with filling the basket, drag and drop a destination folder +to the box icon. All the content of the basket will be copied to that directory. + +LICENSE: +======== + +MiniCopier is free software released under the GNU General Public License (GPL). +The software is also distributed with the PgsLookAndFeel library, (Apache Software +License, see https://pgslookandfeel.dev.java.net). + + +CONTACT: +======== + +If you want to send me bug reports, suggestions, or just tell what you think +of this software: www.adriancourreges.com/about/ + + +CHANGELOG: +========== + +v0.5 +Now support transfer speed limit. +Displays current transfer speed. +Displays remaining time for current file and main queue. +Several bugs fixed. + +v0.4 +As GTK support for swing applications suffers from many problems (combo box bug crashes +the process, plus some graphic issues), MiniCopier now uses the Pagosoft library. +default look'n'feel : pgs +the option -nolnf is removed (edit configuration.ini for look'n'feel settings) + +v0.3 +new Panel to manage the failed transfers (retry, clear) +language support : i18n with French and English +Windows compatibility +config file auto-generated +multiselection in tables +source code published under GPL +new icons +"Credits" dialog updated +Some minor bugs correction + +v0.2: +if possible, host system look'n'feel is used by default (Gnome) (disable with "-nolnf"). +ability to rename the target of a transfer (if collision). +ability to follow or ignore symbolic links. +new panel for setting options. +user can define a default action (for collision and links). +the number of items inside the basket is displayed. +sizes of total queue and current file are displayed. +transfers list now displays size of each transfer and the destination folder. +change of some strings in dialog boxes. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d511905 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Manifest.txt b/Manifest.txt new file mode 100644 index 0000000..2c9ba1a --- /dev/null +++ b/Manifest.txt @@ -0,0 +1,3 @@ +Main-Class: minicopier.Copier +Class-Path: lib/PgsLookAndFeel.jar + diff --git a/MiniCopier.sh b/MiniCopier.sh new file mode 100755 index 0000000..eb9d530 --- /dev/null +++ b/MiniCopier.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +echo "Launching MiniCopier..." +cd `dirname $0` +java -jar MiniCopier.jar $* +echo "MiniCopier terminated." diff --git a/README.md b/README.md new file mode 100644 index 0000000..afd894c --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# MiniCopier + +### [Website](http://www.adriancourreges.com/projects/minicopier/)    [Download](http://www.adriancourreges.com/projects/minicopier/#dl-section)     + +MiniCopier is a graphical copy manager. It provides more comfort and control over file copy operations than basic OS functions. +You can use it to queue several transfers, resume a copy which has failed, limit the speed or pause the copy... +MiniCopier is multiplatform and supports Linux, Windows and MacOS X systems. + +![screenshot-1](http://www.adriancourreges.com/projects/minicopier/minicopier-0.5-screen2.png) + +## Features + +* Pause a copy +* Treatment of the transfers one after the other +* Add new transfers to the queue while a copy is already being processed +* Dynamic management of the queue of remaining transfers +* Limit the speed of the copy +* Skip current transfer to proceed to the next one +* Resume a copy at the exact point where it failed (no need to start over) +* Choose another name for the target if a file already exists +* Can follow or ignore symbolic links (Unix systems only) +* Set a default behavior if target file already exists +* Keep track of the transfers which failed. + + +## Requirements + +Java should be installed. (JRE 5.0 or later) + +## License + +MiniCopier is published under the GNU GPLv2 license. diff --git a/build.xml b/build.xml new file mode 100644 index 0000000..c712eed --- /dev/null +++ b/build.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lib/PgsLookAndFeel.jar b/lib/PgsLookAndFeel.jar new file mode 100644 index 0000000..e7f94e1 Binary files /dev/null and b/lib/PgsLookAndFeel.jar differ diff --git a/src/img/alt/cancel.png b/src/img/alt/cancel.png new file mode 100644 index 0000000..90cfd46 Binary files /dev/null and b/src/img/alt/cancel.png differ diff --git a/src/img/alt/down.png b/src/img/alt/down.png new file mode 100644 index 0000000..bdbee4b Binary files /dev/null and b/src/img/alt/down.png differ diff --git a/src/img/alt/first.png b/src/img/alt/first.png new file mode 100644 index 0000000..d4ca916 Binary files /dev/null and b/src/img/alt/first.png differ diff --git a/src/img/alt/last.png b/src/img/alt/last.png new file mode 100644 index 0000000..f10497c Binary files /dev/null and b/src/img/alt/last.png differ diff --git a/src/img/alt/up.png b/src/img/alt/up.png new file mode 100644 index 0000000..a12bfe3 Binary files /dev/null and b/src/img/alt/up.png differ diff --git a/src/img/basket.png b/src/img/basket.png new file mode 100644 index 0000000..4534948 Binary files /dev/null and b/src/img/basket.png differ diff --git a/src/img/delete.gif b/src/img/delete.gif new file mode 100644 index 0000000..04ad9f7 Binary files /dev/null and b/src/img/delete.gif differ diff --git a/src/img/down.gif b/src/img/down.gif new file mode 100644 index 0000000..e5c16cb Binary files /dev/null and b/src/img/down.gif differ diff --git a/src/img/failed.gif b/src/img/failed.gif new file mode 100644 index 0000000..950762b Binary files /dev/null and b/src/img/failed.gif differ diff --git a/src/img/first.gif b/src/img/first.gif new file mode 100644 index 0000000..7c7cd2b Binary files /dev/null and b/src/img/first.gif differ diff --git a/src/img/icon.gif b/src/img/icon.gif new file mode 100644 index 0000000..ca60e29 Binary files /dev/null and b/src/img/icon.gif differ diff --git a/src/img/icon.ico b/src/img/icon.ico new file mode 100644 index 0000000..519c1be Binary files /dev/null and b/src/img/icon.ico differ diff --git a/src/img/icon.jpg b/src/img/icon.jpg new file mode 100644 index 0000000..53cf58c Binary files /dev/null and b/src/img/icon.jpg differ diff --git a/src/img/icon.png b/src/img/icon.png new file mode 100644 index 0000000..8d2c59a Binary files /dev/null and b/src/img/icon.png differ diff --git a/src/img/last.gif b/src/img/last.gif new file mode 100644 index 0000000..36f3c90 Binary files /dev/null and b/src/img/last.gif differ diff --git a/src/img/list.png b/src/img/list.png new file mode 100644 index 0000000..7330bc5 Binary files /dev/null and b/src/img/list.png differ diff --git a/src/img/minicopier-logo.png b/src/img/minicopier-logo.png new file mode 100644 index 0000000..a45860e Binary files /dev/null and b/src/img/minicopier-logo.png differ diff --git a/src/img/oldtheme/credits.png b/src/img/oldtheme/credits.png new file mode 100644 index 0000000..d15e8e2 Binary files /dev/null and b/src/img/oldtheme/credits.png differ diff --git a/src/img/oldtheme/delete.png b/src/img/oldtheme/delete.png new file mode 100644 index 0000000..aa4bf49 Binary files /dev/null and b/src/img/oldtheme/delete.png differ diff --git a/src/img/oldtheme/down.png b/src/img/oldtheme/down.png new file mode 100644 index 0000000..26cf259 Binary files /dev/null and b/src/img/oldtheme/down.png differ diff --git a/src/img/oldtheme/first.png b/src/img/oldtheme/first.png new file mode 100644 index 0000000..8f083a9 Binary files /dev/null and b/src/img/oldtheme/first.png differ diff --git a/src/img/oldtheme/last.png b/src/img/oldtheme/last.png new file mode 100644 index 0000000..284c59d Binary files /dev/null and b/src/img/oldtheme/last.png differ diff --git a/src/img/oldtheme/list.png b/src/img/oldtheme/list.png new file mode 100644 index 0000000..7330bc5 Binary files /dev/null and b/src/img/oldtheme/list.png differ diff --git a/src/img/oldtheme/options.png b/src/img/oldtheme/options.png new file mode 100755 index 0000000..0c9faff Binary files /dev/null and b/src/img/oldtheme/options.png differ diff --git a/src/img/oldtheme/up.png b/src/img/oldtheme/up.png new file mode 100644 index 0000000..b5d5f5f Binary files /dev/null and b/src/img/oldtheme/up.png differ diff --git a/src/img/options.png b/src/img/options.png new file mode 100755 index 0000000..0c9faff Binary files /dev/null and b/src/img/options.png differ diff --git a/src/img/paste.png b/src/img/paste.png new file mode 100644 index 0000000..1d97f72 Binary files /dev/null and b/src/img/paste.png differ diff --git a/src/img/retry.gif b/src/img/retry.gif new file mode 100644 index 0000000..4458e92 Binary files /dev/null and b/src/img/retry.gif differ diff --git a/src/img/up.gif b/src/img/up.gif new file mode 100644 index 0000000..37134ec Binary files /dev/null and b/src/img/up.gif differ diff --git a/src/minicopier/Basket.java b/src/minicopier/Basket.java new file mode 100644 index 0000000..f1c206c --- /dev/null +++ b/src/minicopier/Basket.java @@ -0,0 +1,51 @@ +/* + Basket.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier; +import java.util.*; + +public class Basket { + + //Contains paths of files/folders to be copied + private Vector basketContent; + + /* Creation of an empty basket */ + public Basket () { + this.basketContent = new Vector() ; + } + + /* Add a path to the basket + * @path path of a folder/file to be copied + */ + public void add (String path) { + this.basketContent.add(path); + } + + public Iterator getIterator() { + return basketContent.iterator(); + } + + public boolean isEmpty() { + return this.basketContent.isEmpty(); + } + + public int getLength(){ + return basketContent.size(); + } +} diff --git a/src/minicopier/Configuration.java b/src/minicopier/Configuration.java new file mode 100644 index 0000000..edbe9c7 --- /dev/null +++ b/src/minicopier/Configuration.java @@ -0,0 +1,187 @@ +/* + Configuration.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier; + +import java.util.*; +import java.io.*; + +import minicopier.gui.DialogMsg; + +public class Configuration { + + private static final String configName = "configuration.ini"; + + public static String language; + + public static boolean alwaysOnTop; + + //Look And Feel + //"pgs" (default) + //"native" + //"metal" + public static String lookAndFeel; + + //What to do if destination file already exists + // 0 -> ask user + // 1 -> ignore + // 2 -> overwrite + // 3 -> overwrite if older + // 4 -> resume + // 5 -> rename + public static int collisionAction; + + //What to do if a source path contains a symbolic link + //(Unix systems only) + // 0 -> follow + // 1 -> ignore + public static int symbolicLinkAction; + + //Is speed limit active? + public static boolean limitSpeed; + + //Maximum throughput of the transfer + public static int nbBytesPerS; + + private static boolean firstLaunch = false; + + public Configuration(){ + + this.limitSpeed = false; + + this.nbBytesPerS = 1024*1024; + + Properties prop = new Properties(); + + File configFile = new File(configName); + + if (!configFile.exists()) { + System.out.println("Configuration file not found. Creating configuration.ini."); + createConfigFile(); + firstLaunch = true; + } + + try { + FileInputStream in = new FileInputStream(configName); + prop.load(in); + in.close(); + } catch (Exception e){ + e.printStackTrace(); + } + + setProp(prop); + + } + + public static void setProp(Properties prop){ + + String readProp; + + //Language + readProp = prop.getProperty("language"); + if (("").equals(readProp)||(readProp==null)){ + language = "SystemLocale"; + } else { + language = readProp; + } + + //Always on top + readProp = prop.getProperty("alwaysontop"); + if (("true").equals(readProp)){ + alwaysOnTop = true; + } else { + alwaysOnTop = false; + } + + //Look an Feel + readProp = prop.getProperty("lnf"); + if (("").equals(readProp)||(readProp==null)){ + lookAndFeel = "pgs"; + } + else { + lookAndFeel = readProp; + } + + //Collision action + readProp = prop.getProperty("collision"); + if (("ask").equals(readProp)){ + collisionAction = 0; + } else if (("cancel").equals(readProp)){ + collisionAction = 1; + } else if (("overwrite").equals(readProp)){ + collisionAction = 2; + } else if (("overwrite_older").equals(readProp)){ + collisionAction = 3; + } else if (("resume").equals(readProp)){ + collisionAction = 4; + } else { + collisionAction = 0; + } + + //Symbolic links action + readProp = prop.getProperty("symlinks"); + if (("follow").equals(readProp)){ + symbolicLinkAction = 0; + } else if (("ignore").equals(readProp)){ + symbolicLinkAction = 1; + } else { + symbolicLinkAction = 0; + } + + } + + public final void createConfigFile(){ + InputStreamReader isr=new InputStreamReader(getClass() + .getClassLoader().getResourceAsStream("minicopier/configuration.ini")); + + BufferedReader buf = new BufferedReader(isr); + + PrintWriter writer; + String toWrite; + + try { + writer = new PrintWriter (new OutputStreamWriter (new FileOutputStream (configName))); + while ((toWrite=buf.readLine())!=null){ + writer.println(toWrite); + } + buf.close(); + writer.close(); + } catch (Exception e){ + System.out.println("Error creation configuration file !"); + } + + } + + public boolean wasFirstLaunch(){ + return this.firstLaunch; + } + + public synchronized static void setSpeedValue(int i){ + Configuration.nbBytesPerS = i; + } + + public synchronized static int getSpeedValue(){ + return Configuration.nbBytesPerS; + } + + + + + +} diff --git a/src/minicopier/Copier.java b/src/minicopier/Copier.java new file mode 100644 index 0000000..65df640 --- /dev/null +++ b/src/minicopier/Copier.java @@ -0,0 +1,700 @@ +/* + Copier.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier; + +import java.io.*; +import java.net.*; +import java.util.*; + +import minicopier.gui.DialogMsg; +import minicopier.gui.MainFrame; +import minicopier.i18n.Language; + +public class Copier { + + //Application name and version + public static String name = "MiniCopier"; + public static String version = "0.5"; + + //The basket contains string paths to the files added by a user + //for a copy. A 'paste' will empty the basket by transferring items + //to the main queue. + private Basket basket; + + //The path of the file being copied + private String currentFileSource; + + //The path of the destination file + private String currentFileDestination; + + //True if the Copier is working on a copy + private Boolean busy; + + //True if the Copier is paused + private Boolean paused; + + //The main queue contains the remaining file-only transfers to be done. + public static MainQueue mainQueue; + + //The failed items + public static FailedQueue failedItems; + + //Thread copying a file + private TransferTask copyOp; + + //Transfer sizes sum + private long queueTotalSize; + + //Bytes already copied + private long queueCurrentSize; + + //Current treated file total size + private long fileTotalSize; + + //Bytes already copied for current file + private long fileCurrentSize; + + //GUI of the copier + private MainFrame gui; + + private long bytesCounter; + + + + /* Create a new Copier */ + public Copier() { + this.basket = new Basket(); + this.mainQueue = new MainQueue(this); + this.failedItems = new FailedQueue(this); + this.busy = false; + this.paused = false; + this.copyOp = null; + this.currentFileSource = ""; + this.currentFileDestination = ""; + this.queueTotalSize = 0; + this.queueCurrentSize = 0; + this.fileTotalSize = 0; + this.fileCurrentSize = 0; + this.gui = null; + this.bytesCounter = 0; + } + + + /* Add a file/folder path to the basket + * @param path path of the file or folder to be copied + */ + public void add2basket (String path) { + this.basket.add(path); + } + + public String getCurrentFileSource() { + return this.currentFileSource; + } + + public String getCurrentFileDestination() { + return this.currentFileDestination; + } + + private void setCurrentFileStrings(String sourcePath, String destPath) { + this.currentFileSource = sourcePath; + this.currentFileDestination = destPath; + this.gui.changeCurrentFileSignal(); + } + + public void setGui(MainFrame view){ + this.gui = view; + } + + public void setPause(boolean b){ + this.paused = b; + this.gui.changePauseStateSignal(b); + } + + public boolean getPause(){ + return this.paused; + } + + public boolean getBusy(){ + return this.busy; + } + + + public void skip(boolean b){ + this.copyOp.setCancelAndQueue(b); + } + + public void addURIString2basket (String uriString) { + String file2add = this.stringURI2StringPath(uriString); + if (file2add != null) { + this.add2basket(file2add); + } + } + + public void paste2URIString(String uriString) { + String folder2Paste = this.stringURI2StringPath(uriString); + if (folder2Paste != null) { + this.paste(folder2Paste); + } + } + + public String stringURI2StringPath (String uriString) { + URI uri = null; + try{ + uri = new URI(uriString); + } catch (URISyntaxException e){ + System.err.println(uriString+" is not a valid URI."); + } + return (uri.getPath()); + } + /* Add a FileToTransfer to the main queue + * @param transfer The file to be put into main queue + */ + public void addFile2Queue(FileToTransfer file){ + + this.mainQueue.addFile(file); + //System.out.println("Transfer\n"+ file.getSourcePath() + "\nto folder " + // + file.getDestinationFilePath() + "\nadded in queue. "); + + } + + /* Add the items of the folder in the queue */ + public void addFolder2Queue(File parentFolder, String destinationFolder) { + + + //Listing directory content + File[] dirlist = parentFolder.listFiles(); + + File currentItem; + FileToTransfer transfer; + String newDestinationPath = ""; + + if (dirlist != null){ //input item is really a folder + + for ( int i = 0; i < dirlist.length; i++) { + //Dealing with directory content + + currentItem = dirlist[i]; + + + if (currentItem.isDirectory()) { + + newDestinationPath = destinationFolder + + File.separator + parentFolder.getName(); + //Recursive call on subfolder + addFolder2Queue(currentItem, newDestinationPath); + + } else { //item is a file + + newDestinationPath = destinationFolder + + File.separator + parentFolder.getName() ; + + transfer = new FileToTransfer(currentItem.getPath(), + newDestinationPath); + this.addFile2Queue(transfer); + } + } + } else { + System.err.println("Error reading directory : "+parentFolder); + } + } + + + + /** Transforms basket items into FileToTransfer (even directories + * content) and transfer them to the main queue. + * @param destinationPath The path to the directory in which basket + * items must be copied. + */ + + public void paste(String destinationPath) { + + System.out.println("Paste requested to : " + destinationPath); + File destination = new File(destinationPath); + if (!destination.isDirectory()) {return;} + + //If it is a new paste, we restart the queue stats + if (!busy) { + this.queueTotalSize = 0; + this.queueCurrentSize = 0; + } + + Basket basketBackup = this.basket ; + this.basket = new Basket(); + Iterator basketContent = basketBackup.getIterator(); + + String filePath; + + File item2add; + + while (basketContent.hasNext()) { + filePath = basketContent.next(); + + item2add = new File(filePath); + + if ((Configuration.symbolicLinkAction == 0) || isNotLink(item2add)) { + if (!item2add.isDirectory()) { //if the item is a file + + System.out.println("adding file to main queue"); + addFile2Queue(new FileToTransfer(filePath,destinationPath)); + + } else { //the item is a folder + //System.out.println("adding folder to main queue"); + this.addFolder2Queue(item2add,destinationPath); + } + } + } + + //We launch the treatment anyway, it will exit by + //itself if necessary + forceStart(); + } + + public void forceStart(){ + ForceTreatment t = new ForceTreatment(); + t.start(); + } + + public void treatQueue() { + + synchronized(this){ + if (busy) {return;} //other instance running... + + this.busy = true; + } + + boolean proceed = true; + boolean append = false; + + + FileToTransfer transfer; + + while (!this.mainQueue.isEmpty()) { + + //Pick & extract first transfer in queue + synchronized (mainQueue) { + transfer = mainQueue.extractFirst(); + } + + this.setCurrentFileStrings(transfer.getSourcePath() + ,transfer.getDestinationFilePath()); + + + //default : we proceed to the copy without append + proceed = true; + append = false; + + //target already exists ? + + File destFile = transfer.getDestinationFile(); + + + + if (destFile.length() > 0) { //target exists + + boolean acceptableAction = false; + + int actionToDo = Configuration.collisionAction; + + while (!acceptableAction) { + + long sourceSize = transfer.getSourceFile().length(); + long destSize = transfer.getDestinationFile().length(); + + switch (actionToDo) { + + case (0) : //ask + int choice = DialogMsg.alreadyExistsChoice( + transfer.getSourcePath(), + sourceSize, + transfer.getDestinationFilePath(), + destSize); + + if (choice==0) { //overwrite + actionToDo = 2; + } + + if (choice==1) { //resume copy + actionToDo = 4; + } + + if (choice==2) { //Rename + actionToDo = 5; + } + + if (choice==3) { //cancel copy + actionToDo = 1; + } + + break; + + case (1) : //ignore + proceed = false; + acceptableAction = true; + break; + + case (2) : //overwrite + + //We just make sure the source path is different + //from the destination path. + + if (transfer.getSourcePath().equals(transfer.getDestinationFilePath())) { + DialogMsg.errorOverwrite(); + acceptableAction = false; + actionToDo = 0; + } else { + //nothing to do : append==false proceed==true + acceptableAction = true; + } + + break; + + case (3): //overwrite if older + proceed = (transfer.getSourceFile().lastModified() + > destFile.lastModified()); + acceptableAction = true; + break; + + case (4) : //resume + //dest file might be a different file but with the same name + //as source. User wanted to resume, so we resume anyway, but + //only if the size of file permit it. + append = (destFile.length() <= transfer.getSize()); + acceptableAction = true; + + + break; + + case (5) : //rename + + String newName = DialogMsg.askNewName(transfer.getName()); + + boolean leaveRename = (newName==null); + + while (newName != null + && (newName.equals(transfer.getName()) || !nameIsAcceptable(newName,transfer.getDestinationFolderPath()))) { + //User confirms a new name + DialogMsg.renameError(); + newName = DialogMsg.askNewName(transfer.getName()); + + } + + if (newName != null) { + //The name is correct, we can change destination path + transfer.changeTargetName(newName); + + //The action will be acceptable if the file doesn't already + //exists, else we ask what to do. + acceptableAction = !(transfer.getDestinationFile().exists()); + } else { //The user has canceled renaming + acceptableAction = false; + } + + actionToDo = 0; //we ask for a new choice + + + break; + + default : break; + + } + + } + + + } + + if (proceed) { //So we must proceed to the copy + + this.setCurrentFileStrings(transfer.getSourcePath() + ,transfer.getDestinationFilePath()); + + //System.out.println("Treating " + transfer.getSourcePath()); + //System.out.println("-> " + transfer.getDestinationFilePath()); + + //Creation of thread + this.copyOp = new TransferTask(this,transfer,append); + + //System.out.println("Beginning transfer..."); + copyOp.start(); + + //Waiting for transfer to finish + try { + copyOp.join(); + } + catch (Exception e) { + System.out.println("Copy Thread Error " + e); + } + + //System.out.println("Copy thread terminated"); + //Transfer has ended (maybe not completed) + if (!copyOp.getCompleted()) { //incomplete transfer + System.out.println("Transfer NOT completed"); + + //Has it been canceled by a skip action ? + if (copyOp.getCancel()) { + System.out.println("(has been skipped)"); + if (copyOp.getPutInQueue()) { //user wants to resume later + mainQueue.addFile(transfer); + } else { //user does not want to resume later + //we add the transfer to the "failed items" list + this.failedItems.addFile(transfer); + } + + } else { //it's an IO exception + System.out.println("(error occured)"); + boolean ioPutInQueue = DialogMsg.transferErrorChoice(); + if (ioPutInQueue) { + //user wants to resume later + mainQueue.addFile(transfer); + } else { + //transfer has failed + this.failedItems.addFile(transfer); + } + + } + + + this.decreaseQueueTotalSize(this.fileTotalSize); + this.decreaseQueueCurrentSize(this.fileCurrentSize); + this.setFileCurrentSize(0); + + } + this.copyOp = null; + + } else { //destination file already exists, and user canceled + this.decreaseQueueTotalSize(transfer.getSize()); + this.setFileTotalSize(0); + this.setFileCurrentSize(0); + this.failedItems.addFile(transfer); + } + + } + + //MainQueue SEEMS to be empty... + //Let's just check other transfers were not added after the test. + + synchronized(this){ + busy=false; + + if (this.mainQueue.isEmpty()) { //empty, indeed + this.setCurrentFileStrings("",""); + return; + } else { //still some transfers remaining + //we launch the treatment once again + forceStart(); + return; + } + } + + } + + public void increaseQueueTotalSize(long nb) { + this.queueTotalSize += nb; + gui.totalSizeSignal(); + } + + public void decreaseQueueTotalSize(long nb){ + this.queueTotalSize -= nb; + gui.totalSizeSignal(); + } + + public long readAndInitBytesCounter(){ + long result = this.bytesCounter; + this.bytesCounter -= result; + return result; + } + + + public void increaseQueueCurrentSize(long nb) { + this.queueCurrentSize += nb; + //For speed measures + this.bytesCounter += nb; + } + + public void decreaseQueueCurrentSize(long nb){ + this.queueCurrentSize -= nb; + } + + public void setFileCurrentSize(long s){ + this.fileCurrentSize = s; + } + + public void increaseFileCurrentSize(long nb) { + this.fileCurrentSize += nb; + increaseQueueCurrentSize(nb); + } + + public void setFileTotalSize(long s) { + this.fileTotalSize = s; + gui.currentSizeSignal(); + } + + public long getTotalBytesRemaining(){ + return this.queueTotalSize - this.queueCurrentSize; + } + + public long getCurrentBytesRemaining(){ + return this.fileTotalSize - this.fileCurrentSize; + } + + public int getTotalPercent() { + int result = 0; + if (this.queueTotalSize != 0) { + Double tmp = (((double)this.queueCurrentSize)/((double)this.queueTotalSize)*100); + result = tmp.intValue(); + } + return result; + } + + public int getCurrentPercent() { + int result = 0; + if (this.fileTotalSize != 0) { + Double tmp =((double)this.fileCurrentSize)/((double)this.fileTotalSize)*100; + result = tmp.intValue(); + } + return result; + } + + public long getQueueTotalSize() { + return this.queueTotalSize; + } + + public long getFileTotalSize() { + return this.fileTotalSize; + } + + public boolean pause() { + if (this.copyOp != null) { + this.copyOp.setPause(); + return true; + } else { + return false; + } + } + + public void unpause() { + if (this.copyOp != null) { + this.copyOp.unpause(); + } + } + + public int basketLength(){ + return this.basket.getLength(); + } + + public void clearBasket() { + this.basket = new Basket(); + } + + + /** Depending on platform, some characters in file's name are + * forbidden. So we just create a temp file and try to rename + * it to know if the name is accepted by the host OS. + * @param fileName new name the user wants to use + * @param dirPath the destination directory path + */ + public boolean nameIsAcceptable(String fileName,String dirPath){ + + boolean result = false; + + try { + + File temp = File.createTempFile("MiniCopier",".tmp"); + + String newPath = temp.getParentFile().getAbsolutePath()+ + File.separator + fileName; + + File newFile = new File(newPath); + + if (temp.renameTo(newFile)) { + result = true; //name is correct for the OS + } + + temp.delete(); + newFile.delete(); + + } catch (Exception e) { + e.printStackTrace(); + } + + //Just making sure the new target isn't a directory + if (result) { + File dirCheck = new File(dirPath + File.separator + fileName); + if (dirCheck.isDirectory()) result = false; + } + + return result; + } + + public static boolean isNotLink(File file) + { + try { + if (!file.exists()) + return true; + else + { + String canonpath = file.getCanonicalPath(); + //System.out.println(canonpath); + String abspath = file.getAbsolutePath(); + //System.out.println(abspath); + return abspath.equals(canonpath); + } + } + catch(IOException ex) { + System.err.println(ex); + return true; + } + } + + /** + * @param args + */ + public static void main(String[] args) { + + //Initialization + Configuration conf = new Configuration(); + Language.init(Configuration.language); + + for (String arg : args) { + //deprecated + //if (arg.equals("-nolnf")) Configuration.nativeLnF = false; + if (arg.equals("-nolnf")) System.out.println("-nolnf : Option no longer available! \n" + + "Please edit \"configuration.ini\" to set the look and feel."); + } + + Copier copier = new Copier(); + MainFrame frame = new MainFrame(copier); + + //Display a welcome dialog to the user for the first launch + if (conf.wasFirstLaunch()) DialogMsg.displayWelcome(); + + frame.setAlwaysOnTop(Configuration.alwaysOnTop); + + } + + + private class ForceTreatment extends Thread { + public void run() { + treatQueue(); + } + } +} + + diff --git a/src/minicopier/FailedQueue.java b/src/minicopier/FailedQueue.java new file mode 100644 index 0000000..f2a7d1b --- /dev/null +++ b/src/minicopier/FailedQueue.java @@ -0,0 +1,118 @@ +/* + FailedQueue.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier; + +import java.util.Vector; + +import javax.swing.table.DefaultTableModel; +import minicopier.gui.DialogMsg; +import minicopier.i18n.Language; + +public class FailedQueue { + +private Copier copier; + + private Vector failedItems; + private DefaultTableModel tableModel; + + + public FailedQueue(Copier c) { + this.copier = c; + this.failedItems = new Vector(); + + String[] columnsNames = {Language.get("MainFrame.TransfersPane.sourceRow"), + Language.get("MainFrame.TransfersPane.sizeRow"), + Language.get("MainFrame.TransfersPane.destinationRow")}; + this.tableModel = new FailedModel(columnsNames,0); + } + + public DefaultTableModel getTableModel() { + return this.tableModel; + } + + + public synchronized boolean isEmpty(){ + return this.failedItems.isEmpty(); + } + + + public synchronized void addFile(FileToTransfer file){ + this.failedItems.add(file); + String[] data = {file.getSourcePath(), + DialogMsg.prettySize(file.getSize()), + file.getDestinationFolderPath() + }; + this.tableModel.addRow(data); + } + + public int[] remove(int i[]) { + + int[] result = {-1,-1}; + int length = i.length; + + int k = 0; + int ind; + int del = 0; + + for (k=0; k<=length-1; k++){ + ind = i[k] - del; + this.failedItems.removeElementAt(ind); + this.tableModel.removeRow(ind); + del++; + } + return result; + } + + public int[] retry(int i[]) { + + int[] result = {-1,-1}; + int length = i.length; + + int k = 0; + int ind; + int del = 0; + + for (k=0; k<=length-1; k++){ + ind = i[k] - del; + copier.addFile2Queue(this.failedItems.get(ind)); + this.failedItems.removeElementAt(ind); + this.tableModel.removeRow(ind); + del++; + } + + copier.forceStart(); + return result; + } + + + + + private class FailedModel extends DefaultTableModel{ + + public FailedModel(Object[] s,int i){ + super(s,i); + } + + public boolean isCellEditable(int row, int column) { + return false; + } + } + +} diff --git a/src/minicopier/FileToTransfer.java b/src/minicopier/FileToTransfer.java new file mode 100644 index 0000000..5d4de9b --- /dev/null +++ b/src/minicopier/FileToTransfer.java @@ -0,0 +1,114 @@ +/* + FileToTransfer.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier; +import java.io.*; + +/** FileToTransfer represents a tranfer requested by the user. + * It contains all the informations about the source file, and where + * it must be copied, according to the user. + */ + +public class FileToTransfer { + + //Source file path + private String sourcePath; + + //File's copy destination path (folder) + private String destinationPath; + + //Destination File Path + private String destinationFilePath; + + //Name of source file + private String name; + + //Size of source file + private long size; + + /** Creation of a FileToTransfer + * @param _path2source system path to the source file + * @param _path2destinationfolder system path to the destination folder + */ + public FileToTransfer(String _path2source, + String _path2destinationfolder) { + + //Using the argument Strings directly work, but the + //display is not fine for Windows, so we create Files + //and get the path (OS-dependent). + + //this.sourcePath = _path2source; + + File sourceFile = new File(_path2source); + + this.sourcePath = sourceFile.getPath(); + this.name = sourceFile.getName(); + this.size = sourceFile.length(); + + //this.destinationPath = _path2destinationfolder; + + + File destFolder = new File(_path2destinationfolder); + this.destinationPath = destFolder.getPath(); + + this.destinationFilePath = this.destinationPath + + File.separator + this.name; + + + } + + public String getSourcePath() { + return this.sourcePath; + } + + public File getSourceFile() { + return new File(this.sourcePath); + } + + public String getDestinationFolderPath() { + return this.destinationPath; + } + + public File getDestinationFolder() { + return new File(this.getDestinationFolderPath()); + } + + public String getDestinationFilePath() { + return this.destinationFilePath; + + } + + public File getDestinationFile() { + return new File(this.getDestinationFilePath()); + } + + public String getName() { + return this.name; + } + + public long getSize() { + return this.size; + } + + public void changeTargetName(String newName){ + this.destinationFilePath = this.destinationPath + File.separator + + newName; + } + +} diff --git a/src/minicopier/MainQueue.java b/src/minicopier/MainQueue.java new file mode 100644 index 0000000..b73d84d --- /dev/null +++ b/src/minicopier/MainQueue.java @@ -0,0 +1,264 @@ +/* + MainQueue.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier; + + +import java.util.Vector; +import javax.swing.table.*; + +import minicopier.gui.DialogMsg; + +import minicopier.i18n.Language; + +public class MainQueue { + + private Copier copier; + + private Vector mainQueue; + + private DefaultTableModel tableModel; + + + public MainQueue(Copier c) { + this.copier = c; + this.mainQueue = new Vector(); + + String[] columnsNames = {Language.get("MainFrame.TransfersPane.sourceRow"), + Language.get("MainFrame.TransfersPane.sizeRow"), + Language.get("MainFrame.TransfersPane.destinationRow")}; + this.tableModel = new TransfersModel(columnsNames,0); + } + + public synchronized void addFile(FileToTransfer file){ + this.mainQueue.add(file); + String[] data = {file.getSourcePath(), + DialogMsg.prettySize(file.getSize()), + file.getDestinationFolderPath() + }; + this.tableModel.addRow(data); + this.copier.increaseQueueTotalSize(file.getSize()); + } + + public DefaultTableModel getTableModel() { + return this.tableModel; + } + + /** Remove and send the first element in queue */ + public synchronized FileToTransfer extractFirst(){ + FileToTransfer first = this.mainQueue.firstElement(); + this.mainQueue.remove(0); + this.tableModel.removeRow(0); + return first; + } + + public synchronized boolean isEmpty(){ + return this.mainQueue.isEmpty(); + } + + public int[] putFirst(int[] i){ + int[] result = {-1,-1}; + int length = i.length; + int k = length-1; + int ind; + int del = 0; + + for (k=length-1; k>=0; k--){ + ind = i[k] + del; + this.mainQueue.add(0,mainQueue.elementAt(ind)); + this.mainQueue.remove(ind+1); + this.tableModel.moveRow(ind,ind,0); + del++; + } + result[0] = 0; + result[1] = length-1; + + /* + if (i < mainQueue.size() && i >= 0) { + this.mainQueue.add(0,mainQueue.elementAt(i)); + this.tableModel.insertRow(0,(Vector)tableModel.getDataVector().elementAt(i)); + this.mainQueue.remove(i+1); + this.tableModel.removeRow(i+1); + result = 0; + } + //System.out.println("Sortie putFirst"); + * + */ + return result; + } + + public int[] putLast(int i[]){ + + int[] result = {-1,-1}; + int length = i.length; + int k = 0; + int ind; + int del = 0; + + for (k=0; k<=length-1; k++){ + ind = i[k] - del; + this.mainQueue.add(mainQueue.elementAt(ind)); + this.mainQueue.remove(ind); + this.tableModel.addRow((Vector)tableModel.getDataVector().elementAt(ind)); + this.tableModel.removeRow(ind); + del++; + } + + result[1] = tableModel.getRowCount()-1; + result[0] = result[1]-(length-1); + + /* + int result = -1; + if (i < mainQueue.size() && i >= 0) { + this.mainQueue.add(mainQueue.elementAt(i)); + this.tableModel.addRow((Vector)tableModel.getDataVector().elementAt(i)); + this.mainQueue.removeElementAt(i); + this.tableModel.removeRow(i); + result = (tableModel.getRowCount()-1); + } + */ + + return result; + } + + public int[] putUp(int[] i) { + + int[] result = {-1,-1}; + int length = i.length; + int start = i[0]; + + int k = length-1; + int ind; + //we usually start inserting at "start-1" except when + //start==0 (first element selected) + int toInsert = (start == 0 ? 0 : start-1); + int del = 0; + + for (k=length-1; k>=0; k--){ + ind = i[k] + del; + this.mainQueue.add(toInsert,mainQueue.elementAt(ind)); + this.mainQueue.remove(ind+1); + this.tableModel.moveRow(ind,ind,toInsert); + del++; + } + + result[0]=toInsert; + result[1]=toInsert+length-1; + + /* + int result = -1; + //we do it for all, except for the first one + if (i < mainQueue.size() || i > 0) { + FileToTransfer f = mainQueue.elementAt(i-1); + mainQueue.setElementAt(mainQueue.elementAt(i),i-1); + mainQueue.setElementAt(f,i); + tableModel.moveRow(i,i,i-1); + + result = i-1; + } + */ + + return result; + } + + public int[] putDown(int i[]) { + + int[] result = {-1,-1}; + int length = i.length; + int last = i[length-1]; + + int k = 0; + int ind; + + //we usually start inserting at "last+2" except when + //last==length-1 (last element selected) + int toInsert = (last == mainQueue.size()-1 ? mainQueue.size() : last+2); + int del =0; + + for (k=0; k<=length-1; k++){ + ind = i[k] - del; + this.mainQueue.add(toInsert,mainQueue.elementAt(ind)); + this.mainQueue.remove(ind); + this.tableModel.moveRow(ind,ind,toInsert-1); + del++; + } + + result[1] = toInsert-1; + result[0] = result[1]-(length-1); + + + /* + int result = -1; + //don't consider the last one + if (i < (mainQueue.size()-1) || i >= 0) { + FileToTransfer f = mainQueue.elementAt(i+1); + mainQueue.setElementAt(mainQueue.elementAt(i),i+1); + mainQueue.setElementAt(f,i); + tableModel.moveRow(i,i,i+1); + + result = i+1; + }*/ + + return result; + } + + public int[] remove(int i[]) { + + int[] result = {-1,-1}; + int length = i.length; + + int k = 0; + int ind; + int del = 0; + + for (k=0; k<=length-1; k++){ + ind = i[k] - del; + this.copier.decreaseQueueTotalSize(mainQueue.elementAt(ind).getSize()); + this.mainQueue.removeElementAt(ind); + this.tableModel.removeRow(ind); + del++; + } + + + /* + int result = -1; + if (i < mainQueue.size() && i >= 0) { + this.copier.decreaseQueueTotalSize(mainQueue.elementAt(i).getSize()); + this.mainQueue.removeElementAt(i); + this.tableModel.removeRow(i); + result = i; + }*/ + + + return result; + } + + public class TransfersModel extends DefaultTableModel{ + + public TransfersModel(Object[] s,int i){ + super(s,i); + } + + public boolean isCellEditable(int row, int column) { + return false; + } + + } + +} diff --git a/src/minicopier/ThroughputLimitInputStream.java b/src/minicopier/ThroughputLimitInputStream.java new file mode 100644 index 0000000..0e8211e --- /dev/null +++ b/src/minicopier/ThroughputLimitInputStream.java @@ -0,0 +1,260 @@ +/* + ThroughputLimitInputStream.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + * This file is adapted from the one of muCommander, http://www.mucommander.com + * Copyright (C) 2002-2008 Maxence Bernard + */ + +package minicopier; + +import java.io.IOException; +import java.io.InputStream; + +/** + * ThroughputLimitInputStream extends InputStream to provide control over the transfer speed and limit it to a specified + * number of bytes per second. + * Whenever the bytes per second quota has been reached, the read and skip methods will lock and won't return + * until either: + *
    a new second commences, bringing the bytes read count back to zero for the new second + *
  • {@link #setThroughputLimit(long)} is called with a more permissive bytes per second value (different from 0), + * yielding to more bytes available for the current second. + * + *

    Setting the throughput limit to 0 effectively blocks all read and skip calls indefinitely. + * Any calls to the read or skip methods will lock, the only way to remove this lock being to call the + * {@link #setThroughputLimit(long)} method with a value different from 0 from another thread. + * + *

    Setting the throughput limit to -1 or any other negative values will disable any limit and make + * this ThroughputLimitInputStream behave just like a normal InputStream. + * + *

    Finally, the {@link #setUnderlyingInputStream(java.io.InputStream)} method allows to use the + * same ThroughputLimitInputStream instance for multiple InputStream instances, keeping the bytes count for the + * current second intact and thus the throughput limit stable. This does not hold true if a new ThroughputLimitInputStream + * is created for each InputStream, the bytes count for the current second starting at 0. + * + * @author Maxence Bernard + */ +public class ThroughputLimitInputStream extends InputStream { + + /** Underlying InputStream */ + private InputStream in; + + + /** Holds the current second, allowing to detect when a new second commences */ + private long currentSecond; + + /** Number of bytes that have been read or skipped this second */ + private long nbBytesReadThisSecond; + + + /** + * Creates a new ThroughputLimitInputStream with no initial throughput limit (-1 value). + * + * @param in underlying stream that is used to read data from + */ + public ThroughputLimitInputStream(InputStream in) { + this.in = in; + } + + /** + * Creates a new ThroughputLimitInputStream with an initial throughput limit. + * + * @param in underlying stream that is used to read data from + * @param bytesPerSecond initial throughput limit in bytes per second + * @see #setThroughputLimit(long) + */ + public ThroughputLimitInputStream(InputStream in, long bytesPerSecond) { + this.in = in; + } + + + /** + * Changes the underlying InputStream which data is read from, keeping the bytes count for the current second intact. + * + *

    Note: the existing underlying InputStream will not be closed, the {@link #close()} method must be called prior + * to calling this method. + * + * @param in the new InputStream to read data from + */ + public void setUnderlyingInputStream(InputStream in) { + this.in = in; + } + + + /** + * Returns the number of bytes that can be read (or skipped) without exceeding the current throughput limit. + * This method blocks until at least 1 byte is available. In other words the method always returns + * strictly positive values. + * + *

    If the current throughput limit is negative (no limit), this method returns immediately Integer.MAX_VALUE. + *

    If the byte quota for the current second has been exceeded, this method locks and returns as soon as a new second + * has started (i.e. bytes are available), or the {@link #setThroughputLimit(long)} with a more permissive value + * has been called. + *

    If the current throughput limit is 0, it will lock undefinitely, until {@link #setThroughputLimit(long)} has + * been called from another thread with a value different from 0. + * + * @return the number of bytes available for reading without exceeding the current throughput limit + */ + private int getNbAllowedBytes() { + + // Update limit counter and retrieve number of milliseconds until next second + long msUntilNextSecond = updateLimitCounter(); + + long allowedBytes; + + long bpsLimit = Configuration.getSpeedValue(); + + synchronized(this) { + // Loop while throughput limit has been exceeded + while((allowedBytes=bpsLimit- nbBytesReadThisSecond)<=0) { + // Throughput limit was removed, return max int value + if(bpsLimit<0) + return Integer.MAX_VALUE; + + try { + // If limit is 0, wait indefinitely for a call to notify() from setThroughputLimit() + if(bpsLimit==0) + wait(); + // Wait until the current second is over for more bytes to be available, + // or until a call to notify() is made from setThroughputLimit() + else { + wait(msUntilNextSecond); + } + } + catch(InterruptedException e) { + // No problem in this unlikely event, loop one more time and wait some more + } + + // Update limit counter and retrieve number of milliseconds until next second + msUntilNextSecond = updateLimitCounter(); + } + } + + return (int)allowedBytes; + } + + + /** + * Checks if the current second has changed. If that's the case, updates the current second value and resets the + * number of bytes read this second. Returns the number of milliseconds until a new second starts. + */ + private long updateLimitCounter() { + long now = System.currentTimeMillis(); + long nowSecond = now/1000; + + // Current second has changed + if(this.currentSecond!=nowSecond) { + this.currentSecond = nowSecond; + this.nbBytesReadThisSecond = 0; + } + + return 1000-(now%1000); + } + + + /** + * Increases the number of bytes read this second to the given number. + * + * @param nbRead number of bytes that have been read or skipped from the underlying stream. + */ + private void addToLimitCounter(long nbRead) { + updateLimitCounter(); + + this.nbBytesReadThisSecond += nbRead; + } + + + //////////////////////////////// + // InputStream implementation // + //////////////////////////////// + + public int read() throws IOException { + + long bpsLimit = Configuration.getSpeedValue(); + + // Wait until at least 1 byte is available if a limit is set + if(bpsLimit>=0) + getNbAllowedBytes(); + + // Read the byte from the underlying stream + int i = in.read(); + + // Increase read counter by 1 + if(i>0) + addToLimitCounter(1); + + return i; + } + + public int read(byte[] bytes) throws IOException { + return this.read(bytes, 0, bytes.length); + } + + public int read(byte[] bytes, int off, int len) throws IOException { + int nbRead; + + long bpsLimit = Configuration.getSpeedValue(); + + // Wait until at least 1 byte is available if a limit is set and try to read as many bytes are available + // without exceeding the throughput limit or the number specified + if(bpsLimit>=0 && Configuration.limitSpeed) + nbRead = in.read(bytes, off, Math.min(getNbAllowedBytes(),len)); + else + nbRead = in.read(bytes, off, len); + + // Increase read counter by the number of bytes that have actually been read by the underlying stream + if(nbRead>0) + addToLimitCounter(nbRead); + + return nbRead; + } + + public long skip(long l) throws IOException { + + long bpsLimit = Configuration.getSpeedValue(); + + long nbSkipped = in.skip(bpsLimit>=0?Math.min(getNbAllowedBytes(),l):l); + + // Increase read counter by the number of bytes that have actually been skipped by the underlying stream + if(nbSkipped>0) + addToLimitCounter(nbSkipped); + + return nbSkipped; + } + + public int available() throws IOException { + return in.available(); + } + + public void close() throws IOException { + in.close(); + } + + public synchronized void mark(int i) { + in.mark(i); + } + + public synchronized void reset() throws IOException { + in.reset(); + } + + public boolean markSupported() { + return in.markSupported(); + } +} \ No newline at end of file diff --git a/src/minicopier/TransferTask.java b/src/minicopier/TransferTask.java new file mode 100644 index 0000000..874dd03 --- /dev/null +++ b/src/minicopier/TransferTask.java @@ -0,0 +1,185 @@ +/* + TransferTask.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier; +import java.io.*; + + +public class TransferTask extends Thread{ + + private Copier copier; + + private File source; + + private File destination; + + private File destinationFolder; + + private Boolean append; + + private Boolean paused; + + private Boolean cancel; + + private long totalSize; + + private long currentSize; + + boolean completed; + + boolean putInQueue; + + + + public TransferTask(Copier c, FileToTransfer file, boolean _app){ + this.copier = c; + this.source=file.getSourceFile(); + this.destination=file.getDestinationFile(); + this.destinationFolder=file.getDestinationFolder(); + this.append=_app; + this.paused = false; + this.cancel = false; + this.totalSize = file.getSize(); + this.currentSize = 0; + this.completed = false; + this.putInQueue = false; + } + + + public boolean getCompleted(){ + return this.completed; + } + + + public void setPause() { + this.paused = true; + } + + public void unpause() { + synchronized(this) { + this.paused = false; + this.notifyAll(); + } + } + + public void setCancelAndQueue(boolean _putInQueue){ + this.cancel = true; + this.putInQueue = _putInQueue; + } + + public boolean getPutInQueue(){ + return this.putInQueue; + } + + public boolean getCancel() { + return this.cancel; + } + + public void run() { + // Streams declarations + ThroughputLimitInputStream sourceFile = null; + java.io.FileOutputStream destinationFile = null; + + // Read by segment of 0.5Mo + int bufferSize = 512*1024; + + this.copier.setFileTotalSize(this.totalSize); + this.copier.setFileCurrentSize(0); + + try { + //creation of directories (if needed) + this.destinationFolder.mkdirs(); + // Creation of file (nothing done if already present) + this.destination.createNewFile(); + + // Opening streams + sourceFile = new ThroughputLimitInputStream(new java.io.FileInputStream(source)); + destinationFile = new java.io.FileOutputStream(destination,this.append); + + if (append) { + // Skip already copied bytes + long already_copied = destination.length(); + long jump_source; + jump_source = sourceFile.skip(already_copied); + + //Might be useless (?) + //risk of skipping too much (javadoc) + while (jump_source != already_copied) { + sourceFile = new ThroughputLimitInputStream(new java.io.FileInputStream(source)); + jump_source = sourceFile.skip(already_copied); + } + + this.copier.increaseFileCurrentSize(jump_source); + } + + // Read by segment of 0.5Mo + byte buffer[]=new byte[bufferSize]; + int nbReads; + + while( (nbReads = sourceFile.read(buffer)) != -1 ) { + destinationFile.write(buffer, 0, nbReads); + + this.copier.increaseFileCurrentSize(nbReads); + + if (this.paused) { + synchronized(this) { + try{ + this.copier.setPause(true); + System.out.println("COPY PAUSED"); + wait(); + this.copier.setPause(false); + System.out.println("COPY RESUMED"); + } + catch (InterruptedException e) {} + } + + } + + if (this.cancel) { //Skip or cancel requested by user + return; + } + + } + + // Copy successful + this.completed = true; + //System.out.println("Transfer completed !"); + + } catch( java.io.FileNotFoundException f ) { + + } catch( java.io.IOException e ) { + System.out.println("IO Exception !"); + + } finally { + // Whatever happens, closing streams + try { + sourceFile.close(); + } catch(Exception e) { + System.out.println("Error closing source file !"); + } + try { + destinationFile.close(); + } catch(Exception e) { + System.out.println("Error closing destination file !"); + } + } + + } + +} diff --git a/src/minicopier/configuration.ini b/src/minicopier/configuration.ini new file mode 100644 index 0000000..6f8a987 --- /dev/null +++ b/src/minicopier/configuration.ini @@ -0,0 +1,21 @@ +#MiniCopier startup configuration file + +#Language +#default : "SystemLocale" +#You can force with : "fr", "en", ... +language=Systemlocale + +#Always on top +alwaysontop=true + +#Look and Feel +#You can try with : "pgs","native","metal" +lnf=pgs + +#Collision default action +#"ask","cancel","overwrite","overwrite_older","resume" +collision=ask + +#Symbolic links +#"follow","ignore" +symlinks=follow diff --git a/src/minicopier/gui/Credits.java b/src/minicopier/gui/Credits.java new file mode 100644 index 0000000..e82ea24 --- /dev/null +++ b/src/minicopier/gui/Credits.java @@ -0,0 +1,165 @@ +/* + Credits.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier.gui; + +import java.awt.Component; +import javax.swing.*; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import minicopier.i18n.Language; +import minicopier.Copier; + +public class Credits extends JDialog { + + private JTextArea credits; + + private JTextArea license; + + private JButton ok; + + public Credits(JFrame owner){ + super(owner,true); + + this.setTitle(Language.get("Credits.Title")); + this.setSize(360, 300); + this.setLocationRelativeTo(null); + + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + mainPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + mainPanel.setBorder(BorderFactory.createEmptyBorder(7,7,7,7)); + + //Panel with logo and version number + JPanel logoPanel = new JPanel(); + logoPanel.setLayout(new BoxLayout(logoPanel, BoxLayout.Y_AXIS)); + logoPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + + JLabel logo = new JLabel(""); + logo.setIcon(new ImageIcon(MainFrame.frame.imageFromJar("img/minicopier-logo.png"))); + + JLabel version = new JLabel(Language.get("Credits.Version")+" : "+Copier.version); + logoPanel.add(logo); + logoPanel.add(version); + logo.setAlignmentX(Component.CENTER_ALIGNMENT); + version.setAlignmentX(Component.CENTER_ALIGNMENT); + + //Tabbed pane with credits and license + JTabbedPane tabbedPane = new JTabbedPane(); + + //Credits Panel + JPanel creditsPanel = new JPanel(); + creditsPanel.setLayout(new BorderLayout()); + + this.credits = new JTextArea(); + credits.setEditable(false); + credits.setWrapStyleWord(true); + credits.setLineWrap(true); + credits.setText(getCreditsText()); + credits.setCaretPosition(0); + credits.setTabSize(1); + + JScrollPane jspCredits = new JScrollPane(credits); + creditsPanel.add(jspCredits,BorderLayout.CENTER); + + //License Panel + JPanel licensePanel = new JPanel(); + licensePanel.setLayout(new BorderLayout()); + + this.license = new JTextArea(); + license.setEditable(false); + license.setWrapStyleWord(true); + license.setLineWrap(true); + license.setText(getLicenseText()); + license.setCaretPosition(0); + credits.setTabSize(1); + + JScrollPane jspLicense = new JScrollPane(license); + licensePanel.add(jspLicense,BorderLayout.CENTER); + + + tabbedPane.add(Language.get("Credits.Title"),creditsPanel); + tabbedPane.add(Language.get("Credits.License.Title"),licensePanel); + + mainPanel.add(logoPanel); + mainPanel.add(tabbedPane); + + this.ok = new JButton("OK"); + this.ok.addActionListener(new ActionOK()); + mainPanel.add(Box.createVerticalStrut(5)); + mainPanel.add(ok); + ok.setAlignmentX(Component.CENTER_ALIGNMENT); + + this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); + this.getContentPane().add(mainPanel); + this.setVisible(true); + } + + public String getCreditsText() { + StringBuffer credits = new StringBuffer(); + + credits.append(Language.get("Credits.Credits.Homepage")+"\n"); + credits.append("http://www.adriancourreges.com/projects/minicopier\n\n"); + credits.append(Language.get("Credits.Credits.Development")+"\n"); + credits.append(Language.get("Credits.Credits.Developers")+"\n\n"); + credits.append(Language.get("Credits.Credits.LanguageTranslation")+"\n"); + credits.append(Language.get("Credits.Credits.LanguageContributors")+"\n\n"); + credits.append(Language.get("Credits.Credits.SendMail")+"\n"); + credits.append("a.courreges@gmail.com"); + + return credits.toString(); + } + + public String getLicenseText() { + StringBuffer license = new StringBuffer(); + + license.append(Language.get("Credits.License.Intro")+"\n\n"); + license.append("Copyright (C) Adrian Courreges\n\n"); + license.append("This program is free software; you can redistribute it and/or modify " + + "it under the terms of the GNU General Public License as published by " + + "the Free Software Foundation; either version 2 of the License, or " + + " (at your option) any later version.\n\n" + + "This program is distributed in the hope that it will be useful, " + + "but WITHOUT ANY WARRANTY; without even the implied warranty of " + + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the " + + "GNU General Public License for more details.\n\n" + + "You should have received a copy of the GNU General Public License " + + "along with this program; see the file COPYING. If not, write to the " + + "Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA."); + + return license.toString(); + + } + + public void exit(){ + this.dispose(); + } + + private class ActionOK implements ActionListener { + public void actionPerformed(ActionEvent e) { + exit(); + } + } + +} diff --git a/src/minicopier/gui/DialogMsg.java b/src/minicopier/gui/DialogMsg.java new file mode 100644 index 0000000..b98ae31 --- /dev/null +++ b/src/minicopier/gui/DialogMsg.java @@ -0,0 +1,277 @@ +/* + DialogMsg.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier.gui; +import javax.swing.JOptionPane; +import minicopier.i18n.Language; + +public class DialogMsg { + + //byte + private static String b; + + private static String kb; //kB + + private static String mb; //MB + + private static String gb; //GB + + private static String tb; //TB + + private static String bps, kbps, mbps, gbps, tbps; + + private static String day, hour, min, sec; + + public static void init() { + b = Language.get("DialogMsg.byte"); + kb = Language.get("DialogMsg.kilobyte"); + mb = Language.get("DialogMsg.megabyte"); + gb = Language.get("DialogMsg.gigabyte"); + tb = Language.get("DialogMsg.terabyte"); + bps = Language.get("DialogMsg.byteps"); + kbps = Language.get("DialogMsg.kilobyteps"); + mbps = Language.get("DialogMsg.megabyteps"); + gbps = Language.get("DialogMsg.gigabyteps"); + tbps = Language.get("DialogMsg.terabyteps"); + day = "d"; + hour = "h"; + min = "m"; + sec = "s"; + } + + + + + public static boolean displayBasketInstructions(boolean empty) { + boolean clearBasket = false; + if (empty) { //Basket is empty, we display use instructions + JOptionPane.showMessageDialog(MainFrame.frame, + Language.get("DialogMsg.displayBasketInstructions.EmptyText"), + Language.get("DialogMsg.displayBasketInstructions.Title"), + JOptionPane.INFORMATION_MESSAGE); + } else { //Basket not empty, possibility to clear + Object[] options = { + Language.get("DialogMsg.displayBasketInstructions.ClearButton"), + Language.get("DialogMsg.displayBasketInstructions.CancelButton")}; + int n = JOptionPane.showOptionDialog(MainFrame.frame, + Language.get("DialogMsg.displayBasketInstructions.FullText"), + Language.get("DialogMsg.displayBasketInstructions.Title"), + JOptionPane.YES_NO_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[1]); + clearBasket = (n==0); + + } + return clearBasket; + } + + + public static int confirmSkip(){ + Object[] options = {Language.get("DialogMsg.confirmSkip.QueueButton"), + Language.get("DialogMsg.confirmSkip.NoButton"), + Language.get("DialogMsg.confirmSkip.CancelButton")}; + int n = JOptionPane.showOptionDialog(MainFrame.frame, + Language.get("DialogMsg.confirmSkip.Text"), + Language.get("DialogMsg.confirmSkip.Title"), + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[2]); + return n; + } + + + + + public static String askNewName(String oldName) { + return JOptionPane.showInputDialog(MainFrame.frame, + Language.get("DialogMsg.askNewName.Text"), + oldName); + } + + public static void renameError(){ + JOptionPane.showMessageDialog(MainFrame.frame, + Language.get("DialogMsg.renameError.Text"), + Language.get("DialogMsg.renameError.Title"), + JOptionPane.ERROR_MESSAGE); + + } + + public static void errorOverwrite() { + JOptionPane.showMessageDialog(MainFrame.frame, + Language.get("DialogMsg.errorOverwrite.Text"), + Language.get("DialogMsg.errorOverwrite.Title"), + JOptionPane.ERROR_MESSAGE); + } + + public static int alreadyExistsChoice(String sourcePath, long sourceSize, + String targetPath, long destSize){ + Object[] options = {Language.get("DialogMsg.alreadyExistsChoice.OverwriteButton"), + Language.get("DialogMsg.alreadyExistsChoice.ResumeButton"), + Language.get("DialogMsg.alreadyExistsChoice.RenameButton"), + Language.get("DialogMsg.alreadyExistsChoice.CancelButton")}; + int n = JOptionPane.showOptionDialog(MainFrame.frame, + Language.get("DialogMsg.alreadyExistsChoice.Text1") + + Language.get("DialogMsg.alreadyExistsChoice.Source") + + " : "+ prettySize(sourceSize)+ "\n"+ + sourcePath + + Language.get("DialogMsg.alreadyExistsChoice.Target")+ + " : "+ prettySize(destSize)+ "\n" + +targetPath+"\n\n" + + Language.get("DialogMsg.alreadyExistsChoice.Text2"), + Language.get("DialogMsg.alreadyExistsChoice.Title"), + JOptionPane.YES_NO_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[3]); + return n; + } + + + public static boolean transferErrorChoice(){ + Object[] options = {Language.get("DialogMsg.transferErrorChoice.QueueButton"), + Language.get("DialogMsg.transferErrorChoice.AbortButton")}; + int n = JOptionPane.showOptionDialog(MainFrame.frame, + Language.get("DialogMsg.transferErrorChoice.Text"), + Language.get("DialogMsg.transferErrorChoice.Title"), + JOptionPane.YES_NO_OPTION, + JOptionPane.ERROR_MESSAGE, + null, + options, + options[0]); + return (n==0); + } + + public static void displayWelcome(){ + JOptionPane.showMessageDialog(null, + Language.get("DialogMsg.welcome.Text"), + Language.get("DialogMsg.welcome.Title"), + JOptionPane.INFORMATION_MESSAGE); + } + + public static void displayCredits() { + JOptionPane.showMessageDialog(MainFrame.frame, + "MiniCopier - 0.2\nNovember 2007\nAdrian Courrèges\n\n" + +"For bug reports or suggestions,\n" + +"please, send an email to :\n" + +"a.courreges@gmail.com", + "Credits", + JOptionPane.INFORMATION_MESSAGE); + } + + /** Conversion of a long representing the size of a file + * in bytes into a user-friendly String. + * e.g. : 71430144 -> "68.12 MB" + * @param s size in bytes + */ + public static String prettySize(long s){ + String unit = ""; + double cut; + + if (s < 1024) { + return s + " " + b; + } else if (s < Math.pow(1024,2)){ + cut = ((double) s) / Math.pow(1024,1); + unit = " " + kb; + } else if (s < Math.pow(1024,3)) { + cut = ((double) s) / Math.pow(1024,2); + unit = " " + mb; + } else if (s < Math.pow(1024,4)) { + cut = ((double) s) / Math.pow(1024,3); + unit = " " + gb; + } else { + cut = ((double) s) / Math.pow(1024,4); + unit = " " + tb; + } + + cut *= 100.0; + cut = Math.floor(cut+0.5); + cut /= 100.0; + + return cut + unit; + } + + public static String prettySpeed(long s){ + String unit = ""; + double cut; + + if (s < 1024) { + return s + " " + bps; + } else if (s < Math.pow(1024,2)){ + cut = ((double) s) / Math.pow(1024,1); + unit = " " + kbps; + } else if (s < Math.pow(1024,3)) { + cut = ((double) s) / Math.pow(1024,2); + unit = " " + mbps; + } else if (s < Math.pow(1024,4)) { + cut = ((double) s) / Math.pow(1024,3); + unit = " " + gbps; + } else { + cut = ((double) s) / Math.pow(1024,4); + unit = " " + tbps; + } + + cut *= 100.0; + cut = Math.floor(cut+0.5); + cut /= 100.0; + + return cut + unit; + } + + public static String prettyTime(long t){ + StringBuffer sb = new StringBuffer(); + if (t < 60) { // x sec + sb.append(t).append(sec); + return sb.toString(); + } + if (t < 3600) { // x min y sec + sb.append(t/60).append(min).append(" "); + t = t % 60; + if (t > 0) { + //if (t<10) sb.append("0"); + sb.append(t).append(sec); + } + + return sb.toString(); + } + if (t < 86400) {// x h y min + sb.append(t/3600).append(hour).append(" "); + t = t % 3600; + if (t/60 > 0) { + //if (t<10) sb.append("0"); + sb.append(t/60).append(min); + } + + return sb.toString(); + } + // x days y hours + sb.append(t/86400).append(day).append(" "); + t = t % 86400; + if (t/3600 > 0) { + sb.append(t/3600).append(hour); + } + + return sb.toString(); + } + +} diff --git a/src/minicopier/gui/FailedPanel.java b/src/minicopier/gui/FailedPanel.java new file mode 100644 index 0000000..e559cd2 --- /dev/null +++ b/src/minicopier/gui/FailedPanel.java @@ -0,0 +1,96 @@ +/* + FailedPanel.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier.gui; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; + +import javax.swing.*; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableModel; + +import minicopier.i18n.Language; + +public class FailedPanel extends JPanel { + + +private MainFrame mainFrame; + + protected QueueJButton retry; + + protected QueueJButton removeFailed; + + protected DefaultTableModel failedModel; + + protected JTable failedList; + + public FailedPanel(MainFrame f){ + super(); + + this.mainFrame = f; + + this.retry = new QueueJButton("img/retry.gif"); + this.retry.setToolTipText(Language.get("Tooltip.Failed.Retry")); + + + this.removeFailed = new QueueJButton("img/delete.gif"); + this.removeFailed.setToolTipText(Language.get("Tooltip.Failed.Clear")); + + failedModel = mainFrame.copier.failedItems.getTableModel(); + failedList = new JTable(failedModel); + //transferList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + failedList.getColumnModel().getColumn(1).setCellRenderer(new RightTableCellRenderer()); + failedList.getColumnModel().getColumn(1).setMaxWidth(80); + failedList.getColumnModel().getColumn(1).setMinWidth(80); + + + + this.setLayout(new BorderLayout()); + + + JScrollPane jspFailed = new JScrollPane(failedList); + jspFailed.setViewportView(failedList); + jspFailed.setPreferredSize(new Dimension(40,40)); + //transferList.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + //jspTransfer.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + + + //Subpanel (west) with queue management buttons + JPanel queueButtonsPanel = new JPanel(); + queueButtonsPanel.setLayout(new BoxLayout(queueButtonsPanel, BoxLayout.Y_AXIS)); + queueButtonsPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + queueButtonsPanel.add(this.retry,Component.CENTER_ALIGNMENT); + queueButtonsPanel.add(this.removeFailed); + + + this.add(queueButtonsPanel,BorderLayout.WEST); + this.add(jspFailed,BorderLayout.CENTER); + + + } + + private class RightTableCellRenderer extends DefaultTableCellRenderer { + public RightTableCellRenderer() { + setHorizontalAlignment(RIGHT); + setVerticalAlignment(CENTER); + } + } +} diff --git a/src/minicopier/gui/MainFrame.java b/src/minicopier/gui/MainFrame.java new file mode 100644 index 0000000..22fada4 --- /dev/null +++ b/src/minicopier/gui/MainFrame.java @@ -0,0 +1,580 @@ +/* + MainFrame.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier.gui; + +import minicopier.i18n.Language; + +import java.awt.*; +import java.awt.event.*; +import java.awt.dnd.*; +import java.awt.datatransfer.*; +import java.io.*; +import java.util.*; +import java.util.List; + +import javax.swing.*; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import minicopier.Configuration; +import minicopier.Copier; + + +public class MainFrame extends JFrame{ + + public static MainFrame frame; + + protected Copier copier; + + private SupPanel supPanel; + + private TransfersPanel transfersPanel; + + private FailedPanel failedPanel; + + private OptionsPanel optionsPanel; + + + public MainFrame(Copier cop) { + + super(); + + frame = this; + + DialogMsg.init(); + + //Look And Feel + + if (Configuration.lookAndFeel.equals("pgs")){ + try { + UIManager.setLookAndFeel("com.pagosoft.plaf.PgsLookAndFeel"); + } catch(Exception e) {} + } + + //Classic swing + else { + UIManager.put("swing.boldMetal", Boolean.FALSE); + if (Configuration.lookAndFeel.equals("native")) { + try { + UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); + //SwingUtilities.updateComponentTreeUI(this); + } catch (Exception e) {} + } + } + this.copier = cop; + copier.setGui(this); + + this.setTitle(copier.name); + this.setSize(470, 400); + //this.setAlwaysOnTop(Configuration.alwaysOnTop); + this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + this.setLocationRelativeTo(null); + this.setIconImage(imageFromJar("img/icon.jpg")); + //this.setUndecorated(true); + + this.supPanel = new SupPanel(this); + + this.transfersPanel = new TransfersPanel(this); + + this.failedPanel = new FailedPanel(this); + + this.optionsPanel = new OptionsPanel(this); + + + + //mainPanel includes a JPanel (top) and a JTabbedPane + JPanel mainPanel = new JPanel(); + mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); + mainPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + + mainPanel.add(this.supPanel); + + // Linking the Panel to the controllers + RequestPauseResume actList = new RequestPauseResume(); + ActionSkip skipList = new ActionSkip(); + ActionCancel cancelList = new ActionCancel(); + ActionCredits creditsList = new ActionCredits(); + ActionBasket basketList = new ActionBasket(); + + supPanel.basket.addMouseListener(basketList); + supPanel.pause.addActionListener(actList); + supPanel.skip.addActionListener(skipList); + supPanel.cancel.addActionListener(cancelList); + + DropTargetListener dropL = new Drop2Basket(); + DropTargetListener dropFL = new Drop2Folder(); + DropTarget dropT = new DropTarget(this.supPanel.basket, dropL); + DropTarget dropFT = new DropTarget(this.supPanel.paste, dropFL); + + + + + JTabbedPane tabbedPane = new JTabbedPane(); + + //Adding the transfers list to the first tab + tabbedPane.addTab(Language.get("MainFrame.TransfersPane.title"), new ImageIcon(imageFromJar("img/list.png")) ,transfersPanel); + //Linking the Panel to the controllers + ActionQueueButtons queueButtonsListener = new ActionQueueButtons(); + transfersPanel.putFirst.addActionListener(queueButtonsListener); + transfersPanel.putUp.addActionListener(queueButtonsListener); + transfersPanel.putDown.addActionListener(queueButtonsListener); + transfersPanel.putLast.addActionListener(queueButtonsListener); + transfersPanel.delete.addActionListener(queueButtonsListener); + + tabbedPane.addTab(Language.get("MainFrame.FailedPane.title"), new ImageIcon(imageFromJar("img/failed.gif")) ,failedPanel); + //Linking the Panel to the controllers + ActionFailedButtons failedButtonsListener = new ActionFailedButtons(); + failedPanel.retry.addActionListener(failedButtonsListener); + failedPanel.removeFailed.addActionListener(failedButtonsListener); + + //Adding the options panel to the second tab + tabbedPane.addTab(Language.get("MainFrame.OptionsPane.title"),new ImageIcon(imageFromJar("img/options.png")),optionsPanel); + //Linking the Panel to the controllers + ActionCollision collisionList = new ActionCollision(); + ActionSymbolic symbolicList = new ActionSymbolic(); + + optionsPanel.collisionBox.addActionListener(collisionList); + optionsPanel.symbolicBox.addActionListener(symbolicList); + optionsPanel.credits.addActionListener(creditsList); + optionsPanel.speedLimitBox.addActionListener(new ActionLimitSpeed()); + ActionSpeedChange speedLimitChange = new ActionSpeedChange(); + optionsPanel.speedValue.addChangeListener(speedLimitChange); + optionsPanel.speedUnitBox.addActionListener(speedLimitChange); + + mainPanel.add(tabbedPane); + + + this.getContentPane().add(mainPanel); + this.setVisible(true); + + + RefreshStats refresh = new RefreshStats(500); + refresh.start(); + + } + + public Image imageFromJar(String path){ + return Toolkit.getDefaultToolkit() + .getImage(getClass().getClassLoader().getResource(path)); + } + + + private class RequestPauseResume implements ActionListener { + public void actionPerformed(ActionEvent e) + { + // to synchro (?) + if(copier.getPause()) { //currently paused + copier.unpause(); + } else { //currently copying + copier.pause(); + } + } + } + + + private class Drop2Basket implements DropTargetListener { + public void drop(DropTargetDropEvent dtde) { + + + dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); + + try { + + Transferable tr = dtde.getTransferable(); + DataFlavor[] flavors = tr.getTransferDataFlavors(); + + for (int i = 0; i < flavors.length; i++) { + + DataFlavor fl = flavors[i]; + + Object obj = tr.getTransferData(flavors[i]); + + + //For Windows and Mac + if (DataFlavor.javaFileListFlavor.equals(fl)) { + List files = (List)tr.getTransferData(fl); + + for (File fi : files){ + System.out.println(fi.toURI().toString()); + copier.addURIString2basket(fi.toURI().toString()); + } + + dtde.dropComplete(true); + basketSizeSignal(); + return; + } + + //For Linux and Mac + else if (obj instanceof String && "uri-list".equals(flavors[i].getSubType()) ){ + + String allFilesPath; + allFilesPath = (String)obj; + + Scanner scan = new Scanner(allFilesPath.trim()); + + while (scan.hasNextLine()) { + copier.addURIString2basket(scan.nextLine()); + } + + dtde.dropComplete(true); + basketSizeSignal(); + return; + + } + + } + + } catch (Exception e) { + e.printStackTrace(); + dtde.rejectDrop(); + } + + dtde.rejectDrop(); + + } + + public void dragExit(DropTargetEvent e){} + public void dropActionChanged(DropTargetDragEvent e){} + public void dragOver(DropTargetDragEvent e){} + public void dragEnter(DropTargetDragEvent e){} + + + } + + private class Drop2Folder implements DropTargetListener { + public void drop(DropTargetDropEvent dtde) { + + dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); + + try { + + Transferable tr = dtde.getTransferable(); + DataFlavor[] flavors = tr.getTransferDataFlavors(); + + for (int i = 0; i < flavors.length; i++) { + + DataFlavor fl = flavors[i]; + + Object obj = tr.getTransferData(flavors[i]); + + //For Windows and Mac + if (DataFlavor.javaFileListFlavor.equals(fl)) { + List files = (List)tr.getTransferData(fl); + + Iterator ite = files.iterator(); + + if (files.size() == 1) { + copier.paste2URIString(ite.next().toURI().toString()); + } + + dtde.dropComplete(true); + basketSizeSignal(); + + return; + + } + + //For Linux and Mac + else if (obj instanceof String && "uri-list".equals(flavors[i].getSubType()) ){ + + String allFilesPath; + allFilesPath = (String)obj; + + Scanner scan = new Scanner(allFilesPath.trim()); + + String destinationFolderURIString = scan.nextLine(); + + if (!scan.hasNextLine()) { + copier.paste2URIString(destinationFolderURIString); + } + + dtde.dropComplete(true); + basketSizeSignal(); + + return; + + } + + } + + } catch (Exception e) { + e.printStackTrace(); + dtde.rejectDrop(); + } + + dtde.rejectDrop(); + + } + + public void dragExit(DropTargetEvent e){} + public void dropActionChanged(DropTargetDragEvent e){} + public void dragOver(DropTargetDragEvent e){} + public void dragEnter(DropTargetDragEvent e){} + + } + + public void changeCurrentFileSignal() { + supPanel.currentFileSource.setText(this.copier.getCurrentFileSource()); + supPanel.currentFileDestination.setText(this.copier.getCurrentFileDestination()); + } + + public void changePauseStateSignal(boolean b) { + if (b) { //Copier has been paused + setTitle(Language.get("MainFrame.paused")+" " + getTitle()); + supPanel.pause.setText(Language.get("MainFrame.SupPanel.ResumeButton")); + } else { //Copier has been resumed + //setTitle(Copier.name); + supPanel.pause.setText(Language.get("MainFrame.SupPanel.PauseButton")); + } + } + + public void totalSizeSignal() { + String totalSize = DialogMsg.prettySize(copier.getQueueTotalSize()); + supPanel.barTotalSize.setText(totalSize); + } + + public void currentSizeSignal() { + String currentSize = DialogMsg.prettySize(copier.getFileTotalSize()); + supPanel.barCurrentSize.setText(currentSize); + } + + public void basketSizeSignal() { + supPanel.basketContent.setText(Language.get("MainFrame.SupPanel.BasketContent")+" "+copier.basketLength()); + } + + + + + private class RefreshStats extends Thread { + + //Delay between two displays (ms) + private int refreshRate; + + public RefreshStats(int l){ + this.refreshRate = l; + } + + public void setRefreshRate(int l){ + this.refreshRate = l; + } + + public void run() { + Vector historyBytes = new Vector(); + int maxSize = 20; + + while (true) { + long timeInit = System.currentTimeMillis(); + if (!copier.getPause()){ + historyBytes.add(0,copier.readAndInitBytesCounter()); + if (historyBytes.size() > maxSize) historyBytes.removeElementAt(historyBytes.size()-1); + double average = 0; + for (long l : historyBytes){ + average += l; + } + try{ + average /= (historyBytes.size()*refreshRate/1000f); + //System.out.println(DialogMsg.prettySize((long)average)); + + String totalETA = "%"; + String currentETA = "%"; + String currentSpeed = ""; + if (average > 0){ + if (copier.getTotalBytesRemaining()>0) { + totalETA += " - "+DialogMsg.prettyTime((long)(copier.getTotalBytesRemaining()/average)); + currentSpeed = " ("+DialogMsg.prettySpeed((long)average)+")"; + } + if (copier.getCurrentBytesRemaining()>0) + currentETA += " - "+DialogMsg.prettyTime((long)(copier.getCurrentBytesRemaining()/average)); + } + supPanel.mainBar.setValue(copier.getTotalPercent()); + supPanel.fileBar.setValue(copier.getCurrentPercent()); + supPanel.mainBar.setString(copier.getTotalPercent()+totalETA); + supPanel.fileBar.setString(copier.getCurrentPercent()+currentETA); + setTitle(copier.getTotalPercent()+"%"+currentSpeed+" MiniCopier"); + } catch (Exception e){e.printStackTrace();} + } + try {Thread.sleep(refreshRate-(int)(System.currentTimeMillis()-timeInit));} + catch (Exception e){} + + } + } + } + + private class ActionQueueButtons implements ActionListener { + public void actionPerformed(ActionEvent e) + { + synchronized (Copier.mainQueue) { + int[] i = transfersPanel.transferList.getSelectedRows(); + int[] newSelectedIndexes = {-1,-1}; + + + if (i.length == 0) { return; } //something must be selected + + else if (e.getSource() == transfersPanel.putFirst){ + newSelectedIndexes = Copier.mainQueue.putFirst(i); + + } + else if (e.getSource() == transfersPanel.putUp){ + newSelectedIndexes = Copier.mainQueue.putUp(i); + } + else if (e.getSource() == transfersPanel.putDown){ + newSelectedIndexes = Copier.mainQueue.putDown(i); + } + else if (e.getSource() == transfersPanel.putLast){ + newSelectedIndexes = Copier.mainQueue.putLast(i); + } + else if (e.getSource() == transfersPanel.delete){ + newSelectedIndexes = Copier.mainQueue.remove(i); + } + else {} + + transfersPanel.transferList.clearSelection(); + + if (newSelectedIndexes[1] != -1) { + transfersPanel.transferList.getSelectionModel(). + addSelectionInterval(newSelectedIndexes[0], + newSelectedIndexes[1]); + } + } + } + } + + private class ActionFailedButtons implements ActionListener { + public void actionPerformed(ActionEvent e) + { + synchronized (Copier.failedItems) { + int[] i = failedPanel.failedList.getSelectedRows(); + + if (i.length == 0) { return; } //something must be selected + + else if (e.getSource() == failedPanel.retry){ + Copier.failedItems.retry(i); + } + else if (e.getSource() == failedPanel.removeFailed){ + Copier.failedItems.remove(i); + } + + transfersPanel.transferList.clearSelection(); + } + } + } + + + private class ActionSkip implements ActionListener { + public void actionPerformed(ActionEvent e) + { + if (copier.getBusy()) { + copier.pause(); + int choice = DialogMsg.confirmSkip(); + + switch (choice) { + //Skip with put in queue + case 0: copier.skip(true); + break; + //Skip without put in queue + case 1: copier.skip(false); + break; + //Cancel skip + case 2 : break; + } + + copier.unpause(); + + } + + } + + } + + private class ActionCancel implements ActionListener { + public void actionPerformed(ActionEvent e){ + //violent termination of the application + // TODO : remove the part already copied of the + // transfer (?) + System.exit(0); + } + } + + private class ActionCredits implements ActionListener { + public void actionPerformed(ActionEvent e){ + //DialogMsg.displayCredits(); + new Credits(MainFrame.frame); + } + } + + private class ActionBasket implements MouseListener { + public void mousePressed(MouseEvent e){ + //User clicked on the basket icon + boolean want2Clear = DialogMsg.displayBasketInstructions(copier.basketLength()==0); + if (want2Clear) { + copier.clearBasket(); + basketSizeSignal(); + } + + } + public void mouseReleased(MouseEvent e){} + public void mouseExited(MouseEvent e){} + public void mouseEntered(MouseEvent e){} + public void mouseClicked(MouseEvent e){} + } + + private class ActionCollision implements ActionListener { + public void actionPerformed(ActionEvent e) { + int i = optionsPanel.collisionBox.getSelectedIndex(); + Configuration.collisionAction = i; + } + } + + private class ActionLimitSpeed implements ActionListener { + public void actionPerformed(ActionEvent e) { + boolean limitEnabled = optionsPanel.speedLimitBox.isSelected(); + optionsPanel.speedValue.setEnabled(limitEnabled); + optionsPanel.speedUnitBox.setEnabled(limitEnabled); + Configuration.limitSpeed = limitEnabled; + updateSpeed(); + } + } + + private class ActionSpeedChange implements ChangeListener, ActionListener { + public void stateChanged(ChangeEvent e) { + updateSpeed(); + } + + public void actionPerformed(ActionEvent e) { + updateSpeed(); + } + } + + private void updateSpeed(){ + Configuration.setSpeedValue((int)Math.pow(1024, optionsPanel.speedUnitBox.getSelectedIndex())*(Integer)optionsPanel.speedValue.getValue()); + //System.out.println(Configuration.nbBytesPerS); + } + + private class ActionSymbolic implements ActionListener { + public void actionPerformed(ActionEvent e) { + int i = optionsPanel.symbolicBox.getSelectedIndex(); + Configuration.symbolicLinkAction = i; + } + } + + + +} diff --git a/src/minicopier/gui/OptionsPanel.java b/src/minicopier/gui/OptionsPanel.java new file mode 100644 index 0000000..1abc14d --- /dev/null +++ b/src/minicopier/gui/OptionsPanel.java @@ -0,0 +1,146 @@ +/* + OptionsPanel.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +package minicopier.gui; + +import java.awt.Dimension; + +import javax.swing.*; + +import minicopier.Configuration; +import minicopier.i18n.Language; + +public class OptionsPanel extends JPanel { + + private MainFrame mainFrame; + + protected JComboBox collisionBox; + + protected JComboBox symbolicBox; + + protected JCheckBox speedLimitBox; + + protected JSpinner speedValue; + + protected JComboBox speedUnitBox; + + protected JButton credits; + + public OptionsPanel(MainFrame f) { + super(); + + this.mainFrame = f; + + this.credits = new JButton(Language + .get("MainFrame.OptionsPane.creditsButton")); + + String[] collisionStrings = { + Language.get("MainFrame.OptionsPane.collision.Ask"), + Language.get("MainFrame.OptionsPane.collision.Cancel"), + Language.get("MainFrame.OptionsPane.collision.Overwrite"), + Language.get("MainFrame.OptionsPane.collision.OverwriteOld"), + Language.get("MainFrame.OptionsPane.collision.Resume") }; + collisionBox = new JComboBox(collisionStrings); + // DefaultListCellRenderer rightRenderer = new + // DefaultListCellRenderer(); + // rightRenderer.setHorizontalAlignment(DefaultListCellRenderer.RIGHT); + // collisionBox.setRenderer(rightRenderer); + collisionBox.setSelectedIndex(Configuration.collisionAction); + + this.speedLimitBox = new JCheckBox(Language.get("MainFrame.OptionsPane.throughput.Sentence")); + this.speedLimitBox.setSelected(false); + SpinnerModel speedModel = new SpinnerNumberModel(1,1,9999,1); + this.speedValue = new JSpinner(speedModel); + this.speedValue.setEnabled(false); + String[] unitValues = { + Language.get("DialogMsg.byteps"), + Language.get("DialogMsg.kilobyteps"), + Language.get("DialogMsg.megabyteps"), + Language.get("DialogMsg.gigabyteps")}; + speedUnitBox = new JComboBox(unitValues); + speedUnitBox.setSelectedIndex(2); + speedUnitBox.setEnabled(false); + + String[] symbolicStrings = { + Language.get("MainFrame.OptionsPane.symLinks.Follow"), + Language.get("MainFrame.OptionsPane.symLinks.Ignore") }; + symbolicBox = new JComboBox(symbolicStrings); + + symbolicBox.setSelectedIndex(Configuration.symbolicLinkAction); + + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + + // Collision option + JPanel collisionPanel = new JPanel(); + collisionPanel.setLayout(new BoxLayout(collisionPanel, BoxLayout.X_AXIS)); + collisionPanel.add(new JLabel(Language.get("MainFrame.OptionsPane.collision.Sentence"))); + + collisionPanel.add(Box.createHorizontalGlue()); + collisionBox.setMaximumSize(collisionBox.getPreferredSize()); + collisionPanel.add(collisionBox); + collisionPanel.setBorder(BorderFactory.createTitledBorder(Language.get("MainFrame.OptionsPane.collision.Title"))); + + // Speed option + JPanel speedPanel = new JPanel(); + speedPanel.setLayout(new BoxLayout(speedPanel, BoxLayout.X_AXIS)); + + + speedPanel.add(this.speedLimitBox); + speedLimitBox.setMaximumSize(speedLimitBox.getPreferredSize()); + speedPanel.add(Box.createHorizontalGlue()); + speedPanel.add(this.speedValue); + speedValue.setMaximumSize(new Dimension( + speedValue.getMinimumSize().width, + speedUnitBox.getMinimumSize().height) + ); + speedPanel.add(Box.createRigidArea(new Dimension(5,5))); + speedPanel.add(speedUnitBox); + speedUnitBox.setMaximumSize(speedUnitBox.getMinimumSize()); + speedPanel.setBorder(BorderFactory.createTitledBorder(Language.get("MainFrame.OptionsPane.throughput.Title"))); + + + + + // Symbolic link option + JPanel symbolicPanel = new JPanel(); + symbolicPanel.setLayout(new BoxLayout(symbolicPanel, BoxLayout.X_AXIS)); + symbolicPanel.add(new JLabel(Language + .get("MainFrame.OptionsPane.symLinks.Sentence"))); + symbolicPanel.add(Box.createHorizontalGlue()); + + symbolicBox.setMaximumSize(symbolicBox.getPreferredSize()); + // symbolicBox.setMinimumSize(collisionBox.getPreferredSize()); + + symbolicPanel.add(symbolicBox); + symbolicPanel.setBorder(BorderFactory.createTitledBorder(Language + .get("MainFrame.OptionsPane.symLinks.Title"))); + + JPanel optionsBottom = new JPanel(); + optionsBottom.setLayout(new BoxLayout(optionsBottom, BoxLayout.X_AXIS)); + optionsBottom.add(this.credits); + optionsBottom.add(Box.createHorizontalGlue()); + + this.add(collisionPanel); + this.add(speedPanel); + this.add(symbolicPanel); + this.add(Box.createVerticalGlue()); + this.add(optionsBottom); + } + +} diff --git a/src/minicopier/gui/QueueJButton.java b/src/minicopier/gui/QueueJButton.java new file mode 100644 index 0000000..cf81983 --- /dev/null +++ b/src/minicopier/gui/QueueJButton.java @@ -0,0 +1,36 @@ +/* + QueueJButton.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier.gui; + +import java.awt.Dimension; + +import javax.swing.ImageIcon; +import javax.swing.JButton; + +public class QueueJButton extends JButton{ + + public QueueJButton(String imagePath) { + super(); + this.setIcon(new ImageIcon(MainFrame.frame.imageFromJar(imagePath))); + this.setMaximumSize(new Dimension(28,28)); + this.setPreferredSize(new Dimension(28,28)); + } + +} diff --git a/src/minicopier/gui/SupPanel.java b/src/minicopier/gui/SupPanel.java new file mode 100644 index 0000000..8e32f10 --- /dev/null +++ b/src/minicopier/gui/SupPanel.java @@ -0,0 +1,173 @@ +/* + SupPanel.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier.gui; + +import java.awt.Component; + +import javax.swing.*; + +import minicopier.i18n.Language; + + +//Panel with DnD pictures + transfer stats +public class SupPanel extends JPanel { + + private MainFrame mainFrame; + + protected JButton pause; + + protected JButton skip; + + protected JButton cancel; + + protected JLabel basket; + + protected JLabel basketContent; + + protected JLabel paste; + + protected JLabel barTotalSize; + + protected JLabel barCurrentSize; + + protected JLabel currentFileSource; + + protected JLabel currentFileDestination; + + protected JProgressBar mainBar; + + protected JProgressBar fileBar; + + public SupPanel(MainFrame f) { + + super(); + + this.mainFrame = f; + + this.pause = new JButton(Language.get("MainFrame.SupPanel.PauseButton")); + this.skip = new JButton(Language.get("MainFrame.SupPanel.SkipButton")); + this.cancel = new JButton(Language.get("MainFrame.SupPanel.CancelButton")); + this.basket = new JLabel(""); + this.basket.setIcon(new ImageIcon(mainFrame.imageFromJar("img/basket.png"))); + this.basketContent = new JLabel(Language.get("MainFrame.SupPanel.BasketContent")+" 0"); + this.paste = new JLabel(""); + this.paste.setIcon(new ImageIcon(mainFrame.imageFromJar("img/paste.png"))); + this.barTotalSize = new JLabel(""); + this.barCurrentSize = new JLabel(""); + this.currentFileSource = new JLabel(mainFrame.copier.getCurrentFileSource()); + this.currentFileDestination = new JLabel(mainFrame.copier.getCurrentFileDestination()); + + //Tooltips + this.basket.setToolTipText(Language.get("Tooltip.Basket")); + this.paste.setToolTipText(Language.get("Tooltip.Paste")); + + this.mainBar = new JProgressBar(0, 100); + mainBar.setValue(0); + mainBar.setStringPainted(true); + + this.fileBar = new JProgressBar(0, 100); + fileBar.setValue(0); + fileBar.setStringPainted(true); + + + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + this.setAlignmentX(Component.CENTER_ALIGNMENT); + + JPanel firstPanel = new JPanel(); + + firstPanel.setLayout(new BoxLayout(firstPanel, BoxLayout.X_AXIS)); + firstPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + + Box basketPanel = Box.createVerticalBox(); + + //basketPanel.setLayout(new BoxLayout(basketPanel,BoxLayout.Y_AXIS)); + basketPanel.add(basket); + basketPanel.add(basketContent,Component.RIGHT_ALIGNMENT); + + basketPanel.setAlignmentX(Component.RIGHT_ALIGNMENT); + + firstPanel.add(basketPanel,Component.RIGHT_ALIGNMENT); + + //Sub JPanel with stats + JPanel subJPanel1 = new JPanel(); + subJPanel1.setLayout(new BoxLayout(subJPanel1, BoxLayout.Y_AXIS)); + + //Above Total progress bar + JPanel aboveTotalBar = new JPanel(); + aboveTotalBar.setLayout(new BoxLayout(aboveTotalBar,BoxLayout.X_AXIS)); + aboveTotalBar.add(new JLabel(Language.get("MainFrame.SupPanel.TotalBarTitle"))); + aboveTotalBar.add(Box.createHorizontalGlue()); + aboveTotalBar.add(this.barTotalSize); + + subJPanel1.add(aboveTotalBar); + subJPanel1.add(mainBar); + + //Above Current progress bar + JPanel aboveCurrentBar = new JPanel(); + aboveCurrentBar.setLayout(new BoxLayout(aboveCurrentBar,BoxLayout.X_AXIS)); + aboveCurrentBar.add(new JLabel(Language.get("MainFrame.SupPanel.CurrentFileBarTitle"))); + aboveCurrentBar.add(Box.createHorizontalGlue()); + aboveCurrentBar.add(this.barCurrentSize); + + + subJPanel1.add(aboveCurrentBar); + subJPanel1.add(fileBar); + + firstPanel.add(subJPanel1); + firstPanel.add(paste); + this.add(firstPanel); + + //Panel with control buttons + JPanel secondPanel = new JPanel(); + secondPanel.setLayout(new BoxLayout(secondPanel, BoxLayout.X_AXIS)); + //secondPanel.setAlignmentX(Component.RIGHT_ALIGNMENT); + + secondPanel.add(pause); + secondPanel.add(skip); + secondPanel.add(cancel); + + this.add(secondPanel); + + //Panel of files strings + JPanel stringPanel = new JPanel(); + stringPanel.setLayout(new BoxLayout(stringPanel, BoxLayout.X_AXIS)); + + + //Subpanel "From:" and "To:" + JPanel ftPanel = new JPanel(); + ftPanel.setLayout(new BoxLayout(ftPanel, BoxLayout.Y_AXIS)); + ftPanel.add(new JLabel(Language.get("MainFrame.SupPanel.SourcePath")+" ")); + ftPanel.add(new JLabel(Language.get("MainFrame.SupPanel.DestinationPath")+" ")); + + + //Subpanel with "source path" and "destination file" + JPanel sourceDestPanel = new JPanel(); + sourceDestPanel.setLayout(new BoxLayout(sourceDestPanel, BoxLayout.Y_AXIS)); + sourceDestPanel.add(this.currentFileSource); + sourceDestPanel.add(this.currentFileDestination); + + stringPanel.add(ftPanel); + stringPanel.add(sourceDestPanel); + stringPanel.add(Box.createVerticalStrut(15)); + this.add(stringPanel); + } + + +} diff --git a/src/minicopier/gui/TransfersPanel.java b/src/minicopier/gui/TransfersPanel.java new file mode 100644 index 0000000..01beff5 --- /dev/null +++ b/src/minicopier/gui/TransfersPanel.java @@ -0,0 +1,112 @@ +/* + TransfersPanel.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier.gui; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; + +import javax.swing.*; +import javax.swing.table.*; + +import minicopier.i18n.Language; + +//Panel with transfer list +public class TransfersPanel extends JPanel { + + private MainFrame mainFrame; + + protected QueueJButton putFirst; + + protected QueueJButton putUp; + + protected QueueJButton putDown; + + protected QueueJButton putLast; + + protected QueueJButton delete; + + protected DefaultTableModel transferModel; + + protected JTable transferList; + + public TransfersPanel(MainFrame f){ + super(); + + this.mainFrame = f; + + this.putFirst = new QueueJButton("img/first.gif"); + this.putFirst.setToolTipText(Language.get("Tooltip.Transfers.First")); + + this.putUp = new QueueJButton("img/up.gif"); + this.putUp.setToolTipText(Language.get("Tooltip.Transfers.Up")); + this.putDown = new QueueJButton("img/down.gif"); + this.putDown.setToolTipText(Language.get("Tooltip.Transfers.Down")); + this.putLast = new QueueJButton("img/last.gif"); + this.putLast.setToolTipText(Language.get("Tooltip.Transfers.Last")); + this.delete = new QueueJButton("img/delete.gif"); + this.delete.setToolTipText(Language.get("Tooltip.Transfers.Cancel")); + + transferModel = mainFrame.copier.mainQueue.getTableModel(); + transferList = new JTable(transferModel); + //transferList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); + transferList.getColumnModel().getColumn(1).setCellRenderer(new RightTableCellRenderer()); + transferList.getColumnModel().getColumn(1).setMaxWidth(80); + transferList.getColumnModel().getColumn(1).setMinWidth(80); + + + + this.setLayout(new BorderLayout()); + + + JScrollPane jspTransfer = new JScrollPane(transferList); + jspTransfer.setViewportView(transferList); + jspTransfer.setPreferredSize(new Dimension(40,40)); + //transferList.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); + //jspTransfer.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS); + + + //Subpanel (west) with queue management buttons + JPanel queueButtonsPanel = new JPanel(); + queueButtonsPanel.setLayout(new BoxLayout(queueButtonsPanel, BoxLayout.Y_AXIS)); + queueButtonsPanel.setAlignmentX(Component.CENTER_ALIGNMENT); + queueButtonsPanel.add(this.putFirst,Component.CENTER_ALIGNMENT); + queueButtonsPanel.add(this.putUp); + queueButtonsPanel.add(this.putDown); + queueButtonsPanel.add(this.putLast); + queueButtonsPanel.add(this.delete); + + + this.add(queueButtonsPanel,BorderLayout.WEST); + this.add(jspTransfer,BorderLayout.CENTER); + + + } + + public class RightTableCellRenderer extends DefaultTableCellRenderer { + public RightTableCellRenderer() { + setHorizontalAlignment(RIGHT); + setVerticalAlignment(CENTER); + } + } + + + +} diff --git a/src/minicopier/i18n/Language.java b/src/minicopier/i18n/Language.java new file mode 100644 index 0000000..f2c8310 --- /dev/null +++ b/src/minicopier/i18n/Language.java @@ -0,0 +1,51 @@ +/* + Language.java / MiniCopier + Copyright (C) 2007-2009 Adrian Courrèges + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +package minicopier.i18n; + +import java.util.*; + +public class Language { + + static ResourceBundle messages; + + /** If argument is "SystemLocale", we will use the system default locale. + * Otherwise, we can force a language with : "fr", "en" ... + * @param languageLocale "SystemLocale", "fr", "en" + * */ + public static void init(String languageLocale) { + + Locale locale; + + if (languageLocale.equals("SystemLocale")) { + locale = Locale.getDefault(); + } else { + locale = new Locale(languageLocale); + } + + Language.messages = ResourceBundle.getBundle("minicopier.i18n.minicopier",locale); + + + } + + public static String get(String m) { + return messages.getString(m); + } + +} diff --git a/src/minicopier/i18n/minicopier.properties b/src/minicopier/i18n/minicopier.properties new file mode 100644 index 0000000..5657e66 --- /dev/null +++ b/src/minicopier/i18n/minicopier.properties @@ -0,0 +1,109 @@ +MainFrame.title=MiniCopier +MainFrame.paused=[Paused] +MainFrame.SupPanel.BasketContent=contains: +MainFrame.SupPanel.TotalBarTitle=Total +MainFrame.SupPanel.CurrentFileBarTitle=Current File +MainFrame.SupPanel.PauseButton=Pause +MainFrame.SupPanel.ResumeButton=Resume +MainFrame.SupPanel.SkipButton=Skip +MainFrame.SupPanel.CancelButton=Cancel +MainFrame.SupPanel.SourcePath=From: +MainFrame.SupPanel.DestinationPath=To: + +MainFrame.TransfersPane.title=Transfers List +MainFrame.TransfersPane.sourceRow=Source +MainFrame.TransfersPane.sizeRow=Size +MainFrame.TransfersPane.destinationRow=Destination + +MainFrame.FailedPane.title=Failed Items + +MainFrame.OptionsPane.title=Options +MainFrame.OptionsPane.collision.Title=Collision action +MainFrame.OptionsPane.collision.Sentence=If target file already exists, always: +MainFrame.OptionsPane.collision.Ask=ask +MainFrame.OptionsPane.collision.Cancel=cancel copy +MainFrame.OptionsPane.collision.Overwrite=overwrite +MainFrame.OptionsPane.collision.OverwriteOld=overwrite if older +MainFrame.OptionsPane.collision.Resume=resume + +MainFrame.OptionsPane.throughput.Title=Transfer Speed +MainFrame.OptionsPane.throughput.Sentence=Limit transfer speed to: + +MainFrame.OptionsPane.symLinks.Title=Symbolic links (Unix systems) +MainFrame.OptionsPane.symLinks.Sentence=If source path contains a link, always: +MainFrame.OptionsPane.symLinks.Follow=follow it +MainFrame.OptionsPane.symLinks.Ignore=ignore it +MainFrame.OptionsPane.creditsButton=About + +DialogMsg.byte=B +DialogMsg.kilobyte=kB +DialogMsg.megabyte=MB +DialogMsg.gigabyte=GB +DialogMsg.terabyte=TB +DialogMsg.byteps=B/s +DialogMsg.kilobyteps=kB/s +DialogMsg.megabyteps=MB/s +DialogMsg.gigabyteps=GB/s +DialogMsg.terabyteps=TB/s + +DialogMsg.displayBasketInstructions.Title=Basket Management +DialogMsg.displayBasketInstructions.EmptyText=Your basket is empty!\n\nPlease drag and drop some files or folders here to fill your basket.\nWhen you want to paste, drag and drop a folder on the other icon. +DialogMsg.displayBasketInstructions.FullText=Your basket is not empty.\nDo you wish to clear it? +DialogMsg.displayBasketInstructions.ClearButton=Clear basket +DialogMsg.displayBasketInstructions.CancelButton=Cancel + +DialogMsg.confirmSkip.Title=Skip current file +DialogMsg.confirmSkip.Text=Do you want to put current file transfer in the\nend of queue, so MiniCopier can resume it later?\n\nSelecting 'No' will cancel this transfer, but not\nerase the already copied part. +DialogMsg.confirmSkip.QueueButton=Yes, put in queue +DialogMsg.confirmSkip.NoButton=No, give up +DialogMsg.confirmSkip.CancelButton=Cancel + +DialogMsg.askNewName.Text=Please, enter a new name for the target file:\n + +DialogMsg.renameError.Title=Incorrect name +DialogMsg.renameError.Text=This name cannot be used!\nPlease, enter a different one. + +DialogMsg.errorOverwrite.Title=Overwrite error +DialogMsg.errorOverwrite.Text=Cannot overwrite!\nSource and target files are the same. + +DialogMsg.alreadyExistsChoice.Title=File already exists +DialogMsg.alreadyExistsChoice.Text1=The target file already exists.\n\n +DialogMsg.alreadyExistsChoice.Source=Source +DialogMsg.alreadyExistsChoice.Target=\nTarget +DialogMsg.alreadyExistsChoice.Text2=What do you want to do? +DialogMsg.alreadyExistsChoice.OverwriteButton=Overwrite +DialogMsg.alreadyExistsChoice.ResumeButton=Resume +DialogMsg.alreadyExistsChoice.RenameButton=Rename +DialogMsg.alreadyExistsChoice.CancelButton=Cancel + +DialogMsg.transferErrorChoice.Title=Transfer error +DialogMsg.transferErrorChoice.Text=An error occurred during transfer!\n\nPossible causes:\n- disk is full\n- you don't have read/write permission\n\nYou can put this copy in queue, so MiniCopier\nwill try to resume later. +DialogMsg.transferErrorChoice.QueueButton=Put in queue +DialogMsg.transferErrorChoice.AbortButton=Give up file + +DialogMsg.welcome.Title=Welcome to MiniCopier! +DialogMsg.welcome.Text=It seems you are using MiniCopier for the first time.\n\nDrag and drop some files you want to copy into the basket and \ndrop a destination folder on the right to launch the process. + +Credits.Title=Credits +Credits.Version=Version +Credits.Credits.Homepage=MiniCopier Homepage: +Credits.Credits.Development=Development: +Credits.Credits.Developers=\tAdrian Courrèges +Credits.Credits.LanguageTranslation=English Translation: +Credits.Credits.LanguageContributors=\tAdrian Courrèges +Credits.Credits.SendMail=For bug reports or suggestions, please send an email to: +Credits.License.Title=License +Credits.License.Intro=MiniCopier is free software released under the GNU General Public License (GPL). + +Tooltip.Basket=Drag and drop files you wish to copy here. +Tooltip.Paste=Drag and drop a folder here to paste the basket content. +Tooltip.Transfers.First=First +Tooltip.Transfers.Up=Up +Tooltip.Transfers.Down=Down +Tooltip.Transfers.Last=Last +Tooltip.Transfers.Cancel=Cancel +Tooltip.Failed.Retry=Retry transfer +Tooltip.Failed.Clear=Clear + + + diff --git a/src/minicopier/i18n/minicopier_en.properties b/src/minicopier/i18n/minicopier_en.properties new file mode 100644 index 0000000..7f1cdfb --- /dev/null +++ b/src/minicopier/i18n/minicopier_en.properties @@ -0,0 +1 @@ +## See minicopier.properties diff --git a/src/minicopier/i18n/minicopier_fr.properties b/src/minicopier/i18n/minicopier_fr.properties new file mode 100644 index 0000000..6d59f94 --- /dev/null +++ b/src/minicopier/i18n/minicopier_fr.properties @@ -0,0 +1,106 @@ +MainFrame.title=MiniCopier +MainFrame.paused=[En pause] +MainFrame.SupPanel.BasketContent=contient: +MainFrame.SupPanel.TotalBarTitle=Total +MainFrame.SupPanel.CurrentFileBarTitle=Fichier en cours +MainFrame.SupPanel.PauseButton=Pause +MainFrame.SupPanel.ResumeButton=Reprendre +MainFrame.SupPanel.SkipButton=Passer +MainFrame.SupPanel.CancelButton=Annuler +MainFrame.SupPanel.SourcePath=De: +MainFrame.SupPanel.DestinationPath=Vers: + +MainFrame.TransfersPane.title=Liste des transferts +MainFrame.TransfersPane.sourceRow=Source +MainFrame.TransfersPane.sizeRow=Taille +MainFrame.TransfersPane.destinationRow=Destination + +MainFrame.FailedPane.title=Echecs + +MainFrame.OptionsPane.title=Options +MainFrame.OptionsPane.collision.Title=Collision de fichiers +MainFrame.OptionsPane.collision.Sentence=Si le fichier cible existe déjà, toujours : +MainFrame.OptionsPane.collision.Ask=demander +MainFrame.OptionsPane.collision.Cancel=annuler +MainFrame.OptionsPane.collision.Overwrite=écraser +MainFrame.OptionsPane.collision.OverwriteOld=écraser si plus ancien +MainFrame.OptionsPane.collision.Resume=reprendre + +MainFrame.OptionsPane.throughput.Title=Vitesse de transfert +MainFrame.OptionsPane.throughput.Sentence=Limiter la vitesse de transfert à: + +MainFrame.OptionsPane.symLinks.Title=Liens symboliques (Systèmes Unix) +MainFrame.OptionsPane.symLinks.Sentence=Si le chemin source contient un lien, toujours : +MainFrame.OptionsPane.symLinks.Follow=le suivre +MainFrame.OptionsPane.symLinks.Ignore=l'ignorer +MainFrame.OptionsPane.creditsButton=A propos + +DialogMsg.byte=o +DialogMsg.kilobyte=kio +DialogMsg.megabyte=Mio +DialogMsg.gigabyte=Gio +DialogMsg.terabyte=Tio +DialogMsg.byteps=o/s +DialogMsg.kilobyteps=kio/s +DialogMsg.megabyteps=Mio/s +DialogMsg.gigabyteps=Gio/s +DialogMsg.terabyteps=Tio/s + +DialogMsg.displayBasketInstructions.Title=Gestion du panier +DialogMsg.displayBasketInstructions.EmptyText=Votre panier est vide !\n\nGlissez-déposez des fichiers ou des répertoires ici pour remplir le panier.\nLorsque vous désirez coller, glissez-déposez un répertoire sur l'autre icône. +DialogMsg.displayBasketInstructions.FullText=Votre panier n'est pas vide.\nSouhaitez-vous le vider ? +DialogMsg.displayBasketInstructions.ClearButton=Vider +DialogMsg.displayBasketInstructions.CancelButton=Annuler + +DialogMsg.confirmSkip.Title=Passer le fichier en cours +DialogMsg.confirmSkip.Text=Souhaitez-vous placer le transfert en cours à la fin\nde la queue pour que MiniCopier le reprenne plus tard ?\n\nChoisir 'Non' annulera le transfert, mais la partie du\nfichier déjà recopiée ne sera pas supprimée. +DialogMsg.confirmSkip.QueueButton=Oui, mettre en queue +DialogMsg.confirmSkip.NoButton=Non, abandonner +DialogMsg.confirmSkip.CancelButton=Annuler + +DialogMsg.askNewName.Text=Veuillez entrer un nouveau nom pour le fichier cible :\n + +DialogMsg.renameError.Title=Nom incorrect +DialogMsg.renameError.Text=Ce nom ne peut pas être utilisé !\nVeuillez en entrer un autre. + +DialogMsg.errorOverwrite.Title=Erreur lors de l'écrasement +DialogMsg.errorOverwrite.Text=Impossible d'écraser !\nLes fichiers source et cible sont identiques. + +DialogMsg.alreadyExistsChoice.Title=Le fichier existe déjà +DialogMsg.alreadyExistsChoice.Text1=Le fichier de destination existe déjà.\n\n +DialogMsg.alreadyExistsChoice.Source=Source +DialogMsg.alreadyExistsChoice.Target=\nCible +DialogMsg.alreadyExistsChoice.Text2=Que souhaitez-vous faire ? +DialogMsg.alreadyExistsChoice.OverwriteButton=Ecraser +DialogMsg.alreadyExistsChoice.ResumeButton=Reprendre +DialogMsg.alreadyExistsChoice.RenameButton=Renommer +DialogMsg.alreadyExistsChoice.CancelButton=Annuler + +DialogMsg.transferErrorChoice.Title=Erreur de copie +DialogMsg.transferErrorChoice.Text=Une erreur est survenue durant le transfert !\n\nCauses possibles:\n- le disque est plein\n- vous n'avez pas de droits de lecture/écriture\n\nVous pouvez remettre cette copie en queue, pour que \nMiniCopier essaie de la reprendre plus tard. +DialogMsg.transferErrorChoice.QueueButton=Mettre en queue +DialogMsg.transferErrorChoice.AbortButton=Abandonner la copie + +DialogMsg.welcome.Title=Bienvenue dans MiniCopier ! +DialogMsg.welcome.Text=Cela semble être votre première utilisation.\n\nGlissez-déposez des fichiers à copier dans le panier et déposez un \ndossier de destination à droite pour lancer le processus de copie. + +Credits.Title=Crédits +Credits.Version=Version +Credits.Credits.Homepage=Page du projet MiniCopier : +Credits.Credits.Development=Développement : +Credits.Credits.Developers=\tAdrian Courrèges +Credits.Credits.LanguageTranslation=Traduction française : +Credits.Credits.LanguageContributors=\tAdrian Courrèges +Credits.Credits.SendMail=Pour toute suggestion ou rapport de bug, vous pouvez envoyer un email à : +Credits.License.Title=Licence +Credits.License.Intro=MiniCopier est un logiciel libre publié sous licence GPL (GNU General Public License). + +Tooltip.Basket=Glissez-déposez des fichiers à copier ici. +Tooltip.Paste=Glissez-déposez un répertoire ici pour y coller le contenu du panier. +Tooltip.Transfers.First=Mettre en premier +Tooltip.Transfers.Up=Monter +Tooltip.Transfers.Down=Descendre +Tooltip.Transfers.Last=Mettre en dernier +Tooltip.Transfers.Cancel=Annuler +Tooltip.Failed.Retry=Retenter le transfert +Tooltip.Failed.Clear=Retirer