From a65a522ef5030ebb944e5e16a7a9cb2d1ba331bc Mon Sep 17 00:00:00 2001 From: Ivailo Monev Date: Sun, 30 Nov 2014 18:15:20 +0000 Subject: [PATCH] added filelight --- CMakeLists.txt | 1 + filelight/AUTHORS | 2 + filelight/CMakeLists.txt | 38 ++ filelight/COPYING | 340 ++++++++++++++ filelight/COPYING-DOCS | 397 ++++++++++++++++ filelight/CTestConfig.cmake | 13 + filelight/ChangeLog | 243 ++++++++++ filelight/INSTALL | 32 ++ filelight/Mainpage.dox | 8 + filelight/Messages.sh | 4 + filelight/NEWS | 1 + filelight/README | 50 ++ filelight/TODO | 28 ++ filelight/filelight.lsm | 13 + filelight/misc/CMakeLists.txt | 27 ++ filelight/misc/filelight.desktop | 160 +++++++ filelight/misc/filelightpart.desktop | 55 +++ filelight/misc/filelightpartui.rc | 44 ++ filelight/misc/filelightrc | 6 + filelight/misc/filelightui.rc | 96 ++++ filelight/misc/hi16-app-filelight.png | Bin 0 -> 862 bytes filelight/misc/hi22-app-filelight.png | Bin 0 -> 1346 bytes filelight/misc/hi32-action-view_filelight.png | Bin 0 -> 1500 bytes filelight/misc/hi32-app-filelight.png | Bin 0 -> 2086 bytes filelight/misc/hi48-app-filelight.png | Bin 0 -> 3428 bytes filelight/misc/hi64-app-filelight.png | Bin 0 -> 4879 bytes filelight/misc/hisc-apps-filelight.svgz | Bin 0 -> 13926 bytes filelight/src/CMakeLists.txt | 22 + filelight/src/app/CMakeLists.txt | 29 ++ filelight/src/app/historyAction.cpp | 113 +++++ filelight/src/app/historyAction.h | 86 ++++ filelight/src/app/main.cpp | 78 ++++ filelight/src/app/mainWindow.cpp | 295 ++++++++++++ filelight/src/app/mainWindow.h | 87 ++++ filelight/src/define.h | 36 ++ filelight/src/part/CMakeLists.txt | 47 ++ filelight/src/part/Config.cpp | 77 ++++ filelight/src/part/Config.h | 60 +++ filelight/src/part/dialog.ui | 426 ++++++++++++++++++ filelight/src/part/fileTree.cpp | 39 ++ filelight/src/part/fileTree.h | 327 ++++++++++++++ filelight/src/part/localLister.cpp | 260 +++++++++++ filelight/src/part/localLister.h | 61 +++ filelight/src/part/part.cpp | 341 ++++++++++++++ filelight/src/part/part.h | 101 +++++ filelight/src/part/progressBox.cpp | 132 ++++++ filelight/src/part/progressBox.h | 58 +++ filelight/src/part/radialMap/CMakeLists.txt | 31 ++ filelight/src/part/radialMap/builder.cpp | 161 +++++++ filelight/src/part/radialMap/builder.h | 56 +++ filelight/src/part/radialMap/labels.cpp | 340 ++++++++++++++ filelight/src/part/radialMap/map.cpp | 381 ++++++++++++++++ filelight/src/part/radialMap/map.h | 82 ++++ filelight/src/part/radialMap/radialMap.h | 110 +++++ filelight/src/part/radialMap/sincos.h | 45 ++ filelight/src/part/radialMap/widget.cpp | 216 +++++++++ filelight/src/part/radialMap/widget.h | 123 +++++ filelight/src/part/radialMap/widgetEvents.cpp | 329 ++++++++++++++ filelight/src/part/remoteLister.cpp | 171 +++++++ filelight/src/part/remoteLister.h | 54 +++ filelight/src/part/scan.cpp | 238 ++++++++++ filelight/src/part/scan.h | 77 ++++ filelight/src/part/settingsDialog.cpp | 242 ++++++++++ filelight/src/part/settingsDialog.h | 68 +++ filelight/src/part/summaryWidget.cpp | 189 ++++++++ filelight/src/part/summaryWidget.h | 43 ++ 66 files changed, 7189 insertions(+) create mode 100644 filelight/AUTHORS create mode 100644 filelight/CMakeLists.txt create mode 100644 filelight/COPYING create mode 100644 filelight/COPYING-DOCS create mode 100644 filelight/CTestConfig.cmake create mode 100644 filelight/ChangeLog create mode 100644 filelight/INSTALL create mode 100644 filelight/Mainpage.dox create mode 100644 filelight/Messages.sh create mode 100644 filelight/NEWS create mode 100644 filelight/README create mode 100644 filelight/TODO create mode 100644 filelight/filelight.lsm create mode 100644 filelight/misc/CMakeLists.txt create mode 100755 filelight/misc/filelight.desktop create mode 100644 filelight/misc/filelightpart.desktop create mode 100644 filelight/misc/filelightpartui.rc create mode 100644 filelight/misc/filelightrc create mode 100644 filelight/misc/filelightui.rc create mode 100644 filelight/misc/hi16-app-filelight.png create mode 100644 filelight/misc/hi22-app-filelight.png create mode 100644 filelight/misc/hi32-action-view_filelight.png create mode 100644 filelight/misc/hi32-app-filelight.png create mode 100644 filelight/misc/hi48-app-filelight.png create mode 100644 filelight/misc/hi64-app-filelight.png create mode 100644 filelight/misc/hisc-apps-filelight.svgz create mode 100644 filelight/src/CMakeLists.txt create mode 100644 filelight/src/app/CMakeLists.txt create mode 100644 filelight/src/app/historyAction.cpp create mode 100644 filelight/src/app/historyAction.h create mode 100644 filelight/src/app/main.cpp create mode 100644 filelight/src/app/mainWindow.cpp create mode 100644 filelight/src/app/mainWindow.h create mode 100644 filelight/src/define.h create mode 100644 filelight/src/part/CMakeLists.txt create mode 100644 filelight/src/part/Config.cpp create mode 100644 filelight/src/part/Config.h create mode 100644 filelight/src/part/dialog.ui create mode 100644 filelight/src/part/fileTree.cpp create mode 100644 filelight/src/part/fileTree.h create mode 100644 filelight/src/part/localLister.cpp create mode 100644 filelight/src/part/localLister.h create mode 100644 filelight/src/part/part.cpp create mode 100644 filelight/src/part/part.h create mode 100644 filelight/src/part/progressBox.cpp create mode 100644 filelight/src/part/progressBox.h create mode 100644 filelight/src/part/radialMap/CMakeLists.txt create mode 100644 filelight/src/part/radialMap/builder.cpp create mode 100644 filelight/src/part/radialMap/builder.h create mode 100644 filelight/src/part/radialMap/labels.cpp create mode 100644 filelight/src/part/radialMap/map.cpp create mode 100644 filelight/src/part/radialMap/map.h create mode 100644 filelight/src/part/radialMap/radialMap.h create mode 100644 filelight/src/part/radialMap/sincos.h create mode 100644 filelight/src/part/radialMap/widget.cpp create mode 100644 filelight/src/part/radialMap/widget.h create mode 100644 filelight/src/part/radialMap/widgetEvents.cpp create mode 100644 filelight/src/part/remoteLister.cpp create mode 100644 filelight/src/part/remoteLister.h create mode 100644 filelight/src/part/scan.cpp create mode 100644 filelight/src/part/scan.h create mode 100644 filelight/src/part/settingsDialog.cpp create mode 100644 filelight/src/part/settingsDialog.h create mode 100644 filelight/src/part/summaryWidget.cpp create mode 100644 filelight/src/part/summaryWidget.h diff --git a/CMakeLists.txt b/CMakeLists.txt index b45a710b..7e09371e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ macro_optional_add_subdirectory (ark) macro_optional_add_subdirectory (bluedevil) macro_optional_add_subdirectory (dragon) macro_optional_add_subdirectory (ffmpegthumbs) +macro_optional_add_subdirectory (filelight) macro_optional_add_subdirectory (gwenview) macro_optional_add_subdirectory (kcalc) macro_optional_add_subdirectory (kcron) diff --git a/filelight/AUTHORS b/filelight/AUTHORS new file mode 100644 index 00000000..76e7b708 --- /dev/null +++ b/filelight/AUTHORS @@ -0,0 +1,2 @@ +Martin Sandsmark +Max Howell diff --git a/filelight/CMakeLists.txt b/filelight/CMakeLists.txt new file mode 100644 index 00000000..5e8f5886 --- /dev/null +++ b/filelight/CMakeLists.txt @@ -0,0 +1,38 @@ +####################################################################### +# Copyright 2008-2009 Martin Sandsmark +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License or (at your option) version 3 or any later version +# accepted by the membership of KDE e.V. (or its successor approved +# by the membership of KDE e.V.), which shall act as a proxy +# defined in Section 14 of version 3 of the license. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +####################################################################### + +project(Filelight) + +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR}) + find_package(KDE4) + include( KDE4Defaults ) + include_directories(${KDE4_INCLUDES}) + + add_definitions(${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) + add_definitions(-DQT_USE_FAST_CONCATENATION -DQT_USE_FAST_OPERATOR_PLUS) +endif() + +add_definitions( -DQT_NO_CAST_FROM_ASCII ) +add_definitions( -DQT_NO_CAST_TO_ASCII ) + +include_directories(src/) + +add_subdirectory(src) +add_subdirectory(misc) diff --git a/filelight/COPYING b/filelight/COPYING new file mode 100644 index 00000000..70190fbd --- /dev/null +++ b/filelight/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Steet, Fifth Floor, Boston, MA 02111-1307 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 Library 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 Steet, Fifth Floor, Boston, MA 02111-1307 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 Library General +Public License instead of this License. diff --git a/filelight/COPYING-DOCS b/filelight/COPYING-DOCS new file mode 100644 index 00000000..4a0fe1c8 --- /dev/null +++ b/filelight/COPYING-DOCS @@ -0,0 +1,397 @@ + GNU Free Documentation License + Version 1.2, November 2002 + + + Copyright (C) 2000,2001,2002 Free Software Foundation, Inc. + 51 Franklin St, 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. + + +0. PREAMBLE + +The purpose of this License is to make a manual, textbook, or other +functional and useful document "free" in the sense of freedom: to +assure everyone the effective freedom to copy and redistribute it, +with or without modifying it, either commercially or noncommercially. +Secondarily, this License preserves for the author and publisher a way +to get credit for their work, while not being considered responsible +for modifications made by others. + +This License is a kind of "copyleft", which means that derivative +works of the document must themselves be free in the same sense. It +complements the GNU General Public License, which is a copyleft +license designed for free software. + +We have designed this License in order to use it for manuals for free +software, because free software needs free documentation: a free +program should come with manuals providing the same freedoms that the +software does. But this License is not limited to software manuals; +it can be used for any textual work, regardless of subject matter or +whether it is published as a printed book. We recommend this License +principally for works whose purpose is instruction or reference. + + +1. APPLICABILITY AND DEFINITIONS + +This License applies to any manual or other work, in any medium, that +contains a notice placed by the copyright holder saying it can be +distributed under the terms of this License. Such a notice grants a +world-wide, royalty-free license, unlimited in duration, to use that +work under the conditions stated herein. The "Document", below, +refers to any such manual or work. Any member of the public is a +licensee, and is addressed as "you". You accept the license if you +copy, modify or distribute the work in a way requiring permission +under copyright law. + +A "Modified Version" of the Document means any work containing the +Document or a portion of it, either copied verbatim, or with +modifications and/or translated into another language. + +A "Secondary Section" is a named appendix or a front-matter section of +the Document that deals exclusively with the relationship of the +publishers or authors of the Document to the Document's overall subject +(or to related matters) and contains nothing that could fall directly +within that overall subject. (Thus, if the Document is in part a +textbook of mathematics, a Secondary Section may not explain any +mathematics.) The relationship could be a matter of historical +connection with the subject or with related matters, or of legal, +commercial, philosophical, ethical or political position regarding +them. + +The "Invariant Sections" are certain Secondary Sections whose titles +are designated, as being those of Invariant Sections, in the notice +that says that the Document is released under this License. If a +section does not fit the above definition of Secondary then it is not +allowed to be designated as Invariant. The Document may contain zero +Invariant Sections. If the Document does not identify any Invariant +Sections then there are none. + +The "Cover Texts" are certain short passages of text that are listed, +as Front-Cover Texts or Back-Cover Texts, in the notice that says that +the Document is released under this License. A Front-Cover Text may +be at most 5 words, and a Back-Cover Text may be at most 25 words. + +A "Transparent" copy of the Document means a machine-readable copy, +represented in a format whose specification is available to the +general public, that is suitable for revising the document +straightforwardly with generic text editors or (for images composed of +pixels) generic paint programs or (for drawings) some widely available +drawing editor, and that is suitable for input to text formatters or +for automatic translation to a variety of formats suitable for input +to text formatters. A copy made in an otherwise Transparent file +format whose markup, or absence of markup, has been arranged to thwart +or discourage subsequent modification by readers is not Transparent. +An image format is not Transparent if used for any substantial amount +of text. A copy that is not "Transparent" is called "Opaque". + +Examples of suitable formats for Transparent copies include plain +ASCII without markup, Texinfo input format, LaTeX input format, SGML +or XML using a publicly available DTD, and standard-conforming simple +HTML, PostScript or PDF designed for human modification. Examples of +transparent image formats include PNG, XCF and JPG. Opaque formats +include proprietary formats that can be read and edited only by +proprietary word processors, SGML or XML for which the DTD and/or +processing tools are not generally available, and the +machine-generated HTML, PostScript or PDF produced by some word +processors for output purposes only. + +The "Title Page" means, for a printed book, the title page itself, +plus such following pages as are needed to hold, legibly, the material +this License requires to appear in the title page. For works in +formats which do not have any title page as such, "Title Page" means +the text near the most prominent appearance of the work's title, +preceding the beginning of the body of the text. + +A section "Entitled XYZ" means a named subunit of the Document whose +title either is precisely XYZ or contains XYZ in parentheses following +text that translates XYZ in another language. (Here XYZ stands for a +specific section name mentioned below, such as "Acknowledgements", +"Dedications", "Endorsements", or "History".) To "Preserve the Title" +of such a section when you modify the Document means that it remains a +section "Entitled XYZ" according to this definition. + +The Document may include Warranty Disclaimers next to the notice which +states that this License applies to the Document. These Warranty +Disclaimers are considered to be included by reference in this +License, but only as regards disclaiming warranties: any other +implication that these Warranty Disclaimers may have is void and has +no effect on the meaning of this License. + + +2. VERBATIM COPYING + +You may copy and distribute the Document in any medium, either +commercially or noncommercially, provided that this License, the +copyright notices, and the license notice saying this License applies +to the Document are reproduced in all copies, and that you add no other +conditions whatsoever to those of this License. You may not use +technical measures to obstruct or control the reading or further +copying of the copies you make or distribute. However, you may accept +compensation in exchange for copies. If you distribute a large enough +number of copies you must also follow the conditions in section 3. + +You may also lend copies, under the same conditions stated above, and +you may publicly display copies. + + +3. COPYING IN QUANTITY + +If you publish printed copies (or copies in media that commonly have +printed covers) of the Document, numbering more than 100, and the +Document's license notice requires Cover Texts, you must enclose the +copies in covers that carry, clearly and legibly, all these Cover +Texts: Front-Cover Texts on the front cover, and Back-Cover Texts on +the back cover. Both covers must also clearly and legibly identify +you as the publisher of these copies. The front cover must present +the full title with all words of the title equally prominent and +visible. You may add other material on the covers in addition. +Copying with changes limited to the covers, as long as they preserve +the title of the Document and satisfy these conditions, can be treated +as verbatim copying in other respects. + +If the required texts for either cover are too voluminous to fit +legibly, you should put the first ones listed (as many as fit +reasonably) on the actual cover, and continue the rest onto adjacent +pages. + +If you publish or distribute Opaque copies of the Document numbering +more than 100, you must either include a machine-readable Transparent +copy along with each Opaque copy, or state in or with each Opaque copy +a computer-network location from which the general network-using +public has access to download using public-standard network protocols +a complete Transparent copy of the Document, free of added material. +If you use the latter option, you must take reasonably prudent steps, +when you begin distribution of Opaque copies in quantity, to ensure +that this Transparent copy will remain thus accessible at the stated +location until at least one year after the last time you distribute an +Opaque copy (directly or through your agents or retailers) of that +edition to the public. + +It is requested, but not required, that you contact the authors of the +Document well before redistributing any large number of copies, to give +them a chance to provide you with an updated version of the Document. + + +4. MODIFICATIONS + +You may copy and distribute a Modified Version of the Document under +the conditions of sections 2 and 3 above, provided that you release +the Modified Version under precisely this License, with the Modified +Version filling the role of the Document, thus licensing distribution +and modification of the Modified Version to whoever possesses a copy +of it. In addition, you must do these things in the Modified Version: + +A. Use in the Title Page (and on the covers, if any) a title distinct + from that of the Document, and from those of previous versions + (which should, if there were any, be listed in the History section + of the Document). You may use the same title as a previous version + if the original publisher of that version gives permission. +B. List on the Title Page, as authors, one or more persons or entities + responsible for authorship of the modifications in the Modified + Version, together with at least five of the principal authors of the + Document (all of its principal authors, if it has fewer than five), + unless they release you from this requirement. +C. State on the Title page the name of the publisher of the + Modified Version, as the publisher. +D. Preserve all the copyright notices of the Document. +E. Add an appropriate copyright notice for your modifications + adjacent to the other copyright notices. +F. Include, immediately after the copyright notices, a license notice + giving the public permission to use the Modified Version under the + terms of this License, in the form shown in the Addendum below. +G. Preserve in that license notice the full lists of Invariant Sections + and required Cover Texts given in the Document's license notice. +H. Include an unaltered copy of this License. +I. Preserve the section Entitled "History", Preserve its Title, and add + to it an item stating at least the title, year, new authors, and + publisher of the Modified Version as given on the Title Page. If + there is no section Entitled "History" in the Document, create one + stating the title, year, authors, and publisher of the Document as + given on its Title Page, then add an item describing the Modified + Version as stated in the previous sentence. +J. Preserve the network location, if any, given in the Document for + public access to a Transparent copy of the Document, and likewise + the network locations given in the Document for previous versions + it was based on. These may be placed in the "History" section. + You may omit a network location for a work that was published at + least four years before the Document itself, or if the original + publisher of the version it refers to gives permission. +K. For any section Entitled "Acknowledgements" or "Dedications", + Preserve the Title of the section, and preserve in the section all + the substance and tone of each of the contributor acknowledgements + and/or dedications given therein. +L. Preserve all the Invariant Sections of the Document, + unaltered in their text and in their titles. Section numbers + or the equivalent are not considered part of the section titles. +M. Delete any section Entitled "Endorsements". Such a section + may not be included in the Modified Version. +N. Do not retitle any existing section to be Entitled "Endorsements" + or to conflict in title with any Invariant Section. +O. Preserve any Warranty Disclaimers. + +If the Modified Version includes new front-matter sections or +appendices that qualify as Secondary Sections and contain no material +copied from the Document, you may at your option designate some or all +of these sections as invariant. To do this, add their titles to the +list of Invariant Sections in the Modified Version's license notice. +These titles must be distinct from any other section titles. + +You may add a section Entitled "Endorsements", provided it contains +nothing but endorsements of your Modified Version by various +parties--for example, statements of peer review or that the text has +been approved by an organization as the authoritative definition of a +standard. + +You may add a passage of up to five words as a Front-Cover Text, and a +passage of up to 25 words as a Back-Cover Text, to the end of the list +of Cover Texts in the Modified Version. Only one passage of +Front-Cover Text and one of Back-Cover Text may be added by (or +through arrangements made by) any one entity. If the Document already +includes a cover text for the same cover, previously added by you or +by arrangement made by the same entity you are acting on behalf of, +you may not add another; but you may replace the old one, on explicit +permission from the previous publisher that added the old one. + +The author(s) and publisher(s) of the Document do not by this License +give permission to use their names for publicity for or to assert or +imply endorsement of any Modified Version. + + +5. COMBINING DOCUMENTS + +You may combine the Document with other documents released under this +License, under the terms defined in section 4 above for modified +versions, provided that you include in the combination all of the +Invariant Sections of all of the original documents, unmodified, and +list them all as Invariant Sections of your combined work in its +license notice, and that you preserve all their Warranty Disclaimers. + +The combined work need only contain one copy of this License, and +multiple identical Invariant Sections may be replaced with a single +copy. If there are multiple Invariant Sections with the same name but +different contents, make the title of each such section unique by +adding at the end of it, in parentheses, the name of the original +author or publisher of that section if known, or else a unique number. +Make the same adjustment to the section titles in the list of +Invariant Sections in the license notice of the combined work. + +In the combination, you must combine any sections Entitled "History" +in the various original documents, forming one section Entitled +"History"; likewise combine any sections Entitled "Acknowledgements", +and any sections Entitled "Dedications". You must delete all sections +Entitled "Endorsements". + + +6. COLLECTIONS OF DOCUMENTS + +You may make a collection consisting of the Document and other documents +released under this License, and replace the individual copies of this +License in the various documents with a single copy that is included in +the collection, provided that you follow the rules of this License for +verbatim copying of each of the documents in all other respects. + +You may extract a single document from such a collection, and distribute +it individually under this License, provided you insert a copy of this +License into the extracted document, and follow this License in all +other respects regarding verbatim copying of that document. + + +7. AGGREGATION WITH INDEPENDENT WORKS + +A compilation of the Document or its derivatives with other separate +and independent documents or works, in or on a volume of a storage or +distribution medium, is called an "aggregate" if the copyright +resulting from the compilation is not used to limit the legal rights +of the compilation's users beyond what the individual works permit. +When the Document is included in an aggregate, this License does not +apply to the other works in the aggregate which are not themselves +derivative works of the Document. + +If the Cover Text requirement of section 3 is applicable to these +copies of the Document, then if the Document is less than one half of +the entire aggregate, the Document's Cover Texts may be placed on +covers that bracket the Document within the aggregate, or the +electronic equivalent of covers if the Document is in electronic form. +Otherwise they must appear on printed covers that bracket the whole +aggregate. + + +8. TRANSLATION + +Translation is considered a kind of modification, so you may +distribute translations of the Document under the terms of section 4. +Replacing Invariant Sections with translations requires special +permission from their copyright holders, but you may include +translations of some or all Invariant Sections in addition to the +original versions of these Invariant Sections. You may include a +translation of this License, and all the license notices in the +Document, and any Warranty Disclaimers, provided that you also include +the original English version of this License and the original versions +of those notices and disclaimers. In case of a disagreement between +the translation and the original version of this License or a notice +or disclaimer, the original version will prevail. + +If a section in the Document is Entitled "Acknowledgements", +"Dedications", or "History", the requirement (section 4) to Preserve +its Title (section 1) will typically require changing the actual +title. + + +9. TERMINATION + +You may not copy, modify, sublicense, or distribute the Document except +as expressly provided for under this License. Any other attempt to +copy, modify, sublicense or distribute the Document 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. + + +10. FUTURE REVISIONS OF THIS LICENSE + +The Free Software Foundation may publish new, revised versions +of the GNU Free Documentation 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. See +http://www.gnu.org/copyleft/. + +Each version of the License is given a distinguishing version number. +If the Document specifies that a particular numbered version of this +License "or any later version" applies to it, you have the option of +following the terms and conditions either of that specified version or +of any later version that has been published (not as a draft) by the +Free Software Foundation. If the Document does not specify a version +number of this License, you may choose any version ever published (not +as a draft) by the Free Software Foundation. + + +ADDENDUM: How to use this License for your documents + +To use this License in a document you have written, include a copy of +the License in the document and put the following copyright and +license notices just after the title page: + + Copyright (c) YEAR YOUR NAME. + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.2 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +If you have Invariant Sections, Front-Cover Texts and Back-Cover Texts, +replace the "with...Texts." line with this: + + with the Invariant Sections being LIST THEIR TITLES, with the + Front-Cover Texts being LIST, and with the Back-Cover Texts being LIST. + +If you have Invariant Sections without Cover Texts, or some other +combination of the three, merge those two alternatives to suit the +situation. + +If your document contains nontrivial examples of program code, we +recommend releasing these examples in parallel under your choice of +free software license, such as the GNU General Public License, +to permit their use in free software. diff --git a/filelight/CTestConfig.cmake b/filelight/CTestConfig.cmake new file mode 100644 index 00000000..da0fad1a --- /dev/null +++ b/filelight/CTestConfig.cmake @@ -0,0 +1,13 @@ +## This file should be placed in the root directory of your project. +## Then modify the CMakeLists.txt file in the root directory of your +## project to incorporate the testing dashboard. +## # The following are required to uses Dart and the Cdash dashboard +## ENABLE_TESTING() +## INCLUDE(CTest) +set(CTEST_PROJECT_NAME "filelight") +set(CTEST_NIGHTLY_START_TIME "20:00:00 CET") + +set(CTEST_DROP_METHOD "http") +set(CTEST_DROP_SITE "my.cdash.org") +set(CTEST_DROP_LOCATION "/submit.php?project=filelight") +set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/filelight/ChangeLog b/filelight/ChangeLog new file mode 100644 index 00000000..079abc47 --- /dev/null +++ b/filelight/ChangeLog @@ -0,0 +1,243 @@ +1.9 + Ported to Qt 4 / KDE 4 + Paint directly on the widget + Remove ugly hack for checking for backingstore + +1.0 + Added copy to clipboard function to segment context menus + Accepts URLs from drag and drop + Click central circle to go "up" + +0.6.3 + Made "Scan Directory..." a KStdAction::open with setText( "Scan Directory..." ); + Swaped map rendering from bitBlt to paintPixmap (again) as some users get problems with the labels being obliterated + LocationBar history is saved inbetween sessions for KDE >= 3.1.3 + Rescans are forced if locationBar Go is pushed for location to current map + Fixed several bugs related to resizing the filemap + Initial map display had a long running issue which meant you had to perform a resize to ensure all detail was shown. I finally put in the hours and fixed it. + Cancelling scans clears the location bar + Option to show files not represented as segments (because they are too small) combined in large segments with relevant summaries + Directories that would normally have no child segments shown now have hidden Files segment shown + Fixed faulty show/hide of progress box when many scans done in quick succession + Session management + Saving of Window/*bar settings inbetween sessions + Fixed popup related non-updates and false action calls + Canvas code cleanup + Stopped combo activated scans being called twice + Improved map generation speed (scan speeds are still slow however) + Appropriate wait cursors are set during long operations (scans, map generation) + Fixed misrendered transparency when tooltips exceed Filelight boundries + Optimised and de-flickered the tooltips + Segments that contain unrepresented files (due to 5 level limit on maps) now show a thick border at the edge to indicate that there are unrepresented files + Commandline now accepts ../ and ./ type arguments + Add "Open Konsole here" to context-menu, also tidied it up and made it properly context relevant + Popup title is now relative to map path to reduce menu width somewhat without resorting to csqueeze() + Fixed bug with not showing unrepresented files for dirs that contained dirs + Improvement selection of units for tooltips (always prefer megabytes unless < 0.01 MB) + Improved selection of decimal places for size display (2sig.fig. please) + Label font sizes now default to the system standard + Labels have indentation relative to their level + Label selection improved (not enough yet though!) + Better looking label line diagonals + When smaller fonts are requested, Filelight maximises the font size differences + Attempted to fix crash at exit bug + Made files shades of gray, made indicator for directories that have unshown files + Removed presence of multi-file segments except in root level circumstances (unless Show Small Files is selected in Settings) + Contrast system revamped, you can now have more contrast + Segment colouring system rewritten, better distinction between different segment types + Used more KStdActions (less work for translation teams at very least) + Fixed bug where segmenttip wasn't shown for 1st pixel but labels were + Directories with unshown files have better arrow heads now thanks to some clever trig + Checked for possible cases where division by zero might occur and corrected code + Huge code rewrite in preparation for transition to KPart (must release soon, this is getting silly) + Allow clicks on the central circle to represent the map's root (eg. middle-click to open in Konqi) +0.6.2 + Attempted a fix for the absurdly large file bug (eg > 1400GB!) + Linking bug fixed for Gentoo systems + Compile bug fixed for GCC < 3 + Fixed crash bug when scan had been stopped and then back then forward pressed + Fixed another compile bug for systems that don't support large files (Debian apparently) + Internationalisation support thanks to patch from André Somers + Made the tooltip translucent (NOT a gimmick, this feature is essential IMO) + Started making debug messages use kdDebug so users can enable/disable the messages + Implemented XML GUI + Vast slot/signal cleanup, fixed many bugs + Fixed a few bugs with the progress box + Cancelled scans now remain stored in the back/forward histories + Fixed incorrect reporting of directory file counts + Increased minimum segment size to 3px outer diameter (any depth) + Added location_bar UI element to toolbar (much work) + The segment tip now appears below the cursor when hovering over the upper part of the map + Increased the recent scans list to 8 entries and made the combohistory work better +0.6.1 + Filelight again compiles on KDE <= 3.1.3 (but with some reduced functionality) + Rewrote HistoryAction code + Improved default choice of smallest font + Lowest level segment labels show whenever mouse is not over anything + Middle click opens segment according to mime type (eg. jpgs open in kuickshow) + Implemented realtime contrast updates + Added antialiasing, font size and contrast settings to appearance tab + Renamed "colour scheme" tab "appearance" + Extruded label fontsizes reflect their segment's depth + Rescans ignore tree cache + Changing scan options clears the tree cache + Directory select dialog shows current path as selected when opened + Scans no longer have to be repeated + Implemented antialiasing for the map, very slow so only happens after resizes + Gracefully handles failures to read file attributes + Rewrote backend with templating, should have done that in first place! + Improved changelog listing methods! + Included support for GNU systems that don't support large files (>2GB) +0.6.0 + Added expanded labels to give more information on segments in focus + Added settings dialogs + Added new colour schemes + Allowed scans to not cross at mount points + Added #Files info for directory tooltips + filemap is now displayed centrally + Shows percentage of files in directories +0.5.1 + Added Filelight to the Utilities submenu in the K-Menu + Tooltips are now presented for file info instead of using the statusbar + Made resizing of the filemap smoother + Improved the icon and made a 48x48 and 22x22 version + Reworked the code somewhat + Scans can now be cancelled + The go history actions are more intelligent + Fixed all the bugs I could find + Attempted to improve filemap contrast some more (still not perfect, but getting there) + Added visual activation cues for various clicks +0.50 + Few bug fixes + Improved performance for rendering and scanning + Improved contrast of the filemap + Added icon + Added left and right click functionality + Improved error handling +0.45 + Added toolbar, implemented history functionality + Click to scan segments + Increased filesize limit to 4096 GB + Fixed required - make bug + Various optimisations +0.4 + Initial public release +1.0 + Added copy to clipboard function to segment context menus + Accepts URLs from drag and drop + Click central circle to go "up" + +0.6.3 + Made "Scan Directory..." a KStdAction::open with setText( "Scan Directory..." ); + Swaped map rendering from bitBlt to paintPixmap (again) as some users get problems with the labels being obliterated + LocationBar history is saved inbetween sessions for KDE >= 3.1.3 + Rescans are forced if locationBar Go is pushed for location to current map + Fixed several bugs related to resizing the filemap + Initial map display had a long running issue which meant you had to perform a resize to ensure all detail was shown. I finally put in the hours and fixed it. + Cancelling scans clears the location bar + Option to show files not represented as segments (because they are too small) combined in large segments with relevant summaries + Directories that would normally have no child segments shown now have hidden Files segment shown + Fixed faulty show/hide of progress box when many scans done in quick succession + Session management + Saving of Window/*bar settings inbetween sessions + Fixed popup related non-updates and false action calls + Canvas code cleanup + Stopped combo activated scans being called twice + Improved map generation speed (scan speeds are still slow however) + Appropriate wait cursors are set during long operations (scans, map generation) + Fixed misrendered transparency when tooltips exceed Filelight boundries + Optimised and de-flickered the tooltips + Segments that contain unrepresented files (due to 5 level limit on maps) now show a thick border at the edge to indicate that there are unrepresented files + Commandline now accepts ../ and ./ type arguments + Add "Open Konsole here" to context-menu, also tidied it up and made it properly context relevant + Popup title is now relative to map path to reduce menu width somewhat without resorting to csqueeze() + Fixed bug with not showing unrepresented files for dirs that contained dirs + Improvement selection of units for tooltips (always prefer megabytes unless < 0.01 MB) + Improved selection of decimal places for size display (2sig.fig. please) + Label font sizes now default to the system standard + Labels have indentation relative to their level + Label selection improved (not enough yet though!) + Better looking label line diagonals + When smaller fonts are requested, Filelight maximises the font size differences + Attempted to fix crash at exit bug + Made files shades of gray, made indicator for directories that have unshown files + Removed presence of multi-file segments except in root level circumstances (unless Show Small Files is selected in Settings) + Contrast system revamped, you can now have more contrast + Segment colouring system rewritten, better distinction between different segment types + Used more KStdActions (less work for translation teams at very least) + Fixed bug where segmenttip wasn't shown for 1st pixel but labels were + Directories with unshown files have better arrow heads now thanks to some clever trig + Checked for possible cases where division by zero might occur and corrected code + Huge code rewrite in preparation for transition to KPart (must release soon, this is getting silly) + Allow clicks on the central circle to represent the map's root (eg. middle-click to open in Konqi) +0.6.2 + Attempted a fix for the absurdly large file bug (eg > 1400GB!) + Linking bug fixed for Gentoo systems + Compile bug fixed for GCC < 3 + Fixed crash bug when scan had been stopped and then back then forward pressed + Fixed another compile bug for systems that don't support large files (Debian apparently) + Internationalisation support thanks to patch from André Somers + Made the tooltip translucent (NOT a gimmick, this feature is essential IMO) + Started making debug messages use kdDebug so users can enable/disable the messages + Implemented XML GUI + Vast slot/signal cleanup, fixed many bugs + Fixed a few bugs with the progress box + Cancelled scans now remain stored in the back/forward histories + Fixed incorrect reporting of directory file counts + Increased minimum segment size to 3px outer diameter (any depth) + Added location_bar UI element to toolbar (much work) + The segment tip now appears below the cursor when hovering over the upper part of the map + Increased the recent scans list to 8 entries and made the combohistory work better +0.6.1 + Filelight again compiles on KDE <= 3.1.3 (but with some reduced functionality) + Rewrote HistoryAction code + Improved default choice of smallest font + Lowest level segment labels show whenever mouse is not over anything + Middle click opens segment according to mime type (eg. jpgs open in kuickshow) + Implemented realtime contrast updates + Added antialiasing, font size and contrast settings to appearance tab + Renamed "colour scheme" tab "appearance" + Extruded label fontsizes reflect their segment's depth + Rescans ignore tree cache + Changing scan options clears the tree cache + Directory select dialog shows current path as selected when opened + Scans no longer have to be repeated + Implemented antialiasing for the map, very slow so only happens after resizes + Gracefully handles failures to read file attributes + Rewrote backend with templating, should have done that in first place! + Improved changelog listing methods! + Included support for GNU systems that don't support large files (>2GB) +0.6.0 + Added expanded labels to give more information on segments in focus + Added settings dialogs + Added new colour schemes + Allowed scans to not cross at mount points + Added #Files info for directory tooltips + filemap is now displayed centrally + Shows percentage of files in directories +0.5.1 + Added Filelight to the Utilities submenu in the K-Menu + Tooltips are now presented for file info instead of using the statusbar + Made resizing of the filemap smoother + Improved the icon and made a 48x48 and 22x22 version + Reworked the code somewhat + Scans can now be cancelled + The go history actions are more intelligent + Fixed all the bugs I could find + Attempted to improve filemap contrast some more (still not perfect, but getting there) + Added visual activation cues for various clicks +0.50 + Few bug fixes + Improved performance for rendering and scanning + Improved contrast of the filemap + Added icon + Added left and right click functionality + Improved error handling +0.45 + Added toolbar, implemented history functionality + Click to scan segments + Increased filesize limit to 4096 GB + Fixed required - make bug + Various optimisations +0.4 + Initial public release diff --git a/filelight/INSTALL b/filelight/INSTALL new file mode 100644 index 00000000..3910e43e --- /dev/null +++ b/filelight/INSTALL @@ -0,0 +1,32 @@ + + Filelight INSTALL +=================== + +Simply: + + $ mkdir build && cd build && cmake .. -DCMAKE_INSTALL_PREFIX=/usr \ + && make && sudo make install + + Troubleshooting +================= + +Check you have the kde development packages installed, they'll be named +something like: + + kde-devel + +This should pull in a lot of other development packages (unless they are already +installed) like: + + x11-devel + qt-devel + +If you get stuck, please email me with any relevant information you can +provide. I will get back to you! Compile issues suck, I know this full well. +-------------------------------------------------------------------------------- + + +We hope you enjoy using Filelight! + +Martin Sandsmark +Max Howell diff --git a/filelight/Mainpage.dox b/filelight/Mainpage.dox new file mode 100644 index 00000000..02a695db --- /dev/null +++ b/filelight/Mainpage.dox @@ -0,0 +1,8 @@ +/** @mainpage Filelight + +The Filelight application + +*/ + +// DOXYGEN_REFERENCES = kdecore +// DOXYGEN_SET_PROJECT_NAME = filelight diff --git a/filelight/Messages.sh b/filelight/Messages.sh new file mode 100644 index 00000000..83cca24d --- /dev/null +++ b/filelight/Messages.sh @@ -0,0 +1,4 @@ +#!bin/sh +$EXTRACTRC `find . -name \*.rc -o -name \*.ui` >> rc.cpp +$XGETTEXT `find . -name \*.cpp -o -name \*.h` -o $podir/filelight.pot +rm -f rc.cpp diff --git a/filelight/NEWS b/filelight/NEWS new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/filelight/NEWS @@ -0,0 +1 @@ + diff --git a/filelight/README b/filelight/README new file mode 100644 index 00000000..115a40e1 --- /dev/null +++ b/filelight/README @@ -0,0 +1,50 @@ + + Filelight README +================== + +Filelight allows you to understand exactly where your diskspace is being used by +graphically representating your filesystem as a set of concentric +segmented-rings. + +-------------------------------------------------------------------------------- + + + Dependencies +============== + +Filelight works on KDElibs >= 4.2 and therefore Qt >= 4.4 (lower versions may +work, but no guarantees). + + + Usage Tips +============ + +* Click on directories to center the view there +* Click on files to launch the application associated with them +* Middle click to open files/directories with Konqueror +* Right click on the central white circle to manipulate the base directory +* Increase the window size to see more detail +* Hover over segments to find out more information +* Available as a view-KPart in Konqueror +* You can specify a directory to scan on startup from the command line, eg: + + $ filelight /home/mxcl + + + Installation +============== + +See the accompanying file; INSTALL. + + + Bugs +====== + +There are less bugs everyday! Please report bugs at http://bugs.kde.org/ +-------------------------------------------------------------------------------- + + +We hope you enjoy using Filelight! + +Martin Sandsmark +Max Howell diff --git a/filelight/TODO b/filelight/TODO new file mode 100644 index 00000000..499a76a5 --- /dev/null +++ b/filelight/TODO @@ -0,0 +1,28 @@ +1.3 + features for sysadmins, split by users etc. + +1.2 + split rendering engine from data-gatherer and allow data to be fed through a pipe thus allowing different data sources to be profiled. + show preview icons next to the labels + last access time colour scheme + History actions need to have menu pulldown just like konqi + show statusbar signal to say that diretories weren't scanned due to lack of permissions (allow user to enter root password, for this scan only or to keep and then scan only bits that were missed) + determine if canvas is too square and shrink map accordingly + +1.1 + If scanning a partition show free space + if the view is square shrink the map slightly as a slightly rectangular view is preferable + use kdirwatcher, then move rescan to scan menu, rename force rescan + clicking center goes up directory + refactor disklister.cpp + implement vectors instead of double-linked lists for file data + Trace inodes in binarytree to avoid recursive hardlinks and counting same directory tree twice + admin mode + fix that Part settings file is not same as app settings file + support other mouse actions for central circle (ie middle click, right click) + write caches for all scans, so if crash or something that root scan that took 20 mins is not lost + +MAYBE + flicking mouse wheel shows different information in tooltip, and there's an off setting, show temp status message to reflect info box + ideally, eventually show which labels are children of which by grouping them with a coloured square or something + create html summary files (pics and text) \ No newline at end of file diff --git a/filelight/filelight.lsm b/filelight/filelight.lsm new file mode 100644 index 00000000..a2154ec1 --- /dev/null +++ b/filelight/filelight.lsm @@ -0,0 +1,13 @@ +Begin3 +title: Filelight +Version: 1.0 +Entered-date: 5/9/2003 +Description: Graphical disk usage information +Keywords: kde disk usage du filesystem diskspace +author: Max Howell +Maintained-by: Martin Sandsmark +Home-page: http://utils.kde.org/projects/filelight +Platform: unix +Copying-policy: GPL +End + diff --git a/filelight/misc/CMakeLists.txt b/filelight/misc/CMakeLists.txt new file mode 100644 index 00000000..0f4a49f5 --- /dev/null +++ b/filelight/misc/CMakeLists.txt @@ -0,0 +1,27 @@ +####################################################################### +# Copyright 2008-2009 Martin Sandsmark +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License or (at your option) version 3 or any later version +# accepted by the membership of KDE e.V. (or its successor approved +# by the membership of KDE e.V.), which shall act as a proxy +# defined in Section 14 of version 3 of the license. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +####################################################################### + +install(FILES filelightpart.desktop DESTINATION ${SERVICES_INSTALL_DIR}) +install(PROGRAMS filelight.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) +install(FILES filelightrc DESTINATION ${CONFIG_INSTALL_DIR}) +install(FILES filelightpartui.rc DESTINATION ${DATA_INSTALL_DIR}/filelightpart) +install(FILES filelightui.rc DESTINATION ${DATA_INSTALL_DIR}/filelight) + +kde4_install_icons(${ICON_INSTALL_DIR}) diff --git a/filelight/misc/filelight.desktop b/filelight/misc/filelight.desktop new file mode 100755 index 00000000..a2be9047 --- /dev/null +++ b/filelight/misc/filelight.desktop @@ -0,0 +1,160 @@ +# KDE Config File +[Desktop Entry] +Name=Filelight +Name[bs]=Filelight +Name[ca]=Filelight +Name[ca@valencia]=Filelight +Name[cs]=Filelight +Name[da]=Filelight +Name[de]=Filelight +Name[el]=Filelight +Name[en_GB]=Filelight +Name[es]=Filelight +Name[et]=Filelight +Name[eu]=Filelight +Name[fi]=Filelight +Name[fr]=Filelight +Name[ga]=Filelight +Name[gl]=Filelight +Name[hr]=Filelight +Name[hu]=Filelight +Name[ia]=Filelight +Name[it]=Filelight +Name[kk]=Filelight +Name[km]=Filelight +Name[ko]=Filelight +Name[lt]=Filelight +Name[lv]=Filelight +Name[mr]=फाईल-लाइट +Name[nb]=Filelight +Name[nds]=Filelight +Name[nl]=Filelight +Name[pa]=ਫਾਇਲਲਾਈਟ +Name[pl]=Filelight +Name[pt]=Filelight +Name[pt_BR]=Filelight +Name[ro]=Filelight +Name[ru]=Filelight +Name[sk]=Filelight +Name[sl]=Filelight +Name[sr]=Фајлајт +Name[sr@ijekavian]=Фајлајт +Name[sr@ijekavianlatin]=Filelight +Name[sr@latin]=Filelight +Name[sv]=Filelight +Name[th]=Filelight +Name[tr]=Filelight +Name[ug]=Filelight +Name[uk]=Filelight +Name[wa]=Filelight +Name[x-test]=xxFilelightxx +Name[zh_CN]=Filelight +Name[zh_TW]=Filelight +GenericName=Disk Usage Statistics +GenericName[ast]=Estadístiques d'usu de discu +GenericName[bs]=Statistika upotrebe diska +GenericName[ca]=Estadístiques d'ús de disc +GenericName[ca@valencia]=Estadístiques d'ús de disc +GenericName[cs]=Statistika použití disku +GenericName[da]=Statistik over diskforbrug +GenericName[de]=Statistik über Festplattenbelegung +GenericName[el]=Στατιστικά χρήσης δίσκου +GenericName[en_GB]=Disk Usage Statistics +GenericName[es]=Estadísticas de uso de discos +GenericName[et]=Kettakasutuse statistika +GenericName[eu]=Diskoen erabilerari buruzko estatistikak +GenericName[fi]=Levynkäyttötilastot +GenericName[fr]=Statistiques d'utilisation de disques +GenericName[ga]=Staitisticí maidir le húsáid diosca +GenericName[gl]=Estatísticas de uso do disco +GenericName[hr]=Statistika o upotrebi diska +GenericName[hu]=Lemezhasználati statisztika +GenericName[ia]=Statisticas de uso de disco +GenericName[it]=Statistiche uso del disco +GenericName[kk]=Дискінің статистикасы +GenericName[km]=ស្ថិតិ​ប្រើប្រាស់​ថាស +GenericName[ko]=디스크 사용 통계 +GenericName[lt]=Disko naudojimo statistika +GenericName[lv]=Diska izlietojuma statistika +GenericName[mr]=डिस्क वापर आकडेवारी +GenericName[nb]=Statistikk over diskbruk +GenericName[nds]=Fastplaatbruuk-Statistik +GenericName[nl]=Gebruiksstatistieken van schijven +GenericName[pa]=ਡਿਸਕ ਵਰਤੋਂ ਅੰਕੜੇ +GenericName[pl]=Statystyki wykorzystania dysku +GenericName[pt]=Estatísticas de Utilização do Disco +GenericName[pt_BR]=Estatísticas de uso do disco +GenericName[ro]=Statistici utilizare disc +GenericName[ru]=Использование дисков +GenericName[sk]=Štatistiky využitia disku +GenericName[sl]=Statistika uporabe diska +GenericName[sr]=Статистика искоришћености дискова +GenericName[sr@ijekavian]=Статистика искоришћености дискова +GenericName[sr@ijekavianlatin]=Statistika iskorišćenosti diskova +GenericName[sr@latin]=Statistika iskorišćenosti diskova +GenericName[sv]=Statistik över diskanvändning +GenericName[th]=สถิติการใช้งานพื้นที่ดิสก์ +GenericName[tr]=Disk Kullanımı İstatistikleri +GenericName[uk]=Статистика використання диска +GenericName[x-test]=xxDisk Usage Statisticsxx +GenericName[zh_CN]=磁盘使用统计 +GenericName[zh_TW]=磁碟用量統計 +Type=Application +Exec=filelight %f +Icon=filelight +Comment=View disk usage information +Comment[ast]=Ver infomación d'usu de Discu +Comment[bs]=Pogledajte podatke o upotrebi diska +Comment[ca]=Veure la informació d'ús de disc +Comment[ca@valencia]=Veure la informació d'ús de disc +Comment[cs]=Zobrazit využití disku +Comment[da]=Vis information om diskforbrug +Comment[de]=Nutzungsinformationen zu Festplatten anzeigen +Comment[el]=Εμφάνιση πληροφοριών χρήσης δίσκου +Comment[en_GB]=View disk usage information +Comment[es]=Muestra información sobre el uso de discos +Comment[et]=Kettakasutuse teabe kuvamine +Comment[eu]=Ikusi diskoen erabilerari buruzko informazioa +Comment[fi]=Näytä levynkäyttö +Comment[fr]=Afficher les informations d'utilisation de disques +Comment[ga]=Taispeáin úsáid diosca +Comment[gl]=Acceda a información de uso do disco. +Comment[hr]=Prikaži informacije o upotrebi diska +Comment[hu]=Lemezhasználati információk megjelenítése +Comment[ia]=Vide information de uso de disco +Comment[it]=Mostra l'uso del disco +Comment[ja]=ディスク使用状況を表示 +Comment[kk]=Дискідегі орынды көрсету +Comment[km]=មើល​ព័ត៌មាន​អំពី​ការ​ប្រើប្រាស់​ថាស +Comment[ko]=디스크 사용량 보기 +Comment[lt]=Žiūrėti disko naudojimo informaciją +Comment[lv]=Skata diska vietas izlietojumu +Comment[mr]=डिस्क वापर माहिती बघा +Comment[nb]=Vis informasjon om bruk av diskplass +Comment[nds]=Wiest Informatschonen över den Bruuk vun de Fastplaat +Comment[nl]=Informatie over schijfgebruik bekijken +Comment[pa]=ਡਿਸਕ ਵਰਤੋਂ ਬਾਰੇ ਜਾਣਕਾਰੀ ਵੇਖੋ +Comment[pl]=Zobacz informacje o wykorzystaniu dysku +Comment[pt]=Ver a informação de utilização do disco +Comment[pt_BR]=Exibe as informações de utilização do disco +Comment[ro]=Vezi informații despre utilizarea discului +Comment[ru]=Наглядное представление файловой системы +Comment[sk]=Zobrazenie informácií o využití disku +Comment[sl]=Oglejte si statistiko uporabe diska +Comment[sr]=Приказ података о искоришћености дискова +Comment[sr@ijekavian]=Приказ података о искоришћености дискова +Comment[sr@ijekavianlatin]=Prikaz podataka o iskorišćenosti diskova +Comment[sr@latin]=Prikaz podataka o iskorišćenosti diskova +Comment[sv]=Visa information om hårddiskanvändning +Comment[th]=แสดงข้อมูลการใช้งานพื้นที่ดิสก์ +Comment[tr]=Disk kullanımını görüntüle +Comment[ug]=دىسكىنىڭ ئىشلىتىلىش ئۇچۇرلىرىنى كۆرسىتىش پروگراممىسى +Comment[uk]=Перегляд інформації щодо використання дисків +Comment[wa]=Vey les informåcions so l' eployaedje del deure plake +Comment[x-test]=xxView disk usage informationxx +Comment[zh_CN]=查看磁盘使用信息 +Comment[zh_TW]=檢視磁碟用量資訊 +Terminal=false +MimeType=inode/directory; +X-DocPath=filelight/index.html +Categories=Qt;KDE;Utility; diff --git a/filelight/misc/filelightpart.desktop b/filelight/misc/filelightpart.desktop new file mode 100644 index 00000000..5ccc178a --- /dev/null +++ b/filelight/misc/filelightpart.desktop @@ -0,0 +1,55 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Radial Map +Name[bs]=Radial Map +Name[ca]=Mapa radial +Name[ca@valencia]=Mapa radial +Name[cs]=Kruhová mapa +Name[da]=Radialkort +Name[de]=Kreisförmige Darstellung +Name[el]=Ακτινωτός χάρτης +Name[en_GB]=Radial Map +Name[es]=Mapa radial +Name[et]=Radiaalkaart +Name[eu]=Mapa erradiala +Name[fi]=Säteittäinen kartta +Name[fr]=Carte radiale +Name[ga]=Mapa Gathach +Name[gl]=Mapa radial +Name[hr]=Zrakasta karta +Name[hu]=Kördiagram +Name[ia]=Mappa radial +Name[it]=Mappa radiale +Name[kk]=Дөңгелек картасы +Name[km]=ផែនទីនៃ​កាំ +Name[ko]=원형 지도 +Name[lt]=Radialinis žemėlapis +Name[lv]=Radial Map +Name[mr]=मध्यबिंदूतून जाणारा नकाशा +Name[nb]=Radielt kart +Name[nds]=Rund Koort +Name[nl]=Radiale grafiek +Name[pa]=ਰੇਡੀਅਨ ਮੈਪ +Name[pl]=Mapa kołowa +Name[pt]=Mapa Radial +Name[pt_BR]=Mapa radial +Name[ro]=Hartă radială +Name[ru]=Радиальная карта +Name[sk]=Radiálna mapa +Name[sl]=Krožen prikaz +Name[sr]=Кружна мапа +Name[sr@ijekavian]=Кружна мапа +Name[sr@ijekavianlatin]=Kružna mapa +Name[sr@latin]=Kružna mapa +Name[sv]=Radiell avbildning +Name[th]=Radial Map +Name[tr]=Radyal Harita +Name[uk]=Радіальна карта +Name[x-test]=xxRadial Mapxx +Name[zh_CN]=雷达图 +Name[zh_TW]=放射狀地圖 +MimeType=inode/directory; +ServiceTypes=KParts/ReadOnlyPart +X-KDE-Library=filelightpart +Type=Service +Icon=view_filelight diff --git a/filelight/misc/filelightpartui.rc b/filelight/misc/filelightpartui.rc new file mode 100644 index 00000000..104487cc --- /dev/null +++ b/filelight/misc/filelightpartui.rc @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/filelight/misc/filelightrc b/filelight/misc/filelightrc new file mode 100644 index 00000000..6cea220b --- /dev/null +++ b/filelight/misc/filelightrc @@ -0,0 +1,6 @@ +[filelight_part] +skipList=/dev/,/proc/,/sys/,/root/ + +[window] +Height=400 +Width=600 diff --git a/filelight/misc/filelightui.rc b/filelight/misc/filelightui.rc new file mode 100644 index 00000000..9a955b6b --- /dev/null +++ b/filelight/misc/filelightui.rc @@ -0,0 +1,96 @@ + + + + &Scan + + + + + + + + + + + + + + &View + + +Main Toolbar + + + + + + + +Location Toolbar + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/filelight/misc/hi16-app-filelight.png b/filelight/misc/hi16-app-filelight.png new file mode 100644 index 0000000000000000000000000000000000000000..ba774dfefbaeaa6556a808ddf9f3bec6602ee9e9 GIT binary patch literal 862 zcmV-k1EKthP) z9$INDf(l;jO%V3xMgIXWxpLMg?QNFJD#m6e_8>1nsoXlPlMlIJ;DmN6cW)nE|iS(X7X z8jTnX2EVr3?PnhW)a!LO&vUKY?V{^Cnx>)aI^*#~_`a_J^m;vlAfQkvocR9*!t*?B zI2__Q4pK^d-$yA$9LHFeg;EM#*RgGzYPDKC02BZg7Z;C}N+p6IAP53{-zSP9q9`JD zT!hq-QexXSrfE_xm$gR#p64|;Ha5^SjW7)VTSRb09^ssv;VWSyguu3Klu~p$9kH~u z(GOmp8 z@o~4v@}7aMDNc+OB?H>Um^?3Fn&zp)4h+LMdeC$n$HZ|=634t%+2zBh{-*qg;@t33s08}a!`u#rfFhV8!y!_+OH1`ZX@Lu7iH?Hz| zb&1!$1JQjqm%MTw7-PyHEMYJ40*bCl*?t@pgLy0G|N{ z5DWE;1lZZxDY~w!!Z0LB61KLs=%rI!Fc8-9JM6VQ!rBaG@+S@Da6}}eKqAz)5@2<8 z)vzo}tgWpvH#diAniPcKh38DBY7_MLcDa62aQ^NB=Vm2}j63)H`K61uu1SE+&CScj zVzK4>zS-;bgp@KBLZt5up4Z<$d;9ckb=SY%-~al}N$Hdj9Pf1U{?%pUqe~Y7rPSl+ o6Xumt{MLEx#b55f+Il$q7yjl;`rp@k=l}o!07*qoM6N<$f|RI|00000 literal 0 HcmV?d00001 diff --git a/filelight/misc/hi22-app-filelight.png b/filelight/misc/hi22-app-filelight.png new file mode 100644 index 0000000000000000000000000000000000000000..de77644d15900df7bc6d0dce382f5a97d4066c6e GIT binary patch literal 1346 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 QZvX%Q07*qoM6N<$g7EqSApigX literal 0 HcmV?d00001 diff --git a/filelight/misc/hi48-app-filelight.png b/filelight/misc/hi48-app-filelight.png new file mode 100644 index 0000000000000000000000000000000000000000..4582874d4de128d51e84d72307d0ad93fd99b81d GIT binary patch literal 3428 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 BT1Nl? literal 0 HcmV?d00001 diff --git a/filelight/misc/hisc-apps-filelight.svgz b/filelight/misc/hisc-apps-filelight.svgz new file mode 100644 index 0000000000000000000000000000000000000000..49fe0e9f13495cb0c23ea6b632ef3a441f98bd1a GIT binary patch literal 13926 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 A)c^nh literal 0 HcmV?d00001 diff --git a/filelight/src/CMakeLists.txt b/filelight/src/CMakeLists.txt new file mode 100644 index 00000000..40148228 --- /dev/null +++ b/filelight/src/CMakeLists.txt @@ -0,0 +1,22 @@ +####################################################################### +# Copyright 2008-2009 Martin Sandsmark +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License or (at your option) version 3 or any later version +# accepted by the membership of KDE e.V. (or its successor approved +# by the membership of KDE e.V.), which shall act as a proxy +# defined in Section 14 of version 3 of the license. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +####################################################################### + +add_subdirectory(app) +add_subdirectory(part) diff --git a/filelight/src/app/CMakeLists.txt b/filelight/src/app/CMakeLists.txt new file mode 100644 index 00000000..d395756a --- /dev/null +++ b/filelight/src/app/CMakeLists.txt @@ -0,0 +1,29 @@ +####################################################################### +# Copyright 2008-2009 Martin Sandsmark +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License or (at your option) version 3 or any later version +# accepted by the membership of KDE e.V. (or its successor approved +# by the membership of KDE e.V.), which shall act as a proxy +# defined in Section 14 of version 3 of the license. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +####################################################################### + +set(filelight_SRCS historyAction.cpp mainWindow.cpp main.cpp) + +kde4_add_app_icon(filelight_SRCS "../../misc/hi*-app-filelight.png") + +kde4_add_executable(filelight ${filelight_SRCS}) + +target_link_libraries(filelight ${KDE4_KDECORE_LIBS} ${KDE4_KPARTS_LIBS} ${KDE4_KDEUI_LIBS}) + +install(TARGETS filelight ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/filelight/src/app/historyAction.cpp b/filelight/src/app/historyAction.cpp new file mode 100644 index 00000000..2ff6a01f --- /dev/null +++ b/filelight/src/app/historyAction.cpp @@ -0,0 +1,113 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "historyAction.h" + +#include +#include +#include +#include +#include +#include + +inline HistoryAction::HistoryAction(const KIcon &icon, const QString &text, KActionCollection *ac) + : KAction(icon, text, ac) + , m_text(text) +{ + // .ui files make this false, but we can't rely on UI file as it isn't compiled in :( + setEnabled(false); +} + +void HistoryAction::push(const QString &path) +{ + if (path.isEmpty()) return; + + if (m_list.isEmpty() || (!m_list.isEmpty() && (m_list.last() != path))) + m_list.append(path); + + setHelpText(path); + setEnabled(true); +} + +QString HistoryAction::pop() +{ + const QString s = m_list.takeLast(); + if (!m_list.isEmpty()) + setHelpText(m_list.last()); + + setEnabled(); + return s; +} + + + +HistoryCollection::HistoryCollection(KActionCollection *ac, QObject *parent) + : QObject(parent) + , m_b(new HistoryAction(KIcon(QLatin1String( "go-previous" )), i18nc("Go to the last path viewed", "Back"), ac)) + , m_f(new HistoryAction(KIcon(QLatin1String( "go-next" )), i18nc("Go to forward in the history of paths viewed", "Forward"), ac)) + , m_receiver(0) +{ + ac->addAction(QLatin1String( "go_back" ), m_b); + ac->addAction(QLatin1String( "go_forward" ), m_f); + connect(m_b, SIGNAL(triggered(bool)), SLOT(pop())); + connect(m_f, SIGNAL(triggered(bool)), SLOT(pop())); +} + +void HistoryCollection::push(const KUrl &url) //slot +{ + if (!url.isEmpty()) + { + if (!m_receiver) + { + m_f->clear(); + m_receiver = m_b; + } + + m_receiver->push(url.path(KUrl::AddTrailingSlash)); + } + m_receiver = 0; +} + +void HistoryCollection::pop() //slot +{ + KUrl url; + const QString path = ((HistoryAction*)sender())->pop(); //FIXME here we remove the constness + url.setPath(path); + + m_receiver = (sender() == m_b) ? m_f : m_b; + + emit activated(url); +} + +void HistoryCollection::save(KConfigGroup &configgroup) +{ + configgroup.writePathEntry("backHistory", m_b->m_list); + configgroup.writePathEntry("forwardHistory", m_f->m_list); +} + +void HistoryCollection::restore(const KConfigGroup &configgroup) +{ + m_b->m_list = configgroup.readPathEntry("backHistory", QStringList()); + m_f->m_list = configgroup.readPathEntry("forwardHistory", QStringList()); + //TODO texts are not updated - no matter +} + +#include "historyAction.moc" diff --git a/filelight/src/app/historyAction.h b/filelight/src/app/historyAction.h new file mode 100644 index 00000000..edbfda4b --- /dev/null +++ b/filelight/src/app/historyAction.h @@ -0,0 +1,86 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef HISTORYACTION_H +#define HISTORYACTION_H + +#include +#include +#include + +class KConfigGroup; +class KActionCollection; + +class HistoryAction : KAction +{ + HistoryAction(const KIcon &icon, const QString &text, KActionCollection *ac); + + friend class HistoryCollection; + +public: + virtual void setEnabled(bool b = true) { + KAction::setEnabled(b && !m_list.isEmpty()); + } + + void clear() { + m_list.clear(); + setEnabled(false); + KAction::setText(m_text); + } + +private: + void setText(); + + void push(const QString &path); + QString pop(); + + const QString m_text; + QStringList m_list; +}; + + +class HistoryCollection : public QObject +{ + Q_OBJECT + +public: + HistoryCollection(KActionCollection *ac, QObject *parent); + + void save(KConfigGroup &configgroup); + void restore(const KConfigGroup &configgroup); + +public slots: + void push(const KUrl&); + void stop() { + m_receiver = 0; + } + +signals: + void activated(const KUrl&); + +private slots: + void pop(); + +private: + HistoryAction *m_b, *m_f, *m_receiver; +}; + +#endif diff --git a/filelight/src/app/main.cpp b/filelight/src/app/main.cpp new file mode 100644 index 00000000..0b538d50 --- /dev/null +++ b/filelight/src/app/main.cpp @@ -0,0 +1,78 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "define.h" +#include "mainWindow.h" + +#include +#include +#include +#include +#include + +static KAboutData about( + APP_NAME, + 0, + ki18n(APP_PRETTYNAME), + APP_VERSION, + ki18n("Graphical disk-usage information"), + KAboutData::License_GPL, + ki18n("(C) 2006 Max Howell\n\ + (C) 2008-2013 Martin Sandsmark"), + KLocalizedString(), + "http://utils.kde.org/projects/filelight"); + + +int main(int argc, char *argv[]) +{ + using Filelight::MainWindow; + + about.addAuthor(ki18n("Martin Sandsmark"), ki18n("Maintainer"), "martin.sandsmark@kde.org", "http://iskrembilen.com/"); + about.addAuthor(ki18n("Max Howell"), ki18n("Original author"), "max.howell@methylblue.com", "http://www.methylblue.com/"); + about.addCredit(ki18n("Lukas Appelhans"), ki18n("Help and support")); + about.addCredit(ki18n("Steffen Gerlach"), ki18n("Inspiration"), 0, "http://www.steffengerlach.de/"); + about.addCredit(ki18n("Mike Diehl"), ki18n("Original documentation"), 0, 0); + about.addCredit(ki18n("Sune Vuorela"), ki18n("Icon"), 0, 0); + about.addCredit(ki18n("Nuno Pinheiro"), ki18n("Icon"), 0, 0); + + KCmdLineArgs::init(argc, argv, &about); + + KCmdLineOptions options; + KLocale *tmpLocale = new KLocale(QLatin1String(APP_NAME)); + options.add(ki18nc("Path in the file system to scan", "+[path]").toString(tmpLocale).toLocal8Bit(), ki18n("Scan 'path'")); + delete tmpLocale; + KCmdLineArgs::addCmdLineOptions(options); + + KApplication app; + + if (!app.isSessionRestored()) { + MainWindow *mw = new MainWindow(); + + KCmdLineArgs* const args = KCmdLineArgs::parsedArgs(); + if (args->count() > 0) mw->scan(args->url(0)); + args->clear(); + + mw->show(); + } + else RESTORE(MainWindow); + + return app.exec(); +} diff --git a/filelight/src/app/mainWindow.cpp b/filelight/src/app/mainWindow.cpp new file mode 100644 index 00000000..80427fb7 --- /dev/null +++ b/filelight/src/app/mainWindow.cpp @@ -0,0 +1,295 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "mainWindow.h" +#include "part/part.h" +#include "historyAction.h" + +#include //std::exit() +#include //setupActions() +#include //locationbar +#include +#include +#include +#include //slotScanFolder +#include //for editToolbar dialog +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //locationbar +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Filelight { + +MainWindow::MainWindow() : KParts::MainWindow(), m_part(0) +{ +// setXMLFile("filelightui.rc"); + KPluginFactory *factory = KPluginLoader(QLatin1String( "filelightpart" )).factory(); + + if (!factory) { + KMessageBox::error(this, i18n("Unable to load the Filelight Part.\nPlease make sure Filelight was correctly installed.")); + std::exit(1); + return; + } + + m_part = static_cast(factory->create(this)); + + if (m_part) { + setStandardToolBarMenuEnabled(true); + setupActions(); + createGUI(m_part); + setCentralWidget(m_part->widget()); + + stateChanged(QLatin1String( "scan_failed" )); //bah! doesn't affect the parts' actions, should I add them to the actionCollection here? + + connect(m_part, SIGNAL(started(KIO::Job*)), SLOT(scanStarted())); + connect(m_part, SIGNAL(completed()), SLOT(scanCompleted())); + connect(m_part, SIGNAL(canceled(QString)), SLOT(scanFailed())); + + connect(m_part, SIGNAL(canceled(QString)), m_histories, SLOT(stop())); + connect(BrowserExtension::childObject(m_part), SIGNAL(openUrlNotify()), SLOT(urlAboutToChange())); + + const KConfigGroup config = KGlobal::config()->group("general"); + m_combo->setHistoryItems(config.readPathEntry("comboHistory", QStringList())); + } else { + KMessageBox::error(this, i18n("Unable to create part widget.")); + std::exit(1); + } + + setAutoSaveSettings(QLatin1String( "window" )); +} + +inline void MainWindow::setupActions() //singleton function +{ + KActionCollection *const ac = actionCollection(); + + m_combo = new KHistoryComboBox(this); + m_combo->setCompletionObject(new KUrlCompletion(KUrlCompletion::DirCompletion)); + m_combo->setAutoDeleteCompletionObject(true); + m_combo->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed)); + m_combo->setDuplicatesEnabled(false); + + KStandardAction::open(this, SLOT(slotScanFolder()), ac); + KStandardAction::quit(this, SLOT(close()), ac); + KStandardAction::up(this, SLOT(slotUp()), ac); + KStandardAction::configureToolbars(this, SLOT(configToolbars()), ac); + KStandardAction::keyBindings(this, SLOT(configKeys()), ac); + + KAction* action; + + action = ac->addAction(QLatin1String( "scan_home" ), this, SLOT(slotScanHomeFolder())); + action->setText(i18n("Scan &Home Folder")); + action->setIcon(KIcon(QLatin1String( "user-home" ))); + action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_Home)); + + action = ac->addAction(QLatin1String( "scan_root" ), this, SLOT(slotScanRootFolder())); + action->setText(i18n("Scan &Root Folder")); + action->setIcon(KIcon(QLatin1String( "folder-red" ))); + + action = ac->addAction(QLatin1String( "scan_rescan" ), m_part, SLOT(rescan())); + action->setText(i18n("Rescan")); + action->setIcon(KIcon(QLatin1String( "view-refresh" ))); + action->setShortcut(KStandardShortcut::reload()); + + + action = ac->addAction(QLatin1String( "scan_stop" ), this, SLOT(slotAbortScan())); + action->setText(i18n("Stop")); + action->setIcon(KIcon(QLatin1String( "process-stop" ))); + action->setShortcut(Qt::Key_Escape); + + action = ac->addAction(QLatin1String( "go" ), m_combo, SIGNAL(returnPressed())); + action->setText(i18n("Go")); + action->setIcon(KIcon(QLatin1String( "go-jump-locationbar" ))); + + action = ac->addAction(QLatin1String( "location_bar" ), 0, 0); + action->setText(i18n("Location Bar")); + action->setDefaultWidget(m_combo); + + action = ac->addAction(QLatin1String( "scan_folder" ), this, SLOT(slotScanFolder())); + action->setText(i18n("Scan Folder")); + action->setIcon(KIcon(QLatin1String( "folder" ))); + + m_recentScans = new KRecentFilesAction(i18n("&Recent Scans"), ac); + m_recentScans->setMaxItems(8); + + m_histories = new HistoryCollection(ac, this); + + m_recentScans->loadEntries(KGlobal::config()->group("general")); + + connect(m_recentScans, SIGNAL(urlSelected(KUrl)), SLOT(slotScanUrl(KUrl))); + connect(m_combo, SIGNAL(returnPressed()), SLOT(slotComboScan())); + connect(m_histories, SIGNAL(activated(KUrl)), SLOT(slotScanUrl(KUrl))); +} + +void MainWindow::closeEvent(QCloseEvent *event) +{ + KConfigGroup config = KGlobal::config()->group("general"); + + m_recentScans->saveEntries(config); + config.writePathEntry("comboHistory", m_combo->historyItems()); + config.sync(); + + KParts::MainWindow::closeEvent(event); +} + +inline void MainWindow::configToolbars() //slot +{ + KEditToolBar dialog(factory(), this); + + if (dialog.exec()) //krazy:exclude=crashy + { + createGUI(m_part); + applyMainWindowSettings(KGlobal::config()->group("window")); + } +} + +inline void MainWindow::configKeys() //slot +{ + KShortcutsDialog::configure(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this, true); +} + +inline void MainWindow::slotScanFolder() +{ + slotScanUrl(KFileDialog::getExistingDirectoryUrl(m_part->url(), this, i18n("Select Folder to Scan"))); +} + +inline void MainWindow::slotScanHomeFolder() { + slotScanPath(QDir::homePath()); +} +inline void MainWindow::slotScanRootFolder() { + slotScanPath(QDir::rootPath()); +} +inline void MainWindow::slotUp() { + slotScanUrl(m_part->url().upUrl()); +} + +inline void MainWindow::slotComboScan() +{ + QString path = m_combo->lineEdit()->text(); + + KUrl url = KUrl(path); + + if (url.isRelative()) + path = QLatin1String( "~/" ) + path; // KUrlCompletion completes relative to ~, not CWD + + path = KShell::tildeExpand(path); + + if (slotScanPath(path)) + m_combo->addToHistory(path); +} + +inline bool MainWindow::slotScanPath(const QString &path) +{ + return slotScanUrl(KUrl(path)); +} + +bool MainWindow::slotScanUrl(const KUrl &url) +{ + const KUrl oldUrl = m_part->url(); + + if (m_part->openUrl(url)) + { + m_histories->push(oldUrl); + return true; + } + else + return false; +} + +inline void MainWindow::slotAbortScan() +{ + if (m_part->closeUrl()) action("scan_stop")->setEnabled(false); +} + +inline void MainWindow::scanStarted() +{ + stateChanged(QLatin1String( "scan_started" )); + m_combo->clearFocus(); +} + +inline void MainWindow::scanFailed() +{ + stateChanged(QLatin1String( "scan_failed" )); + qobject_cast(action("go_up"))->setHelpText(QString()); + m_combo->lineEdit()->clear(); +} + +void MainWindow::scanCompleted() +{ + KAction *goUp = qobject_cast(action("go_up")); + const KUrl url = m_part->url(); + + stateChanged(QLatin1String( "scan_complete" )); + + m_combo->lineEdit()->setText(m_part->prettyUrl()); + + if (url.path(KUrl::LeaveTrailingSlash) == QLatin1String( "/" )) { + goUp->setEnabled(false); + goUp->setHelpText(QString()); + } + else + goUp->setHelpText(url.upUrl().path(KUrl::LeaveTrailingSlash)); + + m_recentScans->addUrl(url); //FIXME doesn't set the tick +} + +inline void MainWindow::urlAboutToChange() +{ + //called when part's URL is about to change internally + //the part will then create the Map and emit completed() + + m_histories->push(m_part->url()); +} + + +/********************************************** + SESSION MANAGEMENT + **********************************************/ + +void MainWindow::saveProperties(KConfigGroup &configgroup) //virtual +{ + m_histories->save(configgroup); + configgroup.writeEntry("currentMap", m_part->url().path()); +} + +void MainWindow::readProperties(const KConfigGroup &configgroup) //virtual +{ + m_histories->restore(configgroup); + slotScanPath(configgroup.group("General").readEntry("currentMap", QString())); +} + +} //namespace Filelight + +#include "mainWindow.moc" diff --git a/filelight/src/app/mainWindow.h b/filelight/src/app/mainWindow.h new file mode 100644 index 00000000..f518c991 --- /dev/null +++ b/filelight/src/app/mainWindow.h @@ -0,0 +1,87 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include + +class KSqueezedTextLabel; +class KHistoryComboBox; +class KRecentFilesAction; + +class HistoryCollection; + + +namespace Filelight { + +class Part; + +class MainWindow : public KParts::MainWindow +{ + Q_OBJECT + +public: + MainWindow(); + + void scan(const KUrl &u) { + slotScanUrl(u); + } + +private slots: + void slotUp(); + void slotComboScan(); + void slotScanFolder(); + void slotScanHomeFolder(); + void slotScanRootFolder(); + bool slotScanUrl(const KUrl&); + bool slotScanPath(const QString&); + void slotAbortScan(); + + void configToolbars(); + void configKeys(); + + void scanStarted(); + void scanFailed(); + void scanCompleted(); + + void urlAboutToChange(); + +protected: + virtual void saveProperties(KConfigGroup&); + virtual void readProperties(const KConfigGroup&); + virtual void closeEvent(QCloseEvent *event); + +private: + Filelight::Part *m_part; + + KSqueezedTextLabel *m_status[2]; + KHistoryComboBox *m_combo; + HistoryCollection *m_histories; + KRecentFilesAction *m_recentScans; + + void setupStatusBar(); + void setupActions(); +}; + +} // namespace Filelight + +#endif diff --git a/filelight/src/define.h b/filelight/src/define.h new file mode 100644 index 00000000..07bdc935 --- /dev/null +++ b/filelight/src/define.h @@ -0,0 +1,36 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2013 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + + +#ifndef DEFINE_H +#define DEFINE_H + +#include "part/Config.h" + +#undef VERSION +#undef PACKAGE +#undef PRETTYNAME + +#define APP_NAME "filelight" +#define APP_VERSION "1.20" +#define APP_PRETTYNAME "Filelight" + +#endif diff --git a/filelight/src/part/CMakeLists.txt b/filelight/src/part/CMakeLists.txt new file mode 100644 index 00000000..2608eb4a --- /dev/null +++ b/filelight/src/part/CMakeLists.txt @@ -0,0 +1,47 @@ +####################################################################### +# Copyright 2008-2009 Martin Sandsmark +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License or (at your option) version 3 or any later version +# accepted by the membership of KDE e.V. (or its successor approved +# by the membership of KDE e.V.), which shall act as a proxy +# defined in Section 14 of version 3 of the license. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +####################################################################### + +add_subdirectory(radialMap) + +set(filelight_PART_SRCS + part.cpp + scan.cpp + progressBox.cpp + Config.cpp + settingsDialog.cpp + fileTree.cpp + localLister.cpp + remoteLister.cpp + summaryWidget.cpp) + +kde4_add_ui_files(filelight_PART_SRCS dialog.ui) + +kde4_add_plugin(filelightpart ${filelight_PART_SRCS}) + +target_link_libraries(filelightpart radialmap + ${KDE4_KDECORE_LIBS} + ${KDE4_KPARTS_LIBS} + ${KDE4_KDEUI_LIBS} + ${KDE4_KIO_LIBS} + ${KDE4_KFILE_LIBS} + ${KDE4_SOLID_LIBS}) + + +install(TARGETS filelightpart DESTINATION ${PLUGIN_INSTALL_DIR}) diff --git a/filelight/src/part/Config.cpp b/filelight/src/part/Config.cpp new file mode 100644 index 00000000..fe81a56d --- /dev/null +++ b/filelight/src/part/Config.cpp @@ -0,0 +1,77 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "Config.h" + +#include +#include +#include +#include +#include + +bool Config::scanAcrossMounts; +bool Config::scanRemoteMounts; +bool Config::scanRemovableMedia; +bool Config::varyLabelFontSizes; +bool Config::showSmallFiles; +bool Config::antialias; +uint Config::contrast; +uint Config::minFontPitch; +uint Config::defaultRingDepth; +Filelight::MapScheme Config::scheme; +QStringList Config::skipList; + +void +Filelight::Config::read() +{ + const KConfigGroup config = KGlobal::config()->group("filelight_part"); + + scanAcrossMounts = config.readEntry("scanAcrossMounts", false); + scanRemoteMounts = config.readEntry("scanRemoteMounts", false); + scanRemovableMedia = config.readEntry("scanRemovableMedia", false); + varyLabelFontSizes = config.readEntry("varyLabelFontSizes", true); + showSmallFiles = config.readEntry("showSmallFiles", false); + contrast = config.readEntry("contrast", 75); + antialias = config.readEntry("antialias", true); + minFontPitch = config.readEntry("minFontPitch", QFont().pointSize() - 3); + scheme = (MapScheme) config.readEntry("scheme", 0); + skipList = config.readEntry("skipList", QStringList()); + + defaultRingDepth = 4; +} + +void +Filelight::Config::write() +{ + KConfigGroup config = KGlobal::config()->group("filelight_part"); + + config.writeEntry("scanAcrossMounts", scanAcrossMounts); + config.writeEntry("scanRemoteMounts", scanRemoteMounts); + config.writeEntry("scanRemovableMedia", scanRemovableMedia); + config.writeEntry("varyLabelFontSizes", varyLabelFontSizes); + config.writeEntry("showSmallFiles", showSmallFiles); + config.writeEntry("contrast", contrast); + config.writeEntry("antialias", antialias); + config.writeEntry("minFontPitch", minFontPitch); + config.writeEntry("scheme", (int)scheme); // TODO: make the enum belong to a qwidget, + //and use magic macros to make it save this properly + config.writePathEntry("skipList", skipList); +} diff --git a/filelight/src/part/Config.h b/filelight/src/part/Config.h new file mode 100644 index 00000000..d95b84c9 --- /dev/null +++ b/filelight/src/part/Config.h @@ -0,0 +1,60 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef Config_H +#define Config_H + +#include + +class KConfig; + +namespace Filelight +{ +enum MapScheme { Rainbow, KDE, HighContrast, FileDensity, ModTime }; + +class Config +{ + static KConfig& kconfig(); + +public: + static void read(); + static void write(); + + //keep everything positive, avoid using DON'T, NOT or NO + + static bool scanAcrossMounts; + static bool scanRemoteMounts; + static bool scanRemovableMedia; + static bool varyLabelFontSizes; + static bool showSmallFiles; + static uint contrast; + static bool antialias; + static uint minFontPitch; + static uint defaultRingDepth; + + static MapScheme scheme; + static QStringList skipList; +}; +} + +using Filelight::Config; + +#endif diff --git a/filelight/src/part/dialog.ui b/filelight/src/part/dialog.ui new file mode 100644 index 00000000..70b658c7 --- /dev/null +++ b/filelight/src/part/dialog.ui @@ -0,0 +1,426 @@ + + + Dialog + + + + 0 + 0 + 556 + 405 + + + + Settings - Filelight + + + + 0 + + + + + + 0 + 0 + + + + false + + + 0 + + + + Scannin&g + + + + + + + 0 + 0 + + + + Do &not scan these folders: + + + Qt::PlainText + + + false + + + m_listBox + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 180 + 21 + + + + + + + + true + + + R&emove + + + false + + + + + + + &Add... + + + false + + + false + + + + + + + + + + 0 + 15 + + + + QFrame::HLine + + + QFrame::Sunken + + + 1 + + + 1 + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 16 + 50 + + + + + + + + false + + + + 0 + 0 + + + + + 32767 + 32767 + + + + false + + + + + + Prevents scanning of filesystems that are not on this computer, e.g. NFS or Samba mounts. + + + Exclude remote files&ystems + + + + + + + Allows scans to enter directories that are part of other filesystems. For example, when unchecked, this will usually prevent the contents of <b>/mnt</b> from being scanned if you scan <b>/</b>. + + + Scan across filesystem &boundaries + + + + + + + false + + + + + + Prevents Filelight from scanning removable media (eg. CD-ROMs). + + + E&xclude removable media + + + + + + + + + + &Appearance + + + + + + + 0 + 0 + + + + Color scheme + + + + + + + + + 0 + + + QLayout::SetNoConstraint + + + 0 + + + + + + 0 + 0 + + + + Co&ntrast + + + Qt::PlainText + + + false + + + contrastSlider + + + + + + + + 0 + 0 + + + + Here you can vary the contrast of the filemap in realtime. + + + 1 + + + 100 + + + Qt::Horizontal + + + + + + + + + + + + Anti-aliasing the filemap makes it clearer and prettier, unfortunately it also makes rendering very slow. + + + &Use anti-aliasing + + + + + + + Some files are too small to be rendered on the filemap. Selecting this option makes these files visible by merging them all into a single "multi-segment". + + + Show small files + + + + + + + true + + + The font size of exploded labels can be varied relative to the depth of the directories they represent. This helps you spot the important labels more easily. Set a sensible minimum font size. + + + Var&y label font sizes + + + + + + + QLayout::SetMinimumSize + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 20 + 20 + + + + + + + + true + + + + 0 + 0 + + + + The smallest font size Filelight can use to render labels. + + + Minimum font si&ze: + + + Qt::PlainText + + + false + + + minFontPitch + + + + + + + 20 + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + KButtonGroup + QGroupBox +
kbuttongroup.h
+ 1 +
+ + KTabWidget + QTabWidget +
ktabwidget.h
+ 1 +
+
+ + tabWidget + varyLabelFontSizes + m_removeButton + m_addButton + scanAcrossMounts + dontScanRemoteMounts + dontScanRemovableMedia + + +
diff --git a/filelight/src/part/fileTree.cpp b/filelight/src/part/fileTree.cpp new file mode 100644 index 00000000..37b69113 --- /dev/null +++ b/filelight/src/part/fileTree.cpp @@ -0,0 +1,39 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "fileTree.h" + +#include +#include + +QString +File::fullPath(const Folder *root /*= 0*/) const +{ + QString path; + + if (root == this) + root = 0; //prevent returning empty string when there is something we could return + + for (const Folder *d = (Folder*)this; d != root && d; d = d->parent()) + path.prepend(d->name()); + + return path; +} diff --git a/filelight/src/part/fileTree.h b/filelight/src/part/fileTree.h new file mode 100644 index 00000000..8b33cea4 --- /dev/null +++ b/filelight/src/part/fileTree.h @@ -0,0 +1,327 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef FILETREE_H +#define FILETREE_H + +#include //qstrdup +#include //decodeName() +#include +#include +#include + +#include + +//TODO these are pointlessly general purpose now, make them incredibly specific + +typedef quint64 FileSize; +typedef quint64 Dirsize; //**** currently unused + +template class Iterator; +template class ConstIterator; +template class Chain; + +template +class Link +{ +public: + Link(T* const t) : prev(this), next(this), data(t) {} + Link() : prev(this), next(this), data(0) {} + +//TODO unlinking is slow and you don't use it very much in this context. +// ** Perhaps you can make a faster deletion system that doesn't bother tidying up first +// ** and then you MUST call some kind of detach() function when you remove elements otherwise + ~Link() { + delete data; + unlink(); + } + + friend class Iterator; + friend class ConstIterator; + friend class Chain; + +private: + void unlink() { + prev->next = next; + next->prev = prev; + prev = next = this; + } + + Link* prev; + Link* next; + + T* data; //ensure only iterators have access to this +}; + + +template +class Iterator +{ +public: + Iterator() : link(0) { } //**** remove this, remove this REMOVE THIS!!! dangerous as your implementation doesn't test for null links, always assumes they can be derefenced + Iterator(Link *p) : link(p) { } + + bool operator==(const Iterator& it) const { + return link == it.link; + } + bool operator!=(const Iterator& it) const { + return link != it.link; + } + bool operator!=(const Link *p) const { + return p != link; + } + + //here we have a choice, really I should make two classes one const the other not + const T* operator*() const { + return link->data; + } + T* operator*() { + return link->data; + } + + Iterator& operator++() { + link = link->next; //**** does it waste time returning in places where we don't use the retval? + return *this; + } + + bool isNull() const { + return (link == 0); //REMOVE WITH ABOVE REMOVAL you don't want null iterators to be possible + } + + void transferTo(Chain &chain) + { + chain.append(remove()); + } + + T* remove() //remove from list, delete Link, data is returned NOT deleted + + { + T* const d = link->data; + Link* const p = link->prev; + + link->data = 0; + delete link; + link = p; //make iterator point to previous element, YOU must check this points to an element + + return d; + } + +private: + Link *link; +}; + + +template +class ConstIterator +{ +public: + ConstIterator(Link *p) : link(p) { } + + bool operator==(const Iterator& it) const { + return link == it.link; + } + bool operator!=(const Iterator& it) const { + return link != it.link; + } + bool operator!=(const Link *p) const { + return p != link; + } + + const T* operator*() const { + return link->data; + } + + ConstIterator& operator++() { + link = link->next; + return *this; + } + +private: + const Link *link; +}; + + +template +class Chain +{ +public: + virtual ~Chain() { + empty(); + } + + void append(T* const data) + { + Link* const link = new Link(data); + + link->prev = head.prev; + link->next = &head; + + head.prev->next = link; + head.prev = link; + } + + void transferTo(Chain &c) + { + if (isEmpty()) return; + + Link* const first = head.next; + Link* const last = head.prev; + + head.unlink(); + + first->prev = c.head.prev; + c.head.prev->next = first; + + last->next = &c.head; + c.head.prev = last; + } + + void empty() { + while (head.next != &head) { + delete head.next; + } + } + + Iterator iterator() const { + return Iterator(head.next); + } + ConstIterator constIterator() const { + return ConstIterator(head.next); + } + const Link *end() const { + return &head; + } + bool isEmpty() const { + return head.next == &head; + } + +private: + Link head; + void operator=(const Chain&); +}; + + +class Folder; +class QString; + +class File +{ +public: + friend class Folder; + +public: + File(const char *name, FileSize size) : m_parent(0), m_name(qstrdup(name)), m_size(size) {} + virtual ~File() { + delete [] m_name; + } + + Folder *parent() const { + return m_parent; + } + const char *name8Bit() const { + return m_name; + } + FileSize size() const { + return m_size; + } + QString name() const { + return QFile::decodeName(m_name); + } + + virtual bool isFolder() const { + return false; + } + + QString fullPath(const Folder* = 0) const; + QString humanReadableSize() const { + return KGlobal::locale()->formatByteSize(m_size); + } + +protected: + File(const char *name, FileSize size, Folder *parent) : m_parent(parent), m_name(qstrdup(name)), m_size(size) {} + + Folder *m_parent; //0 if this is treeRoot + char *m_name; + FileSize m_size; //in units of KiB + +private: + File(const File&); + void operator=(const File&); +}; + + +class Folder : public Chain, public File +{ +public: + Folder(const char *name) : File(name, 0), m_children(0) {} //DON'T pass the full path! + + uint children() const { + return m_children; + } + virtual bool isFolder() const { + return true; + } + + ///appends a Folder + void append(Folder *d, const char *name=0) + { + if (name) { + delete [] d->m_name; + d->m_name = qstrdup(name); + } //directories that had a fullpath copy just their names this way + + m_children += d->children(); //doesn't include the dir itself + d->m_parent = this; + append((File*)d); //will add 1 to filecount for the dir itself + } + + ///appends a File + void append(const char *name, FileSize size) + { + append(new File(name, size, this)); + } + + /// removes a file + void remove(const File *f) { + for (Iterator it = iterator(); it != end(); ++it) + if (f == (*it)) + it.remove(); + + for (Folder *d = this; d; d = d->parent()) + d->m_size -= f->size(); + } + +private: + void append(File *p) + { + m_children++; + m_size += p->size(); + Chain::append(p); + } + + uint m_children; + +private: + Folder(const Folder&); //undefined + void operator=(const Folder&); //undefined +}; + +#endif diff --git a/filelight/src/part/localLister.cpp b/filelight/src/part/localLister.cpp new file mode 100644 index 00000000..d40aea29 --- /dev/null +++ b/filelight/src/part/localLister.cpp @@ -0,0 +1,260 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "localLister.h" + +#include "Config.h" +#include "fileTree.h" +#include "scan.h" + +#include +#include +#include +#include + +#include //postEvent() +#include +#include + +#include +#include +#ifdef Q_OS_SOLARIS +#include +#elif !defined(Q_WS_WIN) +#include +#endif +#include +#include +#include + +#ifdef HAVE_MNTENT_H +#include +#endif + +namespace Filelight +{ +QStringList LocalLister::s_remoteMounts; +QStringList LocalLister::s_localMounts; + +LocalLister::LocalLister(const QString &path, Chain *cachedTrees, ScanManager *parent) + : QThread() + , m_path(path) + , m_trees(cachedTrees) + , m_parent(parent) +{ + //add empty directories for any mount points that are in the path + //TODO empty directories is not ideal as adds to fileCount incorrectly + + QStringList list(Config::skipList); + if (!Config::scanAcrossMounts) list += s_localMounts; + if (!Config::scanRemoteMounts) list += s_remoteMounts; + + foreach(const QString &ignorePath, list) { + if (ignorePath.startsWith(path)) { + m_trees->append(new Folder(ignorePath.toLocal8Bit())); + } + } +} + +void +LocalLister::run() +{ + //recursively scan the requested path + const QByteArray path = QFile::encodeName(m_path); + Folder *tree = scan(path, path); + + //delete the list of trees useful for this scan, + //in a sucessful scan the contents would now be transferred to 'tree' + delete m_trees; + + if (m_parent->m_abort) //scan was cancelled + { + kDebug() << "Scan successfully aborted"; + delete tree; + tree = 0; + } + kDebug() << "Emitting signal to cache results ..."; + emit branchCompleted(tree, true); + kDebug() << "Thread terminating ..."; +} + +#ifndef S_BLKSIZE +#define S_BLKSIZE 512 +#endif + + +#include +static void +outputError(QByteArray path) +{ + ///show error message that stat or opendir may give + +#define out(s) kError() << s ": " << path; break + + switch (errno) { + case EACCES: + out("Inadequate access permissions"); + case EMFILE: + out("Too many file descriptors in use by Filelight"); + case ENFILE: + out("Too many files are currently open in the system"); + case ENOENT: + out("A component of the path does not exist, or the path is an empty string"); + case ENOMEM: + out("Insufficient memory to complete the operation"); + case ENOTDIR: + out("A component of the path is not a folder"); + case EBADF: + out("Bad file descriptor"); + case EFAULT: + out("Bad address"); +#ifndef Q_WS_WIN + case ELOOP: //NOTE shouldn't ever happen + out("Too many symbolic links encountered while traversing the path"); +#endif + case ENAMETOOLONG: + out("File name too long"); + } + +#undef out +} + +Folder* +LocalLister::scan(const QByteArray &path, const QByteArray &dirname) +{ + Folder *cwd = new Folder(dirname); + DIR *dir = opendir(path); + + if (!dir) { + outputError(path); + return cwd; + } + +#ifdef _MSC_VER +//use a wider struct on win for nice handling of files larger than 2 GB +#undef KDE_struct_stat +#undef KDE_lstat +#define KDE_struct_stat struct _stat64 +#define KDE_lstat kdewin32_stat64 +#endif + + KDE_struct_stat statbuf; + dirent *ent; + while ((ent = KDE_readdir(dir))) + { + if (m_parent->m_abort) + return cwd; + + if (qstrcmp(ent->d_name, ".") == 0 || qstrcmp(ent->d_name, "..") == 0) + continue; + + QByteArray new_path = path; + new_path += ent->d_name; + + //get file information + if (KDE_lstat(new_path, &statbuf) == -1) { + outputError(new_path); + continue; + } + + if (S_ISLNK(statbuf.st_mode) || + S_ISCHR(statbuf.st_mode) || + S_ISBLK(statbuf.st_mode) || + S_ISFIFO(statbuf.st_mode)|| + S_ISSOCK(statbuf.st_mode)) + { + continue; + } + + if (S_ISREG(statbuf.st_mode)) //file +#ifndef Q_WS_WIN + cwd->append(ent->d_name, (statbuf.st_blocks * S_BLKSIZE)); +#else + cwd->append(ent->d_name, statbuf.st_size); +#endif + + else if (S_ISDIR(statbuf.st_mode)) //folder + { + Folder *d = 0; + QByteArray new_dirname = ent->d_name; + new_dirname += '/'; + new_path += '/'; + + //check to see if we've scanned this section already + + for (Iterator it = m_trees->iterator(); it != m_trees->end(); ++it) + { + if (new_path == (*it)->name8Bit()) + { + kDebug() << "Tree pre-completed: " << (*it)->name(); + d = it.remove(); + m_parent->m_files += d->children(); + //**** ideally don't have this redundant extra somehow + cwd->append(d, new_dirname); + } + } + + if (!d) //then scan + if ((d = scan(new_path, new_dirname))) //then scan was successful + cwd->append(d); + } + + ++m_parent->m_files; + } + + closedir(dir); + + return cwd; +} + +void LocalLister::readMounts() +{ + const Solid::StorageAccess *partition; + const Solid::StorageVolume *volume; + QStringList remoteFsTypes; + remoteFsTypes << QLatin1String( "smbfs" ) << QLatin1String( "nfs" ) << QLatin1String( "afs" ); //TODO: expand + + foreach (const Solid::Device &device, Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess)) + { // Iterate over all the partitions available. + if (!device.is()) continue; // It happens. + if (!device.is()) continue; + + partition = device.as(); + if (!partition->isAccessible() || partition->filePath() == QLatin1String( "/" ) || partition->filePath().isEmpty()) continue; + + volume = device.as(); + if (remoteFsTypes.contains(volume->fsType())) { + if (!s_remoteMounts.contains(partition->filePath())) { + s_remoteMounts.append(partition->filePath()); + } + } else if (!s_localMounts.contains(partition->filePath())) { + s_localMounts.append(partition->filePath()); + } + } + + kDebug() << "Found the following remote filesystems: " << s_remoteMounts; + kDebug() << "Found the following local filesystems: " << s_localMounts; +} + +}//namespace Filelight + +#include "localLister.moc" + diff --git a/filelight/src/part/localLister.h b/filelight/src/part/localLister.h new file mode 100644 index 00000000..d9c6b4bb --- /dev/null +++ b/filelight/src/part/localLister.h @@ -0,0 +1,61 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef LOCALLISTER_H +#define LOCALLISTER_H + +#include +#include + +class Folder; +template class Chain; + +namespace Filelight +{ +class ScanManager; + +class LocalLister : public QThread +{ + Q_OBJECT + +public: + LocalLister(const QString &path, Chain *cachedTrees, ScanManager *parent); + + static void readMounts(); + +signals: + void branchCompleted(Folder* tree, bool finished); + +private: + QString m_path; + Chain *m_trees; + ScanManager *m_parent; + +private: + virtual void run(); + Folder *scan(const QByteArray&, const QByteArray&); + +private: + static QStringList s_localMounts, s_remoteMounts; //TODO namespace +}; +} + +#endif diff --git a/filelight/src/part/part.cpp b/filelight/src/part/part.cpp new file mode 100644 index 00000000..2a3fea19 --- /dev/null +++ b/filelight/src/part/part.cpp @@ -0,0 +1,341 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2013 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "part.h" + +#include "Config.h" +#include "define.h" +#include "fileTree.h" +#include "progressBox.h" +#include "radialMap/widget.h" +#include "scan.h" +#include "settingsDialog.h" +#include "summaryWidget.h" + +#include //::createAboutData() +#include +#include +#include +#include //::start() +#include +#include +#include + +#include //encodeName() +#include //postInit() hack +#include +#include +#include + +#include //access() +#include + +namespace Filelight { + +K_PLUGIN_FACTORY(filelightPartFactory, registerPlugin();) // produce a factory +K_EXPORT_PLUGIN(filelightPartFactory(KAboutData( + "filelightpart", + 0, + ki18n(APP_PRETTYNAME), + APP_VERSION, + ki18n("Displays file usage in an easy to understand way."), + KAboutData::License_GPL, + ki18n("(c) 2002-2004 Max Howell\n\ + (c) 2008-2013 Martin T. Sandsmark"), + KLocalizedString(), + "http://utils.kde.org/projects/filelight", + "martin.sandsmark@kde.org"). + setProgramIconName(QLatin1String(APP_NAME)). + setCatalogName(APP_NAME))) + +BrowserExtension::BrowserExtension(Part *parent) + : KParts::BrowserExtension(parent) +{} + + +Part::Part(QWidget *parentWidget, QObject *parent, const QList&) + : ReadOnlyPart(parent) + , m_summary(0) + , m_ext(new BrowserExtension(this)) + , m_statusbar(new StatusBarExtension(this)) + , m_map(0) + , m_started(false) +{ + Config::read(); + setComponentData(filelightPartFactory::componentData()); + setXMLFile(QLatin1String( "filelightpartui.rc" )); + + QScrollArea *scrollArea = new QScrollArea(parentWidget); + scrollArea->setWidgetResizable(true); + setWidget(scrollArea); + + QWidget *partWidget = new QWidget(scrollArea); + scrollArea->setWidget(partWidget); + + partWidget->setBackgroundRole(QPalette::Base); + partWidget->setAutoFillBackground(true); + + m_layout = new QGridLayout(); + partWidget->setLayout(m_layout); + + m_manager = new ScanManager(partWidget); + + m_map = new RadialMap::Widget(partWidget); + m_layout->addWidget(m_map); + + m_stateWidget = new ProgressBox(statusBar(), this, m_manager); + m_layout->addWidget(m_stateWidget); + m_stateWidget->hide(); + + m_numberOfFiles = new QLabel(); + m_statusbar->addStatusBarItem(m_numberOfFiles, 0, true); + + KStandardAction::zoomIn(m_map, SLOT(zoomIn()), actionCollection()); + KStandardAction::zoomOut(m_map, SLOT(zoomOut()), actionCollection()); + KAction *action = actionCollection()->addAction(QLatin1String("configure_filelight")); + action->setText(i18n("Configure Filelight...")); + action->setIcon(KIcon(QLatin1String("configure"))); + connect(action, SIGNAL(triggered()), this, SLOT(configFilelight())); + + connect(m_map, SIGNAL(created(const Folder*)), SIGNAL(completed())); + connect(m_map, SIGNAL(created(const Folder*)), SLOT(mapChanged(const Folder*))); + connect(m_map, SIGNAL(activated(KUrl)), SLOT(updateURL(KUrl))); + + // TODO make better system + connect(m_map, SIGNAL(giveMeTreeFor(KUrl)), SLOT(updateURL(KUrl))); + connect(m_map, SIGNAL(giveMeTreeFor(KUrl)), SLOT(openUrl(KUrl))); + + connect(m_manager, SIGNAL(completed(Folder*)), SLOT(scanCompleted(Folder*))); + connect(m_manager, SIGNAL(aboutToEmptyCache()), m_map, SLOT(invalidate())); + + QTimer::singleShot(0, this, SLOT(postInit())); +} + +void +Part::postInit() +{ + if (url().isEmpty()) //if url is not empty openUrl() has been called immediately after ctor, which happens + { + m_map->hide(); + showSummary(); + + //FIXME KXMLGUI is b0rked, it should allow us to set this + //BEFORE createGUI is called but it doesn't + stateChanged(QLatin1String( "scan_failed" )); + } +} + +bool +Part::openUrl(const KUrl &u) +{ + + //TODO everyone hates dialogs, instead render the text in big fonts on the Map + //TODO should have an empty KUrl until scan is confirmed successful + //TODO probably should set caption to QString::null while map is unusable + +#define KMSG(s) KMessageBox::information(widget(), s) + + KUrl uri = u; + uri.cleanPath(KUrl::SimplifyDirSeparators); + const QString path = uri.path(KUrl::AddTrailingSlash); + const QByteArray path8bit = QFile::encodeName(path); + const bool isLocal = uri.protocol() == QLatin1String( "file" ); + + if (uri.isEmpty()) + { + //do nothing, chances are the user accidently pressed ENTER + } + else if (!uri.isValid()) + { + KMSG(i18n("The entered URL cannot be parsed; it is invalid.")); + } + else if ((!isLocal && path[0] != QLatin1Char( '/' )) || (isLocal && !QDir::isAbsolutePath(path))) + { + KMSG(i18n("Filelight only accepts absolute paths, eg. /%1", path)); + } + else if (isLocal && access(path8bit, F_OK) != 0) //stat(path, &statbuf) == 0 + { + KMSG(i18n("Folder not found: %1", path)); + } + else if (isLocal && !QDir(path).isReadable()) //access(path8bit, R_OK | X_OK) != 0 doesn't work on win32 + { + KMSG(i18n("Unable to enter: %1\nYou do not have access rights to this location.", path)); + } + else + { + //we don't want to be using the summary screen anymore + if (m_summary != 0) + m_summary->hide(); + + m_stateWidget->show(); + m_layout->addWidget(m_stateWidget); + + return start(uri); + } + + return false; +} + +bool +Part::closeUrl() +{ + if (m_manager->abort()) + statusBar()->showMessage(i18n("Aborting Scan...")); + + m_map->hide(); + m_stateWidget->hide(); + + showSummary(); + + return ReadOnlyPart::closeUrl(); +} + +void +Part::updateURL(const KUrl &u) +{ + //the map has changed internally, update the interface to reflect this + emit m_ext->openUrlNotify(); //must be done first + emit m_ext->setLocationBarUrl(u.prettyUrl()); + + if (m_manager->running()) + m_manager->abort(); + + if (u == url()) + m_manager->emptyCache(); //same as rescan() + + //do this last, or it breaks Konqi location bar + setUrl(u); +} + +void +Part::configFilelight() +{ + QWidget *dialog = new SettingsDialog(widget()); + + connect(dialog, SIGNAL(canvasIsDirty(int)), m_map, SLOT(refresh(int))); + connect(dialog, SIGNAL(mapIsInvalid()), m_manager, SLOT(emptyCache())); + + dialog->show(); //deletes itself +} + +bool +Part::start(const KUrl &url) +{ + if (!m_started) { + connect(m_map, SIGNAL(mouseHover(QString)), statusBar(), SLOT(message(QString))); + connect(m_map, SIGNAL(created(const Folder*)), statusBar(), SLOT(clear())); + m_started = true; + } + + if (m_manager->running()) + m_manager->abort(); + + m_numberOfFiles->setText(QString()); + + if (m_manager->start(url)) { + setUrl(url); + + const QString s = i18n("Scanning: %1", prettyUrl()); + stateChanged(QLatin1String( "scan_started" )); + emit started(0); //as a Part, we have to do this + emit setWindowCaption(s); + statusBar()->showMessage(s); + m_map->hide(); + m_map->invalidate(); //to maintain ui consistency + + + return true; + } + + return false; +} + +void +Part::rescan() +{ + if (m_summary && !m_summary->isHidden()) { + delete m_summary; + m_summary = 0; + showSummary(); + return; + } + + //FIXME we have to empty the cache because otherwise rescan picks up the old tree.. + m_manager->emptyCache(); //causes canvas to invalidate + m_map->hide(); + m_stateWidget->show(); + start(url()); +} + +void +Part::scanCompleted(Folder *tree) +{ + if (tree) { + statusBar()->showMessage(i18n("Scan completed, generating map...")); + + m_stateWidget->hide(); + m_map->show(); + m_map->create(tree); + + stateChanged(QLatin1String( "scan_complete" )); + } + else { + stateChanged(QLatin1String( "scan_failed" )); + emit canceled(i18n("Scan failed: %1", prettyUrl())); + emit setWindowCaption(QString()); + + statusBar()->clearMessage(); + + setUrl(KUrl()); + } +} + +void +Part::mapChanged(const Folder *tree) +{ + //IMPORTANT -> url() has already been set + + emit setWindowCaption(prettyUrl()); + + const int fileCount = tree->children(); + const QString text = ( fileCount == 0 ) ? + i18n("No files.") : + i18np("1 file", "%1 files",fileCount); + + m_numberOfFiles->setText(text); +} + +void +Part::showSummary() +{ + if (m_summary == 0) { + m_summary = new SummaryWidget(widget()); + m_summary->setObjectName(QLatin1String( "summaryWidget" )); + connect(m_summary, SIGNAL(activated(KUrl)), SLOT(openUrl(KUrl))); + m_summary->show(); + m_layout->addWidget(m_summary); + } + else m_summary->show(); +} + +} //namespace Filelight + +#include "part.moc" diff --git a/filelight/src/part/part.h b/filelight/src/part/part.h new file mode 100644 index 00000000..d0a8a2a3 --- /dev/null +++ b/filelight/src/part/part.h @@ -0,0 +1,101 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef FILELIGHTPART_H +#define FILELIGHTPART_H + +#include +#include +#include +#include + +#include + +using KParts::StatusBarExtension; +namespace RadialMap { +class Widget; +} +class Folder; + + +namespace Filelight +{ +class Part; + +class BrowserExtension : public KParts::BrowserExtension +{ +public: + explicit BrowserExtension(Part*); +}; + + +class Part : public KParts::ReadOnlyPart +{ + Q_OBJECT + +public: + Part(QWidget *, QObject *, const QList&); + + virtual bool openFile() { + return false; //pure virtual in base class + } + virtual bool closeUrl(); + + QString prettyUrl() const { + return url().protocol() == QLatin1String( "file" ) ? url().path() : url().prettyUrl(); + } + +public slots: + virtual bool openUrl(const KUrl&); + void configFilelight(); + void rescan(); + +private slots: + void postInit(); + void scanCompleted(Folder*); + void mapChanged(const Folder*); + +private: + KStatusBar *statusBar() { + return m_statusbar->statusBar(); + } + void showSummary(); + + QLayout *m_layout; + QWidget *m_summary; + BrowserExtension *m_ext; + StatusBarExtension *m_statusbar; + RadialMap::Widget *m_map; + QWidget *m_stateWidget; + class ScanManager *m_manager; + QLabel *m_numberOfFiles; + + bool m_started; + +private: + bool start(const KUrl&); + +private slots: + void updateURL(const KUrl &); +}; +} + +#endif diff --git a/filelight/src/part/progressBox.cpp b/filelight/src/part/progressBox.cpp new file mode 100644 index 00000000..3e23ae98 --- /dev/null +++ b/filelight/src/part/progressBox.cpp @@ -0,0 +1,132 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "progressBox.h" + +#include "scan.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + + +ProgressBox::ProgressBox(QWidget *parent, QObject *part, Filelight::ScanManager *m) + : QWidget(parent) + , m_manager(m) +{ + hide(); + + setObjectName(QLatin1String( "ProgressBox" )); + + setFont(KGlobalSettings::fixedFont()); + setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed); + + setText(999999); + setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); + setMinimumSize(200, 200); + + connect(&m_timer, SIGNAL(timeout()), SLOT(report())); + connect(part, SIGNAL(started(KIO::Job*)), SLOT(start())); + connect(part, SIGNAL(completed()), SLOT(stop())); + connect(part, SIGNAL(canceled(QString)), SLOT(halt())); +} + +void +ProgressBox::start() //slot +{ + m_timer.start(50); //20 times per second - very smooth + report(); + show(); +} + +void +ProgressBox::report() //slot +{ + setText(m_manager->files()); + repaint(); +} + +void +ProgressBox::stop() +{ + m_timer.stop(); +} + +void +ProgressBox::halt() +{ + // canceled by stop button + m_timer.stop(); + QTimer::singleShot(2000, this, SLOT(hide())); +} + +void +ProgressBox::setText(int files) +{ + m_text = i18np("%1 File", "%1 Files", files); + m_textWidth = fontMetrics().width(m_text); + m_textHeight = fontMetrics().height(); +} + +static const int pieces = 4; +static const float angleFactor[] = { -0.75, 0.5, 1.0, -0.3 }; +static const int length[] = { 30, 40, 50, 60 }; +static const int angleOffset[] = { 5760, 0, 0, -5760 }; +static const int aLength[] = { 300, 2000, 200, 2000 }; + +void ProgressBox::paintEvent(QPaintEvent*) +{ + KColorScheme view = KColorScheme(QPalette::Active, KColorScheme::Tooltip); + + QPainter paint(this); + paint.setRenderHint(QPainter::Antialiasing); + static int tick = 0; + tick+=16; + + for (int i=0; i +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef PROGRESSBOX_H +#define PROGRESSBOX_H + +#include +#include + +namespace Filelight { +class ScanManager; +} + +class ProgressBox : public QWidget +{ + Q_OBJECT + +public: + ProgressBox(QWidget*, QObject*, Filelight::ScanManager*); + + void setText(int); + +public slots: + void start(); + void report(); + void stop(); + void halt(); + +protected: + void paintEvent(QPaintEvent *event); + +private: + QTimer m_timer; + Filelight::ScanManager* m_manager; + QString m_text; + int m_textWidth; + int m_textHeight; +}; + +#endif diff --git a/filelight/src/part/radialMap/CMakeLists.txt b/filelight/src/part/radialMap/CMakeLists.txt new file mode 100644 index 00000000..21824008 --- /dev/null +++ b/filelight/src/part/radialMap/CMakeLists.txt @@ -0,0 +1,31 @@ +####################################################################### +# Copyright 2008-2009 Martin Sandsmark +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License or (at your option) version 3 or any later version +# accepted by the membership of KDE e.V. (or its successor approved +# by the membership of KDE e.V.), which shall act as a proxy +# defined in Section 14 of version 3 of the license. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +####################################################################### + +set(radialmap_STAT_SRCS + widget.cpp + builder.cpp + map.cpp + widgetEvents.cpp + labels.cpp) + +kde4_add_library(radialmap ${radialmap_STAT_SRCS} ) + +set_target_properties(radialmap PROPERTIES COMPILE_FLAGS -fPIC) + diff --git a/filelight/src/part/radialMap/builder.cpp b/filelight/src/part/radialMap/builder.cpp new file mode 100644 index 00000000..9f6fbe2a --- /dev/null +++ b/filelight/src/part/radialMap/builder.cpp @@ -0,0 +1,161 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "builder.h" + +#include "widget.h" +#include +#include + +#include //locale object +#include + + +//**** REMOVE NEED FOR the +1 with MAX_RING_DEPTH uses +//**** add some angle bounds checking (possibly in Segment ctor? can I delete in a ctor?) +//**** this class is a mess + +RadialMap::Builder::Builder(RadialMap::Map *m, const Folder* const d, bool fast) + : m_map(m) + , m_root(d) + , m_minSize(static_cast((d->size() * 3) / (PI * m->height() - m->MAP_2MARGIN))) + , m_depth(&m->m_visibleDepth) +{ + m_signature = new Chain [*m_depth + 1]; + + if (!fast)//|| *m_depth == 0) //depth 0 is special case usability-wise //**** WHY?! + { + //determine depth rather than use old one + findVisibleDepth(d); //sets m_depth + } + + m_map->setRingBreadth(); + setLimits(m_map->m_ringBreadth); + build(d); + + m_map->m_signature = m_signature; + + delete [] m_limits; +} + + +void +RadialMap::Builder::findVisibleDepth(const Folder* const dir, const unsigned int depth) +{ + //**** because I don't use the same minimumSize criteria as in the visual function + // this can lead to incorrect visual representation + //**** BUT, you can't set those limits until you know m_depth! + + //**** also this function doesn't check to see if anything is actually visible + // it just assumes that when it reaches a new level everything in it is visible + // automatically. This isn't right especially as there might be no files in the + // dir provided to this function! + + static uint stopDepth = 0; + + if (dir == m_root) + { + stopDepth = *m_depth; + *m_depth = 0; + } + + if (*m_depth < depth) *m_depth = depth; + if (*m_depth >= stopDepth) return; + + for (ConstIterator it = dir->constIterator(); it != dir->end(); ++it) + if ((*it)->isFolder() && (*it)->size() > m_minSize) + findVisibleDepth((Folder *)*it, depth + 1); //if no files greater than min size the depth is still recorded +} + +void +RadialMap::Builder::setLimits(const uint &b) //b = breadth? +{ + double size3 = m_root->size() * 3; + double pi2B = PI * 2 * b; + + m_limits = new uint [*m_depth + 1]; //FIXME delete! + + for (unsigned int d = 0; d <= *m_depth; ++d) + m_limits[d] = (uint)(size3 / (double)(pi2B * (d + 1))); //min is angle that gives 3px outer diameter for that depth +} + + +//**** segments currently overlap at edges (i.e. end of first is start of next) +bool +RadialMap::Builder::build(const Folder* const dir, const unsigned int depth, unsigned int a_start, const unsigned int a_end) +{ + //first iteration: dir == m_root + + if (dir->children() == 0) //we do fileCount rather than size to avoid chance of divide by zero later + return false; + + uint hiddenSize = 0, hiddenFileCount = 0; + + for (ConstIterator it = dir->constIterator(); it != dir->end(); ++it) + { + if ((*it)->size() > m_limits[depth]) + { + unsigned int a_len = (unsigned int)(5760 * ((double)(*it)->size() / (double)m_root->size())); + + Segment *s = new Segment(*it, a_start, a_len); + + (m_signature + depth)->append(s); + + if ((*it)->isFolder()) + { + if (depth != *m_depth) + { + //recurse + s->m_hasHiddenChildren = build((Folder*)*it, depth + 1, a_start, a_start + a_len); + } + else s->m_hasHiddenChildren = true; + } + + a_start += a_len; //**** should we add 1? + + } else { + + hiddenSize += (*it)->size(); + + if ((*it)->isFolder()) //**** considered virtual, but dir wouldn't count itself! + hiddenFileCount += static_cast(*it)->children(); //need to add one to count the dir as well + + ++hiddenFileCount; + } + } + + if (hiddenFileCount == dir->children() && !Config::showSmallFiles) + return true; + + else if ((Config::showSmallFiles && hiddenSize > m_limits[depth]) || (depth == 0 && (hiddenSize > dir->size()/8)) /*|| > size() * 0.75*/) + { + //append a segment for unrepresented space - a "fake" segment + + const QString s = i18np("1 file, with an average size of %2", + "%1 files, with an average size of %2", + hiddenFileCount, + KGlobal::locale()->formatByteSize(hiddenSize/hiddenFileCount)); + + (m_signature + depth)->append(new Segment(new File(s.toUtf8(), hiddenSize), a_start, a_end - a_start, true)); + } + + return false; +} diff --git a/filelight/src/part/radialMap/builder.h b/filelight/src/part/radialMap/builder.h new file mode 100644 index 00000000..75059f55 --- /dev/null +++ b/filelight/src/part/radialMap/builder.h @@ -0,0 +1,56 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef BUILDER_H +#define BUILDER_H + +#include "radialMap.h" //Segment, defines + +template class Chain; +class Folder; + + +namespace RadialMap +{ +class Map; + +//temporary class that builds the Map signature + +class Builder +{ +public: + Builder(Map*, const Folder* const, bool fast=false); + +private: + void findVisibleDepth(const Folder* const dir, const uint=0); + void setLimits(const uint&); + bool build(const Folder* const, const uint=0, uint=0, const uint=5760); + + Map *m_map; + const Folder* const m_root; + const uint m_minSize; + uint *m_depth; + Chain *m_signature; + uint *m_limits; +}; +} + +#endif diff --git a/filelight/src/part/radialMap/labels.cpp b/filelight/src/part/radialMap/labels.cpp new file mode 100644 index 00000000..088f8fcb --- /dev/null +++ b/filelight/src/part/radialMap/labels.cpp @@ -0,0 +1,340 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include +#include +#include + +#include "part/Config.h" +#include "part/fileTree.h" +#include "radialMap.h" +#include "sincos.h" +#include "widget.h" + + + +namespace RadialMap +{ +class Label +{ +public: + Label(const RadialMap::Segment *s, int l) : segment(s), lvl(l), angle(segment->start() + (segment->length() / 2)) { } + + bool tooClose(const int otherAngle) const { + return (angle > otherAngle - LABEL_ANGLE_MARGIN && angle < otherAngle + LABEL_ANGLE_MARGIN); + } + + const RadialMap::Segment *segment; + const unsigned int lvl; + const int angle; + + int x1, y1, x2, y2, x3; + int tx, ty, tw, th; + + QString qs; +}; + +int compareAndSortLabels(Label *item1, Label *item2) +{ + //you add 1440 to work round the fact that later you want the circle split vertically + //and as it is you start at 3 o' clock. It's to do with rightPrevY, stops annoying bug + + int angle1 = (item1)->angle + 1440; + int angle2 = (item2)->angle + 1440; + + // Also sort by level + if (angle1 == angle2) + return (item1->lvl > item2->lvl); + + if (angle1 > 5760) angle1 -= 5760; + if (angle2 > 5760) angle2 -= 5760; + + return (angle1 < angle2); + +} + +void +RadialMap::Widget::paintExplodedLabels(QPainter &paint) const +{ + //we are a friend of RadialMap::Map + + QList list; + unsigned int startLevel = 0; + + + //1. Create list of labels sorted in the order they will be rendered + + if (m_focus && m_focus->file() != m_tree) //separate behavior for selected vs unselected segments + { + //don't bother with files + if (m_focus && m_focus->file() && !m_focus->file()->isFolder()) + return; + + //find the range of levels we will be potentially drawing labels for + //startLevel is the level above whatever m_focus is in + for (const Folder *p = (const Folder*)m_focus->file(); p != m_tree; ++startLevel) + p = p->parent(); + + //range=2 means 2 levels to draw labels for + + unsigned int start, end, minAngle; + + start = m_focus->start(); + end = m_focus->end(); //boundary angles + minAngle = int(m_focus->length() * LABEL_MIN_ANGLE_FACTOR); + + +#define segment (*it) +#define ring (m_map.m_signature + i) + + //**** Levels should be on a scale starting with 0 + //**** range is a useless parameter + //**** keep a topblock var which is the lowestLevel OR startLevel for identation purposes + for (unsigned int i = startLevel; i <= m_map.m_visibleDepth; ++i) + for (Iterator it = ring->iterator(); it != ring->end(); ++it) + if (segment->start() >= start && segment->end() <= end) + if (segment->length() > minAngle) { + list.append(new Label(segment, i)); + } + +#undef ring +#undef segment + + } else { + +#define ring m_map.m_signature + + for (Iterator it = ring->iterator(); it != ring->end(); ++it) + if ((*it)->length() > 288) { + list.append(new Label((*it), 0)); + + } + +#undef ring + + } + qSort(list.begin(), list.end(), compareAndSortLabels); + + //2. Check to see if any adjacent labels are too close together + // if so, remove it (the least significant labels, since we sort by level too). + + int pos = 0; + while (pos < list.size() - 1) + { + if (list[pos]->tooClose(list[pos+1]->angle)) + delete list.takeAt(pos+1); + else + ++pos; + } + + //used in next two steps + bool varySizes; + //**** should perhaps use doubles + int *sizes = new int [ m_map.m_visibleDepth + 1 ]; //**** make sizes an array of floats I think instead (or doubles) + + // If the minimum is larger than the default it fucks up further down + if (paint.font().pointSize() < Config::minFontPitch) { + QFont font = paint.font(); + font.setPointSize(Config::minFontPitch); + paint.setFont(font); + } + + QList::iterator it; + + do + { + //3. Calculate font sizes + + { + //determine current range of levels to draw for + uint range = 0; + + for (it = list.begin(); it != list.end(); ++it) + { + uint lvl = (*it)->lvl; + if (lvl > range) + range = lvl; + + //**** better way would just be to assign if nothing is range + } + + range -= startLevel; //range 0 means 1 level of labels + + varySizes = Config::varyLabelFontSizes && (range != 0); + + if (varySizes) + { + //create an array of font sizes for various levels + //will exceed normal font pitch automatically if necessary, but not minPitch + //**** this needs to be checked lots + + //**** what if this is negative (min size gtr than default size) + uint step = (paint.font().pointSize() - Config::minFontPitch) / range; + if (step == 0) + step = 1; + + for (uint x = range + startLevel, y = Config::minFontPitch; x >= startLevel; y += step, --x) + sizes[x] = y; + } + } + + //4. determine label co-ordinates + + int x1, y1, x2, y2, x3, tx, ty; //coords + double sinra, cosra, ra; //angles + + int cx = m_map.width() / 2 + m_offset.x(); //centre relative to canvas + int cy = m_map.height() / 2 + m_offset.y(); + + int spacer, preSpacer = int(m_map.m_ringBreadth * 0.5) + m_map.m_innerRadius; + int fullStrutLength = (m_map.width() - m_map.MAP_2MARGIN) / 2 + LABEL_MAP_SPACER; //full length of a strut from map center + + int prevLeftY = 0; + int prevRightY = height(); + + bool rightSide; + + QFont font; + + for (it = list.begin(); it != list.end(); ++it) + { + //** bear in mind that text is drawn with QPoint param as BOTTOM left corner of text box + QString string = (*it)->segment->file()->name(); + if (varySizes) + font.setPointSize(sizes[(*it)->lvl]); + QFontMetrics fontMetrics(font); + int fontHeight = fontMetrics.height(); //used to ensure label texts don't overlap + int lineSpacing = fontHeight / 4; + + fontHeight += LABEL_TEXT_VMARGIN; + + rightSide = ((*it)->angle < 1440 || (*it)->angle > 4320); + + ra = M_PI/2880 * (*it)->angle; //convert to radians + sincos(ra, &sinra, &cosra); + + + spacer = preSpacer + m_map.m_ringBreadth * (*it)->lvl; + + x1 = cx + (int)(cosra * spacer); + y1 = cy - (int)(sinra * spacer); + y2 = y1 - (int)(sinra * (fullStrutLength - spacer)); + + if (rightSide) { //righthand side, going upwards + if (y2 > prevRightY /*- fmh*/) //then it is too low, needs to be drawn higher + y2 = prevRightY /*- fmh*/; + } + else //lefthand side, going downwards + if (y2 < prevLeftY/* + fmh*/) //then we're too high, need to be drawn lower + y2 = prevLeftY /*+ fmh*/; + + x2 = x1 - int(double(y2 - y1) / tan(ra)); + ty = y2 + lineSpacing; + + + if (rightSide) { + if (x2 > width() || ty < fontHeight || x2 < x1) { + //skip this strut + //**** don't duplicate this code + list.erase(it); //will delete the label and set it to list.current() which _should_ be the next ptr + break; + } + + prevRightY = ty - fontHeight - lineSpacing; //must be after above's "continue" + + string = fontMetrics.elidedText(string, Qt::ElideMiddle, width() - x2); + + x3 = width() - fontMetrics.width(string) + - LABEL_HMARGIN //outer margin + - LABEL_TEXT_HMARGIN //margin between strut and text + //- ((*it)->lvl - startLevel) * LABEL_HMARGIN; //indentation + ; + if (x3 < x2) x3 = x2; + tx = x3 + LABEL_TEXT_HMARGIN; + + } else { + + if (x2 < 0 || ty > height() || x2 > x1) + { + //skip this strut + list.erase(it); //will delete the label and set it to list.current() which _should_ be the next ptr + break; + } + + prevLeftY = ty + fontHeight - lineSpacing; + + string = fontMetrics.elidedText(string, Qt::ElideMiddle, x2); + + //**** needs a little tweaking: + + tx = fontMetrics.width(string) + LABEL_HMARGIN/* + ((*it)->lvl - startLevel) * LABEL_HMARGIN*/; + if (tx > x2) { //text is too long + tx = LABEL_HMARGIN + x2 - tx; //some text will be lost from sight + x3 = x2; //no text margin (right side of text here) + } else { + x3 = tx + LABEL_TEXT_HMARGIN; + tx = LABEL_HMARGIN /*+ ((*it)->lvl - startLevel) * LABEL_HMARGIN*/; + } + } + + (*it)->x1 = x1; + (*it)->y1 = y1; + (*it)->x2 = x2; + (*it)->y2 = y2; + (*it)->x3 = x3; + (*it)->tx = tx; + (*it)->ty = ty; + (*it)->qs = string; + } + + //if an element is deleted at this stage, we need to do this whole + //iteration again, thus the following loop + //**** in rare case that deleted label was last label in top level + // and last in labelList too, this will not work as expected (not critical) + + } while (it != list.end()); + + + //5. Render labels + + foreach (Label *label, list) { + if (varySizes) { + //**** how much overhead in making new QFont each time? + // (implicate sharing remember) + QFont font = paint.font(); + font.setPointSize(sizes[label->lvl]); + paint.setFont(font); + } + + paint.drawEllipse(label->x1 - 3, label->y1 - 3, 6, 6); //**** CPU intensive! better to use a pixmap + paint.drawLine(label->x1, label->y1, label->x2, label->y2); + paint.drawLine(label->x2, label->y2, label->x3, label->y2); + + paint.drawText(label->tx, label->ty, label->qs); + } + + foreach(Label *label, list) { + delete label; + } + delete [] sizes; +} +} + diff --git a/filelight/src/part/radialMap/map.cpp b/filelight/src/part/radialMap/map.cpp new file mode 100644 index 00000000..9661a6d0 --- /dev/null +++ b/filelight/src/part/radialMap/map.cpp @@ -0,0 +1,381 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include //make() +#include //make() & paint() +#include //ctor +#include //ctor +#include + +#include //make() +#include +#include //kdeColours + +#include "builder.h" +#include "part/Config.h" +#include "part/fileTree.h" +#define SINCOS_H_IMPLEMENTATION (1) +#include "sincos.h" +#include "widget.h" + +RadialMap::Map::Map(bool summary) + : m_signature(NULL) + , m_visibleDepth(DEFAULT_RING_DEPTH) + , m_ringBreadth(MIN_RING_BREADTH) + , m_innerRadius(0) + , m_summary(summary) +{ + + //FIXME this is all broken. No longer is a maximum depth! + const int fmh = QFontMetrics(QFont()).height(); + const int fmhD4 = fmh / 4; + MAP_2MARGIN = 2 * (fmh - (fmhD4 - LABEL_MAP_SPACER)); //margin is dependent on fitting in labels at top and bottom +} + +RadialMap::Map::~Map() +{ + delete [] m_signature; +} + +void RadialMap::Map::invalidate() +{ + delete [] m_signature; + m_signature = NULL; + + m_visibleDepth = Config::defaultRingDepth; +} + +void RadialMap::Map::make(const Folder *tree, bool refresh) +{ + //slow operation so set the wait cursor + QApplication::setOverrideCursor(Qt::WaitCursor); + + { + //build a signature of visible components + delete [] m_signature; + Builder builder(this, tree, refresh); + } + + //colour the segments + colorise(); + + m_centerText = tree->humanReadableSize(); + + //paint the pixmap + paint(); + + QApplication::restoreOverrideCursor(); +} + +void RadialMap::Map::setRingBreadth() +{ + //FIXME called too many times on creation + + m_ringBreadth = (height() - MAP_2MARGIN) / (2 * m_visibleDepth + 4); + + if (m_ringBreadth < MIN_RING_BREADTH) + m_ringBreadth = MIN_RING_BREADTH; + + else if (m_ringBreadth > MAX_RING_BREADTH) + m_ringBreadth = MAX_RING_BREADTH; +} + +bool RadialMap::Map::resize(const QRect &rect) +{ + //there's a MAP_2MARGIN border + +#define mw width() +#define mh height() +#define cw rect.width() +#define ch rect.height() + + if (cw < mw || ch < mh || (cw > mw && ch > mh)) + { + uint size = ((cw < ch) ? cw : ch) - MAP_2MARGIN; + + //this also causes uneven sizes to always resize when resizing but map is small in that dimension + //size -= size % 2; //even sizes mean less staggered non-antialiased resizing + + { + const uint minSize = MIN_RING_BREADTH * 2 * (m_visibleDepth + 2); + + if (size < minSize) + size = minSize; + + //this QRect is used by paint() + m_rect.setRect(0,0,size,size); + } + m_pixmap = QPixmap(m_rect.size()); + + //resize the pixmap + size += MAP_2MARGIN; + + if (m_signature != NULL) + { + setRingBreadth(); + paint(); + } + + return true; + } + +#undef mw +#undef mh +#undef cw +#undef ch + + return false; +} + +void RadialMap::Map::colorise() +{ + QColor cp, cb; + double darkness = 1; + double contrast = (double)Config::contrast / (double)100; + int h, s1, s2, v1, v2; + + QColor kdeColour[2] = { KGlobalSettings::inactiveTitleColor(), KGlobalSettings::activeTitleColor() }; + + double deltaRed = (double)(kdeColour[0].red() - kdeColour[1].red()) / 2880; //2880 for semicircle + double deltaGreen = (double)(kdeColour[0].green() - kdeColour[1].green()) / 2880; + double deltaBlue = (double)(kdeColour[0].blue() - kdeColour[1].blue()) / 2880; + + for (uint i = 0; i <= m_visibleDepth; ++i, darkness += 0.04) + { + for (Iterator it = m_signature[i].iterator(); it != m_signature[i].end(); ++it) + { + if (m_summary){ // Summary view has its own colors, special cased. + if ((*it)->file()->name() == QLatin1String("Used")) { + cb = QApplication::palette().highlight().color(); + cb.getHsv(&h, &s1, &v1); + + if (s1 > 80) + s1 = 80; + + v2 = v1 - int(contrast * v1); + s2 = s1 + int(contrast * (255 - s1)); + + cb.setHsv(h, s1, v1); + cp.setHsv(h, s2, v2); + } + else + { + cp = Qt::gray; + cb = Qt::white; + } + + (*it)->setPalette(cp, cb); + } + else + { + switch (Config::scheme) + { + case Filelight::KDE: + { + //gradient will work by figuring out rgb delta values for 360 degrees + //then each component is angle*delta + + int a = (*it)->start(); + + if (a > 2880) a = 2880 - (a - 2880); + + h = (int)(deltaRed * a) + kdeColour[1].red(); + s1 = (int)(deltaGreen * a) + kdeColour[1].green(); + v1 = (int)(deltaBlue * a) + kdeColour[1].blue(); + + cb.setRgb(h, s1, v1); + cb.getHsv(&h, &s1, &v1); + + break; + } + + case Filelight::HighContrast: + cp.setHsv(0, 0, 0); //values of h, s and v are irrelevant + cb.setHsv(180, 0, int(255.0 * contrast)); + (*it)->setPalette(cp, cb); + continue; + + default: + h = int((*it)->start() / 16); + s1 = 160; + v1 = (int)(255.0 / darkness); //****doing this more often than once seems daft! + } + + v2 = v1 - int(contrast * v1); + s2 = s1 + int(contrast * (255 - s1)); + + if (s1 < 80) s1 = 80; //can fall too low and makes contrast between the files hard to discern + + if ((*it)->isFake()) //multi-file + { + cb.setHsv(h, s2, (v2 < 90) ? 90 : v2); //too dark if < 100 + cp.setHsv(h, 17, v1); + } + else if (!(*it)->file()->isFolder()) //file + { + cb.setHsv(h, 17, v1); + cp.setHsv(h, 17, v2); + } + else //folder + { + cb.setHsv(h, s1, v1); //v was 225 + cp.setHsv(h, s2, v2); //v was 225 - delta + } + + (*it)->setPalette(cp, cb); + + //TODO: + //**** may be better to store KDE colours as H and S and vary V as others + //**** perhaps make saturation difference for s2 dependent on contrast too + //**** fake segments don't work with highContrast + //**** may work better with cp = cb rather than Qt::white + //**** you have to ensure the grey of files is sufficient, currently it works only with rainbow (perhaps use contrast there too) + //**** change v1,v2 to vp, vb etc. + //**** using percentages is not strictly correct as the eye doesn't work like that + //**** darkness factor is not done for kde_colour scheme, and also value for files is incorrect really for files in this scheme as it is not set like rainbow one is + } + } + } +} + +void RadialMap::Map::paint(bool antialias) +{ + KColorScheme scheme(QPalette::Active, KColorScheme::View); + + QPainter paint; + QRect rect = m_rect; + + rect.adjust(5, 5, -5, -5); + m_pixmap.fill(scheme.background().color()); + + //m_rect.moveRight(1); // Uncommenting this breaks repainting when recreating map from cache + + + //**** best option you can think of is to make the circles slightly less perfect, + // ** i.e. slightly eliptic when resizing inbetween + + if (m_pixmap.isNull()) + return; + + if (!paint.begin(&m_pixmap)) { + kWarning() << "Failed to initialize painting, returning..."; + return; + } + + if (antialias && Config::antialias) { + paint.translate(0.7, 0.7); + paint.setRenderHint(QPainter::Antialiasing); + } + + int step = m_ringBreadth; + int excess = -1; + + //do intelligent distribution of excess to prevent nasty resizing + if (m_ringBreadth != MAX_RING_BREADTH && m_ringBreadth != MIN_RING_BREADTH) { + excess = rect.width() % m_ringBreadth; + ++step; + } + + + for (int x = m_visibleDepth; x >= 0; --x) + { + int width = rect.width() / 2; + //clever geometric trick to find largest angle that will give biggest arrow head + uint a_max = int(acos((double)width / double((width + 5))) * (180*16 / M_PI)); + + for (ConstIterator it = m_signature[x].constIterator(); it != m_signature[x].end(); ++it) + { + //draw the pie segments, most of this code is concerned with drawing the little + //arrows on the ends of segments when they have hidden files + + paint.setPen((*it)->pen()); + + if ((*it)->hasHiddenChildren()) + { + //draw arrow head to indicate undisplayed files/directories + QPolygon pts(3); + QPoint pos, cpos = rect.center(); + uint a[3] = { (*it)->start(), (*it)->length(), 0 }; + + a[2] = a[0] + (a[1] / 2); //assign to halfway between + if (a[1] > a_max) + { + a[1] = a_max; + a[0] = a[2] - a_max / 2; + } + + a[1] += a[0]; + + for (int i = 0, radius = width; i < 3; ++i) + { + double ra = M_PI/(180*16) * a[i], sinra, cosra; + + if (i == 2) + radius += 5; + sincos(ra, &sinra, &cosra); + pos.rx() = cpos.x() + static_cast(cosra * radius); + pos.ry() = cpos.y() - static_cast(sinra * radius); + pts.setPoint(i, pos); + } + + paint.setBrush((*it)->pen()); + paint.drawPolygon(pts); + } + + paint.setBrush((*it)->brush()); + paint.drawPie(rect, (*it)->start(), (*it)->length()); + + if ((*it)->hasHiddenChildren()) + { + //**** code is bloated! + paint.save(); + QPen pen = paint.pen(); + int width = 2; + pen.setWidth(width); + paint.setPen(pen); + QRect rect2 = rect; + width /= 2; + rect2.adjust(width, width, -width, -width); + paint.drawArc(rect2, (*it)->start(), (*it)->length()); + paint.restore(); + } + } + + if (excess >= 0) { //excess allows us to resize more smoothly (still crud tho) + if (excess < 2) //only decrease rect by more if even number of excesses left + --step; + excess -= 2; + } + + rect.adjust(step, step, -step, -step); + } + + // if(excess > 0) rect.addCoords(excess, excess, 0, 0); //ugly + + paint.setPen(scheme.foreground().color()); + paint.setBrush(scheme.background().color()); + paint.drawEllipse(rect); + paint.drawText(rect, Qt::AlignCenter, m_centerText); + + m_innerRadius = rect.width() / 2; //rect.width should be multiple of 2 + + paint.end(); +} diff --git a/filelight/src/part/radialMap/map.h b/filelight/src/part/radialMap/map.h new file mode 100644 index 00000000..c3828edd --- /dev/null +++ b/filelight/src/part/radialMap/map.h @@ -0,0 +1,82 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef MAP_H +#define MAP_H + +#include "part/fileTree.h" + +#include + +#include +#include +#include + +namespace RadialMap { +class Segment; + +class Map +{ +public: + explicit Map(bool summary); + ~Map(); + + void make(const Folder *, bool = false); + bool resize(const QRect&); + + bool isNull() const { + return (m_signature == 0); + } + void invalidate(); + + int height() const { + return m_rect.height(); + } + int width() const { + return m_rect.width(); + } + QPixmap pixmap() const { + return m_pixmap; + } + + friend class Builder; + friend class Widget; + +private: + void paint(bool antialias = true); + void colorise(); + void setRingBreadth(); + + Chain *m_signature; + + QRect m_rect; + uint m_visibleDepth; ///visible level depth of system + QPixmap m_pixmap; + uint m_ringBreadth; ///ring breadth + uint m_innerRadius; ///radius of inner circle + QString m_centerText; + bool m_summary; + + uint MAP_2MARGIN; +}; +} + +#endif diff --git a/filelight/src/part/radialMap/radialMap.h b/filelight/src/part/radialMap/radialMap.h new file mode 100644 index 00000000..f6ce159d --- /dev/null +++ b/filelight/src/part/radialMap/radialMap.h @@ -0,0 +1,110 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef RADIALMAP_H +#define RADIALMAP_H + +#include + +class File; + +namespace RadialMap +{ +class Segment //all angles are in 16ths of degrees +{ +public: + Segment(const File *f, uint s, uint l, bool isFake = false) + : m_angleStart(s) + , m_angleSegment(l) + , m_file(f) + , m_hasHiddenChildren(false) + , m_fake(isFake) {} + ~Segment(); + + uint start() const { + return m_angleStart; + } + uint length() const { + return m_angleSegment; + } + uint end() const { + return m_angleStart + m_angleSegment; + } + const File *file() const { + return m_file; + } + const QColor& pen() const { + return m_pen; + } + const QColor& brush() const { + return m_brush; + } + + bool isFake() const { + return m_fake; + } + bool hasHiddenChildren() const { + return m_hasHiddenChildren; + } + + bool intersects(uint a) const { + return ((a >= start()) && (a < end())); + } + + friend class Map; + friend class Builder; + +private: + void setPalette(const QColor &p, const QColor &b) { + m_pen = p; + m_brush = b; + } + + const uint m_angleStart, m_angleSegment; + const File* const m_file; + QColor m_pen, m_brush; + bool m_hasHiddenChildren; + const bool m_fake; +}; +} + + +#ifndef PI +#define PI 3.141592653589793 +#endif +#ifndef M_PI +#define M_PI 3.14159265358979323846264338327 +#endif + +#define MIN_RING_BREADTH 20 +#define MAX_RING_BREADTH 60 +#define DEFAULT_RING_DEPTH 4 //first level = 0 +#define MIN_RING_DEPTH 0 + +#define LABEL_MAP_SPACER 7 +#define LABEL_HMARGIN 10 +#define LABEL_TEXT_HMARGIN 5 +#define LABEL_TEXT_VMARGIN 0 +#define LABEL_ANGLE_MARGIN 32 +#define LABEL_MIN_ANGLE_FACTOR 0.05 +#define LABEL_MAX_CHARS 30 + +#endif diff --git a/filelight/src/part/radialMap/sincos.h b/filelight/src/part/radialMap/sincos.h new file mode 100644 index 00000000..2fe716db --- /dev/null +++ b/filelight/src/part/radialMap/sincos.h @@ -0,0 +1,45 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef SINCOS_H +#define SINCOS_H + +#include + +#if !defined(__GLIBC__) || (__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 1) + +#include + +void +sincos(double angleRadians, double *Sin, double *Cos); + +#ifdef SINCOS_H_IMPLEMENTATION +void +sincos(double angleRadians, double *Sin, double *Cos) +{ + *Sin = qSin(angleRadians); + *Cos = qCos(angleRadians); +} +#endif + +#endif + +#endif diff --git a/filelight/src/part/radialMap/widget.cpp b/filelight/src/part/radialMap/widget.cpp new file mode 100644 index 00000000..0219758a --- /dev/null +++ b/filelight/src/part/radialMap/widget.cpp @@ -0,0 +1,216 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "widget.h" + +#include "part/Config.h" +#include "part/fileTree.h" +#include "radialMap.h" //constants +#include "map.h" + +#include //ctor +#include + +#include //sendEvent +#include //ctor - finding cursor size +#include //slotPostMouseEvent() +#include //member +#include + + +RadialMap::Widget::Widget(QWidget *parent, bool isSummary) + : QWidget(parent) + , m_tree(0) + , m_focus(0) + , m_map(isSummary) + , m_rootSegment(0) //TODO we don't delete it, *shrug* + , m_isSummary(isSummary) + , m_toBeDeleted(0) +{ + setAcceptDrops(true); + setMinimumSize(350, 250); + + connect(this, SIGNAL(created(const Folder*)), SLOT(sendFakeMouseEvent())); + connect(this, SIGNAL(created(const Folder*)), SLOT(update())); + connect(&m_timer, SIGNAL(timeout()), SLOT(resizeTimeout())); +} + +RadialMap::Widget::~Widget() +{ + delete m_rootSegment; +} + + +QString +RadialMap::Widget::path() const +{ + return m_tree->fullPath(); +} + +KUrl +RadialMap::Widget::url(File const * const file) const +{ + return KUrl(file ? file->fullPath() : m_tree->fullPath()); +} + +void +RadialMap::Widget::invalidate() +{ + if (isValid()) + { + //**** have to check that only way to invalidate is this function frankly + //**** otherwise you may get bugs.. + + //disable mouse tracking + setMouseTracking(false); + + //ensure this class won't think we have a map still + m_tree = 0; + m_focus = 0; + + delete m_rootSegment; + m_rootSegment = 0; + + //FIXME move this disablement thing no? + // it is confusing in other areas, like the whole createFromCache() thing + m_map.invalidate(); + update(); + + //tell rest of Filelight + emit invalidated(url()); + } +} + +void +RadialMap::Widget::create(const Folder *tree) +{ + //it is not the responsibility of create() to invalidate first + //skip invalidation at your own risk + + //FIXME make it the responsibility of create to invalidate first + + if (tree) + { + m_focus = 0; + //generate the filemap image + m_map.make(tree); + + //this is the inner circle in the center + m_rootSegment = new Segment(tree, 0, 16*360); + + setMouseTracking(true); + } + + m_tree = tree; + + //tell rest of Filelight + emit created(tree); +} + +void +RadialMap::Widget::createFromCache(const Folder *tree) +{ + //no scan was necessary, use cached tree, however we MUST still emit invalidate + invalidate(); + create(tree); +} + +void +RadialMap::Widget::sendFakeMouseEvent() //slot +{ + QMouseEvent me(QEvent::MouseMove, mapFromGlobal(QCursor::pos()), Qt::NoButton, Qt::NoButton, Qt::NoModifier); + QApplication::sendEvent(this, &me); +} + +void +RadialMap::Widget::resizeTimeout() //slot +{ + // the segments are about to erased! + // this was a horrid bug, and proves the OO programming should be obeyed always! + m_focus = 0; + if (m_tree) + m_map.make(m_tree, true); + update(); +} + +void +RadialMap::Widget::refresh(int filth) +{ + //TODO consider a more direct connection + + if (!m_map.isNull()) + { + switch (filth) + { + case 1: + m_focus=0; + m_map.make(m_tree, true); //true means refresh only + break; + + case 2: + m_map.paint(true); //antialiased painting + break; + + case 3: + m_map.colorise(); //FALL THROUGH! + case 4: + m_map.paint(); + + default: + break; + } + + update(); + } +} + +void +RadialMap::Widget::zoomIn() //slot +{ + if (m_map.m_visibleDepth > MIN_RING_DEPTH) + { + --m_map.m_visibleDepth; + m_focus = 0; + m_map.make(m_tree); + Config::defaultRingDepth = m_map.m_visibleDepth; + update(); + } +} + +void +RadialMap::Widget::zoomOut() //slot +{ + m_focus = 0; + ++m_map.m_visibleDepth; + m_map.make(m_tree); + if (m_map.m_visibleDepth > Config::defaultRingDepth) + Config::defaultRingDepth = m_map.m_visibleDepth; + update(); +} + + +RadialMap::Segment::~Segment() +{ + if (isFake()) + delete m_file; //created by us in Builder::build() +} + +#include "widget.moc" diff --git a/filelight/src/part/radialMap/widget.h b/filelight/src/part/radialMap/widget.h new file mode 100644 index 00000000..04af6d7d --- /dev/null +++ b/filelight/src/part/radialMap/widget.h @@ -0,0 +1,123 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef WIDGET_H +#define WIDGET_H + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "map.h" + +template class Chain; +class Folder; +class File; +namespace KIO { +class Job; +} +class KUrl; + +namespace RadialMap +{ +class Segment; + +class Widget : public QWidget +{ + Q_OBJECT + +public: + explicit Widget(QWidget* = 0, bool = false); + virtual ~Widget(); + QString path() const; + KUrl url(File const * const = 0) const; + + bool isValid() const { + return m_tree != 0; + } + + bool isSummary() const { + return m_isSummary; + } + + friend class Label; //FIXME badness + +public slots: + void zoomIn(); + void zoomOut(); + void create(const Folder*); + void invalidate(); + void refresh(int); + +private slots: + void resizeTimeout(); + void sendFakeMouseEvent(); + void deleteJobFinished(KJob*); + void createFromCache(const Folder*); + +signals: + void activated(const KUrl&); + void invalidated(const KUrl&); + void created(const Folder*); + void mouseHover(const QString&); + void giveMeTreeFor(const KUrl&); + +protected: + virtual void changeEvent(QEvent*); + virtual void dragEnterEvent(QDragEnterEvent*); + virtual void dropEvent(QDropEvent*); + virtual void mouseMoveEvent(QMouseEvent*); + virtual void mousePressEvent(QMouseEvent*); + virtual void paintEvent(QPaintEvent*); + virtual void resizeEvent(QResizeEvent*); + virtual void enterEvent(QEvent*); + +protected: + const Segment *segmentAt(QPoint&) const; //FIXME const reference for a library others can use + const Segment *rootSegment() const { + return m_rootSegment; ///never == 0 + } + const Segment *focusSegment() const { + return m_focus; ///0 == nothing in focus + } + +private: + void paintExplodedLabels(QPainter&) const; + + const Folder *m_tree; + const Segment *m_focus; + QPoint m_offset; + QTimer m_timer; + Map m_map; + Segment *m_rootSegment; + const bool m_isSummary; + const Segment *m_toBeDeleted; +}; +} + +#endif diff --git a/filelight/src/part/radialMap/widgetEvents.cpp b/filelight/src/part/radialMap/widgetEvents.cpp new file mode 100644 index 00000000..46fe5e2e --- /dev/null +++ b/filelight/src/part/radialMap/widgetEvents.cpp @@ -0,0 +1,329 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "part/fileTree.h" +#include "part/Config.h" +#include "radialMap.h" //class Segment +#include "widget.h" + +#include //::mouseMoveEvent() +#include +#include //::mousePressEvent() +#include +#include //::mousePressEvent() +#include +#include +#include +#include //::mousePressEvent() +#include //::mousePressEvent() +#include //::mousePressEvent() +#include +#include + +#include //QApplication::setOverrideCursor() +#include +#include +#include //::resizeEvent() +#include +#include +#include +#include +#include +#include + +#include //::segmentAt() + +void RadialMap::Widget::resizeEvent(QResizeEvent*) +{ + if (m_map.resize(rect())) + m_timer.setSingleShot(true); + m_timer.start(500); //will cause signature to rebuild for new size + + //always do these as they need to be initialised on creation + m_offset.rx() = (width() - m_map.width()) / 2; + m_offset.ry() = (height() - m_map.height()) / 2; +} + +void RadialMap::Widget::paintEvent(QPaintEvent*) +{ + QPainter paint; + paint.begin(this); + + if (!m_map.isNull()) + paint.drawPixmap(m_offset, m_map.pixmap()); + else + { + paint.drawText(rect(), 0, i18nc("We messed up, the user needs to initiate a rescan.", "Internal representation is invalid,\nplease rescan.")); + return; + } + + //exploded labels + if (!m_map.isNull() && !m_timer.isActive()) + { + if (Config::antialias) { + paint.setRenderHint(QPainter::Antialiasing); + //make lines appear on pixel boundaries + paint.translate(0.5, 0.5); + } + paintExplodedLabels(paint); + } +} + +const RadialMap::Segment* RadialMap::Widget::segmentAt(QPoint &e) const +{ + //determine which segment QPoint e is above + + e -= m_offset; + + if (!m_map.m_signature) + return 0; + + if (e.x() <= m_map.width() && e.y() <= m_map.height()) + { + //transform to cartesian coords + e.rx() -= m_map.width() / 2; //should be an int + e.ry() = m_map.height() / 2 - e.y(); + + double length = hypot(e.x(), e.y()); + + if (length >= m_map.m_innerRadius) //not hovering over inner circle + { + uint depth = ((int)length - m_map.m_innerRadius) / m_map.m_ringBreadth; + + if (depth <= m_map.m_visibleDepth) //**** do earlier since you can //** check not outside of range + { + //vector calculation, reduces to simple trigonometry + //cos angle = (aibi + ajbj) / albl + //ai = x, bi=1, aj=y, bj=0 + //cos angle = x / (length) + + uint a = (uint)(acos((double)e.x() / length) * 916.736); //916.7324722 = #radians in circle * 16 + + //acos only understands 0-180 degrees + if (e.y() < 0) a = 5760 - a; + +#define ring (m_map.m_signature + depth) + for (ConstIterator it = ring->constIterator(); it != ring->end(); ++it) + if ((*it)->intersects(a)) + return *it; +#undef ring + } + } + else return m_rootSegment; //hovering over inner circle + } + + return 0; +} + +void RadialMap::Widget::mouseMoveEvent(QMouseEvent *e) +{ + //set m_focus to what we hover over, update UI if it's a new segment + + Segment const * const oldFocus = m_focus; + QPoint p = e->pos(); + + m_focus = segmentAt(p); //NOTE p is passed by non-const reference + + if (m_focus) + { + if (m_focus != oldFocus) //if not same as last time + { + setCursor(Qt::PointingHandCursor); + + QString string = m_focus->file()->fullPath(m_tree) + + QLatin1Char('\n') + + m_focus->file()->humanReadableSize() + + QLatin1Char('\n'); + + if (m_focus->file()->isFolder()) { + int files = static_cast(m_focus->file())->children(); + const uint percent = uint((100 * files) / (double)m_tree->children()); + string += i18np("File: %1", "Files: %1", files); + + if (percent > 0) string += QString(QLatin1String( " (%1%)" )).arg(KGlobal::locale()->formatNumber(percent, 0)); + } + + const KUrl url = Widget::url(m_focus->file()); + if (m_focus == m_rootSegment && url != url.upUrl()) { + string += i18n("\nClick to go up to parent directory"); + } + + QToolTip::showText(e->globalPos(), string, this); + + emit mouseHover(m_focus->file()->fullPath()); + update(); + } + } + else if (oldFocus && oldFocus->file() != m_tree) + { + unsetCursor(); + update(); + + emit mouseHover(QString()); + } +} + +void RadialMap::Widget::enterEvent(QEvent *) +{ + if (!m_focus) return; + + setCursor(Qt::PointingHandCursor); + emit mouseHover(m_focus->file()->fullPath()); + update(); +} + +void RadialMap::Widget::mousePressEvent(QMouseEvent *e) +{ + if (!isEnabled()) + return; + + //m_focus is set correctly (I've been strict, I assure you it is correct!) + + if (m_focus && !m_focus->isFake()) + { + const KUrl url = Widget::url(m_focus->file()); + const bool isDir = m_focus->file()->isFolder(); + + // Actions in the right click menu + QAction* openFileManager = 0; + QAction* openTerminal = 0; + QAction* centerMap = 0; + QAction* openFile = 0; + QAction* copyClipboard = 0; + QAction* deleteItem = 0; + + if (e->button() == Qt::RightButton) + { + KMenu popup; + popup.addTitle(m_focus->file()->fullPath(m_tree)); + + if (isDir) { + openFileManager = popup.addAction(KIcon(QLatin1String( "system-file-manager" )), i18n("Open &File Manager Here")); + + if (url.protocol() == QLatin1String("file" )) + openTerminal = popup.addAction(KIcon(QLatin1String( "utilities-terminal" )), i18n("Open &Terminal Here")); + + if (m_focus->file() != m_tree) { + popup.addSeparator(); + centerMap = popup.addAction(KIcon(QLatin1String( "zoom-in" )), i18n("&Center Map Here")); + } + } + else + openFile = popup.addAction(KIcon(QLatin1String( "document-open" )), i18nc("Scan/open the path of the selected element", "&Open")); + + popup.addSeparator(); + copyClipboard = popup.addAction(KIcon(QLatin1String( "edit-copy" )), i18n("&Copy to clipboard")); + + if (m_focus->file() != m_tree) { + popup.addSeparator(); + deleteItem = popup.addAction(KIcon(QLatin1String( "edit-delete" )), i18n("&Delete")); + } + + QAction* clicked = popup.exec(e->globalPos(), 0); + + if (openFileManager && clicked == openFileManager) { + KRun::runUrl(url.url(),QLatin1String( "inode/directory" ), this); + } else if (openTerminal && clicked == openTerminal) { + KToolInvocation::invokeTerminal(QString(),url.path()); + } else if (centerMap && clicked == centerMap) { + goto section_two; + } else if (openFile && clicked == openFile) { + goto section_two; + } else if (clicked == copyClipboard) { + QMimeData* mimedata = new QMimeData(); + url.populateMimeData(mimedata); + QApplication::clipboard()->setMimeData(mimedata , QClipboard::Clipboard); + } else if (clicked == deleteItem && m_focus->file() != m_tree) { + m_toBeDeleted = m_focus; + const KUrl url = Widget::url(m_toBeDeleted->file()); + const QString message = m_toBeDeleted->file()->isFolder() + ? i18n("The folder at '%1' will be recursively and permanently deleted.", url.prettyUrl()) + : i18n("'%1' will be permanently deleted.", url.prettyUrl()); + const int userIntention = KMessageBox::warningContinueCancel( + this, message, + QString(), KGuiItem(i18n("&Delete"), QLatin1String( "edit-delete" ))); + + if (userIntention == KMessageBox::Continue) { + KIO::Job *job = KIO::del(url); + job->ui()->setWindow(this); + connect(job, SIGNAL(finished(KJob*)), this, SLOT(deleteJobFinished(KJob*))); + QApplication::setOverrideCursor(Qt::BusyCursor); + setEnabled(false); + } + } else { + //ensure m_focus is set for new mouse position + sendFakeMouseEvent(); + } + } + else { // not right mouse button + +section_two: + const QRect rect(e->x() - 20, e->y() - 20, 40, 40); + + if (!isDir || e->button() == Qt::MidButton) { + // KIconEffect::visualActivate(this, rect); // TODO: recreate this + new KRun(url, this, true); //FIXME see above + } + else if (m_focus->file() != m_tree) { // is left click + // KIconEffect::visualActivate(this, rect); // TODO: recreate this + emit activated(url); //activate first, this will cause UI to prepare itself + createFromCache((Folder *)m_focus->file()); + } + else if (url.upUrl() != url) + emit giveMeTreeFor(url.upUrl()); + } + } +} + +void RadialMap::Widget::deleteJobFinished(KJob *job) +{ + QApplication::restoreOverrideCursor(); + setEnabled(true); + if (!job->error() && m_toBeDeleted) { + m_toBeDeleted->file()->parent()->remove(m_toBeDeleted->file()); + delete m_toBeDeleted->file(); + m_toBeDeleted = 0; + m_focus = 0; + m_map.make(m_tree, true); + repaint(); + } else + KMessageBox::error(this, job->errorString(), i18n("Error while deleting")); +} + +void RadialMap::Widget::dropEvent(QDropEvent *e) +{ + KUrl::List uriList = KUrl::List::fromMimeData(e->mimeData()); + if (!uriList.isEmpty()) + emit giveMeTreeFor(uriList.first()); +} + +void RadialMap::Widget::dragEnterEvent(QDragEnterEvent *e) +{ + KUrl::List uriList = KUrl::List::fromMimeData(e->mimeData()); + e->setAccepted(!uriList.isEmpty()); +} + +void RadialMap::Widget::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::ApplicationPaletteChange || + e->type() == QEvent::PaletteChange) + m_map.paint(); +} diff --git a/filelight/src/part/remoteLister.cpp b/filelight/src/part/remoteLister.cpp new file mode 100644 index 00000000..0b84d560 --- /dev/null +++ b/filelight/src/part/remoteLister.cpp @@ -0,0 +1,171 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "remoteLister.h" +#include "fileTree.h" +#include "scan.h" + +#include +#include + +#include +#include +#include + +namespace Filelight +{ + +//TODO: delete all this stuff! + +// You need to use a single DirLister. +// One per folder breaks KIO (seemingly) and also uses un-godly amounts of memory! + +struct Store { + + typedef QLinkedList List; + + /// location of the folder + const KUrl url; + /// the folder on which we are operating + Folder *folder; + /// so we can reference the parent store + Store *parent; + /// directories in this folder that need to be scanned before we can propagate() + List stores; + + Store() : folder(0), parent(0) {} + Store(const KUrl &u, const QString &name, Store *s) + : url(u), folder(new Folder(name.toUtf8() + '/')), parent(s) {} + + + Store* propagate() + { + /// returns the next store available for scanning + + kDebug() << "propagate: " << url << endl; + + if (parent) { + parent->folder->append(folder); + if (parent->stores.isEmpty()) { + return parent->propagate(); + } + else + return parent; + } + + //we reached the root, let's get our next folder scanned + return this; + } + +private: + Store(Store&); + Store &operator=(const Store&); +}; + + +RemoteLister::RemoteLister(const KUrl &url, QWidget *parent, ScanManager* manager) + : KDirLister(parent) + , m_root(new Store(url, url.url(), 0)) + , m_store(m_root) + , m_manager(manager) +{ + setShowingDotFiles(true); // Stupid KDirLister API function names + setMainWindow(parent); + + // Use SIGNAL(result(KIO::Job*)) instead and then use Job::error() + connect(this, SIGNAL(completed()), SLOT(completed())); + connect(this, SIGNAL(canceled()), SLOT(canceled())); +} + +RemoteLister::~RemoteLister() +{ + Folder *tree = isFinished() ? m_store->folder : 0; + + emit branchCompleted(tree, false); + delete m_root; +} + +void +RemoteLister::completed() +{ + kDebug() << "completed: " << url().prettyUrl() << endl; + + // Delay the call to _completed since it can do a "delete this" + QTimer::singleShot(0, this, SLOT(_completed())); +} + +void +RemoteLister::canceled() +{ + kDebug() << "canceled: " << url().prettyUrl() << endl; + + QTimer::singleShot(0, this, SLOT(_completed())); +} + +void +RemoteLister::_completed() +{ + //m_folder is set to the folder we should operate on + + const KFileItemList items = KDirLister::items(); + for (KFileItemList::ConstIterator it = items.begin(), end = items.end(); it != end; ++it) + { + if (it->isDir()) + m_store->stores += new Store(it->url(), it->name(), m_store); + else + m_store->folder->append(it->name().toUtf8(), it->size()); + + m_manager->m_files++; + } + + + if (m_store->stores.isEmpty()) + //no directories to scan, so we need to append ourselves to the parent folder + //propagate() will return the next ancestor that has stores left to be scanned, or root if we are done + m_store = m_store->propagate(); + + if (!m_store->stores.isEmpty()) + { + Store::List::Iterator first = m_store->stores.begin(); + const KUrl url((*first)->url); + Store *currentStore = m_store; + + //we should operate with this store next time this function is called + m_store = *first; + + //we don't want to handle this store again + currentStore->stores.erase(first); + + //this returns _immediately_ + kDebug() << "scanning: " << url << endl; + openUrl(url); + } + else { + kDebug() << "I think we're done\n"; + + Q_ASSERT(m_root == m_store); + + delete this; + } +} +} + +#include "remoteLister.moc" diff --git a/filelight/src/part/remoteLister.h b/filelight/src/part/remoteLister.h new file mode 100644 index 00000000..4bd95ec3 --- /dev/null +++ b/filelight/src/part/remoteLister.h @@ -0,0 +1,54 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef REMOTELISTER_H +#define REMOTELISTER_H + +#include +#include "fileTree.h" + + +namespace Filelight +{ +class ScanManager; + +class RemoteLister : public KDirLister +{ + Q_OBJECT +public: + RemoteLister(const KUrl &url, QWidget *parent, ScanManager* manager); + ~RemoteLister(); + +signals: + void branchCompleted(Folder* tree, bool finished); + +private slots: + void completed(); + void _completed(); + void canceled(); + +private: + class Store *m_root, *m_store; + ScanManager* m_manager; +}; +} + +#endif diff --git a/filelight/src/part/scan.cpp b/filelight/src/part/scan.cpp new file mode 100644 index 00000000..4e3a2282 --- /dev/null +++ b/filelight/src/part/scan.cpp @@ -0,0 +1,238 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "scan.h" + +#include "remoteLister.h" +#include "fileTree.h" +#include "localLister.h" + +#include + +#include + +namespace Filelight +{ + +ScanManager::ScanManager(QObject *parent) + : QObject(parent) + , m_abort(false) + , m_files(0) + , m_mutex() + , m_thread(0) + , m_cache(new Chain) +{ + Filelight::LocalLister::readMounts(); + connect(this, SIGNAL(branchCompleted(Folder*,bool)), this, SLOT(cacheTree(Folder*,bool)), Qt::QueuedConnection); + connect(this, SIGNAL(branchCacheHit(Folder*)), this, SLOT(foundCached(Folder*)), Qt::QueuedConnection); +} + +ScanManager::~ScanManager() +{ + if (m_thread) { + kDebug() << "Attempting to abort scan operation..." << endl; + m_abort = true; + m_thread->wait(); + } + + delete m_cache; + + //RemoteListers are QObjects and get automatically deleted +} + +bool ScanManager::running() const +{ + return m_thread && m_thread->isRunning(); +} + +bool ScanManager::start(const KUrl &url) +{ + QMutexLocker locker(&m_mutex); // The m_mutex gets released once locker is destroyed (goes out of scope). + + //url is guaranteed clean and safe + + kDebug() << "Scan requested for: " << url.prettyUrl() << endl; + + if (running()) { + kWarning() << "Tried to launch two concurrent scans, aborting old one..."; + abort(); + } + + m_files = 0; + m_abort = false; + + if (url.protocol() == QLatin1String( "file" )) + { + const QString path = url.path(KUrl::AddTrailingSlash); + + Chain *trees = new Chain; + + /* CHECK CACHE + * user wants: /usr/local/ + * cached: /usr/ + * + * user wants: /usr/ + * cached: /usr/local/, /usr/include/ + */ + + for (Iterator it = m_cache->iterator(); it != m_cache->end(); ++it) + { + QString cachePath = (*it)->name(); + + if (path.startsWith(cachePath)) //then whole tree already scanned + { + //find a pointer to the requested branch + + kDebug() << "Cache-(a)hit: " << cachePath << endl; + + QStringList split = path.mid(cachePath.length()).split(QLatin1Char( '/' )); + Folder *d = *it; + Iterator jt; + + while (!split.isEmpty() && d != NULL) //if NULL we have got lost so abort!! + { + jt = d->iterator(); + + const Link *end = d->end(); + QString s = split.first(); + if (s.isEmpty()) //found the dir + break; + s += QLatin1Char( '/' ); + + for (d = 0; jt != end; ++jt) + if (s == (*jt)->name()) + { + d = (Folder*)*jt; + break; + } + + split.pop_front(); + } + + if (d) + { + delete trees; + + //we found a completed tree, thus no need to scan + kDebug() << "Found cache-handle, generating map.." << endl; + + emit branchCacheHit(d); + + return true; + } + else + { + //something went wrong, we couldn't find the folder we were expecting + kError() << "Didn't find " << path << " in the cache!\n"; + delete it.remove(); //safest to get rid of it + break; //do a full scan + } + } + else if (cachePath.startsWith(path)) //then part of the requested tree is already scanned + { + kDebug() << "Cache-(b)hit: " << cachePath << endl; + it.transferTo(*trees); + } + } + + m_url.setPath(path); //FIXME stop switching between paths and KURLs all the time + QApplication::setOverrideCursor(Qt::BusyCursor); + //starts listing by itself + m_thread = new Filelight::LocalLister(path, trees, this); + connect(m_thread, SIGNAL(branchCompleted(Folder*,bool)), this, SLOT(cacheTree(Folder*,bool)), Qt::QueuedConnection); + m_thread->start(); + + return true; + } + + m_url = url; + QApplication::setOverrideCursor(Qt::BusyCursor); + //will start listing straight away + Filelight::RemoteLister *remoteLister = new Filelight::RemoteLister(url, (QWidget*)parent(), this); + connect(remoteLister, SIGNAL(branchCompleted(Folder*,bool)), this, SLOT(cacheTree(Folder*,bool)), Qt::QueuedConnection); + remoteLister->setParent(this); + remoteLister->setObjectName(QLatin1String( "remote_lister" )); + remoteLister->openUrl(url); + return true; +} + +bool +ScanManager::abort() +{ + m_abort = true; + + delete findChild(QLatin1String( "remote_lister" )); + + return m_thread && m_thread->wait(); +} + +void +ScanManager::emptyCache() +{ + m_abort = true; + + if (m_thread && m_thread->isRunning()) + m_thread->wait(); + + emit aboutToEmptyCache(); + + m_cache->empty(); +} + +void +ScanManager::cacheTree(Folder *tree, bool finished) +{ + QMutexLocker locker(&m_mutex); // This gets released once it is destroyed. + + if (m_thread) { + kDebug() << "Waiting for thread to terminate ..."; + m_thread->wait(); + kDebug() << "Thread terminated!"; + delete m_thread; //note the lister deletes itself + m_thread = 0; + } + + emit completed(tree); + + if (tree) { + //we don't cache foreign stuff + //we don't recache stuff (thus only type 1000 events) + if (m_url.protocol() == QLatin1String( "file" ) && finished) + //TODO sanity check the cache + m_cache->append(tree); + } + else //scan failed + m_cache->empty(); //FIXME this is safe but annoying + + QApplication::restoreOverrideCursor(); +} + +void +ScanManager::foundCached(Folder *tree) +{ + emit completed(tree); + QApplication::restoreOverrideCursor(); +} + + +} + +#include "scan.moc" diff --git a/filelight/src/part/scan.h b/filelight/src/part/scan.h new file mode 100644 index 00000000..34ac07b4 --- /dev/null +++ b/filelight/src/part/scan.h @@ -0,0 +1,77 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef SCAN_H +#define SCAN_H + +#include +#include +#include + +class QThread; +class Folder; +template class Chain; + +namespace Filelight +{ +class ScanManager : public QObject +{ + Q_OBJECT + + friend class LocalLister; + friend class RemoteLister; + +public: + explicit ScanManager(QObject *parent); + virtual ~ScanManager(); + + bool start(const KUrl&); + bool running() const; + + uint files() const { + return m_files; + } + +public slots: + bool abort(); + void emptyCache(); + void cacheTree(Folder*, bool); + void foundCached(Folder*); + +signals: + void completed(Folder*); + void aboutToEmptyCache(); + void branchCompleted(Folder* tree, bool finished); + void branchCacheHit(Folder* tree); + +private: + bool m_abort; + uint m_files; + + KUrl m_url; + QMutex m_mutex; + QThread *m_thread; + Chain *m_cache; + +}; +} + +#endif diff --git a/filelight/src/part/settingsDialog.cpp b/filelight/src/part/settingsDialog.cpp new file mode 100644 index 00000000..8b2488d8 --- /dev/null +++ b/filelight/src/part/settingsDialog.cpp @@ -0,0 +1,242 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "settingsDialog.h" +#include "Config.h" + +#include +#include +#include + +#include +#include +#include + +SettingsDialog::SettingsDialog(QWidget *parent) : KDialog(parent) +{ + setButtons(KDialog::Reset | KDialog::Close); + + setupUi(this->mainWidget()); + QVBoxLayout *vbox = new QVBoxLayout; + //colourSchemeGroup->setFrameShape(QFrame::NoFrame); + + vbox->addWidget(new QRadioButton(i18n("Rainbow"), this), Filelight::Rainbow); + vbox->addWidget(new QRadioButton(i18n("System Colors"), this), Filelight::KDE); + vbox->addWidget(new QRadioButton(i18n("High Contrast"), this), Filelight::HighContrast); + + colourSchemeGroup->setLayout(vbox); + + //read in settings before you make all those nasty connections! + reset(); //makes dialog reflect global settings + + connect(&m_timer, SIGNAL(timeout()), SIGNAL(mapIsInvalid())); + + connect(m_addButton, SIGNAL(clicked()), SLOT(addFolder())); + connect(m_removeButton, SIGNAL(clicked()), SLOT(removeFolder())); + connect(this, SIGNAL(resetClicked()), SLOT(reset())); + connect(this, SIGNAL(closeClicked()), SLOT(close())); + + connect(colourSchemeGroup, SIGNAL(clicked(int)), SLOT(changeScheme(int))); + connect(contrastSlider, SIGNAL(valueChanged(int)), SLOT(changeContrast(int))); + connect(contrastSlider, SIGNAL(sliderReleased()), SLOT(slotSliderReleased())); + + connect(scanAcrossMounts, SIGNAL(toggled(bool)), SLOT(startTimer())); + connect(dontScanRemoteMounts, SIGNAL(toggled(bool)), SLOT(startTimer())); + connect(dontScanRemovableMedia, SIGNAL(toggled(bool)), SLOT(startTimer())); + connect(scanAcrossMounts, SIGNAL(toggled(bool)), + SLOT(toggleScanAcrossMounts(bool))); + connect(dontScanRemoteMounts, SIGNAL(toggled(bool)), + SLOT(toggleDontScanRemoteMounts(bool))); + connect(dontScanRemovableMedia, SIGNAL(toggled(bool)), + SLOT(toggleDontScanRemovableMedia(bool))); + + connect(useAntialiasing, SIGNAL(toggled(bool)), SLOT(toggleUseAntialiasing(bool))); + connect(varyLabelFontSizes, SIGNAL(toggled(bool)), SLOT(toggleVaryLabelFontSizes(bool))); + connect(showSmallFiles, SIGNAL(toggled(bool)), SLOT(toggleShowSmallFiles(bool))); + + connect(minFontPitch, SIGNAL (valueChanged(int)), SLOT(changeMinFontPitch(int))); + + m_addButton->setIcon(KIcon(QLatin1String( "folder-open" ))); + m_removeButton->setIcon(KIcon(QLatin1String( "list-remove" ))); +} + + +void SettingsDialog::closeEvent(QCloseEvent*) +{ + //if an invalidation is pending, force it now! + if (m_timer.isActive()) m_timer.setInterval(0); + + Config::write(); + + deleteLater(); +} + + +void SettingsDialog::reset() +{ + Config::read(); + + //tab 1 + scanAcrossMounts->setChecked(Config::scanAcrossMounts); + dontScanRemoteMounts->setChecked(!Config::scanRemoteMounts); + dontScanRemovableMedia->setChecked(!Config::scanRemovableMedia); + + dontScanRemoteMounts->setEnabled(Config::scanAcrossMounts); + // dontScanRemovableMedia.setEnabled(Config::scanAcrossMounts); + + m_listBox->clear(); + m_listBox->addItems(Config::skipList); + m_listBox->setCurrentRow(0); + + m_removeButton->setEnabled(m_listBox->count() > 0); + + //tab 2 + if (colourSchemeGroup->selected() != Config::scheme) //TODO: This is probably wrong + { + qobject_cast(colourSchemeGroup->layout()->itemAt(Config::scheme)->widget())->setChecked(true); + //colourSchemeGroup->setSelected(Config::scheme); + //setButton doesn't call a single QButtonGroup signal! + //so we need to call this ourselves (and hence the detection above) + changeScheme(Config::scheme); + } + contrastSlider->setValue(Config::contrast); + + useAntialiasing->setChecked(Config::antialias); + + varyLabelFontSizes->setChecked(Config::varyLabelFontSizes); + minFontPitchLabel->setEnabled(Config::varyLabelFontSizes); + minFontPitch->setEnabled(Config::varyLabelFontSizes); + minFontPitch->setValue(Config::minFontPitch); + showSmallFiles->setChecked(Config::showSmallFiles); +} + + + +void SettingsDialog::toggleScanAcrossMounts(bool b) +{ + Config::scanAcrossMounts = b; + + dontScanRemoteMounts->setEnabled(b); + //dontScanRemovableMedia.setEnabled(b); +} + +void SettingsDialog::toggleDontScanRemoteMounts(bool b) +{ + Config::scanRemoteMounts = !b; +} + +void SettingsDialog::toggleDontScanRemovableMedia(bool b) +{ + Config::scanRemovableMedia = !b; +} + + + +void SettingsDialog::addFolder() +{ + const KUrl url = KDirSelectDialog::selectDirectory(KUrl(QDir::rootPath()), false, this, i18n( "Select Folder to Scan" )); + + //TODO error handling! + //TODO wrong protocol handling! + + if (!url.isEmpty()) + { + const QString path = url.path(KUrl::AddTrailingSlash); + + if (!Config::skipList.contains(path)) + { + Config::skipList.append(path); + m_listBox->addItem(path); + if (m_listBox->currentItem() == 0) m_listBox->setCurrentRow(0); + m_removeButton->setEnabled(true); + } + else KMessageBox::sorry(this, i18n("That folder is already set to be excluded from scans")); + } +} + + +void SettingsDialog::removeFolder() +{ + Config::skipList.removeAll(m_listBox->currentItem()->text()); //removes all entries that match + + //safest method to ensure consistency + m_listBox->clear(); + m_listBox->addItems(Config::skipList); + + m_removeButton->setEnabled(m_listBox->count() > 0); + if (m_listBox->count() > 0) m_listBox->setCurrentRow(0); +} + + +void SettingsDialog::startTimer() +{ + m_timer.setSingleShot(true); + m_timer.start(TIMEOUT); +} + +void SettingsDialog::changeScheme(int s) +{ + Config::scheme = (Filelight::MapScheme)s; + emit canvasIsDirty(1); +} +void SettingsDialog::changeContrast(int c) +{ + Config::contrast = c; + emit canvasIsDirty(3); +} +void SettingsDialog::toggleUseAntialiasing(bool b) +{ + Config::antialias = b; + emit canvasIsDirty(2); +} +void SettingsDialog::toggleVaryLabelFontSizes(bool b) +{ + Config::varyLabelFontSizes = b; + minFontPitchLabel->setEnabled(b); + minFontPitch->setEnabled(b); + emit canvasIsDirty(0); +} +void SettingsDialog::changeMinFontPitch(int p) +{ + Config::minFontPitch = p; + emit canvasIsDirty(0); +} +void SettingsDialog::toggleShowSmallFiles(bool b) +{ + Config::showSmallFiles = b; + emit canvasIsDirty(1); +} + + +void SettingsDialog::slotSliderReleased() +{ + emit canvasIsDirty(2); +} + + +void SettingsDialog::reject() +{ + //called when escape is pressed + reset(); + KDialog::reject(); //**** doesn't change back scheme so far +} + +#include "settingsDialog.moc" diff --git a/filelight/src/part/settingsDialog.h b/filelight/src/part/settingsDialog.h new file mode 100644 index 00000000..1f9c9fa3 --- /dev/null +++ b/filelight/src/part/settingsDialog.h @@ -0,0 +1,68 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef SETTINGSDIALOG_H +#define SETTINGSDIALOG_H + +#include "ui_dialog.h" //generated by uic + +#include +#include + + +class SettingsDialog : public KDialog, public Ui::Dialog +{ + Q_OBJECT + +public: + explicit SettingsDialog(QWidget* = 0); + +protected: + virtual void closeEvent(QCloseEvent *); + virtual void reject(); + +public slots: + void addFolder(); + void removeFolder(); + void toggleScanAcrossMounts(bool); + void toggleDontScanRemoteMounts(bool); + void toggleDontScanRemovableMedia(bool); + void reset(); + void startTimer(); + void toggleUseAntialiasing(bool = true); + void toggleVaryLabelFontSizes(bool); + void changeContrast(int); + void changeScheme(int); + void changeMinFontPitch(int); + void toggleShowSmallFiles(bool); + void slotSliderReleased(); + +signals: + void mapIsInvalid(); + void canvasIsDirty(int); + +private: + QTimer m_timer; + + static const uint TIMEOUT=1000; +}; + +#endif diff --git a/filelight/src/part/summaryWidget.cpp b/filelight/src/part/summaryWidget.cpp new file mode 100644 index 00000000..7aa9822e --- /dev/null +++ b/filelight/src/part/summaryWidget.cpp @@ -0,0 +1,189 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#include "summaryWidget.h" + +#include "Config.h" +#include "fileTree.h" +#include "radialMap/radialMap.h" +#include "radialMap/widget.h" +#include "summaryWidget.moc" + +#include +#include //MyRadialMap::mousePressEvent() +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +struct Disk +{ + QString mount; + QString icon; + + qint64 size; + qint64 used; + qint64 free; //NOTE used+avail != size (clustersize!) +}; + + +struct DiskList : QList +{ + DiskList(); +}; + + +class MyRadialMap : public RadialMap::Widget +{ +public: + MyRadialMap(QWidget *parent) + : RadialMap::Widget(parent, true) + { + } + + virtual void setCursor(const QCursor &c) + { + if (focusSegment() && focusSegment()->file()->name() == QLatin1String( "Used" )) + RadialMap::Widget::setCursor(c); + else + unsetCursor(); + } + + virtual void mousePressEvent(QMouseEvent *e) + { + const RadialMap::Segment *segment = focusSegment(); + + //we will allow right clicks to the center circle + if (segment == rootSegment() && e->button() == Qt::RightButton) + RadialMap::Widget::mousePressEvent(e); + + //and clicks to the used segment + else if (e->button() == Qt::LeftButton ) { + const QRect rect(e->x() - 20, e->y() - 20, 40, 40); +// KIconEffect::visualActivate(this, rect); TODO: Re-enable + emit activated(url()); + } + } +}; + + + +SummaryWidget::SummaryWidget(QWidget *parent) + : QWidget(parent) +{ + qApp->setOverrideCursor(Qt::WaitCursor); + setLayout(new QGridLayout(this)); + createDiskMaps(); + qApp->restoreOverrideCursor(); +} + +void SummaryWidget::createDiskMaps() +{ + DiskList disks; + + const QByteArray free = i18nc("Free space on the disks/partitions", "Free").toUtf8(); + const QByteArray used = i18nc("Used space on the disks/partitions", "Used").toUtf8(); + + KIconLoader loader; + QString text; + + for (DiskList::ConstIterator it = disks.constBegin(), end = disks.constEnd(); it != end; ++it) + { + Disk const &disk = *it; + + if (disk.free == 0 && disk.used == 0) + continue; + + QWidget *volume = new QWidget(this); + QVBoxLayout *volumeLayout = new QVBoxLayout(volume); + RadialMap::Widget *map = new MyRadialMap(this); + + QWidget *info = new QWidget(this); + info->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + QHBoxLayout* horizontalLayout = new QHBoxLayout(info); + + // Create the text and icon under the radialMap. + text = QLatin1String( "" ) + disk.mount + i18nc("Percent used disk space on the partition", " (%1% Used)", disk.used*100/disk.size); + + QLabel *label = new QLabel(text, this); + horizontalLayout->addWidget(label); + QLabel *icon = new QLabel(this); + icon->setPixmap(KIcon(disk.icon).pixmap(16,16)); + horizontalLayout->addWidget(icon); + + horizontalLayout->setAlignment(Qt::AlignCenter); + volumeLayout->addWidget(map); + volumeLayout->addWidget(info); + + // row (=n/2) column (0 or 1) + qobject_cast(layout())->addWidget(volume, layout()->count()/2, layout()->count() % 2); + + Folder *tree = new Folder(disk.mount.toUtf8()); + tree->append(free, disk.free); + tree->append(used, disk.used); + + map->create(tree); //must be done when visible + + connect(map, SIGNAL(activated(KUrl)), SIGNAL(activated(KUrl))); + } +} + +DiskList::DiskList() +{ + const Solid::StorageAccess *partition; + QStringList partitions; + + foreach (const Solid::Device &device, Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess)) + { // Iterate over all the partitions available. + + if (!device.is()) continue; // It happens. + + partition = device.as(); + if (!partition->isAccessible()) continue; + + if (partitions.contains(partition->filePath())) // check for duplicate partitions from Solid + continue; + partitions.append(partition->filePath()); + + KDiskFreeSpaceInfo info = KDiskFreeSpaceInfo::freeSpaceInfo(partition->filePath()); + if (!info.isValid()) continue; + + Disk disk; + disk.mount = partition->filePath(); + disk.icon = device.icon(); + disk.size = info.size(); + disk.free = info.available(); + disk.used = info.used(); + + *this += disk; + } +} diff --git a/filelight/src/part/summaryWidget.h b/filelight/src/part/summaryWidget.h new file mode 100644 index 00000000..96b7498b --- /dev/null +++ b/filelight/src/part/summaryWidget.h @@ -0,0 +1,43 @@ +/*********************************************************************** +* Copyright 2003-2004 Max Howell +* Copyright 2008-2009 Martin Sandsmark +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License as +* published by the Free Software Foundation; either version 2 of +* the License or (at your option) version 3 or any later version +* accepted by the membership of KDE e.V. (or its successor approved +* by the membership of KDE e.V.), which shall act as a proxy +* defined in Section 14 of version 3 of the license. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +***********************************************************************/ + +#ifndef SUMMARYWIDGET_H +#define SUMMARYWIDGET_H + +#include + +#include + +class SummaryWidget : public QWidget +{ + Q_OBJECT + +public: + explicit SummaryWidget(QWidget *parent); + +signals: + void activated(const KUrl&); + +private: + void createDiskMaps(); +}; + +#endif