initial import of ark

This commit is contained in:
Ivailo Monev 2014-11-18 17:46:34 +00:00
commit 07d10d4d4e
147 changed files with 16676 additions and 0 deletions

4
ark/.reviewboardrc Normal file
View file

@ -0,0 +1,4 @@
REPOSITORY = "git://anongit.kde.org/ark"
REVIEWBOARD_URL = "https://git.reviewboard.kde.org"
TARGET_GROUPS = "kdeutils"
TARGET_PEOPLE = "rkcosta"

50
ark/CMakeLists.txt Normal file
View file

@ -0,0 +1,50 @@
project(ark)
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
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)
# If definitions like -D_GNU_SOURCE are needed for these checks they
# should be added to _KDE4_PLATFORM_DEFINITIONS when it is originally
# defined outside this file. Here we include these definitions in
# CMAKE_REQUIRED_DEFINITIONS so they will be included in the build of
# checks below.
set( CMAKE_REQUIRED_DEFINITIONS ${_KDE4_PLATFORM_DEFINITIONS} )
endif()
include( MacroLibrary )
list(APPEND CMAKE_MODULE_PATH ${ark_SOURCE_DIR}/cmake/modules)
macro_optional_find_package(LibArchive)
macro_log_feature(LIBARCHIVE_FOUND "LibArchive" "A library for dealing with a wide variety of archive file formats" "http://code.google.com/p/libarchive/" FALSE "" "Required for among others tar, tar.gz, tar.bz2 formats in Ark.")
configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CTestCustom.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/CTestCustom.cmake)
add_definitions(-DQT_NO_CAST_FROM_ASCII)
option(WITH_TEST_COVERAGE "Build with test coverage support" OFF)
if (WITH_TEST_COVERAGE)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} --coverage")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
endif (WITH_TEST_COVERAGE)
set(SUPPORTED_ARK_MIMETYPES "")
add_subdirectory(plugins)
add_subdirectory(kerfuffle)
add_subdirectory(part)
add_subdirectory(app)
add_subdirectory(doc)
if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})
message(STATUS "Supported MIME types: ${SUPPORTED_ARK_MIMETYPES}")
macro_display_feature_log()
endif(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_SOURCE_DIR})

346
ark/COPYING Normal file
View file

@ -0,0 +1,346 @@
NOTE! The GPL below is copyrighted by the Free Software Foundation, but
the instance of code that it refers to (the kde programs) are copyrighted
by the authors who actually wrote it.
---------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU 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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

216
ark/COPYING.icons Normal file
View file

@ -0,0 +1,216 @@
The Oxygen Icon Theme
Copyright (C) 2007 Nuno Pinheiro <nuno@oxygen-icons.org>
Copyright (C) 2007 David Vignoni <david@icon-king.com>
Copyright (C) 2007 David Miller <miller@oxygen-icons.org>
Copyright (C) 2007 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
Copyright (C) 2007 Kenneth Wimer <kwwii@bootsplash.org>
Copyright (C) 2007 Riccardo Iaconelli <riccardo@oxygen-icons.org>
and others
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see <http://www.gnu.org/licenses/>.
Clarification:
The GNU Lesser General Public License or LGPL is written for
software libraries in the first place. We expressly want the LGPL to
be valid for this artwork library too.
KDE Oxygen theme icons is a special kind of software library, it is an
artwork library, it's elements can be used in a Graphical User Interface, or
GUI.
Source code, for this library means:
- where they exist, SVG;
- otherwise, if applicable, the multi-layered formats xcf or psd, or
otherwise png.
The LGPL in some sections obliges you to make the files carry
notices. With images this is in some cases impossible or hardly useful.
With this library a notice is placed at a prominent place in the directory
containing the elements. You may follow this practice.
The exception in section 5 of the GNU Lesser General Public License covers
the use of elements of this art library in a GUI.
kde-artists [at] kde.org
-----
GNU LESSER GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
This version of the GNU Lesser General Public License incorporates
the terms and conditions of version 3 of the GNU General Public
License, supplemented by the additional permissions listed below.
0. Additional Definitions.
As used herein, "this License" refers to version 3 of the GNU Lesser
General Public License, and the "GNU GPL" refers to version 3 of the GNU
General Public License.
"The Library" refers to a covered work governed by this License,
other than an Application or a Combined Work as defined below.
An "Application" is any work that makes use of an interface provided
by the Library, but which is not otherwise based on the Library.
Defining a subclass of a class defined by the Library is deemed a mode
of using an interface provided by the Library.
A "Combined Work" is a work produced by combining or linking an
Application with the Library. The particular version of the Library
with which the Combined Work was made is also called the "Linked
Version".
The "Minimal Corresponding Source" for a Combined Work means the
Corresponding Source for the Combined Work, excluding any source code
for portions of the Combined Work that, considered in isolation, are
based on the Application, and not on the Linked Version.
The "Corresponding Application Code" for a Combined Work means the
object code and/or source code for the Application, including any data
and utility programs needed for reproducing the Combined Work from the
Application, but excluding the System Libraries of the Combined Work.
1. Exception to Section 3 of the GNU GPL.
You may convey a covered work under sections 3 and 4 of this License
without being bound by section 3 of the GNU GPL.
2. Conveying Modified Versions.
If you modify a copy of the Library, and, in your modifications, a
facility refers to a function or data to be supplied by an Application
that uses the facility (other than as an argument passed when the
facility is invoked), then you may convey a copy of the modified
version:
a) under this License, provided that you make a good faith effort to
ensure that, in the event an Application does not supply the
function or data, the facility still operates, and performs
whatever part of its purpose remains meaningful, or
b) under the GNU GPL, with none of the additional permissions of
this License applicable to that copy.
3. Object Code Incorporating Material from Library Header Files.
The object code form of an Application may incorporate material from
a header file that is part of the Library. You may convey such object
code under terms of your choice, provided that, if the incorporated
material is not limited to numerical parameters, data structure
layouts and accessors, or small macros, inline functions and templates
(ten or fewer lines in length), you do both of the following:
a) Give prominent notice with each copy of the object code that the
Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the object code with a copy of the GNU GPL and this license
document.
4. Combined Works.
You may convey a Combined Work under terms of your choice that,
taken together, effectively do not restrict modification of the
portions of the Library contained in the Combined Work and reverse
engineering for debugging such modifications, if you also do each of
the following:
a) Give prominent notice with each copy of the Combined Work that
the Library is used in it and that the Library and its use are
covered by this License.
b) Accompany the Combined Work with a copy of the GNU GPL and this license
document.
c) For a Combined Work that displays copyright notices during
execution, include the copyright notice for the Library among
these notices, as well as a reference directing the user to the
copies of the GNU GPL and this license document.
d) Do one of the following:
0) Convey the Minimal Corresponding Source under the terms of this
License, and the Corresponding Application Code in a form
suitable for, and under terms that permit, the user to
recombine or relink the Application with a modified version of
the Linked Version to produce a modified Combined Work, in the
manner specified by section 6 of the GNU GPL for conveying
Corresponding Source.
1) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (a) uses at run time
a copy of the Library already present on the user's computer
system, and (b) will operate properly with a modified version
of the Library that is interface-compatible with the Linked
Version.
e) Provide Installation Information, but only if you would otherwise
be required to provide such information under section 6 of the
GNU GPL, and only to the extent that such information is
necessary to install and execute a modified version of the
Combined Work produced by recombining or relinking the
Application with a modified version of the Linked Version. (If
you use option 4d0, the Installation Information must accompany
the Minimal Corresponding Source and Corresponding Application
Code. If you use option 4d1, you must provide the Installation
Information in the manner specified by section 6 of the GNU GPL
for conveying Corresponding Source.)
5. Combined Libraries.
You may place library facilities that are a work based on the
Library side by side in a single library together with other library
facilities that are not Applications and are not covered by this
License, and convey such a combined library under terms of your
choice, if you do both of the following:
a) Accompany the combined library with a copy of the same work based
on the Library, uncombined with any other library facilities,
conveyed under the terms of this License.
b) Give prominent notice with the combined library that part of it
is a work based on the Library, and explaining where to find the
accompanying uncombined form of the same work.
6. Revised Versions of the GNU Lesser General Public License.
The Free Software Foundation may publish revised and/or new versions
of the GNU Lesser 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
Library as you received it specifies that a certain numbered version
of the GNU Lesser General Public License "or any later version"
applies to it, you have the option of following the terms and
conditions either of that published version or of any later version
published by the Free Software Foundation. If the Library as you
received it does not specify a version number of the GNU Lesser
General Public License, you may choose any version of the GNU Lesser
General Public License ever published by the Free Software Foundation.
If the Library as you received it specifies that a proxy can decide
whether future versions of the GNU Lesser General Public License shall
apply, that proxy's public statement of acceptance of any version is
permanent authorization for you to choose that version for the
Library.

13
ark/CTestConfig.cmake Normal file
View file

@ -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 "ark")
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=ark")
set(CTEST_DROP_SITE_CDASH TRUE)

1
ark/CTestCustom.cmake.in Normal file
View file

@ -0,0 +1 @@
set(CTEST_CUSTOM_COVERAGE_EXCLUDE ".moc$" "moc_" "ui_")

52
ark/HACKING Normal file
View file

@ -0,0 +1,52 @@
== Coding Style ==
Ark follows the kdelibs/Qt coding style. For more information about them,
please see:
- http://techbase.kde.org/Policies/Kdelibs_Coding_Style
- http://wiki.qt-project.org/Coding_Style
- http://wiki.qt-project.org/Coding_Conventions
== Sending patches ==
To send patches for Ark, you can either use git's send-email command and send
it to the kde-utils-devel@kde.org mailing list, or use KDE's ReviewBoard tool
in <https://git.reviewboard.kde.org>. In case you choose the latter, the
review should be sent for the Ark product, and the `kdeutils' group should be
added to the review request.
If you already have a KDE commit account, it is still preferrable to contact
the maintainer instead of committing directly, at least to be a good citizen
and especially so that git mistakes are not made (see the `Using git' section).
== Using git ==
The development model adopted by Ark is simple and rely on git's easy merging
and branching capabilities. If in doubt, do not hesitate to ask!
First of all, you should do your work in a separate branch, and each commit
should be as atomic as possible. This way, if you are asked to make changes to
them, the rest of your work is not disturbed and you can easily rebase.
New features are committed to the `master' branch, respecting KDE's Release
Schedule policies. This means the soft and hard freeze periods must be
respected, as well as the string freeze policy.
Bug fixes are committed to the latest stable branch (for example, KDE/4.8),
which is then merged into the `master' branch. Do *NOT* cherry-pick commits
into multiple branches! It makes following history unnecessarily harder for no
reason.
To merge the stable branch into `master', the following steps can be followed:
$ git checkout KDE/4.8 # Whatever the stable branch is
$ # hack, hack, hack
$ # commit
$ git checkout master
$ git merge --log --edit -s recursive -Xours KDE/4.8
Do not worry if unrelated commits (such as translation ones made by KDE's
translation infrastructure) are also merged: translation commits are
automatically reverted when needed, and other commits being merged should be
bug fixes by definition.
When committing your changes, do *NOT* create unnecessary merge commits with
`git pull', as these commits are completely avoidable and make following
history harder. If you are committing your changes, *rebase* first.

8
ark/Mainpage.dox Normal file
View file

@ -0,0 +1,8 @@
/** @mainpage ark
The ark application
*/
// DOXYGEN_REFERENCES = kdecore
// DOXYGEN_SET_PROJECT_NAME = ark

6
ark/Messages.sh Normal file
View file

@ -0,0 +1,6 @@
#! /bin/sh
$EXTRACTRC $(find . -name '*.rc') >> rc.cpp || exit 11
$EXTRACTRC $(find . -name '*.ui') >> rc.cpp || exit 12
$EXTRACTRC $(find . -name '*.kcfg') >> rc.cpp
$XGETTEXT app/*.cpp kerfuffle/*.cpp part/*.cpp plugins/*/*.cpp rc.cpp -o $podir/ark.pot
rm -f rc.cpp

50
ark/app/CMakeLists.txt Normal file
View file

@ -0,0 +1,50 @@
add_subdirectory(icons)
set(ark_SRCS
batchextract.cpp
main.cpp
mainwindow.cpp
)
# For Mac and Windows.
kde4_add_app_icon(ark_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/hi*-apps-ark.png")
kde4_add_executable( ark ${ark_SRCS} )
target_link_libraries( ark kerfuffle ${KDE4_KFILE_LIBS} ${KDE4_KPARTS_LIBS} )
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/ark.desktop.cmake
${CMAKE_CURRENT_BINARY_DIR}/ark.desktop
)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/ark_dndextract.desktop.cmake
${CMAKE_CURRENT_BINARY_DIR}/ark_dndextract.desktop
)
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/ark_servicemenu.desktop.cmake
${CMAKE_CURRENT_BINARY_DIR}/ark_servicemenu.desktop
)
install( TARGETS ark ${INSTALL_TARGETS_DEFAULT_ARGS} )
install(FILES ark_addtoservicemenu.desktop ${CMAKE_CURRENT_BINARY_DIR}/ark_servicemenu.desktop DESTINATION ${SERVICES_INSTALL_DIR}/ServiceMenus)
install( PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/ark.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} )
install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/ark.appdata.xml DESTINATION share/appdata )
install( FILES arkui.rc DESTINATION ${DATA_INSTALL_DIR}/ark )
########### konqueror ark_extract_here plugin ###############
macro_optional_find_package( LibKonq )
macro_log_feature( LIBKONQ_FOUND "LIBKONQ" "libkonq library" "kdebase" FALSE "" "Need to integrate in konqueror" )
if (LIBKONQ_FOUND)
set(extracthere_SRCS batchextract.cpp extractHereDndPlugin.cpp)
kde4_add_plugin(extracthere WITH_PREFIX ${extracthere_SRCS})
target_link_libraries(extracthere kerfuffle ${KDE4_KDECORE_LIBS} ${KDE4_KPARTS_LIBS} ${KDE4_KFILE_LIBS} ${LIBKONQ_LIBRARY} )
include_directories(${LIBKONQ_INCLUDE_DIR})
install( TARGETS extracthere DESTINATION ${PLUGIN_INSTALL_DIR} )
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/ark_dndextract.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
endif (LIBKONQ_FOUND)

226
ark/app/ark.appdata.xml Normal file
View file

@ -0,0 +1,226 @@
<?xml version="1.0" encoding="utf-8"?>
<component type="desktop">
<id>ark.desktop</id>
<metadata_license>CC0-1.0</metadata_license>
<project_license>GPL-2.0+</project_license>
<name>Ark</name>
<name xml:lang="ar">آرك</name>
<name xml:lang="bg">Ark</name>
<name xml:lang="ca">Ark</name>
<name xml:lang="cs">Ark</name>
<name xml:lang="da">Ark</name>
<name xml:lang="de">Ark</name>
<name xml:lang="en-GB">Ark</name>
<name xml:lang="es">Ark</name>
<name xml:lang="et">Ark</name>
<name xml:lang="fi">Ark</name>
<name xml:lang="fr">Ark</name>
<name xml:lang="hu">Ark</name>
<name xml:lang="it">Ark</name>
<name xml:lang="ko">Ark</name>
<name xml:lang="lt">Ark</name>
<name xml:lang="nb">Ark</name>
<name xml:lang="nds">Ark</name>
<name xml:lang="nl">Ark</name>
<name xml:lang="pl">Ark</name>
<name xml:lang="pt">Ark</name>
<name xml:lang="pt-BR">Ark</name>
<name xml:lang="sk">Ark</name>
<name xml:lang="sl">Ark</name>
<name xml:lang="sr">Арк</name>
<name xml:lang="sr-Latn">Ark</name>
<name xml:lang="sr-ijekavian">Арк</name>
<name xml:lang="sr-ijekavianlatin">Ark</name>
<name xml:lang="sv">Ark</name>
<name xml:lang="tr">Ark</name>
<name xml:lang="uk">Ark</name>
<name xml:lang="x-test">xxArkxx</name>
<name xml:lang="zh-CN">Ark</name>
<name xml:lang="zh-TW">檔案壓縮_Ark</name>
<summary>Archiving Tool</summary>
<summary xml:lang="ar">أداة أرشفة</summary>
<summary xml:lang="bg">Работа с архиви</summary>
<summary xml:lang="ca">Eina d'arxivament</summary>
<summary xml:lang="cs">Archivační nástroj</summary>
<summary xml:lang="da">Arkiveringsværktøj</summary>
<summary xml:lang="de">Archivprogramm</summary>
<summary xml:lang="en-GB">Archiving Tool</summary>
<summary xml:lang="es">Herramienta de archivado</summary>
<summary xml:lang="et">Arhiivide haldamise rakendus</summary>
<summary xml:lang="fi">Pakkausohjelma</summary>
<summary xml:lang="fr">Outil d'archivage</summary>
<summary xml:lang="hu">Fájltömörítő</summary>
<summary xml:lang="it">Strumento di archiviazione</summary>
<summary xml:lang="ko">압축 도구</summary>
<summary xml:lang="lt">Archyvavimo įrankis</summary>
<summary xml:lang="nb">Arkiveringsverktøy</summary>
<summary xml:lang="nds">Archievwarktüüch</summary>
<summary xml:lang="nl">Archiefgereedschap</summary>
<summary xml:lang="pl">Narzędzie do archiwizowania</summary>
<summary xml:lang="pt">Ferramenta de Arquivo</summary>
<summary xml:lang="pt-BR">Ferramenta de arquivamento</summary>
<summary xml:lang="sk">Archivačný nástroj</summary>
<summary xml:lang="sl">Orodje za ravnanje z arhivi</summary>
<summary xml:lang="sr">Алатка за архивирање</summary>
<summary xml:lang="sr-Latn">Alatka za arhiviranje</summary>
<summary xml:lang="sr-ijekavian">Алатка за архивирање</summary>
<summary xml:lang="sr-ijekavianlatin">Alatka za arhiviranje</summary>
<summary xml:lang="sv">Arkiveringsverktyg</summary>
<summary xml:lang="tr">Arşivleme Aracı</summary>
<summary xml:lang="uk">Інструмент роботи з архівами</summary>
<summary xml:lang="x-test">xxArchiving Toolxx</summary>
<summary xml:lang="zh-CN">压缩工具</summary>
<summary xml:lang="zh-TW">壓縮工具</summary>
<description>
<p>
Ark is a graphical file compression/decompression utility with support for multiple formats,
including tar, gzip, bzip2, rar and zip, as well as CD-ROM images.
Ark can be used to browse, extract, create, and modify archives.
</p>
<p xml:lang="ar">آرك أداة رسوميّة لضغط وفكّ ضغط الملفات مع دعم لصيغ متعدّدة، منها tar، وgzip، وbzip2، وrar، وzip، إضافة إلى صور الأقراص الضوئيّة. يمكن استخدام آرك لتصفح، واستخراج، وإنشاء، وتعديل الأرشيفات.</p>
<p xml:lang="bg">Ark е графичен инструмент за архивиране/разархивиране, поддържащ множество формати, включително tar, gzip, bzip2, rar и zip, както и образи на CD-ROM. Ark може да се използва за разглеждане, извличане, създаване и променяне на архиви.</p>
<p xml:lang="ca">L'Ark és una utilitat gràfica de compressió/descompressió de fitxers que implementa múltiples formats, incloent tar, gzip, bzip2, rar i zip, i també imatges de CD-ROM. L'Ark es pot utilitzar per explorar, extreure, crear, i modificar arxiua.</p>
<p xml:lang="da">Ark er et grafisk værktøj til komprimering/dekomprimering, med understøttelse for flere formater, derunder tar, gzip, bzip2, rar og zip, såvel som cd-rom-imagefiler. Ark kan bruges til at gennemse, udtrække, oprette og redigere.</p>
<p xml:lang="de">Ark ist ein grafisches Dienstprogramm zum Packen/Entpacken von Dateien mit Unterstützung für mehrere Formate wie tar, gzip, bzip2, rar, zip und auch CD-Abbilder. Mit Ark können Sie können Sie Archive durchsehen, entpacken, erstellen und bearbeiten.</p>
<p xml:lang="en-GB">Ark is a graphical file compression/decompression utility with support for multiple formats, including tar, gzip, bzip2, rar and zip, as well as CD-ROM images. Ark can be used to browse, extract, create, and modify archives.</p>
<p xml:lang="es">Ark es una utilidad gráfica de compresión y descompresión de archivos que permite usar diversos formatos, como tar, gzip, bzip2, rar y zip, así como imágenes de CD-ROM. Ark se puede usar para explorar, extraer, crear y modificar archivos comprimidos.</p>
<p xml:lang="et">Ark on graafiline failide tihendamise ehk kokkupakkimise ja nende lahtipakkimise tööriist, mis toetab väga paljusid vorminguid, sealhulgas tar, gzip, bzip2, rar and zip, samuti CD-ROM-i tõmmised. Arki abil saab arhiivifaile sirvida, lahti pakkida, luua ja muuta.</p>
<p xml:lang="fi">Ark on graafinen tiedostojen pakkaus-/purkuohjelma, joka tukee useita tiedostomuotoja kuten tar, gzip, bzip2, rar and zip sekä myös CD-ROM-levykuvia. Arkilla voi selata, purkaa, luoda ja muuttaa paketteja.</p>
<p xml:lang="fr">Ark est un utilitaire graphique de compression/décompression de fichier prenant en charge de multiples formats, notamment tar, gzip, bzip2, rar et zip, ainsi que les images de CD-ROM. Ark peut être utilisé pour parcourir, extraire, créer et modifier des archives.</p>
<p xml:lang="hu">Az Ark egy grafikus fájltömörítő és kibontó segédprogram többféle formátum támogatásával, beleértve a tar, gzip, bzip2, rar és zip formátumokat, valamint a CD-ROM képfájlokat. Az Ark használható archívumok böngészéséhez, kibontásához, létrehozásához és módosításához.</p>
<p xml:lang="it">Ark è uno strumento grafico per la compressione/decompressione dei file che supporta molti formati, tra i quali tar, gzip, bzip2, rar e zip, così come le immagini dei CD-ROM. Ark può essere utilizzato per sfogliare, estrarre, creare e modificare archivi.</p>
<p xml:lang="ko">Ark는 그래픽 압축 파일 관리 도구이며, tar, gzip, bzip2, rar, zip, CD-ROM 이미지를 포함한 여러 파일 형식을 지원합니다. Ark를 사용하여 압축 파일을 열고, 생성하고, 수정하고, 압축을 풀 수 있습니다.</p>
<p xml:lang="nb">Ark er et verktøy med grafisk brukerflate, for komprimering/dekomprimering, med støtte for mange formater, deriblant tar, gzip, rar og zip, samt CD-ROM-bilder. Ark kan brukes til å bla i, pakke opp, opprette og endre arkiver.</p>
<p xml:lang="nds">Ark is en graafsch Komprimeer- un Dekomprimeerwarktüüch, dat en Barg Formaten ünnerstütten deit. Dor sünd tar, gzip, bzip2, rar un zip bi, un ok CD-ROM-Afbiller. Mit Ark kannst Du Archiven dörkieken, opstellen un ännern un Dateien dor ruttrecken.</p>
<p xml:lang="nl">Ark is een grafisch hulpmiddel om bestanden te comprimeren/uit te pakken met ondersteuning van meerdere formaten, inclusief tar, gzip, bzip2, rar en zip, evenals cd-ROM images. Ark kan gebruikt worden om door archieven te bladeren, deze uit te pakken, te maken en te wijzigen.</p>
<p xml:lang="pl">Ark jest graficznym narzędziem do pakowania/rozpakowywania plików wraz z obsługą różnych formatów, włączając w to tar, gzip, bzip2, rar oraz zip, a także obrazy CD-ROM. Programu Ark można używać do przeglądania, tworzenia, zmieniania oraz wydobywania z archiwów</p>
<p xml:lang="pt">O Ark é um utilitário de compressão/descompressão com o suporte para diversos formatos, incluindo o 'tar', 'gzip', 'bzip2', 'rar' e 'zip', assim como imagens de CD-ROM. O Ark pode ser usado para navegar, extrair, criar e modificar esses pacotes.</p>
<p xml:lang="pt-BR">O Ark é um utilitário de compressão/descompressão com o suporte a diversos formatos, incluindo tar, gzip, bzip2, rar e zip, assim como imagens de CD-ROM. O Ark pode ser usado para navegar, extrair, criar e modificar esses arquivos.</p>
<p xml:lang="sk">Ark je grafický nástroj na kompresiu a dekompresiu súborov s podporou pre mnoho formátov, vrátane tar, gzip bzip2, rar a zip, ako ako CD-ROM obrazy. Ark sa dá použiť na prehliadanie, rozbaľovanie vytváranie a úpravu archívov.</p>
<p xml:lang="sl">Ark je grafično orodje za stiskanje/razširjanje datotek, ki podpira večje število arhivov (med podprtimi so tudi tar, gzip, bzip2, rar in zip) kot tudi odtise CD-ROM. Ark lahko uporabite za brskanje po, razširjanje, ustvarjanje in spreminjanje arhivov.</p>
<p xml:lang="sr">Арк је графичка алатка за компресовање и декомпресовање фајлова. Подржава више формата, међу њима и: тар, гзип, бзип2, РАР, ЗИП, као и ЦД одразе. Може се користити за прегледање, распакивање, стварање и мењање архива.</p>
<p xml:lang="sr-Latn">Ark je grafička alatka za kompresovanje i dekompresovanje fajlova. Podržava više formata, među njima i: tar, gzip, bzip2, RAR, ZIP, kao i CD odraze. Može se koristiti za pregledanje, raspakivanje, stvaranje i menjanje arhiva.</p>
<p xml:lang="sr-ijekavian">Арк је графичка алатка за компресовање и декомпресовање фајлова. Подржава више формата, међу њима и: тар, гзип, бзип2, РАР, ЗИП, као и ЦД одразе. Може се користити за прегледање, распакивање, стварање и мењање архива.</p>
<p xml:lang="sr-ijekavianlatin">Ark je grafička alatka za kompresovanje i dekompresovanje fajlova. Podržava više formata, među njima i: tar, gzip, bzip2, RAR, ZIP, kao i CD odraze. Može se koristiti za pregledanje, raspakivanje, stvaranje i menjanje arhiva.</p>
<p xml:lang="sv">Ark är ett grafiskt verktyg för komprimering och uppackning av filer med stöd för flera format, inklusive tar, gzip, bzip2, rar och zip, samt cd-rom avbilder. Ark kan användas för att bläddra i, packa upp, skapa och ändra arkiv.</p>
<p xml:lang="tr">Ark; tar, gzip, bzip2, rar ve zip dosyalarının yanında CD-ROM kalıplarını da destekleyen grafiksel bir dosya sıkıştırma/açma yardımcısıdır. Ark arşivlerde gezinmek, arşiv ayıklamak, oluşturmak ve değiştirmek için kullanılabilir.</p>
<p xml:lang="uk">Ark — програма з графічним інтерфейсом, призначена для стискання даних у архіви та видобування даних з архівів. Передбачено підтримку декількох форматів архівів, зокрема tar, gzip, bzip2, rar та zip, а також образів дисків. Ark можна використовувати для перегляду, видобування, створення та внесення змін до архівів.</p>
<p xml:lang="x-test">xxArk is a graphical file compression/decompression utility with support for multiple formats, including tar, gzip, bzip2, rar and zip, as well as CD-ROM images. Ark can be used to browse, extract, create, and modify archives.xx</p>
<p xml:lang="zh-CN">Ark 是图形文件压缩和解压工具,支持多种格式,包括 tar, gzip, bzip2, rar and zip 以及 CD-ROM 镜像。Ark 可以用来浏览、解压、创建和修改压缩包。</p>
<p xml:lang="zh-TW">Ark 是一套圖形介面的檔案壓縮/解壓縮工具,支援多種格式,包括 tar, gzip, bzip2, rar 與 zip 等,還有 CD-ROM 映像檔等。Ark 有瀏覽、解開、建立與變更壓縮檔的功能。</p>
<p>Features:</p>
<p xml:lang="ar">المزايا</p>
<p xml:lang="bg">Функции:</p>
<p xml:lang="ca">Característiques:</p>
<p xml:lang="cs">Vlastnosti:</p>
<p xml:lang="da">Funktioner:</p>
<p xml:lang="de">Leistungsmerkmale:</p>
<p xml:lang="en-GB">Features:</p>
<p xml:lang="es">Funcionalidades:</p>
<p xml:lang="et">Omadused:</p>
<p xml:lang="fi">Ominaisuudet:</p>
<p xml:lang="fr">Fonctionnalités :</p>
<p xml:lang="hu">Szolgáltatások:</p>
<p xml:lang="it">Funzionalità:</p>
<p xml:lang="ko">기능:</p>
<p xml:lang="lt">Galimybės:</p>
<p xml:lang="nb">Egenskaper:</p>
<p xml:lang="nds">Markmalen:</p>
<p xml:lang="nl">Mogelijkheden:</p>
<p xml:lang="pl">Cechy:</p>
<p xml:lang="pt">Características:</p>
<p xml:lang="pt-BR">Funcionalidades:</p>
<p xml:lang="sk">Funkcie:</p>
<p xml:lang="sl">Zmožnosti:</p>
<p xml:lang="sr">Могућности:</p>
<p xml:lang="sr-Latn">Mogućnosti:</p>
<p xml:lang="sr-ijekavian">Могућности:</p>
<p xml:lang="sr-ijekavianlatin">Mogućnosti:</p>
<p xml:lang="sv">Funktioner:</p>
<p xml:lang="tr">Özellikler:</p>
<p xml:lang="uk">Можливості:</p>
<p xml:lang="x-test">xxFeatures:xx</p>
<p xml:lang="zh-CN">功能:</p>
<p xml:lang="zh-TW">特色:</p>
<ul>
<li>Several formats supported: gzip, bzip2, zip, rar, 7z and more</li>
<li xml:lang="ar">دعم صيغ متعدّدة: مثل gzip، و bzip2، و zip، و rar، و 7z وغيرها</li>
<li xml:lang="bg">Поддържани са много формати: gzip, bzip2, zip, rar, 7z и други</li>
<li xml:lang="ca">Diversos formats implementats: gzip, bzip2, zip, rar, 7z i més</li>
<li xml:lang="cs">Je podporováno několik formátů: gzip, bzip2, zip, rar, 7z a další</li>
<li xml:lang="da">Flere understøttede formater: gzip, bzip2, zip, rar, 7z og flere</li>
<li xml:lang="de">Mehrere unterstützte Formate wie gzip, bzip2, zip, rar, 7z und mehr</li>
<li xml:lang="en-GB">Several formats supported: gzip, bzip2, zip, rar, 7z and more</li>
<li xml:lang="es">Permite el uso de diversos formatos: gzip, bzip2, zip, rar, 7z y más</li>
<li xml:lang="et">Paljude vormingute toetus: gzip, bzip2, zip, rar, 7z ja veel paljud.</li>
<li xml:lang="fi">Tukee useita tiedostomuotoja: gzip, bzip2, zip, rar, 7z ja monia muita</li>
<li xml:lang="fr">Plusieurs formats sont pris en charge : gzip, bzip2, zip, rar, 7z et d'autres</li>
<li xml:lang="hu">Számos formátum támogatott: gzip, bzip2, zip, rar, 7z és továbbiak</li>
<li xml:lang="it">Diversi formati supportati: gzip, bzip2, zip, rar, 7z e altri</li>
<li xml:lang="ko">다양한 형식 지원: gzip, bzip2, zip, rar, 7z 등</li>
<li xml:lang="nb">Mange formater støttet: gzip, bzip2, zip, rar, 7z og mer</li>
<li xml:lang="nds">En Reeg Fomaten warrt ünnerstütt: gzip, bzip2, zip, rar, 7z un anner</li>
<li xml:lang="nl">Ondersteuning voor een aantal formaten: gzip, bzip2, zip, rar, 7z en meer</li>
<li xml:lang="pl">Obsługa kilku formatów: gzip, bzip2, zip, rar, 7z oraz więcej</li>
<li xml:lang="pt">Diversos formatos suportados: gzip, bzip2, zip, rar, 7z, entre outros</li>
<li xml:lang="pt-BR">Diversos formatos suportados: gzip, bzip2, zip, rar, 7z, entre outros</li>
<li xml:lang="sk">Niektoré z podporovaných formátov: gzip, bzip2, zip, rar, 7z a viac</li>
<li xml:lang="sl">Številne podprte vrste arhivov: gzip, bzip2, zip, rar, 7z in več</li>
<li xml:lang="sr">Више подржаних формата: гзип, бзип2, ЗИП, РАР, 7зип, итд.</li>
<li xml:lang="sr-Latn">Više podržanih formata: gzip, bzip2, ZIP, RAR, 7zip, itd.</li>
<li xml:lang="sr-ijekavian">Више подржаних формата: гзип, бзип2, ЗИП, РАР, 7зип, итд.</li>
<li xml:lang="sr-ijekavianlatin">Više podržanih formata: gzip, bzip2, ZIP, RAR, 7zip, itd.</li>
<li xml:lang="sv">En mängd format stöds: gzip, bzip2, zip, rar, 7z med flera</li>
<li xml:lang="tr">Çeşitli biçimler destekleniyor: gzip, bzip2, zip, rar, 7z ve dahası</li>
<li xml:lang="uk">Підтримка декількох форматів: gzip, bzip2, zip, rar, 7z тощо</li>
<li xml:lang="x-test">xxSeveral formats supported: gzip, bzip2, zip, rar, 7z and morexx</li>
<li xml:lang="zh-CN">支持多种格式gzip, bzip2, zip, rar, 7z 等</li>
<li xml:lang="zh-TW">支援格式gzip, bzip2, zip, rar, 7z 等等</li>
<li>Preview file contents without extracting files</li>
<li xml:lang="ar">استعراض محتوى الملف دون استخراج الملفات منه</li>
<li xml:lang="bg">Преглеждане съдържанието на файла, без извличане на файловете</li>
<li xml:lang="ca">Vista prèvia del contingut de fitxers sense extreure'ls</li>
<li xml:lang="cs">Náhled obsahu souborů bez jejich rozbalení</li>
<li xml:lang="da">Forhåndsvis filindhold uden at udtrække filer</li>
<li xml:lang="de">Vorschau der Dateiinhalte ohne das Entpacken der Dateien</li>
<li xml:lang="en-GB">Preview file contents without extracting files</li>
<li xml:lang="es">Vista previa del contenido de archivos sin extraerlos</li>
<li xml:lang="et">Failide sisu näitamine ilma neid lahti pakkimata.</li>
<li xml:lang="fi">Tiedoston sisällön esikatselu purkamatta tiedostoa</li>
<li xml:lang="fr">Prévisualiser le contenu des fichiers sans les extraire</li>
<li xml:lang="hu">Fájltartalom előnézete a fájlok kibontása nélkül</li>
<li xml:lang="it">Anteprima dei file senza estrarre i file</li>
<li xml:lang="ko">압축 풀지 않고 파일 내용 미리 보기</li>
<li xml:lang="nb">Forhåndsvis innhold uten å pakke ut filer</li>
<li xml:lang="nds">Datein ahn Ruttrecken vörweg ankieken</li>
<li xml:lang="nl">Voorvertoning van inhoud van bestand zonder bestanden uit te pakken</li>
<li xml:lang="pl">Podejrzyj zawartość pliku bez jego wypakowywania</li>
<li xml:lang="pt">Antevisão do conteúdo dos ficheiros sem os extrair</li>
<li xml:lang="pt-BR">Visualização do conteúdo dos arquivos sem precisar extraí-los</li>
<li xml:lang="sk">Náhľad obsahu súboru bez rozbalenia súborov</li>
<li xml:lang="sl">Predoglejte vsebino datoteke brez razširjanja</li>
<li xml:lang="sr">Преглед садржаја фајлова без распакивања.</li>
<li xml:lang="sr-Latn">Pregled sadržaja fajlova bez raspakivanja.</li>
<li xml:lang="sr-ijekavian">Преглед садржаја фајлова без распакивања.</li>
<li xml:lang="sr-ijekavianlatin">Pregled sadržaja fajlova bez raspakivanja.</li>
<li xml:lang="sv">Förhandsgranska filinnehåll utan att packa upp filer</li>
<li xml:lang="tr">Dosyaları ayıklamadan dosya içeriği önizlemesi</li>
<li xml:lang="uk">Попередній перегляд вмісту файлів без розпаковування</li>
<li xml:lang="x-test">xxPreview file contents without extracting filesxx</li>
<li xml:lang="zh-CN">不解压就预览文件</li>
<li xml:lang="zh-TW">不需解壓縮即可預覽檔案內容</li>
</ul>
</description>
<screenshots>
<screenshot type="default">
<image width="834" height="651">http://kde.org/images/screenshots/ark.png</image>
</screenshot>
</screenshots>
<url type="homepage">http://kde.org/applications/utilities/ark/</url>
<url type="bugtracker">https://bugs.kde.org/enter_bug.cgi?format=guided&amp;product=ark</url>
<url type="help">http://docs.kde.org/stable/en/kdeutils/ark/index.html</url>
<project_group>KDE</project_group>
<provides>
<binary>ark</binary>
</provides>
</component>

158
ark/app/ark.desktop.cmake Executable file
View file

@ -0,0 +1,158 @@
[Desktop Entry]
MimeType=@SUPPORTED_ARK_MIMETYPES@
GenericName=Archiving Tool
GenericName[af]=Argiveer Program
GenericName[ar]=أداة أرشفة
GenericName[ast]=Ferramienta p'archivar
GenericName[bg]=Работа с архиви
GenericName[br]=Ostilh merañ an Dielloù
GenericName[bs]=Alatka za arhiviranje
GenericName[ca]=Eina d'arxivament
GenericName[ca@valencia]=Eina d'arxivament
GenericName[cs]=Archivační nástroj
GenericName[cy]=Erfyn Archifo
GenericName[da]=Arkiveringsværktøj
GenericName[de]=Archivprogramm
GenericName[el]=Εργαλείο αρχειοθέτησης
GenericName[en_GB]=Archiving Tool
GenericName[eo]=Arkivilo
GenericName[es]=Archivador
GenericName[et]=Arhiivide haldamise rakendus
GenericName[eu]=Artxibatzeko tresna
GenericName[fa]=ابزار بایگانی
GenericName[fi]=Pakkausohjelma
GenericName[fr]=Outil d'archivage
GenericName[ga]=Uirlis Chartlannaithe
GenericName[gl]=Utilidade de arquivo
GenericName[he]=כלי לניהול ארכיונים
GenericName[hne]=ि
GenericName[hr]=Alat za arhiviranje
GenericName[hu]=Fájltömörítő
GenericName[ia]=Instrumento per archivar
GenericName[id]=Perkakas Pengarsip
GenericName[is]=Vinna með safnskrár
GenericName[it]=Strumento di archiviazione
GenericName[ja]=
GenericName[kk]=Архивтеу құралы
GenericName[km]=
GenericName[ko]=
GenericName[lt]=Archyvavimo priemonė
GenericName[lv]=Arhivēšanas rīks
GenericName[mk]=Алатка за архивирање
GenericName[mr]=
GenericName[ms]=Alatan Pengarkiban
GenericName[nb]=Arkiveringsverktøy
GenericName[nds]=Archievwarktüüch
GenericName[ne]=
GenericName[nl]=Archiefgereedschap
GenericName[nn]=Arkiveringsverktøy
GenericName[pa]=ਿ
GenericName[pl]=Narzędzie do archiwizacji
GenericName[pt]=Ferramenta de Armazenamento
GenericName[pt_BR]=Ferramenta de Arquivamento
GenericName[ro]=Utilitar de arhivare
GenericName[ru]=Архиватор
GenericName[sk]=Archivačný nástroj
GenericName[sl]=Orodje za ravnanje z arhivi
GenericName[sq]=Mjeti Arkivues
GenericName[sr]=Алатка за архивирање
GenericName[sr@ijekavian]=Алатка за архивирање
GenericName[sr@ijekavianlatin]=Alatka za arhiviranje
GenericName[sr@latin]=Alatka za arhiviranje
GenericName[sv]=Arkiveringsverktyg
GenericName[ta]= ி
GenericName[tg]=Асбобҳои Бойгонӣ
GenericName[th]=
GenericName[tr]=Arşivleme Aracı
GenericName[ug]=ئارخىپ قورالى
GenericName[uk]=Засіб роботи з архівами
GenericName[uz]=Arxivlash vositasi
GenericName[uz@cyrillic]=Архивлаш воситаси
GenericName[vi]=Công C Nén
GenericName[wa]=Usteye d' årtchivaedje
GenericName[xh]=Isixhobo Sokuphatha i Archive
GenericName[x-test]=xxArchiving Toolxx
GenericName[zh_CN]=
GenericName[zh_TW]=
Name=Ark
Name[af]=Ark
Name[ar]=أرك
Name[ast]=Ark
Name[bg]=Ark
Name[br]=Ark
Name[bs]=Ark
Name[ca]=Ark
Name[ca@valencia]=Ark
Name[cs]=Ark
Name[cy]=Ark
Name[da]=Ark
Name[de]=Ark
Name[el]=Ark
Name[en_GB]=Ark
Name[eo]=Arko
Name[es]=Ark
Name[et]=Ark
Name[eu]=Ark
Name[fi]=Ark
Name[fr]=Ark
Name[ga]=Ark
Name[gl]=Ark
Name[he]=Ark
Name[hne]=
Name[hr]=Ark
Name[hu]=Ark
Name[ia]=Ark
Name[id]=Ark
Name[is]=Ark
Name[it]=Ark
Name[ja]=Ark
Name[kk]=Ark
Name[km]=Ark
Name[ko]=Ark
Name[lt]=Ark
Name[lv]=Ark
Name[mk]=Ark
Name[mr]=
Name[ms]=Ark
Name[nb]=Ark
Name[nds]=Ark
Name[ne]=
Name[nl]=Ark
Name[nn]=Ark
Name[pa]=
Name[pl]=Ark
Name[pt]=Ark
Name[pt_BR]=Ark
Name[ro]=Ark
Name[ru]=Ark
Name[sk]=Ark
Name[sl]=Ark
Name[sq]=Ark
Name[sr]=Арк
Name[sr@ijekavian]=Арк
Name[sr@ijekavianlatin]=Ark
Name[sr@latin]=Ark
Name[sv]=Ark
Name[ta]=
Name[tg]=Ark
Name[th]=
Name[tr]=Ark
Name[ug]=Ark
Name[uk]=Ark
Name[uz]=Ark
Name[uz@cyrillic]=Ark
Name[vi]=Ark
Name[wa]=Ark
Name[xh]=Ark
Name[x-test]=xxArkxx
Name[zh_CN]=Ark
Name[zh_TW]=Ark
Exec=ark -caption %c %U
Icon=ark
X-DocPath=ark/index.html
Type=Application
Terminal=false
X-DBUS-StartupType=Unique
X-KDE-HasTempFileOption=true
Categories=Qt;KDE;Utility;Archiving;Compression;X-KDE-Utilities-File;
InitialPreference=3

View file

@ -0,0 +1,351 @@
[Desktop Entry]
Type=Service
ServiceTypes=KonqPopupMenu/Plugin
MimeType=all/all;
Actions=compressHere;compressAsZip;compressAsRar;compressAsTar;compressTo;
X-KDE-Submenu=Compress
X-KDE-Submenu[ar]=اضغط
X-KDE-Submenu[ast]=Comprimir
X-KDE-Submenu[bg]=Компресиране
X-KDE-Submenu[bs]=Kompresuj
X-KDE-Submenu[ca]=Compressió
X-KDE-Submenu[ca@valencia]=Compressió
X-KDE-Submenu[cs]=Zkomprimovat
X-KDE-Submenu[da]=Komprimér
X-KDE-Submenu[de]=Komprimieren
X-KDE-Submenu[el]=Συμπίεση
X-KDE-Submenu[en_GB]=Compress
X-KDE-Submenu[es]=Comprimir
X-KDE-Submenu[et]=Paki
X-KDE-Submenu[eu]=Konprimatu
X-KDE-Submenu[fi]=Pakkaa
X-KDE-Submenu[fr]=Compresser
X-KDE-Submenu[ga]=Comhbhrúigh
X-KDE-Submenu[gl]=Comprimir
X-KDE-Submenu[hne]=ि
X-KDE-Submenu[hr]=Zapakiraj
X-KDE-Submenu[hu]=Tömörítés
X-KDE-Submenu[ia]=Comprime
X-KDE-Submenu[id]=Kompres
X-KDE-Submenu[it]=Comprimi
X-KDE-Submenu[ja]=
X-KDE-Submenu[kk]=Сығу
X-KDE-Submenu[km]=
X-KDE-Submenu[ko]=
X-KDE-Submenu[lt]=Suspausti
X-KDE-Submenu[lv]=Saspiest
X-KDE-Submenu[mr]=ि
X-KDE-Submenu[nb]=Komprimer
X-KDE-Submenu[nds]=Komprimeren
X-KDE-Submenu[nl]=Comprimeren
X-KDE-Submenu[nn]=Komprimer
X-KDE-Submenu[pa]=
X-KDE-Submenu[pl]=Kompresuj
X-KDE-Submenu[pt]=Comprimir
X-KDE-Submenu[pt_BR]=Compactar
X-KDE-Submenu[ro]=Comprimă
X-KDE-Submenu[ru]=Упаковать
X-KDE-Submenu[sk]=Komprimovať
X-KDE-Submenu[sl]=Stisni
X-KDE-Submenu[sq]=Ngjish
X-KDE-Submenu[sr]=Компресуј
X-KDE-Submenu[sr@ijekavian]=Компресуј
X-KDE-Submenu[sr@ijekavianlatin]=Kompresuj
X-KDE-Submenu[sr@latin]=Kompresuj
X-KDE-Submenu[sv]=Komprimera
X-KDE-Submenu[th]=
X-KDE-Submenu[tr]=Sıkıştır
X-KDE-Submenu[ug]=پرېس
X-KDE-Submenu[uk]=Стиснути
X-KDE-Submenu[wa]=Rastrinde
X-KDE-Submenu[x-test]=xxCompressxx
X-KDE-Submenu[zh_CN]=
X-KDE-Submenu[zh_TW]=
X-KDE-StartupNotify=false
X-KDE-Priority=TopLevel
[Desktop Action compressHere]
Name=Here
Name[ar]=هنا
Name[ast]=Equí
Name[bg]=Тук
Name[bs]=ovdje
Name[ca]=Aquí
Name[ca@valencia]=Ací
Name[cs]=Sem
Name[da]=Her
Name[de]=Hier
Name[el]=Εδώ
Name[en_GB]=Here
Name[es]=Aquí
Name[et]=Siia
Name[eu]=Hemen
Name[fi]=Tähän
Name[fr]=Ici
Name[ga]=Anseo
Name[gl]=Aquí
Name[hr]=Ovdje
Name[hu]=Ide
Name[ia]=Hic
Name[id]=Di Sini
Name[is]=Hér
Name[it]=Qui
Name[ja]=
Name[kk]=Осында
Name[km]=
Name[ko]=
Name[lt]=Čia
Name[lv]=Šeit
Name[mr]=
Name[nb]=Her
Name[nds]=Hier
Name[nl]=Hier
Name[nn]=Her
Name[pa]=
Name[pl]=Tutaj
Name[pt]=Aqui
Name[pt_BR]=Aqui
Name[ro]=Aici
Name[ru]=В эту папку
Name[sk]=Sem
Name[sl]=Sem
Name[sq]=Këtu
Name[sr]=овде
Name[sr@ijekavian]=овдје
Name[sr@ijekavianlatin]=ovdje
Name[sr@latin]=ovde
Name[sv]=Här
Name[th]=
Name[tr]=Buraya
Name[ug]=بۇ جاي
Name[uk]=Сюди
Name[wa]=Cial
Name[x-test]=xxHerexx
Name[zh_CN]=
Name[zh_TW]=
Icon=ark
Exec=ark --changetofirstpath --add --autofilename tar.gz %F
[Desktop Action compressAsZip]
Name=As ZIP Archive
Name[ar]=ك أرشيف ZIP
Name[ast]=Como archivu comprimíu ZIP
Name[bg]=Като ZIP архив
Name[bs]=kao ZIP arhivu
Name[ca]=Com a arxiu ZIP
Name[ca@valencia]=Com a arxiu ZIP
Name[cs]=Jako archiv ZIP
Name[da]=Som ZIP-arkiv
Name[de]=Als ZIP-Archiv
Name[el]=Ως αρχειοθήκη ZIP
Name[en_GB]=As ZIP Archive
Name[es]=Como archivo comprimido ZIP
Name[et]=ZIP-arhiivina
Name[eu]=ZIP artxibo gisa
Name[fi]=ZIP-paketiksi
Name[fr]=Comme une archive « ZIP »
Name[ga]=Mar Chartlann ZIP
Name[gl]=Como arquivo ZIP
Name[hr]=Kao ZIP-arhiva
Name[hu]=ZIP archívumként
Name[ia]=Como archivo ZIP
Name[id]=Sebagai Arsip ZIP
Name[it]=Come archivio ZIP
Name[ja]=ZIP
Name[kk]=ZIP архиві қылып
Name[km]= ZIP
Name[ko]=ZIP
Name[lt]=ZIP archyvas
Name[lv]=Kā ZIP arhīvu
Name[mr]=ZIP
Name[nb]=Som ZIP-arkiv
Name[nds]=As Zip-Archiev
Name[nl]=Als ZIP-archief
Name[nn]=Som ZIP-arkiv
Name[pa]=ਿ
Name[pl]=Jako archiwum ZIP
Name[pt]=Como Pacote ZIP
Name[pt_BR]=Como arquivo ZIP
Name[ro]=Ca arhivă ZIP
Name[ru]=Как архив ZIP
Name[sk]=Do ZIP archívu
Name[sl]=Kot arhiv ZIP
Name[sq]=Si Arkiv ZIP
Name[sr]=као ЗИП архиву
Name[sr@ijekavian]=као ЗИП архиву
Name[sr@ijekavianlatin]=kao ZIP arhivu
Name[sr@latin]=kao ZIP arhivu
Name[sv]=Som ZIP-arkiv
Name[th]= ZIP
Name[tr]=ZIP Arşivi Olarak
Name[ug]=ZIP ئارخىپى
Name[uk]=Як архів ZIP
Name[wa]=Come årtchive ZIP
Name[x-test]=xxAs ZIP Archivexx
Name[zh_CN]= ZIP
Name[zh_TW]= ZIP
Icon=ark
Exec=ark --changetofirstpath --add --autofilename zip %F
[Desktop Action compressAsRar]
Name=As RAR Archive
Name[ar]=كأرشيف RAR
Name[ast]=Como archivu comprimíu RAR
Name[bg]=Като RAR архив
Name[bs]=kao RAR arhivu
Name[ca]=Com a arxiu RAR
Name[ca@valencia]=Com a arxiu RAR
Name[cs]=Jako archiv RAR
Name[da]=Som RAR-arkiv
Name[de]=Als RAR-Archiv
Name[el]=Ως αρχειοθήκη RAR
Name[en_GB]=As RAR Archive
Name[es]=Como archivo comprimido RAR
Name[et]=RAR-arhiivina
Name[eu]=RAR artxibo gisa
Name[fi]=RAR-paketiksi
Name[fr]=Comme une archive « RAR »
Name[ga]=Mar Chartlann RAR
Name[gl]=Como arquivo RAR
Name[hr]=Kao RAR-arhiva
Name[hu]=RAR archívumként
Name[ia]=Como archivo RAR
Name[id]=Sebagai Arsip RAR
Name[it]=Come archivio RAR
Name[ja]=RAR
Name[kk]=RAR архиві қылып
Name[km]= RAR
Name[ko]=RAR
Name[lt]=RAR archyvas
Name[lv]=Kā RAR arhīvu
Name[mr]=RAR
Name[nb]=Som RAR-arkiv
Name[nds]=As RAR-Archiev
Name[nl]=Als RAR-archief
Name[nn]=Som RAR-arkiv
Name[pa]=RAR
Name[pl]=Jako archiwum RAR
Name[pt]=Como Pacote RAR
Name[pt_BR]=Como arquivo RAR
Name[ro]=Ca arhivă RAR
Name[ru]=Как архив RAR
Name[sk]=Do RAR archívu
Name[sl]=Kot arhiv RAR
Name[sq]=Si Arkiv RAR
Name[sr]=као РАР архиву
Name[sr@ijekavian]=као РАР архиву
Name[sr@ijekavianlatin]=kao RAR arhivu
Name[sr@latin]=kao RAR arhivu
Name[sv]=Som RAR-arkiv
Name[th]= RAR
Name[tr]=RAR Arşivi Olarak
Name[ug]=RAR ئارخىپى
Name[uk]=Як архів RAR
Name[wa]=Come årtchive RAR
Name[x-test]=xxAs RAR Archivexx
Name[zh_CN]= RAR
Name[zh_TW]= RAR
Icon=ark
Exec=ark --changetofirstpath --add --autofilename rar %F
[Desktop Action compressAsTar]
Name=As TAR.GZ Archive
Name[ar]=كأرشيف TAR.GZ
Name[bg]=Като TAR.GZ архив
Name[ca]=Com a arxiu TAR.GZ
Name[cs]=Jako archiv TAR.GZ
Name[da]=Som TAR.GZ-arkiv
Name[de]=Als TAR.GZ-Archiv
Name[en_GB]=As TAR.GZ Archive
Name[es]=Como archivo comprimido TAR.GZ
Name[et]=TAR.GZ-arhiivina
Name[fi]=TAR.GZ-paketiksi
Name[fr]=Comme une archive « TAR.GZ »
Name[hu]=TAR.GZ archívumként
Name[ia]=Como archivo TAR.GZ
Name[it]=Come archivio TAR.GZ
Name[ja]=TAR.GZ
Name[ko]=TAR.GZ
Name[nb]=Som TAR.GZ-arkiv
Name[nds]=As TAR.GZ-Archiev
Name[nl]=Als TAR.GZ-archief
Name[pa]=TAR.GZ
Name[pl]=Jako archiwum TAR.GZ
Name[pt]=Como Pacote TAR.GZ
Name[pt_BR]=Como arquivo TAR.GZ
Name[ru]=Как архив TAR.GZ
Name[sk]=Ako TAR.GZ archív
Name[sl]=Kot arhiv TAR.GZ
Name[sr]=као таргз архиву
Name[sr@ijekavian]=као таргз архиву
Name[sr@ijekavianlatin]=kao targz arhivu
Name[sr@latin]=kao targz arhivu
Name[sv]=Som TAR.GZ-arkiv
Name[tr]=TAR.GZ Arşivi Olarak
Name[uk]=Як архів TAR.GZ
Name[x-test]=xxAs TAR.GZ Archivexx
Name[zh_CN]= TAR.GZ
Name[zh_TW]= TAR.GZ
Icon=ark
Exec=ark --changetofirstpath --add --autofilename tar.gz %F
[Desktop Action compressTo]
Name=Compress To...
Name[ar]=اضغط إلى...
Name[ast]=Comprimir en...
Name[bg]=Компресиране в...
Name[bs]=Kompresuj u...
Name[ca]=Comprimeix a...
Name[ca@valencia]=Comprimeix a...
Name[cs]=Zkomprimovat do...
Name[da]=Komprimér til...
Name[de]=Komprimieren nach ...
Name[el]=Συμπίεση σε...
Name[en_GB]=Compress To...
Name[es]=Comprimir en...
Name[et]=Paki asukohta...
Name[eu]=Konprimatu hona...
Name[fi]=Pakkaa
Name[fr]=Compresser vers...
Name[ga]=Comhbhrúigh Go...
Name[gl]=Comprimir en...
Name[hr]=Zapakiraj u 
Name[hu]=Tömörítés ide
Name[ia]=Comprime a...
Name[id]=Kompres ke...
Name[it]=Comprimi in...
Name[ja]=...
Name[kk]=Мыныған архивтеу,,,
Name[km]=...
Name[ko]= ...
Name[lt]=Suspausti į...
Name[lv]=Saspiest uz...
Name[mr]= ि ...
Name[nb]=Komprimer til 
Name[nds]=Komprimeren as...
Name[nl]=Comprimeren naar...
Name[nn]=Komprimer til 
Name[pa]=...
Name[pl]=Kompresuj do...
Name[pt]=Comprimir Para...
Name[pt_BR]=Compactar para...
Name[ro]=Comprimă în...
Name[ru]=Упаковать в архив...
Name[sk]=Komprimovať do...
Name[sl]=Stisni v ...
Name[sq]=Ngjishe Tek...
Name[sr]=другде...
Name[sr@ijekavian]=другдје...
Name[sr@ijekavianlatin]=drugdje...
Name[sr@latin]=drugde...
Name[sv]=Komprimera till...
Name[th]=...
Name[tr]=Farklı Sıkıştır...
Name[ug]= غا پرېسلاش
Name[uk]=Стиснути до...
Name[wa]=Rastrinde eyet rlomer...
Name[x-test]=xxCompress To...xx
Name[zh_CN]=...
Name[zh_TW]=...
Icon=ark
Exec=ark --add --changetofirstpath --dialog %F

View file

@ -0,0 +1,61 @@
[Desktop Entry]
Type=Service
X-KDE-ServiceTypes=KonqDndPopupMenu/Plugin
X-KDE-Library=libextracthere
Name=Ark Extract Here
Name[ar]=أرك استخرج هنا
Name[ast]=Estrayer equí con Ark
Name[bg]=Извличане тук
Name[bs]=Raspakuj Arkom ovde
Name[ca]=Extracció de l'Ark aquí
Name[ca@valencia]=Extracció de l'Ark ací
Name[cs]=Rozbalit Arkem sem
Name[da]=Pak ud med Ark her
Name[de]=Ark Hierher auspacken
Name[el]=Εξαγωγή Ark εδώ
Name[en_GB]=Ark Extract Here
Name[es]=Extraer aquí con Ark
Name[et]=Paki siia lahti
Name[eu]=Ark: atera hemen
Name[fi]=Ark Pura tähän
Name[fr]=Extraire un fichier « Ark » ici
Name[ga]=Bain an chartlann Ark amach agus cuir é anseo
Name[gl]=Extraer aquí con Ark
Name[hr]=Ovdje Ark-otpakiraj
Name[hu]=Kibontás ide
Name[ia]=Ark Extrahe hic
Name[id]=Ark Ekstrak di Sini
Name[it]=Ark Estrai qui
Name[ja]=Ark
Name[kk]=Мынаған тарқату
Name[km]= Ark
Name[ko]=Ark
Name[lt]=Čia išpakuoti archyvą
Name[lv]=Ark atspiest šeit
Name[mr]=
Name[nb]=Pakk ut arkivet her med Ark
Name[nds]=Hier mit Ark utpacken
Name[nl]=Ark: hier uitpakken
Name[nn]=Pakk ut med Ark her
Name[pa]=
Name[pl]=Rozpakuj tutaj
Name[pt]=Extrair para Aqui
Name[pt_BR]=Extrair com o Ark aqui
Name[ro]=Ark extrage aici
Name[ru]=Распаковать в эту папку
Name[sk]=Ark - rozbaliť sem
Name[sl]=Razširi sem z Arkom
Name[sq]=Ark Ekstrakton Këtu
Name[sr]=Распакуј Арком овде
Name[sr@ijekavian]=Распакуј Арком овдје
Name[sr@ijekavianlatin]=Raspakuj Arkom ovdje
Name[sr@latin]=Raspakuj Arkom ovde
Name[sv]=Packa upp arkiv här
Name[th]=
Name[tr]=Ark Arşivi Buraya Çıkart
Name[uk]=Видобути архів сюди з Ark
Name[wa]=Saetchî foû avou Ark cial
Name[x-test]=xxArk Extract Herexx
Name[zh_CN]=Ark
Name[zh_TW]=
MimeType=@SUPPORTED_ARK_MIMETYPES@

View file

@ -0,0 +1,238 @@
[Desktop Entry]
Type=Service
ServiceTypes=KonqPopupMenu/Plugin
MimeType=@SUPPORTED_ARK_MIMETYPES@
Actions=arkAutoExtractHere;arkExtractTo;arkExtractHere;
X-KDE-Priority=TopLevel
X-KDE-StartupNotify=false
#StartupNotify=false
X-KDE-Submenu=Extract
X-KDE-Submenu[ar]=استخرِج
X-KDE-Submenu[bg]=Извличане
X-KDE-Submenu[bs]=Raspakivanje
X-KDE-Submenu[ca]=Extreu
X-KDE-Submenu[ca@valencia]=Extrau
X-KDE-Submenu[cs]=Rozbalit
X-KDE-Submenu[da]=Pak ud
X-KDE-Submenu[de]=Entpacken
X-KDE-Submenu[el]=Εξαγωγή
X-KDE-Submenu[en_GB]=Extract
X-KDE-Submenu[es]=Extraer
X-KDE-Submenu[et]=Lahtipakkimine
X-KDE-Submenu[eu]=Atera
X-KDE-Submenu[fi]=Pura
X-KDE-Submenu[fr]=Extraire
X-KDE-Submenu[ga]=Bain amach
X-KDE-Submenu[gl]=Extraer
X-KDE-Submenu[hr]=Otpakiraj
X-KDE-Submenu[hu]=Kibontás
X-KDE-Submenu[ia]=Extrahe
X-KDE-Submenu[it]=Estrai
X-KDE-Submenu[ja]=
X-KDE-Submenu[kk]=Тарқату
X-KDE-Submenu[km]=
X-KDE-Submenu[ko]=
X-KDE-Submenu[lt]=Išpakuoti
X-KDE-Submenu[mr]=
X-KDE-Submenu[nb]=Pakk ut
X-KDE-Submenu[nds]=Utpacken
X-KDE-Submenu[nl]=Uitpakken
X-KDE-Submenu[pa]= ਿ
X-KDE-Submenu[pl]=Rozpakuj
X-KDE-Submenu[pt]=Extrair
X-KDE-Submenu[pt_BR]=Extrair
X-KDE-Submenu[ro]=Extrage
X-KDE-Submenu[ru]=Распаковать
X-KDE-Submenu[sk]=Rozbaliť
X-KDE-Submenu[sl]=Razširi
X-KDE-Submenu[sr]=Распакуј
X-KDE-Submenu[sr@ijekavian]=Распакуј
X-KDE-Submenu[sr@ijekavianlatin]=Raspakuj
X-KDE-Submenu[sr@latin]=Raspakuj
X-KDE-Submenu[sv]=Packa upp
X-KDE-Submenu[tr]=Çıkart
X-KDE-Submenu[ug]=ئايرىش
X-KDE-Submenu[uk]=Видобути
X-KDE-Submenu[x-test]=xxExtractxx
X-KDE-Submenu[zh_CN]=
X-KDE-Submenu[zh_TW]=
[Desktop Action arkExtractHere]
Name=Extract Archive Here
Name[ar]=فك الأرشيف هنا
Name[ast]=Estrayer archivu comprimíu equí
Name[bg]=Извличане тук
Name[bs]=Raspakuj arhivu ovde
Name[ca]=Extreu l'arxiu aquí
Name[ca@valencia]=Extrau l'arxiu ací
Name[cs]=Rozbalit archiv sem
Name[da]=Udpak arkiv her
Name[de]=Archiv hierher auspacken
Name[el]=Εξαγωγή αρχειοθήκης εδώ
Name[en_GB]=Extract Archive Here
Name[es]=Extraer archivo comprimido aquí
Name[et]=Paki arhiiv siia lahti
Name[eu]=Atera artxiboa hemen
Name[fi]=Pura paketti tähän
Name[fr]=Extraire l'archive ici
Name[ga]=Bain an chartlann amach agus cuir é anseo
Name[gl]=Extraer o arquivo aquí
Name[hr]=Ovdje otpakiraj arhivu
Name[hu]=Kibontás ide
Name[ia]=Extrahe archivo hic
Name[id]=Ekstrak Arsip di Sini
Name[it]=Estrai l'archivio qui
Name[ja]=
Name[kk]=Архивті мынаған тарқату
Name[km]=
Name[ko]=
Name[lt]=Čia išpakuoti archyvą
Name[lv]=Atspiest arhīvu šeit
Name[mr]=
Name[nb]=Pakk ut arkivet her
Name[nds]=Archiev hier utpacken
Name[nl]=Archief hier uitpakken
Name[nn]=Pakk ut arkivet her
Name[pa]=
Name[pl]=Rozpakuj archiwum tutaj
Name[pt]=Extrair o Pacote para Aqui
Name[pt_BR]=Extrair arquivo aqui
Name[ro]=Extrage arhiva aici
Name[ru]=В эту папку
Name[sk]=Rozbaliť archív sem
Name[sl]=Razširi arhiv sem
Name[sq]=Ekstrakto Arkivin Këtu
Name[sr]=Распакуј архиву овде
Name[sr@ijekavian]=Распакуј архиву овдје
Name[sr@ijekavianlatin]=Raspakuj arhivu ovdje
Name[sr@latin]=Raspakuj arhivu ovde
Name[sv]=Packa upp arkiv här
Name[th]=
Name[tr]=Arşivi Buraya Çıkart
Name[uk]=Видобути архів сюди
Name[wa]=Saetchî l' årtchive foû cial
Name[x-test]=xxExtract Archive Herexx
Name[zh_CN]=
Name[zh_TW]=
Icon=ark
Exec=ark --batch --autodestination %F
[Desktop Action arkExtractTo]
Name=Extract Archive To...
Name[ar]=فك الأرشيف إلى...
Name[ast]=Estrayer archivu comprimíu en...
Name[bg]=Извличане в...
Name[bs]=Raspakuj arhivu u...
Name[ca]=Extreu l'arxiu a...
Name[ca@valencia]=Extrau l'arxiu a...
Name[cs]=Rozbalit archiv do...
Name[da]=Udpak arkiv til...
Name[de]=Archiv auspacken nach ...
Name[el]=Εξαγωγή αρχειοθήκης σε...
Name[en_GB]=Extract Archive To...
Name[es]=Extraer archivo comprimido en...
Name[et]=Paki arhiiv lahti...
Name[eu]=Atera artxiboa hona...
Name[fi]=Pura paketti
Name[fr]=Extraire l'archive vers...
Name[ga]=Bain an chartlann amach go...
Name[gl]=Extraer o arquivo en...
Name[hr]=Otpakiraj arhivu u 
Name[hu]=Kibontás ide
Name[ia]=Extrahe archivo in...
Name[id]=Ekstrak Arsip ke...
Name[it]=Estrai l'archivio in...
Name[ja]=...
Name[kk]=Мынаған тарқату...
Name[km]=...
Name[ko]= ...
Name[lt]=Išpakuoti archyvą į...
Name[lv]=Atspiest arhīvu uz...
Name[mr]= ...
Name[nb]=Pakk ut arkivet til 
Name[nds]=Archiev utpacken as...
Name[nl]=Archief uitpakken naar...
Name[nn]=Pakk ut arkivet til 
Name[pa]=...
Name[pl]=Rozpakuj archiwum do...
Name[pt]=Extrair o Pacote Para...
Name[pt_BR]=Extrair arquivo para...
Name[ro]=Extrage arhiva în...
Name[ru]=Указать путь...
Name[sk]=Rozbaliť archív do...
Name[sl]=Razširi arhiv v ...
Name[sq]=Ekstrakto Arkivin Tek...
Name[sr]=Распакуј архиву у...
Name[sr@ijekavian]=Распакуј архиву у...
Name[sr@ijekavianlatin]=Raspakuj arhivu u...
Name[sr@latin]=Raspakuj arhivu u...
Name[sv]=Packa upp arkiv i...
Name[th]=...
Name[tr]=Arşivi Şuraya Çıkart...
Name[uk]=Видобути архів до...
Name[wa]=Saetchî l' årtchive foû eyet l' rilomer...
Name[x-test]=xxExtract Archive To...xx
Name[zh_CN]=...
Name[zh_TW]=...
Icon=ark
Exec=ark --batch --autodestination --dialog %F
[Desktop Action arkAutoExtractHere]
Name=Extract Archive Here, Autodetect Subfolder
Name[ar]=فك الأرشيف هنا ، تعرف تلقائي للمجلدات الفرعية
Name[ast]=Estrayer archivu comprimíu equí, autodeteutar subcarpeta
Name[bg]=Извличане тук, автоматична подпапка
Name[bs]=Raspakuj arhivu ovdje, pogodi poddirektorij
Name[ca]=Extreu l'arxiu aquí, detecta automàticament la subcarpeta
Name[ca@valencia]=Extrau l'arxiu ací, detecta automàticament la subcarpeta
Name[cs]=Rozbalit archiv sem, automaticky detekovat podsložku
Name[da]=Udpak arkiv her og find automatisk undermappe
Name[de]=Archiv hierher auspacken, Unterordner selbständig ermitteln
Name[el]=Εξαγωγή αρχειοθήκης εδώ, αυτόματη επιλογή υποφακέλου
Name[en_GB]=Extract Archive Here, Autodetect Subfolder
Name[es]=Extraer archivo comprimido aquí, autodetectar subcarpeta
Name[et]=Paki arhiiv siia lahti, tuvasta automaatselt alamkataloog
Name[eu]=Atera artxiboa hona, automatikoki detektatu azpikarpeta
Name[fi]=Pura paketti tähän, tunnista alikansio automaattisesti
Name[fr]=Extraire l'archive ici, auto-détecter les sous-dossiers
Name[ga]=Bain an chartlann amach agus cuir é anseo, braith fofhillteán go huathoibríoch
Name[gl]=Extraer o arquivo aquí, detectar o subcartafol
Name[hr]=Ovdje otpakiraj arhivu i automatski prepoznaj podmapu
Name[hu]=Kibontás ide (automatikus almappalétrehozás)
Name[ia]=Extrahe archivo hic, auto-releva subdossier
Name[id]=Ekstrak Arsip di Sini, Deteksi Otomatis Subfolder
Name[it]=Estrai l'archivio qui, autorileva la sottocartella
Name[ja]=
Name[kk]=Мынаған, ішкі қапшығына тарқату
Name[km]=
Name[ko]=
Name[lt]=Išpakuoti archyvą čia, automatiškai rasti poaplankius
Name[lv]=Atspiest arhīvu šeit, automātiska apakšmape
Name[mr]= ,
Name[nb]=Pakk ut arkivet her, finn undermapper automatisk
Name[nds]=Archiev hier utpacken, Ünnerorner autom. opdecken
Name[nl]=Archief hier uitpakken, submap autodetecteren
Name[nn]=Pakk ut arkivet her / til undermappe
Name[pa]= , -
Name[pl]=Rozpakuj archiwum tutaj, wykryj podkatalogi
Name[pt]=Extrair o Pacote Aqui com Detecção da Sub-Pasta
Name[pt_BR]=Extrair aqui detectando subpasta
Name[ro]=Extrage arhiva aici, autodetectează subdosarul
Name[ru]=Во вложенную папку
Name[sk]=Rozbaliť archív sem, automaticky určiť podpriečinok
Name[sl]=Razširi arhiv sem, samodejno zaznaj podmapo
Name[sq]=Ekstrakto Arkivin Këtu, Vetëdallo Nëndosjen
Name[sr]=Распакуј архиву овде, погоди потфасциклу
Name[sr@ijekavian]=Распакуј архиву овдје, погоди потфасциклу
Name[sr@ijekavianlatin]=Raspakuj arhivu ovdje, pogodi potfasciklu
Name[sr@latin]=Raspakuj arhivu ovde, pogodi potfasciklu
Name[sv]=Packa upp arkiv här, detektera underkatalog automatiskt
Name[th]=,
Name[tr]=Arşivi Buraya Çıkart, Alt Dizinleri Otomatik Belirle
Name[uk]=Видобути архів сюди, автоматично визначити підтеку
Name[wa]=Saetchî l' årtchive foû cial, deteccion otomatike do ridant efant
Name[x-test]=xxExtract Archive Here, Autodetect Subfolderxx
Name[zh_CN]=
Name[zh_TW]=
Icon=ark
Exec=ark --batch --autodestination --autosubfolder %F

17
ark/app/arkui.rc Normal file
View file

@ -0,0 +1,17 @@
<!DOCTYPE kpartgui>
<kpartgui name="ark" version="12">
<MenuBar>
<Menu name="file">
<DefineGroup name="file_save" append="save_merge"/>
</Menu>
<Merge/>
<Menu name="settings">
<DefineGroup name="settings_show" append="show_merge"/>
</Menu>
</MenuBar>
<ToolBar noMerge="1" name="mainToolBar">
<text>Main Toolbar</text>
<Action name="file_new"/>
<Action name="file_open"/>
</ToolBar>
</kpartgui>

292
ark/app/batchextract.cpp Normal file
View file

@ -0,0 +1,292 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009-2010 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "batchextract.h"
#include "kerfuffle/archive.h"
#include "kerfuffle/extractiondialog.h"
#include "kerfuffle/jobs.h"
#include "kerfuffle/queries.h"
#include <KDebug>
#include <KGlobal>
#include <KLocale>
#include <KMessageBox>
#include <KRun>
#include <KIO/RenameDialog>
#include <kwidgetjobtracker.h>
#include <QDir>
#include <QFileInfo>
#include <QTimer>
#include <QWeakPointer>
BatchExtract::BatchExtract()
: KCompositeJob(0),
m_autoSubfolder(false),
m_preservePaths(true),
m_openDestinationAfterExtraction(false)
{
setCapabilities(KJob::Killable);
connect(this, SIGNAL(result(KJob*)), SLOT(showFailedFiles()));
}
BatchExtract::~BatchExtract()
{
if (!m_inputs.isEmpty()) {
KIO::getJobTracker()->unregisterJob(this);
}
}
void BatchExtract::addExtraction(Kerfuffle::Archive* archive)
{
QString destination = destinationFolder();
if ((autoSubfolder()) && (!archive->isSingleFolderArchive())) {
const QDir d(destination);
QString subfolderName = archive->subfolderName();
if (d.exists(subfolderName)) {
subfolderName = KIO::RenameDialog::suggestName(destination, subfolderName);
}
d.mkdir(subfolderName);
destination += QLatin1Char( '/' ) + subfolderName;
}
Kerfuffle::ExtractionOptions options;
options[QLatin1String( "PreservePaths" )] = preservePaths();
Kerfuffle::ExtractJob *job = archive->copyFiles(QVariantList(), destination, options);
kDebug() << QString(QLatin1String( "Registering job from archive %1, to %2, preservePaths %3" )).arg(archive->fileName()).arg(destination).arg(preservePaths());
addSubjob(job);
m_fileNames[job] = qMakePair(archive->fileName(), destination);
connect(job, SIGNAL(percent(KJob*,ulong)),
this, SLOT(forwardProgress(KJob*,ulong)));
connect(job, SIGNAL(userQuery(Kerfuffle::Query*)),
this, SLOT(slotUserQuery(Kerfuffle::Query*)));
}
void BatchExtract::slotUserQuery(Kerfuffle::Query *query)
{
query->execute();
}
bool BatchExtract::autoSubfolder() const
{
return m_autoSubfolder;
}
void BatchExtract::setAutoSubfolder(bool value)
{
m_autoSubfolder = value;
}
void BatchExtract::start()
{
QTimer::singleShot(0, this, SLOT(slotStartJob()));
}
void BatchExtract::slotStartJob()
{
// If none of the archives could be loaded, there is no subjob to run
if (m_inputs.isEmpty()) {
emitResult();
return;
}
foreach(Kerfuffle::Archive *archive, m_inputs) {
addExtraction(archive);
}
KIO::getJobTracker()->registerJob(this);
emit description(this,
i18n("Extracting file..."),
qMakePair(i18n("Source archive"), m_fileNames.value(subjobs().at(0)).first),
qMakePair(i18n("Destination"), m_fileNames.value(subjobs().at(0)).second)
);
m_initialJobCount = subjobs().size();
kDebug() << "Starting first job";
subjobs().at(0)->start();
}
void BatchExtract::showFailedFiles()
{
if (!m_failedFiles.isEmpty()) {
KMessageBox::informationList(0, i18n("The following files could not be extracted:"), m_failedFiles);
}
}
void BatchExtract::slotResult(KJob *job)
{
kDebug();
// TODO: The user must be informed about which file caused the error, and that the other files
// in the queue will not be extracted.
if (job->error()) {
kDebug() << "There was en error, " << job->errorText();
setErrorText(job->errorText());
setError(job->error());
removeSubjob(job);
KMessageBox::error(NULL, job->errorText().isEmpty() ?
i18n("There was an error during extraction.") : job->errorText()
);
emitResult();
return;
} else {
removeSubjob(job);
}
if (!hasSubjobs()) {
if (openDestinationAfterExtraction()) {
KUrl destination(destinationFolder());
destination.cleanPath();
KRun::runUrl(destination, QLatin1String( "inode/directory" ), 0);
}
kDebug() << "Finished, emitting the result";
emitResult();
} else {
kDebug() << "Starting the next job";
emit description(this,
i18n("Extracting file..."),
qMakePair(i18n("Source archive"), m_fileNames.value(subjobs().at(0)).first),
qMakePair(i18n("Destination"), m_fileNames.value(subjobs().at(0)).second)
);
subjobs().at(0)->start();
}
}
void BatchExtract::forwardProgress(KJob *job, unsigned long percent)
{
Q_UNUSED(job)
int jobPart = 100 / m_initialJobCount;
setPercent(jobPart *(m_initialJobCount - subjobs().size()) + percent / m_initialJobCount);
}
bool BatchExtract::addInput(const KUrl& url)
{
Kerfuffle::Archive *archive = Kerfuffle::Archive::create(url.pathOrUrl(), this);
if ((archive == NULL) || (!QFileInfo(url.pathOrUrl()).exists())) {
m_failedFiles.append(url.fileName());
return false;
}
m_inputs.append(archive);
return true;
}
bool BatchExtract::openDestinationAfterExtraction() const
{
return m_openDestinationAfterExtraction;
}
bool BatchExtract::preservePaths() const
{
return m_preservePaths;
}
QString BatchExtract::destinationFolder() const
{
if (m_destinationFolder.isEmpty()) {
return QDir::currentPath();
} else {
return m_destinationFolder;
}
}
void BatchExtract::setDestinationFolder(const QString& folder)
{
if (QFileInfo(folder).isDir()) {
m_destinationFolder = folder;
}
}
void BatchExtract::setOpenDestinationAfterExtraction(bool value)
{
m_openDestinationAfterExtraction = value;
}
void BatchExtract::setPreservePaths(bool value)
{
m_preservePaths = value;
}
bool BatchExtract::showExtractDialog()
{
QWeakPointer<Kerfuffle::ExtractionDialog> dialog =
new Kerfuffle::ExtractionDialog;
if (m_inputs.size() > 1) {
dialog.data()->batchModeOption();
}
dialog.data()->setAutoSubfolder(autoSubfolder());
dialog.data()->setCurrentUrl(destinationFolder());
dialog.data()->setPreservePaths(preservePaths());
if (m_inputs.size() == 1) {
if (m_inputs.at(0)->isSingleFolderArchive()) {
dialog.data()->setSingleFolderArchive(true);
}
dialog.data()->setSubfolder(m_inputs.at(0)->subfolderName());
}
if (!dialog.data()->exec()) {
delete dialog.data();
return false;
}
setAutoSubfolder(dialog.data()->autoSubfolders());
setDestinationFolder(dialog.data()->destinationDirectory().pathOrUrl());
setOpenDestinationAfterExtraction(dialog.data()->openDestinationAfterExtraction());
setPreservePaths(dialog.data()->preservePaths());
delete dialog.data();
return true;
}
#include <batchextract.moc>

234
ark/app/batchextract.h Normal file
View file

@ -0,0 +1,234 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009-2010 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef BATCHEXTRACT_H
#define BATCHEXTRACT_H
#include <kcompositejob.h>
#include <KUrl>
#include <QtCore/QMap>
#include <QtCore/QPair>
#include <QtCore/QString>
#include <QtCore/QStringList>
namespace Kerfuffle
{
class Archive;
class Query;
}
/**
* This class schedules the extraction of all given compressed archives.
*
* Like AddToArchive, this class does not need the GUI to be active, and
* provides the functionality available from the --batch command-line option.
*
* @author Harald Hvaal <haraldhv@stud.ntnu.no>
*/
class BatchExtract : public KCompositeJob
{
Q_OBJECT
public:
/**
* Creates a new BatchExtract object.
*/
BatchExtract();
/**
* Destroys a BatchExtract object.
*/
virtual ~BatchExtract();
/**
* Creates an ExtractJob for the given @p archive and puts it on the queue.
*
* If necessary, the destination directory for the archive is created.
*
* @param archive The archive that will be extracted.
*
* @see setAutoSubfolder
*/
void addExtraction(Kerfuffle::Archive* archive);
/**
* A wrapper that calls slotStartJob() when the event loop has started.
*/
void start();
/**
* Whether to automatically create a folder inside the destination
* directory if the archive has more than one directory or file
* at top level.
*
* @return @c true Create the subdirectory automatically.
* @return @c false Do not create the subdirectory automatically.
*/
bool autoSubfolder() const;
/**
* Set whether a folder should be created when necessary so
* the archive is extracted to it.
*
* If set to @c true, when the archive does not consist of a
* single folder with the other files and directories inside,
* a directory will be automatically created inside the destination
* directory and the archive will be extracted there.
*
* @param value Whether to create this directory automatically
* when needed.
*/
void setAutoSubfolder(bool value);
/**
* Adds a file to the list of files that will be extracted.
*
* @param url The file that will be added to the list.
*
* @return @c true The file exists and a suitable plugin
* could be found for it.
* @return @c false The file does not exist or a suitable
* plugin could not be found.
*/
bool addInput(const KUrl& url);
/**
* Shows the extract options dialog before extracting the files.
*
* @return @c true The user has set some options and clicked OK.
* @return @c false The user has canceled extraction.
*/
bool showExtractDialog();
/**
* Returns the destination directory where the archives
* will be extracted to.
*
* @return The destination directory. If no directory has been manually
* set with setDestinationFolder, QDir::currentPath() will be
* returned.
*/
QString destinationFolder() const;
/**
* Sets the directory the archives will be extracted to.
*
* If @c setSubfolder has been used, the final destination
* directory will be the concatenation of both.
*
* If @p folder does not exist, the current destination
* folder will not change.
*
* @param folder The directory that will be used.
*/
void setDestinationFolder(const QString& folder);
/**
* Returns whether the destination folder should
* be open after all archives are extracted.
*
* @return @c true Open the destination folder.
* @return @c false Do not open the destination folder.
*/
bool openDestinationAfterExtraction() const;
/**
* Whether to open the destination folder after
* all archives are extracted.
*
* @param value Whether to open the destination.
*/
void setOpenDestinationAfterExtraction(bool value);
/**
* Whether all files should be extracted to the same directory,
* even if they're in different directories in the archive.
*
* This is also known as "flat" extraction.
*
* @return @c true Paths should be preserved.
* @return @c false Paths should be ignored.
*/
bool preservePaths() const;
/**
* Sets whether paths should be preserved during extraction.
*
* When it is set to false, all files are extracted to a single
* directory, regardless of their hierarchy in the archive.
*
* @param value Whether to preserve paths.
*/
void setPreservePaths(bool value);
private slots:
/**
* Updates the percentage of the job that has been completed.
*/
void forwardProgress(KJob *job, unsigned long percent);
/**
* Shows a dialog with a list of all the files that could not
* be successfully extracted.
*/
void showFailedFiles();
/**
* Shows an error message if the current job hasn't finished
* successfully, and advances to the next extraction job if
* there are more.
*/
void slotResult(KJob *job);
/**
* Shows a query dialog, which may happen when a file already exists.
*/
void slotUserQuery(Kerfuffle::Query *query);
/**
* Does the real work for start() and extracts all scheduled files.
*
* Each extraction job is started after the last one finishes.
* The jobs are executed in the order they were added via addInput().
*/
void slotStartJob();
private:
int m_initialJobCount;
QMap<KJob*, QPair<QString, QString> > m_fileNames;
bool m_autoSubfolder;
QList<Kerfuffle::Archive*> m_inputs;
QString m_destinationFolder;
QStringList m_failedFiles;
bool m_preservePaths;
bool m_openDestinationAfterExtraction;
};
#endif // BATCHEXTRACT_H

View file

@ -0,0 +1,82 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2009 Harald Hvaal <haraldhv (at@at) stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "extractHereDndPlugin.h"
#include "batchextract.h"
#include "kerfuffle/archive.h"
#include <KAction>
#include <KDebug>
#include <KPluginFactory>
#include <KPluginLoader>
#include <KLocale>
#include <kfileitemlistproperties.h>
K_PLUGIN_FACTORY(ExtractHerePluginFactory,
registerPlugin<ExtractHereDndPlugin>();
)
K_EXPORT_PLUGIN(ExtractHerePluginFactory("stupidname", "ark"))
void ExtractHereDndPlugin::slotTriggered()
{
kDebug() << "Preparing job";
BatchExtract *batchJob = new BatchExtract();
batchJob->setAutoSubfolder(true);
batchJob->setDestinationFolder(m_dest.pathOrUrl());
batchJob->setPreservePaths(true);
foreach(const KUrl& url, m_urls) {
batchJob->addInput(url);
}
batchJob->start();
kDebug() << "Started job";
}
ExtractHereDndPlugin::ExtractHereDndPlugin(QObject* parent, const QVariantList&)
: KonqDndPopupMenuPlugin(parent)
{
}
void ExtractHereDndPlugin::setup(const KFileItemListProperties& popupMenuInfo,
KUrl destination,
QList<QAction*>& userActions)
{
const QString extractHereMessage = i18nc("@action:inmenu Context menu shown when an archive is being drag'n'dropped", "Extract here");
if (!Kerfuffle::supportedMimeTypes().contains(popupMenuInfo.mimeType())) {
kDebug() << popupMenuInfo.mimeType() << "is not a supported mimetype";
return;
}
kDebug() << "Plugin executed";
KAction *action = new KAction(KIcon(QLatin1String("archive-extract")),
extractHereMessage, NULL);
connect(action, SIGNAL(triggered()), this, SLOT(slotTriggered()));
userActions.append(action);
m_dest = destination;
m_urls = popupMenuInfo.urlList();
}
#include "extractHereDndPlugin.moc"

View file

@ -0,0 +1,46 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2009 Harald Hvaal <haraldhv (at@at) stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef EXTRACTHEREDNDPLUGIN_H
#define EXTRACTHEREDNDPLUGIN_H
#include <kurl.h>
#include <konq_dndpopupmenuplugin.h>
class ExtractHereDndPlugin : public KonqDndPopupMenuPlugin
{
Q_OBJECT
private slots:
void slotTriggered();
public:
ExtractHereDndPlugin(QObject* parent, const QVariantList&);
virtual void setup(const KFileItemListProperties& popupMenuInfo,
KUrl destination,
QList<QAction*>& userActions);
private:
KUrl m_dest;
QList<KUrl> m_urls;
};
#endif /* EXTRACTHEREDNDPLUGIN_H */

View file

@ -0,0 +1 @@
kde4_install_icons(${ICON_INSTALL_DIR})

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 528 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

210
ark/app/main.cpp Normal file
View file

@ -0,0 +1,210 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "mainwindow.h"
#include "batchextract.h"
#include "kerfuffle/addtoarchive.h"
#include <KAboutData>
#include <KApplication>
#include <KCmdLineArgs>
#include <KDebug>
#include <KLocale>
#include <QByteArray>
#include <QFileInfo>
using Kerfuffle::AddToArchive;
int main(int argc, char **argv)
{
KAboutData aboutData("ark", 0, ki18n("Ark"),
"2.19", ki18n("KDE Archiving tool"),
KAboutData::License_GPL,
ki18n("(c) 1997-2011, The Various Ark Developers"),
KLocalizedString(),
"http://utils.kde.org/projects/ark"
);
aboutData.addAuthor(ki18n("Raphael Kubo da Costa"),
ki18n("Maintainer"),
"rakuco@FreeBSD.org");
aboutData.addAuthor(ki18n("Harald Hvaal"),
ki18n("Former Maintainer"),
"haraldhv@stud.ntnu.no");
aboutData.addAuthor(ki18n("Henrique Pinto"),
ki18n("Former Maintainer"),
"henrique.pinto@kdemail.net");
aboutData.addAuthor(ki18n("Helio Chissini de Castro"),
ki18n("Former maintainer"),
"helio@kde.org");
aboutData.addAuthor(ki18n("Georg Robbers"),
KLocalizedString(),
"Georg.Robbers@urz.uni-hd.de");
aboutData.addAuthor(ki18n("Roberto Selbach Teixeira"),
KLocalizedString(),
"maragato@kde.org");
aboutData.addAuthor(ki18n("Francois-Xavier Duranceau"),
KLocalizedString(),
"duranceau@kde.org");
aboutData.addAuthor(ki18n("Emily Ezust (Corel Corporation)"),
KLocalizedString(),
"emilye@corel.com");
aboutData.addAuthor(ki18n("Michael Jarrett (Corel Corporation)"),
KLocalizedString(),
"michaelj@corel.com");
aboutData.addAuthor(ki18n("Robert Palmbos"),
KLocalizedString(),
"palm9744@kettering.edu");
aboutData.addCredit(ki18n("Bryce Corkins"),
ki18n("Icons"),
"dbryce@attglobal.net");
aboutData.addCredit(ki18n("Liam Smit"),
ki18n("Ideas, help with the icons"),
"smitty@absamail.co.za");
aboutData.addCredit(ki18n("Andrew Smith"),
ki18n("bkisofs code"),
QByteArray(),
"http://littlesvr.ca/misc/contactandrew.php");
aboutData.setProgramIconName(QLatin1String("ark"));
KCmdLineArgs::init(argc, argv, &aboutData);
KCmdLineOptions option;
option.add("+[url]", ki18n("URL of an archive to be opened"));
option.add("d").add("dialog", ki18n("Show a dialog for specifying the options for the operation (extract/add)"));
option.add("o").add("destination <directory>", ki18n("Destination folder to extract to. Defaults to current path if not specified."));
option.add(":", ki18n("Options for adding files"));
option.add("c").add("add", ki18n("Query the user for an archive filename and add specified files to it. Quit when finished."));
option.add("t").add("add-to <filename>", ki18n("Add the specified files to 'filename'. Create archive if it does not exist. Quit when finished."));
option.add("p").add("changetofirstpath", ki18n("Change the current dir to the first entry and add all other entries relative to this one."));
option.add("f").add("autofilename <suffix>", ki18n("Automatically choose a filename, with the selected suffix (for example rar, tar.gz, zip or any other supported types)"));
option.add(":", ki18n("Options for batch extraction:"));
option.add("b").add("batch", ki18n("Use the batch interface instead of the usual dialog. This option is implied if more than one url is specified."));
option.add("e").add("autodestination", ki18n("The destination argument will be set to the path of the first file supplied."));
option.add("a").add("autosubfolder", ki18n("Archive contents will be read, and if detected to not be a single folder archive, a subfolder with the name of the archive will be created."));
KCmdLineArgs::addCmdLineOptions(option);
KCmdLineArgs::addTempFileOption();
KApplication application;
application.setQuitOnLastWindowClosed(false);
//session restoring
if (application.isSessionRestored()) {
MainWindow* window = NULL;
if (KMainWindow::canBeRestored(1)) {
window = new MainWindow;
window->restore(1);
if (!window->loadPart()) {
delete window;
window = NULL;
}
}
if (window == NULL) {
return -1;
}
} else { //new ark window (no restored session)
// open any given URLs
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
if (args->isSet("add") || args->isSet("add-to")) {
AddToArchive *addToArchiveJob = new AddToArchive;
application.connect(addToArchiveJob, SIGNAL(result(KJob*)), SLOT(quit()), Qt::QueuedConnection);
if (args->isSet("changetofirstpath")) {
addToArchiveJob->setChangeToFirstPath(true);
}
if (args->isSet("add-to")) {
addToArchiveJob->setFilename(args->getOption("add-to"));
}
if (args->isSet("autofilename")) {
addToArchiveJob->setAutoFilenameSuffix(args->getOption("autofilename"));
}
for (int i = 0; i < args->count(); ++i) {
//TODO: use the returned value here?
addToArchiveJob->addInput(args->url(i));
}
if (args->isSet("dialog")) {
if (!addToArchiveJob->showAddDialog()) {
return 0;
}
}
addToArchiveJob->start();
} else if (args->isSet("batch")) {
BatchExtract *batchJob = new BatchExtract;
application.connect(batchJob, SIGNAL(result(KJob*)), SLOT(quit()), Qt::QueuedConnection);
for (int i = 0; i < args->count(); ++i) {
batchJob->addInput(args->url(i));
}
if (args->isSet("autosubfolder")) {
kDebug() << "Setting autosubfolder";
batchJob->setAutoSubfolder(true);
}
if (args->isSet("autodestination")) {
QString autopath = QFileInfo(args->url(0).path()).path();
kDebug() << "By autodestination, setting path to " << autopath;
batchJob->setDestinationFolder(autopath);
}
if (args->isSet("destination")) {
kDebug() << "Setting destination to " << args->getOption("destination");
batchJob->setDestinationFolder(args->getOption("destination"));
}
if (args->isSet("dialog")) {
if (!batchJob->showExtractDialog()) {
return 0;
}
}
batchJob->start();
} else {
MainWindow *window = new MainWindow;
if (!window->loadPart()) { // if loading the part fails
return -1;
}
if (args->count()) {
kDebug() << "trying to open" << args->url(0);
if (args->isSet("dialog")) {
window->setShowExtractDialog(true);
}
window->openUrl(args->url(0));
}
window->show();
}
}
kDebug() << "Entering application loop";
return application.exec();
}

259
ark/app/mainwindow.cpp Normal file
View file

@ -0,0 +1,259 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2002-2003: Georg Robbers <Georg.Robbers@urz.uni-hd.de>
* Copyright (C) 2003: Helio Chissini de Castro <helio@conectiva.com>
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "mainwindow.h"
#include "kerfuffle/archive.h"
#include "part/interface.h"
#include <KPluginLoader>
#include <KPluginFactory>
#include <KMessageBox>
#include <KApplication>
#include <KLocale>
#include <KActionCollection>
#include <KStandardAction>
#include <KFileDialog>
#include <KRecentFilesAction>
#include <KGlobal>
#include <KDebug>
#include <KEditToolBar>
#include <KShortcutsDialog>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QWeakPointer>
static bool isValidArchiveDrag(const QMimeData *data)
{
return ((data->hasUrls()) && (data->urls().count() == 1));
}
MainWindow::MainWindow(QWidget *)
: KParts::MainWindow()
{
setXMLFile(QLatin1String( "arkui.rc" ));
setupActions();
statusBar();
if (!initialGeometrySet()) {
resize(640, 480);
}
setAutoSaveSettings(QLatin1String( "MainWindow" ));
setAcceptDrops(true);
}
MainWindow::~MainWindow()
{
if (m_recentFilesAction) {
m_recentFilesAction->saveEntries(KGlobal::config()->group("Recent Files"));
}
delete m_part;
m_part = 0;
}
void MainWindow::dragEnterEvent(QDragEnterEvent * event)
{
kDebug() << event;
Interface *iface = qobject_cast<Interface*>(m_part);
if (iface->isBusy()) {
return;
}
if ((event->source() == NULL) &&
(isValidArchiveDrag(event->mimeData()))) {
event->acceptProposedAction();
}
return;
}
void MainWindow::dropEvent(QDropEvent * event)
{
kDebug() << event;
Interface *iface = qobject_cast<Interface*>(m_part);
if (iface->isBusy()) {
return;
}
if ((event->source() == NULL) &&
(isValidArchiveDrag(event->mimeData()))) {
event->acceptProposedAction();
}
//TODO: if this call provokes a message box the drag will still be going
//while the box is onscreen. looks buggy, do something about it
openUrl(event->mimeData()->urls().at(0));
}
void MainWindow::dragMoveEvent(QDragMoveEvent * event)
{
kDebug() << event;
Interface *iface = qobject_cast<Interface*>(m_part);
if (iface->isBusy()) {
return;
}
if ((event->source() == NULL) &&
(isValidArchiveDrag(event->mimeData()))) {
event->acceptProposedAction();
}
}
bool MainWindow::loadPart()
{
KPluginLoader loader(QLatin1String( "arkpart" ));
KPluginFactory *factory = loader.factory();
if (factory) {
m_part = static_cast<KParts::ReadWritePart*>(factory->create<KParts::ReadWritePart>(this));
}
if (!factory || !m_part) {
KMessageBox::error(this, i18n("Unable to find Ark's KPart component, please check your installation."));
kWarning() << "Error loading Ark KPart: " << loader.errorString();
return false;
}
m_part->setObjectName( QLatin1String("ArkPart" ));
setCentralWidget(m_part->widget());
createGUI(m_part);
connect(m_part, SIGNAL(busy()), this, SLOT(updateActions()));
connect(m_part, SIGNAL(ready()), this, SLOT(updateActions()));
connect(m_part, SIGNAL(quit()), this, SLOT(quit()));
return true;
}
void MainWindow::setupActions()
{
m_newAction = KStandardAction::openNew(this, SLOT(newArchive()), actionCollection());
m_openAction = KStandardAction::open(this, SLOT(openArchive()), actionCollection());
KStandardAction::quit(this, SLOT(quit()), actionCollection());
m_recentFilesAction = KStandardAction::openRecent(this, SLOT(openUrl(KUrl)), actionCollection());
m_recentFilesAction->setToolBarMode(KRecentFilesAction::MenuMode);
m_recentFilesAction->setToolButtonPopupMode(QToolButton::DelayedPopup);
m_recentFilesAction->setIconText(i18nc("action, to open an archive", "Open"));
m_recentFilesAction->setStatusTip(i18n("Click to open an archive, click and hold to open a recently-opened archive"));
m_recentFilesAction->setToolTip(i18n("Open an archive"));
m_recentFilesAction->loadEntries(KGlobal::config()->group("Recent Files"));
connect(m_recentFilesAction, SIGNAL(triggered()),
this, SLOT(openArchive()));
createStandardStatusBarAction();
KStandardAction::configureToolbars(this, SLOT(editToolbars()), actionCollection());
KStandardAction::keyBindings(this, SLOT(editKeyBindings()), actionCollection());
}
void MainWindow::updateActions()
{
Interface *iface = qobject_cast<Interface*>(m_part);
m_newAction->setEnabled(!iface->isBusy());
m_openAction->setEnabled(!iface->isBusy());
m_recentFilesAction->setEnabled(!iface->isBusy());
}
void MainWindow::editKeyBindings()
{
KShortcutsDialog dlg(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this);
dlg.addCollection(actionCollection());
dlg.addCollection(m_part->actionCollection());
dlg.configure();
}
void MainWindow::editToolbars()
{
saveMainWindowSettings(KGlobal::config()->group(QLatin1String("MainWindow")));
QWeakPointer<KEditToolBar> dlg = new KEditToolBar(factory(), this);
dlg.data()->exec();
createGUI(m_part);
applyMainWindowSettings(KGlobal::config()->group(QLatin1String("MainWindow")));
delete dlg.data();
}
void MainWindow::openArchive()
{
Interface *iface = qobject_cast<Interface*>(m_part);
Q_ASSERT(iface);
const KUrl url = KFileDialog::getOpenUrl(KUrl("kfiledialog:///ArkOpenDir"),
Kerfuffle::supportedMimeTypes().join( QLatin1String( " " )),
this);
openUrl(url);
}
void MainWindow::openUrl(const KUrl& url)
{
if (!url.isEmpty()) {
m_part->setArguments(m_openArgs);
if (m_part->openUrl(url)) {
m_recentFilesAction->addUrl(url);
} else {
m_recentFilesAction->removeUrl(url);
}
}
}
void MainWindow::setShowExtractDialog(bool option)
{
if (option) {
m_openArgs.metaData()[QLatin1String( "showExtractDialog" )] = QLatin1String( "true" );
} else {
m_openArgs.metaData().remove(QLatin1String( "showExtractDialog" ));
}
}
void MainWindow::quit()
{
close();
}
void MainWindow::newArchive()
{
Interface *iface = qobject_cast<Interface*>(m_part);
Q_ASSERT(iface);
const QStringList mimeTypes = Kerfuffle::supportedWriteMimeTypes();
kDebug() << "Supported mimetypes are" << mimeTypes.join( QLatin1String( " " ));
const KUrl saveFileUrl =
KFileDialog::getSaveUrl(KUrl("kfiledialog:///ArkNewDir"),
mimeTypes.join(QLatin1String(" ")));
m_openArgs.metaData()[QLatin1String( "createNewArchive" )] = QLatin1String( "true" );
openUrl(saveFileUrl);
m_openArgs.metaData().remove(QLatin1String( "showExtractDialog" ));
m_openArgs.metaData().remove(QLatin1String( "createNewArchive" ));
}

66
ark/app/mainwindow.h Normal file
View file

@ -0,0 +1,66 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <KParts/MainWindow>
#include <KParts/ReadWritePart>
#include <KUrl>
class KRecentFilesAction;
class MainWindow: public KParts::MainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
bool loadPart();
void dragEnterEvent(class QDragEnterEvent * event);
void dropEvent(class QDropEvent * event);
void dragMoveEvent(class QDragMoveEvent * event);
public slots:
void openUrl(const KUrl& url);
void setShowExtractDialog(bool);
private slots:
void updateActions();
void newArchive();
void openArchive();
void quit();
void editKeyBindings();
void editToolbars();
private:
void setupActions();
KParts::ReadWritePart *m_part;
KRecentFilesAction *m_recentFilesAction;
QAction *m_openAction;
QAction *m_newAction;
KParts::OpenUrlArguments m_openArgs;
};
#endif // MAINWINDOW_H

View file

@ -0,0 +1,22 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View file

@ -0,0 +1,46 @@
# - Try to find libarchive
# Once done this will define
#
# LIBARCHIVE_FOUND - system has libarchive
# LIBARCHIVE_INCLUDE_DIR - the libarchive include directory
# LIBARCHIVE_LIBRARY - Link this to use libarchive
# HAVE_LIBARCHIVE_GZIP_SUPPORT - whether libarchive has been compiled with gzip support
# HAVE_LIBARCHIVE_LZMA_SUPPORT - whether libarchive has been compiled with lzma support
# HAVE_LIBARCHIVE_XZ_SUPPORT - whether libarchive has been compiled with xz support
# HAVE_LIBARCHIVE_RPM_SUPPORT - whether libarchive has been compiled with rpm support
# HAVE_LIBARCHIVE_CAB_SUPPORT - whether libarchive has been compiled with cab support
#
# Copyright (c) 2006, Pino Toscano, <toscano.pino@tiscali.it>
#
# Redistribution and use is allowed according to the terms of the BSD license.
# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
include(CheckLibraryExists)
if (LIBARCHIVE_LIBRARY AND LIBARCHIVE_INCLUDE_DIR)
# in cache already
set(LIBARCHIVE_FOUND TRUE)
else (LIBARCHIVE_LIBRARY AND LIBARCHIVE_INCLUDE_DIR)
find_path(LIBARCHIVE_INCLUDE_DIR archive.h
${GNUWIN32_DIR}/include
)
find_library(LIBARCHIVE_LIBRARY NAMES archive libarchive archive2 libarchive2
PATHS
${GNUWIN32_DIR}/lib
)
if (LIBARCHIVE_LIBRARY)
check_library_exists(${LIBARCHIVE_LIBRARY} archive_write_set_compression_gzip "" HAVE_LIBARCHIVE_GZIP_SUPPORT)
check_library_exists(${LIBARCHIVE_LIBRARY} archive_write_set_compression_lzma "" HAVE_LIBARCHIVE_LZMA_SUPPORT)
check_library_exists(${LIBARCHIVE_LIBRARY} archive_write_set_compression_xz "" HAVE_LIBARCHIVE_XZ_SUPPORT)
check_library_exists(${LIBARCHIVE_LIBRARY} archive_read_support_compression_rpm "" HAVE_LIBARCHIVE_RPM_SUPPORT)
check_library_exists(${LIBARCHIVE_LIBRARY} archive_read_disk_entry_from_file "" HAVE_LIBARCHIVE_READ_DISK_API)
check_library_exists(${LIBARCHIVE_LIBRARY} archive_read_support_format_cab "" HAVE_LIBARCHIVE_CAB_SUPPORT)
endif (LIBARCHIVE_LIBRARY)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibArchive DEFAULT_MSG LIBARCHIVE_INCLUDE_DIR LIBARCHIVE_LIBRARY HAVE_LIBARCHIVE_GZIP_SUPPORT)
mark_as_advanced(LIBARCHIVE_INCLUDE_DIR LIBARCHIVE_LIBRARY HAVE_LIBARCHIVE_GZIP_SUPPORT HAVE_LIBARCHIVE_LZMA_SUPPORT HAVE_LIBARCHIVE_RPM_SUPPORT HAVE_LIBARCHIVE_CAB_SUPPORT)
endif (LIBARCHIVE_LIBRARY AND LIBARCHIVE_INCLUDE_DIR)

2
ark/config.h.cmake Normal file
View file

@ -0,0 +1,2 @@
#cmakedefine HAVE_LIBARCHIVE_LZMA_SUPPORT ${HAVE_LIBARCHIVE_LZMA_SUPPORT}
#cmakedefine HAVE_LIBARCHIVE_XZ_SUPPORT ${HAVE_LIBARCHIVE_XZ_SUPPORT}

5
ark/doc/CMakeLists.txt Normal file
View file

@ -0,0 +1,5 @@
########### install files ###############
#
kde4_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR ark)
kde4_create_manpage(man-ark.1.docbook 1 INSTALL_DESTINATION ${MAN_INSTALL_DIR})

BIN
ark/doc/ark-mainwindow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

336
ark/doc/index.docbook Normal file
View file

@ -0,0 +1,336 @@
<?xml version="1.0" ?>
<!DOCTYPE book PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
<!ENTITY kappname "&ark;">
<!ENTITY package "kdeutils">
<!ENTITY % addindex "IGNORE">
<!ENTITY % English "INCLUDE" > <!-- change language only here -->
]>
<book id="ark" lang="&language;">
<bookinfo>
<title>The &ark; Handbook</title>
<authorgroup>
<author>
&Matt.Johnston; &Matt.Johnston.mail;
</author>
<!-- TRANS:ROLES_OF_TRANSLATORS -->
</authorgroup>
<copyright>
<year>2000</year>
<holder>&Matt.Johnston;</holder>
</copyright>
<copyright>
<year>2004</year>
<holder>Henrique Pinto</holder>
</copyright>
<legalnotice>&FDLNotice;</legalnotice>
<date>2013-06-21</date>
<releaseinfo>2.19 &kde; (4.11)</releaseinfo>
<abstract>
<para>&ark; is an archive manager for &kde;.</para></abstract>
<keywordset>
<keyword>KDE</keyword>
<keyword>gzip</keyword>
<keyword>gunzip</keyword>
<keyword>tar</keyword>
<keyword>archive</keyword>
<keyword>zip</keyword>
<keyword>compression</keyword>
<keyword>7z</keyword>
<keyword>kdeutils</keyword>
<keyword>ark</keyword>
</keywordset>
</bookinfo>
<chapter id="introduction">
<title>Introduction</title>
<para>&ark; is a program for viewing, extracting, creating and modifying
archives.
&ark; can handle various archive formats such as
<command>tar</command>, <command>gzip</command>,
<command>bzip2</command>, <command>zip</command>, <command>rar</command>,
<command>7zip</command>, <command>xz</command>, <command>rpm</command>,
<command>cab</command> and <command>deb</command> (support for certain archive formats depend on
the appropriate command-line programs being installed).</para>
<screenshot>
<screeninfo>&ark;'s main window</screeninfo>
<mediaobject>
<imageobject>
<imagedata fileref="ark-mainwindow.png" format="PNG"/>
</imageobject>
<textobject>
<phrase>&ark;'s main window</phrase>
</textobject>
</mediaobject>
</screenshot>
</chapter>
<chapter id="using-ark">
<title>Using &ark;</title>
<sect1 id="ark-open">
<title>Opening Archives</title>
<para>To open an archive in &ark;, choose
<guimenuitem>Open...</guimenuitem> (<keycombo
action="simul">&Ctrl;<keycap>O</keycap></keycombo>) from the <guimenu>File</guimenu>
menu. You can also open archive files by dragging and dropping from
&konqueror; or &dolphin;. Archive files should be associated with &ark;, so you can
also <mousebutton>right</mousebutton> click a file in &konqueror; or &dolphin; and
select <guimenuitem>Open with &ark;</guimenuitem> to open it or select an extract action for this file.</para>
<para>If you have enabled the information panel in the <guimenu>Settings</guimenu> menu
additional information about the selected folders or files in the archive is displayed.</para>
</sect1>
<sect1 id="ark-work-files">
<title>Working with Files</title>
<para>Once an archive has been opened, you can perform various
operations on the files inside the archive. By
selecting a file and using the <guimenu>Action</guimenu>
menu, you can choose what you want to do:</para>
<itemizedlist>
<listitem>
<para><guimenuitem>Add File...</guimenuitem> will add files from your disk to the archive.</para>
</listitem>
<listitem>
<para><guimenuitem>Add Folder...</guimenuitem> will add folders from your disk to the archive.</para>
</listitem>
<listitem>
<para><guimenuitem>Delete</guimenuitem> (<keycap>Del</keycap>) will remove the currently
selected file(s) from the archive.</para>
</listitem>
<listitem>
<para><guimenuitem>Extract...</guimenuitem> (<keycombo
action="simul">&Ctrl;<keycap>E</keycap></keycombo>) opens a submenu with previously accessed folders,
and you can select to quick extract into any of them.</para>
</listitem>
<listitem>
<para><guimenuitem>Preview</guimenuitem> will open the file in the associated viewer for that file type.</para>
<!-- File Associations Embedding first item in Service Preference Order for a file type-->
</listitem>
</itemizedlist>
</sect1>
<sect1 id="ark-extract">
<title>Extracting Archives and Removing Files</title>
<para>Once an archive has been opened in &ark;, it can be extracted. To
quick extract files from an archive, you can select
<guisubmenu>Extract</guisubmenu> from the
<guimenu>Action</guimenu> menu. This opens a submenu with previously accessed folders,
and you can select to quick extract into any of them. Clicking on the check mark at
the right side of the <guibutton>Extract</guibutton> button in the toolbar performes the same action.
</para>
<para>
To open the <guilabel>Extract</guilabel> dialog click the <guibutton>Extract</guibutton>
button in the toolbar or use the shortcut <keycombo action="simul">&Ctrl;
<keycap>E</keycap></keycombo>.
This dialog allows you to select where you will extract files to. The default
location is the folder the archive is in.</para>
<para>You can specify to extract the files into a subfolder. The default name of this
subfolder is the first part of the archive name, but you can edit it to your needs.
If you want to preserve paths when extracting, select this option.
You may also choose to open the destination folder
in &konqueror; or &dolphin; or close &ark; once the extraction is complete.</para>
<para>If a file in the archive list is highlighted, you can
also select which files to extract:</para>
<itemizedlist>
<listitem>
<para><guimenuitem>Selected files only</guimenuitem> extracts only the files
which have been selected.</para>
</listitem>
<listitem>
<para><guimenuitem>All files</guimenuitem> extracts the entire contents of the
archive.</para>
</listitem>
</itemizedlist>
<para>To extract a single file from an archive, click on the filename and drag it to the
target folder
</para>
<!--FIXME extract several files by drag + drop? - only in basyskom branch-->
<para>Extracting files from an archive does not change the archive and its contents.
Use <menuchoice><guimenu>Action</guimenu><guimenuitem>Delete</guimenuitem></menuchoice>
(<keysym>Del</keysym>) for this task.
</para>
</sect1>
<sect1 id="ark-create">
<title>Creating Archives and Adding Files</title>
<para>To create a new archive in &ark;, choose
<guimenuitem>New</guimenuitem> from the <guimenu>File</guimenu>
menu.</para>
<para>You can then type the name of the archive, with the appropriate
extension (<literal role="extension">tar.gz</literal>, <literal
role="extension">zip</literal>, <literal role="extension">bz2</literal>
&etc;) or select a supported format in the <guilabel>Filter</guilabel> combo box
and check the <guilabel>Automatically select filename extension</guilabel> option.
To add files to the archive, choose <guimenuitem>Add
File...</guimenuitem> from the <guimenu>Action</guimenu> menu. If you
want to add an entire folder to an archive, choose <guimenuitem>Add
Folder...</guimenuitem> from the <guimenu>Action</guimenu> menu.</para>
<para>An alternative way to add files to the archive is to drag one or more files
from &konqueror;, &dolphin; or the desktop into the main &ark; window, and it will
be added to the current archive.</para>
</sect1>
</chapter>
<chapter id="ark-in_filemanager">
<title>Using &ark; in the Filemanager</title>
<para>Clicking with the &RMB; on an archive in a filemanager like &dolphin; or &konqueror;
displays a context menu with an item <guimenuitem>Open with Ark</guimenuitem>.
The menu has these additional items to extract an archive using &ark;:
</para>
<itemizedlist>
<listitem>
<para><guimenuitem>Extract Archive Here, Autodetect Subfolder</guimenuitem> creates a
subfolder in the folder with the archive and extracts the folders and files into it.</para>
</listitem>
<listitem>
<para><guimenuitem>Extract Archive To...</guimenuitem> works like in &ark;.</para>
</listitem>
<listitem>
<para><guimenuitem>Extract Archive Here</guimenuitem> extracts the content of the archive into same folder.</para>
</listitem>
</itemizedlist>
<para>
&dolphin;'s or &konqueror;'s context menu for a selection displays these actions in the
<guimenu>Compress</guimenu> submenu:
</para>
<itemizedlist>
<listitem>
<para><guimenuitem>Here</guimenuitem> creates a Tar archive (gzip-compressed) in the current folder
with the extension <filename class="extension">.tar.gz</filename>.</para>
</listitem>
<listitem>
<para><guimenuitem>As ZIP Archive</guimenuitem>, <guimenuitem>As RAR Archive</guimenuitem>
or <guimenuitem>As ZIP/TAR Archive</guimenuitem> creates these archive types in the current folder.</para>
</listitem>
<listitem>
<para><guimenuitem>Compress To...</guimenuitem> opens a dialog where you can select folder, name and archive type.</para>
</listitem>
</itemizedlist>
</chapter>
<chapter id="batchmode">
<title>Advanced Batch Mode</title>
<para>&ark; has an advanced batch mode to manage archives without launching a &GUI; interface.
This mode allows you to extract or create archives and add files to them.</para>
<para>The batch mode is documented in <ulink url="man:/ark">&ark;'s man page</ulink>.
</para>
</chapter>
<chapter id="credits">
<title>Credits and License</title>
<para>&ark; is Copyright &copy; 1997-2008, The Various &ark; Developers</para>
<itemizedlist>
<title>Authors:</title>
<listitem><para>Raphael Kubo da Costa
<email>rakuco@FreeBSD.org</email></para></listitem>
<listitem><para>Harald Hvaal
<email>haraldhv@stud.ntnu.no</email></para></listitem>
<listitem><para>Helio Chissini de Castro
<email>helio@conectiva.com.br</email></para></listitem>
<listitem><para>Georg Robbers
<email>Georg.Robbers@urz.uni-hd.de</email></para></listitem>
<listitem><para>Henrique Pinto
<email>henrique.pinto@kdemail.net</email></para></listitem>
<listitem><para>Roberto Selbach Teixeira
<email>maragato@kde.org</email></para></listitem>
<listitem><para>Robert Palmbos
<email>palm9744@kettering.edu</email></para></listitem>
<listitem><para>Francois-Xavier Duranceau
<email>duranceau@kde.org</email></para></listitem>
<listitem><para>Corel Corporation (author: Emily Ezust)
<email>emilye@corel.com</email></para></listitem>
<listitem><para>Corel Corporation (author: Michael Jarrett)
<email>michaelj@corel.com</email></para></listitem>
</itemizedlist>
<para>Documentation Copyright &copy; 2000 &Matt.Johnston;
&Matt.Johnston.mail;</para>
<para>Documentation updated for &kde; 3.3 by Henrique Pinto
<email>henrique.pinto@kdemail.net</email>.</para>
<!-- TRANS:CREDIT_FOR_TRANSLATORS -->
&underFDL;
&underGPL;
</chapter>
<appendix id="installation">
<title>Installation</title>
<sect1 id="getting-ark">
<title>How to obtain &ark;</title>
&install.intro.documentation;
</sect1>
<sect1 id="requirements">
<title>Requirements</title>
<para>In order to successfully use &ark;, you need &kde;
4. &GNU; <command>tar</command> and a recent
<command>gzip</command> are also needed if you want &ark; to handle tar archives. To handle other
file formats, you need the appropriate command line programs, such as <command>bzip2</command>,
<command>zip</command>, <command>unzip</command>, <command>ar</command>, <command>rar</command>,
<command>7zip</command>, <command>xz</command>, <command>rpm</command>,
<command>cab</command> and <command>deb</command>.</para>
</sect1>
<sect1 id="compilation">
<title>Compilation and Installation</title>
&install.compile.documentation;
</sect1>
</appendix>
&documentation.index;
</book>
<!--
Local Variables:
mode: sgml
sgml-minimize-attributes: nil
sgml-general-insert-case: lower
End:
-->

208
ark/doc/man-ark.1.docbook Normal file
View file

@ -0,0 +1,208 @@
<?xml version="1.0" ?>
<!DOCTYPE refentry PUBLIC "-//KDE//DTD DocBook XML V4.2-Based Variant V1.1//EN" "dtd/kdex.dtd" [
<!ENTITY kappname "&ark;">
<!ENTITY % English "INCLUDE">
]>
<refentry lang="&language;">
<refentryinfo>
<title>&kde; User's Manual</title>
<author><firstname>Lauri</firstname><surname>Watts</surname> <contrib>Initial version of &ark; man page.</contrib></author>
<author><firstname>Raphael</firstname><surname>Kubo da Costa</surname> <contrib>Update &ark; man page.</contrib></author>
<date>2013-08-23</date>
<releaseinfo>2.19 (&kde; 4.11)</releaseinfo>
<productname>K Desktop Environment</productname>
</refentryinfo>
<refmeta>
<refentrytitle><command>ark</command></refentrytitle>
<manvolnum>1</manvolnum>
</refmeta>
<refnamediv>
<refname><command>ark</command></refname>
<refpurpose>&kde; archiving tool</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>ark</command>
<group choice="opt"><option>-b</option></group>
<group choice="opt"><option>-a</option></group>
<group choice="opt"><option>-e</option></group>
<group choice="opt"><option>-c</option></group>
<group choice="opt"><option>-f</option> <replaceable>
suffix</replaceable></group>
<group choice="opt"><option>-p</option></group>
<group choice="opt"><option>-t</option> <replaceable>
file</replaceable></group>
<group choice="opt"><option>-d</option></group>
<group choice="opt"><option>-o</option> <replaceable>
directory</replaceable></group>
<arg choice="opt">&kde; Generic Options</arg>
<arg choice="opt">&Qt; Generic Options</arg>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>&ark; is a program for managing various compressed file formats
within &kde;. Archives can be viewed, extracted, created
and modified with &ark;. The program can handle various
formats such as <application>tar</application>,
<application>gzip</application>, <application>bzip2</application>,
<application>zip</application>, <application>rar</application>
(when the appropriate libraries or command-line programs are
installed).</para>
</refsect1>
<refsect1>
<title>Operation modes</title>
<para>&ark; can be used either as a stand-alone &GUI; program as well as a
command-line program in order to perform some specific tasks.</para>
<para>If invoked without the -b (--batch) or -c (--add) options, &ark; is started
as a normal &GUI; program.</para>
<para>When the -b (--batch) option is used, &ark; can be used to extract the
contents of one or more files directly from the command-line, without
launching its &GUI;.</para>
<para>When the -c (--add) option is used, &ark; prompts for files that should
be added to a new archive or to an existing archive.</para>
<para></para>
<para></para>
</refsect1>
<refsect1>
<title>Options</title>
<variablelist>
<varlistentry>
<term><option>-d, --dialog</option></term>
<listitem>
<para>Show a dialog for specifying the options for a batch or add operation.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-o, --destination
<replaceable>directory</replaceable></option></term>
<listitem><para>Default the extraction directory to <replaceable>directory</replaceable>.
If not passed, the current path is used.</para>
</listitem>
</varlistentry>
</variablelist>
<refsect2>
<title>Options for adding files</title>
<variablelist>
<varlistentry>
<term><option>-c, --add</option></term>
<listitem>
<para>Query the user for an archive filename and add specified files to it.
Quit when finished.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-t, --add-to <replaceable>filename</replaceable></option></term>
<listitem>
<para>Add the specified files to <replaceable>filename</replaceable>. Create archive
if it does not exist. Quit when finished.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-p, --changetofirstpath</option></term>
<listitem>
<para>Change the current directory to the first entry and add all other entries relative
to this one.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-f, --autofilename <replaceable>suffix</replaceable></option></term>
<listitem>
<para>Automatically choose a filename, with the selected <replaceable>suffix</replaceable>
(for example rar, tar.gz, zip or any other supported types).</para>
</listitem>
</varlistentry>
</variablelist>
</refsect2>
<refsect2>
<title>Options for batch extraction</title>
<variablelist>
<varlistentry>
<term><option>-b, --batch</option></term>
<listitem>
<para>Use the batch interface instead of the usual dialog. This option is implied
if more than one url is specified.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-e, --autodestination</option></term>
<listitem>
<para>The destination argument will be set to the path of the first file
supplied.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><option>-a, --autosubfolder</option></term>
<listitem>
<para>Archive contents will be read, and if detected to not be a single folder archive,
a subfolder by the name of the archive will be created.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect2>
</refsect1>
<refsect1>
<title>Examples</title>
<variablelist>
<varlistentry>
<term><userinput><command>ark</command> <option>--batch</option>
<replaceable>archive.tar.bz2</replaceable></userinput></term>
<listitem>
<para>Will extract <replaceable>archive.tar.bz2</replaceable> into the current directory
without showing any &GUI;.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput><command>ark</command> <option>-b</option> <option>-d</option>
<replaceable>archive.tar.bz2</replaceable> <replaceable>archive2.zip</replaceable></userinput></term>
<listitem>
<para>Will first show an extraction options dialog and then extract both
<replaceable>archive.tar.bz2</replaceable> and <replaceable>archive2.zip</replaceable>
into the directory chosen in the dialog.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><userinput><command>ark</command> <option>--add-to</option>
<replaceable>my-archive.zip</replaceable> <replaceable>photo1.jpg</replaceable>
<replaceable>text.txt</replaceable></userinput></term>
<listitem>
<para>Will create <replaceable>my-archive.zip</replaceable> if does not exist and
then add <replaceable>photo1.jpg</replaceable> and <replaceable>text.txt</replaceable> to it.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Authors</title>
<para>&ark; is currently maintained by &Harald.Hvaal; &Harald.Hvaal.mail;
and &Raphael.Kubo.da.Costa; &Raphael.Kubo.da.Costa.mail;.</para>
<para>This man page was first written by &Lauri.Watts;
&Lauri.Watts.mail; in 2005 for &kde; 3.4, and was later updated in 2009 by
&Raphael.Kubo.da.Costa; &Raphael.Kubo.da.Costa.mail;.</para>
</refsect1>
</refentry>

View file

@ -0,0 +1,50 @@
macro_optional_find_package(QJSON)
macro_log_feature(QJSON_FOUND "qjson" "A library for processing and serializing JSON files" "http://qjson.sourceforge.net" FALSE "" "Required for compiling Ark's unit tests")
########### next target ###############
set(kerfuffle_SRCS
archive.cpp
archiveinterface.cpp
jobs.cpp
extractiondialog.cpp
adddialog.cpp
queries.cpp
addtoarchive.cpp
cliinterface.cpp
)
kde4_add_kcfg_files(kerfuffle_SRCS settings.kcfgc)
kde4_add_ui_files(kerfuffle_SRCS extractiondialog.ui adddialog.ui )
kde4_add_library(kerfuffle SHARED ${kerfuffle_SRCS})
target_link_libraries(kerfuffle ${KDE4_KFILE_LIBS} ${KDE4_KPARTS_LIBS})
if (NOT WIN32)
target_link_libraries(kerfuffle ${KDE4_KPTY_LIBS})
endif (NOT WIN32)
set_target_properties(kerfuffle PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION})
install(TARGETS kerfuffle ${INSTALL_TARGETS_DEFAULT_ARGS})
install(FILES kerfufflePlugin.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR})
install(FILES ark.kcfg DESTINATION ${KCFG_INSTALL_DIR})
if (QJSON_FOUND)
# This is a hack to make QJSON work with both 0.7.1 (the latest stable)
# and the current master (b440550), which uses a different casing for
# the CMake variables.
# It should be removed when QJSON master becomes sane and reverts the
# casing again.
if (QJSON_LIBRARIES AND QJSON_INCLUDE_DIR)
set(KERFUFFLE_QJSON_LIBRARIES "${QJSON_LIBRARIES}")
set(KERFUFFLE_QJSON_INCLUDE_DIR "${QJSON_INCLUDE_DIR}")
else (QJSON_LIBRARIES AND QJSON_INCLUDE_DIR)
set(KERFUFFLE_QJSON_LIBRARIES "${qjson_LIBRARIES}")
set(KERFUFFLE_QJSON_INCLUDE_DIR "${qjson_INCLUDE_DIR}")
endif (QJSON_LIBRARIES AND QJSON_INCLUDE_DIR)
add_subdirectory(tests)
endif (QJSON_FOUND)

135
ark/kerfuffle/adddialog.cpp Normal file
View file

@ -0,0 +1,135 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009,2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "adddialog.h"
#include "ui_adddialog.h"
#include "kerfuffle/archive.h"
#include <KConfigGroup>
#include <KFilePlacesModel>
#include <KGlobal>
#include <QFileInfo>
#include <QStandardItemModel>
namespace Kerfuffle
{
class AddDialogUI: public QWidget, public Ui::AddDialog
{
public:
AddDialogUI(QWidget *parent = 0)
: QWidget(parent) {
setupUi(this);
}
};
AddDialog::AddDialog(const QStringList& itemsToAdd,
const KUrl & startDir,
const QString & filter,
QWidget * parent,
QWidget * widget
)
: KFileDialog(startDir, filter, parent, widget)
{
setOperationMode(KFileDialog::Saving);
setMode(KFile::File | KFile::LocalOnly);
setConfirmOverwrite(true);
setCaption(i18n("Compress to Archive"));
loadConfiguration();
connect(this, SIGNAL(okClicked()), SLOT(updateDefaultMimeType()));
m_ui = new AddDialogUI(this);
mainWidget()->layout()->addWidget(m_ui);
setupIconList(itemsToAdd);
// Set up a default name if there's only one file to compress
if (itemsToAdd.size() == 1) {
const QFileInfo fileInfo(itemsToAdd.first());
const QString fileName =
fileInfo.isDir() ? fileInfo.dir().dirName() : fileInfo.baseName();
// #272914: Add an extension when it is present, otherwise KFileDialog
// will not automatically add it as baseFileName is a file which
// already exists.
setSelection(fileName + currentFilterMimeType()->mainExtension());
}
//These extra options will be implemented in a 4.2+ version of
//ark
m_ui->groupExtraOptions->hide();
}
void AddDialog::loadConfiguration()
{
m_config = KConfigGroup(KGlobal::config()->group("AddDialog"));
const QString defaultMimeType = QLatin1String( "application/x-compressed-tar" );
const QStringList writeMimeTypes = Kerfuffle::supportedWriteMimeTypes();
const QString lastMimeType = m_config.readEntry("LastMimeType", defaultMimeType);
if (writeMimeTypes.contains(lastMimeType)) {
setMimeFilter(writeMimeTypes, lastMimeType);
} else {
setMimeFilter(writeMimeTypes, defaultMimeType);
}
}
void AddDialog::setupIconList(const QStringList& itemsToAdd)
{
QStandardItemModel* listModel = new QStandardItemModel(this);
QStringList sortedList(itemsToAdd);
sortedList.sort();
Q_FOREACH(const QString& urlString, sortedList) {
KUrl url(urlString);
QStandardItem* item = new QStandardItem;
item->setText(url.fileName());
QString iconName = KMimeType::iconNameForUrl(url);
item->setIcon(KIcon(iconName));
item->setData(QVariant(url), KFilePlacesModel::UrlRole);
listModel->appendRow(item);
}
m_ui->compressList->setModel(listModel);
}
void AddDialog::updateDefaultMimeType()
{
m_config.writeEntry("LastMimeType", currentMimeFilter());
}
}
#include "adddialog.moc"

63
ark/kerfuffle/adddialog.h Normal file
View file

@ -0,0 +1,63 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ADDDIALOG_H
#define ADDDIALOG_H
#include "kerfuffle_export.h"
#include <KConfigGroup>
#include <KFileDialog>
namespace Kerfuffle
{
class KERFUFFLE_EXPORT AddDialog : public KFileDialog
{
Q_OBJECT
public:
AddDialog(const QStringList & itemsToAdd,
const KUrl & startDir,
const QString & filter,
QWidget * parent,
QWidget * widget = 0
);
private:
class AddDialogUI *m_ui;
KConfigGroup m_config;
void loadConfiguration();
void setupIconList(const QStringList& itemsToAdd);
private slots:
void updateDefaultMimeType();
};
}
#endif

113
ark/kerfuffle/adddialog.ui Normal file
View file

@ -0,0 +1,113 @@
<ui version="4.0" >
<class>AddDialog</class>
<widget class="QWidget" name="AddDialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>565</width>
<height>113</height>
</rect>
</property>
<property name="minimumSize" >
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QGroupBox" name="groupCompressFiles" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="MinimumExpanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title" >
<string>Files/Folders to Compress</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" >
<item>
<widget class="QListView" name="compressList" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="autoFillBackground" >
<bool>false</bool>
</property>
<property name="frameShape" >
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth" >
<number>0</number>
</property>
<property name="editTriggers" >
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="selectionMode" >
<enum>QAbstractItemView::NoSelection</enum>
</property>
<property name="iconSize" >
<size>
<width>32</width>
<height>32</height>
</size>
</property>
<property name="textElideMode" >
<enum>Qt::ElideMiddle</enum>
</property>
<property name="verticalScrollMode" >
<enum>QAbstractItemView::ScrollPerPixel</enum>
</property>
<property name="resizeMode" >
<enum>QListView::Adjust</enum>
</property>
<property name="viewMode" >
<enum>QListView::IconMode</enum>
</property>
<property name="uniformItemSizes" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupExtraOptions" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title" >
<string>Extra Compression Options</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<widget class="QLabel" name="label" >
<property name="text" >
<string>Easter egg for the developers:
This is where future versions will have extra compression options for the various compression interfaces.</string>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

View file

@ -0,0 +1,213 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "addtoarchive.h"
#include "adddialog.h"
#include "archive.h"
#include "jobs.h"
#include <KConfig>
#include <kdebug.h>
#include <kjobtrackerinterface.h>
#include <kmessagebox.h>
#include <klocale.h>
#include <kio/job.h>
#include <QFileInfo>
#include <QDir>
#include <QTimer>
#include <QWeakPointer>
namespace Kerfuffle
{
AddToArchive::AddToArchive(QObject *parent)
: KJob(parent), m_changeToFirstPath(false)
{
}
AddToArchive::~AddToArchive()
{
}
void AddToArchive::setAutoFilenameSuffix(const QString& suffix)
{
m_autoFilenameSuffix = suffix;
}
void AddToArchive::setChangeToFirstPath(bool value)
{
m_changeToFirstPath = value;
}
void AddToArchive::setFilename(const KUrl& path)
{
m_filename = path.pathOrUrl();
}
void AddToArchive::setMimeType(const QString & mimeType)
{
m_mimeType = mimeType;
}
bool AddToArchive::showAddDialog(void)
{
QWeakPointer<Kerfuffle::AddDialog> dialog = new Kerfuffle::AddDialog(
m_inputs, // itemsToAdd
KUrl(m_firstPath), // startDir
QLatin1String( "" ), // filter
NULL, // parent
NULL); // widget
bool ret = dialog.data()->exec();
if (ret) {
kDebug() << "Returned URL:" << dialog.data()->selectedUrl();
kDebug() << "Returned mime:" << dialog.data()->currentMimeFilter();
setFilename(dialog.data()->selectedUrl());
setMimeType(dialog.data()->currentMimeFilter());
}
delete dialog.data();
return ret;
}
bool AddToArchive::addInput(const KUrl& url)
{
m_inputs << url.pathOrUrl(
QFileInfo(url.pathOrUrl()).isDir() ?
KUrl::AddTrailingSlash :
KUrl::RemoveTrailingSlash
);
if (m_firstPath.isEmpty()) {
QString firstEntry = url.pathOrUrl(KUrl::RemoveTrailingSlash);
m_firstPath = QFileInfo(firstEntry).dir().absolutePath();
}
return true;
}
void AddToArchive::start()
{
QTimer::singleShot(0, this, SLOT(slotStartJob()));
}
// TODO: If this class should ever be called outside main.cpp,
// the returns should be preceded by emitResult().
void AddToArchive::slotStartJob(void)
{
kDebug();
Kerfuffle::CompressionOptions options;
if (!m_inputs.size()) {
KMessageBox::error(NULL, i18n("No input files were given."));
return;
}
Kerfuffle::Archive *archive;
if (!m_filename.isEmpty()) {
archive = Kerfuffle::Archive::create(m_filename, m_mimeType, this);
kDebug() << "Set filename to " << m_filename;
} else {
if (m_autoFilenameSuffix.isEmpty()) {
KMessageBox::error(NULL, i18n("You need to either supply a filename for the archive or a suffix (such as rar, tar.gz) with the <command>--autofilename</command> argument."));
return;
}
if (m_firstPath.isEmpty()) {
kDebug() << "Weird, this should not happen. no firstpath defined. aborting";
return;
}
QString base = QFileInfo(m_inputs.first()).absoluteFilePath();
if (base.endsWith(QLatin1Char('/'))) {
base.chop(1);
}
QString finalName = base + QLatin1Char( '.' ) + m_autoFilenameSuffix;
//if file already exists, append a number to the base until it doesn't
//exist
int appendNumber = 0;
while (QFileInfo(finalName).exists()) {
++appendNumber;
finalName = base + QLatin1Char( '_' ) + QString::number(appendNumber) + QLatin1Char( '.' ) + m_autoFilenameSuffix;
}
kDebug() << "Autoset filename to "<< finalName;
archive = Kerfuffle::Archive::create(finalName, m_mimeType, this);
}
if (archive == NULL) {
KMessageBox::error(NULL, i18n("Failed to create the new archive. Permissions might not be sufficient."));
return;
} else if (archive->isReadOnly()) {
KMessageBox::error(NULL, i18n("It is not possible to create archives of this type."));
return;
}
if (m_changeToFirstPath) {
if (m_firstPath.isEmpty()) {
kDebug() << "Weird, this should not happen. no firstpath defined. aborting";
return;
}
const QDir stripDir(m_firstPath);
for (int i = 0; i < m_inputs.size(); ++i) {
m_inputs[i] = stripDir.absoluteFilePath(m_inputs.at(i));
}
options[QLatin1String( "GlobalWorkDir" )] = stripDir.path();
kDebug() << "Setting GlobalWorkDir to " << stripDir.path();
}
Kerfuffle::AddJob *job =
archive->addFiles(m_inputs, options);
KIO::getJobTracker()->registerJob(job);
connect(job, SIGNAL(result(KJob*)),
this, SLOT(slotFinished(KJob*)));
job->start();
}
void AddToArchive::slotFinished(KJob *job)
{
kDebug();
if (job->error()) {
KMessageBox::error(NULL, job->errorText());
}
emitResult();
}
}

View file

@ -0,0 +1,85 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ADDTOARCHIVE_H
#define ADDTOARCHIVE_H
#include "kerfuffle_export.h"
#include <KJob>
#include <KUrl>
/**
* Compresses all input files into an archive.
*
* This is a job class that creates a compressed archive
* with all the given input files.
*
* It provides the functionality for the --add command-line
* option, and does not need the GUI to be running.
*
* @author Harald Hvaal <haraldhv@stud.ntnu.no>
*/
namespace Kerfuffle
{
class KERFUFFLE_EXPORT AddToArchive : public KJob
{
Q_OBJECT
public:
AddToArchive(QObject *parent = 0);
~AddToArchive();
bool showAddDialog();
void setPreservePaths(bool value);
void setChangeToFirstPath(bool value);
public slots:
bool addInput(const KUrl& url);
void setAutoFilenameSuffix(const QString& suffix);
void setFilename(const KUrl& path);
void setMimeType(const QString & mimeType);
void start();
private slots:
void slotFinished(KJob*);
void slotStartJob();
private:
QString m_filename;
QString m_strippedPath;
QString m_autoFilenameSuffix;
QString m_firstPath;
QString m_mimeType;
QStringList m_inputs;
bool m_changeToFirstPath;
};
}
#endif // ADDTOARCHIVE_H

333
ark/kerfuffle/archive.cpp Normal file
View file

@ -0,0 +1,333 @@
/*
* Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (c) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (c) 2009-2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "archive.h"
#include "archiveinterface.h"
#include "jobs.h"
#include <QByteArray>
#include <QEventLoop>
#include <QFile>
#include <QFileInfo>
#include <KDebug>
#include <KPluginLoader>
#include <KMimeType>
#include <KMimeTypeTrader>
#include <KServiceTypeTrader>
static bool comparePlugins(const KService::Ptr &p1, const KService::Ptr &p2)
{
return (p1->property(QLatin1String( "X-KDE-Priority" )).toInt()) > (p2->property(QLatin1String( "X-KDE-Priority" )).toInt());
}
static QString determineMimeType(const QString& filename)
{
if (!QFile::exists(filename)) {
return KMimeType::findByPath(filename)->name();
}
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
return QString();
}
const qint64 maxSize = 0x100000; // 1MB
const qint64 bufferSize = qMin(maxSize, file.size());
const QByteArray buffer = file.read(bufferSize);
return KMimeType::findByNameAndContent(filename, buffer)->name();
}
static KService::List findPluginOffers(const QString& filename, const QString& fixedMimeType)
{
KService::List offers;
const QString mimeType = fixedMimeType.isEmpty() ? determineMimeType(filename) : fixedMimeType;
if (!mimeType.isEmpty()) {
offers = KMimeTypeTrader::self()->query(mimeType, QLatin1String( "Kerfuffle/Plugin" ), QLatin1String( "(exist Library)" ));
qSort(offers.begin(), offers.end(), comparePlugins);
}
return offers;
}
namespace Kerfuffle
{
Archive *Archive::create(const QString &fileName, QObject *parent)
{
return create(fileName, QString(), parent);
}
Archive *Archive::create(const QString &fileName, const QString &fixedMimeType, QObject *parent)
{
qRegisterMetaType<ArchiveEntry>("ArchiveEntry");
const KService::List offers = findPluginOffers(fileName, fixedMimeType);
if (offers.isEmpty()) {
kDebug() << "Could not find a plugin to handle" << fileName;
return NULL;
}
const QString pluginName = offers.first()->library();
kDebug() << "Loading plugin" << pluginName;
KPluginFactory * const factory = KPluginLoader(pluginName).factory();
if (!factory) {
kDebug() << "Invalid plugin factory for" << pluginName;
return NULL;
}
QVariantList args;
args.append(QVariant(QFileInfo(fileName).absoluteFilePath()));
ReadOnlyArchiveInterface * const iface = factory->create<ReadOnlyArchiveInterface>(0, args);
if (!iface) {
kDebug() << "Could not create plugin instance" << pluginName << "for" << fileName;
return NULL;
}
return new Archive(iface, parent);
}
Archive::Archive(ReadOnlyArchiveInterface *archiveInterface, QObject *parent)
: QObject(parent),
m_iface(archiveInterface),
m_hasBeenListed(false),
m_isPasswordProtected(false),
m_isSingleFolderArchive(false)
{
Q_ASSERT(archiveInterface);
archiveInterface->setParent(this);
}
Archive::~Archive()
{
}
bool Archive::isReadOnly() const
{
return m_iface->isReadOnly();
}
KJob* Archive::open()
{
return 0;
}
KJob* Archive::create()
{
return 0;
}
ListJob* Archive::list()
{
ListJob *job = new ListJob(m_iface, this);
job->setAutoDelete(false);
//if this job has not been listed before, we grab the opportunity to
//collect some information about the archive
if (!m_hasBeenListed) {
connect(job, SIGNAL(result(KJob*)),
this, SLOT(onListFinished(KJob*)));
}
return job;
}
DeleteJob* Archive::deleteFiles(const QList<QVariant> & files)
{
if (m_iface->isReadOnly()) {
return 0;
}
DeleteJob *newJob = new DeleteJob(files, static_cast<ReadWriteArchiveInterface*>(m_iface), this);
return newJob;
}
AddJob* Archive::addFiles(const QStringList & files, const CompressionOptions& options)
{
Q_ASSERT(!m_iface->isReadOnly());
AddJob *newJob = new AddJob(files, options, static_cast<ReadWriteArchiveInterface*>(m_iface), this);
connect(newJob, SIGNAL(result(KJob*)),
this, SLOT(onAddFinished(KJob*)));
return newJob;
}
ExtractJob* Archive::copyFiles(const QList<QVariant> & files, const QString & destinationDir, ExtractionOptions options)
{
ExtractionOptions newOptions = options;
if (isPasswordProtected()) {
newOptions[QLatin1String( "PasswordProtectedHint" )] = true;
}
ExtractJob *newJob = new ExtractJob(files, destinationDir, newOptions, m_iface, this);
return newJob;
}
QString Archive::fileName() const
{
return m_iface->filename();
}
void Archive::onAddFinished(KJob* job)
{
//if the archive was previously a single folder archive and an add job
//has successfully finished, then it is no longer a single folder
//archive (for the current implementation, which does not allow adding
//folders/files other places than the root.
//TODO: handle the case of creating a new file and singlefolderarchive
//then.
if (m_isSingleFolderArchive && !job->error()) {
m_isSingleFolderArchive = false;
}
}
void Archive::onListFinished(KJob* job)
{
ListJob *ljob = qobject_cast<ListJob*>(job);
m_extractedFilesSize = ljob->extractedFilesSize();
m_isSingleFolderArchive = ljob->isSingleFolderArchive();
m_isPasswordProtected = ljob->isPasswordProtected();
m_subfolderName = ljob->subfolderName();
if (m_subfolderName.isEmpty()) {
QFileInfo fi(fileName());
QString base = fi.completeBaseName();
//special case for tar.gz/bzip2 files
if (base.right(4).toUpper() == QLatin1String(".TAR")) {
base.chop(4);
}
m_subfolderName = base;
}
m_hasBeenListed = true;
}
void Archive::listIfNotListed()
{
if (!m_hasBeenListed) {
KJob *job = list();
connect(job, SIGNAL(userQuery(Kerfuffle::Query*)),
SLOT(onUserQuery(Kerfuffle::Query*)));
QEventLoop loop(this);
connect(job, SIGNAL(result(KJob*)),
&loop, SLOT(quit()));
job->start();
loop.exec(); // krazy:exclude=crashy
}
}
void Archive::onUserQuery(Query* query)
{
query->execute();
}
bool Archive::isSingleFolderArchive()
{
listIfNotListed();
return m_isSingleFolderArchive;
}
bool Archive::isPasswordProtected()
{
listIfNotListed();
return m_isPasswordProtected;
}
QString Archive::subfolderName()
{
listIfNotListed();
return m_subfolderName;
}
void Archive::setPassword(const QString &password)
{
m_iface->setPassword(password);
}
QStringList supportedMimeTypes()
{
const QLatin1String constraint("(exist Library)");
const QLatin1String basePartService("Kerfuffle/Plugin");
const KService::List offers = KServiceTypeTrader::self()->query(basePartService, constraint);
KService::List::ConstIterator it = offers.constBegin();
KService::List::ConstIterator itEnd = offers.constEnd();
QStringList supported;
for (; it != itEnd; ++it) {
KService::Ptr service = *it;
QStringList mimeTypes = service->serviceTypes();
foreach (const QString& mimeType, mimeTypes) {
if (mimeType != basePartService && !supported.contains(mimeType)) {
supported.append(mimeType);
}
}
}
kDebug() << "Returning" << supported;
return supported;
}
QStringList supportedWriteMimeTypes()
{
const QLatin1String constraint("(exist Library) and ([X-KDE-Kerfuffle-ReadWrite] == true)");
const QLatin1String basePartService("Kerfuffle/Plugin");
const KService::List offers = KServiceTypeTrader::self()->query(basePartService, constraint);
KService::List::ConstIterator it = offers.constBegin();
KService::List::ConstIterator itEnd = offers.constEnd();
QStringList supported;
for (; it != itEnd; ++it) {
KService::Ptr service = *it;
QStringList mimeTypes = service->serviceTypes();
foreach (const QString& mimeType, mimeTypes) {
if (mimeType != basePartService && !supported.contains(mimeType)) {
supported.append(mimeType);
}
}
}
kDebug() << "Returning" << supported;
return supported;
}
} // namespace Kerfuffle

149
ark/kerfuffle/archive.h Normal file
View file

@ -0,0 +1,149 @@
/*
* Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (c) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (c) 2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCHIVE_H
#define ARCHIVE_H
#include "kerfuffle_export.h"
#include <QString>
#include <QStringList>
#include <QHash>
class KJob;
namespace Kerfuffle
{
class ListJob;
class ExtractJob;
class DeleteJob;
class AddJob;
class Query;
class ReadOnlyArchiveInterface;
/**
* Meta data related to one entry in a compressed archive.
*
* When creating a plugin, information about every single entry in
* an archive is contained in an ArchiveEntry, and metadata
* is set with the entries in this enum.
*
* Please notice that not all archive formats support all the properties
* below, so set those that are available.
*/
enum EntryMetaDataType {
FileName = 0, /**< The entry's file name */
InternalID, /**< The entry's ID for Ark's internal manipulation */
Permissions, /**< The entry's permissions */
Owner, /**< The user the entry belongs to */
Group, /**< The user group the entry belongs to */
Size, /**< The entry's original size */
CompressedSize, /**< The compressed size for the entry */
Link, /**< The entry is a symbolic link */
Ratio, /**< The compression ratio for the entry */
CRC, /**< The entry's CRC */
Method, /**< The compression method used on the entry */
Version, /**< The archiver version needed to extract the entry */
Timestamp, /**< The timestamp for the current entry */
IsDirectory, /**< The entry is a directory */
Comment,
IsPasswordProtected, /**< The entry is password-protected */
Custom = 1048576
};
typedef QHash<int, QVariant> ArchiveEntry;
/**
These are the extra options for doing the compression. Naming convention
is CamelCase with either Global, or the compression type (such as Zip,
Rar, etc), followed by the property name used
*/
typedef QHash<QString, QVariant> CompressionOptions;
typedef QHash<QString, QVariant> ExtractionOptions;
class KERFUFFLE_EXPORT Archive : public QObject
{
Q_OBJECT
public:
static Archive *create(const QString &fileName, QObject *parent = 0);
static Archive *create(const QString &fileName, const QString &fixedMimeType, QObject *parent = 0);
~Archive();
QString fileName() const;
bool isReadOnly() const;
KJob* open();
KJob* create();
ListJob* list();
DeleteJob* deleteFiles(const QList<QVariant> & files);
/**
* Compression options that should be handled by all interfaces:
*
* GlobalWorkDir - Change to this dir before adding the new files.
* The path names should then be added relative to this directory.
*
* TODO: find a way to actually add files to specific locations in
* the archive
* (not supported yet) GlobalPathInArchive - a path relative to the
* archive root where the files will be added under
*
*/
AddJob* addFiles(const QStringList & files, const CompressionOptions& options = CompressionOptions());
ExtractJob* copyFiles(const QList<QVariant> & files, const QString & destinationDir, ExtractionOptions options = ExtractionOptions());
bool isSingleFolderArchive();
QString subfolderName();
bool isPasswordProtected();
void setPassword(const QString &password);
private slots:
void onListFinished(KJob*);
void onAddFinished(KJob*);
void onUserQuery(Kerfuffle::Query*);
private:
Archive(ReadOnlyArchiveInterface *archiveInterface, QObject *parent = 0);
void listIfNotListed();
ReadOnlyArchiveInterface *m_iface;
bool m_hasBeenListed;
bool m_isPasswordProtected;
bool m_isSingleFolderArchive;
QString m_subfolderName;
qlonglong m_extractedFilesSize;
};
KERFUFFLE_EXPORT QStringList supportedMimeTypes();
KERFUFFLE_EXPORT QStringList supportedWriteMimeTypes();
} // namespace Kerfuffle
#endif // ARCHIVE_H

View file

@ -0,0 +1,122 @@
/*
* Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (c) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (c) 2009-2012 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "archiveinterface.h"
#include <kdebug.h>
#include <kfileitem.h>
#include <QFileInfo>
#include <QDir>
namespace Kerfuffle
{
ReadOnlyArchiveInterface::ReadOnlyArchiveInterface(QObject *parent, const QVariantList & args)
: QObject(parent), m_waitForFinishedSignal(false)
{
kDebug();
m_filename = args.first().toString();
}
ReadOnlyArchiveInterface::~ReadOnlyArchiveInterface()
{
}
QString ReadOnlyArchiveInterface::filename() const
{
return m_filename;
}
bool ReadOnlyArchiveInterface::isReadOnly() const
{
return true;
}
bool ReadOnlyArchiveInterface::open()
{
return true;
}
void ReadOnlyArchiveInterface::setPassword(const QString &password)
{
m_password = password;
}
QString ReadOnlyArchiveInterface::password() const
{
return m_password;
}
bool ReadOnlyArchiveInterface::doKill()
{
//default implementation
return false;
}
bool ReadOnlyArchiveInterface::doSuspend()
{
//default implementation
return false;
}
bool ReadOnlyArchiveInterface::doResume()
{
//default implementation
return false;
}
ReadWriteArchiveInterface::ReadWriteArchiveInterface(QObject *parent, const QVariantList & args)
: ReadOnlyArchiveInterface(parent, args)
{
}
ReadWriteArchiveInterface::~ReadWriteArchiveInterface()
{
}
bool ReadOnlyArchiveInterface::waitForFinishedSignal()
{
return m_waitForFinishedSignal;
}
void ReadOnlyArchiveInterface::setWaitForFinishedSignal(bool value)
{
m_waitForFinishedSignal = value;
}
bool ReadWriteArchiveInterface::isReadOnly() const
{
QFileInfo fileInfo(filename());
if (fileInfo.exists()) {
return ! fileInfo.isWritable();
} else {
return !fileInfo.dir().exists(); // TODO: Should also check if we can create a file in that directory
}
}
} // namespace Kerfuffle
#include "archiveinterface.moc"

View file

@ -0,0 +1,138 @@
/*
* Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (c) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (c) 2009-2012 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef ARCHIVEINTERFACE_H
#define ARCHIVEINTERFACE_H
#include "archive.h"
#include "kerfuffle_export.h"
#include <QObject>
#include <QStringList>
#include <QString>
#include <QVariantList>
namespace Kerfuffle
{
class Query;
class KERFUFFLE_EXPORT ReadOnlyArchiveInterface: public QObject
{
Q_OBJECT
public:
explicit ReadOnlyArchiveInterface(QObject *parent, const QVariantList & args);
virtual ~ReadOnlyArchiveInterface();
/**
* Returns the filename of the archive currently being handled.
*/
QString filename() const;
/**
* Returns whether the file can only be read.
*
* @return @c true The file cannot be written.
* @return @c false The file can be read and written.
*/
virtual bool isReadOnly() const;
virtual bool open();
/**
* List archive contents.
* This runs the process of reading archive contents.
* When subclassing, you can block as long as you need, the function runs
* in its own thread.
* @returns whether the listing succeeded.
* @note If returning false, make sure to emit the error() signal beforewards to notify
* the user of the error condition.
*/
virtual bool list() = 0;
void setPassword(const QString &password);
/**
* Extract files from archive.
* Globally recognized extraction options:
* @li PreservePaths - preserve file paths (extract flat if false)
* @li RootNode - node in the archive which will correspond to the @arg destinationDirectory
* When subclassing, you can block as long as you need, the function runs
* in its own thread.
* @returns whether the listing succeeded.
* @note If returning false, make sure to emit the error() signal beforewards to notify
* the user of the error condition.
*/
virtual bool copyFiles(const QList<QVariant> & files, const QString & destinationDirectory, ExtractionOptions options) = 0;
bool waitForFinishedSignal();
virtual bool doKill();
virtual bool doSuspend();
virtual bool doResume();
signals:
void error(const QString &message, const QString &details = QString());
void entry(const ArchiveEntry &archiveEntry);
void entryRemoved(const QString &path);
void progress(double progress);
void info(const QString &info);
void finished(bool result);
void userQuery(Query *query);
protected:
QString password() const;
/**
* Setting this option to true will not exit the thread with the
* exit of the various functions, but rather when finished(bool) is
* called. Doing this one can use the event loop easily while doing
* the operation.
*/
void setWaitForFinishedSignal(bool value);
private:
QString m_filename;
QString m_password;
bool m_waitForFinishedSignal;
};
class KERFUFFLE_EXPORT ReadWriteArchiveInterface: public ReadOnlyArchiveInterface
{
Q_OBJECT
public:
explicit ReadWriteArchiveInterface(QObject *parent, const QVariantList & args);
virtual ~ReadWriteArchiveInterface();
virtual bool isReadOnly() const;
//see archive.h for a list of what the compressionoptions might
//contain
virtual bool addFiles(const QStringList & files, const CompressionOptions& options) = 0;
virtual bool deleteFiles(const QList<QVariant> & files) = 0;
};
} // namespace Kerfuffle
#endif // ARCHIVEINTERFACE_H

25
ark/kerfuffle/ark.kcfg Normal file
View file

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
<kcfgfile name="arkrc"/>
<group name="Extraction">
<entry name="openDestinationFolderAfterExtraction" type="Bool">
<label>Open destination folder after extraction</label>
<default>false</default>
</entry>
<entry name="closeAfterExtraction" type="Bool">
<label>Close Ark after extraction</label>
<default>false</default>
</entry>
<entry name="preservePaths" type="Bool">
<label>Preserve paths when extracting</label>
<default>true</default>
</entry>
</group>
<group name="MainWindow">
<entry name="splitterSizes" type="IntList" />
<entry name="splitterSizesWithBothWidgets" type="IntList" />
</group>
</kcfg>

View file

@ -0,0 +1,751 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2009 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009-2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "cliinterface.h"
#include "queries.h"
#ifdef Q_OS_WIN
# include <KProcess>
#else
# include <KPtyDevice>
# include <KPtyProcess>
#endif
#include <KStandardDirs>
#include <KDebug>
#include <KLocale>
#include <QApplication>
#include <QDateTime>
#include <QDir>
#include <QEventLoop>
#include <QFile>
#include <QProcess>
#include <QThread>
#include <QTimer>
namespace Kerfuffle
{
CliInterface::CliInterface(QObject *parent, const QVariantList & args)
: ReadWriteArchiveInterface(parent, args),
m_process(0),
m_listEmptyLines(false),
m_abortingOperation(false)
{
//because this interface uses the event loop
setWaitForFinishedSignal(true);
if (QMetaType::type("QProcess::ExitStatus") == 0) {
qRegisterMetaType<QProcess::ExitStatus>("QProcess::ExitStatus");
}
}
void CliInterface::cacheParameterList()
{
m_param = parameterList();
Q_ASSERT(m_param.contains(ExtractProgram));
Q_ASSERT(m_param.contains(ListProgram));
Q_ASSERT(m_param.contains(PreservePathSwitch));
Q_ASSERT(m_param.contains(FileExistsExpression));
Q_ASSERT(m_param.contains(FileExistsInput));
}
CliInterface::~CliInterface()
{
Q_ASSERT(!m_process);
}
void CliInterface::setListEmptyLines(bool emptyLines)
{
m_listEmptyLines = emptyLines;
}
bool CliInterface::list()
{
cacheParameterList();
m_operationMode = List;
QStringList args = m_param.value(ListArgs).toStringList();
substituteListVariables(args);
if (!runProcess(m_param.value(ListProgram).toStringList(), args)) {
failOperation();
return false;
}
return true;
}
bool CliInterface::copyFiles(const QList<QVariant> & files, const QString & destinationDirectory, ExtractionOptions options)
{
kDebug();
cacheParameterList();
m_operationMode = Copy;
//start preparing the argument list
QStringList args = m_param.value(ExtractArgs).toStringList();
//now replace the various elements in the list
for (int i = 0; i < args.size(); ++i) {
QString argument = args.at(i);
kDebug() << "Processing argument " << argument;
if (argument == QLatin1String( "$Archive" )) {
args[i] = filename();
}
if (argument == QLatin1String( "$PreservePathSwitch" )) {
QStringList replacementFlags = m_param.value(PreservePathSwitch).toStringList();
Q_ASSERT(replacementFlags.size() == 2);
bool preservePaths = options.value(QLatin1String( "PreservePaths" )).toBool();
QString theReplacement;
if (preservePaths) {
theReplacement = replacementFlags.at(0);
} else {
theReplacement = replacementFlags.at(1);
}
if (theReplacement.isEmpty()) {
args.removeAt(i);
--i; //decrement to compensate for the variable we removed
} else {
//but in this case we don't have to decrement, we just
//replace it
args[i] = theReplacement;
}
}
if (argument == QLatin1String( "$PasswordSwitch" )) {
//if the PasswordSwitch argument has been added, we at least
//assume that the format of the switch has been added as well
Q_ASSERT(m_param.contains(PasswordSwitch));
//we will decrement i afterwards
args.removeAt(i);
//if we get a hint about this being a password protected archive, ask about
//the password in advance.
if ((options.value(QLatin1String("PasswordProtectedHint")).toBool()) &&
(password().isEmpty())) {
kDebug() << "Password hint enabled, querying user";
Kerfuffle::PasswordNeededQuery query(filename());
emit userQuery(&query);
query.waitForResponse();
if (query.responseCancelled()) {
failOperation();
return false;
}
setPassword(query.password());
}
QString pass = password();
if (!pass.isEmpty()) {
QStringList theSwitch = m_param.value(PasswordSwitch).toStringList();
for (int j = 0; j < theSwitch.size(); ++j) {
//get the argument part
QString newArg = theSwitch.at(j);
//substitute the $Path
newArg.replace(QLatin1String( "$Password" ), pass);
//put it in the arg list
args.insert(i + j, newArg);
++i;
}
}
--i; //decrement to compensate for the variable we replaced
}
if (argument == QLatin1String( "$RootNodeSwitch" )) {
//if the RootNodeSwitch argument has been added, we at least
//assume that the format of the switch has been added as well
Q_ASSERT(m_param.contains(RootNodeSwitch));
//we will decrement i afterwards
args.removeAt(i);
QString rootNode;
if (options.contains(QLatin1String( "RootNode" ))) {
rootNode = options.value(QLatin1String( "RootNode" )).toString();
kDebug() << "Set root node " << rootNode;
}
if (!rootNode.isEmpty()) {
QStringList theSwitch = m_param.value(RootNodeSwitch).toStringList();
for (int j = 0; j < theSwitch.size(); ++j) {
//get the argument part
QString newArg = theSwitch.at(j);
//substitute the $Path
newArg.replace(QLatin1String( "$Path" ), rootNode);
//put it in the arg list
args.insert(i + j, newArg);
++i;
}
}
--i; //decrement to compensate for the variable we replaced
}
if (argument == QLatin1String( "$Files" )) {
args.removeAt(i);
for (int j = 0; j < files.count(); ++j) {
args.insert(i + j, escapeFileName(files.at(j).toString()));
++i;
}
--i;
}
}
kDebug() << "Setting current dir to " << destinationDirectory;
QDir::setCurrent(destinationDirectory);
if (!runProcess(m_param.value(ExtractProgram).toStringList(), args)) {
failOperation();
return false;
}
return true;
}
bool CliInterface::addFiles(const QStringList & files, const CompressionOptions& options)
{
cacheParameterList();
m_operationMode = Add;
const QString globalWorkDir = options.value(QLatin1String( "GlobalWorkDir" )).toString();
const QDir workDir = globalWorkDir.isEmpty() ? QDir::current() : QDir(globalWorkDir);
if (!globalWorkDir.isEmpty()) {
kDebug() << "GlobalWorkDir is set, changing dir to " << globalWorkDir;
QDir::setCurrent(globalWorkDir);
}
//start preparing the argument list
QStringList args = m_param.value(AddArgs).toStringList();
//now replace the various elements in the list
for (int i = 0; i < args.size(); ++i) {
const QString argument = args.at(i);
kDebug() << "Processing argument " << argument;
if (argument == QLatin1String( "$Archive" )) {
args[i] = filename();
}
if (argument == QLatin1String( "$Files" )) {
args.removeAt(i);
for (int j = 0; j < files.count(); ++j) {
// #191821: workDir must be used instead of QDir::current()
// so that symlinks aren't resolved automatically
// TODO: this kind of call should be moved upwards in the
// class hierarchy to avoid code duplication
const QString relativeName =
workDir.relativeFilePath(files.at(j));
args.insert(i + j, relativeName);
++i;
}
--i;
}
}
if (!runProcess(m_param.value(AddProgram).toStringList(), args)) {
failOperation();
return false;
}
return true;
}
bool CliInterface::deleteFiles(const QList<QVariant> & files)
{
cacheParameterList();
m_operationMode = Delete;
//start preparing the argument list
QStringList args = m_param.value(DeleteArgs).toStringList();
//now replace the various elements in the list
for (int i = 0; i < args.size(); ++i) {
QString argument = args.at(i);
kDebug() << "Processing argument " << argument;
if (argument == QLatin1String( "$Archive" )) {
args[i] = filename();
} else if (argument == QLatin1String( "$Files" )) {
args.removeAt(i);
for (int j = 0; j < files.count(); ++j) {
args.insert(i + j, escapeFileName(files.at(j).toString()));
++i;
}
--i;
}
}
m_removedFiles = files;
if (!runProcess(m_param.value(DeleteProgram).toStringList(), args)) {
failOperation();
return false;
}
return true;
}
bool CliInterface::runProcess(const QStringList& programNames, const QStringList& arguments)
{
QString programPath;
for (int i = 0; i < programNames.count(); i++) {
programPath = KStandardDirs::findExe(programNames.at(i));
if (!programPath.isEmpty())
break;
}
if (programPath.isEmpty()) {
const QString names = programNames.join(QLatin1String(", "));
emit error(i18ncp("@info", "Failed to locate program <filename>%2</filename> on disk.",
"Failed to locate programs <filename>%2</filename> on disk.", programNames.count(), names));
emit finished(false);
return false;
}
kDebug() << "Executing" << programPath << arguments;
if (m_process) {
m_process->waitForFinished();
delete m_process;
}
#ifdef Q_OS_WIN
m_process = new KProcess;
#else
m_process = new KPtyProcess;
m_process->setPtyChannels(KPtyProcess::StdinChannel);
QEventLoop loop;
connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), &loop, SLOT(quit()), Qt::DirectConnection);
#endif
m_process->setOutputChannelMode(KProcess::MergedChannels);
m_process->setNextOpenMode(QIODevice::ReadWrite | QIODevice::Unbuffered | QIODevice::Text);
m_process->setProgram(programPath, arguments);
connect(m_process, SIGNAL(readyReadStandardOutput()), SLOT(readStdout()), Qt::DirectConnection);
connect(m_process, SIGNAL(finished(int,QProcess::ExitStatus)), SLOT(processFinished(int,QProcess::ExitStatus)), Qt::DirectConnection);
m_stdOutData.clear();
m_process->start();
#ifdef Q_OS_WIN
bool ret = m_process->waitForFinished(-1);
#else
bool ret = (loop.exec(QEventLoop::WaitForMoreEvents | QEventLoop::ExcludeUserInputEvents) == 0);
#endif
Q_ASSERT(!m_process);
return ret;
}
void CliInterface::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
kDebug() << exitCode << exitStatus;
//if the m_process pointer is gone, then there is nothing to worry
//about here
if (!m_process) {
return;
}
if (m_operationMode == Delete) {
foreach(const QVariant& v, m_removedFiles) {
emit entryRemoved(v.toString());
}
}
//handle all the remaining data in the process
readStdout(true);
delete m_process;
m_process = 0;
emit progress(1.0);
if (m_operationMode == Add) {
list();
return;
}
//and we're finished
emit finished(true);
}
void CliInterface::failOperation()
{
// TODO: Would be good to unit test #304764/#304178.
kDebug();
doKill();
}
void CliInterface::readStdout(bool handleAll)
{
//when hacking this function, please remember the following:
//- standard output comes in unpredictable chunks, this is why
//you can never know if the last part of the output is a complete line or not
//- console applications are not really consistent about what
//characters they send out (newline, backspace, carriage return,
//etc), so keep in mind that this function is supposed to handle
//all those special cases and be the lowest common denominator
if (m_abortingOperation)
return;
Q_ASSERT(m_process);
if (!m_process->bytesAvailable()) {
//if process has no more data, we can just bail out
return;
}
//if the process is still not finished (m_process is appearantly not
//set to NULL if here), then the operation should definitely not be in
//the main thread as this would freeze everything. assert this.
Q_ASSERT(QThread::currentThread() != QApplication::instance()->thread());
QByteArray dd = m_process->readAllStandardOutput();
m_stdOutData += dd;
QList<QByteArray> lines = m_stdOutData.split('\n');
//The reason for this check is that archivers often do not end
//queries (such as file exists, wrong password) on a new line, but
//freeze waiting for input. So we check for errors on the last line in
//all cases.
// TODO: QLatin1String() might not be the best choice here.
// The call to handleLine() at the end of the method uses
// QString::fromLocal8Bit(), for example.
// TODO: The same check methods are called in handleLine(), this
// is suboptimal.
bool foundErrorMessage =
(checkForErrorMessage(QLatin1String( lines.last() ), WrongPasswordPatterns) ||
checkForErrorMessage(QLatin1String( lines.last() ), ExtractionFailedPatterns) ||
checkForPasswordPromptMessage(QLatin1String(lines.last())) ||
checkForFileExistsMessage(QLatin1String( lines.last() )));
if (foundErrorMessage) {
handleAll = true;
}
//this is complex, here's an explanation:
//if there is no newline, then there is no guaranteed full line to
//handle in the output. The exception is that it is supposed to handle
//all the data, OR if there's been an error message found in the
//partial data.
if (lines.size() == 1 && !handleAll) {
return;
}
if (handleAll) {
m_stdOutData.clear();
} else {
//because the last line might be incomplete we leave it for now
//note, this last line may be an empty string if the stdoutdata ends
//with a newline
m_stdOutData = lines.takeLast();
}
foreach(const QByteArray& line, lines) {
if (!line.isEmpty() || (m_listEmptyLines && m_operationMode == List)) {
handleLine(QString::fromLocal8Bit(line));
}
}
}
void CliInterface::handleLine(const QString& line)
{
// TODO: This should be implemented by each plugin; the way progress is
// shown by each CLI application is subject to a lot of variation.
if ((m_operationMode == Copy || m_operationMode == Add) && m_param.contains(CaptureProgress) && m_param.value(CaptureProgress).toBool()) {
//read the percentage
int pos = line.indexOf(QLatin1Char( '%' ));
if (pos != -1 && pos > 1) {
int percentage = line.mid(pos - 2, 2).toInt();
emit progress(float(percentage) / 100);
return;
}
}
if (m_operationMode == Copy) {
if (checkForPasswordPromptMessage(line)) {
kDebug() << "Found a password prompt";
Kerfuffle::PasswordNeededQuery query(filename());
emit userQuery(&query);
query.waitForResponse();
if (query.responseCancelled()) {
failOperation();
return;
}
setPassword(query.password());
const QString response(password() + QLatin1Char('\n'));
writeToProcess(response.toLocal8Bit());
return;
}
if (checkForErrorMessage(line, WrongPasswordPatterns)) {
kDebug() << "Wrong password!";
emit error(i18n("Incorrect password."));
failOperation();
return;
}
if (checkForErrorMessage(line, ExtractionFailedPatterns)) {
kDebug() << "Error in extraction!!";
emit error(i18n("Extraction failed because of an unexpected error."));
failOperation();
return;
}
if (handleFileExistsMessage(line)) {
return;
}
}
if (m_operationMode == List) {
if (checkForPasswordPromptMessage(line)) {
kDebug() << "Found a password prompt";
Kerfuffle::PasswordNeededQuery query(filename());
emit userQuery(&query);
query.waitForResponse();
if (query.responseCancelled()) {
failOperation();
return;
}
setPassword(query.password());
const QString response(password() + QLatin1Char('\n'));
writeToProcess(response.toLocal8Bit());
return;
}
if (checkForErrorMessage(line, WrongPasswordPatterns)) {
kDebug() << "Wrong password!";
emit error(i18n("Incorrect password."));
failOperation();
return;
}
if (checkForErrorMessage(line, ExtractionFailedPatterns)) {
kDebug() << "Error in extraction!!";
emit error(i18n("Extraction failed because of an unexpected error."));
failOperation();
return;
}
if (handleFileExistsMessage(line)) {
return;
}
readListLine(line);
return;
}
}
bool CliInterface::checkForPasswordPromptMessage(const QString& line)
{
const QString passwordPromptPattern(m_param.value(PasswordPromptPattern).toString());
if (passwordPromptPattern.isEmpty())
return false;
if (m_passwordPromptPattern.isEmpty()) {
m_passwordPromptPattern.setPattern(m_param.value(PasswordPromptPattern).toString());
}
if (m_passwordPromptPattern.indexIn(line) != -1) {
return true;
}
return false;
}
bool CliInterface::checkForFileExistsMessage(const QString& line)
{
if (m_existsPattern.isEmpty()) {
m_existsPattern.setPattern(m_param.value(FileExistsExpression).toString());
}
if (m_existsPattern.indexIn(line) != -1) {
kDebug() << "Detected file existing!! Filename " << m_existsPattern.cap(1);
return true;
}
return false;
}
bool CliInterface::handleFileExistsMessage(const QString& line)
{
if (!checkForFileExistsMessage(line)) {
return false;
}
const QString filename = m_existsPattern.cap(1);
Kerfuffle::OverwriteQuery query(QDir::current().path() + QLatin1Char( '/' ) + filename);
query.setNoRenameMode(true);
emit userQuery(&query);
kDebug() << "Waiting response";
query.waitForResponse();
kDebug() << "Finished response";
QString responseToProcess;
const QStringList choices = m_param.value(FileExistsInput).toStringList();
if (query.responseOverwrite()) {
responseToProcess = choices.at(0);
} else if (query.responseSkip()) {
responseToProcess = choices.at(1);
} else if (query.responseOverwriteAll()) {
responseToProcess = choices.at(2);
} else if (query.responseAutoSkip()) {
responseToProcess = choices.at(3);
} else if (query.responseCancelled()) {
if (choices.count() < 5) { // If the program has no way to cancel the extraction, we resort to killing it
return doKill();
}
responseToProcess = choices.at(4);
}
Q_ASSERT(!responseToProcess.isEmpty());
responseToProcess += QLatin1Char( '\n' );
writeToProcess(responseToProcess.toLocal8Bit());
return true;
}
bool CliInterface::checkForErrorMessage(const QString& line, int parameterIndex)
{
QList<QRegExp> patterns;
if (m_patternCache.contains(parameterIndex)) {
patterns = m_patternCache.value(parameterIndex);
} else {
if (!m_param.contains(parameterIndex)) {
return false;
}
foreach(const QString& rawPattern, m_param.value(parameterIndex).toStringList()) {
patterns << QRegExp(rawPattern);
}
m_patternCache[parameterIndex] = patterns;
}
foreach(const QRegExp& pattern, patterns) {
if (pattern.indexIn(line) != -1) {
return true;
}
}
return false;
}
bool CliInterface::doKill()
{
if (m_process) {
// Give some time for the application to finish gracefully
m_abortingOperation = true;
if (!m_process->waitForFinished(5)) {
m_process->kill();
}
m_abortingOperation = false;
return true;
}
return false;
}
bool CliInterface::doSuspend()
{
return false;
}
bool CliInterface::doResume()
{
return false;
}
void CliInterface::substituteListVariables(QStringList& params)
{
for (int i = 0; i < params.size(); ++i) {
const QString parameter = params.at(i);
if (parameter == QLatin1String( "$Archive" )) {
params[i] = filename();
}
}
}
QString CliInterface::escapeFileName(const QString& fileName) const
{
return fileName;
}
void CliInterface::writeToProcess(const QByteArray& data)
{
Q_ASSERT(m_process);
Q_ASSERT(!data.isNull());
kDebug() << "Writing" << data << "to the process";
#ifdef Q_OS_WIN
m_process->write(data);
#else
m_process->pty()->write(data);
#endif
}
}
#include "cliinterface.moc"

View file

@ -0,0 +1,349 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2009 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009-2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CLIINTERFACE_H
#define CLIINTERFACE_H
#include "archiveinterface.h"
#include "kerfuffle_export.h"
#include <QtCore/QProcess>
#include <QtCore/QRegExp>
class KProcess;
class KPtyProcess;
namespace Kerfuffle
{
enum CliInterfaceParameters {
///////////////[ COMMON ]/////////////
/**
* Bool (default false)
* Will look for the %-sign in the stdout while working, in the form of
* (2%, 14%, 35%, etc etc), and report progress based upon this
*/
CaptureProgress = 0,
/**
* QString
* Default: empty
* A regexp pattern that matches the program's password prompt.
*/
PasswordPromptPattern,
///////////////[ LIST ]/////////////
/**
* QStringList
* The names to the program that will handle listing of this
* archive (eg "rar"). Will be searched for in PATH
*/
ListProgram,
/**
* QStringList
* The arguments that are passed to the program above for
* listing the archive. Special strings that will be
* substituted:
* $Archive - the path of the archive
*/
ListArgs,
///////////////[ EXTRACT ]/////////////
/**
* QStringList
* The names to the program that will handle extracting of this
* archive (eg "rar"). Will be searched for in PATH
*/
ExtractProgram,
/**
* QStringList
* The arguments that are passed to the program above for
* extracting the archive. Special strings that will be
* substituted:
* $Archive - the path of the archive
* $Files - the files selected to be extracted, if any
* $PreservePathSwitch - the flag for extracting with full paths
* $RootNodeSwitch - the internal work dir in the archive (for example
* when the user has dragged a folder from the archive and wants it
* extracted relative to it)
* $PasswordSwitch - the switch setting the password. Note that this
* will not be inserted unless the listing function has emitted an
* entry with the IsPasswordProtected property set to true.
*/
ExtractArgs,
/**
* Bool (default false)
* When passing directories to the extract program, do not
* include trailing slashes
* e.g. if the user selected "foo/" and "foo/bar" in the gui, the
* paths "foo" and "foo/bar" will be sent to the program.
*/
NoTrailingSlashes,
/**
* QStringList
* This should be a qstringlist with either two elements. The first
* string is what PreservePathSwitch in the ExtractArgs will be replaced
* with if PreservePath is True/enabled. The second is for the disabled
* case. An empty string means that the argument will not be used in
* that case.
* Example: for rar, "x" means extract with full paths, and "e" means
* extract without full paths. in this case we will use the stringlist
* ("x", "e"). Or, for another format that might use the switch
* "--extractFull" for preservePaths, and nothing otherwise: we use the
* stringlist ("--extractFull", "")
*/
PreservePathSwitch,
/**
* QStringList (default empty)
* The format of the root node switch. The variable $Path will be
* substituted for the path string.
* Example: ("--internalPath=$Path)
* or ("--path", "$Path")
*/
RootNodeSwitch,
/**
* QStringList (default empty)
* The format of the root node switch. The variable $Password will be
* substituted for the password string. NOTE: supplying passwords
* through a virtual terminal is not supported (yet?), because this
* is not cross platform compatible. As of KDE 4.3 there are no plans to
* change this.
* Example: ("-p$Password)
* or ("--password", "$Password")
*/
PasswordSwitch,
/**
* QString
* This is a regexp, defining how to recognize a "File already exists"
* prompt when extracting. It should have one captured string, which is
* the filename of the file/folder that already exists.
*/
FileExistsExpression,
/**
* int
* This sets on what output channel the FileExistsExpression regex
* should be applied on, in other words, on what stream the "file
* exists" output will appear in. Values accepted:
* 0 - Standard error, stderr (default)
* 1 - Standard output, stdout
*/
FileExistsMode,
/**
* QStringList
* The various responses that can be supplied as a response to the
* "file exists" prompt. The various items are to be supplied in the
* following order:
* index 0 - Yes (overwrite)
* index 1 - No (skip/do not overwrite)
* index 2 - All (overwrite all)
* index 3 - Do not overwrite any files (autoskip)
* index 4 - Cancel operation
*/
FileExistsInput,
///////////////[ DELETE ]/////////////
/**
* QStringList
* The names to the program that will handle deleting of elements in this
* archive format (eg "rar"). Will be searched for in PATH
*/
DeleteProgram,
/**
* QStringList
* The arguments that are passed to the program above for
* deleting from the archive. Special strings that will be
* substituted:
* $Archive - the path of the archive
* $Files - the files selected to be deleted
*/
DeleteArgs,
/**
* QStringList
* Default: empty
* A list of regexp patterns that will cause the extraction to exit
* with a general fail message
*/
ExtractionFailedPatterns,
/**
* QStringList
* Default: empty
* A list of regexp patterns that will alert the user that the password
* was wrong.
*/
WrongPasswordPatterns,
///////////////[ ADD ]/////////////
/**
* QStringList
* The names to the program that will handle adding in this
* archive format (eg "rar"). Will be searched for in PATH
*/
AddProgram,
/**
* QStringList
* The arguments that are passed to the program above for
* adding to the archive. Special strings that will be
* substituted:
* $Archive - the path of the archive
* $Files - the files selected to be added
*/
AddArgs
};
typedef QHash<int, QVariant> ParameterList;
class KERFUFFLE_EXPORT CliInterface : public ReadWriteArchiveInterface
{
Q_OBJECT
public:
enum OperationMode {
List, Copy, Add, Delete
};
OperationMode m_operationMode;
explicit CliInterface(QObject *parent, const QVariantList & args);
virtual ~CliInterface();
virtual bool list();
virtual bool copyFiles(const QList<QVariant> & files, const QString & destinationDirectory, ExtractionOptions options);
virtual bool addFiles(const QStringList & files, const CompressionOptions& options);
virtual bool deleteFiles(const QList<QVariant> & files);
virtual ParameterList parameterList() const = 0;
virtual bool readListLine(const QString &line) = 0;
bool doKill();
bool doSuspend();
bool doResume();
/**
* Returns the list of characters which are preceded by a
* backslash when a file name in an archive is passed to
* a program.
*
* @see setEscapedCharacters().
*/
QString escapedCharacters();
/**
* Sets which characters will be preceded by a backslash when
* a file name in an archive is passed to a program.
*
* @see escapedCharacters().
*/
void setEscapedCharacters(const QString& characters);
/**
* Sets if the listing should include empty lines.
*
* The default value is false.
*/
void setListEmptyLines(bool emptyLines);
private:
void substituteListVariables(QStringList& params);
void cacheParameterList();
/**
* Checks whether a line of the program's output is a password prompt.
*
* It uses the regular expression in the @c PasswordPromptPattern parameter
* for the check.
*
* @param line A line of the program's output.
*
* @return @c true if the given @p line is a password prompt, @c false
* otherwise.
*/
bool checkForPasswordPromptMessage(const QString& line);
bool checkForFileExistsMessage(const QString& line);
bool handleFileExistsMessage(const QString& filename);
bool checkForErrorMessage(const QString& line, int parameterIndex);
void handleLine(const QString& line);
void failOperation();
/**
* Run @p programName with the given @p arguments.
* The method waits until @p programName is finished to exit.
*
* @param programName The program that will be run (not the whole path).
* @param arguments A list of arguments that will be passed to the program.
*
* @return @c true if the program was found and the process ran correctly,
* @c false otherwise.
*/
bool runProcess(const QStringList& programNames, const QStringList& arguments);
/**
* Performs any additional escaping and processing on @p fileName
* before passing it to the underlying process.
*
* The default implementation returns @p fileName unchanged.
*
* @param fileName String to escape.
*/
virtual QString escapeFileName(const QString &fileName) const;
/**
* Wrapper around KProcess::write() or KPtyDevice::write(), depending on
* the platform.
*/
void writeToProcess(const QByteArray& data);
QByteArray m_stdOutData;
QRegExp m_existsPattern;
QRegExp m_passwordPromptPattern;
QHash<int, QList<QRegExp> > m_patternCache;
#ifdef Q_OS_WIN
KProcess *m_process;
#else
KPtyProcess *m_process;
#endif
ParameterList m_param;
QVariantList m_removedFiles;
bool m_listEmptyLines;
bool m_abortingOperation;
private slots:
void readStdout(bool handleAll = false);
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);
};
}
#endif /* CLIINTERFACE_H */

View file

@ -0,0 +1,234 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2009 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "extractiondialog.h"
#include "settings.h"
#include <KLocale>
#include <KIconLoader>
#include <KMessageBox>
#include <KStandardDirs>
#include <KDebug>
#include <KIO/NetAccess>
#include <QDir>
#include "ui_extractiondialog.h"
namespace Kerfuffle
{
class ExtractionDialogUI: public QFrame, public Ui::ExtractionDialog
{
public:
ExtractionDialogUI(QWidget *parent = 0)
: QFrame(parent) {
setupUi(this);
}
};
ExtractionDialog::ExtractionDialog(QWidget *parent)
: KDirSelectDialog(KUrl(), false, parent)
{
m_ui = new ExtractionDialogUI(this);
mainWidget()->layout()->addWidget(m_ui);
setCaption(i18nc("@title:window", "Extract"));
m_ui->iconLabel->setPixmap(DesktopIcon(QLatin1String( "archive-extract" )));
m_ui->filesToExtractGroupBox->hide();
m_ui->allFilesButton->setChecked(true);
m_ui->extractAllLabel->show();
setSingleFolderArchive(false);
m_ui->autoSubfolders->hide();
loadSettings();
connect(this, SIGNAL(finished(int)), SLOT(writeSettings()));
}
void ExtractionDialog::loadSettings()
{
setOpenDestinationFolderAfterExtraction(ArkSettings::openDestinationFolderAfterExtraction());
setCloseAfterExtraction(ArkSettings::closeAfterExtraction());
setPreservePaths(ArkSettings::preservePaths());
}
void ExtractionDialog::setSingleFolderArchive(bool value)
{
m_ui->singleFolderGroup->setChecked(!value);
}
void ExtractionDialog::batchModeOption()
{
m_ui->autoSubfolders->show();
m_ui->autoSubfolders->setEnabled(true);
m_ui->singleFolderGroup->hide();
m_ui->extractAllLabel->setText(i18n("Extract multiple archives"));
}
void ExtractionDialog::accept()
{
if (extractToSubfolder()) {
if (subfolder().contains(QLatin1String( "/" ))) {
KMessageBox::error(NULL, i18n("The subfolder name may not contain the character '/'."));
return;
}
const QString pathWithSubfolder = url().pathOrUrl(KUrl::AddTrailingSlash) + subfolder();
while (1) {
if (KIO::NetAccess::exists(pathWithSubfolder, KIO::NetAccess::SourceSide, 0)) {
if (QFileInfo(pathWithSubfolder).isDir()) {
int overwrite = KMessageBox::questionYesNoCancel(0, i18nc("@info", "The folder <filename>%1</filename> already exists. Are you sure you want to extract here?", pathWithSubfolder), i18n("Folder exists"), KGuiItem(i18n("Extract here")), KGuiItem(i18n("Retry")), KGuiItem(i18n("Cancel")));
if (overwrite == KMessageBox::No) {
// The user clicked Retry.
continue;
} else if (overwrite == KMessageBox::Cancel) {
return;
}
} else {
KMessageBox::detailedError(0,
i18nc("@info", "The folder <filename>%1</filename> could not be created.", subfolder()),
i18nc("@info", "<filename>%1</filename> already exists, but is not a folder.", subfolder()));
return;
}
} else if (!KIO::NetAccess::mkdir(pathWithSubfolder, 0)) {
KMessageBox::detailedError(0,
i18nc("@info", "The folder <filename>%1</filename> could not be created.", subfolder()),
i18n("Please check your permissions to create it."));
return;
}
break;
}
}
KDirSelectDialog::accept();
}
void ExtractionDialog::setSubfolder(const QString& subfolder)
{
m_ui->subfolder->setText(subfolder);
}
QString ExtractionDialog::subfolder() const
{
return m_ui->subfolder->text();
}
ExtractionDialog::~ExtractionDialog()
{
delete m_ui;
m_ui = 0;
}
void ExtractionDialog::setShowSelectedFiles(bool value)
{
if (value) {
m_ui->filesToExtractGroupBox->show();
m_ui->selectedFilesButton->setChecked(true);
m_ui->extractAllLabel->hide();
} else {
m_ui->filesToExtractGroupBox->hide();
m_ui->selectedFilesButton->setChecked(false);
m_ui->extractAllLabel->show();
}
}
bool ExtractionDialog::extractAllFiles() const
{
return m_ui->allFilesButton->isChecked();
}
void ExtractionDialog::setAutoSubfolder(bool value)
{
m_ui->autoSubfolders->setChecked(value);
}
bool ExtractionDialog::autoSubfolders() const
{
return m_ui->autoSubfolders->isChecked();
}
bool ExtractionDialog::extractToSubfolder() const
{
return m_ui->singleFolderGroup->isChecked();
}
void ExtractionDialog::setOpenDestinationFolderAfterExtraction(bool value)
{
m_ui->openFolderCheckBox->setChecked(value);
}
void ExtractionDialog::setCloseAfterExtraction(bool value)
{
m_ui->closeAfterExtraction->setChecked(value);
}
void ExtractionDialog::setPreservePaths(bool value)
{
m_ui->preservePaths->setChecked(value);
}
bool ExtractionDialog::preservePaths() const
{
return m_ui->preservePaths->isChecked();
}
bool ExtractionDialog::openDestinationAfterExtraction() const
{
return m_ui->openFolderCheckBox->isChecked();
}
bool ExtractionDialog::closeAfterExtraction() const
{
return m_ui->closeAfterExtraction->isChecked();
}
KUrl ExtractionDialog::destinationDirectory() const
{
if (extractToSubfolder()) {
return QString(url().pathOrUrl(KUrl::AddTrailingSlash) + subfolder() + QLatin1Char( '/' ));
} else {
return url().pathOrUrl(KUrl::AddTrailingSlash);
}
}
void ExtractionDialog::writeSettings()
{
ArkSettings::setOpenDestinationFolderAfterExtraction(openDestinationAfterExtraction());
ArkSettings::setCloseAfterExtraction(closeAfterExtraction());
ArkSettings::setPreservePaths(preservePaths());
ArkSettings::self()->writeConfig();
}
}
#include "extractiondialog.moc"

View file

@ -0,0 +1,78 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef EXTRACTIONDIALOG_H
#define EXTRACTIONDIALOG_H
#include "kerfuffle_export.h"
#include <KDirSelectDialog>
#include <KDialog>
namespace Kerfuffle
{
class KERFUFFLE_EXPORT ExtractionDialog : public KDirSelectDialog
{
Q_OBJECT
public:
ExtractionDialog(QWidget *parent = 0);
virtual ~ExtractionDialog();
void setShowSelectedFiles(bool);
void setSingleFolderArchive(bool);
void setPreservePaths(bool);
void batchModeOption();
void setOpenDestinationFolderAfterExtraction(bool);
void setCloseAfterExtraction(bool);
void setAutoSubfolder(bool value);
bool extractAllFiles() const;
bool openDestinationAfterExtraction() const;
bool closeAfterExtraction() const;
bool extractToSubfolder() const;
bool autoSubfolders() const;
bool preservePaths() const;
KUrl destinationDirectory() const;
QString subfolder() const;
virtual void accept();
public Q_SLOTS:
void setSubfolder(const QString& subfolder);
private Q_SLOTS:
void writeSettings();
private:
void loadSettings();
class ExtractionDialogUI *m_ui;
};
}
#endif // EXTRACTIONDIALOG_H

View file

@ -0,0 +1,220 @@
<ui version="4.0" >
<author>Henrique Pinto &lt;henrique.pinto@kdemail.net></author>
<class>ExtractionDialog</class>
<widget class="QWidget" name="ExtractionDialog" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>314</width>
<height>422</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Fixed" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle" >
<string>Extraction Dialog</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2" >
<item>
<layout class="QHBoxLayout" name="horizontalLayout" >
<item>
<widget class="QLabel" name="iconLabel" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Minimum" hsizetype="Fixed" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize" >
<size>
<width>256</width>
<height>256</height>
</size>
</property>
<property name="text" >
<string/>
</property>
<property name="pixmap" >
<pixmap>../../../../../kde-devel/ark-trunk/pics/ox32-action-ark_extract.png</pixmap>
</property>
<property name="scaledContents" >
<bool>false</bool>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="extractAllLabel" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Maximum" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="font" >
<font>
<pointsize>15</pointsize>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text" >
<string>Extract All Files</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="singleFolderGroup" >
<property name="title" >
<string>&amp;Extraction into subfolder:</string>
</property>
<property name="checkable" >
<bool>true</bool>
</property>
<property name="checked" >
<bool>false</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout" >
<item>
<widget class="KLineEdit" name="subfolder" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="showClearButton" stdset="0" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Expanding" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title" >
<string>Options</string>
</property>
<layout class="QGridLayout" name="gridLayout" >
<item row="6" column="0" >
<spacer name="verticalSpacer" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" >
<widget class="QCheckBox" name="openFolderCheckBox" >
<property name="text" >
<string>Open &amp;destination folder after extraction</string>
</property>
</widget>
</item>
<item row="3" column="0" >
<widget class="QCheckBox" name="closeAfterExtraction" >
<property name="text" >
<string>Close &amp;Ark after extraction</string>
</property>
</widget>
</item>
<item row="4" column="0" >
<widget class="QCheckBox" name="preservePaths" >
<property name="text" >
<string>&amp;Preserve paths when extracting</string>
</property>
</widget>
</item>
<item row="5" column="0" >
<widget class="QCheckBox" name="autoSubfolders" >
<property name="text" >
<string>&amp;Automatically create subfolders</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="filesToExtractGroupBox" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title" >
<string comment="@title:group">Extract</string>
</property>
<property name="flat" >
<bool>false</bool>
</property>
<property name="checkable" >
<bool>false</bool>
</property>
<layout class="QVBoxLayout" >
<item>
<widget class="QRadioButton" name="selectedFilesButton" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>&amp;Selected files only</string>
</property>
<property name="checked" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="allFilesButton" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Fixed" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>All &amp;files</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KLineEdit</class>
<extends>QLineEdit</extends>
<header>klineedit.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

346
ark/kerfuffle/jobs.cpp Normal file
View file

@ -0,0 +1,346 @@
/*
* Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (c) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (c) 2009-2012 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "jobs.h"
#include <QThread>
#include <KDebug>
#include <KLocale>
//#define DEBUG_RACECONDITION
namespace Kerfuffle
{
class Job::Private : public QThread
{
public:
Private(Job *job, QObject *parent = 0)
: QThread(parent)
, q(job)
{
connect(q, SIGNAL(result(KJob*)), SLOT(quit()));
}
virtual void run();
private:
Job *q;
};
void Job::Private::run()
{
q->doWork();
if (q->isRunning()) {
exec();
}
#ifdef DEBUG_RACECONDITION
QThread::sleep(2);
#endif
}
Job::Job(ReadOnlyArchiveInterface *interface, QObject *parent)
: KJob(parent)
, m_archiveInterface(interface)
, m_isRunning(false)
, d(new Private(this))
{
static bool onlyOnce = false;
if (!onlyOnce) {
qRegisterMetaType<QPair<QString, QString> >("QPair<QString,QString>");
onlyOnce = true;
}
setCapabilities(KJob::Killable);
}
Job::~Job()
{
if (d->isRunning()) {
d->wait();
}
delete d;
}
ReadOnlyArchiveInterface *Job::archiveInterface()
{
return m_archiveInterface;
}
bool Job::isRunning() const
{
return m_isRunning;
}
void Job::start()
{
m_isRunning = true;
d->start();
}
void Job::emitResult()
{
m_isRunning = false;
KJob::emitResult();
}
void Job::connectToArchiveInterfaceSignals()
{
connect(archiveInterface(), SIGNAL(error(QString,QString)), SLOT(onError(QString,QString)));
connect(archiveInterface(), SIGNAL(entry(ArchiveEntry)), SLOT(onEntry(ArchiveEntry)));
connect(archiveInterface(), SIGNAL(entryRemoved(QString)), SLOT(onEntryRemoved(QString)));
connect(archiveInterface(), SIGNAL(progress(double)), SLOT(onProgress(double)));
connect(archiveInterface(), SIGNAL(info(QString)), SLOT(onInfo(QString)));
connect(archiveInterface(), SIGNAL(finished(bool)), SLOT(onFinished(bool)), Qt::DirectConnection);
connect(archiveInterface(), SIGNAL(userQuery(Query*)), SLOT(onUserQuery(Query*)));
}
void Job::onError(const QString & message, const QString & details)
{
Q_UNUSED(details)
setError(1);
setErrorText(message);
}
void Job::onEntry(const ArchiveEntry & archiveEntry)
{
emit newEntry(archiveEntry);
}
void Job::onProgress(double value)
{
setPercent(static_cast<unsigned long>(100.0*value));
}
void Job::onInfo(const QString& info)
{
emit infoMessage(this, info);
}
void Job::onEntryRemoved(const QString & path)
{
emit entryRemoved(path);
}
void Job::onFinished(bool result)
{
kDebug() << result;
archiveInterface()->disconnect(this);
emitResult();
}
void Job::onUserQuery(Query *query)
{
emit userQuery(query);
}
bool Job::doKill()
{
kDebug();
bool ret = archiveInterface()->doKill();
if (!ret) {
kDebug() << "Killing does not seem to be supported here.";
}
return ret;
}
ListJob::ListJob(ReadOnlyArchiveInterface *interface, QObject *parent)
: Job(interface, parent)
, m_isSingleFolderArchive(true)
, m_isPasswordProtected(false)
, m_extractedFilesSize(0)
{
connect(this, SIGNAL(newEntry(ArchiveEntry)),
this, SLOT(onNewEntry(ArchiveEntry)));
}
void ListJob::doWork()
{
emit description(this, i18n("Loading archive..."));
connectToArchiveInterfaceSignals();
bool ret = archiveInterface()->list();
if (!archiveInterface()->waitForFinishedSignal()) {
onFinished(ret);
}
}
qlonglong ListJob::extractedFilesSize() const
{
return m_extractedFilesSize;
}
bool ListJob::isPasswordProtected() const
{
return m_isPasswordProtected;
}
bool ListJob::isSingleFolderArchive() const
{
return m_isSingleFolderArchive;
}
void ListJob::onNewEntry(const ArchiveEntry& entry)
{
m_extractedFilesSize += entry[ Size ].toLongLong();
m_isPasswordProtected |= entry [ IsPasswordProtected ].toBool();
if (m_isSingleFolderArchive) {
const QString fileName(entry[FileName].toString());
const QString basePath(fileName.split(QLatin1Char( '/' )).at(0));
if (m_basePath.isEmpty()) {
m_basePath = basePath;
m_subfolderName = basePath;
} else {
if (m_basePath != basePath) {
m_isSingleFolderArchive = false;
m_subfolderName.clear();
}
}
}
}
QString ListJob::subfolderName() const
{
return m_subfolderName;
}
ExtractJob::ExtractJob(const QVariantList& files, const QString& destinationDir, ExtractionOptions options, ReadOnlyArchiveInterface *interface, QObject *parent)
: Job(interface, parent)
, m_files(files)
, m_destinationDir(destinationDir)
, m_options(options)
{
setDefaultOptions();
}
void ExtractJob::doWork()
{
QString desc;
if (m_files.count() == 0) {
desc = i18n("Extracting all files");
} else {
desc = i18np("Extracting one file", "Extracting %1 files", m_files.count());
}
emit description(this, desc);
connectToArchiveInterfaceSignals();
kDebug() << "Starting extraction with selected files:"
<< m_files
<< "Destination dir:" << m_destinationDir
<< "Options:" << m_options;
bool ret = archiveInterface()->copyFiles(m_files, m_destinationDir, m_options);
if (!archiveInterface()->waitForFinishedSignal()) {
onFinished(ret);
}
}
void ExtractJob::setDefaultOptions()
{
ExtractionOptions defaultOptions;
defaultOptions[QLatin1String("PreservePaths")] = false;
ExtractionOptions::const_iterator it = defaultOptions.constBegin();
for (; it != defaultOptions.constEnd(); ++it) {
if (!m_options.contains(it.key())) {
m_options[it.key()] = it.value();
}
}
}
QString ExtractJob::destinationDirectory() const
{
return m_destinationDir;
}
ExtractionOptions ExtractJob::extractionOptions() const
{
return m_options;
}
AddJob::AddJob(const QStringList& files, const CompressionOptions& options , ReadWriteArchiveInterface *interface, QObject *parent)
: Job(interface, parent)
, m_files(files)
, m_options(options)
{
}
void AddJob::doWork()
{
emit description(this, i18np("Adding a file", "Adding %1 files", m_files.count()));
ReadWriteArchiveInterface *m_writeInterface =
qobject_cast<ReadWriteArchiveInterface*>(archiveInterface());
Q_ASSERT(m_writeInterface);
connectToArchiveInterfaceSignals();
bool ret = m_writeInterface->addFiles(m_files, m_options);
if (!archiveInterface()->waitForFinishedSignal()) {
onFinished(ret);
}
}
DeleteJob::DeleteJob(const QVariantList& files, ReadWriteArchiveInterface *interface, QObject *parent)
: Job(interface, parent)
, m_files(files)
{
}
void DeleteJob::doWork()
{
emit description(this, i18np("Deleting a file from the archive", "Deleting %1 files", m_files.count()));
ReadWriteArchiveInterface *m_writeInterface =
qobject_cast<ReadWriteArchiveInterface*>(archiveInterface());
Q_ASSERT(m_writeInterface);
connectToArchiveInterfaceSignals();
int ret = m_writeInterface->deleteFiles(m_files);
if (!archiveInterface()->waitForFinishedSignal()) {
onFinished(ret);
}
}
} // namespace Kerfuffle
#include "jobs.moc"

171
ark/kerfuffle/jobs.h Normal file
View file

@ -0,0 +1,171 @@
/*
* Copyright (c) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (c) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (c) 2009-2012 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef JOBS_H
#define JOBS_H
#include "kerfuffle_export.h"
#include "archiveinterface.h"
#include "archive.h"
#include "queries.h"
#include <KJob>
#include <QList>
#include <QVariant>
#include <QString>
namespace Kerfuffle
{
class ThreadExecution;
class KERFUFFLE_EXPORT Job : public KJob
{
Q_OBJECT
public:
void start();
bool isRunning() const;
protected:
Job(ReadOnlyArchiveInterface *interface, QObject *parent = 0);
virtual ~Job();
virtual bool doKill();
virtual void emitResult();
ReadOnlyArchiveInterface *archiveInterface();
void connectToArchiveInterfaceSignals();
public slots:
virtual void doWork() = 0;
protected slots:
virtual void onError(const QString &message, const QString &details);
virtual void onInfo(const QString &info);
virtual void onEntry(const ArchiveEntry &archiveEntry);
virtual void onProgress(double progress);
virtual void onEntryRemoved(const QString &path);
virtual void onFinished(bool result);
virtual void onUserQuery(Query *query);
signals:
void entryRemoved(const QString & entry);
void error(const QString& errorMessage, const QString& details);
void newEntry(const ArchiveEntry &);
void userQuery(Kerfuffle::Query*);
private:
ReadOnlyArchiveInterface *m_archiveInterface;
bool m_isRunning;
class Private;
Private * const d;
};
class KERFUFFLE_EXPORT ListJob : public Job
{
Q_OBJECT
public:
explicit ListJob(ReadOnlyArchiveInterface *interface, QObject *parent = 0);
qlonglong extractedFilesSize() const;
bool isPasswordProtected() const;
bool isSingleFolderArchive() const;
QString subfolderName() const;
public slots:
virtual void doWork();
private:
bool m_isSingleFolderArchive;
bool m_isPasswordProtected;
QString m_subfolderName;
QString m_basePath;
qlonglong m_extractedFilesSize;
private slots:
void onNewEntry(const ArchiveEntry&);
};
class KERFUFFLE_EXPORT ExtractJob : public Job
{
Q_OBJECT
public:
ExtractJob(const QVariantList& files, const QString& destinationDir, ExtractionOptions options, ReadOnlyArchiveInterface *interface, QObject *parent = 0);
QString destinationDirectory() const;
ExtractionOptions extractionOptions() const;
public slots:
virtual void doWork();
private:
// TODO: Maybe this should be a method if ExtractionOptions were a class?
void setDefaultOptions();
QVariantList m_files;
QString m_destinationDir;
ExtractionOptions m_options;
};
class KERFUFFLE_EXPORT AddJob : public Job
{
Q_OBJECT
public:
AddJob(const QStringList& files, const CompressionOptions& options, ReadWriteArchiveInterface *interface, QObject *parent = 0);
public slots:
virtual void doWork();
private:
QStringList m_files;
CompressionOptions m_options;
};
class KERFUFFLE_EXPORT DeleteJob : public Job
{
Q_OBJECT
public:
DeleteJob(const QVariantList& files, ReadWriteArchiveInterface *interface, QObject *parent = 0);
public slots:
virtual void doWork();
private:
QVariantList m_files;
};
} // namespace Kerfuffle
#endif // JOBS_H

View file

@ -0,0 +1,74 @@
[Desktop Entry]
Type=ServiceType
X-KDE-ServiceType=Kerfuffle/Plugin
Comment=Plugin for handling of archive formats for the Kerfuffle library
Comment[ar]=ملحق للتحكم بهيئات الأرشيف لمكتبة Kerfuffle
Comment[ast]=Aplicación pa remanar los formatos de ficheru de la llibrería Kerfuffle
Comment[bg]=Приствка за работа с архиви за Kerfuffle
Comment[bs]=Priključak za rukovanje formatima arhiva za biblioteku Kerfafl
Comment[ca]=Connector per gestionar els formats d'arxiu per la biblioteca Kerfuffle
Comment[ca@valencia]=Connector per gestionar els formats d'arxiu per la biblioteca Kerfuffle
Comment[cs]=Modul pro správu formátů archivů pro knihovnu Kerfuffle
Comment[da]=Plugin til håndtering af arkivformater til Kerfuffle-biblioteket
Comment[de]=Modul zum Umgang mit Archivformaten der Kerfuffle-Bibliothek
Comment[el]=Πρόσθετο χειρισμού μορφών αρχειοθήκης για τη βιβλιοθήκη Kerfuffle
Comment[en_GB]=Plugin for handling of archive formats for the Kerfuffle library
Comment[es]=Complemento para manejar formatos de archivo de la biblioteca Kerfuffle
Comment[et]=Kerfuffle'i teegi arhiivifailide haldamise plugin
Comment[eu]=Kerfuffle liburutegiaren artxibo-formatuak erabiltzeko plugina
Comment[fi]=Kerfuffle-kirjaston pakettimuotojen tuki
Comment[fr]=Module externe de gestion des formats d'archives pour la bibliothèque « Kerfuffle »
Comment[ga]=Breiseán a láimhseálann formáidí cartlainne thar ceann na leabharlainne Kerfuffle
Comment[gl]=Extensión da biblioteca Kerfuffle para manexar formatos de arquivo
Comment[he]=תוסף לניהול פורמטי ארכיונים לספריית Kerfuffle
Comment[hne]= ि
Comment[hr]=Priključak za upravljanje arhivnim formatima za biblioteku Kerfuffle
Comment[hu]=Archívumkezelő modul a Kerfuffle programkönyvtárhoz
Comment[ia]=Plugin per manear formatos de archivo per le libreria Kerfuffle
Comment[id]=Pengaya untuk menangani format arsip pustaka Kerfuffle
Comment[it]=Estensione per gestire formati di archivio per la libreria Kerfuffle
Comment[ja]=Kerfuffle
Comment[kk]=Kerfuffle жиын файлға арналған архив пішіндерімен айналасу плагині
Comment[km]= Kerfuffle
Comment[ko]=Kerfuffle
Comment[lt]=Kerfuffle bibliotekos archyvų formatų palaikymo priedas
Comment[lv]=Arhīva failu tipu izmantošanas spraudnis Kerfuffle bibliotēkai.
Comment[mr]= ि
Comment[nb]=Programtillegg som håndterer arkivformater, for Kerfuffle-biblioteket
Comment[nds]=Archievformaten-Moduul för de Kerfuffle-Bibliotheek
Comment[nl]=Plug-in voor het afhandelen van archiefformaten voor de Kerfuffle-bibliotheek
Comment[nn]=Programtillegg som handsamar arkivformat for Kerfuffle-biblioteket
Comment[pl]=Wtyczka do obsługi formatów archiwów w bibliotece Kerfuffle
Comment[pt]='Plugin' para lidar com os formatos de pacotes da biblioteca Kerfuffle
Comment[pt_BR]=Plugin para manipulação de formatos de arquivo, para a biblioteca Kerfuffle
Comment[ro]=Modul pentru pentru mînuirea formatelor de arhive pentru biblioteca Kerfuffle
Comment[ru]=Расширение для поддержки архивов с помощью библиотеки Kerfuffle
Comment[sk]=Modul pre spracovanie formátov archívov pre knižnicu Kerfuffle
Comment[sl]=Vstavek za ravnanje z arhivi za knjižnico Kerfuffle
Comment[sq]=Plugin që merret me formatet e arkivave për librarinë Kerfuffle
Comment[sr]=Прикључак за руковање форматима архива за библиотеку Керфафл
Comment[sr@ijekavian]=Прикључак за руковање форматима архива за библиотеку Керфафл
Comment[sr@ijekavianlatin]=Priključak za rukovanje formatima arhiva za biblioteku Kerfuffle
Comment[sr@latin]=Priključak za rukovanje formatima arhiva za biblioteku Kerfuffle
Comment[sv]=Insticksprogram för hantering av arkivformat för Kerfuffle-biblioteket
Comment[ta]=ி ி ி
Comment[th]= Kerfuffle
Comment[tr]=Kerfuffle kitaplığı için arşiv biçimleri eklentileri
Comment[uk]=Додаток для роботи з форматами архівів для бібліотеки Kerfuffle
Comment[x-test]=xxPlugin for handling of archive formats for the Kerfuffle libraryxx
Comment[zh_CN]=Kerfuffle
Comment[zh_TW]= Kerfuffle
# Priority. The greater the better.
[PropertyDef::X-KDE-Priority]
Type=int
# Revision of the plugin API.
[PropertyDef::X-KDE-Kerfuffle-APIRevision]
Type=int
# Is the plugin read-write or not? (i.e., can it create archives?)
[PropertyDef::X-KDE-Kerfuffle-ReadWrite]
Type=bool

View file

@ -0,0 +1,53 @@
/*
* This file is part of the KDE project
*
* Copyright (C) 2007 David Faure <faure@kde.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef KERFUFFLE_EXPORT_H
#define KERFUFFLE_EXPORT_H
/* needed for KDE_EXPORT and KDE_IMPORT macros */
#include <kdemacros.h>
#include <kpluginfactory.h>
#ifndef KERFUFFLE_EXPORT
# if defined(MAKE_KERFUFFLE_LIB)
/* We are building this library */
# define KERFUFFLE_EXPORT KDE_EXPORT
# else
/* We are using this library */
# define KERFUFFLE_EXPORT KDE_IMPORT
# endif
#endif
# ifndef KERFUFFLE_EXPORT_DEPRECATED
# define KERFUFFLE_EXPORT_DEPRECATED KDE_DEPRECATED KERFUFFLE_EXPORT
# endif
#define KERFUFFLE_EXPORT_PLUGIN(p) \
K_PLUGIN_FACTORY( ArkPluginFactory, registerPlugin< p >(); ) \
K_EXPORT_PLUGIN( ArkPluginFactory("p") )
#endif

204
ark/kerfuffle/queries.cpp Normal file
View file

@ -0,0 +1,204 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "queries.h"
#include <KLocale>
#include <KPasswordDialog>
#include <kdebug.h>
#include <kio/renamedialog.h>
#include <QApplication>
#include <QWeakPointer>
namespace Kerfuffle
{
Query::Query()
{
m_responseMutex.lock();
}
QVariant Query::response()
{
return m_data.value(QLatin1String( "response" ));
}
void Query::waitForResponse()
{
kDebug();
//if there is no response set yet, wait
if (!m_data.contains(QLatin1String("response"))) {
m_responseCondition.wait(&m_responseMutex);
}
m_responseMutex.unlock();
}
void Query::setResponse(QVariant response)
{
kDebug();
m_data[QLatin1String( "response" )] = response;
m_responseCondition.wakeAll();
}
OverwriteQuery::OverwriteQuery(const QString &filename) :
m_noRenameMode(false),
m_multiMode(true)
{
m_data[QLatin1String( "filename" )] = filename;
}
void OverwriteQuery::execute()
{
// If we are being called from the KPart, the cursor is probably Qt::WaitCursor
// at the moment (#231974)
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
KIO::RenameDialog_Mode mode = (KIO::RenameDialog_Mode)(KIO::M_OVERWRITE | KIO::M_SKIP);
if (m_noRenameMode) {
mode = (KIO::RenameDialog_Mode)(mode | KIO::M_NORENAME);
}
if (m_multiMode) {
mode = (KIO::RenameDialog_Mode)(mode | KIO::M_MULTI);
}
KUrl sourceUrl(m_data.value(QLatin1String( "filename" )).toString());
KUrl destUrl(m_data.value(QLatin1String( "filename" )).toString());
sourceUrl.cleanPath();
destUrl.cleanPath();
QWeakPointer<KIO::RenameDialog> dialog = new KIO::RenameDialog(
NULL,
i18n("File already exists"),
sourceUrl,
destUrl,
mode);
dialog.data()->exec();
m_data[QLatin1String("newFilename")] = dialog.data()->newDestUrl().pathOrUrl();
setResponse(dialog.data()->result());
delete dialog.data();
QApplication::restoreOverrideCursor();
}
bool OverwriteQuery::responseCancelled()
{
return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_CANCEL;
}
bool OverwriteQuery::responseOverwriteAll()
{
return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_OVERWRITE_ALL;
}
bool OverwriteQuery::responseOverwrite()
{
return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_OVERWRITE;
}
bool OverwriteQuery::responseRename()
{
return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_RENAME;
}
bool OverwriteQuery::responseSkip()
{
return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_SKIP;
}
bool OverwriteQuery::responseAutoSkip()
{
return m_data.value(QLatin1String( "response" )).toInt() == KIO::R_AUTO_SKIP;
}
QString OverwriteQuery::newFilename()
{
return m_data.value(QLatin1String( "newFilename" )).toString();
}
void OverwriteQuery::setNoRenameMode(bool enableNoRenameMode)
{
m_noRenameMode = enableNoRenameMode;
}
bool OverwriteQuery::noRenameMode()
{
return m_noRenameMode;
}
void OverwriteQuery::setMultiMode(bool enableMultiMode)
{
m_multiMode = enableMultiMode;
}
bool OverwriteQuery::multiMode()
{
return m_multiMode;
}
PasswordNeededQuery::PasswordNeededQuery(const QString& archiveFilename, bool incorrectTryAgain)
{
m_data[QLatin1String( "archiveFilename" )] = archiveFilename;
m_data[QLatin1String( "incorrectTryAgain" )] = incorrectTryAgain;
}
void PasswordNeededQuery::execute()
{
// If we are being called from the KPart, the cursor is probably Qt::WaitCursor
// at the moment (#231974)
QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
QWeakPointer<KPasswordDialog> dlg = new KPasswordDialog;
dlg.data()->setPrompt(i18nc("@info", "The archive <filename>%1</filename> is password protected. Please enter the password to extract the file.", m_data.value(QLatin1String( "archiveFilename" )).toString()));
if (m_data.value(QLatin1String("incorrectTryAgain")).toBool()) {
dlg.data()->showErrorMessage(i18n("Incorrect password, please try again."), KPasswordDialog::PasswordError);
}
const bool notCancelled = dlg.data()->exec();
const QString password = dlg.data()->password();
m_data[QLatin1String("password")] = password;
setResponse(notCancelled && !password.isEmpty());
QApplication::restoreOverrideCursor();
delete dlg.data();
}
QString PasswordNeededQuery::password()
{
return m_data.value(QLatin1String( "password" )).toString();
}
bool PasswordNeededQuery::responseCancelled()
{
return !m_data.value(QLatin1String( "response" )).toBool();
}
}

111
ark/kerfuffle/queries.h Normal file
View file

@ -0,0 +1,111 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef QUERIES_H
#define QUERIES_H
#include "kerfuffle_export.h"
#include <QString>
#include <QHash>
#include <QWaitCondition>
#include <QMutex>
#include <QVariant>
namespace Kerfuffle
{
typedef QHash<QString, QVariant> QueryData;
class KERFUFFLE_EXPORT Query
{
public:
/**
* Execute the response. Will happen in the GUI thread, so it's
* safe to use widgets/gui elements here. Must call setResponse
* when done.
*/
virtual void execute() = 0;
/**
* Will block until the response have been set
*/
void waitForResponse();
QVariant response();
protected:
/**
* Protected constructor
*/
Query();
virtual ~Query() {}
void setResponse(QVariant response);
QueryData m_data;
private:
QWaitCondition m_responseCondition;
QMutex m_responseMutex;
};
class KERFUFFLE_EXPORT OverwriteQuery : public Query
{
public:
explicit OverwriteQuery(const QString& filename);
void execute();
bool responseCancelled();
bool responseOverwriteAll();
bool responseOverwrite();
bool responseRename();
bool responseSkip();
bool responseAutoSkip();
QString newFilename();
void setNoRenameMode(bool enableNoRenameMode);
bool noRenameMode();
void setMultiMode(bool enableMultiMode);
bool multiMode();
private:
bool m_noRenameMode;
bool m_multiMode;
};
class KERFUFFLE_EXPORT PasswordNeededQuery : public Query
{
public:
explicit PasswordNeededQuery(const QString& archiveFilename, bool incorrectTryAgain = false);
void execute();
bool responseCancelled();
QString password();
};
}
#endif /* ifndef QUERIES_H */

View file

@ -0,0 +1,7 @@
File=ark.kcfg
ClassName=ArkSettings
Singleton=true
Mutators=true
SetUserTexts=true
IncludeFiles=\"kerfuffle/kerfuffle_export.h\"
Visibility=KERFUFFLE_EXPORT

View file

@ -0,0 +1,21 @@
include_directories(${KERFUFFLE_QJSON_INCLUDE_DIR})
set(RUNTIME_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
set(JSONINTERFACE_SOURCES
jsonarchiveinterface.cpp
jsonparser.cpp
)
kde4_add_library(jsoninterface STATIC ${JSONINTERFACE_SOURCES})
macro(KERFUFFLE_UNIT_TESTS)
foreach(_testname ${ARGN})
kde4_add_unit_test(${_testname} NOGUI ${_testname}.cpp)
target_link_libraries(${_testname} jsoninterface kerfuffle ${KDE4_KDEUI_LIBS} ${QT_QTTEST_LIBRARY} ${KERFUFFLE_QJSON_LIBRARIES})
endforeach(_testname)
endmacro(KERFUFFLE_UNIT_TESTS)
KERFUFFLE_UNIT_TESTS(
archivetest
jobstest
)

View file

@ -0,0 +1,76 @@
/*
* Copyright (c) 2010-2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "kerfuffle/archive.h"
#include <qtest_kde.h>
class ArchiveTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testFileName();
void testIsPasswordProtected();
void testOpenNonExistentFile();
};
QTEST_KDEMAIN_CORE(ArchiveTest)
void ArchiveTest::testFileName()
{
Kerfuffle::Archive *archive = Kerfuffle::Archive::create(QLatin1String("/tmp/foo.tar.gz"), this);
if (!archive) {
QSKIP("There is no plugin to handle tar.gz files. Skipping test.", SkipSingle);
}
QCOMPARE(archive->fileName(), QLatin1String("/tmp/foo.tar.gz"));
}
void ArchiveTest::testIsPasswordProtected()
{
Kerfuffle::Archive *archive;
archive = Kerfuffle::Archive::create(QLatin1String(KDESRCDIR "data/archivetest_encrypted.zip"), this);
if (!archive) {
QSKIP("There is no plugin to handle zip files. Skipping test.", SkipSingle);
}
QVERIFY(archive->isPasswordProtected());
archive->deleteLater();
archive = Kerfuffle::Archive::create(QLatin1String(KDESRCDIR "data/archivetest_unencrypted.zip"), this);
QVERIFY(!archive->isPasswordProtected());
}
void ArchiveTest::testOpenNonExistentFile()
{
QSKIP("How should we deal with files that do not exist? Should factory() return NULL?", SkipSingle);
}
#include "archivetest.moc"

View file

@ -0,0 +1,20 @@
[
{
"FileName": "aDir/",
"IsDirectory": true
},
{
"FileName": "aDir/b.txt"
},
{
"FileName": "aDir/aDirInside/",
"IsDirectory": true
},
{
"FileName": "aDir/aDirInside/anotherDir/",
"IsDirectory": true
},
{
"FileName": "aDir/aDirInside/anotherDir/file.txt"
}
]

View file

@ -0,0 +1,16 @@
[
{
"FileName": "aDir/",
"IsDirectory": true
},
{
"FileName": "aDir/b.txt"
},
{
"FileName": "anotherDir/",
"IsDirectory": true
},
{
"FileName": "anotherDir/file.txt"
}
]

View file

@ -0,0 +1,8 @@
[
{
"FileName": "a.txt"
},
{
"FileName": "file.txt"
}
]

View file

@ -0,0 +1,9 @@
[
{
"FileName": "aDir/",
"IsDirectory": true
},
{
"FileName": "aDir/b.txt"
}
]

View file

@ -0,0 +1,13 @@
[
{
"FileName": "foo.txt",
"IsPasswordProtected": true
},
{
"FileName": "bar.txt"
},
{
"FileName": "aDirectory/",
"IsDirectory": true
}
]

View file

@ -0,0 +1,5 @@
[
{
"FileName": "a.txt"
}
]

View file

@ -0,0 +1,16 @@
[
{
"FileName": "aDir/anotherDir/bar.txt"
},
{
"FileName": "aDir/foo.txt"
},
{
"FileName": "aDir/anotherDir/",
"IsDirectory": true
},
{
"FileName": "aDir/",
"IsDirectory": true
}
]

View file

@ -0,0 +1,15 @@
[
{
"FileName": "a.txt"
},
{
"FileName": "aDir/",
"IsDirectory": true
},
{
"FileName": "aDir/b.txt"
},
{
"FileName": "c.txt"
}
]

View file

@ -0,0 +1,18 @@
[
{
"FileName": "a.txt",
"Size": 5
},
{
"FileName": "aDir/",
"IsDirectory": true
},
{
"FileName": "aDir/b.txt",
"Size": 954
},
{
"FileName": "c.txt",
"Size": 45000
}
]

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,366 @@
/*
* Copyright (c) 2010-2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "kerfuffle/jobs.h"
#include "jsonarchiveinterface.h"
#include <kdebug.h>
#include <kglobal.h>
#include <qtest_kde.h>
#include <qeventloop.h>
#include <qsignalspy.h>
using Kerfuffle::ArchiveEntry;
class JobsTest : public QObject
{
Q_OBJECT
public:
JobsTest();
protected Q_SLOTS:
void init();
void slotNewEntry(const ArchiveEntry& entry);
private Q_SLOTS:
// ListJob-related tests
void testExtractedFilesSize();
void testIsPasswordProtected();
void testIsSingleFolderArchive();
void testListEntries();
// ExtractJob-related tests
void testExtractJobAccessors();
// DeleteJob-related tests
void testRemoveEntry();
// AddJob-related tests
void testAddEntry();
private:
JSONArchiveInterface *createArchiveInterface(const QString& filePath);
QList<Kerfuffle::ArchiveEntry> listEntries(JSONArchiveInterface *iface);
void startAndWaitForResult(KJob *job);
QList<Kerfuffle::ArchiveEntry> m_entries;
QEventLoop m_eventLoop;
};
QTEST_KDEMAIN_CORE(JobsTest)
JobsTest::JobsTest()
: QObject(0)
, m_eventLoop(this)
{
// Hackish way to make sure the i18n stuff
// is called from the main thread
KGlobal::locale();
qRegisterMetaType<ArchiveEntry>("ArchiveEntry");
}
void JobsTest::init()
{
m_entries.clear();
}
JSONArchiveInterface *JobsTest::createArchiveInterface(const QString& filePath)
{
QVariantList args;
args.append(filePath);
JSONArchiveInterface *iface = new JSONArchiveInterface(this, args);
if (!iface->open()) {
kDebug() << "Could not open" << filePath;
return NULL;
}
return iface;
}
void JobsTest::startAndWaitForResult(KJob *job)
{
connect(job, SIGNAL(result(KJob*)), &m_eventLoop, SLOT(quit()));
job->start();
m_eventLoop.exec();
}
void JobsTest::testExtractedFilesSize()
{
Kerfuffle::ListJob *listJob;
JSONArchiveInterface *noSizeIface =
createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json"));
JSONArchiveInterface *sizeIface =
createArchiveInterface(QLatin1String(KDESRCDIR "data/archive002.json"));
listJob = new Kerfuffle::ListJob(noSizeIface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QCOMPARE(listJob->extractedFilesSize(), 0LL);
listJob = new Kerfuffle::ListJob(sizeIface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QCOMPARE(listJob->extractedFilesSize(), 45959LL);
noSizeIface->deleteLater();
sizeIface->deleteLater();
}
void JobsTest::testIsPasswordProtected()
{
Kerfuffle::ListJob *listJob;
JSONArchiveInterface *noPasswordIface =
createArchiveInterface(QLatin1String(KDESRCDIR "data/archive002.json"));
JSONArchiveInterface *passwordIface =
createArchiveInterface(QLatin1String(KDESRCDIR "data/archive-password.json"));
listJob = new Kerfuffle::ListJob(noPasswordIface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QVERIFY(!listJob->isPasswordProtected());
listJob = new Kerfuffle::ListJob(passwordIface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QVERIFY(listJob->isPasswordProtected());
noPasswordIface->deleteLater();
passwordIface->deleteLater();
}
void JobsTest::testIsSingleFolderArchive()
{
JSONArchiveInterface *iface =
createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json"));
Kerfuffle::ListJob *listJob = new Kerfuffle::ListJob(iface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QVERIFY(!listJob->isSingleFolderArchive());
QCOMPARE(listJob->subfolderName(), QString());
iface->deleteLater();
iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive-singlefile.json"));
listJob = new Kerfuffle::ListJob(iface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QVERIFY(listJob->isSingleFolderArchive());
QCOMPARE(listJob->subfolderName(), QLatin1String("a.txt"));
iface->deleteLater();
iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive-onetopfolder.json"));
listJob = new Kerfuffle::ListJob(iface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QVERIFY(listJob->isSingleFolderArchive());
QCOMPARE(listJob->subfolderName(), QLatin1String("aDir"));
iface->deleteLater();
iface = createArchiveInterface
(QLatin1String(KDESRCDIR "data/archive-multiplefolders.json"));
listJob = new Kerfuffle::ListJob(iface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QVERIFY(!listJob->isSingleFolderArchive());
QCOMPARE(listJob->subfolderName(), QString());
iface->deleteLater();
iface = createArchiveInterface
(QLatin1String(KDESRCDIR "data/archive-nodir-manyfiles.json"));
listJob = new Kerfuffle::ListJob(iface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QVERIFY(!listJob->isSingleFolderArchive());
QCOMPARE(listJob->subfolderName(), QString());
iface->deleteLater();
iface = createArchiveInterface
(QLatin1String(KDESRCDIR "data/archive-deepsinglehierarchy.json"));
listJob = new Kerfuffle::ListJob(iface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QVERIFY(listJob->isSingleFolderArchive());
QCOMPARE(listJob->subfolderName(), QLatin1String("aDir"));
iface->deleteLater();
iface = createArchiveInterface
(QLatin1String(KDESRCDIR "data/archive-unorderedsinglefolder.json"));
listJob = new Kerfuffle::ListJob(iface, this);
listJob->setAutoDelete(false);
startAndWaitForResult(listJob);
QVERIFY(listJob->isSingleFolderArchive());
QCOMPARE(listJob->subfolderName(), QLatin1String("aDir"));
iface->deleteLater();
}
void JobsTest::testListEntries()
{
JSONArchiveInterface *iface =
createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json"));
QList<Kerfuffle::ArchiveEntry> archiveEntries(listEntries(iface));
QStringList entries;
entries.append(QLatin1String("a.txt"));
entries.append(QLatin1String("aDir/"));
entries.append(QLatin1String("aDir/b.txt"));
entries.append(QLatin1String("c.txt"));
QCOMPARE(entries.count(), archiveEntries.count());
for (int i = 0; i < entries.count(); ++i) {
Kerfuffle::ArchiveEntry e(archiveEntries.at(i));
QCOMPARE(entries[i], e[Kerfuffle::FileName].toString());
}
iface->deleteLater();
}
void JobsTest::slotNewEntry(const ArchiveEntry& entry)
{
m_entries.append(entry);
}
QList<Kerfuffle::ArchiveEntry> JobsTest::listEntries(JSONArchiveInterface *iface)
{
m_entries.clear();
Kerfuffle::ListJob *listJob = new Kerfuffle::ListJob(iface, this);
connect(listJob, SIGNAL(newEntry(ArchiveEntry)),
SLOT(slotNewEntry(ArchiveEntry)));
startAndWaitForResult(listJob);
return m_entries;
}
void JobsTest::testExtractJobAccessors()
{
JSONArchiveInterface *iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json"));
Kerfuffle::ExtractJob *job =
new Kerfuffle::ExtractJob(QVariantList(), QLatin1String("/tmp/some-dir"),
Kerfuffle::ExtractionOptions(), iface, this);
Kerfuffle::ExtractionOptions defaultOptions;
defaultOptions[QLatin1String("PreservePaths")] = false;
QCOMPARE(job->destinationDirectory(), QLatin1String("/tmp/some-dir"));
QCOMPARE(job->extractionOptions(), defaultOptions);
job->setAutoDelete(false);
startAndWaitForResult(job);
QCOMPARE(job->destinationDirectory(), QLatin1String("/tmp/some-dir"));
QCOMPARE(job->extractionOptions(), defaultOptions);
Kerfuffle::ExtractionOptions options;
options[QLatin1String("PreservePaths")] = true;
options[QLatin1String("foo")] = QLatin1String("bar");
options[QLatin1String("pi")] = 3.14f;
job = new Kerfuffle::ExtractJob(QVariantList(), QLatin1String("/root"),
options, iface, this);
QCOMPARE(job->destinationDirectory(), QLatin1String("/root"));
QCOMPARE(job->extractionOptions(), options);
job->setAutoDelete(false);
startAndWaitForResult(job);
QCOMPARE(job->destinationDirectory(), QLatin1String("/root"));
QCOMPARE(job->extractionOptions(), options);
}
void JobsTest::testRemoveEntry()
{
QVariantList filesToDelete;
JSONArchiveInterface *iface;
Kerfuffle::DeleteJob *deleteJob;
QStringList expectedEntries;
filesToDelete.append(QLatin1String("c.txt"));
iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json"));
deleteJob = new Kerfuffle::DeleteJob(filesToDelete, iface, this);
startAndWaitForResult(deleteJob);
QList<Kerfuffle::ArchiveEntry> archiveEntries(listEntries(iface));
expectedEntries.append(QLatin1String("a.txt"));
expectedEntries.append(QLatin1String("aDir/"));
expectedEntries.append(QLatin1String("aDir/b.txt"));
QCOMPARE(archiveEntries.count(), expectedEntries.count());
for (int i = 0; i < expectedEntries.count(); ++i) {
const Kerfuffle::ArchiveEntry e(archiveEntries.at(i));
QCOMPARE(expectedEntries[i], e[Kerfuffle::FileName].toString());
}
iface->deleteLater();
// TODO: test for errors
}
void JobsTest::testAddEntry()
{
JSONArchiveInterface *iface = createArchiveInterface(QLatin1String(KDESRCDIR "data/archive001.json"));
QList<Kerfuffle::ArchiveEntry> archiveEntries = listEntries(iface);
QCOMPARE(archiveEntries.count(), 4);
QStringList newEntries = QStringList() << QLatin1String("foo");
Kerfuffle::AddJob *addJob =
new Kerfuffle::AddJob(newEntries, Kerfuffle::CompressionOptions(), iface, this);
startAndWaitForResult(addJob);
archiveEntries = listEntries(iface);
QCOMPARE(archiveEntries.count(), 5);
addJob = new Kerfuffle::AddJob(newEntries, Kerfuffle::CompressionOptions(), iface, this);
startAndWaitForResult(addJob);
archiveEntries = listEntries(iface);
QCOMPARE(archiveEntries.count(), 5);
newEntries = QStringList() << QLatin1String("bar") << QLatin1String("aDir/test.txt");
addJob = new Kerfuffle::AddJob(newEntries, Kerfuffle::CompressionOptions(), iface, this);
startAndWaitForResult(addJob);
archiveEntries = listEntries(iface);
QCOMPARE(archiveEntries.count(), 7);
iface->deleteLater();
}
#include "jobstest.moc"

View file

@ -0,0 +1,109 @@
/*
* Copyright (c) 2010-2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "jsonarchiveinterface.h"
#include <kdebug.h>
#include <qjson/parser.h>
#include <qfile.h>
JSONArchiveInterface::JSONArchiveInterface(QObject *parent, const QVariantList& args)
: Kerfuffle::ReadWriteArchiveInterface(parent, args)
{
}
JSONArchiveInterface::~JSONArchiveInterface()
{
}
bool JSONArchiveInterface::list()
{
JSONParser::JSONArchive::const_iterator it = m_archive.constBegin();
for (; it != m_archive.constEnd(); ++it) {
emit entry(*it);
}
return true;
}
bool JSONArchiveInterface::open()
{
QFile file(filename());
if (!file.exists()) {
return false;
}
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
return false;
}
m_archive = JSONParser::parse(&file);
return !m_archive.isEmpty();
}
bool JSONArchiveInterface::addFiles(const QStringList& files, const Kerfuffle::CompressionOptions& options)
{
Q_UNUSED(options)
foreach (const QString& file, files) {
if (m_archive.contains(file)) {
return false;
}
Kerfuffle::ArchiveEntry e;
e[Kerfuffle::FileName] = file;
m_archive[file] = e;
}
return true;
}
bool JSONArchiveInterface::copyFiles(const QList<QVariant>& files, const QString& destinationDirectory, Kerfuffle::ExtractionOptions options)
{
Q_UNUSED(files)
Q_UNUSED(destinationDirectory)
Q_UNUSED(options)
return true;
}
bool JSONArchiveInterface::deleteFiles(const QList<QVariant>& files)
{
foreach (const QVariant& file, files) {
const QString fileName = file.toString();
if (m_archive.contains(fileName)) {
m_archive.remove(fileName);
emit entryRemoved(fileName);
}
}
return true;
}
#include "jsonarchiveinterface.moc"

View file

@ -0,0 +1,67 @@
/*
* Copyright (c) 2010-2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef JSONARCHIVEINTERFACE_H
#define JSONARCHIVEINTERFACE_H
#include "jsonparser.h"
#include "kerfuffle/archive.h"
#include "kerfuffle/archiveinterface.h"
/**
* A dummy archive interface used by our test cases.
*
* It reads a JSON file which defines the contents of the archive.
* For the file format description, see the documentation for @c JSONParser.
*
* The file's content is read to memory when open() is called and the archive
* is then closed. This means that this class never changes the file's content
* on disk, and entry addition or deletion do not change the original file.
*
* @sa JSONParser
*
* @author Raphael Kubo da Costa <rakuco@FreeBSD.org>
*/
class JSONArchiveInterface : public Kerfuffle::ReadWriteArchiveInterface
{
Q_OBJECT
public:
explicit JSONArchiveInterface(QObject *parent, const QVariantList& args);
virtual ~JSONArchiveInterface();
virtual bool list();
virtual bool open();
virtual bool addFiles(const QStringList& files, const Kerfuffle::CompressionOptions& options);
virtual bool copyFiles(const QList<QVariant>& files, const QString& destinationDirectory, Kerfuffle::ExtractionOptions options);
virtual bool deleteFiles(const QList<QVariant>& files);
private:
JSONParser::JSONArchive m_archive;
};
#endif

View file

@ -0,0 +1,133 @@
/*
* Copyright (c) 2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "jsonparser.h"
#include "kerfuffle/archiveinterface.h"
#include <KDE/KDebug>
#include <QtCore/QLatin1String>
#include <qjson/parser.h>
typedef QMap<QString, Kerfuffle::EntryMetaDataType> ArchiveProperties;
static ArchiveProperties archiveProperties()
{
static ArchiveProperties properties;
if (!properties.isEmpty()) {
return properties;
}
properties[QLatin1String("FileName")] = Kerfuffle::FileName;
properties[QLatin1String("InternalID")] = Kerfuffle::InternalID;
properties[QLatin1String("Permissions")] = Kerfuffle::Permissions;
properties[QLatin1String("Owner")] = Kerfuffle::Owner;
properties[QLatin1String("Group")] = Kerfuffle::Group;
properties[QLatin1String("Size")] = Kerfuffle::Size;
properties[QLatin1String("CompressedSize")] = Kerfuffle::CompressedSize;
properties[QLatin1String("Link")] = Kerfuffle::Link;
properties[QLatin1String("Ratio")] = Kerfuffle::Ratio;
properties[QLatin1String("CRC")] = Kerfuffle::CRC;
properties[QLatin1String("Method")] = Kerfuffle::Method;
properties[QLatin1String("Version")] = Kerfuffle::Version;
properties[QLatin1String("Timestamp")] = Kerfuffle::Timestamp;
properties[QLatin1String("IsDirectory")] = Kerfuffle::IsDirectory;
properties[QLatin1String("Comment")] = Kerfuffle::Comment;
properties[QLatin1String("IsPasswordProtected")] = Kerfuffle::IsPasswordProtected;
return properties;
}
JSONParser::JSONParser()
{
}
JSONParser::~JSONParser()
{
}
JSONParser::JSONArchive JSONParser::parse(const QString &json)
{
bool ok;
QJson::Parser parser;
const QVariant result = parser.parse(json.toUtf8(), &ok);
if (!ok) {
kDebug() << "Line" << parser.errorLine() << ":" << parser.errorString();
return JSONParser::JSONArchive();
}
return createJSONArchive(result);
}
JSONParser::JSONArchive JSONParser::parse(QIODevice *json)
{
bool ok;
QJson::Parser parser;
const QVariant result = parser.parse(json, &ok);
if (!ok) {
kDebug() << "Line" << parser.errorLine() << ":" << parser.errorString();
return JSONParser::JSONArchive();
}
return createJSONArchive(result);
}
JSONParser::JSONArchive JSONParser::createJSONArchive(const QVariant &json)
{
static const ArchiveProperties properties = archiveProperties();
JSONParser::JSONArchive archive;
foreach (const QVariant &entry, json.toList()) {
const QVariantMap entryMap = entry.toMap();
if (!entryMap.contains(QLatin1String("FileName"))) {
continue;
}
Kerfuffle::ArchiveEntry archiveEntry;
QVariantMap::const_iterator entryIterator = entryMap.constBegin();
for (; entryIterator != entryMap.constEnd(); ++entryIterator) {
if (properties.contains(entryIterator.key())) {
archiveEntry[properties[entryIterator.key()]] = entryIterator.value();
} else {
kDebug() << entryIterator.key() << "is not a valid entry key";
}
}
const QString fileName = entryMap[QLatin1String("FileName")].toString();
archive[fileName] = archiveEntry;
}
return archive;
}

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2011 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ( INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef JSONPARSER_H
#define JSONPARSER_H
#include "kerfuffle/archive.h"
#include <QtCore/QIODevice>
#include <QtCore/QMap>
#include <QtCore/QString>
/**
* Simple parser which reads JSON files and creates @c ArchiveEntry objects
* from it.
*
* The JSON file is expected to follow a specific format that describes an
* archive read by Kerfuffle.
*
* The format consists of a list of dictionaries whose keys are values from the
* EntryMetaDataType enum. The only required key for each entry is FileName;
* other values which are omitted for each entry are assumed to be 0 or false.
*
* Example file:
* @code
* [
* { "FileName": "foo", "IsPasswordProtected": true },
* { "FileName": "aDir/", "IsDirectory": true }
* ]
* @endcode
*
* @author Raphael Kubo da Costa <rakuco@FreeBSD.org>
*/
class JSONParser
{
public:
typedef QMap<QString, Kerfuffle::ArchiveEntry> JSONArchive;
~JSONParser();
static JSONArchive parse(const QString &json);
static JSONArchive parse(QIODevice *json);
private:
JSONParser();
/**
* Parses each entry in the QVariant obtained from parsing a JSON file and
* creates a @c JSONArchive from them.
*
* If an entry does not have a "FileName" key, it is ignored. Keys which do
* not correspond to a value in the EntryMetaDataType enum are ignored.
*
* @return A new @c JSONArchive corresponding to the parsed JSON file. If a
* parsing error occurs, it is empty.
*/
static JSONArchive createJSONArchive(const QVariant &json);
};
#endif // JSONPARSER_H

30
ark/part/CMakeLists.txt Normal file
View file

@ -0,0 +1,30 @@
set(arkpart_PART_SRCS
part.cpp
infopanel.cpp
arkviewer.cpp
archivemodel.cpp
archiveview.cpp
jobtracker.cpp
)
qt4_add_dbus_adaptor(arkpart_PART_SRCS dnddbusinterface.xml part.h Ark::Part)
kde4_add_ui_files(arkpart_PART_SRCS infopanel.ui )
kde4_add_ui_files(arkpart_PART_SRCS jobtracker.ui )
kde4_add_plugin(arkpart ${arkpart_PART_SRCS})
target_link_libraries(arkpart kerfuffle ${KDE4_KFILE_LIBS} ${KDE4_KPARTS_LIBS} ${KDE4_KDEUI_LIBS})
configure_file(
${CMAKE_CURRENT_SOURCE_DIR}/ark_part.desktop.cmake
${CMAKE_CURRENT_BINARY_DIR}/ark_part.desktop
)
install(TARGETS arkpart DESTINATION ${PLUGIN_INSTALL_DIR})
########### install files ###############
install( FILES ${CMAKE_CURRENT_BINARY_DIR}/ark_part.desktop DESTINATION ${SERVICES_INSTALL_DIR} )
install( FILES ark_part.rc DESTINATION ${DATA_INSTALL_DIR}/ark )

986
ark/part/archivemodel.cpp Normal file
View file

@ -0,0 +1,986 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2010-2012 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "archivemodel.h"
#include "kerfuffle/archive.h"
#include "kerfuffle/jobs.h"
#include <KDebug>
#include <KIconLoader>
#include <KLocale>
#include <KMimeType>
#include <KIO/NetAccess>
#include <QDir>
#include <QFont>
#include <QLatin1String>
#include <QList>
#include <QMimeData>
#include <QPersistentModelIndex>
#include <QPixmap>
#include <QtDBus/QtDBus>
using namespace Kerfuffle;
class ArchiveDirNode;
//used to speed up the loading of large archives
static ArchiveNode* s_previousMatch = NULL;
K_GLOBAL_STATIC(QStringList, s_previousPieces)
// TODO: This class hierarchy needs some love.
// Having a parent take a child class as a parameter in the constructor
// should trigger one's spider-sense (TM).
class ArchiveNode
{
public:
ArchiveNode(ArchiveDirNode *parent, const ArchiveEntry & entry)
: m_parent(parent)
{
setEntry(entry);
}
virtual ~ArchiveNode()
{
}
const ArchiveEntry &entry() const
{
return m_entry;
}
void setEntry(const ArchiveEntry& entry)
{
m_entry = entry;
const QStringList pieces = entry[FileName].toString().split(QLatin1Char( '/' ), QString::SkipEmptyParts);
m_name = pieces.isEmpty() ? QString() : pieces.last();
if (entry[IsDirectory].toBool()) {
m_icon = KIconLoader::global()->loadMimeTypeIcon(KMimeType::mimeType(QLatin1String("inode/directory"))->iconName(), KIconLoader::Small);
} else {
const KMimeType::Ptr mimeType = KMimeType::findByPath(m_entry[FileName].toString(), 0, true);
m_icon = KIconLoader::global()->loadMimeTypeIcon(mimeType->iconName(), KIconLoader::Small);
}
}
ArchiveDirNode *parent() const
{
return m_parent;
}
int row() const;
virtual bool isDir() const
{
return false;
}
QPixmap icon() const
{
return m_icon;
}
QString name() const
{
return m_name;
}
protected:
void setIcon(const QPixmap &icon)
{
m_icon = icon;
}
private:
ArchiveEntry m_entry;
QPixmap m_icon;
QString m_name;
ArchiveDirNode *m_parent;
};
class ArchiveDirNode: public ArchiveNode
{
public:
ArchiveDirNode(ArchiveDirNode *parent, const ArchiveEntry & entry)
: ArchiveNode(parent, entry)
{
}
~ArchiveDirNode()
{
clear();
}
QList<ArchiveNode*> entries()
{
return m_entries;
}
void setEntryAt(int index, ArchiveNode* value)
{
m_entries[index] = value;
}
void appendEntry(ArchiveNode* entry)
{
m_entries.append(entry);
}
void removeEntryAt(int index)
{
delete m_entries.takeAt(index);
}
virtual bool isDir() const
{
return true;
}
ArchiveNode* find(const QString & name)
{
foreach(ArchiveNode *node, m_entries) {
if (node && (node->name() == name)) {
return node;
}
}
return 0;
}
ArchiveNode* findByPath(const QStringList & pieces, int index = 0)
{
if (index == pieces.count()) {
return 0;
}
ArchiveNode *next = find(pieces.at(index));
if (index == pieces.count() - 1) {
return next;
}
if (next && next->isDir()) {
return static_cast<ArchiveDirNode*>(next)->findByPath(pieces, index + 1);
}
return 0;
}
void returnDirNodes(QList<ArchiveDirNode*> *store)
{
foreach(ArchiveNode *node, m_entries) {
if (node->isDir()) {
store->prepend(static_cast<ArchiveDirNode*>(node));
static_cast<ArchiveDirNode*>(node)->returnDirNodes(store);
}
}
}
void clear()
{
qDeleteAll(m_entries);
m_entries.clear();
}
private:
QList<ArchiveNode*> m_entries;
};
/**
* Helper functor used by qStableSort.
*
* It always sorts folders before files.
*
* @internal
*/
class ArchiveModelSorter
{
public:
ArchiveModelSorter(int column, Qt::SortOrder order)
: m_sortColumn(column)
, m_sortOrder(order)
{
}
virtual ~ArchiveModelSorter()
{
}
inline bool operator()(const QPair<ArchiveNode*, int> &left, const QPair<ArchiveNode*, int> &right) const
{
if (m_sortOrder == Qt::AscendingOrder) {
return lessThan(left, right);
} else {
return !lessThan(left, right);
}
}
protected:
bool lessThan(const QPair<ArchiveNode*, int> &left, const QPair<ArchiveNode*, int> &right) const
{
const ArchiveNode * const leftNode = left.first;
const ArchiveNode * const rightNode = right.first;
// #234373: sort folders before files
if ((leftNode->isDir()) && (!rightNode->isDir())) {
return (m_sortOrder == Qt::AscendingOrder);
} else if ((!leftNode->isDir()) && (rightNode->isDir())) {
return !(m_sortOrder == Qt::AscendingOrder);
}
const QVariant &leftEntry = leftNode->entry()[m_sortColumn];
const QVariant &rightEntry = rightNode->entry()[m_sortColumn];
switch (m_sortColumn) {
case FileName:
return leftNode->name() < rightNode->name();
case Size:
case CompressedSize:
return leftEntry.toInt() < rightEntry.toInt();
default:
return leftEntry.toString() < rightEntry.toString();
}
// We should not get here.
Q_ASSERT(false);
return false;
}
private:
int m_sortColumn;
Qt::SortOrder m_sortOrder;
};
int ArchiveNode::row() const
{
if (parent()) {
return parent()->entries().indexOf(const_cast<ArchiveNode*>(this));
}
return 0;
}
ArchiveModel::ArchiveModel(const QString &dbusPathName, QObject *parent)
: QAbstractItemModel(parent)
, m_rootNode(new ArchiveDirNode(0, ArchiveEntry()))
, m_dbusPathName(dbusPathName)
{
}
ArchiveModel::~ArchiveModel()
{
delete m_rootNode;
m_rootNode = 0;
}
QVariant ArchiveModel::data(const QModelIndex &index, int role) const
{
if (index.isValid()) {
ArchiveNode *node = static_cast<ArchiveNode*>(index.internalPointer());
switch (role) {
case Qt::DisplayRole: {
//TODO: complete the columns
int columnId = m_showColumns.at(index.column());
switch (columnId) {
case FileName:
return node->name();
case Size:
if (node->isDir()) {
int dirs;
int files;
const int children = childCount(index, dirs, files);
return KIO::itemsSummaryString(children, files, dirs, 0, false);
} else if (node->entry().contains(Link)) {
return QVariant();
} else {
return KIO::convertSize(node->entry()[ Size ].toULongLong());
}
case CompressedSize:
if (node->isDir() || node->entry().contains(Link)) {
return QVariant();
} else {
qulonglong compressedSize = node->entry()[ CompressedSize ].toULongLong();
if (compressedSize != 0) {
return KIO::convertSize(compressedSize);
} else {
return QVariant();
}
}
case Ratio: // TODO: Use node->entry()[Ratio] when available
if (node->isDir() || node->entry().contains(Link)) {
return QVariant();
} else {
qulonglong compressedSize = node->entry()[ CompressedSize ].toULongLong();
qulonglong size = node->entry()[ Size ].toULongLong();
if (compressedSize == 0 || size == 0) {
return QVariant();
} else {
int ratio = int(100 * ((double)size - compressedSize) / size);
return QString(QString::number(ratio) + QLatin1String( " %" ));
}
}
case Timestamp: {
const QDateTime timeStamp = node->entry().value(Timestamp).toDateTime();
return KGlobal::locale()->formatDateTime(timeStamp);
}
default:
return node->entry().value(columnId);
}
break;
}
case Qt::DecorationRole:
if (index.column() == 0) {
return node->icon();
}
return QVariant();
case Qt::FontRole: {
QFont f;
f.setItalic(node->entry()[ IsPasswordProtected ].toBool());
return f;
}
default:
return QVariant();
}
}
return QVariant();
}
Qt::ItemFlags ArchiveModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
if (index.isValid()) {
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | defaultFlags;
}
return 0;
}
QVariant ArchiveModel::headerData(int section, Qt::Orientation, int role) const
{
if (role == Qt::DisplayRole) {
if (section >= m_showColumns.size()) {
kDebug() << "WEIRD: showColumns.size = " << m_showColumns.size()
<< " and section = " << section;
return QVariant();
}
int columnId = m_showColumns.at(section);
switch (columnId) {
case FileName:
return i18nc("Name of a file inside an archive", "Name");
case Size:
return i18nc("Uncompressed size of a file inside an archive", "Size");
case CompressedSize:
return i18nc("Compressed size of a file inside an archive", "Compressed");
case Ratio:
return i18nc("Compression rate of file", "Rate");
case Owner:
return i18nc("File's owner username", "Owner");
case Group:
return i18nc("File's group", "Group");
case Permissions:
return i18nc("File permissions", "Mode");
case CRC:
return i18nc("CRC hash code", "CRC");
case Method:
return i18nc("Compression method", "Method");
case Version:
//TODO: what exactly is a file version?
return i18nc("File version", "Version");
case Timestamp:
return i18nc("Timestamp", "Date");
case Comment:
return i18nc("File comment", "Comment");
default:
return i18nc("Unnamed column", "??");
}
}
return QVariant();
}
QModelIndex ArchiveModel::index(int row, int column, const QModelIndex &parent) const
{
if (hasIndex(row, column, parent)) {
ArchiveDirNode *parentNode = parent.isValid() ? static_cast<ArchiveDirNode*>(parent.internalPointer()) : m_rootNode;
Q_ASSERT(parentNode->isDir());
ArchiveNode *item = parentNode->entries().value(row, 0);
if (item) {
return createIndex(row, column, item);
}
}
return QModelIndex();
}
QModelIndex ArchiveModel::parent(const QModelIndex &index) const
{
if (index.isValid()) {
ArchiveNode *item = static_cast<ArchiveNode*>(index.internalPointer());
Q_ASSERT(item);
if (item->parent() && (item->parent() != m_rootNode)) {
return createIndex(item->parent()->row(), 0, item->parent());
}
}
return QModelIndex();
}
ArchiveEntry ArchiveModel::entryForIndex(const QModelIndex &index)
{
if (index.isValid()) {
ArchiveNode *item = static_cast<ArchiveNode*>(index.internalPointer());
Q_ASSERT(item);
return item->entry();
}
return ArchiveEntry();
}
int ArchiveModel::childCount(const QModelIndex &index, int &dirs, int &files) const
{
if (index.isValid()) {
dirs = files = 0;
ArchiveNode *item = static_cast<ArchiveNode*>(index.internalPointer());
Q_ASSERT(item);
if (item->isDir()) {
const QList<ArchiveNode*> entries = static_cast<ArchiveDirNode*>(item)->entries();
foreach(const ArchiveNode *node, entries) {
if (node->isDir()) {
dirs++;
} else {
files++;
}
}
return entries.count();
}
return 0;
}
return -1;
}
int ArchiveModel::rowCount(const QModelIndex &parent) const
{
if (parent.column() <= 0) {
ArchiveNode *parentNode = parent.isValid() ? static_cast<ArchiveNode*>(parent.internalPointer()) : m_rootNode;
if (parentNode && parentNode->isDir()) {
return static_cast<ArchiveDirNode*>(parentNode)->entries().count();
}
}
return 0;
}
int ArchiveModel::columnCount(const QModelIndex &parent) const
{
return m_showColumns.size();
if (parent.isValid()) {
return static_cast<ArchiveNode*>(parent.internalPointer())->entry().size();
}
}
void ArchiveModel::sort(int column, Qt::SortOrder order)
{
if (m_showColumns.size() <= column) {
return;
}
emit layoutAboutToBeChanged();
QList<ArchiveDirNode*> dirNodes;
m_rootNode->returnDirNodes(&dirNodes);
dirNodes.append(m_rootNode);
const ArchiveModelSorter modelSorter(m_showColumns.at(column), order);
foreach(ArchiveDirNode* dir, dirNodes) {
QVector < QPair<ArchiveNode*,int> > sorting(dir->entries().count());
for (int i = 0; i < dir->entries().count(); ++i) {
ArchiveNode *item = dir->entries().at(i);
sorting[i].first = item;
sorting[i].second = i;
}
qStableSort(sorting.begin(), sorting.end(), modelSorter);
QModelIndexList fromIndexes;
QModelIndexList toIndexes;
for (int r = 0; r < sorting.count(); ++r) {
ArchiveNode *item = sorting.at(r).first;
toIndexes.append(createIndex(r, 0, item));
fromIndexes.append(createIndex(sorting.at(r).second, 0, sorting.at(r).first));
dir->setEntryAt(r, sorting.at(r).first);
}
changePersistentIndexList(fromIndexes, toIndexes);
emit dataChanged(
index(0, 0, indexForNode(dir)),
index(dir->entries().size() - 1, 0, indexForNode(dir)));
}
emit layoutChanged();
}
Qt::DropActions ArchiveModel::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
QStringList ArchiveModel::mimeTypes() const
{
QStringList types;
// MIME types we accept for dragging (eg. Dolphin -> Ark).
types << QLatin1String("text/uri-list")
<< QLatin1String("text/plain")
<< QLatin1String("text/x-moz-url");
// MIME types we accept for dropping (eg. Ark -> Dolphin).
types << QLatin1String("application/x-kde-ark-dndextract-service")
<< QLatin1String("application/x-kde-ark-dndextract-path");
return types;
}
QMimeData *ArchiveModel::mimeData(const QModelIndexList &indexes) const
{
Q_UNUSED(indexes)
QMimeData *mimeData = new QMimeData;
mimeData->setData(QLatin1String("application/x-kde-ark-dndextract-service"),
QDBusConnection::sessionBus().baseService().toUtf8());
mimeData->setData(QLatin1String("application/x-kde-ark-dndextract-path"),
m_dbusPathName.toUtf8());
return mimeData;
}
bool ArchiveModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
{
Q_UNUSED(action)
Q_UNUSED(row)
Q_UNUSED(column)
Q_UNUSED(parent)
if (!data->hasUrls()) {
return false;
}
QStringList paths;
foreach(const QUrl &url, data->urls()) {
paths << url.toLocalFile();
}
//for now, this code is not used because adding files to paths inside the
//archive is not supported yet. need a solution for this later.
QString path;
#if 0
if (parent.isValid()) {
QModelIndex droppedOnto = index(row, column, parent);
if (entryForIndex(droppedOnto).value(IsDirectory).toBool()) {
kDebug() << "Using entry";
path = entryForIndex(droppedOnto).value(FileName).toString();
} else {
path = entryForIndex(parent).value(FileName).toString();
}
}
kDebug() << "Dropped onto " << path;
#endif
emit droppedFiles(paths, path);
return true;
}
// For a rationale, see bugs #194241 and #241967
QString ArchiveModel::cleanFileName(const QString& fileName)
{
if ((fileName == QLatin1String("/")) ||
(fileName == QLatin1String("."))) { // "." is present in ISO files
return QString();
} else if (fileName.startsWith(QLatin1String("./"))) {
return fileName.mid(2);
}
return fileName;
}
ArchiveDirNode* ArchiveModel::parentFor(const ArchiveEntry& entry)
{
QStringList pieces = entry[ FileName ].toString().split(QLatin1Char( '/' ), QString::SkipEmptyParts);
if (pieces.isEmpty()) {
return NULL;
}
pieces.removeLast();
if (s_previousMatch) {
//the number of path elements must be the same for the shortcut
//to work
if (s_previousPieces->count() == pieces.count()) {
bool equal = true;
//make sure all the pieces match up
for (int i = 0; i < s_previousPieces->count(); ++i) {
if (s_previousPieces->at(i) != pieces.at(i)) {
equal = false;
break;
}
}
//if match return it
if (equal) {
return static_cast<ArchiveDirNode*>(s_previousMatch);
}
}
}
ArchiveDirNode *parent = m_rootNode;
foreach(const QString &piece, pieces) {
ArchiveNode *node = parent->find(piece);
if (!node) {
ArchiveEntry e;
e[ FileName ] = (parent == m_rootNode) ?
piece : parent->entry()[ FileName ].toString() + QLatin1Char( '/' ) + piece;
e[ IsDirectory ] = true;
node = new ArchiveDirNode(parent, e);
insertNode(node);
}
if (!node->isDir()) {
ArchiveEntry e(node->entry());
node = new ArchiveDirNode(parent, e);
//Maybe we have both a file and a directory of the same name
// We avoid removing previous entries unless necessary
insertNode(node);
}
parent = static_cast<ArchiveDirNode*>(node);
}
s_previousMatch = parent;
*s_previousPieces = pieces;
return parent;
}
QModelIndex ArchiveModel::indexForNode(ArchiveNode *node)
{
Q_ASSERT(node);
if (node != m_rootNode) {
Q_ASSERT(node->parent());
Q_ASSERT(node->parent()->isDir());
return createIndex(node->row(), 0, node);
}
return QModelIndex();
}
void ArchiveModel::slotEntryRemoved(const QString & path)
{
kDebug() << "Removed node at path " << path;
const QString entryFileName(cleanFileName(path));
if (entryFileName.isEmpty()) {
return;
}
ArchiveNode *entry = m_rootNode->findByPath(entryFileName.split(QLatin1Char( '/' ), QString::SkipEmptyParts));
if (entry) {
ArchiveDirNode *parent = entry->parent();
QModelIndex index = indexForNode(entry);
beginRemoveRows(indexForNode(parent), entry->row(), entry->row());
//delete parent->entries()[ entry->row() ];
//parent->entries()[ entry->row() ] = 0;
parent->removeEntryAt(entry->row());
endRemoveRows();
} else {
kDebug() << "Did not find the removed node";
}
}
void ArchiveModel::slotUserQuery(Kerfuffle::Query *query)
{
query->execute();
}
void ArchiveModel::slotNewEntryFromSetArchive(const ArchiveEntry& entry)
{
// we cache all entries that appear when opening a new archive
// so we can all them together once it's done, this is a huge
// performance improvement because we save from doing lots of
// begin/endInsertRows
m_newArchiveEntries.push_back(entry);
}
void ArchiveModel::slotNewEntry(const ArchiveEntry& entry)
{
newEntry(entry, NotifyViews);
}
void ArchiveModel::newEntry(const ArchiveEntry& receivedEntry, InsertBehaviour behaviour)
{
if (receivedEntry[FileName].toString().isEmpty()) {
kDebug() << "Weird, received empty entry (no filename) - skipping";
return;
}
//if there are no addidional columns registered, then have a look at the
//entry and populate some
if (m_showColumns.isEmpty()) {
//these are the columns we are interested in showing in the display
static const QList<int> columnsForDisplay =
QList<int>()
<< FileName
<< Size
<< CompressedSize
<< Permissions
<< Owner
<< Group
<< Ratio
<< CRC
<< Method
<< Version
<< Timestamp
<< Comment;
QList<int> toInsert;
foreach(int column, columnsForDisplay) {
if (receivedEntry.contains(column)) {
toInsert << column;
}
}
beginInsertColumns(QModelIndex(), 0, toInsert.size() - 1);
m_showColumns << toInsert;
endInsertColumns();
kDebug() << "Show columns detected: " << m_showColumns;
}
//make a copy
ArchiveEntry entry = receivedEntry;
//#194241: Filenames such as "./file" should be displayed as "file"
//#241967: Entries called "/" should be ignored
QString entryFileName = cleanFileName(entry[FileName].toString());
if (entryFileName.isEmpty()) { // The entry contains only "." or "./"
return;
}
entry[FileName] = entryFileName;
/// 1. Skip already created nodes
if (m_rootNode) {
ArchiveNode *existing = m_rootNode->findByPath(entry[ FileName ].toString().split(QLatin1Char( '/' )));
if (existing) {
kDebug() << "Refreshing entry for" << entry[FileName].toString();
// Multi-volume files are repeated at least in RAR archives.
// In that case, we need to sum the compressed size for each volume
qulonglong currentCompressedSize = existing->entry()[CompressedSize].toULongLong();
entry[CompressedSize] = currentCompressedSize + entry[CompressedSize].toULongLong();
//TODO: benchmark whether it's a bad idea to reset the entry here.
existing->setEntry(entry);
return;
}
}
/// 2. Find Parent Node, creating missing ArchiveDirNodes in the process
ArchiveDirNode *parent = parentFor(entry);
/// 3. Create an ArchiveNode
QString name = entry[ FileName ].toString().split(QLatin1Char( '/' ), QString::SkipEmptyParts).last();
ArchiveNode *node = parent->find(name);
if (node) {
node->setEntry(entry);
} else {
if (entry[ FileName ].toString().endsWith(QLatin1Char( '/' )) || (entry.contains(IsDirectory) && entry[ IsDirectory ].toBool())) {
node = new ArchiveDirNode(parent, entry);
} else {
node = new ArchiveNode(parent, entry);
}
insertNode(node, behaviour);
}
}
void ArchiveModel::slotLoadingFinished(KJob *job)
{
//kDebug() << entry;
foreach(const ArchiveEntry &entry, m_newArchiveEntries) {
newEntry(entry, DoNotNotifyViews);
}
reset();
m_newArchiveEntries.clear();
emit loadingFinished(job);
}
void ArchiveModel::insertNode(ArchiveNode *node, InsertBehaviour behaviour)
{
Q_ASSERT(node);
ArchiveDirNode *parent = node->parent();
Q_ASSERT(parent);
if (behaviour == NotifyViews) {
beginInsertRows(indexForNode(parent), parent->entries().count(), parent->entries().count());
}
parent->appendEntry(node);
if (behaviour == NotifyViews) {
endInsertRows();
}
}
Kerfuffle::Archive* ArchiveModel::archive() const
{
return m_archive.data();
}
KJob* ArchiveModel::setArchive(Kerfuffle::Archive *archive)
{
m_archive.reset(archive);
m_rootNode->clear();
s_previousMatch = 0;
s_previousPieces->clear();
Kerfuffle::ListJob *job = NULL;
m_newArchiveEntries.clear();
if (m_archive) {
job = m_archive->list(); // TODO: call "open" or "create"?
connect(job, SIGNAL(newEntry(ArchiveEntry)),
this, SLOT(slotNewEntryFromSetArchive(ArchiveEntry)));
connect(job, SIGNAL(result(KJob*)),
this, SLOT(slotLoadingFinished(KJob*)));
connect(job, SIGNAL(userQuery(Kerfuffle::Query*)),
this, SLOT(slotUserQuery(Kerfuffle::Query*)));
emit loadingStarted();
// TODO: make sure if it's ok to not have calls to beginRemoveColumns here
m_showColumns.clear();
}
reset();
return job;
}
ExtractJob* ArchiveModel::extractFile(const QVariant& fileName, const QString & destinationDir, const Kerfuffle::ExtractionOptions options) const
{
QList<QVariant> files;
files << fileName;
return extractFiles(files, destinationDir, options);
}
ExtractJob* ArchiveModel::extractFiles(const QList<QVariant>& files, const QString & destinationDir, const Kerfuffle::ExtractionOptions options) const
{
Q_ASSERT(m_archive);
ExtractJob *newJob = m_archive->copyFiles(files, destinationDir, options);
connect(newJob, SIGNAL(userQuery(Kerfuffle::Query*)),
this, SLOT(slotUserQuery(Kerfuffle::Query*)));
return newJob;
}
AddJob* ArchiveModel::addFiles(const QStringList & filenames, const CompressionOptions& options)
{
if (!m_archive) {
return NULL;
}
if (!m_archive->isReadOnly()) {
AddJob *job = m_archive->addFiles(filenames, options);
connect(job, SIGNAL(newEntry(ArchiveEntry)),
this, SLOT(slotNewEntry(ArchiveEntry)));
connect(job, SIGNAL(userQuery(Kerfuffle::Query*)),
this, SLOT(slotUserQuery(Kerfuffle::Query*)));
return job;
}
return 0;
}
DeleteJob* ArchiveModel::deleteFiles(const QList<QVariant> & files)
{
Q_ASSERT(m_archive);
if (!m_archive->isReadOnly()) {
DeleteJob *job = m_archive->deleteFiles(files);
connect(job, SIGNAL(entryRemoved(QString)),
this, SLOT(slotEntryRemoved(QString)));
connect(job, SIGNAL(finished(KJob*)),
this, SLOT(slotCleanupEmptyDirs()));
connect(job, SIGNAL(userQuery(Kerfuffle::Query*)),
this, SLOT(slotUserQuery(Kerfuffle::Query*)));
return job;
}
return 0;
}
void ArchiveModel::slotCleanupEmptyDirs()
{
kDebug();
QList<QPersistentModelIndex> queue;
QList<QPersistentModelIndex> nodesToDelete;
//add root nodes
for (int i = 0; i < rowCount(); ++i) {
queue.append(QPersistentModelIndex(index(i, 0)));
}
//breadth-first traverse
while (!queue.isEmpty()) {
QPersistentModelIndex node = queue.takeFirst();
ArchiveEntry entry = entryForIndex(node);
//kDebug() << "Trying " << entry[FileName].toString();
if (!hasChildren(node)) {
if (!entry.contains(InternalID)) {
nodesToDelete << node;
}
} else {
for (int i = 0; i < rowCount(node); ++i) {
queue.append(QPersistentModelIndex(index(i, 0, node)));
}
}
}
foreach(const QPersistentModelIndex& node, nodesToDelete) {
ArchiveNode *rawNode = static_cast<ArchiveNode*>(node.internalPointer());
kDebug() << "Delete with parent entries " << rawNode->parent()->entries() << " and row " << rawNode->row();
beginRemoveRows(parent(node), rawNode->row(), rawNode->row());
rawNode->parent()->removeEntryAt(rawNode->row());
endRemoveRows();
//kDebug() << "Removed entry " << entry[FileName].toString();
}
}
#include "archivemodel.moc"

126
ark/part/archivemodel.h Normal file
View file

@ -0,0 +1,126 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef ARCHIVEMODEL_H
#define ARCHIVEMODEL_H
#include <QAbstractItemModel>
#include <QScopedPointer>
#include <kjobtrackerinterface.h>
#include "kerfuffle/archive.h"
using Kerfuffle::ArchiveEntry;
namespace Kerfuffle
{
class Query;
}
class ArchiveNode;
class ArchiveDirNode;
class ArchiveModel: public QAbstractItemModel
{
Q_OBJECT
public:
ArchiveModel(const QString &dbusPathName, QObject *parent = 0);
~ArchiveModel();
QVariant data(const QModelIndex &index, int role) const;
Qt::ItemFlags flags(const QModelIndex &index) const;
QVariant headerData(int section, Qt::Orientation orientation,
int role = Qt::DisplayRole) const;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
int columnCount(const QModelIndex &parent = QModelIndex()) const;
virtual void sort(int column, Qt::SortOrder order = Qt::AscendingOrder);
//drag and drop related
virtual Qt::DropActions supportedDropActions() const;
virtual QStringList mimeTypes() const;
virtual QMimeData * mimeData(const QModelIndexList & indexes) const;
virtual bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent);
KJob* setArchive(Kerfuffle::Archive *archive);
Kerfuffle::Archive *archive() const;
Kerfuffle::ArchiveEntry entryForIndex(const QModelIndex &index);
int childCount(const QModelIndex &index, int &dirs, int &files) const;
Kerfuffle::ExtractJob* extractFile(const QVariant& fileName, const QString & destinationDir, const Kerfuffle::ExtractionOptions options = Kerfuffle::ExtractionOptions()) const;
Kerfuffle::ExtractJob* extractFiles(const QList<QVariant>& files, const QString & destinationDir, const Kerfuffle::ExtractionOptions options = Kerfuffle::ExtractionOptions()) const;
Kerfuffle::AddJob* addFiles(const QStringList & paths, const Kerfuffle::CompressionOptions& options = Kerfuffle::CompressionOptions());
Kerfuffle::DeleteJob* deleteFiles(const QList<QVariant> & files);
signals:
void loadingStarted();
void loadingFinished(KJob *);
void extractionFinished(bool success);
void error(const QString& error, const QString& details);
void droppedFiles(const QStringList& files, const QString& path = QString());
private slots:
void slotNewEntryFromSetArchive(const ArchiveEntry& entry);
void slotNewEntry(const ArchiveEntry& entry);
void slotLoadingFinished(KJob *job);
void slotEntryRemoved(const QString & path);
void slotUserQuery(Kerfuffle::Query *query);
void slotCleanupEmptyDirs();
private:
/**
* Strips file names that start with './'.
*
* For more information, see bug 194241.
*
* @param fileName The file name that will be stripped.
*
* @return @p fileName without the leading './'
*/
QString cleanFileName(const QString& fileName);
ArchiveDirNode* parentFor(const Kerfuffle::ArchiveEntry& entry);
QModelIndex indexForNode(ArchiveNode *node);
static bool compareAscending(const QModelIndex& a, const QModelIndex& b);
static bool compareDescending(const QModelIndex& a, const QModelIndex& b);
/**
* Insert the node @p node into the model, ensuring all views are notified
* of the change.
*/
enum InsertBehaviour { NotifyViews, DoNotNotifyViews };
void insertNode(ArchiveNode *node, InsertBehaviour behaviour = NotifyViews);
void newEntry(const Kerfuffle::ArchiveEntry& entry, InsertBehaviour behaviour);
QList<Kerfuffle::ArchiveEntry> m_newArchiveEntries; // holds entries from opening a new archive until it's totally open
QList<int> m_showColumns;
QScopedPointer<Kerfuffle::Archive> m_archive;
ArchiveDirNode *m_rootNode;
QString m_dbusPathName;
};
#endif // ARCHIVEMODEL_H

142
ark/part/archiveview.cpp Normal file
View file

@ -0,0 +1,142 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008-2009 Harald Hvaal <haraldhv (at@at) stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "archiveview.h"
#include <KDebug>
#include <KGlobalSettings>
#include <QApplication>
#include <QDragEnterEvent>
#include <QDragMoveEvent>
#include <QMouseEvent>
ArchiveView::ArchiveView(QWidget *parent)
: QTreeView(parent)
, m_mouseButtons(Qt::NoButton)
{
connect(this, SIGNAL(pressed(QModelIndex)),
SLOT(updateMouseButtons()));
connect(this, SIGNAL(clicked(QModelIndex)),
SLOT(slotClicked(QModelIndex)));
connect(this, SIGNAL(doubleClicked(QModelIndex)),
SLOT(slotDoubleClicked(QModelIndex)));
}
// FIXME: this is a workaround taken from Dolphin until QTBUG-1067 is resolved
void ArchiveView::updateMouseButtons()
{
m_mouseButtons = QApplication::mouseButtons();
}
void ArchiveView::slotClicked(const QModelIndex& index)
{
if (KGlobalSettings::singleClick()) {
if (m_mouseButtons != Qt::LeftButton) { // FIXME: see QTBUG-1067
return;
}
// If the user is pressing shift or control, more than one item is being selected
const Qt::KeyboardModifiers modifier = QApplication::keyboardModifiers();
if ((modifier & Qt::ShiftModifier) || (modifier & Qt::ControlModifier)) {
return;
}
emit itemTriggered(index);
}
}
void ArchiveView::slotDoubleClicked(const QModelIndex& index)
{
if (!KGlobalSettings::singleClick()) {
emit itemTriggered(index);
}
}
void ArchiveView::setModel(QAbstractItemModel *model)
{
kDebug();
QTreeView::setModel(model);
setSelectionMode(QAbstractItemView::ExtendedSelection);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setAlternatingRowColors(true);
setAnimated(true);
setAllColumnsShowFocus(true);
setSortingEnabled(true);
//drag and drop
setDragEnabled(true);
setAcceptDrops(true);
setDropIndicatorShown(true);
setDragDropMode(QAbstractItemView::DragDrop);
}
void ArchiveView::startDrag(Qt::DropActions supportedActions)
{
//only start the drag if it's over the filename column. this allows dragging selection in
//tree/detail view
if (currentIndex().column() != 0) {
return;
}
kDebug() << "Singling out the current selection...";
selectionModel()->setCurrentIndex(currentIndex(), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
QTreeView::startDrag(supportedActions);
}
void ArchiveView::dragEnterEvent(QDragEnterEvent * event)
{
//TODO: if no model, trigger some mechanism to create one automatically!
kDebug() << event;
if (event->source() == this) {
//we don't support internal drops yet.
return;
}
QTreeView::dragEnterEvent(event);
}
void ArchiveView::dropEvent(QDropEvent * event)
{
kDebug() << event;
if (event->source() == this) {
//we don't support internal drops yet.
return;
}
QTreeView::dropEvent(event);
}
void ArchiveView::dragMoveEvent(QDragMoveEvent * event)
{
if (event->source() == this) {
//we don't support internal drops yet.
return;
}
QTreeView::dragMoveEvent(event);
if (event->mimeData()->hasFormat(QLatin1String("text/uri-list"))) {
event->acceptProposedAction();
}
}

54
ark/part/archiveview.h Normal file
View file

@ -0,0 +1,54 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2008 Harald Hvaal <haraldhv (at@at) stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef ARCHIVEVIEW_H
#define ARCHIVEVIEW_H
#include <QTreeView>
class ArchiveView : public QTreeView
{
Q_OBJECT
public:
ArchiveView(QWidget *parent = 0);
virtual void dragEnterEvent(class QDragEnterEvent * event);
virtual void dropEvent(class QDropEvent * event);
virtual void dragMoveEvent(class QDragMoveEvent * event);
virtual void startDrag(Qt::DropActions supportedActions);
void setModel(QAbstractItemModel *model);
protected slots:
void slotClicked(const QModelIndex & index);
void slotDoubleClicked(const QModelIndex & index);
private slots:
void updateMouseButtons();
signals:
void itemTriggered(const QModelIndex & index);
private:
Qt::MouseButtons m_mouseButtons; // FIXME: workaround until QTBUG-1067 is resolved
};
#endif /* ARCHIVEVIEW_H */

View file

@ -0,0 +1,152 @@
[Desktop Entry]
Icon=ark
Name=Archiver
Name[af]=Argifeerder
Name[ar]=المؤرشف
Name[ast]=Archivador
Name[bg]=Архиватор
Name[br]=Dieller
Name[bs]=Arhivar
Name[ca]=Arxivador
Name[ca@valencia]=Arxivador
Name[cs]=Archivátor
Name[cy]=Archifydd
Name[da]=Arkivbehandler
Name[de]=Archivprogramm
Name[el]=Πρόγραμμα αρχειοθέτησης
Name[en_GB]=Archiver
Name[eo]=Arkivilo
Name[es]=Archivador
Name[et]=Arhiveerija
Name[eu]=Artxibatzeko programa
Name[fa]=بایگانیکننده
Name[fi]=Pakkausohjelma
Name[fr]=Archiveur
Name[ga]=Archiver
Name[gl]=Arquivador
Name[he]=מנהל הארכיונים
Name[hne]=ि
Name[hr]=Arhiver
Name[hu]=Ark fájltömörítő
Name[ia]=Archivator
Name[id]=Pengarsip
Name[is]=Skráasafnari
Name[it]=Utilità di archiviazione
Name[ja]=
Name[kk]=Архивтегіш
Name[km]=
Name[ko]=Archiver
Name[lt]=Archyvatorius
Name[lv]=Arhivātors
Name[mk]=Архивер
Name[mr]=
Name[ms]=Pengarkib
Name[nb]=Arkivbehandler
Name[nds]=Archivater
Name[ne]=
Name[nl]=Archiefgereedschap
Name[nn]=Arkivprogram
Name[pa]=
Name[pl]=Ark
Name[pt]=Ark
Name[pt_BR]=Arquivador
Name[ro]=Arhivator
Name[ru]=Архиватор
Name[sk]=Archivačný nástroj
Name[sl]=Pakirnik
Name[sq]=Arkivues
Name[sr]=Архивар
Name[sr@ijekavian]=Архивар
Name[sr@ijekavianlatin]=Arhivar
Name[sr@latin]=Arhivar
Name[sv]=Arkiverare
Name[ta]= ி
Name[tg]=Бойгонигар
Name[th]=
Name[tr]=Arşivleyici
Name[uk]=Архіватор
Name[uz]=Arxivlagich
Name[uz@cyrillic]=Архивлагич
Name[vi]=Trình Lưu Tr
Name[wa]=Årtchiveu
Name[xh]=Umenzi woshicilelo lukawonke-wonke noxwebhu lweMbali
Name[x-test]=xxArchiverxx
Name[zh_CN]=
Name[zh_TW]=
Comment=Archive Handling Tool
Comment[af]=Argief Handtering Program
Comment[ar]=أداة التعامل مع الملفات المضغوطة
Comment[ast]=Ferramienta pa remanar archivadores
Comment[bg]=Работа с архиви
Comment[br]=Ostilh merañ an dielloù
Comment[bs]=Alatka za rukovanje arhivama
Comment[ca]=Eina per a treballar amb arxius
Comment[ca@valencia]=Eina per a treballar amb arxius
Comment[cs]=Program pro práci s archivy
Comment[cy]=Erfyn Triniaeth Archif
Comment[da]=Arkivbehandlingsværktøj
Comment[de]=Archiv-Verwaltung
Comment[el]=Εργαλείο χειρισμού αρχειοθηκών
Comment[en_GB]=Archive Handling Tool
Comment[eo]=Administrilo por arĥivoj
Comment[es]=Herramienta de gestión de archivos
Comment[et]=Arhiivide haldamise rakendus
Comment[eu]=Artxiboak kudeatzeko tresna
Comment[fa]=ابزار گرداندن بایگانی
Comment[fi]=Pakkausohjelma
Comment[fr]=Outil de manipulation d'archives
Comment[ga]=Uirlis Láimhseála Cartlainne
Comment[gl]=Utilidade de manexo de arquivos
Comment[he]=כלי לניהול ארכיונים
Comment[hne]=ि
Comment[hr]=Uslužni program za arhiviranje
Comment[hu]=Tömörítőprogram
Comment[ia]=Instrumento de manear archivo
Comment[id]=Perkakas Penanganan Arsip
Comment[is]=Vinna með safnskrár
Comment[it]=Gestione degli archivi
Comment[ja]=
Comment[kk]=Архивпен айналысу құралы
Comment[km]=
Comment[ko]=
Comment[lt]=Archyvo valdymo priemonė
Comment[lv]=Arhīvu apstrādes rīks
Comment[mk]=Алатка за справување со архивирани датотеки
Comment[mr]=
Comment[ms]=Alatan Pengendalian Arkib
Comment[nb]=Arkivbehandlingsverktøy
Comment[nds]=En Warktüüch för de Archievpleeg
Comment[ne]=ि
Comment[nl]=Hulpprogramma voor het beheren van archieven
Comment[nn]=Verktøy for arkivhandsaming
Comment[pa]= ਿ
Comment[pl]=Program obsługi archiwów
Comment[pt]=Programa de gestão de arquivos
Comment[pt_BR]=Ferramenta de manipulação de arquivos
Comment[ro]=Utilitar de manipulare arhive
Comment[ru]=Программа работы с архивами
Comment[sk]=Nástroj na prácu s archívmi
Comment[sl]=Orodje za ravnanje z arhivi
Comment[sq]=Mjet Për Përpunimin e Arkivave
Comment[sr]=Алатка за руковање архивама
Comment[sr@ijekavian]=Алатка за руковање архивама
Comment[sr@ijekavianlatin]=Alatka za rukovanje arhivama
Comment[sr@latin]=Alatka za rukovanje arhivama
Comment[sv]=Verktyg för att hantera filarkiv
Comment[ta]= ி
Comment[tg]=Асбобҳои Дасткории Бойгонӣ
Comment[th]=
Comment[tr]=Arşiv İşleme Aracı
Comment[uk]=Засіб роботи з архівами
Comment[uz]=Arxiv uchun vosita
Comment[uz@cyrillic]=Архив учун восита
Comment[vi]=Công C X Lý Các Tp Tin Nén
Comment[wa]=Usteye po-z apougnî les årtchives
Comment[xh]=Isixhobo sokuphatha i Archive
Comment[x-test]=xxArchive Handling Toolxx
Comment[zh_CN]=
Comment[zh_TW]=
X-KDE-ServiceTypes=KParts/ReadOnlyPart,Browser/View
X-KDE-Library=arkpart
Type=Service
MimeType=@SUPPORTED_ARK_MIMETYPES@

28
ark/part/ark_part.rc Normal file
View file

@ -0,0 +1,28 @@
<!DOCTYPE kpartgui>
<kpartgui name="ark_part" version="1">
<MenuBar>
<Menu name="file">
<text>&amp;File</text>
<Action name="file_save_as" group="file_save"/>
</Menu>
<Menu name="action">
<text>&amp;Action</text>
<Action name="add"/>
<Action name="add-dir"/>
<Action name="delete"/>
<Action name="extract"/>
<Action name="preview"/>
</Menu>
<Menu name="settings">
<text>&amp;Settings</text>
<Action name="show-infopanel" group="settings_show"/>
</Menu>
</MenuBar>
<ToolBar name="mainToolBar">
<Action name="add"/>
<Action name="add-dir"/>
<Action name="delete"/>
<Action name="extract"/>
<Action name="preview"/>
</ToolBar>
</kpartgui>

266
ark/part/arkviewer.cpp Normal file
View file

@ -0,0 +1,266 @@
/*
* ark: A program for modifying archives via a GUI.
*
* Copyright (C) 2004-2008 Henrique Pinto <henrique.pinto@kdemail.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "arkviewer.h"
#include <KLocale>
#include <KMimeTypeTrader>
#include <KMimeType>
#include <KDebug>
#include <KUrl>
#include <KGlobal>
#include <KIconLoader>
#include <KVBox>
#include <KMessageBox>
#include <KProgressDialog>
#include <KPushButton>
#include <KRun>
#include <KIO/NetAccess>
#include <QHBoxLayout>
#include <QFile>
#include <QFrame>
#include <QLabel>
ArkViewer::ArkViewer(QWidget * parent, Qt::WFlags flags)
: KDialog(parent, flags)
{
setButtons(Close);
m_widget = new KVBox(this);
m_widget->layout()->setSpacing(10);
setMainWidget(m_widget);
connect(this, SIGNAL(finished()), SLOT(dialogClosed()));
}
ArkViewer::~ArkViewer()
{
}
void ArkViewer::dialogClosed()
{
KConfigGroup conf = KGlobal::config()->group("Viewer");
saveDialogSize(conf);
if (m_part) {
KProgressDialog progressDialog
(this, i18n("Closing preview"),
i18n("Please wait while the preview is being closed..."));
progressDialog.setMinimumDuration(500);
progressDialog.setModal(true);
progressDialog.setAllowCancel(false);
progressDialog.progressBar()->setRange(0, 0);
// #261785: this preview dialog is not modal, so we need to delete
// the previewed file ourselves when the dialog is closed;
// we used to remove it at the end of ArkViewer::view() when
// QDialog::exec() was called instead of QDialog::show().
const QString previewedFilePath(m_part.data()->url().pathOrUrl());
m_part.data()->closeUrl();
if (!previewedFilePath.isEmpty()) {
QFile::remove(previewedFilePath);
}
}
}
void ArkViewer::view(const QString& fileName, QWidget *parent)
{
KMimeType::Ptr mimeType = KMimeType::findByPath(fileName);
kDebug() << "MIME type" << mimeType->name();
KService::Ptr viewer = ArkViewer::getViewer(mimeType);
const bool needsExternalViewer = (!viewer.isNull() &&
!viewer->hasServiceType(QLatin1String("KParts/ReadOnlyPart")));
if (needsExternalViewer) {
// We have already resolved the MIME type and the service above.
// So there is no point in using KRun::runUrl() which would need
// to do the same again.
const KUrl::List fileUrlList = KUrl(fileName);
// The last argument (tempFiles) set to true means that the temporary
// file will be removed when the viewer application exits.
KRun::run(*viewer, fileUrlList, parent, true);
return;
}
bool viewInInternalViewer = true;
if (viewer.isNull()) {
// No internal viewer available for the file. Ask the user if it
// should be previewed as text/plain.
int response;
if (!mimeType->isDefault()) {
// File has a defined MIME type, and not the default
// application/octet-stream. So it could be viewable as
// plain text, ask the user.
response = KMessageBox::warningContinueCancel(parent,
i18n("The internal viewer cannot preview this type of file<nl/>(%1).<nl/><nl/>Do you want to try to view it as plain text?", mimeType->name()),
i18nc("@title:window", "Cannot Preview File"),
KGuiItem(i18nc("@action:button", "Preview as Text"), KIcon(QLatin1String("text-plain"))),
KStandardGuiItem::cancel(),
QString(QLatin1String("PreviewAsText_%1")).arg(mimeType->name()));
}
else {
// No defined MIME type, or the default application/octet-stream.
// There is still a possibility that it could be viewable as plain
// text, so ask the user. Not the same as the message/question
// above, because the wording and default are different.
response = KMessageBox::warningContinueCancel(parent,
i18n("The internal viewer cannot preview this unknown type of file.<nl/><nl/>Do you want to try to view it as plain text?"),
i18nc("@title:window", "Cannot Preview File"),
KGuiItem(i18nc("@action:button", "Preview as Text"), KIcon(QLatin1String("text-plain"))),
KStandardGuiItem::cancel(),
QString(),
KMessageBox::Dangerous);
}
if (response == KMessageBox::Cancel) {
viewInInternalViewer = false;
}
else { // set for viewer later
mimeType = KMimeType::mimeType(QLatin1String("text/plain"));
}
}
if (viewInInternalViewer) {
ArkViewer *internalViewer = new ArkViewer(parent, Qt::Window);
if (internalViewer->viewInInternalViewer(fileName, mimeType)) {
internalViewer->show();
// The internal viewer is showing the file, and will
// remove the temporary file in dialogClosed(). So there
// is no more to do here.
return;
}
else {
KMessageBox::sorry(parent, i18n("The internal viewer cannot preview this file."));
delete internalViewer;
}
}
// Only get here if there is no internal viewer available or could be
// used for the file, and no external viewer was opened. Nothing can be
// done with the temporary file, so remove it now.
QFile::remove(fileName);
}
void ArkViewer::keyPressEvent(QKeyEvent *event)
{
KPushButton *defButton = button(defaultButton());
// Only handle the event the usual way if the default button has focus
// Otherwise, pressing enter on KatePart still closes the dialog, for example.
if ((defButton) && (defButton->hasFocus())) {
KDialog::keyPressEvent(event);
}
event->accept();
}
// This sets the default size of the dialog. It will only take effect in the case
// where there is no saved size in the config file - it sets the default values
// for KDialog::restoreDialogSize().
QSize ArkViewer::sizeHint() const
{
return QSize(560, 400);
}
bool ArkViewer::viewInInternalViewer(const QString& fileName, const KMimeType::Ptr& mimeType)
{
const KUrl fileUrl(fileName);
setCaption(fileUrl.fileName());
restoreDialogSize(KGlobal::config()->group("Viewer"));
QFrame *header = new QFrame(m_widget);
QHBoxLayout *headerLayout = new QHBoxLayout(header);
QLabel *iconLabel = new QLabel(header);
headerLayout->addWidget(iconLabel);
iconLabel->setPixmap(KIconLoader::global()->loadMimeTypeIcon(mimeType->iconName(), KIconLoader::Desktop));
iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
KVBox *headerRight = new KVBox(header);
headerLayout->addWidget(headerRight);
new QLabel(QString(QLatin1String( "<qt><b>%1</b></qt>" ))
.arg(fileUrl.fileName()), headerRight
);
new QLabel(mimeType->comment(), headerRight);
header->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum);
m_part = KMimeTypeTrader::self()->createPartInstanceFromQuery<KParts::ReadOnlyPart>(mimeType->name(),
m_widget,
this);
if (!m_part.data()) {
return false;
}
if (m_part.data()->browserExtension()) {
connect(m_part.data()->browserExtension(),
SIGNAL(openUrlRequestDelayed(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)),
SLOT(slotOpenUrlRequestDelayed(KUrl,KParts::OpenUrlArguments,KParts::BrowserArguments)));
}
m_part.data()->openUrl(fileUrl);
return true;
}
void ArkViewer::slotOpenUrlRequestDelayed(const KUrl& url, const KParts::OpenUrlArguments& arguments, const KParts::BrowserArguments& browserArguments)
{
kDebug() << "Opening URL: " << url;
Q_UNUSED(arguments)
Q_UNUSED(browserArguments)
KRun *runner = new KRun(url, 0, 0, false);
runner->setRunExecutables(false);
}
KService::Ptr ArkViewer::getViewer(const KMimeType::Ptr &mimeType)
{
// No point in even trying to find anything for application/octet-stream
if (mimeType->isDefault()) {
return KService::Ptr();
}
// Try to get a read-only kpart for the internal viewer
KService::List offers = KMimeTypeTrader::self()->query(mimeType->name(), QString::fromLatin1("KParts/ReadOnlyPart"));
// If we can't find a kpart, try to get an external application
if (offers.size() == 0) {
offers = KMimeTypeTrader::self()->query(mimeType->name(), QString::fromLatin1("Application"));
}
if (offers.size() > 0) {
return offers.first();
} else {
return KService::Ptr();
}
}
#include "arkviewer.moc"

63
ark/part/arkviewer.h Normal file
View file

@ -0,0 +1,63 @@
/*
* ark: A program for modifying archives via a GUI.
*
* Copyright (C) 2004-2008, Henrique Pinto <henrique.pinto@kdemail.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef ARKVIEWER_H
#define ARKVIEWER_H
#include <KDialog>
#include <KMimeType>
#include <KParts/BrowserExtension>
#include <KParts/ReadOnlyPart>
#include <KService>
#include <QtCore/QWeakPointer>
class ArkViewer : public KDialog
{
Q_OBJECT
public:
virtual ~ArkViewer();
virtual QSize sizeHint() const;
static void view(const QString& fileName, QWidget* parent = 0);
protected:
virtual void keyPressEvent(QKeyEvent *event);
protected slots:
void slotOpenUrlRequestDelayed(const KUrl& url, const KParts::OpenUrlArguments& arguments, const KParts::BrowserArguments& browserArguments);
private slots:
void dialogClosed();
private:
explicit ArkViewer(QWidget* parent = 0, Qt::WFlags flags = 0);
static KService::Ptr getViewer(const KMimeType::Ptr& mimeType);
bool viewInInternalViewer(const QString& fileName, const KMimeType::Ptr& mimeType);
QWeakPointer<KParts::ReadOnlyPart> m_part;
QWidget *m_widget;
};
#endif // ARKVIEWER_H

View file

@ -0,0 +1,8 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.ark.DndExtract">
<method name="extractSelectedFilesTo">
<arg direction="in" type="s" name="path" />
</method>
</interface>
</node>

213
ark/part/infopanel.cpp Normal file
View file

@ -0,0 +1,213 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "infopanel.h"
#include "kerfuffle/archive.h"
#include <QLabel>
#include <QVBoxLayout>
#include <QFileInfo>
#include <KLocale>
#include <KMimeType>
#include <KIconLoader>
#include <KIO/NetAccess>
using namespace Kerfuffle;
static QPixmap getMimeIcon(const QString& mimeName)
{
return KIconLoader::global()->loadMimeTypeIcon(mimeName, KIconLoader::Desktop, KIconLoader::SizeHuge);
}
InfoPanel::InfoPanel(ArchiveModel *model, QWidget *parent)
: QFrame(parent), m_model(model)
{
setupUi(this);
// Make the file name font bigger than the rest
QFont fnt = fileName->font();
if (fnt.pointSize() > -1) {
fnt.setPointSize(fnt.pointSize() + 1);
} else {
fnt.setPixelSize(fnt.pixelSize() + 3);
}
fileName->setFont(fnt);
updateWithDefaults();
}
InfoPanel::~InfoPanel()
{
}
void InfoPanel::updateWithDefaults()
{
iconLabel->setPixmap(KIconLoader::global()->loadIcon(QLatin1String( "utilities-file-archiver" ), KIconLoader::Desktop, KIconLoader::SizeHuge));
const QString currentFileName = prettyFileName();
if (currentFileName.isEmpty()) {
fileName->setText(i18n("No archive loaded"));
} else {
fileName->setText(currentFileName);
}
additionalInfo->setText(QString());
hideMetaData();
hideActions();
}
QString InfoPanel::prettyFileName() const
{
if (m_prettyFileName.isEmpty()) {
if (m_model->archive()) {
QFileInfo fileInfo(m_model->archive()->fileName());
return fileInfo.fileName();
}
}
return m_prettyFileName;
}
void InfoPanel::setPrettyFileName(const QString& fileName)
{
m_prettyFileName = fileName;
}
void InfoPanel::setIndex(const QModelIndex& index)
{
if (!index.isValid()) {
updateWithDefaults();
} else {
const ArchiveEntry& entry = m_model->entryForIndex(index);
KMimeType::Ptr mimeType;
if (entry[ IsDirectory ].toBool()) {
mimeType = KMimeType::mimeType(QLatin1String( "inode/directory" ));
} else {
mimeType = KMimeType::findByPath(entry[ FileName ].toString(), 0, true);
}
iconLabel->setPixmap(getMimeIcon(mimeType->iconName()));
if (entry[ IsDirectory ].toBool()) {
int dirs;
int files;
const int children = m_model->childCount(index, dirs, files);
additionalInfo->setText(KIO::itemsSummaryString(children, files, dirs, 0, false));
} else if (entry.contains(Link)) {
additionalInfo->setText(i18n("Symbolic Link"));
} else {
if (entry.contains(Size)) {
additionalInfo->setText(KIO::convertSize(entry[ Size ].toULongLong()));
} else {
additionalInfo->setText(i18n("Unknown size"));
}
}
const QStringList nameParts = entry[ FileName ].toString().split(QLatin1Char( '/' ), QString::SkipEmptyParts);
const QString name = (nameParts.count() > 0) ? nameParts.last() : entry[ FileName ].toString();
fileName->setText(name);
metadataLabel->setText(metadataTextFor(index));
showMetaData();
}
}
void InfoPanel::setIndexes(const QModelIndexList &list)
{
if (list.size() == 0) {
setIndex(QModelIndex());
} else if (list.size() == 1) {
setIndex(list[ 0 ]);
} else {
iconLabel->setPixmap(KIconLoader::global()->loadIcon(QLatin1String( "utilities-file-archiver" ), KIconLoader::Desktop, KIconLoader::SizeHuge));
fileName->setText(i18np("One file selected", "%1 files selected", list.size()));
quint64 totalSize = 0;
foreach(const QModelIndex& index, list) {
const ArchiveEntry& entry = m_model->entryForIndex(index);
totalSize += entry[ Size ].toULongLong();
}
additionalInfo->setText(KIO::convertSize(totalSize));
hideMetaData();
}
}
void InfoPanel::showMetaData()
{
firstSeparator->show();
metadataLabel->show();
}
void InfoPanel::hideMetaData()
{
firstSeparator->hide();
metadataLabel->hide();
}
void InfoPanel::showActions()
{
secondSeparator->show();
actionsLabel->show();
}
void InfoPanel::hideActions()
{
secondSeparator->hide();
actionsLabel->hide();
}
QString InfoPanel::metadataTextFor(const QModelIndex &index)
{
const ArchiveEntry& entry = m_model->entryForIndex(index);
QString text;
KMimeType::Ptr mimeType;
if (entry[ IsDirectory ].toBool()) {
mimeType = KMimeType::mimeType(QLatin1String( "inode/directory" ));
} else {
mimeType = KMimeType::findByPath(entry[ FileName ].toString(), 0, true);
}
text += i18n("<b>Type:</b> %1<br/>", mimeType->comment());
if (entry.contains(Owner)) {
text += i18n("<b>Owner:</b> %1<br/>", entry[ Owner ].toString());
}
if (entry.contains(Group)) {
text += i18n("<b>Group:</b> %1<br/>", entry[ Group ].toString());
}
if (entry.contains(Link)) {
text += i18n("<b>Target:</b> %1<br/>", entry[ Link ].toString());
}
if (entry.contains(IsPasswordProtected) && entry[ IsPasswordProtected ].toBool()) {
text += i18n("<b>Password protected:</b> Yes<br/>");
}
return text;
}
#include "infopanel.moc"

76
ark/part/infopanel.h Normal file
View file

@ -0,0 +1,76 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef INFOPANEL_H
#define INFOPANEL_H
#include "kerfuffle/archive.h"
#include "archivemodel.h"
#include "ui_infopanel.h"
#include <QFrame>
class InfoPanel: public QFrame, Ui::InformationPanel
{
Q_OBJECT
public:
explicit InfoPanel(ArchiveModel *model, QWidget *parent = 0);
virtual ~InfoPanel();
void setIndex(const QModelIndex &);
void setIndexes(const QModelIndexList &list);
/**
* Returns the file name that is displayed on the info panel.
*
* @return The current file name. If no pretty name has been
* set, it returns the name of the loaded archive.
*/
QString prettyFileName() const;
/**
* Sets a different file name for the current open archive.
*
* This is particularly useful when a temporary archive (from
* a remote location) is loaded, and the window title shows the
* remote file name and the info panel, by default, would show
* the name of the temporary downloaded file.
*
* @param fileName The new file name.
*/
void setPrettyFileName(const QString& fileName);
void updateWithDefaults();
private:
void showMetaData();
void hideMetaData();
void showActions();
void hideActions();
QString metadataTextFor(const QModelIndex &);
ArchiveModel *m_model;
QString m_prettyFileName;
};
#endif // INFOPANEL_H

126
ark/part/infopanel.ui Normal file
View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>InformationPanel</class>
<widget class="QWidget" name="InformationPanel" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>118</width>
<height>300</height>
</rect>
</property>
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Maximum" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle" >
<string>Information Panel</string>
</property>
<layout class="QVBoxLayout" >
<item>
<widget class="QLabel" name="iconLabel" >
<property name="text" >
<string/>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="KSqueezedTextLabel" name="fileName">
<property name="font">
<font>
<weight>75</weight>
<bold>true</bold>
</font>
</property>
<property name="text">
<string notr="true">KSqueezedTextLabel</string>
</property>
<property name="alignment">
<set>Qt::AlignCenter</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="textElideMode">
<enum>Qt::ElideRight</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="additionalInfo" >
<property name="text" >
<string>Unknown file type</string>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<widget class="Line" name="firstSeparator" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="metadataLabel" >
<property name="text" >
<string>Metadata Label</string>
</property>
<property name="margin" >
<number>10</number>
</property>
<property name="indent" >
<number>20</number>
</property>
</widget>
</item>
<item>
<widget class="Line" name="secondSeparator" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="actionsLabel" >
<property name="text" >
<string>ActionsLabel</string>
</property>
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>KSqueezedTextLabel</class>
<extends>QLabel</extends>
<header>ksqueezedtextlabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

37
ark/part/interface.h Normal file
View file

@ -0,0 +1,37 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef INTERFACE_H
#define INTERFACE_H
#include <QStringList>
#include <QtPlugin>
class Interface
{
public:
virtual ~Interface() {}
virtual bool isBusy() const = 0;
};
Q_DECLARE_INTERFACE(Interface, "org.kde.kerfuffle.partinterface/0.42")
#endif // INTERFACE_H

107
ark/part/jobtracker.cpp Normal file
View file

@ -0,0 +1,107 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "jobtracker.h"
#include <KDebug>
JobTrackerWidget::JobTrackerWidget(QWidget *parent)
: QFrame(parent)
{
setupUi(this);
}
JobTracker::JobTracker(QWidget *parent)
: KAbstractWidgetJobTracker(parent)
{
m_ui = new JobTrackerWidget(parent);
resetUi();
}
JobTracker::~JobTracker()
{
foreach(KJob *job, m_jobs) {
job->kill();
delete job;
}
}
void JobTracker::description(KJob *job, const QString &title, const QPair< QString, QString > &f1, const QPair< QString, QString > &f2)
{
Q_UNUSED(job)
Q_UNUSED(f1)
Q_UNUSED(f2)
m_ui->descriptionLabel->setText(QString(QLatin1String( "<b>%1</b>" )).arg(title));
m_ui->descriptionLabel->show();
}
void JobTracker::infoMessage(KJob *job, const QString &plain, const QString &rich)
{
Q_UNUSED(job)
Q_UNUSED(rich)
m_ui->informationLabel->setText(plain);
m_ui->informationLabel->show();
}
void JobTracker::warning(KJob *job, const QString &plain, const QString &rich)
{
Q_UNUSED(job)
Q_UNUSED(rich)
m_ui->informationLabel->setText(plain);
}
void JobTracker::registerJob(KJob *job)
{
m_jobs << job;
KJobTrackerInterface::registerJob(job);
m_ui->show();
m_ui->informationLabel->hide();
m_ui->progressBar->show();
}
void JobTracker::percent(KJob *job, unsigned long percent)
{
Q_UNUSED(job)
m_ui->progressBar->setMaximum(100);
m_ui->progressBar->setMinimum(0);
m_ui->progressBar->setValue(percent);
}
void JobTracker::unregisterJob(KJob *job)
{
m_jobs.remove(job);
KJobTrackerInterface::unregisterJob(job);
resetUi();
}
void JobTracker::resetUi()
{
m_ui->hide();
m_ui->descriptionLabel->hide();
m_ui->informationLabel->hide();
m_ui->progressBar->setMaximum(0);
m_ui->progressBar->setMinimum(0);
}
QWidget* JobTracker::widget(KJob *)
{
return m_ui;
}

68
ark/part/jobtracker.h Normal file
View file

@ -0,0 +1,68 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008 Harald Hvaal <haraldhv@stud.ntnu.no>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef JOBTRACKER_H
#define JOBTRACKER_H
#include <QFrame>
#include <kabstractwidgetjobtracker.h>
#include "ui_jobtracker.h"
class KJob;
class JobTrackerWidget: public QFrame, public Ui::JobTrackerWidget
{
Q_OBJECT
public:
JobTrackerWidget(QWidget *parent = 0);
};
class JobTracker: public KAbstractWidgetJobTracker
{
Q_OBJECT
public:
JobTracker(QWidget *parent = 0);
~JobTracker();
virtual QWidget *widget(KJob *);
public slots:
virtual void registerJob(KJob *job);
virtual void unregisterJob(KJob *job);
protected slots:
virtual void description(KJob *job, const QString &title, const QPair< QString, QString > &f1, const QPair< QString, QString > &f2);
virtual void infoMessage(KJob *job, const QString &plain, const QString &rich);
virtual void warning(KJob *job, const QString &plain, const QString &rich);
virtual void percent(KJob *job, unsigned long percent);
private slots:
void resetUi();
private:
JobTrackerWidget *m_ui;
QSet<KJob*> m_jobs;
};
#endif // JOBTRACKER_H

92
ark/part/jobtracker.ui Normal file
View file

@ -0,0 +1,92 @@
<ui version="4.0" >
<class>JobTrackerWidget</class>
<widget class="QWidget" name="JobTrackerWidget" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>409</width>
<height>16</height>
</rect>
</property>
<property name="windowTitle" >
<string>Job Tracker</string>
</property>
<layout class="QHBoxLayout" >
<property name="leftMargin" >
<number>4</number>
</property>
<property name="topMargin" >
<number>1</number>
</property>
<property name="rightMargin" >
<number>4</number>
</property>
<property name="bottomMargin" >
<number>1</number>
</property>
<item>
<widget class="QLabel" name="descriptionLabel" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text" >
<string>&lt;b>Job Description&lt;/b></string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="informationLabel" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Expanding" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="text" >
<string>Some Information about the job</string>
</property>
</widget>
</item>
<item>
<widget class="QProgressBar" name="progressBar" >
<property name="sizePolicy" >
<sizepolicy vsizetype="Preferred" hsizetype="Preferred" >
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>50</width>
<height>0</height>
</size>
</property>
<property name="maximum" >
<number>100</number>
</property>
<property name="value" >
<number>-1</number>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>

916
ark/part/part.cpp Normal file
View file

@ -0,0 +1,916 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009-2012 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "part.h"
#include "archivemodel.h"
#include "archiveview.h"
#include "arkviewer.h"
#include "dnddbusinterfaceadaptor.h"
#include "infopanel.h"
#include "jobtracker.h"
#include "kerfuffle/archive.h"
#include "kerfuffle/extractiondialog.h"
#include "kerfuffle/jobs.h"
#include "kerfuffle/settings.h"
#include <KAboutData>
#include <KAction>
#include <KActionCollection>
#include <KApplication>
#include <KConfigGroup>
#include <KDebug>
#include <KFileDialog>
#include <KGuiItem>
#include <KIO/Job>
#include <KIO/NetAccess>
#include <KIcon>
#include <KInputDialog>
#include <KMessageBox>
#include <KPluginFactory>
#include <KRun>
#include <KSelectAction>
#include <KStandardDirs>
#include <KStandardGuiItem>
#include <KTempDir>
#include <KToggleAction>
#include <KXMLGUIFactory>
#include <QAction>
#include <QCursor>
#include <QHeaderView>
#include <QMenu>
#include <QMimeData>
#include <QMouseEvent>
#include <QScopedPointer>
#include <QSplitter>
#include <QTimer>
#include <QVBoxLayout>
#include <QWeakPointer>
#include <QtDBus/QtDBus>
using namespace Kerfuffle;
K_PLUGIN_FACTORY(Factory, registerPlugin<Ark::Part>();)
K_EXPORT_PLUGIN(Factory("ark"))
namespace Ark
{
static quint32 s_instanceCounter = 1;
Part::Part(QWidget *parentWidget, QObject *parent, const QVariantList& args)
: KParts::ReadWritePart(parent),
m_splitter(0),
m_busy(false),
m_jobTracker(0)
{
Q_UNUSED(args)
setComponentData(Factory::componentData(), false);
new DndExtractAdaptor(this);
const QString pathName = QString(QLatin1String("/DndExtract/%1")).arg(s_instanceCounter++);
if (!QDBusConnection::sessionBus().registerObject(pathName, this)) {
kFatal() << "Could not register a D-Bus object for drag'n'drop";
}
m_model = new ArchiveModel(pathName, this);
m_splitter = new QSplitter(Qt::Horizontal, parentWidget);
setWidget(m_splitter);
m_view = new ArchiveView;
m_infoPanel = new InfoPanel(m_model);
m_splitter->addWidget(m_view);
m_splitter->addWidget(m_infoPanel);
QList<int> splitterSizes = ArkSettings::splitterSizes();
if (splitterSizes.isEmpty()) {
splitterSizes.append(200);
splitterSizes.append(100);
}
m_splitter->setSizes(splitterSizes);
setupView();
setupActions();
connect(m_model, SIGNAL(loadingStarted()),
this, SLOT(slotLoadingStarted()));
connect(m_model, SIGNAL(loadingFinished(KJob*)),
this, SLOT(slotLoadingFinished(KJob*)));
connect(m_model, SIGNAL(droppedFiles(QStringList,QString)),
this, SLOT(slotAddFiles(QStringList,QString)));
connect(m_model, SIGNAL(error(QString,QString)),
this, SLOT(slotError(QString,QString)));
connect(this, SIGNAL(busy()),
this, SLOT(setBusyGui()));
connect(this, SIGNAL(ready()),
this, SLOT(setReadyGui()));
connect(this, SIGNAL(completed()),
this, SLOT(setFileNameFromArchive()));
m_statusBarExtension = new KParts::StatusBarExtension(this);
setXMLFile(QLatin1String( "ark_part.rc" ));
}
Part::~Part()
{
factory()->removeClient(this);
saveSplitterSizes();
m_extractFilesAction->menu()->deleteLater();
}
void Part::registerJob(KJob* job)
{
if (!m_jobTracker) {
m_jobTracker = new JobTracker(widget());
m_statusBarExtension->addStatusBarItem(m_jobTracker->widget(0), 0, true);
m_jobTracker->widget(job)->show();
}
m_jobTracker->registerJob(job);
emit busy();
connect(job, SIGNAL(result(KJob*)), this, SIGNAL(ready()));
}
// TODO: One should construct a KUrl out of localPath in order to be able to handle
// non-local destinations (ie. trash:/ or a remote location)
// See bugs #189322 and #204323.
void Part::extractSelectedFilesTo(const QString& localPath)
{
kDebug() << "Extract to " << localPath;
if (!m_model) {
return;
}
if (m_view->selectionModel()->selectedRows().count() != 1) {
m_view->selectionModel()->setCurrentIndex(m_view->currentIndex(), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
if (m_view->selectionModel()->selectedRows().count() != 1) {
return;
}
QVariant internalRoot;
kDebug() << "valid " << m_view->currentIndex().parent().isValid();
if (m_view->currentIndex().parent().isValid()) {
internalRoot = m_model->entryForIndex(m_view->currentIndex().parent()).value(InternalID);
}
if (internalRoot.isNull()) {
//we have the special case valid parent, but the parent does not
//actually correspond to an item in the archive, but an automatically
//created folder. for now, we will just use the filename of the node
//instead, but for plugins that rely on a non-filename value as the
//InternalId, this WILL break things. TODO find a solution
internalRoot = m_model->entryForIndex(m_view->currentIndex().parent()).value(FileName);
}
QList<QVariant> files = selectedFilesWithChildren();
if (files.isEmpty()) {
return;
}
kDebug() << "selected files are " << files;
Kerfuffle::ExtractionOptions options;
options[QLatin1String( "PreservePaths" )] = true;
if (!internalRoot.isNull()) {
options[QLatin1String("RootNode")] = internalRoot;
}
ExtractJob *job = m_model->extractFiles(files, localPath, options);
registerJob(job);
connect(job, SIGNAL(result(KJob*)),
this, SLOT(slotExtractionDone(KJob*)));
job->start();
}
void Part::setupView()
{
m_view->setModel(m_model);
m_view->setSortingEnabled(true);
connect(m_view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(updateActions()));
connect(m_view->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
this, SLOT(selectionChanged()));
//TODO: fix an actual eventhandler
connect(m_view, SIGNAL(itemTriggered(QModelIndex)),
this, SLOT(slotPreview(QModelIndex)));
connect(m_model, SIGNAL(columnsInserted(QModelIndex,int,int)),
this, SLOT(adjustColumns()));
}
void Part::setupActions()
{
KToggleAction *showInfoPanelAction = new KToggleAction(i18nc("@action:inmenu", "Show information panel"), this);
actionCollection()->addAction(QLatin1String( "show-infopanel" ), showInfoPanelAction);
showInfoPanelAction->setChecked(m_splitter->sizes().at(1) > 0);
connect(showInfoPanelAction, SIGNAL(triggered(bool)),
this, SLOT(slotToggleInfoPanel(bool)));
m_saveAsAction = KStandardAction::saveAs(this, SLOT(slotSaveAs()), actionCollection());
m_previewAction = actionCollection()->addAction(QLatin1String( "preview" ));
m_previewAction->setText(i18nc("to preview a file inside an archive", "Pre&view"));
m_previewAction->setIcon(KIcon( QLatin1String( "document-preview-archive" )));
m_previewAction->setStatusTip(i18n("Click to preview the selected file"));
m_previewAction->setShortcuts(QList<QKeySequence>() << Qt::Key_Return << Qt::Key_Space);
connect(m_previewAction, SIGNAL(triggered(bool)),
this, SLOT(slotPreview()));
m_extractFilesAction = actionCollection()->addAction(QLatin1String( "extract" ));
m_extractFilesAction->setText(i18n("E&xtract"));
m_extractFilesAction->setIcon(KIcon( QLatin1String( "archive-extract" )));
m_extractFilesAction->setStatusTip(i18n("Click to open an extraction dialog, where you can choose to extract either all files or just the selected ones"));
m_extractFilesAction->setShortcut(QKeySequence( QLatin1String( "Ctrl+E" ) ));
connect(m_extractFilesAction, SIGNAL(triggered(bool)),
this, SLOT(slotExtractFiles()));
m_addFilesAction = actionCollection()->addAction(QLatin1String( "add" ));
m_addFilesAction->setIcon(KIcon( QLatin1String( "archive-insert" )));
m_addFilesAction->setText(i18n("Add &File..."));
m_addFilesAction->setStatusTip(i18n("Click to add files to the archive"));
connect(m_addFilesAction, SIGNAL(triggered(bool)),
this, SLOT(slotAddFiles()));
m_addDirAction = actionCollection()->addAction(QLatin1String( "add-dir" ));
m_addDirAction->setIcon(KIcon( QLatin1String( "archive-insert-directory" )));
m_addDirAction->setText(i18n("Add Fo&lder..."));
m_addDirAction->setStatusTip(i18n("Click to add a folder to the archive"));
connect(m_addDirAction, SIGNAL(triggered(bool)),
this, SLOT(slotAddDir()));
m_deleteFilesAction = actionCollection()->addAction(QLatin1String( "delete" ));
m_deleteFilesAction->setIcon(KIcon( QLatin1String( "archive-remove" )));
m_deleteFilesAction->setText(i18n("De&lete"));
m_deleteFilesAction->setShortcut(Qt::Key_Delete);
m_deleteFilesAction->setStatusTip(i18n("Click to delete the selected files"));
connect(m_deleteFilesAction, SIGNAL(triggered(bool)),
this, SLOT(slotDeleteFiles()));
updateActions();
}
void Part::updateActions()
{
bool isWritable = m_model->archive() && (!m_model->archive()->isReadOnly());
m_previewAction->setEnabled(!isBusy() && (m_view->selectionModel()->selectedRows().count() == 1)
&& isPreviewable(m_view->selectionModel()->currentIndex()));
m_extractFilesAction->setEnabled(!isBusy() && (m_model->rowCount() > 0));
m_addFilesAction->setEnabled(!isBusy() && isWritable);
m_addDirAction->setEnabled(!isBusy() && isWritable);
m_deleteFilesAction->setEnabled(!isBusy() && (m_view->selectionModel()->selectedRows().count() > 0)
&& isWritable);
QMenu *menu = m_extractFilesAction->menu();
if (!menu) {
menu = new QMenu;
m_extractFilesAction->setMenu(menu);
connect(menu, SIGNAL(triggered(QAction*)),
this, SLOT(slotQuickExtractFiles(QAction*)));
// Remember to keep this action's properties as similar to
// m_extractFilesAction's as possible (except where it does not make
// sense, such as the text or the shortcut).
QAction *extractTo = menu->addAction(i18n("Extract To..."));
extractTo->setIcon(m_extractFilesAction->icon());
extractTo->setStatusTip(m_extractFilesAction->statusTip());
connect(extractTo, SIGNAL(triggered(bool)), SLOT(slotExtractFiles()));
menu->addSeparator();
QAction *header = menu->addAction(i18n("Quick Extract To..."));
header->setEnabled(false);
header->setIcon(KIcon( QLatin1String( "archive-extract" )));
}
while (menu->actions().size() > 3) {
menu->removeAction(menu->actions().last());
}
const KConfigGroup conf(KGlobal::config(), "DirSelect Dialog");
const QStringList dirHistory = conf.readPathEntry("History Items", QStringList());
for (int i = 0; i < qMin(10, dirHistory.size()); ++i) {
const KUrl dirUrl(dirHistory.at(i));
QAction *newAction = menu->addAction(dirUrl.pathOrUrl());
newAction->setData(dirUrl.pathOrUrl());
}
}
void Part::slotQuickExtractFiles(QAction *triggeredAction)
{
// #190507: triggeredAction->data.isNull() means it's the "Extract to..."
// action, and we do not want it to run here
if (!triggeredAction->data().isNull()) {
kDebug() << "Extract to " << triggeredAction->data().toString();
const QString userDestination = triggeredAction->data().toString();
QString finalDestinationDirectory;
const QString detectedSubfolder = detectSubfolder();
if (!isSingleFolderArchive()) {
finalDestinationDirectory = userDestination +
QDir::separator() + detectedSubfolder;
QDir(userDestination).mkdir(detectedSubfolder);
} else {
finalDestinationDirectory = userDestination;
}
Kerfuffle::ExtractionOptions options;
options[QLatin1String( "PreservePaths" )] = true;
QList<QVariant> files = selectedFiles();
ExtractJob *job = m_model->extractFiles(files, finalDestinationDirectory, options);
registerJob(job);
connect(job, SIGNAL(result(KJob*)),
this, SLOT(slotExtractionDone(KJob*)));
job->start();
}
}
bool Part::isPreviewable(const QModelIndex& index) const
{
return index.isValid() && (!m_model->entryForIndex(index)[ IsDirectory ].toBool());
}
void Part::selectionChanged()
{
m_infoPanel->setIndexes(m_view->selectionModel()->selectedRows());
}
KAboutData* Part::createAboutData()
{
return new KAboutData("ark", 0, ki18n("ArkPart"), "3.0");
}
bool Part::openFile()
{
const QString localFile(localFilePath());
const QFileInfo localFileInfo(localFile);
const bool creatingNewArchive =
arguments().metaData()[QLatin1String("createNewArchive")] == QLatin1String("true");
if (localFileInfo.isDir()) {
KMessageBox::error(NULL, i18nc("@info",
"<filename>%1</filename> is a directory.",
localFile));
return false;
}
if (creatingNewArchive) {
if (localFileInfo.exists()) {
int overwrite = KMessageBox::questionYesNo(NULL, i18nc("@info", "The archive <filename>%1</filename> already exists. Would you like to open it instead?", localFile), i18nc("@title:window", "File Exists"), KGuiItem(i18n("Open File")), KStandardGuiItem::cancel());
if (overwrite == KMessageBox::No) {
return false;
}
}
} else {
if (!localFileInfo.exists()) {
KMessageBox::sorry(NULL, i18nc("@info", "The archive <filename>%1</filename> was not found.", localFile), i18nc("@title:window", "Error Opening Archive"));
return false;
}
}
QScopedPointer<Kerfuffle::Archive> archive(Kerfuffle::Archive::create(localFile, m_model));
if ((!archive) || ((creatingNewArchive) && (archive->isReadOnly()))) {
QStringList mimeTypeList;
QHash<QString, QString> mimeTypes;
if (creatingNewArchive) {
mimeTypeList = Kerfuffle::supportedWriteMimeTypes();
} else {
mimeTypeList = Kerfuffle::supportedMimeTypes();
}
foreach(const QString& mime, mimeTypeList) {
KMimeType::Ptr mimePtr(KMimeType::mimeType(mime));
if (mimePtr) {
// Key = "application/zip", Value = "Zip Archive"
mimeTypes[mime] = mimePtr->comment();
}
}
QStringList mimeComments(mimeTypes.values());
mimeComments.sort();
bool ok;
QString item;
if (creatingNewArchive) {
item = KInputDialog::getItem(i18nc("@title:window", "Invalid Archive Type"),
i18nc("@info", "Ark cannot create archives of the type you have chosen.<nl/><nl/>Please choose another archive type below."),
mimeComments, 0, false, &ok);
} else {
item = KInputDialog::getItem(i18nc("@title:window", "Unable to Determine Archive Type"),
i18nc("@info", "Ark was unable to determine the archive type of the filename.<nl/><nl/>Please choose the correct archive type below."),
mimeComments,
0,
false,
&ok);
}
if ((!ok) || (item.isEmpty())) {
return false;
}
archive.reset(Kerfuffle::Archive::create(localFile, mimeTypes.key(item), m_model));
}
if (!archive) {
KMessageBox::sorry(NULL, i18nc("@info", "Ark was not able to open the archive <filename>%1</filename>. No plugin capable of handling the file was found.", localFile), i18nc("@title:window", "Error Opening Archive"));
return false;
}
KJob *job = m_model->setArchive(archive.take());
registerJob(job);
job->start();
m_infoPanel->setIndex(QModelIndex());
if (arguments().metaData()[QLatin1String( "showExtractDialog" )] == QLatin1String( "true" )) {
QTimer::singleShot(0, this, SLOT(slotExtractFiles()));
}
return true;
}
bool Part::saveFile()
{
return true;
}
bool Part::isBusy() const
{
return m_busy;
}
void Part::slotLoadingStarted()
{
}
void Part::slotLoadingFinished(KJob *job)
{
kDebug();
if (job->error()) {
if (arguments().metaData()[QLatin1String( "createNewArchive" )] != QLatin1String( "true" )) {
KMessageBox::sorry(NULL, i18nc("@info", "Loading the archive <filename>%1</filename> failed with the following error: <message>%2</message>", localFilePath(), job->errorText()), i18nc("@title:window", "Error Opening Archive"));
// The file failed to open, so reset the open archive, info panel and caption.
m_model->setArchive(NULL);
m_infoPanel->setPrettyFileName(QString());
m_infoPanel->updateWithDefaults();
emit setWindowCaption(QString());
}
}
m_view->sortByColumn(0, Qt::AscendingOrder);
m_view->expandToDepth(0);
// After loading all files, resize the columns to fit all fields
m_view->header()->resizeSections(QHeaderView::ResizeToContents);
updateActions();
}
void Part::setReadyGui()
{
kDebug();
QApplication::restoreOverrideCursor();
m_busy = false;
m_view->setEnabled(true);
updateActions();
}
void Part::setBusyGui()
{
kDebug();
QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
m_busy = true;
m_view->setEnabled(false);
updateActions();
}
void Part::setFileNameFromArchive()
{
const QString prettyName = url().fileName();
m_infoPanel->setPrettyFileName(prettyName);
m_infoPanel->updateWithDefaults();
emit setWindowCaption(prettyName);
}
void Part::slotPreview()
{
slotPreview(m_view->selectionModel()->currentIndex());
}
void Part::slotPreview(const QModelIndex & index)
{
if (!isPreviewable(index)) {
return;
}
const ArchiveEntry& entry = m_model->entryForIndex(index);
if (!entry.isEmpty()) {
Kerfuffle::ExtractionOptions options;
options[QLatin1String( "PreservePaths" )] = true;
ExtractJob *job = m_model->extractFile(entry[ InternalID ], m_previewDir.name(), options);
registerJob(job);
connect(job, SIGNAL(result(KJob*)),
this, SLOT(slotPreviewExtracted(KJob*)));
job->start();
}
}
void Part::slotPreviewExtracted(KJob *job)
{
// FIXME: the error checking here isn't really working
// if there's an error or an overwrite dialog,
// the preview dialog will be launched anyway
if (!job->error()) {
const ArchiveEntry& entry =
m_model->entryForIndex(m_view->selectionModel()->currentIndex());
QString fullName =
m_previewDir.name() + QLatin1Char('/') + entry[FileName].toString();
// Make sure a maliciously crafted archive with parent folders named ".." do
// not cause the previewed file path to be located outside the temporary
// directory, resulting in a directory traversal issue.
fullName.remove(QLatin1String("../"));
ArkViewer::view(fullName, widget());
} else {
KMessageBox::error(widget(), job->errorString());
}
setReadyGui();
}
void Part::slotError(const QString& errorMessage, const QString& details)
{
if (details.isEmpty()) {
KMessageBox::error(widget(), errorMessage);
} else {
KMessageBox::detailedError(widget(), errorMessage, details);
}
}
bool Part::isSingleFolderArchive() const
{
return m_model->archive()->isSingleFolderArchive();
}
QString Part::detectSubfolder() const
{
if (!m_model) {
return QString();
}
return m_model->archive()->subfolderName();
}
void Part::slotExtractFiles()
{
if (!m_model) {
return;
}
QWeakPointer<Kerfuffle::ExtractionDialog> dialog = new Kerfuffle::ExtractionDialog;
if (m_view->selectionModel()->selectedRows().count() > 0) {
dialog.data()->setShowSelectedFiles(true);
}
dialog.data()->setSingleFolderArchive(isSingleFolderArchive());
dialog.data()->setSubfolder(detectSubfolder());
dialog.data()->setCurrentUrl(QFileInfo(m_model->archive()->fileName()).path());
if (dialog.data()->exec()) {
//this is done to update the quick extract menu
updateActions();
QVariantList files;
//if the user has chosen to extract only selected entries, fetch these
//from the listview
if (!dialog.data()->extractAllFiles()) {
files = selectedFilesWithChildren();
}
kDebug() << "Selected " << files;
Kerfuffle::ExtractionOptions options;
if (dialog.data()->preservePaths()) {
options[QLatin1String("PreservePaths")] = true;
}
options[QLatin1String("FollowExtractionDialogSettings")] = true;
const QString destinationDirectory = dialog.data()->destinationDirectory().pathOrUrl();
ExtractJob *job = m_model->extractFiles(files, destinationDirectory, options);
registerJob(job);
connect(job, SIGNAL(result(KJob*)),
this, SLOT(slotExtractionDone(KJob*)));
job->start();
}
delete dialog.data();
}
QList<QVariant> Part::selectedFilesWithChildren()
{
Q_ASSERT(m_model);
QModelIndexList toIterate = m_view->selectionModel()->selectedRows();
for (int i = 0; i < toIterate.size(); ++i) {
QModelIndex index = toIterate.at(i);
for (int j = 0; j < m_model->rowCount(index); ++j) {
QModelIndex child = m_model->index(j, 0, index);
if (!toIterate.contains(child)) {
toIterate << child;
}
}
}
QVariantList ret;
foreach(const QModelIndex & index, toIterate) {
const ArchiveEntry& entry = m_model->entryForIndex(index);
if (entry.contains(InternalID)) {
ret << entry[ InternalID ];
}
}
return ret;
}
QList<QVariant> Part::selectedFiles()
{
QStringList toSort;
foreach(const QModelIndex & index, m_view->selectionModel()->selectedRows()) {
const ArchiveEntry& entry = m_model->entryForIndex(index);
toSort << entry[ InternalID ].toString();
}
toSort.sort();
QVariantList ret;
foreach(const QString &i, toSort) {
ret << i;
}
return ret;
}
void Part::slotExtractionDone(KJob* job)
{
kDebug();
if (job->error()) {
KMessageBox::error(widget(), job->errorString());
} else {
ExtractJob *extractJob = qobject_cast<ExtractJob*>(job);
Q_ASSERT(extractJob);
const bool followExtractionDialogSettings =
extractJob->extractionOptions().value(QLatin1String("FollowExtractionDialogSettings"), false).toBool();
if (!followExtractionDialogSettings) {
return;
}
if (ArkSettings::openDestinationFolderAfterExtraction()) {
KUrl destinationDirectory(extractJob->destinationDirectory());
destinationDirectory.cleanPath();
KRun::runUrl(destinationDirectory, QLatin1String("inode/directory"), widget());
}
if (ArkSettings::closeAfterExtraction()) {
emit quit();
}
}
}
void Part::adjustColumns()
{
kDebug();
m_view->header()->setResizeMode(0, QHeaderView::ResizeToContents);
}
void Part::slotAddFiles(const QStringList& filesToAdd, const QString& path)
{
if (filesToAdd.isEmpty()) {
return;
}
kDebug() << "Adding " << filesToAdd << " to " << path;
kDebug() << "Warning, for now the path argument is not implemented";
QStringList cleanFilesToAdd(filesToAdd);
for (int i = 0; i < cleanFilesToAdd.size(); ++i) {
QString& file = cleanFilesToAdd[i];
if (QFileInfo(file).isDir()) {
if (!file.endsWith(QLatin1Char( '/' ))) {
file += QLatin1Char( '/' );
}
}
}
CompressionOptions options;
QString firstPath = cleanFilesToAdd.first();
if (firstPath.right(1) == QLatin1String( "/" )) {
firstPath.chop(1);
}
firstPath = QFileInfo(firstPath).dir().absolutePath();
kDebug() << "Detected relative path to be " << firstPath;
options[QLatin1String( "GlobalWorkDir" )] = firstPath;
AddJob *job = m_model->addFiles(cleanFilesToAdd, options);
if (!job) {
return;
}
connect(job, SIGNAL(result(KJob*)),
this, SLOT(slotAddFilesDone(KJob*)));
registerJob(job);
job->start();
}
void Part::slotAddFiles()
{
kDebug();
// #264819: passing widget() as the parent will not work as expected.
// KFileDialog will create a KFileWidget, which runs an internal
// event loop to stat the given directory. This, in turn, leads to
// events being delivered to widget(), which is a QSplitter, which
// in turn reimplements childEvent() and will end up calling
// QWidget::show() on the KFileDialog (thus showing it in a
// non-modal state).
// When KFileDialog::exec() is called, the widget is already shown
// and nothing happens.
const QStringList filesToAdd =
KFileDialog::getOpenFileNames(KUrl("kfiledialog:///ArkAddFiles"),
QString(), widget()->parentWidget(),
i18nc("@title:window", "Add Files"));
slotAddFiles(filesToAdd);
}
void Part::slotAddDir()
{
kDebug();
const QString dirToAdd = KFileDialog::getExistingDirectory(KUrl("kfiledialog:///ArkAddFiles"), widget(), i18nc("@title:window", "Add Folder"));
if (!dirToAdd.isEmpty()) {
slotAddFiles(QStringList() << dirToAdd);
}
}
void Part::slotAddFilesDone(KJob* job)
{
kDebug();
if (job->error()) {
KMessageBox::error(widget(), job->errorString());
}
}
void Part::slotDeleteFilesDone(KJob* job)
{
kDebug();
if (job->error()) {
KMessageBox::error(widget(), job->errorString());
}
}
void Part::slotDeleteFiles()
{
kDebug();
const int reallyDelete =
KMessageBox::questionYesNo(NULL,
i18n("Deleting these files is not undoable. Are you sure you want to do this?"),
i18nc("@title:window", "Delete files"),
KStandardGuiItem::del(),
KStandardGuiItem::cancel(),
QString(),
KMessageBox::Dangerous | KMessageBox::Notify);
if (reallyDelete == KMessageBox::No) {
return;
}
DeleteJob *job = m_model->deleteFiles(selectedFilesWithChildren());
connect(job, SIGNAL(result(KJob*)),
this, SLOT(slotDeleteFilesDone(KJob*)));
registerJob(job);
job->start();
}
void Part::slotToggleInfoPanel(bool visible)
{
QList<int> splitterSizes;
if (visible) {
splitterSizes = ArkSettings::splitterSizesWithBothWidgets();
} else {
splitterSizes = m_splitter->sizes();
ArkSettings::setSplitterSizesWithBothWidgets(splitterSizes);
splitterSizes[1] = 0;
}
m_splitter->setSizes(splitterSizes);
saveSplitterSizes();
}
void Part::saveSplitterSizes()
{
ArkSettings::setSplitterSizes(m_splitter->sizes());
ArkSettings::self()->writeConfig();
}
void Part::slotSaveAs()
{
KUrl saveUrl = KFileDialog::getSaveUrl(KUrl(QLatin1String( "kfiledialog:///ArkSaveAs/" ) + url().fileName()), QString(), widget());
if ((saveUrl.isValid()) && (!saveUrl.isEmpty())) {
if (KIO::NetAccess::exists(saveUrl, KIO::NetAccess::DestinationSide, widget())) {
int overwrite = KMessageBox::warningContinueCancel(widget(),
i18nc("@info", "An archive named <filename>%1</filename> already exists. Are you sure you want to overwrite it?", saveUrl.fileName()),
QString(),
KStandardGuiItem::overwrite());
if (overwrite != KMessageBox::Continue) {
return;
}
}
KUrl srcUrl = KUrl::fromPath(localFilePath());
if (!QFile::exists(localFilePath())) {
if (url().isLocalFile()) {
KMessageBox::error(widget(),
i18nc("@info", "The archive <filename>%1</filename> cannot be copied to the specified location. The archive does not exist anymore.", localFilePath()));
return;
} else {
srcUrl = url();
}
}
KIO::Job *copyJob = KIO::file_copy(srcUrl, saveUrl, -1, KIO::Overwrite);
if (!KIO::NetAccess::synchronousRun(copyJob, widget())) {
KMessageBox::error(widget(),
i18nc("@info", "The archive could not be saved as <filename>%1</filename>. Try saving it to another location.", saveUrl.pathOrUrl()));
}
}
}
} // namespace Ark

126
ark/part/part.h Normal file
View file

@ -0,0 +1,126 @@
/*
* ark -- archiver for the KDE project
*
* Copyright (C) 2007 Henrique Pinto <henrique.pinto@kdemail.net>
* Copyright (C) 2008-2009 Harald Hvaal <haraldhv@stud.ntnu.no>
* Copyright (C) 2009 Raphael Kubo da Costa <rakuco@FreeBSD.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#ifndef PART_H
#define PART_H
#include "interface.h"
#include <KParts/Part>
#include <KParts/StatusBarExtension>
#include <KTempDir>
#include <QModelIndex>
class ArchiveModel;
class InfoPanel;
class KAbstractWidgetJobTracker;
class KAboutData;
class KAction;
class KJob;
class QAction;
class QSplitter;
class QTreeView;
namespace Ark
{
class Part: public KParts::ReadWritePart, public Interface
{
Q_OBJECT
Q_INTERFACES(Interface)
public:
Part(QWidget *parentWidget, QObject *parent, const QVariantList &);
~Part();
static KAboutData* createAboutData();
virtual bool openFile();
virtual bool saveFile();
bool isBusy() const;
public slots:
void extractSelectedFilesTo(const QString& localPath);
private slots:
void slotLoadingStarted();
void slotLoadingFinished(KJob *job);
void slotPreview();
void slotPreview(const QModelIndex & index);
void slotPreviewExtracted(KJob*);
void slotError(const QString& errorMessage, const QString& details);
void slotExtractFiles();
void slotExtractionDone(KJob*);
void slotQuickExtractFiles(QAction*);
void slotAddFiles();
void slotAddFiles(const QStringList& files, const QString& path = QString());
void slotAddDir();
void slotAddFilesDone(KJob*);
void slotDeleteFiles();
void slotDeleteFilesDone(KJob*);
void saveSplitterSizes();
void slotToggleInfoPanel(bool);
void slotSaveAs();
void updateActions();
void selectionChanged();
void adjustColumns();
void setBusyGui();
void setReadyGui();
void setFileNameFromArchive();
signals:
void busy();
void ready();
void quit();
private:
void setupView();
void setupActions();
bool isSingleFolderArchive() const;
QString detectSubfolder() const;
bool isPreviewable(const QModelIndex& index) const;
QList<QVariant> selectedFiles();
QList<QVariant> selectedFilesWithChildren();
void registerJob(KJob *job);
ArchiveModel *m_model;
QTreeView *m_view;
KAction *m_previewAction;
KAction *m_extractFilesAction;
KAction *m_addFilesAction;
KAction *m_addDirAction;
KAction *m_deleteFilesAction;
KAction *m_saveAsAction;
InfoPanel *m_infoPanel;
QSplitter *m_splitter;
KTempDir m_previewDir;
bool m_busy;
KAbstractWidgetJobTracker *m_jobTracker;
KParts::StatusBarExtension *m_statusBarExtension;
};
} // namespace Ark
#endif // PART_H

21
ark/plugins/CLI-README Normal file
View file

@ -0,0 +1,21 @@
In this folder is a general template of what one needs to implement
support for a plugin using the cli interface in ark.
Here are the steps.
1. First, create a copy of the cliplugin folder
2. Change plugins/CMakeLists.txt to include the new subfolder
3. Rename the kerfuffle_cli.desktop to a unique name, for example
kerfuffle_rar.desktop.
4. Fill in the parts in the desktop marked with TODO
5. Update the plugins/yourplugin/CMakeLists.txt file, replacing all
instances of kerfuffle_cli with kerfuffle_yourplugin (where yourplugin
must be a unique plugin name)
6. Implement/modify cliplugin.cpp to fit your archive type.
Refer to kerfuffle/cliinterface.h for explanations on the values that
needs to be implemented. The class name does not need to be changed
Then finally, email the plugin to the ark maintainer for a code review before
it is committed to trunk :D
Have fun

Some files were not shown because too many files have changed in this diff Show more