diff --git a/CREDITS b/CREDITS index 5e8f849e..66b7c395 100644 --- a/CREDITS +++ b/CREDITS @@ -1,121 +1,121 @@ Credits, Copyrights and Licenses NOTE: If your name is missing in this Krusader credits list feel free to add your name in the proper section. Since it's almost impossible to add all contributor names in this list, we would like to thank everyone who has contributed to Krusader. You can get a full list of contributors from git log of the official repository located at [https://cgit.kde.org/krusader.git/] KRUSADER Program Copyrights: Copyright (C) 2000-2003 Shie Erlich Copyright (C) 2000-2003 Rafi Yanai -Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] +Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] This program is licensed under the terms of the GNU General Public License, version 2. For details see COPYING file. Documentation Copyrights: Copyright (C) 2000-2003 Shie Erlich Copyright (C) 2000-2003 Rafi Yanai Copyright (C) 2004-2010 Frank Schoolmeesters -Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] +Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] The documentation is licensed under the terms of the GNU Free Documentation License. Krusader is developed by a dedicated team of individuals, known as the Krusader Krew. For the list of members please see README file. The project is written using KDevelop, Qt Creator and Qt™ Designer. We would especially like to thank Dirk Eschler the Krusader web master. If you visit the Krusader website https://krusader.org/ you'll appreciate the hard work and effort that Dirk has put into the site as much as we do. This documentation is licensed under the terms of the GNU Free Documentation License. SPECIAL THANKS - kde.org - kde translation teams - Sourceforge.net, webhosting services - Jonas Bähr (webspace) - Christian Bock (former webspace) - Jiri Klement, KDE4 porting help - kde-files.org & KDE-APPS.org creator Frank Karlitschek - Google.com - Krusader Mac OS-X port with the fink-project: Jonas Bähr, Bodo Tasche, Alexander K. Hansen Catalin Hritcu - Krusader Mac OS-X port with the macports.org project. - Technical Communication team of 2007, University of Oulu, Finland; Documentation fixes for Krusader 1.80.0 - Jiří Paleček, QA, bug-hunting, patches and general help - Andrew Neupokoev (nwmod) Krusader Logo and Icons, art contest winner. - Angel Ramos, Debian® package maintainer - Terry "Fudoki" Wilkinson, handbook proofreader - Gábor Lehel (illissius), Viewer module for the 3rd Hand Panel - Szombathelyi György, ISO KIO slave - David Harel, panel toolbar buttons - Jan Willem van de Meent (Adios), Krusader icons - usefularts.org, Krusader icons - Hans Loffler, folder history list - Jan Halasa, new bookmark module - Mikolaj Machowski, Usability and QA - Cristi Dumitrescu, QA, bug-hunting, patches and general help - Aurelien Gateau, patch for KViewer - Milan Brabec, the first patch ever! TRANSLATORS Thanks to all translators team of l10n.kde.org Below are translator credits before Krusader has joined KDE Extragear. Thanks to the Documentation translators: - Russian: Roman Batejkin Thanks to the GUI translators: - Bosnian: Asim Husanovic - Brazilian Portuguese: Doutor Zero - Bulgarian: Milen Ivanov - Catalan: Rafael Munoz Rodriguez, Quim Perez Noguer - Chinese: Jinghua Luo - Czech: Martin Sixta, Václav Jůza - Danish: Anders Bruun Olsen, Christian Sonne, Peter H.S. - Dutch: Frank Schoolmeesters - French: René-Pierre Lehmann, David Guillerm - German: Christoph Thielecke, Dirk Eschler - Greek: Spiros Georgaras - Hungarian: Kukk Zoltan, Arpad Biro, Karai Csaba - Italian: Giuseppe Bordoni - Japanese: UTUMI Hirosi, Hideki Kimura - Korean: Jee Seongnam - Lithuanian: Dovydas Sankauskas - Polish: Marcin Szafran, Lukasz Janyst, Marcin Garski, Pawel Salawa, Tomek Grzejszczyk - Portuguese: Bruno Queirós - Russian: Dmitry V. Chernyak, Denis Koryavov, Nick Shaforostoff, Dmitry A. Bugay - Serbian: Sasa Tomic - Slovak: Zdenko Podobný - Slovenian: Matej Urbančič - Spanish: Rafael Munoz Rodriguez, Alejandro Araiza Alvarado, Alex Araiza - Swedish: Erik Johansson, Anders Lindén, Peter Landgren - Turkish: Bekir Sonat - Ukrainian: Ivan Petrouchtchak And to all the other translators, we are sorry we can not list all of your names here. Please take a look in the headers of the foo.po files to see more translator credits. DONATIONS We would like to thank the following organisations and individuals for their recent financial support. In addition we would to thank those who have contributed anonymously. Your support is very much appreciated. - distroWatch.com (donation) - Sven Opitz (the first donation ever!) diff --git a/doc/advanced-functions.docbook b/doc/advanced-functions.docbook index f6161a79..fda54577 100644 --- a/doc/advanced-functions.docbook +++ b/doc/advanced-functions.docbook @@ -1,60 +1,60 @@ Advanced functions &vfs; &compare; &occupied-space; &splitter; &checksum; Send Files by Email Select a file and use Right-Click menu Send by Email . Selecting this option will open a new &kmail; window with the file already attached. Just fill in the subject and recipient(s) and send it. Of course, &kmail; must be properly configured. &profiles; ACL permissions ACL permissions are fully supported when using properties, preserve attributes and the synchronizer. To view/modify the ACL permissions, select a file and use the Context Menu: Properties Permissions Advanced permissions . At Copy/Move, the ACL permissions are also copied. The synchronizer keeps the ACL permissions as well. diff --git a/doc/archives.docbook b/doc/archives.docbook index 86db9124..a7a4dad6 100644 --- a/doc/archives.docbook +++ b/doc/archives.docbook @@ -1,144 +1,144 @@ Archive Handling Archives Browsing Archives The Virtual file systems (VFS) allows you to browse archives as if it was a directory. Currently &krusader; supports the following archives types: ace, arj, bzip2, deb, gzip, iso, lha, rar, rpm, tar, xz, zip, and 7-zip and supports the following passworded archives: arj, ace, rar and zip. Passwords can be stored in Kwallet. Please note that the archive support first needs to be properly configured in Konfigurator. To open the archive, use &Enter;, (double-)click or the Right Arrow. &krusader; supports also hidden archives, these are renamed archives with a *wrong* extension, ⪚ OpenOffice uses zip archives with the following extensions: .odt, .sxw and .odw. &Enter; will open the OpenOffice document and the Right Arrow will open the archive. Another example is J2EE, where the .jar extension means a zip archive. Since &krusader; auto-detects the supported archive types, it will open with Right Arrow even if it has another MIME type. Please note that browsing archives has a few limitations: Not all functions are supported by all archive types. (&ie;: you can not delete files in ace or rar archives) The permissions you have inside the archive are the same as the permissions you have for the archive. The command line (if visible) will not follow you inside the archive but will point to the archive directory. Before any action is performed on files/directories inside the archive, they must be extracted. The archive will be repacked when you leave it or when &krusader; detects that extracted files have changed. Packing and un-packing are "blocking operations" that display a progress dialog. However, this might change in the future since we are planning to create more background operations. Unpack Files There are 2 ways to extract files from archives: If you want to unpack a whole archive(s), browse to the archive location, select the archive(s) and select File Unpack or &Alt;&Shift; U. &krusader; will ask you where to put the extracted files. The default location is the inactive panel directory. If you do not cancel the operation, &krusader; will try to unpack all the selected files in the inactive panel. If you only want to extract a part of the archive, then browse the archive and copy the files you want to extract to their new location just as you would copy "normal" files. Note that unpacking from a browsed archive takes more time (file by file unpack) compared to unpacking a whole archive with the first method. The unpack operation can be put in a queue. Packing Files If you want to create a new archive, begin by selecting the elements you want to pack in the active panel and select File Pack or &Alt;&Shift; P. A dialog will pop-up prompting you to select the archive name and location. Select the packer from one of the supported file name extensions. To add files to an existing archive(s), open the archive in one panel and the files to be copied in the other panel, then simply copy the files into the archive in exactly the same way you copy files into a "normal" directory. The pack operation can be put in a queue. Testing Archives &krusader; will not handle corrupted archives since it may result in data loss. If you receive an error message when opening an archive, or if you suspect that there is something wrong with the archive, you should test it before use. To test an archive, browse (navigate) to the archive location and select the archive. Next select File Test Archive or &Alt; E. &krusader; will test the archive file and inform you whether the archive passed or failed the file integrity test. diff --git a/doc/basic-functions.docbook b/doc/basic-functions.docbook index 3ab299de..2ce42d1e 100644 --- a/doc/basic-functions.docbook +++ b/doc/basic-functions.docbook @@ -1,451 +1,451 @@ Basic Functions Controls General This is a rather short but important section that will go into the details of controlling &krusader;. This section does not try to cover all the various key combinations for two reasons: there are just too many of them most of &krusader; actions are configurable in the Konfigurator Panel page We will only mention the most important keyboard shortcuts with the default configuration, but keep in mind that most of the Key-Bindings are configurable. If you find that you use a certain command a lot and want to know the shortcut for this command, or you want to change this command shortcut, then check out the Configure Shortcuts window (Settings Configure Shortcuts...). Moving Around By Moving Around we mean the transfer of the keyboard and mouse focus between the different parts of the &krusader; main window. The focus can be in one of five places: the Left or Right Panel, the Menu Bar, the Command Line or the Terminal Emulator. The panel that has the focus is called the Active Panel. An Active Panel will remain active until the other panel receives the focus (&ie;: if the Left Panel was active and you clicked on the Command Line - then the Left Panel remains the Active Panel). You must deliberately change which panel is active. The most common way to transfer the focus to a specific panel is to use the mouse to click on that panel. But you should be aware of the following: Clicking on the Toolbar, the FN keys bar or the Status Bar does not change the focus. Pushing the Run in Terminal Mode button in the Command Line will not transfer the focus, so you have to click inside the Input Line. When you choose a menu, the Menu Bar will become focused. It remains focused until you choose a command - the focus returns to the previous owner. There are, of course, ways to use the keyboard to change the focus: The Key will switch panels, if one of the panels has the focus or rotate between all the commands in a menu, if the Menu Bar is active. The &Ctrl; Down Arrow will take you from the Active Panel to the Command Line or Terminal Emulator, and the &Ctrl; Up Arrow will take you back from the Command Line to the Active Panel. The &Esc; Key will make the Menu Bar return the focus, if it has it, to the previous Active Panel. If you happen to be inside the Terminal Emulator, you can use the Key, or the mouse, to navigate to an Active Panel. Pressing &Alt; Underlined Letter from the Menu Bar will open that menu (unless this key combination is a "Reserved Key", used by one of &krusader;'s actions). Selecting &krusader; offers 4 Selection Modes, to say nothing about the Quickselect bar, only &krusader;'s Selection Mode is explained here. Selecting is a skill you need to master in order to get the most out of &krusader;. Since the Tree Panel allows select only one directory at a time, this paragraph mainly explains how to select files in the List Panel. Moving the cursor is easy. Left-clicking on a file or directory (referred to herein as "elements" meaning files and directories) will select it. Here are some useful pointers that will make &krusader; even easier to use (assuming you are using &krusader;'s Mouse Selection Mode): The Space and Insert keys will toggle the selection of the file under the cursor without affecting the selection of other files/directories, the cursor will go one position down. Left Clicking on a file will select, or unselect, all previously selected files. &Ctrl; Left Clicking will toggle the selection of the file under the cursor without affecting the selection of other files/directories. &Shift; Left Clicking will select all the elements between the previous cursor location and the new one. &Shift; Home selects everything above the cursor (and deselects everything below the cursor, if selected). &Shift; End selects everything below the cursor (and unselects everything above the cursor, if selected). The ".." entry is not selectable. The Edit menu can offer more ways to select your files. Executing Commands There is not a lot to say here, all you need to do is select some files (if you do not, &krusader; will operate on the file(s) or directory(s) that have the focus), choose a Command from the Menu Bar or use a Keyboard Shortcut (or the Right Click Menu) and the selected Command executes. See also Executing Files. Quick search This feature will do a quick search for the file name in the Active List Panel. Type foo(one character) to do a quick search in the Active List Panel for a file beginning with "foo". A small quick search dialog box will open below the Active List Panel. Type the first few characters of the desired file name (more than one character allowed), the cursor will jump to that filename (if it exists), ⪚ type "ab" to search for a file which begins with "ab". The Up/Down Arrow will jump to the next or previous match. &Esc; will close the quick search line. The cursor will stay on the last file found. The Quicksearch supports regular expressions for searching files. If you press &Alt; foo, the key binding shortcut will be executed. If there is no configured key binding, the remaining letter foo will be used for the quick search. Use Configure Shortcuts window (Settings Configure Shortcuts...). Quick filter This feature will do a quick filter for the file list in the Active Panel. Press &Ctrl;I to open Quickfilter bar. Type foo (one character) to filter out from the Active Panel all the files that do not have "foo" in names. You can use wildcards for the filtering. ⪚ use "a*b" to filter files which have "a" and "b" in their names. The Quickfilter follows the case-sensitivity setting of Quicksearch. Pressing &Enter; in Quickfilter mode sets focus to panel, when panel or Quickfilter is focused, &Esc; closes Quickfilter. To change the default shortcut of Quickfilter use Configure Shortcuts window (Settings Configure Shortcuts...). Quick select This feature will do a quick select for the file list in the Active Panel. Press &Ctrl;&Shift;S to open Quickselect bar. Type foo (one character) to filter out from the Active Panel all the files that do not have "foo" in names. You can use wildcards for the filtering. ⪚ use "a*b" to filter files which have "a" and "b" in their names. The Quickselect follows the case-sensitivity setting of Quicksearch. Pressing &Enter; in Quickselect mode sets focus to panel, when panel or Quickselect is focused, &Esc; closes Quickselect. To change the default shortcut of Quickselect use Configure Shortcuts window (Settings Configure Shortcuts...). Context Menu &krusader; has many Context menus that allow you to do fast operations with the mouse, usually a right-click will open the Context menu (depending on your Selection Modes settings). This list gives an overview of the most important context menus. Use them to discover the available commands. Main Toolbar (orientation, text position, icon size) List Panel on a file or directory Command Line (undo, paste, text completion...) Terminal emulator (send signal, font, history...) Folder tabs (new, duplicate, close) KruSearcher Search results file list (F3 View, F4 Edit) MountMan (unmount, format...) Synchronize Directories File List UserActions &konqueror; Right-Click actions are shown in &krusader; Show/hide Column Headers Bookmarks enable/disable permanent bookmarks ... Basic File Management Executing Files You can only execute files in the Active List Panel. To execute a file just (double-)click on it or press &Enter; when it is under the list cursor. &krusader; will open it in the default application for this file type (picture, text file...) or the file will be executed (script, binary...). If you want to use another application to open the file, click with the &RMB; on the file and go to the Open with sub-menu which will offer more options. &krusader; is compatible with the &plasma; default file manager for all file types except archives that are opened inside the &krusader; panel and not in an external application. See the archives page in Konfigurator for details. Copying and Moving To copy or move files/directories just select them and press F5 to copy or F6 to move them. &krusader; tries to preserve the time of modification. When copying a directory owner is changed to the user which copies the directory, group is changed to the default group of the user. When copying a file owner is changed to the user which copies the file, group is changed to default group of the user, and permissions are preserved. The owner, the group and the permissions are preserved when moving file or directory. When copying or moving files/directories a dialog appears and allows you to choose the operation destination. The default destination is the other-panel browsed directory. If you enter a partial &URL;, &krusader; will use the current panel directory as the &URL; base.
Copy dialog Copy dialog
Queue manager The copy, move, pack and unpack operations can be queued. After selecting copy or move, hit F2 to use it. After selecting pack or unpack, hit F2 to use it. Or the direct shortcuts: copy by queue &Shift;F5 , move by queue &Shift;F6 . Queue manager performs actions one-by-one. For example, if you have a pen drive (which is slow), and you want to copy 40 files onto it, it is much better copying them one-by-one instead of starting to copy 40 files in parallel. That is why enqueuing is important. If you pack/unpack 40 files in parallel, you overload your computer, but adding them to a queue, is much more useful. It is possible to switch the Queue Manager mode by the Settings Job Queue Mode menu item. Deleting - move to &plasma; Trash Deleting files/directories is done by selecting them and pressing F8 or Delete. By default it will be moved to &plasma; Trash. You can open the &plasma; Trash with the Trash icon in the Main Toolbar or with the trash:/ KIO slave. Physically &plasma; Trash is located in ~/.local/share/Trash/ or Trash in the subdirectory in user home directory which can be determined using the qtpaths --paths GenericDataLocation command. &Shift; Delete will delete the file permanently. A dialog will ask for your confirmation and will warn you when deleting non-empty directories. Of course only operations that are permitted to do by the Operating System will be performed - you will be notified otherwise. If you do not wish to see the confirmation dialogs, you can disable them in the Konfigurator advanced page. Shred Files Shred was removed from &kde; 4 (and as consequence &krusader;-2 for &kde; 4). The main reason is probably that shredding is filesystem dependent and even if you overwrite the file 100 times, it is not sure, that it will be deleted from the disk finally. Quote from the kgpg posts: The shred feature has been removed from the KDE library. Kgpg will not support this feature anymore. Moderns file systems use journalisation. So the shred feature should be implemented in the file system. Not in kgpg. But you might use a proper shred UserAction for your filesystem. But keep in mind that if you want to be 100% sure that it is impossible that someone can read a deleted file, you need to destroy your harddrive hardware... Renaming Files, Creating Directories and Link Handling Rename the file under the cursor with the F2 key or with two single mouse clicks. If only the file name needs to be renamed and not the extension, you can configure this in the Konfigurator General page. Create a new directory with the F7 key. Right-clicking on a regular file will give you the option Link Handling New Symlink. A dialog will prompt you to enter a symlink name. That symlink will point to the file/directory you right-clicked on. If the file you right-clicked on is a symlink, you will also be presented with the Link Handling Redirect Link option that will allow you to change the link target. Viewing and Editing files KrViewer has a chapter of its own.
&archives;
diff --git a/doc/bookmarks.docbook b/doc/bookmarks.docbook index b53152a8..85b5a24c 100644 --- a/doc/bookmarks.docbook +++ b/doc/bookmarks.docbook @@ -1,297 +1,297 @@ BookMan: Organize your Bookmarks Bookmarks BookMan is &krusader;'s Bookmark tool for bookmarking folders, local and remote URLs, and later returning to them in a click of a button. The Bookman menu is divided into four sections: Your personal bookmarks Popular &URL;s Permanent bookmarks Manage bookmarks You can bookmark inside a remote file system (&FTP; or SAMBA) and later use the bookmark to quickly connect to the remote machine, but you cannot bookmark inside an archive.
Bookmark menu Bookmark menu
Using Bookmarks Usually, you click on the BookMan II button (the rightmost button to the right of the address bar at the top of the active panel) when you are in the target folder. For example, to bookmark /usr/tmp, navigate &krusader; there and click the BookMan II button and select Bookmark Current. When the Add Bookmark dialog box appears, the &URL; line will say /usr/tmp. Name is for the name you would like to give to the bookmark (⪚: temporary folder). Hotkeys are supported by adding & to the bookmark name ⪚ &sourceforge, &home, down&loads &etc;, so you can quickly call the bookmark name with &Alt; foo. If you add the bookmark without entering a name, the bookmarks name will be the same as its target. URL is where the bookmark points to. Create in creates bookmark folders for organizing your bookmarks. To use your bookmarks, click the BookMan II button and choose a bookmark. You may also use the Key-Bindings: &Ctrl; B to bookmark the current item, &Alt; right/left arrow to open right/left panel bookmarks list and &Ctrl; D to open the bookmarks of the active panel. From the context menu of the bookmark (&RMB; click) you can open it in a new Folder tab. There is a quick search bar at the top of the BookMan II menu. The search bar will always be visible in the bookmarks menu if you check the corresponding item on the Panel Konfigurator's page. Below are the rules to handle bookmarks: Just type letters to search: bookmarks are matched by name prefix If multiple bookmarks are matched by the prefix, they are highlighted and the first one is selected - hit &Enter; to activate the bookmark Once a single bookmark is matched, it is immediately triggered. That is when you type 'h' and only one bookmark starts with 'h', it is executed immediately - no &Enter; needed Accelerators work only if the keystroke is the first one &Backspace; is supported Search is case insensitive unless you type a capital letter. That is typing HamBurglar will only match HamBurglar but not hamburglar or Hamburglar. Search bar is intentionally a read-only field Password handling One of the benefits of the bookmark manager is that the passwords are saved using &plasma;'s services - where ⪚ your &konqueror;/&kmail; password are saved. If you have a wallet, &plasma; will save the passwords there. By clicking the bookmarks, a dialog will open and ask for your username and password. Supply them and click Save password. The passwords and usernames will be saved securely by &plasma;'s wallet (make sure it is enabled by your distro). The downside here is that if the system was reinstalled and you do not backup your passwords from the wallet as well as &krusader;'s bookmark file, something will be lost. Popular URLs Popular URLs The submenu Popular URLs holds persistent the most popular visited &URL;s (local or remote). This submenu displays the top 15 popular URLs, sorted by ranking, so that the top &URL; is the most popular. This is easy for when you need to visit &URL;s often, but do not want to create Bookmarks, it serves as a temporary bookmark list for the most visited links. Quicksearch in Popular URLs is a fast way to browse/select the popular &URL;s: Open the Popular URLs or use &Ctrl; Z Type a few letters to narrow the search Press &Enter; (you will go to the &URL; from list) Permanent bookmarks These are hardcoded bookmarks to the top bookmark menu; there is no need for them to be saved to the file. With the Context Menu you can enable/disable these permanent bookmarks. The following permanent bookmarks are available: Trash bin uses &plasma;'s trash:/ protocol. Local Network uses &plasma;'s remote:/ protocol. Virtual Filesystem Virtual file systems (VFS) virt:/ is not a virtual folder, but a container for &URL;s from different file systems. It is used for ⪚ the Feed to Listbox feature of the search module and the synchronizer. The location toolbar will display ⪚ virt:/Search results 1 or virt:/Synchronise results 1. Jump back or &Ctrl; J brings you back to your starting position when you did create a new tab in the panel window. This feature is very handy if you go deep into a directory tree, then you can return with one action to the starting point. With Set jump back point here or &Ctrl;&Shift; J sets the current directory as a jump back point. Manage bookmarks To edit and organize your bookmarks, by clicking the BookMan II button and selecting the Manage Bookmarks, KeditBookmarks will be displayed. This is where you can edit/remove and rearrange the bookmarks. KeditBookmarks is a bookmark editor for &konqueror;, &krusader; and other applications, which use the XBEL standard for the bookmark format. The bookmarks are stored in ~/.local/share/krusader/krbookmarks.xml. The default folder for the bookmarks file krusader/krbookmarks.xml is ~/.local/share. You can determine the needed folder in your system using the qtpaths --paths GenericDataLocation command in terminal. This local file exists only if you have edited &krusader; bookmarks. It will be created after the first run of KeditBookmarks from &krusader;. The default system bookmarks are stored in the kfileplaces/bookmarks.xml file in the directory which can be determined using the qtpaths --paths GenericDataLocation command. KeditBookmarks is easy to use, however, if you need more information, please read the KeditBookmarks handbook.
<application>KeditBookmarks</application> Bookmark Manager KeditBookmarks Bookmark Manager
Bookmarks as action buttons on the Toolbar Bookmarks can be placed as buttons on the Main Toolbar or the Actions Toolbar. You can even make a key-binding for them: Create your Bookmarks. If you want, create keybindings for the bookmarks in the Configure Shortcuts window (Settings Configure Shortcuts...). Add your bookmarks as action buttons on the Main Toolbar or the Actions Toolbar with the Configure Toolbars window (Settings Configure Toolbars...).
diff --git a/doc/checksum.docbook b/doc/checksum.docbook index 54f42d9b..e0b1798e 100644 --- a/doc/checksum.docbook +++ b/doc/checksum.docbook @@ -1,51 +1,51 @@ Checksum Creation and Verification Checksum File Create Checksum : &krusader; checks which tools you have and if those tools allow recursive operation (in case you selected folders), you can generate a checksum by choosing a checksum type from the list (md5, sha, &etc;). You can then save the checksum to a file, usually called checksum.md5 or checksum.sha1. File Verify Checksum : &krusader; checks if you have a tool that supports the type of checksum you need (from your specified checksum file) and displays the files that failed the checksum (if any). The system abstracts over different checksum mechanisms and Checksum Utilities. At the moment, the following checksums are supported: md5, sha1, sha256, sha224, sha256, sha384, sha512, tiger, whirlpool, cfv and crc. Please check your Checksum Utilities settings before using this function. diff --git a/doc/compare.docbook b/doc/compare.docbook index 4ad00d96..e2665837 100644 --- a/doc/compare.docbook +++ b/doc/compare.docbook @@ -1,105 +1,105 @@ Compare &krusader; contains several compare functions: Compare by Content, Compare Directories and the Synchronizer compare function. Compare by Content Compare Content Select one file in each panel and use File Compare by content . &krusader; will open an external graphical difference tool that is configured. &kompare;, KDiff3 and xxdiff are supported. Compare Directories Compare Directories Compare the files of each panel by using Edit Compare Directories . By default newer and single files will be selected in each panel, this behaviour can be changed in the Compare Setup menu. The files are not compared by content but by their names and dates only. Edit Compare Setup Configures the Compare Directories function. Select Newer and Single (default) Select Single Select Newer Select Different and Single Select Different diff --git a/doc/configuration-files.docbook b/doc/configuration-files.docbook index a884e317..1fd23440 100644 --- a/doc/configuration-files.docbook +++ b/doc/configuration-files.docbook @@ -1,158 +1,158 @@ Configuration Files Configuration files This appendix gives an overview of the configuration files used by &krusader;. krusaderui.rc This file stores the users toolbar settings and the menu structure. Location: /usr/share/kxmlgui5/krusader/krusaderui.rc, or ~/.local/share/kxmlgui5/krusader/krusaderui.rc, or kxmlgui5/krusader/krusaderui.rc in the directory which can be determined using the qtpaths --paths GenericDataLocation command krusaderrc This file stores the &krusader; configuration. Location: ~/.config/krusaderrc The following settings are ⪚ stored: Actions Toolbar Archives Colors Dependencies DiskUsage DiskUsageViewer General HTML Settings KFileDialog KFileDialog Speedbar KrDetailedViewLeft KrDetailedViewRight KrViewerWindow KrViewerWindow Toolbar extraToolBar Look & Feel Notification Messages Panel Profiles Private Protocols Search Startup Synchronize Locate SynchronizerProfile - 1 krbookmarks.xml This file stores the &krusader; bookmarks and uses the XBEL standard. Location: ~/.local/share/krusader/krbookmarks.xml or krusader/krbookmarks.xml in the directory which can be determined using the qtpaths --paths GenericDataLocation command useractions.xml This file stores the UserActions configuration. Location: ~/.local/share/krusader/useractions.xml or krusader/useractions.xml in the directory which can be determined using the qtpaths --paths GenericDataLocation command useraction-examples.xml This is the default UserActions provided by developers. Location: $KDEDIR/share/krusader/useraction-examples.xml foo.keymap This is a .ini file that holds a Key binding profile. Until &krusader;-1.70.0 this was a binary file. &krusader; is backwards compatible for importing this legacy binary format. Location: /usr/share/krusader/foo.keymap foo.color This is a binary file that holds the Color Scheme. Location: /usr/share/krusader/foo.color diff --git a/doc/credits-and-license.docbook b/doc/credits-and-license.docbook index e0ab89c9..b2386229 100644 --- a/doc/credits-and-license.docbook +++ b/doc/credits-and-license.docbook @@ -1,162 +1,162 @@ Credits and License Credits License &krusader; -Program copyright 2000-2003 Shie Erlich and Rafi Yanai, 2004-2018 &krusader; Krew +Program copyright 2000-2003 Shie Erlich and Rafi Yanai, 2004-2019 &krusader; Krew krusader-devel@googlegroups.com &krusader; is developed by a dedicated team of individuals, known as the &krusader; Krew. Shie Erlich, author (retired) erlich * users.sourceforge.net Rafi Yanai, author (retired) yanai * users.sourceforge.net Dirk Eschler, webmaster (retired) deschler * users.sourceforge.net Csaba Karai, developer (retired) ckarai * users.sourceforge.net Heiner Eichmann, developer (retired) h.eichmann * gmx.de Jonas Bähr, developer (retired) jonas.baehr * web.de Václav Jůza, developer (retired) vaclavjuza * seznam.cz Jan Lepper, developer (retired) jan_lepper * gmx.de Andrey Matveyakin, developer (retired) a.matveyakin * gmail.com Davide Gianforte, developer davide * gengisdave.org Toni Asensi Esteve, developer toni.asensi * kdemail.net Alexander Bikadorov, developer alex.bikadorov * kdemail.net Martin Kostolný, developer clearmartin * gmail.com Nikita Melnichenko, developer nikita+kde * melnichenko.name Frank Schoolmeesters, Documentation & Marketing coordinator (retired) frank_schoolmeesters * yahoo.com Richard Holt, Documentation & Proofing (retired) richard.holt * gmail.com Yuri Chornoivan, Documentation yurchor * ukr.net Matej Urbančič, Marketing & Product Research (retired) matej.urban * gmail.com We would especially like to thank Dirk Eschler the &krusader; web master. If you visit the &krusader; website you will appreciate the hard work and effort that Dirk has put into the site as much as we do. We would like to thank everyone who has contributed to &krusader;. Sorry we can not mention all of you in this handbook. For a more complete list of &krusader; contributors please take a look at the CREDITS and Changelog file online or in the &krusader; sources. Special thanks to: kde.org KDE translation teams Sourceforge.net Jonas Bähr (webspace) store.kde.org Google.com Documentation Copyright 2000-2003 Shie Erlich and Rafi Yanai. Documentation Copyright 2004-2011 Frank Schoolmeesters. - Documentation Copyright 2004-2018 &krusader; Krew. + Documentation Copyright 2004-2019 &krusader; Krew. &underFDL; - Copyright 2004-2018 &krusader; Krew krusader-devel@googlegroups.com. + Copyright 2004-2019 &krusader; Krew krusader-devel@googlegroups.com. &underGPL; diff --git a/doc/diskusage.docbook b/doc/diskusage.docbook index 45079e0e..831aac64 100644 --- a/doc/diskusage.docbook +++ b/doc/diskusage.docbook @@ -1,138 +1,138 @@ Disk Usage Disk Usage The Disk Usage is based on the code of Filelight. The Disk Usage shows you how your disk space is being used by a graphical representation of your file system. The following presentations are possible: Line view Detailed view Filelight view: as a set of concentric segmented-rings Choose Tools Disk Usage or &Alt;&Shift; S to start this feature in a separate window, or open it inside the Sidebar. Disk Usage Keybindings, right click menu. Del: Delete &Ctrl; E: Exclude &Shift; Arrow Up: Up one directory &Ctrl; N: new search &Ctrl; R: refresh &Ctrl; I: include all &Shift; Arrow Down: step into View menu: &Ctrl; L: Line view &Ctrl; D: Detailed view &Ctrl; F: Filelight view &Shift; Arrow Right: Next view &Shift; Arrow Left: Previous view diff --git a/doc/editors-note.docbook b/doc/editors-note.docbook index 97bff907..917e4b1c 100644 --- a/doc/editors-note.docbook +++ b/doc/editors-note.docbook @@ -1,72 +1,72 @@ Note from the Editors As you may notice, this handbook is written by various authors. The draft explanation of new features is done by the developers on the krusader-devel mailinglist, the editors add this information in this handbook. We have taken much care in ensuring technical and vocabulary consistency. The authors write in English even though it is not their native language; therefore, you may notice strange sentence constructions. Do not hesitate to let us know, if something is not clear to you. An open source project's greatest strength is derived from getting user feedback. That is why we would love to hear what you have to say. Documentation contributors are always welcome!, since this is a time consuming task. If you have ideas on how to improve the content, if you would like to write about an undocumented &krusader; feature, or if you have comments or suggestions about the &krusader; Handbook, please let us know. You can contact us even if you find typos! For information about the &krusader; documentation project, please contact the &krusader; documentation coordinator, Yuri Chornoivan yurchor@ukr.net. This handbook is available in several formats. via the Help Menu when using &krusader; the Online Handbook The most recent version of this handbook is available when using &krusader; from Git. If you are using a development or &git; version, it is possible that the latest new features are not yet in the "The &krusader; Handbook". Please read the Changelog file online or in the &krusader; sources to find out more about the new features. For a brief description, please read the NEWS file online or in the &krusader; sources. User feedback and Bug reports are always welcome! All mentioned Trademarks and Copyrights in this handbook belong to their respective owners. diff --git a/doc/faq.docbook b/doc/faq.docbook index ffc11ffd..331fcf84 100644 --- a/doc/faq.docbook +++ b/doc/faq.docbook @@ -1,1037 +1,1037 @@ Frequently Asked Questions (&FAQ;) FAQ If you have problems with &krusader; please check the installation procedure, as your problem may be caused by a bad installation. The &FAQ; is divided into three sections: Installation &FAQ; (this page) Usage &FAQ; (issues with running/using &krusader;) General &FAQ; (bug reports, forum, mailing list, ...) If you feel that a &FAQ; is missing or if something is not clear to you, please let us know. Installation &FAQ; Does &krusader; need &plasma; to run? No, &krusader; does not need the &plasma; window manager to run on your computer but &krusader;'s natural environment is &plasma;, because it relies on services provided by the &kf5-full; base libraries. Only some shared libraries are needed ⪚ &kf5; libraries, &Qt; libraries, &etc; This means that &krusader; runs on GNOME, AfterStep, XFce and other window managers provided the appropriate libraries are installed on your computer. All this is not a problem since the apt-get world can resolve these dependencies instantly. You do not need to switch to the &plasma; window manager to use &krusader;; you can still use GNOME, AfterStep, XFce or another window manager. However, the configuration of the &krusader; fonts and some behavior is done by running systemsettings. A lot depends on what you want to do with &krusader;. You should know that &krusader; uses the &kf5; KIO Slaves to access remote file systems, and support for only a limited number of file system types are shipped with &kf5; libraries, most of the KIO Slave are bundled with &kf5-full; (including fish, sftp and tar). To summarize, if you want a working &krusader; - install &kf5-full;. If you want a functional &krusader; then you need &kf5-full; and some additional &plasma; packages. For maximum functionality, &krusader; needs some &kde; Applications (&ark;, &kdiff3;, KRename), but the &plasma; window manager is optional. I cannot find a package for &krusader;, what to do? Check if your distribution provides &krusader;, if it does not then contact your distribution creator and ask them to include &krusader;! Did you take a look at the installation procedure page? If you have and found nothing, you can always compile &krusader; from source. Feel free to post a help request if you encounter problems. I am having trouble compiling and installing &krusader;, how do I send a help request? Please read our detailed installation procedure and the FAQ. If you cannot find a solution to your problem then feel free to post a help request at our &krusader; forum or use the &krusader;-users mailing list. But before posting a help request, please try the following: Use the search function on the &kde; Forum, your problem might be solved or discussed already. If you cannot find a solution, please make a note of the following issues: the &krusader; version used the &Linux; distribution + version used a good description of the problem Without this information, it is very difficult for us to help you. If you give us the error messages (⪚ make error messages) please start with the FIRST error message, all the error messages which follow the first error message are consequences and not the cause of your problem. Send out your help request. Thank you for your co-operation! Why does &krusader;-git show an old version number in the about box? Because we only change the version number just before we do a new release. &krusader;-git will show an older version number, so ignore the version number and check the download date. We simply have not yet decided what the next version number will be. Usage &FAQ; Why does &krusader; freeze or hang on a dead mountpoint? When &krusader; is ⪚ browsing an NFS share and the NFS server goes down, &krusader; will freeze. This &krusader; (and all other open internal viewers/editors) freeze is fatal, and can only be corrected with the kill -9. We have no solution for this. This is an issue not confined to file managers, or even the &Linux; OS! The problem is that you "hang" on the first access to the dead share - so there is no way around it, no check to avoid it, even ls will freeze. Just trying to read something - anything - is enough to get you stuck. The one and only way around this architectural problem is using a multi-threaded design - this way if we get stuck, we do not hang the entire application event loop, but we do not think that the time is right for adding threads, we are not sure that *all* the &kf5; systems out there are using &Qt;-mt (the multi threaded version of the &Qt; library), and the &krusader; Krew may not be the right people to address this major issue in any case; so currently this is just a bug we have to learn to live with. When I try to resize &krusader; to make it smaller, I discover that I cannot resize it below a certain size. Why? See the F1, F2 (&etc;) buttons? They are not allowing &krusader; to downsize as they have a minimum size. Just disable them Settings Show FN Keys Bar and you will be able to resize &krusader; to your liking. Since version 1.51 we have improved this greatly: when downsizing the buttons will look like 'F5 ..py'. When the button faces are too small to read a tooltip will give the complete text. The minimum width is 45 pixels for each button. Samba ISO 8859-x codepage / What to do if &krusader; does not read shared directories containing special international characters? &krusader; does not handle (yet) Samba ISO 8859-x codepages, if you use a codepage different than 8859-1 you will have to do a manual configuration. Create or modify the file: ~/.smb/smb.conf [global] workgroup = MyWorkGroup (ex. WORKGROUP) client code page = MyCodePage (ex. 852) character set = MyCharSet (ex. ISO8859-2) You can try to configure smb.conf with appropriate &systemsettings; module. &krusader; reports "krarc:... protocol not supported" error at opening an archive file, what to do? Install the krarc slave properly: Copy the kio_krarc.so, kio_krarc.la files into /usr/{lib or lib64}/qt5/plugins Copy the krarc.protocol file into /usr/share/services or the directory where the KIO slaves are placed in your &Linux; distribution. I get the error message "Protocol not supported by Krusader: "krarc:/path/to/foo-archive", when I try to open foo-Archive, what to do? The icons, kio_slaves and documentation must be installed in the correct places in the &kf5; directory tree. The kio_krarc.* files must be in the same directory with the other KIO slaves. Try this: locate kio_tar.* and copy/link the kio_krarc.* files to the same location. Do not forget to run # ldconfig on this directory when you are done. What to do if an external tool does not seem to work? &krusader; uses several programs as external tools, and sometimes they appear not to work. Open a terminal and check if tool foo is installed. $ foo Check if tool foo is properly configured in the Konfigurator Dependencies page. For Archiving tools: autodetect the archives again with the Auto Configure button in the Konfigurator archive page. Check the Konfigurator Protocol page If it does not work, backup your XDG_CONFIG_HOME/krusaderrc (default is $HOME/.config/krusaderc) configuration file and remove it from this location. Restart &krusader;, &krusader; will now start the first start configuration wizard, follow these guidelines. How to executing jar files (and not enter the jar archive)? Go to the Konfigurator Protocol page and remove application/x-jar from the krarc node, &krusader; should no longer enter the archive. Global file associations are handled by &plasma; and not by &krusader;. To associate the jar extension: Open &plasma; &systemsettings;: systemsettings Applications File Associations Enter jar as filename pattern Add java as application Why do I have trouble with my &FTP; connection? This problem often occurs when you are behind a firewall or proxy. Open System Settings Network Settings Connection Preferences , Enable Passive Mode (PASV) has to be turned on or off, depending on its current setting. Now try your &FTP; session again to see if it works. Also, make sure you have no other &FTP; sessions open (using web browsers, &etc;), they can cause complications. More information can be found in Active &FTP; vs. Passive &FTP;, a Definitive Explanation. I get FTP protocol not supported by &krusader; error when trying to open a remote &FTP; directory, what to do? The reason for this error is that '&FTP; via &HTTP; proxy' feature is not yet supported by &krusader;. This error may be caused by a misconfiguration of the proxy settings in &systemsettings;. Modify the proxy settings to not use &HTTP; proxy and &FTP; will work. How do not add .part suffix when copying files via &FTP;? When uploading files a .part suffix is added to the filename, once the upload is complete the filename is renamed automagically to remove the .part suffix. This works great but sometimes some &FTP; servers do not allow a rename operation. You can solve this by unchecking the checkbox Mark partially uploaded files in &plasma; &systemsettings;. The check box is located at Network Settings Connection Preferences . How can I close a remote connection (⪚ a &FTP; connection)? It is explained in the remote connections chapter. I am experiencing problems with the media:/ protocol, why? The media:/ protocol was removed since &kde; 4. Please use mtp:/ protocol instead. How can I disable the default sounds, ⪚ when I do a delete action? Those are the default &plasma; System sounds, and not related to &krusader;. If you want to disable them globally open your &plasma; &systemsettings; ( systemsettings): Personalization Notifications Manage Notifications , Event Source: "Plasma Workspace" and uncheck sound items you do not like. Where the remote connection manager? Use our Bookmark Manager. Use a remote &URL; and bookmark it. Why does not MIME type magic sometimes work inside archives? When you enter an archive and press F3 to view a file that has no known extension, ⪚ README, INSTALL &etc; And if the viewer opens in hex-mode instead of the usual mode, than you need to configure: KDE System Settings File Associations Application octet-stream , binary viewer needs to be removed. Where is Konfigurator, do i need to install the &plasma; to use it? Konfigurator is &krusader;s configuration module, if you have installed &krusader; then you have also Konfigurator. For some reason some people think it is an other &plasma; application, but it is not, so you do not need to install the full fledged &plasma; to use Konfigurator. When running &krusader;, use Settings Configure &krusader; , and it will start Konfigurator. Please read the manual, &krusader; has many many configuration options, a lot of things can be customized to your needs with Konfigurator. How can I set the look & feel of 2 different users to be the same? Presuming that the current setup is the good configuration, you can copy the configuration to the other user. # cp ~/.config/krusaderrc /home/foo_user/.config And when the other user is Root use: # cp ~/.config/krusaderrc /root/.config General &FAQ; How can I report a wish, a suggestion, or a comment? An open source project's greatest strength is derived from getting user feedback. That is why we love to hear what you have to say. Your "gripes" are our instructions. After about 6 months, programmers can no longer see their own mistakes. It is natural. We want the ideas, qritiques, and feedback because we all want to make &krusader; the best and most useful file manager available anywhere. The most convenient way to contact us is to use the krusader-users mailing list. For information on what remains to be done look at the &krusader;-devel mailing list. We review and discuss every submission. How can I send a patch? It is not easy to write patch guidelines, but here are some. Patches, new code or new features are always welcome! Focus only on one problem at a time, so the developers can easily understand you, and commit your patch it works. Preferably, you can start a discussion with the developers on the krusader-devel mailing lists or on the &krusader; Phabricator page. Unfortunately, we cannot apply a patch if the patch is in conflict with the "general design" of the &krusader; code (read for more instructions in this FAQ). Please send only one change request per mail, so that the discussion is easier to follow. The modifications should be committed step by step, checking each line. Submit your patch files into the &krusader; group on the Phabricator, so that all patch proposals stay organised and do not get lost, tar.gz archives or diffs are usually ok. Do not copy changes into a mail. Most mailers will change the white spaces, so that the diff will not apply or the code snipped does not diff. In the past, patch mails went lost or disorganized in many many mails, so please use the Phabricator, thanks! Use portable solutions, &krusader; should run on: All POSIX (&Linux;/BSD/&UNIX;-like OSes), &Solaris;. All BSD Platforms (FreeBSD/NetBSD/OpenBSD/&MacOS;). &kde; 3.3- &kde; 3.5 and even on GNOME with &kde;libs (for &krusader;-2.x) . GCC 2.95 - GCC 5.4 Architecture changes are made by the team only. A final note: always keep in mind a patch might be rejected. Either it has side effects, which we could not fix or it contradicts with the idea behind the patched module. In this sense, a software project differs from, let's say a wiki: a software project has to have someone to make all the final decisions. Otherwise the software will not work. Furthermore: keep in mind that the examination of patches might take time, as we all have private lives. How can I report a bug? Bugs?!?!? Well, ok.... We have an online Bug Tracking System. Using the online bug-tracker helps us have a clear and orderly way to know how many bugs are open, bug priority and follow-ups. It saves us from browsing through the entire &krusader; forum, searching for yesterdays bug. Please use this system. Before reporting a bug, please read the following: Check if the bug is already posted in the online bug list of the Bug Tracking System. If you cannot find this bug mentioned, please submit it into the bug tracker by clicking the Submit New button in the bug-tracker window. Please submit the following issues: the &krusader; version used, the &Linux; distribution + version used, processor type, and as good a description of the problem as you can manage. Thank you for your co-operation! If possible, try to do the same operation with &konqueror; or another &kde; application. If you encounter the same problem then it is possibly a KIO Slaves or &kf5-full; bug, and not a &krusader; bug. &krusader; uses the &kf5; libraries and the KIO Slave for many operations. In some cases you can encounter problems if your distribution is incorrectly configured, please test it first as explained above. How can i send good debug or crash reports? Usually binary packages that are been used by your distribution do not contain debug information. Since we usually only develop and fix &krusader;-git, compile &krusader;-git in debugmode and install it, than check if the bug still exist. $ cmake Install valgrind, a suite of tools for debugging and profiling &Linux; programs. Run Valgrind/&krusader; $ valgrind {foo_path}/krusader The valgrind tool will write to stdout, what really happens. If you could send these information before the crash, it is almost sure, that we may fix or tell you what to do. What to do if the KDEcrashhandler sends no useful backtrace information? Usually binary packages that are been used by your distribution do not contain debug information. Since we usually only develop and fix &krusader;-git, compile &krusader;-git in debugmode and install it, than check if the bug still exist. $ cmake If the KDEKrashhandler still does not provide useful backtrace information, than sometimes a coredump will give better information. Run Krusader, with disabled crashhandler. $ krusader On a crash you will get a .core file, usually in your home directory. Run gdb, the GNU Project Debugger $ gdb corefile krusader Now type bt to get the backtrace and type q to quit gdb. Often the best debug results will be given when using the valgrind tool. Does &krusader; have a mailing list? Yes, currently we have 5 mailing lists. No spam, no bother, just &krusader;. Feel free to subscribe and unsubscribe. Tip. our mailing lists can be browsed online or read with a newsreader, so that you do not even need to subscribe to follow the action on the mailing lists. &krusader;-devel is the developer mailing list (read-only). If you want to follow the development of &krusader; on the cutting edge, this is the list to follow. &krusader;-users is the &krusader; users mailing list. Here you can ask for help and talk with the &krusader; users and developers. Does &krusader; provide news feeds? Yes, we do. Several news feeds in various formats are available. krusader-devel feed, krusader-users feed. Does &krusader; have an IRC channel? Yes, we do. Feel free to talk to the &krusader; Krew and fellow-users via freenode.org servers. The server is irc.freenode.org, the channel is #krusader. Everyone is welcome. How does the &krusader; forum work? In the spirit of freedom of speech, everything that is &krusader; related can be discussed in our forum. It does not matter if you are a newcomer or an advanced user, everyone is welcome. An open source project's greatest strength is derived from getting user feedback. That is why we love to hear what you have to say. With your feedback we can make &krusader; better and better; otherwise, we are not aware of issues and ideas you may have. But please remember the following, so that we can maintain some order in the chaos. If the Documentation, FAQ and &krusader;-devel (please use the search function ) cannot help you, do not hesitate to post on our forum. The &krusader; Krew or the &krusader; community is always available to help you. Please use the search function of the &kde; Forum, your issue may have been previously discussed (this allows us to minimize the double/triple/... postings). If your issue has already been discussed in the past there is a great chance that you will have an instant solution to your problem. If the issue is currently being discussed, you can join in the discussion. Some questions are asked over and over again, that is why we have created this FAQ. This allows us to spend more time developing &krusader;. Thank you for your co-operation! How can I translate &krusader; into my native language? If you are interested in translating the &krusader; GUI or documentation, please contact the KDE translation team of your language. How can I support &krusader;? You can support &krusader; in many different ways. Please send us feedback, bug reports, patches, donations, translations, ... Why is &konqueror; in "midnight commander style" not an OFM? The two panels and a commandline are available, all the other stuff like OFM features and the OFM interaction with the user is missing from this profile. Why should I use an OFM? An Orthodox File Manager (OFM) is much faster than a one panel filemanager and faster than the command line. If you would like to know how &krusader; feels, there is only one way to discover: install it on your computer and use it for a while. If you prefer to waste time and lose productivity, continue to use one panel filemanagers which are based on &Windows; Explorer. Matej Urbančič has written a blog on why OFM is better than a one panel filemanager. diff --git a/doc/features.docbook b/doc/features.docbook index d91f1cec..17f230e2 100644 --- a/doc/features.docbook +++ b/doc/features.docbook @@ -1,518 +1,518 @@ Features Features The most important features and improvements are highlighted here, it would make this list too long if we were to include all the features and improvements. You will discover even more features in this documentation and in the ChangeLog and NEWS files. <ulink url="http://www.softpanorama.org/OFM/index.shtml">Orthodox File Manager (OFM)</ulink> features Two powerful and easy to use OFM panels. Command Line and optional Terminal emulator below. Strong keyboard orientation and the ability to perform all functions without the mouse. Context-dependent invocation of scripts and programs ⪚ mouse click/&Enter; (execute or open with the associated application), F3 (view) and F4 (edit). User Menu, create your own customized menu. History ⪚ Folder History, Popular &URL;s, &etc; Virtual file systems (VFS) ⪚ for Remote Connections, archives, search results, synchronizer. Powerful internal viewer and editor via kparts supporting almost every file format. Advanced Search module: searching in archives and search content on remote file systems. Several Panel view modes via the Sidebar. &krusader; tools Remote Connections: &FTP;/SAMBA with SFTP/SCP support via KIO Slaves Locate &GUI; frontend Synchronize Directories Mount-Manager Disk Usage Root mode &krusader; features Archive Handling: browsing, unpack, pack, testing. Supported archives: ace, arj, bzip2, deb, gzip, iso, lha, rar, rpm, tar, zip and 7-zip. Checksum Creation and Verification mechanism that supports md5, sha1, sha256, sha224, sha256, sha384, sha512, tiger, whirlpool, cfv, and crc. Calculate occupied space of files and folders, archives and remote filesystems. Directory comparison and filtering. Compare files by content via external diff programs like &kompare;, KDiff3 or xxdiff. Rename files and folders via KRename Completely supports drag'n'drop: drag from &konqueror; straight into a .tar.gz file, from &krusader;'s panel into the trash bin, the desktop or into &konqueror; Preserve date for local targets at copy/move operations. F9 opens a Terminal Window in the current directory. Easy editing of file permissions and ownership and Numeric Permissions. Selection filters used in ⪚ Synchronizer and KruSearcher. Jump-Back MIME type-aware. Tabbed panels and Tabbed Editor/Viewer. Quick search, select and filter File splitter Mouse Gestures Quick Navigation in the Location Toolbar. WhatsThis Help function in favour of tooltips where reasonable. Sync-browsing Customize &krusader; to your own needs Konfigurator &krusader;'s configuration center. Main Toolbar, Actions Toolbar for user actions and bookmarks. Key-bindings Colors Panel profiles Bookmark-Manager Selection Modes Many more features can be configured the way you like. &krusader; Extensions UserActions Color Profiles Key-binding Profiles Download and upload your favourite &krusader; extensions at store.kde.org. New features in the &krusader;-1.80.0 "Final 3rd Stone" release Archive improvements Password support for: arj, ace, rar and zip. Passwords can be stored in Kwallet 7-zip support, by external applications 7z and 7za. Auto-detection support, allowing you to open "hidden archives" (⪚ OpenOffice documents). Packing/unpacking is now in the background. Brief View Many improvements of the UserAction system. Countless usability enhancements, especially in Konfigurator and the heavily reordered menubar. Media button Copy/Move: preserve all attributes (time, owner, group). Dim the colors of the inactive panel. Renaming a filename without extension, single instance mode, start to system tray, check this out in Konfigurator Configurable Atomic extensions, so that extensions like .tar.gz are shown as one part in the Ext Column. Context menu for bookmarks and enable/disable special bookmarks. Restyled MenuBar Terminal emulator enhancements bringing new functions, new usages and new look. Synchronizer enhancements and parallel threads for slow &FTP; servers. Single instance mode, start to system tray Context menu for permanent bookmarks. Full support for ACL permissions, like properties, preserve attributes, synchronizer and other. Searcher-results, Locate-results, Synchronizer-results: support for dragging items and copy to clipboard (&Ctrl;C). Many bug fixes. New features in the &krusader;-1.90.0 "Power Stone" release Last release for &kde; 3.x with a big collection of Useractions. New features in the &krusader;-2.0.0 "Mars Pathfinder" release The first release for the &kde; 4.x series &krusader; works on &Windows;! (But not so useful and powerful yet as under &Linux;) Queue manager! Five methods in the panel! New features in the &krusader;-2.1.0-beta1 "Rusty Clutch" release Fast text/hex viewer for huge filesizes. Locked tabs. Queued packing / unpacking. Better trash integration. The quick search, select and filter bar supports regular expressions for matching files. New features in the &krusader;-2.5.0 "Clear Skies" release Ported to &kf5-full;. The new Quickfilter bar with three different modes to operate: select, search and filter. Reworked Queue Manager (JobMan) and the new Job Toolbar. New features in the &krusader;-2.7.0 "Peace of Mind" release Quick Search in Bookmark menu. Pinned tabs. Tooltip with file information when you hover a file name. Extended options for Tree Panel at the Side Bar. If you are using a development or &git; version, it is possible that the latest new features are not yet in the "The &krusader; Handbook". Please read the Changelog file online or in the &krusader; sources to find out more about the new features. For a brief description, please read the NEWS file online or in the &krusader; sources. User feedback and Bug reports are always welcome! diff --git a/doc/glossary.docbook b/doc/glossary.docbook index 0e400cf9..0e116e69 100644 --- a/doc/glossary.docbook +++ b/doc/glossary.docbook @@ -1,379 +1,379 @@ Glossary This chapter is intended to explain the various words which have been used throughout the &krusader; documentation. If you believe some acronyms or terms are missing, please do not hesitate to contact the &krusader; documentation team. Thanks to wikipedia.org the free encyclopedia that anyone can edit. ACL Access Control List; a concept in computer security used to enforce privilege separation. It is a means of determining the appropriate access rights to a given object depending on certain aspects of the process that is making the request. BSD Berkeley Software Distribution; refers to any of several free &UNIX;-compatible operating systems, derived from &BSD; &UNIX;. CVS Concurrent Versions System; an important component of Source Configuration Management (SCM). By using it, developers can record the history of source files and documents. DEB This is a binary file format that is used by Debian and Debian-based distributions. It is a suffix of a installation file specifically built for these distributions; ⪚ krusader_1.70.1-1_amd64.deb. Simply described it is a special archive containing all the program files and their proposed location on the system. &DBus; &DBus; is an inter-service messaging system. Developed by &RedHat;, it was heavily influenced by &kde; 3 &DCOP;, which it supersedes. &krusader; can use &DBus; to communicate with other applications. On the other hand, you can use &DBus; to control &krusader; from other applications. Just use qdbusviewer to examine the possibilities. &DCOP; Desktop Communication Protocol; the interprocess communication protocol used by &kde; 3 desktop environment. It enables various &kde; 3 applications to communicate with each other. Replaced with &DBus; in &kde; 4. FAQ Frequently Asked Questions; a document where questions that arise many times are answered. If you have a question to the developers of &krusader;, you should always have a look at the FAQ first. &FTP; File Transfer Protocol; it is an Internet protocol that allows you to retrieve files from so-called &FTP; servers. Git Git; a distributed version control system that replaces Subversion. It is used by many software projects including &kde; and &krusader;. GPL GNU General Public License; a software license created by the Free Software Foundation defining the terms for releasing free software. &GUI; Graphical User Interface. ISO An ISO image (.iso) is an informal term for a disk image of an ISO 9660 file system. More loosely, it refers to any optical disk image, even a UDF image. &kde; K Desktop Environment; a project to develop a free graphical desktop environment for &UNIX; compatible systems. Key Binding All features of &krusader; are available through the menubar, but you can also bind (link) a certain key combination to that function. You will find, however, that using the keyboard is remarkably faster than using the menubar or GUI. Keyboard usage is an important tool for Orthodox File Managers. &krusader; comes with several predefined Key Bindings. KPart KPart; KParts is the name of the component framework for the &kde; desktop environment. KParts are analogous to Bonobo components in GNOME, both of which are based on the same concepts as &Microsoft;'s Object Linking and Embedding. ⪚ if you use &krusader;'s viewer to view a PDF file, &okular; will be launched inside &krusader;'s viewer. KIO or kioslave KDE Input/Output; also known as KIO Slaves is part of the &kde; architecture. It provides access to files, web sites and other resources through a single consistent API. mount Mounting; in computer science, is the process of making a file system ready for use by the operating system, typically by reading certain index data structures from storage into memory ahead of time. The term recalls a period in the history of computing when an operator had to mount a magnetic tape or hard disk on a spindle before using it. OFM Orthodox File Manager; also known as "Commanders". Members of this family of file managers use simple yet very powerful interface that is a direct derivative of the Norton Commander (NC) interface. RPM This is the binary file format for distributions based on the RPM Package Manager, a widely used packaging tool for the &Linux; operating system. If you still have to get &krusader; and your system supports RPM packages, you should get &krusader; packages ending in .rpm. SSH, Secure Shell SSH; is a set of standards and an associated network protocol that allows establishing a secure channel between a local and a remote computer. SVN, Subversion Subversion; a version control system that is a compelling replacement for CVS. It is used by many software projects including &kde;. Terminal emulator Terminal emulator; simply a windowed shell; this is known as command line window in some other environments. If you want to use the shell and type the commands, you should know at least a few of the system-level commands for your operating system. POSIX Portable Operating System Interface for uniX; a collective name of a family of related standards specified by the IEEE to define the application programming interface (API) for software compatible with variants of the &UNIX; operating system. &URL; Universal Resource Locator; a universal resource locator is the technical term for what is commonly referred to as a websites address. Examples of &URL;s include https://krusader.org and Remote Connections. VFS Virtual file systems (VFS) is a basic OFM feature, this an abstracted layer over all kinds of archived information (ZIP files, &FTP; servers, TAR archives, NFS filesystems, SAMBA shares, ISO CD/DVD images, RPM catalogs, &etc;), which allows the user to access all the information in these divergent types of file systems transparently - just like entering an ordinary sub-directory! &krusader; supports several Virtual file systems (VFS). &XML; Extensible Markup Language; a very flexible text format derived from SGML (ISO 8879). Originally designed to meet the challenges of large-scale electronic publishing, &XML; is also playing an increasingly important role in the exchange of a wide variety of data on the Web and elsewhere. Zeroconf Zeroconf; or Zero Configuration Networking is a set of techniques that automatically create a usable IP network without configuration or special servers. This allows inexpert users to connect computers, networked printers, and other items together and expect them to work automatically. diff --git a/doc/help.docbook b/doc/help.docbook index a308d3a4..a1f48cb2 100644 --- a/doc/help.docbook +++ b/doc/help.docbook @@ -1,104 +1,104 @@ Help &krusader; Help Here is your opportunity to contribute to the &krusader; project. There are things that need to be done, but cannot be done by us. If you feel you can help, do not hesitate to contact us; every OpenSource can use some help. Please also check the contribute donate &URL; page on our website for more recent information. Thanks! To get contributor account and commit changes to &krusader; by yourselves you should follow this tutorial. Some useful advices on using &kde; Git can be found here. It is a good practice to use review board for the non-trivial patches. Documentation Help is needed (we are looking for documentation writers) for keeping the &krusader; documentation up to date. The draft explanation of new features is done by the developers on the krusader-devel mailing list, but we need editors to add this information in the handbook. By playing with the new features these editors can give valuable information to the developers for making the new features even better! &krusader; Extensions Please upload your favourite extensions at store.kde.org, so that they become available for the &krusader; community. Maybe they will be shipped by default with &krusader; at the next release :-)). Spread the Word &krusader; If you like &krusader;, let people know! Write articles and reviews for internet or print publications. Encourage your friends to give &krusader; a try. Participate in forums to help others in the community. GUI Translations &krusader; is available in many languages, but sometimes translations do need an update or translations to new languages are always welcome. If you are interested in translating the &krusader; GUI, please contact the &kde; translation team of your language. Documentation Translations If you are interested in translating this &krusader; documentation, please contact the &kde; translation team of your language. Donations Actually, we had not considered donations, until users asked us for a way to Donate to the project. &krusader; is, and always will be free, in all the terms of the GNU Public License. Packagers We are always in need of packagers for different distro rpm's, deb's, etc. All help is welcome. FAQ If you have a question that is missing in the FAQ, we would like to hear it. Rate &krusader; On some websites like freshmeat.net or linux-apps.com you can give a rate, if you like &krusader; go to one of these sites and rate &krusader;, thanks. Others Feedback, Bug reports, patches, &etc; are always welcome! An open source project's greatest strength is derived from getting user feedback. diff --git a/doc/index.docbook b/doc/index.docbook index 7b665c62..2d262543 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,369 +1,370 @@ ]> The &krusader; Handbook Davide Gianforte Developer
davide@gengisdave.org
Toni Asensi Esteve Developer
toni.asensi@kdemail.net
Alexander Bikadorov Developer
alex.bikadorov@kdemail.net
Martin Kostolný Developer
clearmartin@gmail.com
Nikita Melnichenko Developer
nikita+kde@melnichenko.name
Yuri Chornoivan Documentation
yurchor@ukr.net
Shie Erlich Author and initial Handbook author (retired)
erlich@users.sourceforge.net
Rafi Yanai Author and initial Handbook author (retired)
yanai@users.sourceforge.net
Csaba Karai Developer (retired)
ckarai@users.sourceforge.net
Heiner Eichmann Developer (retired)
h.eichmann@gmx.de
Jonas Bähr Developer (retired)
jonas.baehr@web.de
Václav Jůza Developer (retired)
vaclavjuza@seznam.cz
Simon Persson Developer (retired)
simon.persson@mykolab.com
Jan Lepper Developer (retired)
jan_lepper@gmx.de
Andrey Matveyakin Developer (retired)
a.matveyakin@gmail.com
Dirk Eschler Webmaster (retired)
deschler@users.sourceforge.net
Frank Schoolmeesters Documentation and Marketing coordinator (retired)
frank_schoolmeesters@yahoo.com
Richard Holt Documentation and Proofing (retired)
richard.holt@gmail.com
Matej Urbančič Marketing and Product Research (retired)
matej.urban@gmail.com
2000 2001 2002 2003 Rafi Yanai Shie Erlich 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 + 2019 Krusader Krew Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.1 or any later version published by the Free Software Foundation; with no Invariant Sections, with no Front-Cover Texts, and with no Back-Cover Texts. A copy of the license is included in the &krusader; sources. 2018-08-12 2.7.1 &krusader; is an advanced, twin-panel (commander-style) file-manager for Plasma and other desktops in the *nix world KDE krusader commander filemanager twinpanel linux file manager dual pane linux-commander kde-commander
&help; &editors-note; &introduction; &features; &user-interface; &basic-functions; &advanced-functions; &menu-commands; &keyboard-commands; &mouse-commands; &krusader-tools; &konfigurator; &faq; &credits-and-license; &configuration-files; &useraction-xml; &release-overview; &glossary; &documentation.index;
diff --git a/doc/introduction.docbook b/doc/introduction.docbook index da10b1e1..7df076e5 100644 --- a/doc/introduction.docbook +++ b/doc/introduction.docbook @@ -1,162 +1,162 @@ Introduction Introduction Package description &krusader; is a simple, easy, powerful, twin-panel file manager (commander-style) for &plasma; and other *nix desktops, similar to Midnight Commander or Total Commander. It provides all the file management features you could possibly want. Krusader also includes extensive archive handling, mounted file system support, &FTP;, advanced search module, viewer/editor, directory synchronization, file content comparisons, powerful batch renaming and much, much more. &krusader; supports archive formats: ace, arj, bzip2, deb, gzip, iso, lha, rar, rpm, tar, zip and 7-zip and handles other KIO Slaves such as smb:// or fish://. &krusader; is almost completely customizable and therefore very user-friendly. Try &krusader;.
&krusader; screenshot &krusader; screen shot
More &krusader; Screenshots can be viewed on our website.
Welcome to &krusader;! Our aim is to give you a simple, intuitive tool that easily handles the most important tasks you perform on your computer each and every day: manage and process your files the way you want. Whether you are a &Linux; expert or a novice, &krusader; makes routine file management tasks quick and accurate, allowing you to focus on the big picture, not command line syntax. This is especially helpful if you are new to xxNIX operating systems. To do just that, &krusader; uses a good-looking &GUI; and supports drag and drop as well as MIME types, making it easy to use. &krusader; has many useful features. It is fast, and handles archives transparently. In addition, &krusader; streamlines many complex file management tasks, reducing fatigue, errors, and distraction. Basically, &krusader; provides all the helpful features you have come to expect from other quality file management tools like Midnight Commander and Total Commander. &krusader; is designed to integrate seamlessly with the &plasma;, the desktop of choice for many programmers, network engineers, and other power users of &Linux;. With &krusader; even novice users can quickly access the power, convenience, and feature rich capabilities of the &plasma;. &krusader; is a free open-source project, and it is released under the &GNU; General Public License (GPL), although the developers still retain the copyright for the project and its name, and manage the project's continuing evolution. The &krusader; Project mission is to create an "all-in-one" file manager, which will do all the ordinary things you would expect of it, and more. Since the principle behind &krusader; is based on the Orthodox File Manager (OFM), dual-pane filemanagers (Midnight Commander, Norton Commander, &etc;), it is certain that &krusader; is able to handle all the standard Copy, Move, Delete, &etc; file operations. From the start, virtual file systems became the main focus. Virtual file systems (VFS) are an abstracted layer over all kinds of archived information (ZIP files, &FTP; servers, TAR archives, NFS filesystems, SAMBA shares, ISO CD/DVD images, RPM catalogs, &etc;), which allow the user to access all the information in these divergent types of file systems transparently - just like entering an ordinary sub-directory. Files can then be copied or moved around (except ISO, rpm, tar.gz) between these data sources/containers effortlessly. &krusader; currently supports VFSs for all popular and some specialized types of archives (ace, arj, bzip2, gzip, iso, lha, rar, tar, zip and 7-zip), configuration packages (rpm, deb), and traditional remote file system types (&FTP;, NFS, Samba, FISH, SFTP). &krusader; also includes a built-in Mount-Manager MountMan, which enables you to mount or unmount file systems of all types with a click of a mouse. The user is able to obtain usage and other property information easily, in a consistent and understandable format in seconds. Ease and consistency is the goal. A Bookmark-Manager for local files/remote &URL;s and an Advanced Search module is also included to speed the locating of needed files. &krusader; also allows the user to configure Toolbars in order to provide a wide variety of additional functionality, such as File Comparison (several user selectable methods available), Multiple File Rename, and standard Terminal windows. Advanced users benefit greatly from being able to carry out clerical tasks effortlessly, without distraction from the real task at hand. Convenient shortcut keys enable the user to navigate directory trees with ease, create multiple tabbed directory views simultaneously, and access fully functional history lists. Due to efficient programming and low system overhead, &krusader; is perhaps the most responsive all around system management tool used to access the full potential of a computer using &plasma;, or any other standards compliant &X-Window; window manager. Although designed specifically for - and tightly integrated with - &plasma;, &krusader; works equally well with GNOME, MATE, Enlightenment, &windowmaker;, IceWM, XFCE, &etc;, as long as the &kf5; libraries are installed. New extensibility is provided by the flexible UserActions, a &krusader; innovation that allows unlimited additional custom functionality to be added to suit the specific needs of users. We are planning to develop more modules in the near future, thereby enhancing &krusader;. Information on what is proposed, approved, or remains to be completed can be found in the &krusader; Phabricator page. No other program we know of, offers so many useful, easily accessible features in such an intuitive and simple format. Best of all, we listen, and implement good suggestions that are consonant with the &krusader; Project's goal: making &krusader; the very best File Manager available. We hope you will enjoy it - we do!
diff --git a/doc/keyboard-commands.docbook b/doc/keyboard-commands.docbook index 87ec952f..889592cc 100644 --- a/doc/keyboard-commands.docbook +++ b/doc/keyboard-commands.docbook @@ -1,2039 +1,2039 @@ Keyboard Commands Commands Keyboard Keyboard Usage In this chapter you will learn how to use the keyboard effectively, because most operations can be done more quickly by keyboard than with the mouse. It will take some time to learn the Key-Bindings, but mastering them saves you a lot of time. Please note that &krusader; can use Key-binding Profiles to swap easily to other Key-binding setups, ⪚ of other commanders, or you can create your own key-bindings. After starting &krusader;, the keyboard action can begin. Use &Ctrl;L to jump to the Location Toolbar where you can type the desired directory. After &Ctrl;Down arrow you are in the Terminal emulator and you can type any command you desire. Use &Ctrl;Up arrow to jump back to the active panel. If you want to jump to a file or directory that starts with f, then simply press f on the keyboard to use the search bar. Use the &Enter; key to execute the file or to open a directory that has the focus. Be sure to remember the Function Keys - F3 to View, F4 to Edit, F5 to Copy, &Alt;&Shift;U to Unpack, &etc; You can also perform several operations with the Folder tabs, including several selection operations. And at the end you can close &krusader; with F10. Key-Bindings Key-Bindings Commands Keyboard Most of the key-bindings (shortcuts) are configurable in the Settings Configure Shortcuts menu, if you like to use other key-bindings then the default ones. You can even configure more actions to a Key-Binding (for the actions that do not have a key-binding set by default). Please note that some key-bindings are not &krusader; key-bindings (⪚ &plasma; key-bindings). The key-bindings that &krusader; uses by default are listed below. Function (FN) Keys These are the Key-Bindings of the FN Keys Bar. These Key-Bindings are configurable since version 1.51. F1 Help F2 Rename files. F3 View files. F4 Edit files. F5 Copy files. F6 Move files F7 Create a new directory. F8 Delete (or move to Trash) files. F9 Terminal. F10 Quit &krusader; &Shift; keys &Shift; F1 What's this? &Shift; F2 Multi-rename (Krename). &Shift; F3 Enter an &URL; to view. &Shift; F4 Edit new file. &Shift; F5 Copy by queue. &Shift; F6 Move by queue. &Shift; F10 View all files. &Shift; F12 Custom view files. &Shift; Left Arrow Change to left folder tab. &Shift; Right Arrow Change to right folder tab. &Alt; keys &Alt; + Select All. &Alt; - Unselect All. &Alt; * Invert Selection. &Alt; . Show/Hide hidden (dot) files. &Alt; / MountMan. &Alt; ` User Menu &Alt; Home Home &Alt; &Enter; Properties. &Alt; Left Arrow Left bookmarks. &Alt; Right Arrow Right bookmarks. &Alt; Down Arrow Sidebar. &Alt; F1..F12 Standard (&plasma;) key-bindings. &Alt;+&Shift; keys &Alt;&Shift; B Brief View &Alt;&Shift; C Compare Directories &Alt;&Shift; D Detailed View &Alt;&Shift; E Test Archive. &Alt;&Shift; K Start Root mode &krusader;. &Alt;&Shift; L Panel profiles. &Alt;&Shift; O Sync panels aka "Equals Button (=)". &Alt;&Shift; P Pack files. &Alt;&Shift; Q Queue Manager &Alt;&Shift; S Disk Usage. &Alt;&Shift; U Unpack files. &Ctrl; keys &Ctrl; B Add bookmark for the current item. &Ctrl; D Open Bookmarksin the active panel. &Ctrl; E Edit file as root (Default Useraction). &Ctrl; F Open Quicksearch bar. &Ctrl; H Open History list in the active panel. &Ctrl; I Open QuickFilter bar. &Ctrl; J A Safari-like Jump-Back. &Ctrl; L Go to the Location Toolbar (origin) as in Firefox and &konqueror;. &Ctrl; M Open media list. &Ctrl; N New Network Connection dialog. &Ctrl; O Select directory dialog to open this directory in the panel. &Ctrl; P Split file. &Ctrl; Q Quit &krusader;. &Ctrl; R Reload (Refresh) panel. &Ctrl; S Search. &Ctrl; U Swap panels (do not swap all folder tabs). &Ctrl; W Close Current tab. &Ctrl; Y Synchronize Directories. &Ctrl; Z Popular URLs. &Ctrl; + Select group. &Ctrl; - Unselect group. &Ctrl; / Open command line history list. &Ctrl; Down arrow Go from the active panel to the command line/terminal emulator. &Ctrl; Up arrow Go from the command line/terminal emulator to the active panel. &Ctrl; Up arrow Go from the active panel to the Location Toolbar. &Ctrl; Home Jump to the Home directory. &Ctrl; Left or Right arrow Focus a file or directory on the left panel, press &Ctrl; Left arrow and the right panel changes: on a file: the right panel gets the same path as the left panel. on a directory: refreshes the right panel with the contents of the directory. For the right panel: press &Ctrl; Right arrow and the left panel will change. &Ctrl;&Backspace; Jump to the Root directory. &Ctrl; PageUp Up one directory. &Ctrl; = Go to the directory from other panel. &Ctrl; Return When media menu is open (un)mounts the highlighted device. &Ctrl;+&Shift; keys &Ctrl;&Shift; D Disconnect remote connection. &Ctrl;&Shift; F Activate search bar to find entries in the active tab. &Ctrl;&Shift; J Set jump back point. &Ctrl;&Shift; L Locate &GUI; frontend. &Ctrl;&Shift; O Move current tab to other side of the window. &Ctrl;&Shift; S Open Quickselect bar. &Ctrl;&Shift; U Swap Sides (also swap all folder tabs). &Ctrl;&Shift; Left arrow Open left media list. &Ctrl;&Shift; Right arrow Open right media list.. &Ctrl;&Shift; Up arrow Open terminal emulator, independently, whether the command line is shown or not. &Ctrl;&Shift; Down arrow Close terminal emulator, independently, whether the command line is shown or not. &Ctrl;&Shift; PageUp Move current tab to the left. &Ctrl;&Shift; PageDown Move current tab to the right. &Ctrl;+&Alt; keys &Ctrl;&Alt; M Mount (Default Useraction). &Ctrl;&Alt; N New tab. &Ctrl;&Alt; R Toggle the List Panel between horizontal and vertical mode. &Ctrl;&Alt; S Create a new symlink. &Ctrl;&Alt; T Show/hide the embedded terminal. &Ctrl;&Alt; Left Arrow Left History list. &Ctrl;&Alt; Right Arrow Right History list. &Ctrl;&Alt; = Equal Panel Size (Default Useraction). &Ctrl;&Alt;&Shift; N Duplicate a tab. &Ctrl;&Alt;&Enter; Open current folder in a new tab. General &Ctrl; keys &Ctrl; A Select all. &Ctrl; PageDown Move to the lower part in the current directory. &Ctrl; F1..F12 Standard (&plasma;) key-bindings. &Ctrl;&Shift; F1..F12 Standard (&plasma;) key-bindings. Other keys foo Quick search, quick select or quick filter. See this configuration page on how to configure the default mode. Switch between the panels &Enter; On a file: open/execute that file On an archive file: browse the archive as if it was a directory. &Esc; Will make the menu bar lose the focus if it has it. Delete Delete (or move to Trash). &Shift; Delete Delete permanently. Space On a file: toggle the selection of the file down one position without affecting the selection of other files/directories. &Backspace; One directory up. Insert Does the same as the Space key and goes down one position to toggle the next file. Menu Right-click menu. Home Cursor jumps to the top of the list. End Cursor jumps to the last file in the list. Command Line Command Line keybindings. Up arrow and Down arrow Scroll through previously typed commands. &Ctrl; / Open the command line history list. &Ctrl; Up arrow Jump from the command line to the active panel. &Ctrl; Down arrow Jump from the active panel to the command line. &Ctrl;&Enter; Insert current file / directory name without path to current command line position. &Ctrl;&Shift;&Enter; Insert current file / directory with full path name to current command line position. Terminal emulator Terminal emulator keybindings. &Ctrl; F Toggle between normal and full screen Terminal emulator. &Ctrl; V Insert from clipboard. &Ctrl; Up arrow Jump from the Terminal Emulator to the active panel if the Command line is hidden. &Ctrl; Down arrow Jump from the active panel to the Terminal Emulator if the Command line is hidden. &Ctrl;&Shift; Up/Down arrow Always focus/unfocus the Terminal Emulator independently whether the Command Line is shown or not. &Shift; Insert Insert from clipboard. Synchronizer Synchronizer keybindings. &Ctrl; W Reverse direction &Alt; Down arrow Exclude &Alt; Up arrow Restore original task &Alt; Left arrow Copy to left &Alt; Right arrow Copy to right &Alt; Delete Mark for delete Default Useractions Default UserActions keybindings provided by &krusader;. &Ctrl; E Edit a file as root. &Ctrl;&Alt; C Copy current item to clipboard. &Ctrl;&Alt; M Mount a new file system. &Ctrl;&Alt; = Equal panel-size. Meta A Enqueue in &amarok;. Meta 1 Sort by Name. Meta 2 Sort by extension. Meta 3 Sort the active panel by size. Meta 4 Sort by modified. Meta F5 Backup current file (Default Useraction). Other Key-bindings Selecting files KrViewer Disk Usage Locate &GUI; frontend &systemsettings; -> Common Appearance and Behavior -> Shortcuts and Gestures UserActions configurable key-bindings diff --git a/doc/konfigurator.docbook b/doc/konfigurator.docbook index 2d86d0af..a38fbbfc 100644 --- a/doc/konfigurator.docbook +++ b/doc/konfigurator.docbook @@ -1,2063 +1,2063 @@ Konfigurator: &krusader;'s Configuration Center Configure Krusader Konfigurator is &krusader;s configuration center. When running &krusader;, use Settings Configure &krusader; , and it will start Konfigurator. There you can modify the way &krusader; works and customize it to your own needs. At any time, pressing the Apply button applies the changes, pressing Close closes Konfigurator, and pressing Defaults re-applies &krusader;'s "factory" settings. Konfigurator is divided into pages, each containing items related to that page. For some changes to the &GUI;, you have to close and restart &krusader;. Since &krusader;-1.80.0 the Key-Bindings and Main Toolbar-Actions Toolbar have received their own configuration windows and so they are not a part anymore of Konfigurator, you can configure them now via the Settings menu. Startup
Startup configuration Startup configuration
The startup page determines the way that &krusader; looks (and works) when it is launched. It is divided into two main parts: General Startup Profile: Starts the selected Panel profile at startup. <Last session> is a special Panel Profile, it is saved automatically when closing &krusader;. Show splashscreen: Displays a splash screen when starting &krusader;. Single Instance Mode: Allows only one &krusader; instance to run. Fallback Icon Theme: Allows you to configure a fallback icon theme for &krusader;. Whenever icon is not found in system icon theme, this theme will be used as a fallback. If the fallback theme does not contain the icon, Breeze or Oxygen will be used if any of these are present. User Interface Determines which parts of the user interface are visible after start-up. Save last position, size and panel settings: When launched, &krusader; resizes itself to the size it was when last shut-down. &krusader; also appears in the same location on the screen, having panels sorted and aligned as they were. If this option is unchecked, you can use the menu Window Save Position option to manually set &krusader; size and position at start-up. Update default panel settings: If checked, the default panel settings will be updated after start-up. Start to tray: if checked, &krusader; starts to tray (if minimize to tray is set), without showing the main window. Save component settings on exit: If checked, restores the user interface components to their condition when last shut-down. Show function keys: If checked, the FN Keys Bar is visible after start-up. Show statusbar: If checked, the Statusbar is visible after start-up. Available only if the Save component settings on exit item is unchecked. Show command line: If checked, the Command Line is visible after start-up. Available only if the Save component settings on exit item is unchecked. Show embedded terminal: If checked, the Terminal emulator is visible after start-up. Available only if the Save component settings on exit item is unchecked.
Panel Here you can determine the look and feel of panel - which means finetuning the application to your needs. The page is divided into six tabs: General, View, Buttons, Selection Mode, Media Menu, and Layout: General <guilabel>Navigator bar</guilabel> Edit Mode by default: If checked, show editable path in Navigator bar by default. Show full path by default: If checked, always show full path in Navigator bar by default. <guilabel>Operation</guilabel> Autoselect Directories: When you select a group of files (either by using Select Group or by using Select All), &krusader; checks this option. If checked, the directories matching the select criteria are also selected. Otherwise, only files are selected. Rename selects extension: When you rename a file, the whole text is selected. If you want total commanderlike renaming of just the name, without extension, deselect this item. Unselect files before copy/move: If checked, &krusader; remove the selection marks from files or folders before copying or moving them to the new location. Filter dialog remembers settings: If checked, the filter dialog will be opened with the last filter settings that where applied to the panel. <guilabel>Tabs</guilabel> Use fullpath tab names: If checked, display the full path in the Folder tabs; otherwise only the last part of the path is displayed. Show new/close tab buttons: If checked, &krusader; displays new and close buttons on tabs. Tab Bar position: Lets you choose Tab bar position (Bottom or Top). Show Tab Bar on single tab: If checked, &krusader; displays the Tab Bar even if there is only one tab on the panel. <guilabel>Search bar</guilabel> Start by typing: allows to select if quick search feature is used. When checked, you can open search bar and start searching by typing something while viewing folder contents in the active panel. Case sensitive: When you use the search or filter feature: If checked (the &UNIX; default), all files beginning with capital letters appear before files beginning with lower case letters; otherwise, all files beginning with a specified letter (capital or otherwise) will be displayed together. Up/Down cancels search: Pressing Arrow Up or Arrow Down cancels search bar. Directory navigation with Right Arrow: If checked, pressing the Right arrow key enters directory if no search text editing intention is captured. Position: Lets you choose search and filter bar position (Bottom or Top). Default mode: Lets you choose the search bar default mode between Search, Select and Filter. You can change the mode later using the search bar itself. <guilabel>Bookmark Search</guilabel> Always show search bar: If checked, make bookmark search bar always visible. Search in special items: If checked, bookmark search is also applied to special items in bookmark menu like Trash, Popular URLs, Jump Back, &etc; <guilabel>Status/Totalsbar</guilabel> Show size in bytes too: If checked, &krusader; displays size in bytes on statusbar or totalsbar. Show space information: If checked, &krusader; displays free/total disk space in the Status-/Totalsbar. View <guilabel>General</guilabel> View font: Allows you to change the font used inside the file lists. Tooltip delay (msec): Allows you to configure the delay in milliseconds between the moment you have stopped mouse pointer above an item in a file list and the moment when the tooltip will be shown. Use human readable file size: If checked, the file size appears in kB, Mb &etc;, and not in bytes (default). Numeric permissions: Permission column (Panel View tab) shows octal numbers '0755' instead of 'rwxr-xr-x'. Show hidden files: If checked, &krusader; displays the "dot-files" which are otherwise hidden. Load the user defined folder icons: If checked, &krusader; loads the user defined folder icons. Always show current item: If checked, &krusader; shows current item border decoration in inactive panel. Sort method Krusader (default): the comparison used so far. Uses comparison using locale rules (even context rules). Alphabetical: strings are compared character by character (no context rules from locale applied). Alphabet characters are compared using locale rules, special characters are compared by the character code. Alphabetical and numbers: the same as above, but if the names contains numbers, the numbers are compared numerically instead of alphabetically. Character code: comparison by character code (quick). Character code and numbers: the same as above, but with numerical comparison of numbers. Case sensitive sorting: If checked (the &UNIX; default), all files beginning with capital letters appear before files beginning with non-capital letters; otherwise, all files beginning with a specified letter (capital or otherwise) appear together. Always sort dirs by name: Sorts directories by name, regardless of the sort column. Show directories first: If checked directories have precedence in folder lists. Locale aware sorting: Sorts files and directories according to the current locale settings. <guilabel>View modes</guilabel> Default view mode: Allows you to change the default view mode. You can choose Detailed View or Brief View. On tabs of both view modes you can define the following options. Default icon size: Allows you to change the size of the icons in the file lists. The available sizes are 16x16, 22x22, 32x32 and 48x48 pixels. Use icons in the filenames: If checked, show the icons in file names and folders. Show previews by default: If checked, show the previews of file contents. To configure the columns use the right click menu in the panel when viewing files. The left and right panel use their own columns independently. The following columns are available: Name: Shows the filename without the part after the last dot, this latter part is displayed in the Ext column. When the Ext column is made hidden, the complete filename is shown in the Name column like ⪚ &konqueror; does. Ext: Shows the last part of the filename (the part after the last dot) in the Ext column, and not as a complete filename in the Name column like ⪚ &konqueror; does. Type: Shows the MIME type field. Size: Shows the size field. Modified: Shows the modified date and time field. Perms: Shows the full permissions ⪚ "rwxr-xr-x" or as octal numbers '0755' instead with enable/disable Numeric Permissions in Panel View Tab. rwx: Shows only the rights of the current user ⪚ "-rw". Owner: Shows the owner field. Group: Shows the group field. Uncheck the columns not in use. This allows more space for columns in use. Buttons Toolbar buttons have icons: If checked, &krusader; displays icons on toolbar buttons. Show Media Button: If checked, &krusader; displays media button. Show Back Button: If checked, &krusader; displays back button. Show Forward Button: If checked, &krusader; displays forward button. Show History Button: If checked, &krusader; displays history button. Show Bookmarks Button: If checked, &krusader; displays bookmarks button. Show Panel Toolbar: If checked, &krusader; displays the Panel Toolbar. You can make the buttons on the Panel Toolbar visible or hidden: <guilabel>Visible Panel Toolbar buttons</guilabel> Equal button (=) Up button (..) Home button (~) Root button (/) Toggle-button for sync-browsing: If checked, shows the Sync-browsing button. Selection Mode Here you can configure the selection modes. <guilabel>General</guilabel> &krusader; Mode: The way &krusader; has worked from day one. Both mouse keys allow selecting files. To select more than one file, hold the &Ctrl; key and click the &LMB;. Right-click menu appears with a short click on the &RMB;. Konqueror Mode: pressing the &LMB; selects files -- you can click and select multiple files. Right-click menu appears with a short click on the &RMB;. Total commander Mode: Pressing the &RMB; selects multiple files and the right-click menu appears with pressing and holding the &RMB;. The Left Mouse Button does not select, but sets the current file without affecting the current selection. Ergonomic Mode: The &LMB; does not select, but sets the current file without affecting the current selection. The &RMB; invokes the context-menu. You can select with &Ctrl; key and the left button. Custom Selection Mode: Create your own selection style! <guilabel>Details</guilabel> Double-click selects (classic): A single click on a file will select and focus, a double click opens the file or steps into the directory. Obey global selection policy: Pressing the &LMB; selects files -- Use global setting: &plasma; System Settings -> Input Devices -> Mouse. Custom Selection Mode items: Based on &plasma;'s selection mode Left mouse button selects Left mouse button preserves selection &Shift;/&Ctrl;-Left mouse button selects Right mouse button selects Right mouse button preserves selection &Shift;/&Ctrl;-Right mouse button selects Spacebar moves down Insert moves down Right-clicking pops context menu immediately Media Menu Using this tab you can select the Media button menu contents: Show Mount Path: show the mount path of partition if checked. Show File System Type: show the file system type of partition if checked. Show Size: allows selection of the size representation in media menu. Can be Always (default, always show the size of partition), When Device has no Label (show the size for partitions with no label only) or Never (never show the size in media menu). Layout You can choose the layout and frame settings: Layout: allows to define layout. The default options are Default, Compact, and Classic. Frame Color: defines frame color. Can be Defined by Layout, None, or Statusbar. Frame Shape: defines frame color. Can be Defined by Layout, None, or Statusbar. Frame Shadow: defines frame color. Can be Defined by Layout, None, or Statusbar. Colors This page configures the colors of the List Panel and the Synchronizer. The KDE default colors is the default color configuration. General Use the default KDE colors: this is the default, it uses &plasma; color defined in the &systemsettings;. Use Alternate Background color: If checked, the Background color and the Alternate background color alternates line by line. When you do not use the &plasma; default colors, you can configure the alternate colors in the Colors box. Show current item even if not focused: If checked, shows the last cursor position in the non active list panel. This option is only available when you do not use the &plasma; default colors. Dim the colors of the inactive panel: If checked, the colors of the inactive panel are dimmed. These settings can be configured in the Inactive tab of the Colors box. This option is only available when you do not use the &plasma; default colors. Colors Configure the colors the way you like, you can see the result in the Preview section. The following items of the active and inactive panel can be configured: Foreground Directory foreground Executable foreground Symbolic link foreground Invalid symlink foreground Background Alternate background Selected foreground Selected background Alternate selected background Current foreground Selected current foreground Current background The following items of the Synchronizer can be configured: Equals foreground Equals background Differing foreground Differing background Copy to left foreground Copy to left background Copy to right foreground Copy to left background Delete foreground Delete background Preview Here you see a preview of the configured colors. Color schemes With the Import Color Scheme and Export Color Scheme buttons you can load and save a Color Scheme profile. This allows &krusader; to use Total Commander, Midnight Commander, foo-commander or your own custom color profile. The Color Schemes are stored in /usr/share/krusader, the foo.color is a binary file that holds the color scheme. Midnight Commander and Total Commander Color scheme files are provided. Please upload your favorite Color schemes so that they become available for the &krusader; community. Thanks! General Here you configure the basic operations. The page is divided into three tabs: General, Viewer/Editor, and Atomic extensions. General The following options determine basic aspects of operation: Warn on exit: if checked, &krusader; warns you every time when exit action is triggered. Minimize to tray: If checked, the &krusader; icon will appear in the system tray. When you minimize &krusader;, it will not appear in the taskbar. It also will not exit if you close the main window. Instead you can quit &krusader; by selecting Quit from the File menu or the context menu of the tray icon. Use MIME type magic: MIME type magic is a mechanism which allows &krusader; to inspect the files in the panels and determine their type even if the file has no identifying extension. For example, if you take an image file - image1.jpg - and rename it to image1, &krusader; will still know it is an image file and work with it accordingly. However, this mode of operation is slower (while refreshing the panel, or changing directories), so try &krusader; with and without MIME type magic and decide what is best for you. Temp Directory: this option selects the base directory for &krusader;'s temporary files. The actual files will be created in separate directories under the 'Temp directory', so that each user running &krusader; will have their own temporary subdirectories under the chosen directory. You must choose a temporary directory to which all users running &krusader; have full permissions! Delete mode (Delete files/Move to trash): when &krusader; deletes files it can either really delete them or move them to the trash folder, thereby making them available via &plasma;'s trash. External Terminal: choose which terminal emulator will be used when &krusader; opens a console window (via F9, or Tools Start Terminal menu option). Embedded Terminal sends Chdir on panel change: if unchecked, there will be no cd command sent to the Terminal Emulator if the panel folder changes. Toggle Fullscreen Embedded Terminal: if checked, &krusader; shows fullscreen terminal in Midnight Commander style (Terminal is shown instead of &krusader; window). Viewer/Editor Here you can change the viewer and editor settings: Internal editor and viewer opens each file in separate window: check this item if you want each file to be opened in separate window. Default viewer mode: Generic mode: use the systems default viewer. Text mode: view the file in text-only mode. Hex mode: view the file in hex-only mode. Lister mode: Fast text/hexadecimal viewer for huge filesizes. &krusader; lister never loads the whole file into memory, just a little part of it, and uses caching for faster scrolling. Remote files are downloaded, but during the download, viewing the downloaded part is possible. If a file constantly changes, then it can also be viewed, lister will always see its current state. Use lister if the text file is bigger than: 10 MB (default value): checks whether a text file is bigger than 10 MB (configurable), and if yes, then &krusader; Lister is used instead of &plasma;'s default viewer. Use Okteta as Hex viewer: if checked (default option), use &plasma; hex editor Okteta to view raw data. To use this option you should install Okteta package first. Editor: choose what editor will be used when editing a file (via F4). Default the internal viewer is used, this is the &plasma;'s default viewer. Atomic extensions Atomic extensions: predefined atomic extensions like .tar.gz are shown as one part in the Ext Column of the List Panel. Advanced This page handles more advanced issues, so you should double-check your actions here: modifying settings here makes &krusader; a more powerful and dangerous tool. The page is divided into three parts: General Automount filesystems: this makes &krusader; try to mount a file system before entering it. For example, if you click on /mnt/cdrom (and /mnt/cdrom appears in /etc/fstab as a mount point), &krusader; will check if it is mounted. If not, it will try and mount it for you and then enter, so that you would see the contents of your cdrom. Note, however, that &krusader; will NOT unmount when exiting /mnt/cdrom. MountMan will not (un)mount the following mount points: If you have file systems that you do not want to accidentally unmount (or mount) then enter a list of mount points separated by commas (⪚ /, /boot, /tmp) and MountMan will not try to (un)mount them. Confirmations By checking the options in this section, &krusader; will ask for confirmation before doing a specific action; otherwise, the following actions will be done without warning: deleting non empty directories deleting file(s) copying file(s) moving file(s) Fine-Tuning Icon Cache Size: &krusader; uses an icon cache, so it will not have to reload icons that have been used before. Of course, as the cache grows bigger, it can store more icons and further speed operations, but the memory footprint of &krusader; will become bigger. Arguments of updatedb: sets additional arguments for updatedb, please read the manpages for more information. Archives This page configures some aspects of archive handling in &krusader;. krarc ioslave Enable Write Support: this option allows you to enable writing files into an archive. The down side is that if a power failure occurs during the process, the files that were moved might already be deleted, but not yet packed into the archive. Moving archives into themselves will delete them. Archive handling Browse Archives As Directories: if checked, &krusader; will handle the archives transparently and let you open them as folders; otherwise, &krusader; will attempt to invoke an application which opens archives of that type. Fine-Tuning Test archives after packing: this option automatically runs a test on a newly packed archive. It is safer, but takes longer. Test archives before unpacking: this option automatically runs a test before unpacking. Some corrupted archives might cause a crash; therefore, it is better to test archives before unpacking. Dependencies page This page configures the full path of the external applications. It is even possible to configure the full path of &krusader;! General tab Here you can configure the full path of the following external applications: application configurable full path kdesu /usr/bin/kdesu kget /usr/bin/kget mailer /usr/bin/kmail diff utility /usr/bin/kompare krename /usr/bin/krename krusader /usr/bin/krusader locate /usr/bin/locate mount /bin/mount umount /bin/umount updatedb /usr/bin/updatedb By default &kompare; is used as external diff utility but you can also use your favorite diff utility ⪚ xxdiff or KDiff3, just fill in the full path and enjoy. Packers tab In the Packers tab you will see a list of archive formats. Some are filled and some are empty. The ones that are available (filled) are supported by &krusader;. For them, &krusader; will handle the archives transparently and let you open them as folders (provided you have checked the Browse Archives As Directories item in the Archives section); otherwise, &krusader; will attempt to invoke an application which opens archives of that type. If a certain archive item is empty, it means that &krusader; could not find the appropriate executables in the configured path. The next archives are supported: ace, arj, bzip2, deb, gzip, iso, lha, lzma, rar, rpm, tar, xz, zip and 7z. packer configurable full path 7z usr/bin/7z arj usr/bin/arj bzip2 usr/bin/bzip2 cpio /bin/cpio dpkg /bin/dpkg gzip /usr/bin/gzip lha /usr/bin/lha lzma /usr/bin/lzma rar /usr/bin/rar tar /bin/tar unace /usr/bin/unace unarj /usr/bin/unarj unrar /usr/bin/unrar unzip /usr/bin/unzip zip /usr/bin/zip xz /usr/bin/xz &krusader; may not be compatible with ACE despite our best efforts. Unace uses closed source and contains additional lines that make Unace get into infinite loop if the stdin is redirected to somewhere else. It works in the same manner as 'su', where you cannot enter the password only from the stdin. &krusader; >= 1-51 emulates the command line environment to enable co-operation with Unace, but we have noticed that Unace always changes its output format from release to release, making co-operation almost impossible. Checksum Utilities tab Here you can configure the full path of the following external Checksum Utilities: Checksum Utilities configurable full path supported checksums md5sum /usr/bin/md5sum md5 sha1sum /usr/bin/sha1sum sha1 md5deep /usr/bin/md5deep md5 sha1deep /usr/bin/sha1deep sha1 sha224sum /usr/bin/sha224sum sha224 sha256sum /usr/bin/sha256sum sha256 sha256deep /usr/bin/sha256deep sha256 sha384sum /usr/bin/sha384sum sha384 sha512sum /usr/bin/sha512sum sha512 tigerdeep /usr/bin/tigerdeep tiger whirlpooldeep /usr/bin/whirlpooldeep whirlpool cfv /usr/bin/cfv md5, sha1, sfv, crc UserActions Here you can configure the terminal for UserActions and the font for the output-collection. The default terminal is konsole --noclose -e. To set up, configure and manage your UserActions use ActionMan (UserActions &URL;). Protocols This page links the MIMEs to protocols. ⪚ protocol "tar" is linked to MIME "application/x-tar". In the Links you will see ⪚ Iso application/x-iso krarc application/x-ace application/x-arj application/x-bzip2 application/x-cpio application/x-deb application/x-debian-package application/x-gzip application/x-jar application/x-lha application/x-rar application/x-rpm application/x-zip tar application/x-tar application/x-tarz application/x-tbz application/x-tgz
diff --git a/doc/krusader-tools.docbook b/doc/krusader-tools.docbook index cb8768e4..b1bde967 100644 --- a/doc/krusader-tools.docbook +++ b/doc/krusader-tools.docbook @@ -1,33 +1,33 @@ &krusader; Tools &bookmarks; &diskusage; &search; &viewer-editor; &locate; &mount; &remote-connections; &synchronizer; &krusader-useractions; diff --git a/doc/locate.docbook b/doc/locate.docbook index 7243fe07..d61ecba4 100644 --- a/doc/locate.docbook +++ b/doc/locate.docbook @@ -1,131 +1,131 @@ Locate &GUI; Frontend Locate &krusader; has a &GUI; front end for the locate command (findutils). Locate is used for fast file searching not from a directory but a database. The package contains among other files, locate and updatedb. updatedb goes through the local file system and stores the file entries in the database. cron often calls updatedb every night (set with KCron). Choose Tools Locate or &Ctrl;&Shift; L to start this feature. Konfigurator can set additional arguments for updatedb. Checkbox Options Show only the existing files: if the database contains entries which no longer exist any more (deleted, moved since the last updatedb), locate does not list them. It checks the existence of each file before the listing in the results window (slow). Case Sensitive: unchecking it allows lower and upper case search. Action buttons Locate: executes locate foo foo and places its output into the results window. Stop: stops the locating process. UpdateDB: starts updatedb for updating the locate database after entering the root password. Close: closes the locate window. Results window Double-click on an item: steps to its directory, makes the item visible and closes the locate dialog. Right click: edits/views/finds the menu. F3: views the current item. F4: edits the current item. &Ctrl; F: finds files in the results window. &Ctrl; N: shows the next search result. &Ctrl; P: shows the previous search result. The results window supports dragging items to other windows and copy to clipboard (&Ctrl;C). diff --git a/doc/man-krusader.1.docbook b/doc/man-krusader.1.docbook index e68c16e4..d572f772 100644 --- a/doc/man-krusader.1.docbook +++ b/doc/man-krusader.1.docbook @@ -1,306 +1,306 @@ ]> KDE User's Manual KrusaderKrew Krusader man page. krusader-devel@googlegroups.com 2017-05-06 Krusader krusader 1 krusader advanced twin-panel file manager and &FTP; client by KDE krusader Options url Description Krusader is an advanced twin panel (commander style) file manager for Plasma and other desktops in the *nix world, similar to Midnight or Total Commander. It provides all the file management features you could possibly want. Plus: extensive archive handling, mounted filesystem support, &FTP;, advanced search module, viewer/editor, directory synchronization (disabled by default), file content comparisons, powerful batch renaming and much much more. It supports the following archive formats: ace, arj, bzip2, deb, gzip, iso, lha, rar, rpm, tar, xz, zip, and 7-zip and can handle other KIOSlaves such as smb:// or fish:// It is (almost) completely customizable, very user friendly, fast and looks great on your desktop! :-) You should give it a try. Options this lists the options available at the command line lists version information for &krusader; show the authors show the license <path> start left panel at <path> <path> start right panel at <path> <panel-profile> load <panel-profile> on startup enables debug output if there is already a tab open with that url, it is activated, otherwise a new tab is opened in the active panel Examples $ krusader --left=/mnt/cdrom --right=ftp://downloads@myserver.net $ krusader --left=/home,/usr,smb://workgroup.net --right=fish://myserver.net $ krusader --profile=ftp_managment Files Configuration files: ~/.config/krusaderrc ~/.local/share/krusader/krbookmarks.xml or krusader/krbookmarks.xml in the directory which can be determined using the qtpaths --paths GenericDataLocation command ~/.local/share/krusader/useractions.xml or krusader/useractions.xml in the directory which can be determined using the qtpaths --paths GenericDataLocation command /usr/share/kxmlgui5/krusader/krusaderui.rc ~/.local/share/kxmlgui5/krusader/krusaderui.rc or kxmlgui5/krusader/krusaderui.rc in the directory which can be determined using the qtpaths --paths GenericDataLocation command Platforms All POSIX: Linux, Solaris, All BSD Platforms: FreeBSD, MacOS-X KDE 2.x krusader v1.01 stable but there are no planned changes. KDE 3.x Krusader v1.01 needs KDElibs 2 Krusader v1.02 - 1.40 needs KDElibs 3 Krusader 1.40 prefers >= KDE 3.2 Krusader v1.50 - v1.51: KDE 3.2 - KDE 3.3 Krusader v1.60.0- v1.70.0: KDE 3.3 - KDE 3.4 Krusader 1.70.x-1.80.x: KDE 3.4 - KDE 3.5 KDE 4.x Krusader 2.0-2.4: KDE 4 KDE Frameworks 5 Krusader 2.5 and newer The latest version of Krusader can be found at the Krusader website. Features OFM filemanager features. Strong keyboard orientation. Powerful internal viewer and editor. Advanced search module that can search in archives. Supports: ace, arj, bzip2, deb, gzip, iso, lha, rar, rpm, tar, xz, zip, and 7-zip. Directory comparison and filtering. &FTP;/Samba client with a connection manager with SFTP/SCP support. Synchronizer, UserActions, Embedded console. Compare files by content. Easy editing of file permissions. Diskusage, Calculate occupied space. Checksum creation-verification. Fully MIME type-aware (with or without magic!). Mountmanager, Locate, Krename support. Root mode, Tabbed panels, Sidebar. Mouse selection modes. Profiles for: Panels, Key-bindings, Colors. For more features read https://docs.kde.org/development/en/extragear-utils/krusader/features.html License Krusader is distributed under the terms of the GNU General Public License. License version 2 as published by the Free Software Foundation. See the built-in help for details on the License and the lack of warranty. The copyright for the project and its name are still held by Shie Erlich and Rafi Yanai. i18n Krusader is translated into many languages. https://l10n.kde.org/stats/gui/trunk-kf5/po/krusader.po/ Read the KDE translation howto if you want translate Krusader in your native language. FAQ The latest version of the KRUSADER FAQ can be found on KDE documentation site. See Also The Krusader Handbook, at the krusader help menu. kf5options(7) qt5options(7) Authors Krusader is developed by a dedicated team of individuals, known as the Krusader Krew. Shie Erlich, author [erlich {*} users {.} sourceforge {.} net] (retired) Rafi Yanai, author [yanai {*} users {.} sourceforge {.} net] (retired) Dirk Eschler, Webmaster [deschler {*} users {.} sourceforge {.} net] (retired) Csaba Karai, Developer [ckarai {*} users {.} sourceforge {.} net] (retired) Heiner Eichmann, Developer [h {.} eichmann {*} gmx.de] (retired) Jonas Baehr, Developer [jonas.baehr {*} web.de] (retired) Vaclav Juza, Developer [vaclavjuza {*} seznam {.} cz] (retired) Jan Lepper, Developer [jan_lepper {*} gmx {.} de] (retired) Andrey Matveyakin, Developer [a.matveyakin {*} gmail {.} com] (retired) Davide Gianforte, Developer [davide {*} gengisdave {.} org] Alexander Bikadorov, Developer [alex.bikadorov {*} kdemail {.} net] Martin Kostolny, Developer [clearmartin {*} zoho {.} com] Toni Asensi Esteve, Developer [toni.asensi {*} kdemail {.} net] Nikita Melnichenko, Developer [nikita+kde {*} melnichenko {.} name] Frank Schoolmeesters, Documentation & Marketing Coordinator [frank_schoolmeesters {*} yahoo {.} com] (retired) Richard Holt, Documentation & Proofing [richard {.} holt {*} gmail {.} com] (retired) Matej Urbancic, Marketing & Product Research [matej {*} amis {.} net] (retired) Yuri Chornoivan, Documentation yurchor@ukr.net The project is written using KDevelop and Qt Designer. Bugs See the TODO file in the distribution for information on what remains to be done. For fixes, patches and comments mail to krusader-devel@googlegroups.com. Krusader Krew diff --git a/doc/menu-commands.docbook b/doc/menu-commands.docbook index 73916567..20677ed6 100644 --- a/doc/menu-commands.docbook +++ b/doc/menu-commands.docbook @@ -1,1848 +1,1848 @@ Menu Commands Commands Menu File Menu &Shift; F4 File New Text File Creates a new text file in the current directory, unless another directory is specified. The new file is opened for editing after it is created. F7 File New Directory Creates a new directory in the current directory. &Alt;&Ctrl; S File New Symlink Creates a symbolic link to the currently selected file. F3 File View File Opens the currently selected file for viewing. F4 File Edit File Opens the currently selected file for editing. F5 File Copy to other panel Copy the currently selected files or directories to the other panel. F6 File Move Opens a dialog to move the currently selected files or directories to a specified location. &Alt;&Shift; P File Pack Creates a new archive of all the selected files and directories in the active panel. &Alt;&Shift; U File Unpack Unpacks all the selected files in the active panel. &Alt;&Shift; E File Test Archive Tests archive for corruption. File Compare by Content Compares two current files by content - one from each panel via an external graphical diff utility. By default &kompare; is used, but you can also use ⪚ xxdiff or KDiff3 , change it in the Konfigurator Dependencies page. &Shift; F2 File Multi Rename This menu option starts Krename, a very powerful external batch renamer. Krename Features Renaming a list of files based on a set of expressions Copying/moving a list of files to another directory Convert filenames to upper/lower case Adding numbers to filenames Finding and replacing parts of the filename Renaming MP3/OGG Vorbis files based on their ID3 tags Setting access and modification dates Changing permissions and file ownership A plugin API which allows you to extend KRename's features Renaming directories recursively Support for KFilePlugins Undo renaming And many more.... File Create Checksum Checksum creation on file(s) and/or folder(s). File Verify Checksum Verify Checksum &Ctrl; P File Split File The Split file function splits a file up into multiple smaller files so that it can be stored on several smaller media (like diskettes, zip-drives, ...) or sent by e-mail. &Ctrl; B File Combine Files The Combine files function combines multiple files into one file after the Split file function was used. &Ctrl; Q File Quit Closes &krusader; and cleans up the temporary directory, the same as pressing F10 key. Edit Menu &Ctrl; X Edit Cut Cuts the selected file(s) to the clipboard to be moved to another location. &Ctrl; C Edit Copy Copies the selected file(s) to the clipboard to be moved to another location. &Ctrl; V Edit Paste Pastes previously cut or copied items from the clipboard to the current directory. F8 Edit Delete Deletes the currently selected file(s). &Ctrl; + Edit Select Group Opens a dialog which allows you to select files in the active panel. In the Search for dialog place the main search criteria. Enter a filename, a wildcard ( *.o.*, *.c &etc;) or both - separated by a space. When using 'text', the results are the same as '*text*'. You can exclude files from the search with '|' (⪚ '*.cpp *.h | *.moc.cpp'). With the profile handler you can manage your selections for future usage. A double-click on a profile is the same as entering the selection and pressing the OK button. More options for Select Group dialog are explained in the Search dialog, which are basically almost the same dialogs. &Ctrl; - Edit Unselect Group The opposite of Select Group. The files that match the pattern in the active panel will be deselected. &Alt; + Edit Select All Selects all files in the active panel. You can also select all directories by activating the Autoselect directories option in the Konfigurator Panel page. &Alt; - Edit Unselect All Unselects all the files in the active panel. &Alt; * Edit Invert Selection Inverts the selection status of all the files in the active panel (&ie; selected files will become unselected and deselected files will become selected). Edit Restore Selection Restore the selection status of previously unselected items. &Ctrl; F Edit Find in folder Toggles &krusader; quick search function. &Alt;&Shift; C Edit Compare Directories Toggles &krusader; Compare Directories function. Edit Compare Setup Configures the Compare Directories function. Select Newer and Single (default) Select Single Select Newer Select Different and Single Select Different Edit Calculate Occupied Space Calculates occupied space of files and folders, archives and remote filesystems. &Alt; Return Edit Properties Opens the properties dialog for the currently selected file. (KP refers to Key Pad.) Go Menu &Alt; Up Go Up Navigates to the parent directory of the active panel. &Alt; Left Go Back Navigates to the previously viewed directory in the active panel. &Alt; Right Go Forward Navigates to the next viewed directory in the active panel. Becomes available only if Go Back was used beforehand. &Alt; Home Go Home Navigates to the home directory of the current user. &Ctrl; &Backspace; Go Root Navigates to the root directory of the system. &Ctrl; Z Go Popular URLs Displays a listing of frequently viewed directories from which you may navigate to one by double-clicking on it. &Ctrl;&Shift; J Go Set Jump Back Point Sets the current directory as a "jump back" point. To navigate to this directory quickly, select the Jump Back command from the Go menu. &Ctrl; J Go Jump Back Returns to a previous "jump back" point. View Menu The action in this submenu usually affects the current &krusader; session. If you want to make permanent changes use Konfigurator Startup page. View Zoom In Increase the view scale. View Zoom Out Decrease the view scale. View Default Zoom Reset the view scale. &Alt;&Shift; D View Detailed View Shows the file names, file size, creation date and time and their attributes. &Alt;&Shift; B View Brief View Shows only the file names. &Alt; . View Show Hidden Files Toggles the option to display the hidden files in the &krusader; panels. &Shift; F10 View All Files Turns off all filters and display all files. &Shift; F12 View Custom Allows you to install a custom filter on the &krusader; panel. Only files that match the filter pattern(s) and directories will be displayed. Do not forget to deactivate the custom filter after use, or some files may not be visible. View Show Previews If checked, show the previews for all files and folders on the active panel. View Select Remote Charset Selects the remote charset for Remote Connections. &Ctrl; R View Reload Refreshes the contents of the active panel. View Save settings as default Allows you to save the view settings of the current tab for new instances of this view type. View Apply settings to other tabs Allows you to apply the view settings of the current tab to the other tabs. Useractions Menu You can add your own menu entries here by defining your own UserActions. Useractions Manage User Actions... Opens the UserActions manager. Meta A UseractionsMultimedia Enqueue in Amarok Appends selected item(s) to &amarok; playlist. &Ctrl;&Alt; UseractionsSamples Equal panel-size Sets the ratio between the two panels to 50/50. &Ctrl;E UseractionsSamples Edit as root Allows you to edit a file with kwrite using root permissions. &Ctrl;&Alt;M UseractionsSamples Mount Mounts a new filesystem. &Ctrl;&Alt;C UseractionsSamples Copy current item to clipboard Copies current item name to the clipboard. MetaF5 UseractionsSystem Backup current Backups current file in current directory and asks the user for a new filename. By default .old is appended to the original filename. Meta 1 UseractionsUser Interface Sort By Name When this action is turned on, the main sorting key in the &krusader; panel becomes the file name. Meta 2 UseractionsUser Interface Sort By Extension When this action is turned on, the main sorting key in the &krusader; panel becomes the file extension (the part of the filename after the last '.'). This is useful for grouping files of the same type closer together. Meta 3 UseractionsUser Interface Sort By Size When this action is turned on, the main sorting key in the &krusader; panel becomes the file size. This is useful for determining the largest files in a particular directory. Meta 4 UseractionsUser Interface Sort By Modified When this action is turned on, the main sorting key in the &krusader; panel becomes the last modified date for each file. This is useful for finding files you recently worked on. Tools Menu &Ctrl; S Tools Search Opens KruSearcher - the &krusader; search module. &Ctrl;&Shift; L Tools Locate Opens the Locate &GUI; frontend for fast file searching. &Ctrl; Y Tools Synchronize Directories The Synchronize Directories function compares the left and right panels and shows the differences between them. After the compare function, you can move files/directories so that they can be synchronized. &Alt; / Tools MountMan Opens submenu with items to open MountMan or mount/unmount various mounting points. &Alt;&Shift; S Tools Disk Usage Opens Disk Usage. &Ctrl; N Tools New Net Connection Opens the New Network Connection dialog to start a &FTP;, SMB, FISH or SFTP connection to a remote host. If you leave the user name and password fields empty, you will login as anonymous. You can Bookmark these remote sessions. &Ctrl;&Shift; D Tools Disconnect From Net Ends the remote connection in the active panel and returns to the start-up path. Tools Start Terminal Opens a terminal window in the default directory (usually your home directory). You can choose your favorite terminal application in the Konfigurator General page. &Alt;&Shift; K Tools Start Root Mode Krusader Starts &krusader; in Root mode at the same location. Root mode &krusader; requires kdesu by default, if kdesu is not available or if you prefer ⪚ gksu when using GNOME you can change this behaviour in the Konfigurator Dependencies page. Be careful when using &krusader; with ROOT PRIVILEGES. Tools Empty Trash Empties the trash bin. Window Menu &Ctrl;&Shift; N Window New Tab Opens a new tab in the active panel. Window Lock Tab/Unlock Tab Locks then unlocks current tab in the active panel. &Ctrl;&Alt;&Shift; N Window Duplicate Current Tab Opens a new tab that is a duplicate of the active tab in the active panel. &Ctrl;&Shift; O Window Move Current Tab to Other Side Moves current tab to the other side of &krusader; window. &Ctrl; W Window Close Current Tab Closes the current tab in the active panel. This command is only available if there is more than one tab in the active panel. Window Close Inactive Tabs Closes all the tabs in the active panel except for the active. Window Close Duplicated Tabs Closes the duplicated tabs in the active panel. &Ctrl; . Window Next Tab Navigates to the next tab in the active panel. This command is only available if there is more than one tab in the active panel. &Ctrl; , Window Previous Tab Navigates to the previous tab in the active panel. This command is only available if there is more than one tab in the active panel. &Alt;&Shift; L Window Profiles Opens a menu where Panel profiles can be saved and restored. &Ctrl; U Window Swap Panels The left panel will become the right panel and vice versa, only the current tabs will be swapped. &Ctrl;&Shift; U Window Swap Sides The complete left panel will become the right panel and vice versa, and all tabs will be swapped. &Ctrl;&Alt; R Window Vertical Mode Toggles the List Panel between horizontal and vertical mode. &Ctrl;&Alt; F Window Toggle Fullscreen Terminal Emulator Toggles full screen mode of the terminal emulator. This option is only available when Show Terminal Emulator is activated. Window Save Position Saves the current size and position of the &krusader; main window. This action can be automated with Save last position, size and panel settings in Konfigurator Startup page Settings Menu Settings Toolbars Show Main Toolbar Shows the Main Toolbar if checked. Settings Toolbars Show Job Toolbar Shows the Job Toolbar if checked. Settings Toolbars Show Actions Toolbar Shows the Actions Toolbar if checked. Settings Show Statusbar Shows the Statusbar if chosen. Settings Show FN Keys Bar Shows the FN Keys Bar if checked. &Ctrl;&Alt; T Settings Show Terminal Emulator Show the Terminal Emulator if checked. Settings Show Command Line Shows the Command Line if checked. Settings Command Execution Mode Setup Allows you to choose command execution mode. You can choose one of the following options. Start and Forget: start execution and do not wait for the results. Display Separated Standard and Error Output: show the output to stdout and stderr on the separate panels. Display Mixed Standard and Error Output: show the output to stdout and stderr on the single panel (default). Start in New Terminal: execute the command in the new terminal window. Send to Embedded Terminal Emulator: send the command to the embedded terminal emulator. Settings Job Queue Mode Toggles the Queue Manager mode. If checked then pressing F2 or the corresponding button in the copy/move dialog immediately starts the job even if there are running jobs in queue. If unchecked then the job will be put into the queue if the queue is not empty. Otherwise the job will be started immediately. Settings Configure Shortcuts Configure Shortcuts Opens a dialog which allows you to configure the &krusader; key bindings. With the Import shortcuts and Export shortcuts buttons you can load and save a Key-binding profile. This allows &krusader; to have the Total Commander, Midnight Commander, foo-commander, or your custom Key-bindings. The only limitation is that global &plasma; key-bindings and some &krusader; key-bindings cannot be changed yet, as well as some features in foo-commander that we either do not have or need. The Key-bindings are stored in /usr/share/krusader, foo.keymap.info contains a description, foo.keymap is an ini file that holds the Key-bindings. Until &krusader;-1.70.1 this was a binary file, &krusader; is backwards compatible for importing this legacy binary format. If a *.keymap.info text file exists, &krusader; will display it, showing additional information regarding the loaded Key-bindings file. Here you have a chance to exit without importing the proposed Key-bindings file. A Total Commander Key-bindings file is provided. Please upload your favorite Key-bindings schemes so that they become available for the &krusader; community. Thanks! Settings Configure Toolbars Opens a dialog which allows you to configure the Main Toolbar or the Actions Toolbar. You can add action buttons of your favourite UserActions to the desired toolbar. Settings Configure &krusader; Opens Konfigurator - the &krusader; configuration center. Help Menu Additionally, &krusader; has the common &kde; Help menu items, for more information read the sections about the Help Menu of the &kde; Fundamentals. diff --git a/doc/mount.docbook b/doc/mount.docbook index 6e7a4bcd..c607e280 100644 --- a/doc/mount.docbook +++ b/doc/mount.docbook @@ -1,89 +1,89 @@ MountMan: work with your mounted file systems Mount MountMan is a tool which helps you manage your mounted file systems. Once invoked, it displays a list of every mounted file system. For each file system, MountMan displays its name (which is the actual device name - &ie; /dev/sda1 for a first partition on first HDD), its type (ext4, ext3, ntfs, vfat, ReiserFS...) and its mount point on your system (the directory on which the file system is mounted). If you want filter out non-removable devices from the list, just check the item Show only removable devices at the left of the device list.
MountMan MountMan
MountMan also displays usage information using total size, free size, and percentage of available space free. If those numbers say N/A, that usually means that the file system is not mounted. Left clicking on a file system displays a pie chart on the left of the window, graphically displaying the usage information for the file system. Clicking on a non-mounted file system will display not mounted instead of the graph. Double-clicking on a file system will close MountMan and open that file system inside &krusader;'s active panel. Right-clicking on a file system will open a small menu which displays what actions can be performed on that file system. At the moment, you can only mount, unmount and eject (if clicking on a removable file system, ⪚, a &CD-ROM;). We plan to expand MountMan in the next evolution of &krusader;. It will be able to format partitions, edit /etc/fstab, create new file systems and more.... By the way, we have started working on it, see "Quickmode for MountMan". Quickmode for MountMan To activate Quickmode for MountMan, click lateral button with arrow on the MountMan button on the Main Toolbar. General idea: display a list of all possible mount points. Each time the menu is displayed, it determines if a mount point is mounted or not and associates the correct action (mount or umount). This offers a quick interface to MountMan. It is working, and currently uses KMountMan::mount and unmount. It uses the new &kf5-full; services for fstab-reading and this will enable us to remove a lot of code from the "old" MountMan.
diff --git a/doc/mouse-commands.docbook b/doc/mouse-commands.docbook index 6eae7f2c..86f1c35a 100644 --- a/doc/mouse-commands.docbook +++ b/doc/mouse-commands.docbook @@ -1,43 +1,43 @@ Mouse Commands Commands Mouse Mouse Usage In this chapter you will be guided in using &krusader; effectively with a mouse and to configure mouse gestures through KHotKeys. By simply clicking the mouse you can select different sections of &krusader;. Another chapter describes the selection modes, and how you can configure them: Selection Mode diff --git a/doc/occupied-space.docbook b/doc/occupied-space.docbook index 75b6f2de..cd6728a6 100644 --- a/doc/occupied-space.docbook +++ b/doc/occupied-space.docbook @@ -1,60 +1,60 @@ Calculate Occupied Space Occupied Space There are two ways to calculate the occupied space of files/directories. Pressing the configurable shortcut for the Calculate occupied space on a directory under the cursor instantly calculates the occupied size. To calculate how much disk space is occupied by the selected files and directories in the active panel select Edit Calculate Occupied Space . After a small delay, a dialog box will be displayed with total occupied space and the number of files and directories you selected. The space occupied by every selected directory will be shown as if the user just pressed configurable shortcut on those directories. If the active panel is browsing an archive, the numbers will apply to the unpacked size of the selected files and directories, not their compressed size. After the calculation the selection state will be toggled and the cursor will move one step downwards. Calculating the occupied space on remote file systems is supported. Performing this operation on a very large file system (thousands of files) may be time-consuming. You can cancel the calculation process at any time by clicking the Cancel button. diff --git a/doc/profiles.docbook b/doc/profiles.docbook index 4fd9c48f..ecdd01ad 100644 --- a/doc/profiles.docbook +++ b/doc/profiles.docbook @@ -1,70 +1,70 @@ Profiles Profiles With profiles you can save and restore your favorite settings. Several features support profiles, you can have ⪚ different panel profiles (work, home, remote connections, &etc;), search profiles, synchroniser profiles, &etc; Panel Profiles A panel profile contains the following: all the tab paths (left or right), the current tab (left or right) and the active panel (left or right). All this information is stored in the krusaderrc file. You can have several panel profiles, ⪚ file management, &FTP; management, home, work, &etc; The panel profile can be saved and restored in the Window menu. The default startup profile will be used when starting &krusader;, but you can override it with a command-line option. Color Profiles Colormaps can be saved and restored with Color Profiles. Key-binding Profiles Keymaps can be saved and restored with Key-binding Profiles. Search Profiles If you regularly perform the same search operation, you can save the search settings (⪚ include files, exclude files, &etc;) in a Search Profile. Synchronizer Profiles If you regularly perform the same synchronize operation, you can save the synchronize settings in a Synchronizer Profile. diff --git a/doc/release-overview.docbook b/doc/release-overview.docbook index a0130974..a5420c3d 100644 --- a/doc/release-overview.docbook +++ b/doc/release-overview.docbook @@ -1,730 +1,730 @@ Release overview Release overview This appendix gives an overview of the &krusader; releases. Release overview &krusader; version release date &kde; version 2.7.1 "Peace of Mind" 2018-08-12 &kde; Frameworks 5 2.7.0 "Peace of Mind" 2018-05-13 &kde; Frameworks 5 2.6.0 "Stiff Challenges" 2017-04-12 &kde; Frameworks 5 2.5.0 "Clear Skies" 2016-10-22 &kde; Frameworks 5 2.4.0-beta1 "Migration" 2011-06-26 &kde; 4.0 - 4.6 2.3.0-beta1 "New Horizons" 2010-12-25 &kde; 4.0 - 4.5 2.2.0-beta1 "DeKade" 2010-04-30 &kde; 4.0 - 4.4 2.1.0-beta1 "Rusty Clutch" 2009-10-31 &kde; 4.0 - 4.3 2.0.0 "Mars Pathfinder" 2009-04-11 &kde; 4.0 - 4.2 2.0.0-beta2 "Space Odyssey" 2008-12-27 &kde; 4.0 - 4.2 2.0.0-beta1 "Phoenix Egg" 2008-06-07 &kde; 4.0 - 4.1 1.90.0 "Power Stone" 2008-03-15 &kde; 3.2 - 3.5 1.80.0 "Final 3rd Stone" 2007-07-21 &kde; 3.4 - 3.5 1.80.0-beta2 "Last Unstable Stone" 2007-03-31 &kde; 3.4 - 3.5 1.80.0-beta1 "The Last Krusade" 2007-01-07 &kde; 3.4 - 3.5 1.70.1 "Round Robin" 2006-07-17 &kde; 3.3 - 3.5 1.70.0 "Round Robin" 2006-02-12 &kde; 3.3 - 3.5 1.70.0-beta2 "Afterburner" 2005-11-05 &kde; 3.3 - 3.5 1.70.0-beta1 "Hellfire" 2005-10-02 &kde; 3.3 - 3.5 1.60.1 2005-10-30 &kde; 3.3 - 3.4 1.60.0 2005-04-10 &kde; 3.3 - 3.4 1.60.0-beta2 2005-03-21 &kde; 3.3 - 3.4 1.60.0-beta1 2005-03-03 &kde; 3.3 - 3.4 1.51 2004-12-14 &kde; 3.2 - 3.3 1.50 2004-10-31 &kde; 3.2 - 3.3 1.50-beta1 2004-10-17 &kde; 3.2 - 3.3 1.40 2004-07-20 &kde; 3.2 1.40-beta2 2004-06-22 &kde; 3.2 1.40-beta1 2004-04-11 &kde; 3.2 1.30 2003-11-14 &kde; 3.0 - 3.2 1.29-beta1 2003-10-02 &kde; 3.0 - 3.2 1.25-beta1 2003-08-15 &kde; 3.0 - 3.2 1.21-beta1 2003-07-18 &kde; 3.0 - 3.2 1.20 2003-05-31 &kde; 3.0 - 3.2 1.12-beta2 2003-05-09 &kde; 3.0 - 3.2 1.12-beta1 2002-12-23 &kde; 3.0 - 3.2 1.11 2002-10-17 &kde; 3.0 - 3.2 1.10 2002-08-15 &kde; 2 1.02 (first KDE3 version) 2002-04-16 &kde; 3 1.01 2002-04-01 &kde; 2 1.00 2002-01-01 &kde; 2 0.99 2001-12-01 &kde; 2 0.98 2001-10-27 &kde; 2 0.97 2001-10-08 &kde; 2 0.95 2001-07-22 &kde; 2 0.93 2001-05-31 &kde; 2 0.92 2001-05-17 &kde; 2 0.91 2001-05-10 &kde; 2 0.90 2001-04-30 &kde; 2 0.79 2001-02-25 &kde; 2 0.75 2000-01-12 &kde; 2 0.70 (milestone 3) 2000-11-07 &kde; 2 0.69 2000-10-28 &kde; 2 0.65 2000-08-19 &kde; 2 0.60 2000-07-19 &kde; 2 M2 (milestone 2) 2000-07-11 &kde; 2 M1 (milestone 1) 2000-05-?? &kde; 2 (Kleopatra 1.91) Project began 2000-04-30 Kleopatra 1.91
Note: older 0.x beta releases are not included in this table.
diff --git a/doc/remote-connections.docbook b/doc/remote-connections.docbook index f1c915f7..3a91a2f6 100644 --- a/doc/remote-connections.docbook +++ b/doc/remote-connections.docbook @@ -1,281 +1,281 @@ Remote Connections Remote Connections Remote connections are easily made by typing the &URL; in the Location Toolbar; these are actually KIO Slaves. Please note that the &krusader; panel does not support all KIO Slave ⪚ http:// will not work in the panel, but will work in the viewer. Some examples: ftp://public.ftpserver.org/directory/ fish://username@hostname/ sftp://username:password@sftp.foo.org/ ftp://username@my.server.org:21/directory/ smb://username:password@server/share ftp://username@proxyusername:password@proxipassword@hostname/directory nfs://<host>:<port><url-path> webdav://www.server.com/path/ You can bookmark these &URL;s, however, please read the Bookman section regarding securely save passwords. For connecting to multiple locations, bookmark these &URL;s and open them one by one, or open them all together by using Panel profiles. To switch from one to another location, just open a Folder tabs for each. There are three ways to start a remote connection: Type the &URL; in the Location Toolbar Select Tools New Net Connection which will pop-up a dialog that will ask for the remote site details. This dialog is handy if you are not used to type remote &URL;s in the Location Toolbar. Leaving the password and user name fields empty will log you in as anonymous. Note: we are planning to rewrite this dialog window. You can bookmark a directory on a remote host and return to this directory from the bookmark button on the top corner of your panel just like in a web browser. After you log on to a remote server you can browse it just like your local hard drive with the following exceptions: You cannot execute files on remote servers. Permissions cannot always be calculated on remote servers (depends on server and access method) so you might get a ? on the permissions columns for some files. Disk usage information is not available for most remote filesystems. To change the charset of the remote host use View Select Remote Charset . You can close the current Active Remote Connection by two separate methods: Manually: Add the disconnect button to the Main Toolbar and click on it. Automatically: Change the &URL; in the Location Toolbar . &krusader; is a file manager that supports remote connections via KIO Slaves, but if you are looking for even more advanced remote connections features, ⪚ an advanced &FTP; client we recommend you to use ⪚ LFTP or FileZilla. LAN connections via fish:/ protocol (zeroconf) Remote LAN Connections (zeroconf) This section is contributed by Andrew Svet (z-vet), feedback about this chapter is appreciated. Thanks! This works on a Debian system, so it will work on Debian and derivatives (Kubuntu &etc;), though it should work on other Linuxes as well. We assume that you have SSH installed, configured and working on every machine on LAN you want to connect to/from. There are plenty of very good tutorials about SSH on the net, ⪚ at linuxhomenetworking.com or just google for it. We use the default SSH port (22) for this chapter. Remember to change it if you use different one. All modifications, editing &etc; must be done as root. Let's start with installing all the packages we need: # apt-get install Everything is installed, now let's do some configuration. First, we need our services to be announced on LAN. That is why we installed avahi-daemon: it represents your machine on local network and allows other applications to publish services they provide. Avahi-daemon comes with example ssh.service configuration file found in /usr/share/doc/avahi-daemon/examples. In order to get the service to be announced on LAN we need to copy this file to /etc/avahi/services directory: # cp Now we need fish:/ protocol to be announced too, so we use an ssh.service file as a template for fish.service: # cp This file is just a copy of ssh.service. Edit the fish.service file and replace "Remote Terminal on %h" with "Fish to %h" and "_ssh._tcp" with "_fish._tcp". Here is how it looks after edit: <?xml version="1.0" standalone='no'?><!--*-nxml-*--> <!DOCTYPE service-group SYSTEM "avahi-service.dtd"> <!-- $Id: remote-connections.docbook,v 1.6 2007/05/02 18:07:28 codeknight Exp $ --> <!-- This file is part of avahi. avahi 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 2 of the License, or (at your option) any later version. avahi is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR <!-- See avahi.service(5) for more information about this configuration file --> <service-group> <name replace-wildcards="yes">FISH to %h</name> <service> <type>_fish._tcp</type> <port>22</port> </service> </service-group> Save the modified file. Now we need to create a new file _fish._tcp, open a text editor and add the next lines: Name=FISH Protocol (ssh) Type=_fish._tcp UserEntry=u PathEntry=path PasswordEntry=p And save the file /usr/share/apps/zeroconf/_fish._tcp Do the same on each machine on your LAN, then restart avahi-daemon: # /etc/init.d/ avahi-daemon Then open &krusader; and type in location-toolbar-lnk: zeroconf:/ to open the zeroconf connection. Enter the Fish Protocol directory. Inside you will find the links to each machine that announced fish:/ on your LAN, the location-toolbar-lnk: will point to zeroconf:/_fish._tcp Double clicking on any of these machines, them will bring up the password prompt, asking you for your ssh key passphrase (if password was set). Enter your passphrase. Congratulations: you connected to remote machine using &krusader;! diff --git a/doc/search.docbook b/doc/search.docbook index 2b9ddf3f..d9ae3924 100644 --- a/doc/search.docbook +++ b/doc/search.docbook @@ -1,364 +1,364 @@ KruSearcher: find what you are looking for Search Welcome to &krusader;'s powerful search module - nicknamed KruSearcher. It is the most able tool (that we know of) for &Linux;, since it allows so many different ways to quickly find the file you are looking for. The search function is also available on remote file systems. It is divided into two levels, the general and the advanced. Let's take a look at the general page.
General Search General Search
The page is divided into four parts: top (search for) part, bottom part - titled Containing text, left part - titled Search in and right part which is called Do not search in. Obviously, each part handles different aspects of the search. Let's look at them closely. Top Part: Search for: here you enter the main search criteria. You can enter a file name, a wildcard ( *.o.*, *.c &etc;) or both - separated by a space. If you type 'text' the results is the same as '*text*'. You can exclude files from the search with '|' (⪚ '*.cpp *.h | *.moc.cpp') . You can use quotation marks for names that contain spaces. Filter "Program Files" searches out those files/directories the name of which is Program Files. Case sensitive: unchecking it will allow lower and upper case search (&ie;: *.c interprets as *.c AND *.C). Of type: this box lets you search for a file not only by its name, but also by its MIME type. For example, you can search for all the audio files whose name begins with B. Usually this option defaults to 'all files', but you can choose to search for archives, directories, images, text files, videos and audio files. Left and Right Part: Search in and Do not search in: Using those two parts, you can specify a search exactly the way you want it. For example, you might want to search for a file in the whole file system (beginning with /), but do not want to search inside /mnt. All you need to do is write / in the Search in box, and write /mnt in the Do not search in box. If you want to enter more than one directory in one of the list boxes, just type the first name and press &Enter;. The directory name will be copied to the bigger list box and you will be able to enter another name. The input line has an auto-completion feature, corresponding to &plasma; global settings. If you wish, you can click on the folder icon, and browse to the directory you wish to add. It is possible to define files and directories which will be filtered out from the search results using the Exclude Folder Names input field at the bottom of the Do not search in box. Items in the filtering list should be space-separated. Spaces in the filtering list items can be escaped or quoted. Example: .git "target build" build\ krusader Bottom Part: Text: entering text here makes &krusader; search for it inside the files ( grep). This way you can search for all header files ( *.h) which include the word 'testing 123'. RegExp switch: toggle the regular expressions mode. A drop-down list that is shown after clicking down arrow on the right allows you to enter special symbols of regular expressions. Encoding: allows you to choose the text encoding. Match whole word only: allows you to specify that a complete match (letters and length) must be found. Case sensitive: refers to your text being searched for in upper and lower case or the exact phrase you entered. Search in sub folders: perform a recursive search and dive into every directory on the way. Search in archives: &krusader; will search for your files inside every supported archive. This, however, takes longer to perform. If you check this checkbox, you will notice you cannot grep inside files anymore. This is done, since looking inside archived files forces &krusader; to extract them, which results in an extremely time-consuming search. Follow links: if checked, &krusader; will follow soft-links during the search. Profiles: if you have to regularly perform the same search operation, you can save the search settings ⪚ include files, exclude files, &etc;... Press the 'profile' button, and you can add/load/save/remove search profiles. Query to clipboard: if checked, &krusader; will place search text to clipboard when a found file is opened. The above screen shot shows a search for all the files which end with c, cpp or h and include the string 'testing 123'. &krusader; will search in /root, /opt, /bin and /usr, but not in /usr/lib and /usr/share. Clicking the Search button starts the search and displays the results page. During the search, you may press the Stop button to stop the search. The Close button is not operational during a search, so you must first stop the search and then Close the window. When a result is found, double-clicking on it will take &krusader;'s active panel point to the found files - but will not close the search window, so you will be able to click on a different result. Sometimes, you need to narrow your search even more. Krusearcher allows a much finer search, and for that, let's look at the Advanced Page...
Advanced Search Advanced Search
This screen shot shows a search for files whose size is between 10KiB and 150KiB, which were modified between October 10th and November 1st, which belong to any user in the 'nobody' group, and are readable and writable by anyone in the 'nobody' group, but only readable to the rest of the world. The advanced page is divided into three parts: size, date and ownership. Size Allows you to choose the size range of the file you are looking for. By checking the check boxes, you can search for a file which is bigger than XXX bytes (or KB, MB), smaller than XXX bytes or, by checking both search for a file which size is bigger than XXX but smaller than YYY. Date This part offers three different ways of defining a date criteria: Modified between: allows you to enter two dates. &krusader; will search for files with a modification date between the first date and the second one, inclusive. You can click on the date icon (near the input boxes) to open a standard date window - which allows you to easily browse through the calendar and search for the desired date. Clicking on a date will close the window and the date will appear in the input box. Not modified after: choosing this option makes &krusader; search for files that were NOT modified after a given date, which is the same as searching for files older than that date. Modified in the last/not modified in the last: in these input boxes, you do not enter a date but a number. This number represents days/weeks/months (as chosen in the near drop box). Entering the number 5 in the upper input box, makes &krusader; search for files that were modified in the last 5 days. Entering the number 2 in the lower input box makes &krusader; search for files that did NOT change in the last 2 days. The combination of both results in a search for files that were changed in the last 5 days, but NOT in the last 2 days. Ownership Belongs to user/group: by checking each of the corresponding check boxes, you can search for files which belong to a certain user and/or group. The drop box lists all the user names and group names in the system, just choose one and go ahead. Permissions: divided into owner, group and all - it allows you to choose certain permissions for the file. You can choose to specify permissions for the owner, group, all or any combination. The question mark (?) is a wildcard - which means that any permission is valid. To clarify, the screen shot above (the ownership part) describes a search for files that belong to any user in group 'users', are readable, writable but not executable to any user in the group, are readable but not writable or executable to the rest of the world and have unknown permissions to its owner. Results Clicking on the Search button to start the search and open the Results window. Here you can see the progress and the results of the search action. If you want you can stop the current search action with the Stop button. In the result list select the file and use F3/ F4 to view/edit or use the right-click menu. When you click on a found item, the directory of the active panel will change if the selected item is not in the current panel directory. The results window supports dragging items to other windows and copy to clipboard (&Ctrl;C). When you close the Krusearcher window, the selected item is selected in the active panel. Feed to listbox When you click the Feed to listbox button after the results are displayed, then &krusader; ask for a Query Name, this name will be used in the Results-Tab in the List Panel that holds the search results. You can do whatever you want on the files in the new tab. The location toolbar will display ⪚ virt:/Search results 1. The files in the Results-Tab are actually the original files. if you delete a file - it is removed!
diff --git a/doc/splitter.docbook b/doc/splitter.docbook index 6c685fc6..bbc532b5 100644 --- a/doc/splitter.docbook +++ b/doc/splitter.docbook @@ -1,77 +1,77 @@ File Splitter File Splitter This function splits one (large) file up into multiple smaller files so that they can be placed on smaller media (⪚ ZIP drives, ...) or sent by email. The multiple files can be combined again to recreate the original file. &krusader; and Total Commander split/combine functions are compatible. To split a file choose File Split or &Ctrl; P: a Split dialog will open to confirm the operation. If the name of the original file is foo, then the name of the smaller files will be foo.000 foo.001 foo.002 &etc; There is no theoretical limit in the number of smaller files, but it is recommended to limit the number to a maximum of 1023 files. &krusader; creates a foo.crc file. This file contains the name, size and the crc checksum of the original file. It is possible to combine the file without the foo.crc file but in this case &krusader; cannot check if the original file was recreated correctly. An example of a foo.crc file: filename=foo size=432998467868 crc32=21EB719A To combine the multiple files choose, select a partial file foo.xxx and choose File Combine files or &Ctrl; B. The target directory is the inactive panel and can be changed. The file names must be numbered in ascending order: foo.001, foo.002, foo.003 &etc; or have letters in ascending order: (" split letter method") fileaa, fileab, fileac &etc; If the file foo.crc is found in the same directory as the partial files, &krusader; will use this file to check the combined file for errors. diff --git a/doc/synchronizer.docbook b/doc/synchronizer.docbook index 3c6f9b67..9fd8f66f 100644 --- a/doc/synchronizer.docbook +++ b/doc/synchronizer.docbook @@ -1,739 +1,739 @@ Synchronizer Synchronizer Introduction This function compares two directories with all subdirectories and shows the differences between them. After selecting some options and the Compare function you can synchronize the files and directories. One panel could be an &FTP; server. Do not forget the Compare by content option if the file size stays equal. Choose Tools Synchronize Directories or &Ctrl; Y to start this feature. The colors are configurable. The synchronizer has its own keybindings. For local files: The synchronizer changes the date information to the original dates.
Synchronizer Synchronizer
Procedure: Set the Left directory and the Right directory that you want to compare. Configure the optional File Filter, General Filters and Advanced filters. The General Filters includes a Do not search in option which lets you exclude directories from comparison. Set the Checkbox options: Recurse subdirectories, Follow symlinks, Compare by content, Ignore Date, Asymmetric and Ignore Case. Click on the Compare button to compare the directories. By using Show options, you can include/exclude particular file types. The files that are not on the list will be untouched by synchronization. If needed, you can change the Task operations determined by the Comparator with the right click menu. When you agree with the Compare results, click on the Synchronize button to show the synchronize dialog. After selecting the options of the Synchronizer, click on the Start button to start the synchronization process. A detailed explanation of all the functions and buttons is described in the following text.
Comparator Panel elements Left Directory: The left side base directory. Right Directory: The right side base directory. File Filter: Filters the filenames at synchronizing. ⪚ *.png selects only files the names of which end with .png. Directories are listed if the directory name ends with .png or if the directory contains files which end with .png. Multiple include/exclude example: '*.cpp *.h | *.moc.cpp' includes *.cpp *.h and exclude *.moc.cpp. General filters: Search for, containing text Advanced filters: Size, date, ownership Filename filtering criteria You can make use of wild cards. Multiple patterns are separated by space (means logical OR) and patterns are excluded from the search using the pipe symbol. If the pattern is ended with a slash (*pattern*/), it means that pattern relates to recursive search of directories. pattern - means to search those files/directories whose name is a pattern, recursive search goes through all subdirectories independently of the value of the pattern. pattern/ - means to search all files/directories, but recursive search goes through/excludes the directories whose name is a pattern. You can use quotation marks for names that contain spaces. Filter "Program Files" searches out those files/directories the name of which is Program Files. Examples: *.o *.h *.c?? *.cpp *.h | *.moc.cpp * | CVS/ .svn/ Note: the search term 'text' is equivalent to '*text*'. Checkbox options Recurse subdirectories: The Synchronizer walks through subdirectories when comparing. Follow symlinks: The Synchronizer follows the symbolic links to directories (not the files!). Compare by content: The files the sizes of which are equal will be compared by content as well. Ignore Date: Ignores the date information (useful for &FTP;, smb, archive, ... ) the date information is relevant *only* in the local file system. Asymmetric: The Left Directory is the target directory, the Right Directory is the source directory. This function synchronizes the content of the left directory to the right directory. The files that exist only on the left side will be deleted. The files that exist only on the right side will be copied to left. The files considered to be equal will be untouched. Those files considered to be different will be copied to left. Use this feature with care! This function is useful if the date information is unusable, or to synchronize the left directory to the content of a file server (like a download). Alternatively, it is useful to make a backup from the right directory to the left directory. NOTE: Do NOT use this option to synchronize a desktop and a notebook (⪚ files that exist only on the left side will be deleted!). Scroll Results: This is for slow &FTP; servers, ... When the Synchronizer has finished comparing a file, it puts the file into the end of the list at once and scrolls the window, if necessary. It is important, if you want to know what the Synchronizer is doing. If the &FTP; server is slow, the comparison of a directory may take a lot of time. By turning this option on, you can see that the comparator is "alive", but slow. For fast file systems, it can reduce the speed of comparing slightly. Ignore Case: Case insensitive filename compare, this is useful when synchronizing &Windows; filesystems. Ignore hidden files: Ignores files that start with a dot. When everything mentioned above is done, click on the Compare button to compare the directories, the results are displayed in the File List. File List The File List has the following columns: "Left name | Left size | Left time | Task | Right time | Right size | Right name". Here you can check the compare results and modify the synchronization actions if desired. The text color defines the default copy direction determined by the comparator. Green: Copy from left to right. Blue: Copy from right to left or delete on the right side in asymmetric mode. Red: Files which are different by the given criteria (no copy direction). Black: Files which are identical (no copy direction). A double click on a file calls the &krusader;s Compare by content function. The Tasks (<=>) column defines the default copy direction, which can be modified by the user if needed. The results window supports dragging items to other windows ( drag [left], &Shift;+drag [right] ) and copy left/right selected items to clipboard (&Ctrl;C). File List: Tasks (<=>) column This column displays the task that is planned for the Synchronizer, this task can be modified with the right click menu. ->: Copy from the left side to the right side. For directories: mkdir in the right side. =: The files are the same, do not do anything. !=: The file is excluded, or the Synchronizer does not know what to do. <-: copy from the right side to the left side. For directories: mkdir in the left side DEL: delete files from the right side. Show options Here you can include/exclude particular file types. Those files, that are not in the list, will be untouched at synchronization. ->: Enable/Disable the copy task from left to right. =: Enable/Disable listing the equal files. !=: Enable/Disable listing the excluded / "do not know what to do" files. <-: Enable/Disable the copy task from right to left. Trash: Enable/Disable the file deleting from the left side task. Duplicates: Enable/Disable those files that exist on both sides. Singles: Enable/Disable those files that exist only on one side. Action buttons Compare: Compares the directories, the results are displayed on the File List. Stop: Stops comparing. Synchronize: Shows the synchronize dialog. Close: Closes the Synchronizer window. The Status line may contain The number of scanned directories while comparing. The filename when comparing by content (for big files). The number of files on the list. Right click menu on the File List The right click menu is split up in three sections: 1. change the Task operations determined by the Comparator. Select one or multiple files. The change will be applied on the selection and not only on the right clicked item. If the selected item is a directory, the change will be applied on its files/subdirectories as well. Synchronize Directories: Starts synchronization. Exclude: Excludes a file/dir from synchronization ( task change to != ). Restore original operation: Task change to the original result of comparison. Reverse direction: Task change ( -> to <-, and <- to -> ). Copy from right to left: Task change to <-. Copy from left to right: Task change to ->. Delete ( left single ): Task change to DEL. 2. change the selections Select items: Opens a dialog which allows you to select items. This is the Select Group dialog from the Edit menu. Unselect items: Opens a dialog which allows you to deselect items. This is the Unselect Group dialog from the Edit menu. Invert selection: Invert the selection status of all items. 3. others (note that the following operations work on the right-clicked file and not on the selection). View left file: Starts viewer on the left file. View right file: Starts viewer on the right file. Compare Files: Starts the diff utility (⪚ &kompare;) on the files. Other buttons Profiles: If you have to synchronize the same directories often, you can save the settings ⪚ include files, exclude files, ... Press the Profile button, and you will be able to add / load / save / remove synchronizer profiles. Swap sides: Swaps the File List. Synchronize with kget If you want to synchronize a local directory with an unstable &FTP; server, kget is a much better solution than simple file copying. After comparing you may right click on the result list and select Synchronize with KGet to execute the synchronization, after that the listed files will be downloaded with kget instead of &krusader;. Of course kget needs to be installed on your computer; otherwise, it will not be available. Synchronization After pressing the Synchronize button the synchronization dialog appears. Check boxes Here you confirm the copy and delete operations of the Synchronizer Right to left: copy X1 files ( Y1 bytes ) enable / disable the copy from right to left. Left to right: copy X2 files ( Y2 bytes ) enable / disable the copy from left to right. Left: delete X3 files ( Y3 bytes ) enable / disable the left side deleting. Confirm overwrites: By switching this option on, Krusader will show the rename, skip, overwrite, skip all, overwrite all dialog box, before overwriting the file. Status labels Ready: A1/A2 files, B1/B2. This line is changed at synchronizing, showing that synchronizer finished with A1 files from A2 ( B1 bytes from B2 ). Progress bar: Shows the progress on the synchronization (based on bytes). Action buttons Start: Starts synchronization. Pause / Resume: Pauses/resumes the synchronization process. Close: Closes the synchronization dialog (and stops synchronization). Parallel synchronization One can give the number of the 'quasi' threads. Can drastically increase the speed when synchronized with slow &FTP; servers. Does not affect the speed of local synchronization. Remote "compare by content" and "directory compare" is done in parallel. Remote synchronization is also done in parallel, except the mkdir tasks (mkdir must be before copy).
diff --git a/doc/user-interface.docbook b/doc/user-interface.docbook index dc5ab9bd..699aa2a3 100644 --- a/doc/user-interface.docbook +++ b/doc/user-interface.docbook @@ -1,821 +1,821 @@ User Interface User Interface OFM User Interface The OFM file manager concept contains many features that make them powerful enough to belong among the best file managers today. The interface is simple: left panel, right panel and a command line below. Because of interaction between these three items the file managing will become more efficient. If you would like to know how &krusader; works you can try it yourself, just install it on your computer. To accept an Orthodox File Manager (OFM) like &krusader;, the user needs to get used to new ideas that are actually already 20 years old. If you prefer to waste time and lose a lot of productivity, you can always continue to use one panel file managers that are based on &Windows; Explorer. &krusader; Main Window
&krusader; Main Window &krusader; Main Window
The User Interface is based on Orthodox File Manager (OFM) paradigm, a 20- year old principle that has proven its bones. It is known as simple, easy and powerful. In the following sections we will cover the basic functions of each part with the exception of: The Menu Bar which has a chapter of its own. The Bookmarks which has a chapter of its own. The Status Bar shows the properties of the current file (that is being) in focus.
Toolbars &krusader; comes with several Toolbars. Main Toolbar Toolbar Main Toolbar
Main Toolbar Main Toolbar
The &krusader; Main Toolbar is a standard &kde; toolbar that can be dragged around the application (please unlock it first by unchecking the Lock Toolbar Positions item in the right-click menu) or configured via a Right-Click menu. You can decide whether or not you want to display it each time you start &krusader; in the Konfigurator Startup page, and you can toggle its use in the current session in the Settings menu. The Main Toolbar contents can be configured from the Configure Toolbars in the Settings menu. Since various commands and options in &krusader; are context-dependent, some commands are not always available. The icon of an inappropriate or inactive action will be deactivated (grayed-out) disabling its use. Bookmarks can be placed into the Main Toolbar and the Actions Toolbar with the mouse using "drag and drop".
Job Toolbar Toolbar Job Toolbar By default, the &krusader; Job Toolbar (Queue Manager or simply the JobMan) is located at the right part of the Main Toolbar. This toolbar allows you to view the job progress, to pause and resume the job list execution and to undo the last executed job. Actions Toolbar Toolbar Actions Toolbar UserActions and Bookmarks can be "plugged" into the Actions Toolbar. To show it, use Settings Show Actions Toolbar . To add user actions, use Settings Configure Toolbars and select Actions Toolbar. You can, for example, drag the Actions Toolbar to the right side using the mouse. Location Toolbar Toolbar Location Toolbar Below the Main Toolbar there is a Location Toolbar for each panel. It shows the path the panel is currently pointing to. When entering archives the path will be displayed in the following format: "archive type:/directory/archive_filename/the directory inside the archive". This format makes the paths uniform and easy to read. The Location Toolbar supports also KIO Slaves but not all of them, some can only be used with the viewer and some can not be used. The user can enter a desired path by clicking on it. /mnt/cdrom/ settings:/ will browse and open entries of the &systemsettings;. trash:/ opens trash folder. mtp:/ uses the kio_mtp to access, upload and organize the multimedia stored on Android-based devices. And last but not least Remote Connections . You can copy-paste a &URL; in the Location Toolbar or use a middle click. With the Right-Click menu you can configure the auto-complete function. An optional Clear Location Toolbar button is also available. Quick Navigation: &Ctrl; point with mouse in the middle of &URL; of the Location Toolbar. A pop-up window shows the next &URL; location. Pressing &Ctrl; + Left Mouse Button shows the location where the mouse was pointing to. This feature allows faster navigation in a big directory tree. Panel Toolbar Toolbar Panel Toolbar Each panel has a configurable Panel Toolbar. The complete Panel Toolbar or specific buttons can be made visible or be hidden. OpenDir Button: opens the directory browser. Equals Button (=): changes the panel directory to the other panel directory. Up Button (..): changes the panel directory to the parent directory. Home Button (~): changes the panel directory to the home directory. Root Button (/): changes the panel directory to the root directory. Sync-browsing Button: when active, each directory change in the specific panel is performed in the other panel as well.
Panels The &krusader; panels are where most of the action takes place. Currently, there are two types of panels: List Panel: shows the files and directories. Sidebar: has several display modes: Preview, Tree, Quickselect, View and Disk Usage for the file or directory that has the focus. The Sidebar may be left open or closed, as desired. &krusader; is a "twin-panel" file manager so there are two panels: the "Left" panel and the "Right" panel, or a more important logical distinction, the *Active* panel and the *Inactive* panel. The active panel is the one that will accept your mouse and keyboard input. If you choose a command from the Menu-Bar or Toolbar the command will act upon the selected files/directories in the active panel. You can switch between panels using the key or by clicking on the Information or Totals labels, or by selecting any file in a panel with the mouse. As always, default startup settings can be modified with the Konfigurator Startup page, and you control the current session with the Settings menu.
List Panel List Panel
List Panel Panel List Panel This is the default panel and the one you will probably use most of the time. This panel shows the contents of one directory which can be local (part of your mounted file systems, either a native file or the files inside an archive) or remote files (accessed via &FTP;, NFS or Samba). There are two modes: Detailed View that shows the file names, file size, creation date and time and their attributes. And Brief View that shows only the file names, the number of brief columns can be changed, by right clicking on the Name header. File copy/paste/cut via clipboard between &konqueror; and &krusader; &Ctrl; C, &Ctrl; V, &Ctrl; X is supported. &Alt;&Ctrl; R toggles the List Panel between horizontal and vertical mode. The following items are available: Information Label : shows how much free space is physically available on the file system, the file system capacity and where the filesystem is mounted. If the information is not available (most often in the case of remote file system) a message will be displayed with the reason for unavailability instead of the information. The Media button on the left of the Information Label displays a list of all available media, where you can select the desired media (HDD partition, DVD, USB stick, &etc;). Pressing &Ctrl;Return when the Media menu is opened mounts the highlighted media when it is unmounted, and unmounts it when it is mounted. The context menu of a medium gives several options like Mount, Open in a new tab, &etc; The Media menu is configurable. Column Headers : here you can change the sort order of the files and directories as desired. The default sort order is by Name. The sort order can be instantly changed by clicking on one of the Column Headers. To reverse the sort order, click a second time on the Column Header. An arrow will appear on the right of the Column Header text, showing the sort direction (Up arrow = A to Z sorting, Down arrow = Z to A sorting). Default UserActions set a keybinding for the Column Headers. The default Column Headers are: Name, Ext, Size, Modified, rwx. Predefined Atomic extensions like .tar.gz are shown as one part in the Ext Column. More optional Column Headers are available with the &RMB;. The width Column Headers can be changed with the mouse by single clicking and moving the divider line to the desired width (keep the mouse button pressed). Both List Panels remember the sort order and column width. The Column Headers can be changed and saved individually to each panel. File List : displays the files and directories in the browsed directory. The search bar allows you to search for files by typing their names, or the first few characters of a file name. Pressing &Enter; or double clicking on a file will open/execute that file. You can select/unselect files using the mouse, with the Insert key or the Edit menu. Totals Label : selecting or deselecting files will change the Totals Label : selecting or deselecting files will change the Totals Label at the bottom of the panel, which displays how many files you have selected (and how much total disk space they use) as well as the total file and disk usage of the current directory (as opposed to the information label that displays information for the whole file system). If a directory contains a lot of subdirectories, it may not be possible to drop the &URL; onto the panel (on which only directories are visible), but the &URL; can be dropped onto the "Information Label" or "Totals Label" instead. When a custom filter is set, it will show the setting, example: [*.cpp]. When using the "&kde; default colors", then the selected files are shown in blue and the current file is surrounded by a fine-lined rectangle. By default an icon is shown according to the file type. Use Konfigurator Panel page to change the default configuration to meet your individual needs. Some useful List panel Key-Bindings: &Ctrl; R will refresh the panel. Focus a file or directory on the left panel, press &Ctrl; Right arrow and the right panel changes: On a file: the right panel gets the same path as the left panel. On a directory: right panel opens the directory focused on the left panel. For the right panel: press &Ctrl; Left arrow and the left panel will change. Sidebar Sidebar Sidebar (3rd Panel) This is "&krusader; is 3rd Hand" or the 3rd Panel, click on the arrow-up button on the right of the Totals Label or &Alt; Down-Arrow to open the Sidebar. This panel has several display modes which can be activated by clicking on the appropriate button. Preview Panel : gives a preview of the file that has the focus (currently selected file). Tree Panel : is used to quickly browse the local directory tree. This panel behaves like the list panel but only one directory may be selected at a time and double clicking or pressing &Enter; on a directory will open that directory in the active panel. The panels fully support drag and drop (copy, move, link) mouse actions. Quick Panel : is used to quickly select files, ⪚ enter *.png and click on the Go button. The "floppy" button stores the current selection. The "select group dialog" is also available. Viewer Panel : views a text file, views an image, &etc; A thumbnail view is generated whenever you open the Viewer Panel. Disk Usage Panel : views the Disk Usage of a directory. It is possible to change the sidebar placement using the rotation button which is located to the left of the arrow down button at the bottom part of the &krusader; tab. Each click on this button moves the sidebar on one position in the loop "bottom-left-top-right". If needed you can resize the window ⪚ to better display a picture or view a file contents. Click on the arrow down button to close the 3rd Hand Panel. Folder History Both panels remember recently visited folders. Click on the Folder history button ("watch" symbol) to open the folder history list. Here you can quickly change to previously visited folders. The currently focused folder is checked. On every new start of &krusader;, the history list is populated by the items from previous run (history is saved).
Command Line / Terminal Emulator This part of the GUI can have four modes: Command Line, Terminal Emulator, show both and show none. You can choose your mode of operation either on start-up with the Konfigurator Startup page, or for the current session with the Settings menu . Command Line
Command Line Command line
The traditional command line mode offers a single line of input to enter commands, it also features three buttons: command history (down arrow) to quick open previous used commands Useraction expander (green plus button) to add easy Useraction placeholders run in terminal mode (console icon) button with the next options: Start and Forget Display Separated Standard and Error Output Display Mixed Standard and Error Output Start in New Terminal Send to Embedded Terminal Emulator When you start typing your commands, the command line auto-completion feature will make its offers according to what you choose in the &systemsettings;. If you want to change the way auto-complete behaves in the current &krusader; session, right click on the command line and change it. To the left of the input line you can find the local path that the active panel is currently pointing to. This path is where your command will be executed. Typing cd <directory> in the command line will also cause the active panel to point to this directory. Click here to view the command line keybindings.
Terminal Emulator
Terminal Emulator Terminal emulator
The terminal emulator mode is in fact a small console and acts like one. You can change the active directory in the terminal by using the cd command. The terminal emulator will follow the active panel directory. You can drag a file from the List Panel and name of file will pasted into Terminal Emulator. If you drag multiple files, the names will pasted into Terminal Emulator. A full-screen terminal emulator can be used when configured or &Ctrl; &Shift;F toggles between normal and full screen mode. If the command line is hidden, press &Ctrl; Arrow down to focus the terminal emulator. &Ctrl; Arrow up brings you back down. &Ctrl;&Enter; and &Ctrl; &Shift; &Enter; paste the filename. You can close the emulator by typing exit . Click here to view the Terminal emulator keybindings.
Function (FN) Keys Bar
Function (FN) Keys Bar Function (FN) Keys Bar
This bar gets its name from the function (FN) keys it represents. For each button on this bar there is a corresponding function key that performs the same action. This bar is derived from the design of the first twin-panel file managers and the FN keys usually act the same way with two changes: the F2 key is used to rename files and the F9 key will open a terminal at the last local path that the active panel pointed to. Mkdir F7 can create whole directory trees on the fly ⪚ foo/bar/test. You can choose to display or not the FN keys bar when starting up &krusader; via the Konfigurator Startup page, and you can toggle its use in the current session with the Settings menu. The actions and Key-Bindings performed by the function keys are configurable since version 1.51, they will remain active with or without the FN keys bar displayed.
Folder Tabs
Folder Tabs Folder Tabs
With Folder Tabs you can quickly change to multiple folders; each panel has its own Folder Tabs. The Folder Tabs positions are saved when closing &krusader;. To switch between Folder Tabs, click them with the mouse. Squeezed Folder Tabs have tooltips to display the full path. The following Folder Tabs operations are available: To open a new tab and keep the current tab opened: Right-click on a directory and select Open in New Tab. To lock a tab, Right-click and select Lock Tab. To pin a tab, Right-click and select Pin Tab. The pinned tab is a locked tab but with temporarily changeable address. It resets to pinned address on tab reactivation. To unpin the pinned tab right-click on it and choose the Unpin Tab item. To duplicate a tab: Right-click on the folder tab and select Duplicate Current Tab, or use &Ctrl;&Alt;&Shift; N. To close a tab (except for the last tab): Right-click on the tab and select Close Current Tab, or use the &MMB;, or use the Folder tab red cross button, or use &Ctrl; W. To create a "home" tab: click on the Home button. To change tabs: &Ctrl; , or &Ctrl; .. To open the current folder in a new tab: use &Ctrl;&Alt;&Enter;. To close all other tabs, Right-click and select Close Inactive Tabs. To close duplicated tabs, Right-click and select Close Duplicated Tabs. To move tab on the other panel, drag it to the area of the other panel with the &LMB; (the mouse cursor will become an arrow pointing left or right) and drop it. You can also press &Ctrl;&Shift; O.
Buttons Several buttons for fast operations are available: Main Toolbar, Actions Toolbar, Panel Toolbar, Location Toolbar, Media, Folder history, Bookmarks, Tab, Commandline, Function keys.
diff --git a/doc/useraction-xml.docbook b/doc/useraction-xml.docbook index 664d652f..ab6322ea 100644 --- a/doc/useraction-xml.docbook +++ b/doc/useraction-xml.docbook @@ -1,215 +1,215 @@ useractions.xml This appendix gives several examples of useractions.xml files. With this you can begin to discover the power of the UserActions and start learning how to use them. Once you understand how to write UserActions you will find that &krusader; is an even more powerful, customizable tool. Please consider uploading your UserActions at store.kde.org as we are collecting them. If your UserAction is selected by us it will be shipped with &krusader; at the next release. Runs uptime <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="uptime" > <title>Up-time</title> <tooltip>tells how long since the computer has been booted</tooltip> <icon>clock</icon> <category>information</category> <description same_as="tooltip" /> <command executionmode="collect_output" >uptime</command> <defaultshortcut>Alt+Ctrl+U</defaultshortcut> </action> </KrusaderUserActions> Runs ls -l <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="ls -l" > <title>ls -l in current dir</title> <description same_as="tooltip" /> <command executionmode="collect_output" >ls -l %aPath% %oPath%</command> </action> </KrusaderUserActions> Echo Placeholder <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="test_placeholder" > <title>Echo Placeholder</title> <tooltip>Echo's the placeholder values</tooltip> <category>Testing</category> <description same_as="tooltip" /> <command executionmode="collect_output" onmultiplefiles="call_each" >echo -e "aPath %aPath()% \naCurrent: %aCurrent()%"</command> </action> </KrusaderUserActions> This useraction copies current path and filename to clipboard: <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="clipboard current" > <title>Copy to Clipboard</title> <tooltip>Copy to clipboard</tooltip> <icon>klipper</icon> <category>System</category> <description>Copy current path and filename to clipboard</description> <command>%_Clipboard("%aCurrent%")%</command> <defaultshortcut>Win+C</defaultshortcut> </action> </KrusaderUserActions> Selects .diff and *.h in the active panel: <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="test_select" > <title>Add selection *.diff and *.h</title> <category>Selection</category> <command>%aSelect("*.diff", "add")% %aSelect("*.h", "add")%</command> </action> </KrusaderUserActions> Active panel changes to mnt/floppy (bookmark): <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="mnt/floppy bookmark" > <title>Jump to /mnt/floppy</title> <category>Bookmarks</category> <command>%aGoto("/mnt/floppy", "yes")%</command> </action> </KrusaderUserActions> Select in the non-active panel, all filenames that are highlighted in the active panel: <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="asdfasdf" > <title>Select in Other Panel</title> <category>Selection</category> <description>Select all filenames that are selected in the active panel, also in the non-active panel</description> <command>%oSelect("%aList("selected", " ", "Yes")%")%</command> </action> </KrusaderUserActions> Make a backup of the current file/folder to foo.bak into the current directory: <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="backup current" > <title>Backup in Current Directory</title> <tooltip>Backup in current directory</tooltip> <icon>document-save-as</icon> <category>System</category> <description same_as="tooltip" /> <command>%_Copy("%aCurrent%", "%_Ask("new name", "%aCurrent%.bak")%")%</command> <defaultshortcut>Shift+F5</defaultshortcut> </action> </KrusaderUserActions> Opens KruSearcher: <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="search" > <title>Search Test</title> <command>%_NewSearch("Search 2")%</command> </action> </KrusaderUserActions> Sets the selected picture as wallpaper: <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="set wallpaper" > <title>Set as Wallpaper</title> <tooltip>Set as wallpaper</tooltip> <icon>image</icon> <category>Service Menu</category> <description>Set as wallpaper (scaled)</description> <command>dcop kdesktop KBackgroundIface setWallpaper "%aCurrent%" 6</command> <defaultshortcut>Win+W</defaultshortcut> </action> </KrusaderUserActions> This useraction edits a file with root permissions using kdesu: <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="edit as root" > <title>Edit as Root</title> <tooltip>Edit as root</tooltip> <icon>kwrite</icon> <category>System</category> <description>Edit a file with root permissions using kdesu</description> <command>kdesu kwrite %aCurrent%</command> <defaultshortcut>Win+F4</defaultshortcut> </action> </KrusaderUserActions> This useraction Add item(s) to Amarok playlist: <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="amarok enqueue" > <title>Enqueue in Amarok</title> <tooltip>Append selected item(s) to Amarok playlist</tooltip> <icon>amarok</icon> <category>Multimedia</category> <description same_as="tooltip" /> <command>amarok --append %aList("Selected")%</command> <defaultshortcut>Win+A</defaultshortcut> </action> </KrusaderUserActions> Opens Synchronizer: <!DOCTYPE KrusaderUserActions> <KrusaderUserActions> <action name="synchronizer" > <title>Synchronizer</title> <command>%_Sync("Sync 2")%</command> </action> </KrusaderUserActions> diff --git a/doc/useractions.docbook b/doc/useractions.docbook index d350a8d7..de3de487 100644 --- a/doc/useractions.docbook +++ b/doc/useractions.docbook @@ -1,648 +1,648 @@ UserActions UserActions With ActionMan you can set up, configure and manage UserActions. Some general settings are configured with Konfigurator. With UserActions you can perform actions on files in the panel or to access &krusader; internal functions with parameters directly using placeholders. The actions integrate seamlessly into &plasma; action system, which means that the standard Edit Toolbar and Edit Shortcut dialogs will show UserActions, too. The UserActions are stored in ~/.local/share/krusader/useractions.xml or krusader/useraction.xml in the directory which can be determined using the qtpaths --paths GenericDataLocation command. Several examples are included in the documentation. UserActions can be edited / added / imported / exported by using ActionMan. The default UserActions are stored in /usr/share/krusader/useraction-examples.xml. UserActions can appear nearly everywhere where "normal" KActions can be placed. The actions can even be placed in the menu bar, but for that the krusaderui.rc file must be edited. A few examples: Useractions Menu User Menu Actions Toolbar right click menus &etc; &krusader;'s UserActions tool is very powerful and customizable if you are familiar with writing UserActions in general. Several UserActions are provided by default. Please upload your favorite UserActions so that they become available for the &krusader; community. Thanks!
ActionMan ActionMan
Basically, UserActions are a method to call external programs with variable parameters. For example, you could have a UserAction with the following xmms to enqueue all selected items of the active panel to the current instance of xmms. Additionally, there is limited access to &krusader; internal functions requiring parameters. For example, %aPanelSize("80")% will set the width of the active panel to 80% of the &krusader; mainwindow. Since the parameter of placeholders can contain other placeholders, few scripts are possible. Managing UserActions Open Konfigurator and choose User Actions Start ActionMan, in which you can add, edit, remove, import and export UserActions. Add Action: If you add an new action, you get an empty input mask where you can enter all the properties you desire. The action will be added as soon as you press Apply. Afterwards, the name is shown in the list on the left. To edit a UserAction: Select the UserAction on the left. Then choose it if you want to edit its properties. The changes will only take effect when you press Apply. To remove a UserAction: Select the UserAction on the left and click the button. To import a UserAction: If you import some actions, they will be automatically added to your list. If there are name conflicts (the names have to be unique because these are the ID for &plasma; action system), you are asked to resolve them. For this, the list on the left will only show the actions where conflicts exist. You can now give them new names or remove them. Export Action: If you export a UserAction you have to give a filename in which to store it. If it does not exist, it will be created. If the file already contains UserActions, the action you are exporting will be added to that file. All actions you have defined are now shown in the user menu and in &plasma; dialogs for changing shortcuts and managing the toolbar. In addition, all actions available for the current item will also show up in the right click menu. Basic Properties Identifier, Title and Command line are always required, all the other properties are optional. Identifier: A unique name of the UserAction, used to identify it for &plasma; action system. Icon button: The icon for your UserAction. Category: Categories are used for a better overview. They appear as submenu items in the Useractions menu. Title: The title displayed in the menus or dialogs. Tooltip: A tooltip for your UserAction, ⪚ displayed in the toolbar on mouseover. Description: A description of what the UserAction does. This is also displayed as What's This if you &Shift; F1 click on your UserAction. Command: The command which will be executed. You can add placeholder using a GUI with the add button. Workdir: The working directory for the command which will be executed. Execution mode: Normal: Normal execution mode. Run in terminal: Runs the command in the terminal. Run in embedded terminal emulator: Runs the command in the embedded terminal. Collect output: Collects the output of the executed program in a &GUI; window. Separate standard error: When "Collect output" is used the stdout and stderr are separately collected. Command accepts: Local files (no URLs): Tells the placeholder it should return local addresses. URLs (remote and local): Tells the placeholder it should return &URL;s. Default shortcut: Configures a default shortcut for the UserAction. Enabled: if checked, the useraction is shown in the User Menu, otherwise the useraction will be hidden. Command-line syntax Basically, everything you type in the command line will get executed (if you type "ls -l", "ls -l" gets executed). You have the possibility to get a character string from &krusader; which represents the current state of the panel. This is done using placeholders. A placeholder begins with a percent-sign ('%') and is followed by a panel indicator ('a' for the active, 'o' for the other, 'l' for the left and 'r' for the right panel. If the placeholder does not need a panel to operate on, you have to indicate this by an underscore ('_')). Then comes the name of the placeholder (see the list below), which may get some parameters enclosed in quotes. Finally, again the percent sign. This sounds very complicated, so let's make an example: '%aList("Selected")%' is replaced by a list of all selected items in the active panel. So a command like 'xmms --enqueue %aList("All", " ", "", "*.mp3")%' will execute xmms with a list of all .mp3s in the current panel, separated by a single blank. Currently, these placeholders are implemented: Path - replaced by the panels path Parameter (optional): Automatic escape spaces. Default: yes Count - replaced by the number of <first parameter> Parameter: Which items; either "All", "Selected", "Files" or "Dirs" Filter - replaced by the panel's filter mask Current - replaced by the current item Parameter (optional): Omit the current path. Default: no Parameter (optional): Automatic escape spaces. Default: yes List - replaced by a list of all <first parameter> Parameter: Which items; either "All", "Selected", "Files" or "Dirs" Parameter (optional): Separator between the items. Default: " " Parameter (optional): Omit the current path. Default: no Parameter (optional): Filtermask (for all but "Selected"). Default: * Parameter (optional): Automatic escape spaces. Default: yes Select - manipulates the selection in a panel Parameter: Filtermask Parameter (optional): manipulate in which way; either "Set", "Add" or "Remove". Default: "Set" Goto - changes the panels' path to <first parameter> Parameter: A relative or absolute path, or an URL Parameter (optional): Open the location in a new tab. Default: no Ask - asks the user for some text and is replaced by the answer Parameter: The Question Parameter (optional): A default answer Parameter (optional): A caption for the question box Clipboard - manipulates the clipboard Parameter: The text that should go to the clipboard (you may want to use "%aCurrent%" here) Parameter (optional): Append the text to the current content of the clipboard with this separator Copy - copies a file, useful for quick, local backups Parameter: What should be copied Parameter: Where it should be copied Sync - opens the Synchronizer with a given profile Parameter: A profile for the Synchronizer NewSearch - opens the search windows with a given profile Parameter: A profile for the search module Profile - loads a given panel profile Parameter: A panel profile Each - splits the commandline into a list. These commands are executed one after another. Parameter: A list item (all, all files, all dirs, all selected). Move - move from source to destination. Parameter: A source Parameter: A destination PanelSize - change the ratio between the two panels. Parameter (optional): A integer value, ⪚, 80 makes the active panel use 80% of &krusader;'s width (height in vertical mode), omitting the parameter means 50%. Ask - cancel the execution. Parameter (optional): A string for the cancel question. ListFile - is replaced by path/file name of a temporary file containing a list of items Parameter: path/filename ColSort - set the sorting on a column of a specific panel. Parameter: Column: Either "Name", "Ext", "Type", "Size", "Modified", "Perms", "rwx", "Owner" and "Group" Parameter: Sort sequence: Either "Toggle", "Asc", "Desc" View - set the view mode. Parameter: View mode: Either "generic", "text", "hex" Parameter: Window Mode: Either "tab", "window" A GUI-based helper for placeholder adding is provided. Spaces in Path, Current and List are by default, automatically escaped. There is one more important thing to know: All placeholders that interact with &krusader; internal functions are called at expand time (meaning directly when the placeholders are replaced). External programs are called at execution time (meaning after all placeholders are replaced). Advanced Properties Here you can configure where your command should be visible (for the right click menu). In addition, it is possible to change the command executed and confirm it separately. You can also set a user under which the command should be executed. Configures if the action is valid for a Protocol, Path, MIME type or File name. Tweaking the command line before being executed. Set a different user for the execution (this has no effect in &krusader; internal functions)
diff --git a/doc/vfs.docbook b/doc/vfs.docbook index 2859aed4..abb3a1a8 100644 --- a/doc/vfs.docbook +++ b/doc/vfs.docbook @@ -1,103 +1,103 @@ Virtual file systems (VFS) VFS A basic OFM feature is VFS, this an abstracted layer over all kinds of archived information (ZIP files, &FTP; servers, TAR archives, NFS filesystems, SAMBA shares, ISO CD/DVD images, RPM catalogs, &etc;), which allows the user to access all the information in these divergent types of filesystems transparently - just like entering an ordinary sub-directory. &krusader; supports several virtual file systems: Remote connections VFS: provides the capability of working with a remote connection session (&FTP;, NFS, Samba, FISH, SFTP) like with local filesystems. It is perfect for complex remote operations and almost as powerful as most standalone GUI remote clients. Archive VFS: allows to browse archives in VFS as it was a directory (ace, arj, bzip2, deb, gzip, iso, lha, rar, rpm, tar, zip and 7-zip). Search VFS: Feed to listbox places the search results in VFS. Synchronizer VFS: places the synchronizer results in VFS. Actions you perform on the files in VFS are performed on the 'real' files. You do not just delete files from the VFS - you delete them from your hard drive. Limitations: you cannot create directories inside a VFS. It is possible to keep the directory structure when doing a copy from a virtual folder to a non virtual folder, by selecting the "Keep virtual directory structure" check box of the copy dialog. Imagine the following virtual folder: $ file:/home/myhome/mydir1/myfile1 $ file:/home/myhome/mydir1/myfile2 $ file:/home/myhome/mydir2/myfile3 Then do the following steps: go to the virtual folder and select the files select a destination folder (non virtual!) press F5-> copy dialog appears Check Keep virtual directory structure Select /home/myhome/ for base &URL; Start copy by pressing OK The result will be: $ destinationdir/mydir1/myfile1 $ destinationdir/mydir1/myfile2 $ destinationdir/mydir2/myfile3 diff --git a/doc/viewer-editor.docbook b/doc/viewer-editor.docbook index 4b062af4..01625cf1 100644 --- a/doc/viewer-editor.docbook +++ b/doc/viewer-editor.docbook @@ -1,2453 +1,2453 @@ KrViewer: &krusader;'s internal viewer-editor Viewer Editor Pressing &Enter; on a selected file opens the file with the associated application. The editor-viewer is tabbed, configure it on the Konfigurator General page Viewer To view a file as fast as possible according to its type, just put it under the cursor and press F3. &krusader;'s internal viewer is actually a part of &konqueror;, which can basically view every file type viewable by &konqueror; (⪚ display pictures, play music, show the content of an archive). This is called the 'Generic viewer', for which you need to configure the MIME types. The viewer works as follows: It tries to view the file with the 'Generic viewer'. When the file type (MIME type) cannot be determined or when a file (⪚ a binary) does not have an associated action, it disables the 'generic viewer'. The file is treated as a text file, which is the 'Text viewer'. The user can change in the KrViewer menu between: 'Generic viewer' (if available), 'Text viewer' and 'Hex viewer'. You can configure the default viewer mode on the Konfigurator General page . &Shift; F3 views a &URL; on demand, ⪚: man:/ views the manpages. man:/krusader views manpage of &krusader;. info:/ views the infopages. https://krusader.org views a webpage. ftp://ftp.kde.org/pub/kde/ views a &FTP; server. sftp://sftp.foo/ views a secure &FTP; server. file:/home/frank views the home directory of Frank. tar:/home/frank/archive.tar.gz/ opens content browser window for the tar.gz archive. KrViewer can have the following menus: File, Edit, View, Settings and Krviewer, depending on which file type is viewed. They will be discussed in the Editor section. The internal viewer can follow links on &HTML; pages. With &Ctrl;&Shift; E the viewer can start &krusader;'s internal editor (which is basically the same application). The embedded viewer is not written by us, it is supplied via &kf5; and KIO Slaves. We cannot and do not want to change it, using these libraries reduces the amount of written code. So we do not have to reinvent the wheel ;-) Editor Editor &krusader;'s internal editor has almost everything that you can expect of an editor. Editing is similar to viewing but with the F4 key. The default editor is &krusader;'s internal editor. You can change the default editor in the Konfigurator General page, if you prefer to use an external editor. When you use &krusader;'s internal editor for the first time, it is recommended to look at each section of the Settings menu, and configure it the way you want it. Menu overview There are different menus and menu items in simple and advanced mode. The advanced mode is enabled by checking the option Enable power user mode (&kde; 3 mode) on the Appearance page in &kappname;s settings. The File Menu &Ctrl;S File Save This saves the current document. If there has already been a save of the document then this will overwrite the previously saved file without asking for the user's consent. If it is the first save of a new document the save as dialog (described below) will be invoked. File Save As... This allows a document to be saved with a new file name. F5 File Reload Reloads the active file from disk. This command is useful if another program or process has changed the file while you have it open in &kappname;. &Ctrl;P File Print... Opens a simple print dialog allowing the user to specify what, where, and how to print. File Export as HTML Export your file in &HTML; format so your document can be viewed as a web page. This item is only displayed when the plugin Exporter is loaded. The Edit Menu &Ctrl;Z Edit Undo This is used to eliminate or reverse the most recent user action or operation. &Ctrl;&Shift;Z EditRedo This will reverse the most recent change (if any) made using Undo &Ctrl;X EditCut This command deletes the current selection and places it on the clipboard. The clipboard is a feature that works invisibly to provide a way to transfer data between applications. &Ctrl;C EditCopy This copies the currently selected text to the clipboard so that it may be pasted elsewhere. The clipboard is a feature that works invisibly to provide a way to transfer data between applications. &Ctrl;V EditPaste This will insert the contents of the clipboard at the cursor position. The clipboard is feature that works invisibly to provide a way to transfer data between applications. EditCopy as HTML This copies the currently selected text to the clipboard as &HTML;. This item is only displayed when the plugin Exporter is loaded. &Ctrl;A EditSelect All This will select the entire document. This could be very useful for copying the entire file to another application. &Ctrl;&Shift;A EditDeselect Deselects the selected text in the editor if any. &Ctrl;&Shift;B EditBlock Selection Mode Toggles Selection Mode. When the Selection Mode is BLOCK, you can make vertical selections, ⪚ select column 5 to 10 in lines 9 to 15. The status bar shows the current state of the Selection Mode, either BLOCK or LINE. Meta&Ctrl;V EditVI input Mode Switch to a vi-like, modal editing mode. This mode supports the most used commands and motions from vim's normal and visual mode and has an optional vi mode statusbar. This status bar shows commands while they are being entered, output from commands and the current mode.The behavior of this mode can be configured in the Vi Input Mode section of the Editing page in &kappname;'s settings dialog. Ins EditOverwrite Mode Toggles the Insert/Overwrite modes. When the mode is INS, you insert characters where the cursor is. When the mode is OVR, writing characters will replace the current characters if your cursor is positioned before any character. The status bar shows the current state of the Overwrite Mode, either INS or OVR. &Ctrl;F EditFind... This opens the incremental search bar at the bottom of the editor window. On the left side of the bar is a button with an icon to close the bar, followed by a small text box for entering the search pattern. When you start entering characters of your search pattern, the search starts immediately. If there is a match in the text this is highlighted and the background color of the entry field changes to light green. If the search pattern does not match any string in the text, this is indicated by a light red background color of the entry field and Not found is displayed at the right side of the bar. Use the Next or Previous button to jump to the next or previous match in the document. You can choose whether the search should be case sensitive. Selecting Match Case will limit finds to entries that match the case (upper or lower) of each of the characters in the search pattern. Click on the button with a green arrow icon at the right side of the incremental search bar to switch to the power search and replace bar. F3 EditFind Next This repeats the last find operation, if any, without calling the incremental search bar. &Shift;F3 Edit Find Previous This repeats the last find operation, if any, without calling the incremental search bar, and searching backwards instead of forwards through the document. &Ctrl;R EditReplace... This command opens the power search and replace bar. On the upper left side of the bar is a button with an icon to close the bar, followed by a small text box for entering the search pattern. You can control the search mode by selecting Plain text, Whole words, Escape sequences or Regular expression from the drop down box. If Escape sequences or Regular expression are selected, the Add... menuitem at the bottom of the context menu of the text boxes will be enabled and allows you to add escape sequences or regular expression items to the search or replace pattern from predefined lists. Use the Next or Previous button to jump to the next or previous match in the document. Enter the text to replace with in the text box labeled Replace and click the Replace button to replace only the highlighted text or the Replace All button to replace the search text in the whole document. You can modify the search and replace behavior by selecting different options at the bottom of the bar. Selecting Match Case will limit finds to entries that match the case (upper or lower) of each of the characters in the search pattern. Selection Only will search and replace within the current selection only. The Find All button highlights all matches in the document and shows the number of found matches in a small popup. Click on the button with a green arrow icon at the right side of the power search and replace bar to switch to the incremental search bar. &Ctrl;H EditFind Selected Finds next occurrence of selected text. &Ctrl;&Shift;H EditFind Selected Backwards Finds previous occurrence of selected text. &Ctrl;G Edit Go to Line... This opens the goto line bar at the bottom of the window which is used to have the cursor jump to a particular line (specified by number) in the document. The line number may be entered directly into the text box or graphically by clicking on the up or down arrow spin controls at the side of the text box. The little up arrow will increase the line number and the down arrow decrease it. Close the bar with a click on the button with an icon on the left side of the bar. The View Menu The View menu allows you to manage settings specific to the active editor, and to manage frames. View New Window Create another window containing the current document. All changes to the document in one window are reflected in the other window and vice versa. F7 View Switch to Command Line Displays the Katepart command line at the bottom of the window. In the command line, type help to get help and help list to get a list of commands. ViewShow the Javascript Console This opens a tool view that allows you to run Javascript code interactively. For more information, see Extending &kate; with Scripts View Schema Select a font schema. F10 View Dynamic Word Wrap The text lines will be wrapped at the view border on the screen. View Dynamic Word Wrap Indicators Choose when and how the dynamic word wrap indicators should be displayed. This is only available if the Dynamic Word Wrap option is checked. View Show Static Word Wrap Marker If this option is checked, a vertical line will be drawn at the word wrap column as defined in the Settings Configure Editor... in the Editing tab. Please note that the word wrap marker is only drawn if you use a fixed pitch font. F6 View Show Icon Border This is a toggle item. Setting it on checked will make the Icon Border visible in the left side of the active editor, and vice versa. The Icon Border indicates the positions of the marked lines in the editor. F11 View Show Line Numbers This is a toggle Item. Setting it on checked will make a pane displaying the line numbers of the document visible in the left border of the active editor, and vice versa. View Show Scrollbar Marks If this option is checked, the view will show marks on the vertical scrollbar. The marks are equivalent to the marks on the Icon Border. F9 View Show Folding Markers If this option is checked, the marks for code folding will be shown. View Code Folding These options pertain to code folding: F9 Show Folding Markers Toggles the display of the folding marker pane in the left side of the view. Fold Current Node Collapse the region that contains the cursor. Unfold Current Node Expand the region that contains the cursor. &Ctrl;&Shift;- Fold Toplevel Nodes Collapse all toplevel regions in the document. Click on the right pointing triangle to expand all toplevel regions. Enlarge Font This increases the display font size. Shrink Font This decreases the display font size. The Bookmarks Menu &Ctrl;B Bookmarks Set Bookmark Sets or removes a bookmark in the current line of the active document. (If it is there, it is removed, otherwise one is set). Bookmarks Clear All Bookmarks This command will remove all the markers from the document as well as the list of markers which is appended at the bottom of this menu item. &Alt; PgUp BookmarksPrevious This will move the cursor to beginning of the first above line with a bookmark. The menuitem text will include the line number and the first piece of text on the line. This item is only available when there is a bookmark in a line above the cursor. &Alt; PgDown BookmarksNext This will move the cursor to beginning of the next line with a bookmark. The menuitem text will include the line number and the first piece of text on the line. This item is only available when there is a bookmark in a line below the cursor. At the bottom of this menu, a list of bookmarks appears if any bookmarks are available for this window. The Tools Menu Tools Read Only Mode Set the current document to Read Only mode. This prevents any text addition and any changes in the document formatting. Tools Mode Choose the filetype scheme you prefer for the active document. This overwrites the global filetype mode set in Settings Configure Editor... in the Filetypes tab for your current document only. Tools Highlighting Choose the Highlighting scheme you prefer for the active document. This overwrites the global highlighting mode set in Settings Configure Editor... for your current document only. Tools Indentation Choose the style of indentation you want for your active document. This overwrites the global indentation mode set in Settings Configure Editor... for your current document only. Tools Encoding You can overwrite the default encoding set in Settings Configure Editor... in the Open/Save page to set a different encoding for your current document. The encoding you set here will be only valid for your current document. Tools End of Line Choose your preferred end of line mode for your active document. This overwrites the global end of line mode set in Settings Configure Editor... for your current document only. Tools Add Byte Mark Order (BOM) Checking this action you can explicitly add a byte order mark for unicode encoded documents. The byte order mark (BOM) is a Unicode character used to signal the endianness (byte order) of a text file or stream, for more information see Byte Order Mark. &Ctrl;Space Tools Invoke Code Completion Manually invoke command completion, usually by using a shortcut bound to this action. Tools Word Completion Reuse Word Below (&Ctrl; 9) and Reuse Word Above (&Ctrl; 8) complete the currently typed text by searching for similar words backward or forward from the current cursor position. Shell Completion pops up a completion box with matching entries. &Ctrl;&Shift;O Tools Automatic Spell Checking When Automatic Spell Checking is enabled, wrong text is underlined in the document on-the-fly. ToolsSpelling... This initiates the spellchecking program - a program designed to help the user catch and correct any spelling errors. Clicking on this entry will start the checker and bring up the speller dialog box through which the user can control the process. There are four settings lined up vertically in the center of the dialog with their corresponding labels just to the left. Starting at the top they are: Unknown word: Here, the spellchecker indicates the word currently under consideration. This happens when the checker encounters a word not in its dictionary - a file containing a list of correctly spelled words against which it compares each word in the editor. Replace with: If the checker has any similar words in its dictionary the first one will be listed here. The user can accept the suggestion, type in his or her own correction, or choose a different suggestion from the next box. Language: If you have installed multiple dictionaries, here you can select which dictionary/language should be used. On the right side of the dialog box are 6 buttons that allow the user to control the spellcheck process. They are: Add to Dictionary Pressing this button adds the Unknown word to the checker's dictionary. This means that in the future the checker will always consider this word to be correctly spelled. Suggest The checker may list here a number of possible replacements for the word under consideration. Clicking on any one of the suggestions will cause that word to be entered in the Replace with box, above. Replace This button has the checker replace the word under consideration in the document with the word in the Replace with box. Replace All This button causes the checker to replace not only the current Unknown word: but to automatically make the same substitution for any other occurrences of this Unknown word in the document. Ignore Activating this button will have the checker move on without making any changes. Ignore All This button tells the checker to do nothing with the current Unknown word: and to pass over any other instances of the same word. This only applies to the current spellcheck run. If the checker is run again later it will stop on this same word. Three more buttons are located horizontally along the bottom of the spellcheck dialog. They are: Help This invokes the &kde; help system with the help page for this dialog. Finished This button ends the spellcheck process, and returns to the document. Cancel This button cancels the spellcheck process, all modifications are reverted, and you will return to your document. Tools Spelling (from cursor)... This initiates the spellchecking program but it starts where your cursor is instead of at the beginning of the document. Tools Spellcheck Selection... Spellchecks the current selection. Tools Change Dictionary Displays a drop down box with all available dictionaries for spellchecking at the bottom of the editor window. This allows easy switching of the spellcheck dictionary ⪚ for automatic spellcheck of text in different languages. ToolsClean Indentation This cleans the indentation for the current selection or for the line the cursor is currently in. Cleaning the indentation ensures that all your selected text follows the indentation mode you choose. Tools Align Causes a realign of the current line or selected lines using the indentation mode and indentation settings in the document. &Ctrl;D Tools Comment This adds one space to the beginning of the line where the text cursor is located or to the beginning of any selected lines. &Ctrl;&Shift;D Tools Uncomment This removes one space (if any exist) from the beginning of the line where the text cursor is located or from the beginning of any selected lines. &Ctrl;U Tools Uppercase Put the selected text or the letter after the cursor in uppercase. &Ctrl;&Shift;U Tools Lowercase Put the selected text or the letter after the cursor in lowercase. &Ctrl;&Alt;U Tools Capitalize Capitalize the selected text or the current word. &Ctrl;J Tools Join Lines Joins the selected lines, or the current line and the line below with one white space character as a separator. Leading/trailing white space on joined lines is removed in the affected ends. Tools Apply Word Wrap Apply static word wrapping on all the document. That means that a new line of text will automatically start when the current line exceeds the length specified by the Wrap words at option in the Editing tab in SettingsConfigure Editor... If the plugin Insert File is enabled, you will find here an additional menu item Insert File.... The Settings Menu Settings Configure Editor... This menu item opens a dialog whereby several different settings may be adjusted. The KrViewer Menu &Ctrl;&Shift; G KrViewer Generic viewer &krusader;'s internal viewer is actually part of &konqueror;, which can basically view every file type viewable by &konqueror; (⪚ display pictures, play music, show the content of an archive). &Ctrl;&Shift; T KrViewer Text viewer View the file in text mode. &Ctrl;&Shift; H KrViewer Hex viewer View the file in hex mode. &Ctrl;&Shift; L KrViewer Lister Fast text/hex viewer for huge filesizes, done by Krusader lister. &Ctrl;&Shift; E KrViewer Text editor Edit the file in text mode. &Ctrl;&Shift; D KrViewer Detach Tab Detach browsing Tab and open in a new window. &Ctrl; Q KrViewer Quit Closes &krusader;'s viewer/editor window. The Editor Component Configuration This group contains all pages related to the editor component of &kappname;. Most of the settings here are defaults, they can be overridden by defining a filetype, by Document Variables or by changing them per document during an editing session. Appearance General Dynamic Word Wrap If this option is checked, the text lines will be wrapped at the view border on the screen. Dynamic word wrap indicators (if applicable) Choose when the Dynamic word wrap indicators should be displayed, either Off, Follow Line Numbers or Always on. Align dynamically wrapped lines to indentation depth: Enables the start of dynamically wrapped lines to be aligned vertically to the indentation level of the first line. This can help to make code and markup more readable.Additionally, this allows you to set a maximum width of the screen, as a percentage, after which dynamically wrapped lines will no longer be vertically aligned. For example, at 50%, lines whose indentation levels are deeper than 50% of the width of the screen will not have vertical alignment applied to subsequent wrapped lines. Whitespace Highlighting Highlight tabulators The editor will display a » symbol to indicate the presence of a tab in the text. Highlight trailing spaces The editor will display dots to indicate the presence of extra whitespace at the end of lines. Advanced Show indentation lines If this is checked, the editor will display vertical lines to help identifying indent lines. Highlight range between selected brackets If this is enabled, the range between the selected matching brackets will be highlighted. Borders Borders Show folding markers (if available) If this option is checked, the current view will display marks for code folding, if code folding is available. Show icon border If this is checked, you will see an icon border on the left hand side. The icon border shows bookmark signs for instance. Show line numbers If this is checked, you will see line numbers on the left hand side. Show scrollbar marks If this option is checked the current view will show marks on the vertical scrollbar. These marks will for instance show bookmarks. Sort Bookmarks Menu By creation Each new bookmark will be added to the bottom, independently from where it is placed in the document. By position The bookmarks will be ordered by the line numbers they are placed at. Fonts & Colors This section of the dialog lets you configure all fonts and colors in any color scheme you have, as well creating new schemes or deleting existing ones. Each scheme has settings for colors, fonts and normal and highlight text styles. &kappname; will preselect the currently active scheme for you, if you want to work on a different scheme start by selecting that from the Schema combobox. With the New and Delete button you can create a new scheme or delete existing ones. At the bottom of the page you can select the Default schema for &kappname;. Colors Text Area Background Normal text This is the default background for the editor area, it will be the dominant color on the editor area. Selected text This is the background for selected text. The default is the global selection color, as set in your &plasma; color preferences. Current line Set the color for the current line. Setting this a bit different from the Normal text background helps to keep focus on the current line. Bookmark This combobox lets you set overlay colors for various mark types. The color is mixed into the background color of a marked line, so that a line with more marks or a marked line that is current has a background that is a mix of more colors. The mark colors are also used if you enable display of scrollbar marks. Additional Elements Left border background This color is used for the marks, line numbers and folding marker borders in the left side of the editor view when they are displayed. Line numbers This color is used to draw the line numbers on the left side of the view when displayed. Bracket highlight This color is used to draw the background of matching brackets. Word wrap markers This color is used to draw a pattern to the left of dynamically wrapped lines when those are aligned vertically, as well as for the static word wrap marker. Tab and space markers This color is used to draw white space indicators when enabled. Spelling mistake line sets the color of the line that is used to indicate spelling mistakes. Fonts Here you can choose the font for the schema. You can choose from any font available on your system, and set a default size. A sample text displays at the bottom of the dialog, so you can see the effect of your choices. Normal Text Styles The normal text styles are inherited by the highlight text styles, allowing the editor to present text in a very consistent way, for example comment text is using the same style in almost all of the text formats that &kappname; can highlight. The name in the list of styles is using the style configured for the item, providing you with an immediate preview when configuring a style. Each style lets you select common attributes as well as foreground and background colors. To unset a background color, rightclick to use the context menu. Highlighting Text Styles Here you can edit the text styles used by a specific highlight definition. The editor preselects the highlight used by your current document. To work on a different highlight, select one in the Highlight combobox above the style list. The name in the list of styles is using the style configured for the item, providing you with an immediate preview when configuring a style. Each style lets you select common attributes as well as foreground and background colors. To unset a background color, rightclick to use the context menu. In addition you can see if a style is equal to the default style used for the item, and set it to that if not. You will notice that many highlights contain other highlights represented by groups in the style list. For example most highlights import the Alert highlight, and many source code formats imports the Doxygen highlight. Editing colors in those groups only affects the styles when used in the edited highlight format. Editing General Static Word Wrap Word wrap is a feature that causes the editor to automatically start a new line of text and move (wrap) the cursor to the beginning of that new line. &kappname; will automatically start a new line of text when the current line reaches the length specified by the Wrap Words At: option. Enable static word wrap Turns static word wrap on or off. Show static word wrap markers (if applicable) If this option is checked, a vertical line will be drawn at the word wrap column as defined in the Settings Configure Editor... in the Editing tab. Please note that the word wrap marker is only drawn if you use a fixed pitch font. Wrap words at: If the Enable static word wrap option is selected this entry determines the length (in characters) at which the editor will automatically start a new line. Misc Remove trailing spaces while editing &kappname; will automatically eliminate extra spaces at the ends of lines of text. Auto brackets When the user types a left bracket ([, (, or {) &kappname; automatically enters the right bracket (}, ), or ]) to the right of the cursor. Copy/Cut the current line if no selection If this option is enabled and the text selection is empty, copy and cut action are performed for the line of text at the actual cursor position. Allow scrolling past the end of the document This option lets you scroll past the end of the document. This can be used to vertically centre the bottom of the document, or put it on top of the current view. Cursor & Selection Text Cursor Movement Smart home and smart end When selected, pressing the home key will cause the cursor to skip white space and go to the start of a line's text. Wrap cursor When on, moving the insertion cursor using the Left and Right keys will go on to previous/next line at beginning/end of the line, similar to most editors.When off, the insertion cursor cannot be moved left of the line start, but it can be moved off the line end, which can be very handy for programmers. When this option is chosen, moving the cursor with the arrow keys off the end of a line (to the right) causes it to jump down to the beginning of the next line. Likewise when the cursor is moved past the beginning of a line (to the left) it jumps up to the end of the preceding line. When this option is not selected, moving the cursor right past the end of a line merely causes it to continue horizontally in the same line and trying to move it left past the beginning does nothing. PageUp/PageDown moves cursor This option changes the behavior of the cursor when the user presses the Page Up or Page Down key. If unselected the text cursor will maintain its relative position within the visible text in &kappname; as new text becomes visible as a result of the operation. So if the cursor is in the middle of the visible text when the operation occurs it will remain there (except when one reaches the beginning or end.) With this option selected, the first key press will cause the cursor to move to either the top or bottom of the visible text as a new page of text is displayed. Autocenter cursor (lines): Sets the number of lines to maintain visible above and below the cursor when possible. Text Selection Mode Normal Selections will be overwritten by typed text and will be lost on cursor movement. Persistent Selections will stay even after cursor movement and typing. Indentation Default Indentation mode: Select the automatic indentation mode you want to use as default. It is strongly recommended to use None or Normal here, and use filetype configurations to set other indentation modes for text formats like C/C++ code or &XML;. Indent using Tabulators When this is enabled the editor will insert tabulator characters when you press the key or use automatic indentation. Spaces When this is enabled the editor will insert a calculated number of spaces according to the position in the text and the setting when you press the key or use automatic indentation. Tabulators and Spaces When this is enabled, the editor will insert spaces as describe above when indenting or pressing at the beginning of a line, but insert tabulators when the key is pressed in the middle or end of a line. Tab width: This configures the number of spaces that are displayed in place of a tabulator character. Indentation width: The indentation width is the number of spaces which is used to indent a line. If configured to indent using tabulators, a tabulator character is inserted if the indentation is divisible by the tab width. Indentation Properties Keep Extra Spaces If this option is disabled, changing the indentation level aligns a line to a multiple of the width specified in Indentation width. Adjust indentation of code pasted from the clipboard If this option is selected, pasted code from the clipboard is indented. Triggering the Undo action removes the indentation. Indentation Actions Backspace key in leading blank space unindents If this option is selected, the &Backspace; key decreases the indentation level if the cursor is located in the leading blank space of a line. Tab key action (if no selection exists) If you want to align the current line in the current code block like in emacs, make a shortcut to the action Align. Always advance to the next tab position If this option is selected, the key always inserts white space so that the next tab position is reached. If the option Insert spaces instead of tabulators on the General tab in the Editing page is enabled, spaces are inserted; otherwise, a single tabulator is inserted. Always increase indentation level If this option is selected, the key always indents the current line by the number of character positions specified in Indentation width. Increase indentation level if in leading blank space If this option is selected, the key either indents the current line or advances to the next tab position. If the insertion point is at or before the first non-space character in the line, or if there is a selection, the current line is indented by the number of character positions specified in Indentation width. If the insertion point is located after the first non-space character in the line and there is no selection, white space is inserted so that the next tab position is reached: if the option Insert spaces instead of tabulators on the General tab in the Editing page is enabled, spaces are inserted; otherwise, a single tabulator is inserted. Auto Completion General Enable auto completion If enabled, a word completion box automatically pops up during typing showing a list of text entries to complete the current text under the cursor. Minimal word length to complete While typing text, the word completion searches for words in the document starting with the already typed text. This option configures the minimal amount of characters that are needed to make the word completion active and pop up a completion box. Vi Input Mode General Use VI input mode When selected, the vi input mode will be enabled when opening a new view. You can still toggle the vi input mode on/off for a particular view in the Edit menu. Let Vi commands override Kate shortcuts When selected, Vi commands will override &kappname;'s built-in commands. For example: &Ctrl;R will redo, and override the standard action (showing the search and replace dialog). Hide the Vi mode status bar By default, an extra status bar will be used when the Vi input mode is enabled. This status bar shows commands while they are being typed and messages/errors produced by Vi commands. Checking this option will hide this extra status line. Key Mapping Key mapping is used to change the meaning of typed keys. This allows you to move commands to other keys or make special keypresses for doing a series of commands. Example: F2 -> I-- &Esc; This will prepend I-- to a line when pressing F2. Spellcheck These configuration options are described in the documentation for the &systemsettings; module Spell Checker. Open/Save General File Format Encoding: This defines the standard encoding to use to open/save files, if not changed in the open/save dialog or by using a command line option. Encoding Detection Select an item from the drop down box, either to disable autodetection or use Universal to enable autodetection for all encodings. But as this may probably only detect utf-8/utf-16, selecting a region will use custom heuristics for better results. If neither the encoding chosen as standard above, nor the encoding specified in the open/save dialog, nor the encoding specified on command line match the content of the file, this detection will be run. Fallback Encoding This defines the fallback encoding to try for opening files if neither the encoding chosen as standard above, nor the encoding specified in the open/ save dialog, nor the encoding specified on command line match the content of the file. Before this is used, an attempt will be made to determine the encoding to use by looking for a byte order marker at start of file: if one is found, the right unicode encoding will be chosen; otherwise encoding detection will run, if both fail fallback encoding will be tried. End of line: Choose your preferred end of line mode for your active document. You have the choice between &UNIX;, DOS/&Windows; or Macintosh. Automatic end of line detection Check this if you want the editor to autodetect the end of line type. The first found end of line type will be used for the whole file. Enable byte order marker The byte order mark is a special sequence at the beginning of unicode encoded documents. It helps editors to open text documents with the correct unicode encoding. For more information see Byte Order Mark. Automatic Cleanups on Load/Save Remove trailing spaces The editor will automatically eliminate extra spaces at the ends of lines of text while loading/saving the file. Advanced Folder Config File Search depth for config file: The editor will search the given number of folder levels upwards for &kappname; config file and load the settings line from it. Further information about these hidden folder config files you find in the document variables section. Backup on Save Backing up on save will cause &kappname; to copy the disk file to <prefix><filename><suffix> before saving changes. The suffix defaults to ~ and prefix is empty by default. Local files Check this if you want backups of local files when saving. Remote files Check this if you want backups of remote files when saving. Prefix Enter the prefix to prepend to the backup file names. Suffix Enter the suffix to add to the backup file names. Disable swap file syncing &kappname; is able to recover (most of) what was written after last save in case of a crash or power failure. A swap file (.swp.<filename>) is created after the first editing action on a document. If the user doesn’t save the changes and &kappname; crashes, the swap file remains on the disk. When opening a file, &kappname; checks if there is a swap file for the document and if it is, it asks the user whether he wants to recover the lost data or not. The user has the possibility to view the differences between the original file and the recovered one, too. The swap file is deleted after every save and on normal exit. &kappname; syncs the swap files on the disk every 15 seconds, but only if they have changed since the last sync. The user can disable the swap files syncing if he wants, by checking the Disable swap file syncing box, but this can lead to more data loss. Modes & Filetypes This page allows you to override the default configuration for documents of specified MIME types. When the editor loads a document, it will try if it matches the file masks or MIME types for one of the defined filetypes, and if so apply the variables defined. If more filetypes match, the one with the highest priority will be used. Filetype: The filetype with the highest priority is the one displayed in the first drop down box. If more filetypes were found, they are also listed. New This is used to create a new filetype. After you click on this button, the fields below get empty and you can fill the properties you want for the new filetype. Delete To remove an existing filetype, select it from the drop down box and press the Delete button. Properties of current filetype The filetype with the highest priority is the one displayed in the first drop down box. If more filetypes were found, they are also listed. Name: The name of the filetype will be the text of the corresponding menu item. This name is displayed in the ToolsFiletypes Section: The section name is used to organize the file types in menus. This is also used in the ToolsFiletypes menu. Variables: This string allows you to configure &kappname;'s settings for the files selected by this MIME type using &kappname; variables. You can set almost any configuration option, such as highlight, indent-mode, etc. Press Edit to see a list of all available variables and their descriptions. Select the checkbox on the left to enable a particular variable and then set the value of the variable on the right. Some variables provide a drop-down box to select possible values from while others require you to enter a valid value manually. For complete information on these variables, see Configuring with Document Variables. Highlighting: If you create a new file type, this drop down box allows you to select a filetype for highlighting. Indentation Mode: The drop down box specifies the indentation mode for new documents. File extensions: The wildcards mask allows you to select files by filename. A typical mask uses an asterisk and the file extension, for example *.txt; *.text. The string is a semicolon-separated list of masks. MIME types: Displays a wizard that helps you easily select MIME types. Priority: Sets a priority for this file type. If more than one file type selects the same file, the one with the highest priority will be used. Download Highlighting Files... Click this button to download new or updated syntax highlight descriptions from the &kappname; website. Extensions The Plugins tab lists all available plugins and you can check those you want to use. Click on the Information button to open the About dialog of this plugin. Once a configurable plugin is checked, the Configure button is enabled and you can click it in order to configure the highlighted plugin. Editor Component Plugins AutoBrace The autobrace plugin supersedes the &kappname; internal Auto Brackets feature. It automatically inserts a closing brace } at the beginning of the next line after ending a line with an opening one { and pressing the &Enter; key. Data Tools Enables data tools like thesaurus and spell check (if installed). Data tools are only available when text is selected, or when the right mouse button is clicked over a word. If no data tools are offered even when text is selected, you need to install them. If this plugin is enabled and data tools are installed, additional items appear at the end of the context menu. Exporter This command allows you to export the current document with all highlighting information into a HTML document using FileExport as HTML. Additionally you can use Edit Copy as HTML to copy the currently selected text as HTML to the system clipboard. IconInserter The icon inserter plugin is only of use for &kde; developers: It adds an item Insert KIcon-Code into the context menu of the editor. If activated, the &plasma;'s icon chooser opens (showing application icons, action icons &etc;..). If you click on it, the file name without file extension will be inserted as text. Useful only for setting icons via &kf5; through the KIcon() class. Insane (not ZEN) HTML coding (light edition) A plugin, which does zen-coding like selector completion. For more information see Zen Coding. Insert File This plugin allows you to insert any readable file at the cursor position. If enabled, the Tools menu has an additional menu item Insert File. diff --git a/iso/iso.cpp b/iso/iso.cpp index 99ad1689..ee237f82 100644 --- a/iso/iso.cpp +++ b/iso/iso.cpp @@ -1,527 +1,527 @@ /***************************************************************************** * Copyright (C) 2000 David Faure * * Copyright (C) 2002 Szombathelyi György * * Copyright (C) 2003 Leo Savernik * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is heavily based on ktar from kdelibs * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "iso.h" #include // QtCore #include #include #include #include #include #include #include #include "libisofs/iso_fs.h" #include "kiso.h" #include "kisofile.h" #include "kisodirectory.h" #include "../krusader/compat.h" using namespace KIO; extern "C" { int Q_DECL_EXPORT kdemain(int argc, char **argv) { //qDebug() << "Starting " << getpid() << endl; if (argc != 4) { fprintf(stderr, "Usage: kio_iso protocol domain-socket1 domain-socket2\n"); exit(-1); } kio_isoProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); //qDebug() << "Done" << endl; return 0; } } // extern "C" typedef struct { char magic[8]; char uncompressed_len[4]; unsigned char header_size; unsigned char block_size; char reserved[2]; /* Reserved for future use, MBZ */ } compressed_file_header; static const unsigned char zisofs_magic[8] = { 0x37, 0xE4, 0x53, 0x96, 0xC9, 0xDB, 0xD6, 0x07 }; kio_isoProtocol::kio_isoProtocol(const QByteArray &pool, const QByteArray &app) : SlaveBase("iso", pool, app) { //qDebug() << "kio_isoProtocol::kio_isoProtocol" << endl; m_isoFile = nullptr; } kio_isoProtocol::~kio_isoProtocol() { delete m_isoFile; } bool kio_isoProtocol::checkNewFile(QString fullPath, QString & path, int startsec) { //qDebug() << "kio_isoProtocol::checkNewFile " << fullPath << " startsec: " << //startsec << endl; // Are we already looking at that file ? if (m_isoFile && startsec == m_isoFile->startSec() && m_isoFile->fileName() == fullPath.left(m_isoFile->fileName().length())) { // Has it changed ? QT_STATBUF statbuf; if (QT_STAT(QFile::encodeName(m_isoFile->fileName()), &statbuf) == 0) { if (m_mtime == statbuf.st_mtime) { path = fullPath.mid(m_isoFile->fileName().length()); //qDebug() << "kio_isoProtocol::checkNewFile returning " << path << endl; if(path.endsWith(DIR_SEPARATOR_CHAR)) { path.chop(1); } if(path.isEmpty()) { path = DIR_SEPARATOR_CHAR; } return true; } } } //qDebug() << "Need to open a new file" << endl; // Close previous file if (m_isoFile) { m_isoFile->close(); delete m_isoFile; m_isoFile = nullptr; } // Find where the iso file is in the full path int pos = 0; QString isoFile; path.clear(); int len = fullPath.length(); if (len != 0 && fullPath[ len - 1 ] != DIR_SEPARATOR_CHAR) fullPath += DIR_SEPARATOR_CHAR; //qDebug() << "the full path is " << fullPath << endl; while ((pos = fullPath.indexOf(DIR_SEPARATOR_CHAR, pos + 1)) != -1) { QString tryPath = fullPath.left(pos); //qDebug() << fullPath << " trying " << tryPath << endl; QT_STATBUF statbuf; if (QT_LSTAT(QFile::encodeName(tryPath), &statbuf) == 0 && !S_ISDIR(statbuf.st_mode)) { bool isFile = true; if (S_ISLNK(statbuf.st_mode)) { char symDest[256]; memset(symDest, 0, 256); int endOfName = readlink(QFile::encodeName(tryPath), symDest, 256); if (endOfName != -1) { if (QDir(QString::fromLocal8Bit(symDest)).exists()) isFile = false; } } if (isFile) { isoFile = tryPath; m_mtime = statbuf.st_mtime; m_mode = statbuf.st_mode; path = fullPath.mid(pos + 1); //qDebug() << "fullPath=" << fullPath << " path=" << path << endl; if(path.endsWith(DIR_SEPARATOR_CHAR)) { path.chop(1); } if(path.isEmpty()) { path = DIR_SEPARATOR_CHAR; } //qDebug() << "Found. isoFile=" << isoFile << " path=" << path << endl; break; } } } if (isoFile.isEmpty()) { //qDebug() << "kio_isoProtocol::checkNewFile: not found" << endl; return false; } // Open new file //qDebug() << "Opening KIso on " << isoFile << endl; m_isoFile = new KIso(isoFile); m_isoFile->setStartSec(startsec); if (!m_isoFile->open(QIODevice::ReadOnly)) { //qDebug() << "Opening " << isoFile << " failed." << endl; delete m_isoFile; m_isoFile = nullptr; return false; } return true; } void kio_isoProtocol::createUDSEntry(const KArchiveEntry * isoEntry, UDSEntry & entry) { entry.clear(); entry.UDS_ENTRY_INSERT(UDSEntry::UDS_NAME, isoEntry->name()); entry.UDS_ENTRY_INSERT(UDSEntry::UDS_FILE_TYPE, isoEntry->permissions() & S_IFMT); // keep file type only entry.UDS_ENTRY_INSERT(UDSEntry::UDS_ACCESS, isoEntry->permissions() & 07777); // keep permissions only if (isoEntry->isFile()) { long long si = ((KIsoFile *)isoEntry)->realsize(); if (!si) si = ((KIsoFile *)isoEntry)->size(); entry.UDS_ENTRY_INSERT(UDSEntry::UDS_SIZE, si); } else { entry.UDS_ENTRY_INSERT(UDSEntry::UDS_SIZE, 0L); } entry.UDS_ENTRY_INSERT(UDSEntry::UDS_USER, isoEntry->user()); entry.UDS_ENTRY_INSERT(UDSEntry::UDS_GROUP, isoEntry->group()); entry.UDS_ENTRY_INSERT((uint)UDSEntry::UDS_MODIFICATION_TIME, isoEntry->date().toTime_t()); entry.UDS_ENTRY_INSERT(UDSEntry::UDS_ACCESS_TIME, isoEntry->isFile() ? ((KIsoFile *)isoEntry)->adate() : ((KIsoDirectory *)isoEntry)->adate()); entry.UDS_ENTRY_INSERT(UDSEntry::UDS_CREATION_TIME, isoEntry->isFile() ? ((KIsoFile *)isoEntry)->cdate() : ((KIsoDirectory *)isoEntry)->cdate()); entry.UDS_ENTRY_INSERT(UDSEntry::UDS_LINK_DEST, isoEntry->symLinkTarget()); } void kio_isoProtocol::listDir(const QUrl &url) { //qDebug() << "kio_isoProtocol::listDir " << url.url() << endl; QString path; if (!checkNewFile(getPath(url), path, url.hasFragment() ? url.fragment(QUrl::FullyDecoded).toInt() : -1)) { QByteArray _path(QFile::encodeName(getPath(url))); //qDebug() << "Checking (stat) on " << _path << endl; QT_STATBUF buff; if (QT_STAT(_path.data(), &buff) == -1 || !S_ISDIR(buff.st_mode)) { error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } // It's a real dir -> redirect QUrl redir; redir.setPath(getPath(url)); if (url.hasFragment()) redir.setFragment(url.fragment(QUrl::FullyDecoded)); //qDebug() << "Ok, redirection to " << redir.url() << endl; redir.setScheme("file"); redirection(redir); finished(); // And let go of the iso file - for people who want to unmount a cdrom after that delete m_isoFile; m_isoFile = nullptr; return; } if (path.isEmpty()) { QUrl redir(QStringLiteral("iso:/")); //qDebug() << "url.path()==" << getPath(url) << endl; if (url.hasFragment()) redir.setFragment(url.fragment(QUrl::FullyDecoded)); redir.setPath(getPath(url) + QString::fromLatin1(DIR_SEPARATOR)); //qDebug() << "kio_isoProtocol::listDir: redirection " << redir.url() << endl; redir.setScheme("file"); redirection(redir); finished(); return; } //qDebug() << "checkNewFile done" << endl; const KArchiveDirectory* root = m_isoFile->directory(); const KArchiveDirectory* dir; if (!path.isEmpty() && path != DIR_SEPARATOR) { //qDebug() << QString("Looking for entry %1").arg(path) << endl; const KArchiveEntry* e = root->entry(path); if (!e) { error(KIO::ERR_DOES_NOT_EXIST, path); return; } if (! e->isDirectory()) { error(KIO::ERR_IS_FILE, path); return; } dir = (KArchiveDirectory*)e; } else { dir = root; } QStringList l = dir->entries(); totalSize(l.count()); UDSEntry entry; QStringList::Iterator it = l.begin(); for (; it != l.end(); ++it) { //qDebug() << (*it) << endl; const KArchiveEntry* isoEntry = dir->entry((*it)); createUDSEntry(isoEntry, entry); listEntry(entry); } finished(); //qDebug() << "kio_isoProtocol::listDir done" << endl; } void kio_isoProtocol::stat(const QUrl &url) { QString path; UDSEntry entry; //qDebug() << "kio_isoProtocol::stat " << url.url() << endl; if (!checkNewFile(getPath(url), path, url.hasFragment() ? url.fragment(QUrl::FullyDecoded).toInt() : -1)) { // We may be looking at a real directory - this happens // when pressing up after being in the root of an archive QByteArray _path(QFile::encodeName(getPath(url))); //qDebug() << "kio_isoProtocol::stat (stat) on " << _path << endl; QT_STATBUF buff; if (QT_STAT(_path.data(), &buff) == -1 || !S_ISDIR(buff.st_mode)) { //qDebug() << "isdir=" << S_ISDIR(buff.st_mode) << " errno=" << strerror(errno) << endl; error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } // Real directory. Return just enough information for KRun to work entry.UDS_ENTRY_INSERT(UDSEntry::UDS_NAME, url.fileName()); //qDebug() << "kio_isoProtocol::stat returning name=" << url.fileName() << endl; entry.UDS_ENTRY_INSERT(UDSEntry::UDS_FILE_TYPE, buff.st_mode & S_IFMT); statEntry(entry); finished(); // And let go of the iso file - for people who want to unmount a cdrom after that delete m_isoFile; m_isoFile = nullptr; return; } const KArchiveDirectory* root = m_isoFile->directory(); const KArchiveEntry* isoEntry; if (path.isEmpty()) { path = QString::fromLatin1(DIR_SEPARATOR); isoEntry = root; } else { isoEntry = root->entry(path); } if (!isoEntry) { error(KIO::ERR_DOES_NOT_EXIST, path); return; } createUDSEntry(isoEntry, entry); statEntry(entry); finished(); } void kio_isoProtocol::getFile(const KIsoFile *isoFileEntry, const QString &path) { unsigned long long size, pos = 0; bool mime = false, zlib = false; QByteArray fileData, pointer_block, inbuf, outbuf; char *pptr = nullptr; compressed_file_header *hdr; int block_shift; unsigned long nblocks; unsigned long fullsize = 0, block_size = 0, block_size2 = 0; size_t ptrblock_bytes; unsigned long cstart, cend, csize; uLong bytes; size = isoFileEntry->realsize(); if (size >= sizeof(compressed_file_header)) zlib = true; if (!size) size = isoFileEntry->size(); totalSize(size); if (!size) mimeType("application/x-zerosize"); if (size && !m_isoFile->device()->isOpen()) { m_isoFile->device()->open(QIODevice::ReadOnly); // seek(0) ensures integrity with the QIODevice's built-in buffer // see bug #372023 for details m_isoFile->device()->seek(0); } if (zlib) { fileData = isoFileEntry->dataAt(0, sizeof(compressed_file_header)); if (fileData.size() == sizeof(compressed_file_header) && !memcmp(fileData.data(), zisofs_magic, sizeof(zisofs_magic))) { hdr = (compressed_file_header*) fileData.data(); block_shift = hdr->block_size; block_size = 1UL << block_shift; block_size2 = block_size << 1; fullsize = isonum_731(hdr->uncompressed_len); nblocks = (fullsize + block_size - 1) >> block_shift; ptrblock_bytes = (nblocks + 1) * 4; pointer_block = isoFileEntry->dataAt(hdr->header_size << 2, ptrblock_bytes); if ((unsigned long)pointer_block.size() == ptrblock_bytes) { inbuf.resize(block_size2); if (inbuf.size()) { outbuf.resize(block_size); if (outbuf.size()) pptr = pointer_block.data(); else { error(KIO::ERR_COULD_NOT_READ, path); return; } } else { error(KIO::ERR_COULD_NOT_READ, path); return; } } else { error(KIO::ERR_COULD_NOT_READ, path); return; } } else { zlib = false; } } while (pos < size) { if (zlib) { cstart = isonum_731(pptr); pptr += 4; cend = isonum_731(pptr); csize = cend - cstart; if (csize == 0) { outbuf.fill(0, -1); } else { if (csize > block_size2) { //err = EX_DATAERR; break; } inbuf = isoFileEntry->dataAt(cstart, csize); if ((unsigned long)inbuf.size() != csize) { break; } bytes = block_size; // Max output buffer size if ((uncompress((Bytef*) outbuf.data(), &bytes, (Bytef*) inbuf.data(), csize)) != Z_OK) { break; } } if (((fullsize > block_size) && (bytes != block_size)) || ((fullsize <= block_size) && (bytes < fullsize))) { break; } if (bytes > fullsize) bytes = fullsize; fileData = outbuf; fileData.resize(bytes); fullsize -= bytes; } else { fileData = isoFileEntry->dataAt(pos, 65536); if (fileData.size() == 0) break; } if (!mime) { QMimeDatabase db; QMimeType mt = db.mimeTypeForFileNameAndData(path, fileData); if (mt.isValid()) { //qDebug() << "Emitting mimetype " << mt.name() << endl; mimeType(mt.name()); mime = true; } } data(fileData); pos += fileData.size(); processedSize(pos); } if (pos != size) { error(KIO::ERR_COULD_NOT_READ, path); return; } fileData.resize(0); data(fileData); processedSize(pos); finished(); } void kio_isoProtocol::get(const QUrl &url) { //qDebug() << "kio_isoProtocol::get" << url.url() << endl; QString path; if (!checkNewFile(getPath(url), path, url.hasFragment() ? url.fragment(QUrl::FullyDecoded).toInt() : -1)) { error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } const KArchiveDirectory* root = m_isoFile->directory(); const KArchiveEntry* isoEntry = root->entry(path); if (!isoEntry) { error(KIO::ERR_DOES_NOT_EXIST, path); return; } if (isoEntry->isDirectory()) { error(KIO::ERR_IS_DIRECTORY, path); return; } const auto* isoFileEntry = dynamic_cast(isoEntry); if (!isoEntry->symLinkTarget().isEmpty()) { //qDebug() << "Redirection to " << isoEntry->symLinkTarget() << endl; QUrl realURL = QUrl(url).resolved(QUrl(isoEntry->symLinkTarget())); //qDebug() << "realURL= " << realURL.url() << endl; realURL.setScheme("file"); redirection(realURL); finished(); return; } getFile(isoFileEntry, path); if (m_isoFile->device()->isOpen()) m_isoFile->device()->close(); } QString kio_isoProtocol::getPath(const QUrl &url) { QString path = url.path(); REPLACE_DIR_SEP2(path); #ifdef Q_WS_WIN if (path.startsWith(DIR_SEPARATOR)) { int p = 1; while (p < path.length() && path[ p ] == DIR_SEPARATOR_CHAR) p++; /* /C:/Folder */ if (p + 2 <= path.length() && path[ p ].isLetter() && path[ p + 1 ] == ':') { path = path.mid(p); } } #endif return path; } diff --git a/iso/iso.h b/iso/iso.h index 2fe8aa65..411e5a4b 100644 --- a/iso/iso.h +++ b/iso/iso.h @@ -1,59 +1,59 @@ /***************************************************************************** * Copyright (C) 2000 David Faure * * Copyright (C) 2002 Szombathelyi György * * Copyright (C) 2003 Leo Savernik * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is heavily based on ktar from kdelibs * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef ISO_H #define ISO_H // QtCore #include #include #include #include "kisofile.h" class KIso; class kio_isoProtocol : public KIO::SlaveBase { public: kio_isoProtocol(const QByteArray &pool, const QByteArray &app); virtual ~kio_isoProtocol(); virtual void listDir(const QUrl &url) Q_DECL_OVERRIDE; virtual void stat(const QUrl &url) Q_DECL_OVERRIDE; virtual void get(const QUrl &url) Q_DECL_OVERRIDE; protected: void getFile(const KIsoFile *isoFileEntry, const QString &path); void createUDSEntry(const KArchiveEntry * isoEntry, KIO::UDSEntry & entry); bool checkNewFile(QString fullPath, QString & path, int startsec); QString getPath(const QUrl &url); KIso * m_isoFile; time_t m_mtime; int m_mode; }; #endif diff --git a/iso/kiso.cpp b/iso/kiso.cpp index 545c7e44..e9c408df 100644 --- a/iso/kiso.cpp +++ b/iso/kiso.cpp @@ -1,521 +1,521 @@ /***************************************************************************** * Copyright (C) 2000 David Faure * * Copyright (C) 2002 Szombathelyi György * * Copyright (C) 2003 Leo Savernik * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is heavily based on ktar from kdelibs * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "kiso.h" // QtCore #include #include #include #include #include #include #include #include #include #include #include "libisofs/isofs.h" #include "qfilehack.h" #ifdef Q_OS_LINUX #undef __STRICT_ANSI__ #include #define __STRICT_ANSI__ #include #include #endif //////////////////////////////////////////////////////////////////////// /////////////////////////// KIso /////////////////////////////////// //////////////////////////////////////////////////////////////////////// /** * puts the track layout of the device 'fname' into 'tracks' * tracks structure: start sector, track number, ... * tracks should be 100*2 entry long (this is the maximum in the CD-ROM standard) * currently it's linux only, porters are welcome */ static int getTracks(const char *fname, int *tracks) { KRFUNC; int ret = 0; memset(tracks, 0, 200*sizeof(int)); #ifdef Q_OS_LINUX int fd, i; struct cdrom_tochdr tochead; struct cdrom_tocentry tocentry; //qDebug() << "getTracks open:" << fname << endl; fd = QT_OPEN(fname, O_RDONLY | O_NONBLOCK); if (fd > 0) { if (ioctl(fd, CDROMREADTOCHDR, &tochead) != -1) { // qDebug() << "getTracks first track:" << tochead.cdth_trk0 // << " last track " << tochead.cdth_trk1 << endl; for (i = tochead.cdth_trk0;i <= tochead.cdth_trk1;++i) { if (ret > 99) break; memset(&tocentry, 0, sizeof(struct cdrom_tocentry)); tocentry.cdte_track = i; tocentry.cdte_format = CDROM_LBA; if (ioctl(fd, CDROMREADTOCENTRY, &tocentry) < 0) break; // qDebug() << "getTracks got track " << i << " starting at: " << // tocentry.cdte_addr.lba << endl; if ((tocentry.cdte_ctrl & 0x4) == 0x4) { tracks[ret<<1] = tocentry.cdte_addr.lba; tracks[(ret<<1)+1] = i; ret++; } } } close(fd); } #endif return ret; } class KIso::KIsoPrivate { public: KIsoPrivate() = default; QStringList dirList; }; KIso::KIso(const QString& filename, const QString & _mimetype) : KArchive(nullptr) { KRFUNC; KRDEBUG("Starting KIso: " << filename << " - type: " << _mimetype); m_startsec = -1; m_filename = filename; d = new KIsoPrivate; QString mimetype(_mimetype); bool forced = true; if (mimetype.isEmpty()) { QMimeDatabase db; QMimeType mt = db.mimeTypeForFile(filename, QMimeDatabase::MatchContent); if (mt.isValid()) mimetype = mt.name(); //qDebug() << "KIso::KIso mimetype=" << mimetype << endl; // Don't move to prepareDevice - the other constructor theoretically allows ANY filter if (mimetype == "application/x-tgz" || mimetype == "application/x-targz" || // the latter is deprecated but might still be around mimetype == "application/x-webarchive") // that's a gzipped tar file, so ask for gzip filter mimetype = "application/x-gzip"; else if (mimetype == "application/x-tbz") // that's a bzipped2 tar file, so ask for bz2 filter mimetype = "application/x-bzip2"; else { // Something else. Check if it's not really gzip though (e.g. for KOffice docs) QFile file(filename); if (file.open(QIODevice::ReadOnly)) { char firstByte; char secondByte; char thirdByte; file.getChar(&firstByte); file.getChar(&secondByte); file.getChar(&thirdByte); if (firstByte == 0037 && secondByte == (char)0213) mimetype = "application/x-gzip"; else if (firstByte == 'B' && secondByte == 'Z' && thirdByte == 'h') mimetype = "application/x-bzip2"; else if (firstByte == 'P' && secondByte == 'K' && thirdByte == 3) { char fourthByte; file.getChar(&fourthByte); if (fourthByte == 4) mimetype = "application/x-zip"; } } } forced = false; } prepareDevice(filename, mimetype, forced); } void KIso::prepareDevice(const QString & filename, const QString & mimetype, bool forced) { KRFUNC; KRDEBUG("Preparing: " << filename << " - type: " << mimetype << " - using the force: " << forced); /* 'hack' for Qt's false assumption that only S_ISREG is seekable */ if ("inode/blockdevice" == mimetype) setDevice(new QFileHack(filename)); else { if ("application/x-gzip" == mimetype || "application/x-bzip2" == mimetype) forced = true; KCompressionDevice *device; if(mimetype.isEmpty()) { device = new KFilterDev(filename); } else { device = new KCompressionDevice(filename, KFilterDev::compressionTypeForMimeType(mimetype)); } if (device->compressionType() == KCompressionDevice::None && forced) { delete device; } else { setDevice(device); } } } KIso::KIso(QIODevice * dev) : KArchive(dev) { d = new KIsoPrivate; } KIso::~KIso() { // mjarrett: Closes to prevent ~KArchive from aborting w/o device if (isOpen()) close(); if (!m_filename.isEmpty()) delete device(); // we created it ourselves delete d; } /* callback function for libisofs */ static int readf(char *buf, unsigned int start, unsigned int len, void *udata) { KRFUNC; QIODevice* dev = (static_cast(udata))->device(); // seek(0) ensures integrity with the QIODevice's built-in buffer // see bug #372023 for details dev->seek(0); if (dev->seek((qint64)start << (qint64)11)) { if ((dev->read(buf, len << 11u)) != -1) return (len); } //qDebug() << "KIso::ReadRequest failed start: " << start << " len: " << len << endl; return -1; } /* callback function for libisofs */ static int mycallb(struct iso_directory_record *idr, void *udata) { KRFUNC; auto *iso = static_cast(udata); QString path, user, group, symlink; int i; int access; int time, cdate, adate; rr_entry rr; bool special = false; KArchiveEntry *entry = nullptr, *oldentry = nullptr; char z_algo[2], z_params[2]; long long z_size = 0; if ((idr->flags[0] & 1) && !iso->showhidden) return 0; if (iso->level) { if (isonum_711(idr->name_len) == 1) { switch (idr->name[0]) { case 0: path += ("."); special = true; break; case 1: path += (".."); special = true; break; } } if (iso->showrr && ParseRR(idr, &rr) > 0) { if (!special) path = rr.name; symlink = rr.sl; access = rr.mode; time = rr.t_mtime; adate = rr.t_atime; cdate = rr.t_ctime; user.setNum(rr.uid); group.setNum(rr.gid); z_algo[0] = rr.z_algo[0];z_algo[1] = rr.z_algo[1]; z_params[0] = rr.z_params[0];z_params[1] = rr.z_params[1]; z_size = rr.z_size; } else { access = iso->dirent->permissions() & ~S_IFMT; adate = cdate = time = isodate_915(idr->date, 0); user = iso->dirent->user(); group = iso->dirent->group(); if (idr->flags[0] & 2) access |= S_IFDIR; else access |= S_IFREG; if (!special) { if (iso->joliet) { for (i = 0;i < (isonum_711(idr->name_len) - 1);i += 2) { void *p = &(idr->name[i]); QChar ch(be2me_16(*(ushort *)p)); if (ch == ';') break; path += ch; } } else { for (i = 0;i < isonum_711(idr->name_len);++i) { if (idr->name[i] == ';') break; if (idr->name[i]) path += (idr->name[i]); } } if (path.endsWith('.')) path.resize(path.length() - 1); } } if (iso->showrr) FreeRR(&rr); if (idr->flags[0] & 2) { entry = new KIsoDirectory(iso, path, access | S_IFDIR, time, adate, cdate, user, group, symlink); } else { entry = new KIsoFile(iso, path, access, time, adate, cdate, user, group, symlink, (long long)(isonum_733(idr->extent)) << (long long)11, isonum_733(idr->size)); if (z_size)(dynamic_cast (entry))->setZF(z_algo, z_params, z_size); } iso->dirent->addEntry(entry); } if ((idr->flags[0] & 2) && (iso->level == 0 || !special)) { if (iso->level) { oldentry = iso->dirent; iso->dirent = dynamic_cast(entry); } iso->level++; ProcessDir(&readf, isonum_733(idr->extent), isonum_733(idr->size), &mycallb, udata); iso->level--; if (iso->level) iso->dirent = dynamic_cast(oldentry); } return 0; } void KIso::addBoot(struct el_torito_boot_descriptor* bootdesc) { KRFUNC; int i; long long size; boot_head boot; boot_entry *be; QString path; KIsoFile *entry; path = "Catalog"; entry = new KIsoFile(this, path, dirent->permissions() & ~S_IFDIR, dirent->date(), dirent->adate(), dirent->cdate(), dirent->user(), dirent->group(), QString(), (long long)isonum_731(bootdesc->boot_catalog) << (long long)11, (long long)2048); dirent->addEntry(entry); if (!ReadBootTable(&readf, isonum_731(bootdesc->boot_catalog), &boot, this)) { i = 1; be = boot.defentry; while (be) { size = BootImageSize(isonum_711(be->data.d_e.media), isonum_721(be->data.d_e.seccount)); path = "Default Image"; if (i > 1) path += " (" + QString::number(i) + ')'; entry = new KIsoFile(this, path, dirent->permissions() & ~S_IFDIR, dirent->date(), dirent->adate(), dirent->cdate(), dirent->user(), dirent->group(), QString(), (long long)isonum_731(be->data.d_e.start) << (long long)11, size << (long long)9); dirent->addEntry(entry); be = be->next; i++; } FreeBootTable(&boot); } } void KIso::readParams() { KRFUNC; KConfig *config; config = new KConfig("kio_isorc"); KConfigGroup group(config, QString()); showhidden = group.readEntry("showhidden", false); showrr = group.readEntry("showrr", true); delete config; } bool KIso::openArchive(QIODevice::OpenMode mode) { KRFUNC; iso_vol_desc *desc; QString path, uid, gid; QT_STATBUF buf; int tracks[2*100], trackno = 0, i, access, c_b, c_i, c_j; KArchiveDirectory *root; struct iso_directory_record* idr; struct el_torito_boot_descriptor* bootdesc; if (mode == QIODevice::WriteOnly) return false; readParams(); d->dirList.clear(); tracks[0] = 0; if (m_startsec > 0) tracks[0] = m_startsec; //qDebug() << " m_startsec: " << m_startsec << endl; /* We'll use the permission and user/group of the 'host' file except * in Rock Ridge, where the permissions are stored on the file system */ if (QT_STAT(m_filename.toLocal8Bit(), &buf) < 0) { /* defaults, if stat fails */ memset(&buf, 0, sizeof(struct stat)); buf.st_mode = 0777; } else { /* If it's a block device, try to query the track layout (for multisession) */ if (m_startsec == -1 && S_ISBLK(buf.st_mode)) trackno = getTracks(m_filename.toLatin1(), (int*) & tracks); } uid.setNum(buf.st_uid); gid.setNum(buf.st_gid); access = buf.st_mode & ~S_IFMT; //qDebug() << "KIso::openArchive number of tracks: " << trackno << endl; if (trackno == 0) trackno = 1; for (i = 0;i < trackno;++i) { c_b = 1;c_i = 1;c_j = 1; root = rootDir(); if (trackno > 1) { path.clear(); QTextStream(&path) << "Track " << tracks[(i<<1)+1]; root = new KIsoDirectory(this, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); rootDir()->addEntry(root); } desc = ReadISO9660(&readf, tracks[i<<1], this); if (!desc) { //qDebug() << "KIso::openArchive no volume descriptors" << endl; continue; } while (desc) { switch (isonum_711(desc->data.type)) { case ISO_VD_BOOT: bootdesc = (struct el_torito_boot_descriptor*) & (desc->data); if (!memcmp(EL_TORITO_ID, bootdesc->system_id, ISODCL(8, 39))) { path = "El Torito Boot"; if (c_b > 1) path += " (" + QString::number(c_b) + ')'; dirent = new KIsoDirectory(this, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); root->addEntry(dirent); addBoot(bootdesc); c_b++; } break; case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: idr = (struct iso_directory_record*) & (((struct iso_primary_descriptor*) & desc->data)->root_directory_record); joliet = JolietLevel(&desc->data); if (joliet) { QTextStream(&path) << "Joliet level " << joliet; if (c_j > 1) path += " (" + QString::number(c_j) + ')'; } else { path = "ISO9660"; if (c_i > 1) path += " (" + QString::number(c_i) + ')'; } dirent = new KIsoDirectory(this, path, access | S_IFDIR, buf.st_mtime, buf.st_atime, buf.st_ctime, uid, gid, QString()); root->addEntry(dirent); level = 0; mycallb(idr, this); if (joliet) c_j++; else c_i++; break; } desc = desc->next; } free(desc); } device()->close(); return true; } bool KIso::closeArchive() { KRFUNC; d->dirList.clear(); return true; } bool KIso::writeDir(const QString&, const QString&, const QString&, mode_t, time_t, time_t, time_t) { return false; } bool KIso::prepareWriting(const QString&, const QString&, const QString&, qint64, mode_t, time_t, time_t, time_t) { return false; } bool KIso::finishWriting(qint64) { return false; } bool KIso::writeSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, time_t, time_t, time_t) { return false; } bool KIso::doWriteDir(const QString&, const QString&, const QString&, mode_t, const QDateTime&, const QDateTime &, const QDateTime &) { return false; } bool KIso::doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime&, const QDateTime&, const QDateTime&) { return false; } bool KIso::doPrepareWriting(const QString& , const QString& , const QString& , qint64, mode_t, const QDateTime&, const QDateTime&, const QDateTime&) { return false; } bool KIso::doFinishWriting(qint64) { return false; } void KIso::virtual_hook(int id, void* data) { KArchive::virtual_hook(id, data); } diff --git a/iso/kiso.h b/iso/kiso.h index fb277b95..64f6e139 100644 --- a/iso/kiso.h +++ b/iso/kiso.h @@ -1,124 +1,124 @@ /***************************************************************************** * Copyright (C) 2000 David Faure * * Copyright (C) 2002 Szombathelyi György * * Copyright (C) 2003 Leo Savernik * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is heavily based on ktar from kdelibs * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KISO_H #define KISO_H // QtCore #include #include #include #include "../krusader/krdebuglogger.h" #include "kisofile.h" #include "kisodirectory.h" /** * @short A class for reading (optionally compressed) iso9660 files. */ class KIso : public KArchive { public: /** * Creates an instance that operates on the given filename. * using the compression filter associated to given mimetype. * * @param filename is a local path (e.g. "/home/weis/myfile.tgz") * @param mimetype "application/x-gzip" or "application/x-bzip2" * Do not use application/x-tgz or so. Only the compression layer ! * If the mimetype is omitted, it will be determined from the filename. */ explicit KIso(const QString& filename, const QString & mimetype = QString()); /** * Creates an instance that operates on the given device. * The device can be compressed (KFilterDev) or not (QFile, etc.). * WARNING: don't assume that giving a QFile here will decompress the file, * in case it's compressed! */ explicit KIso(QIODevice * dev); /** * If the .iso is still opened, then it will be * closed automatically by the destructor. */ virtual ~KIso(); /** * The name of the os file, as passed to the constructor * Null if you used the QIODevice constructor. */ QString fileName() { return m_filename; } bool writeDir(const QString& , const QString& , const QString&, mode_t, time_t, time_t, time_t); bool writeSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, time_t, time_t, time_t); bool prepareWriting(const QString& , const QString& , const QString& , qint64, mode_t, time_t, time_t, time_t); bool finishWriting(qint64); void setStartSec(int startsec) { m_startsec = startsec; } int startSec() { return m_startsec; } bool showhidden, showrr; int level, joliet; KIsoDirectory *dirent; protected: /** * Opens the archive for reading. * Parses the directory listing of the archive * and creates the KArchiveDirectory/KArchiveFile entries. * */ void readParams(); virtual bool openArchive(QIODevice::OpenMode mode) Q_DECL_OVERRIDE; virtual bool closeArchive() Q_DECL_OVERRIDE; virtual bool doWriteDir(const QString&, const QString&, const QString&, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) Q_DECL_OVERRIDE; virtual bool doWriteSymLink(const QString &, const QString &, const QString &, const QString &, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) Q_DECL_OVERRIDE; virtual bool doPrepareWriting(const QString& , const QString& , const QString& , qint64, mode_t, const QDateTime &, const QDateTime &, const QDateTime &) Q_DECL_OVERRIDE; virtual bool doFinishWriting(qint64) Q_DECL_OVERRIDE; private: /** * @internal */ void addBoot(struct el_torito_boot_descriptor* bootdesc); void prepareDevice(const QString & filename, const QString & mimetype, bool forced = false); int m_startsec; QString m_filename; protected: virtual void virtual_hook(int id, void* data) Q_DECL_OVERRIDE; private: class KIsoPrivate; KIsoPrivate * d; }; #endif diff --git a/iso/kisodirectory.cpp b/iso/kisodirectory.cpp index 37e5fac9..10414eee 100644 --- a/iso/kisodirectory.cpp +++ b/iso/kisodirectory.cpp @@ -1,35 +1,35 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "kisodirectory.h" KIsoDirectory::KIsoDirectory(KArchive* archive, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink) : KArchiveDirectory(archive, name, access, QDateTime::fromTime_t(date), user, group, symlink) { m_adate = adate; m_cdate = cdate; } KIsoDirectory::~KIsoDirectory() = default; diff --git a/iso/kisodirectory.h b/iso/kisodirectory.h index 617daa1a..7a63970d 100644 --- a/iso/kisodirectory.h +++ b/iso/kisodirectory.h @@ -1,49 +1,49 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KISODIRECTORY_H #define KISODIRECTORY_H // QtCore #include #include class KIsoDirectory : public KArchiveDirectory { public: KIsoDirectory(KArchive* archive, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink); ~KIsoDirectory(); int date() const { return m_date; } int adate() const { return m_adate; } int cdate() const { return m_cdate; } private: int m_date, m_adate, m_cdate; }; #endif diff --git a/iso/kisofile.cpp b/iso/kisofile.cpp index 584ea93d..cedace25 100644 --- a/iso/kisofile.cpp +++ b/iso/kisofile.cpp @@ -1,60 +1,60 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "kisofile.h" KIsoFile::KIsoFile(KArchive* archive, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink, long long pos, long long size) : KArchiveFile(archive, name, access, QDateTime::fromTime_t(date), user, group, symlink, pos, size) { m_adate = adate; m_cdate = cdate; m_algo[0] = 0;m_algo[1] = 0;m_parms[0] = 0;m_parms[1] = 0;m_realsize = 0; } KIsoFile::~KIsoFile() = default; void KIsoFile::setZF(char algo[2], char parms[2], long long realsize) { m_algo[0] = algo[0];m_algo[1] = algo[1]; m_parms[0] = parms[0];m_parms[1] = parms[1]; m_realsize = realsize; } QByteArray KIsoFile::dataAt(long long pos, int count) const { QByteArray r; int rlen; if (archive()->device()->seek(position() + pos)) { r.resize(((pos + count) < size()) ? count : size() - pos); if (r.size()) { rlen = archive()->device()->read(r.data(), r.size()); if (rlen == - 1) r.resize(0); else if (rlen != (int)r.size()) r.resize(rlen); } } return r; } diff --git a/iso/kisofile.h b/iso/kisofile.h index 7f4cac3c..f1a2d35a 100644 --- a/iso/kisofile.h +++ b/iso/kisofile.h @@ -1,57 +1,57 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KISOFILE_H #define KISOFILE_H // QtCore #include #include class KIsoFile : public KArchiveFile { public: KIsoFile(KArchive* archive, const QString& name, int access, int date, int adate, int cdate, const QString& user, const QString& group, const QString& symlink, long long pos, long long size); ~KIsoFile(); void setZF(char algo[2], char parms[2], long long realsize); int adate() const { return m_adate; } int cdate() const { return m_cdate; } long long realsize() const { return m_realsize; } virtual QByteArray dataAt(long long pos, int count) const; private: /* hide this member function, it's broken by design, because the full data often requires too much memory */ char m_algo[2], m_parms[2]; long long m_realsize; int m_adate, m_cdate; long long m_curpos; }; #endif diff --git a/iso/libisofs/bswap.h b/iso/libisofs/bswap.h index cdf272e3..6546acad 100644 --- a/iso/libisofs/bswap.h +++ b/iso/libisofs/bswap.h @@ -1,118 +1,118 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * From the mplayer project (www.mplayerhq.hu) * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef BSWAP_H #define BSWAP_H /* this file doesn't exist #ifdef HAVE_CONFIG_H #include "config.h" #endif */ #ifdef HAVE_BYTESWAP_H #include #else #ifdef ARCH_X86 inline static unsigned short ByteSwap16(unsigned short x) { __asm("xchgb %b0,%h0" : "=q"(x) : "0"(x)); return x; } #define bswap_16(x) ByteSwap16(x) inline static unsigned int ByteSwap32(unsigned int x) { #if __CPU__ > 386 __asm("bswap %0": "=r"(x) : #else __asm("xchgb %b0,%h0\n" " rorl $16,%0\n" " xchgb %b0,%h0": "=q"(x) : #endif "0"(x)); return x; } #define bswap_32(x) ByteSwap32(x) inline static unsigned long long int ByteSwap64(unsigned long long int x) { register union { __extension__ unsigned long long int __ll; unsigned int __l[2]; } __x; asm("xchgl %0,%1": "=r"(__x.__l[0]), "=r"(__x.__l[1]): "0"(bswap_32((unsigned long)x)), "1"(bswap_32((unsigned long)(x >> 32)))); return __x.__ll; } #define bswap_64(x) ByteSwap64(x) #else #define bswap_16(x) (((x) & 0x00ff) << 8 | ((x) & 0xff00) >> 8) /* code from bits/byteswap.h (C) 1997, 1998 Free Software Foundation, Inc. */ #define bswap_32(x) \ ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >> 8) | \ (((x) & 0x0000ff00) << 8) | (((x) & 0x000000ff) << 24)) #define bswap_64(x) \ (__extension__ \ ({ union { __extension__ unsigned long long int __ll; \ unsigned int __l[2]; } __w, __r; \ __w.__ll = (x); \ __r.__l[0] = bswap_32 (__w.__l[1]); \ __r.__l[1] = bswap_32 (__w.__l[0]); \ __r.__ll; })) #endif /* !ARCH_X86 */ #endif /* !HAVE_BYTESWAP_H */ /* be2me ... BigEndian to MachineEndian le2me ... LittleEndian to MachineEndian */ #ifdef WORDS_BIGENDIAN #define be2me_16(x) (x) #define be2me_32(x) (x) #define be2me_64(x) (x) #define le2me_16(x) bswap_16(x) #define le2me_32(x) bswap_32(x) #define le2me_64(x) bswap_64(x) #else #define be2me_16(x) bswap_16(x) #define be2me_32(x) bswap_32(x) #define be2me_64(x) bswap_64(x) #define le2me_16(x) (x) #define le2me_32(x) (x) #define le2me_64(x) (x) #endif #endif diff --git a/iso/libisofs/el_torito.h b/iso/libisofs/el_torito.h index 2d4d9058..10f41619 100644 --- a/iso/libisofs/el_torito.h +++ b/iso/libisofs/el_torito.h @@ -1,86 +1,86 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * From the linux kernel * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef EL_TORITO_H #define EL_TORITO_H #include "iso_fs.h" #define EL_TORITO_ID "EL TORITO SPECIFICATION\0\0\0\0\0\0\0\0\0" struct el_torito_boot_descriptor { char type [ISODCL(1, 1)]; /* 711 */ char id [ISODCL(2, 6)]; char version [ISODCL(7, 7)]; /* 711 */ char system_id [ISODCL(8, 39)]; /* achars */ char unused [ISODCL(40, 71)]; char boot_catalog [ISODCL(72, 75)]; /* 731 */ }; struct validation_entry { char type [ISODCL(1, 1)]; /* 1 */ char platform [ISODCL(2, 2)]; char unused [ISODCL(3, 4)]; char id [ISODCL(5, 28)]; char cheksum [ISODCL(29, 30)]; char key [ISODCL(31, 31)]; /* 0x55 */ char key2 [ISODCL(32, 32)]; /* 0xaa */ }; struct default_entry { char bootid [ISODCL(1, 1)]; char media [ISODCL(2, 2)]; char loadseg [ISODCL(3, 4)]; char systype [ISODCL(5, 5)]; char unused [ISODCL(6, 6)]; char seccount [ISODCL(7, 8)]; char start [ISODCL(9, 12)]; char unused2 [ISODCL(13, 32)]; }; struct section_header { char headerid [ISODCL(1, 1)]; char platform [ISODCL(2, 2)]; char entries [ISODCL(3, 4)]; char id [ISODCL(5, 32)]; }; struct section_entry { char bootid [ISODCL(1, 1)]; char media [ISODCL(2, 2)]; char loadseg [ISODCL(3, 4)]; char systype [ISODCL(5, 5)]; char unused [ISODCL(6, 6)]; char seccount [ISODCL(7, 8)]; char start [ISODCL(9, 12)]; char selcrit [ISODCL(13, 13)]; char vendor_selcrit [ISODCL(14, 32)]; }; struct section_entry_ext { char extid [ISODCL(1, 1)]; char extrec [ISODCL(2, 2)]; char vendor_selcrit [ISODCL(3, 32)]; }; #endif diff --git a/iso/libisofs/iso_fs.h b/iso/libisofs/iso_fs.h index 3f373c99..aea4d297 100644 --- a/iso/libisofs/iso_fs.h +++ b/iso/libisofs/iso_fs.h @@ -1,252 +1,252 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * From the linux kernel * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef ISO_FS_H #define ISO_FS_H #include "bswap.h" #ifdef Q_WS_WIN #define DIR_SEPARATOR "/" #define DIR_SEPARATOR2 "\\" #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_CHAR2 '\\' #define REPLACE_DIR_SEP2(x) x = x.replace( DIR_SEPARATOR2, DIR_SEPARATOR ); #else #define DIR_SEPARATOR "/" #define DIR_SEPARATOR2 "/" #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_CHAR2 '/' #define REPLACE_DIR_SEP2(x) #endif /* * The isofs filesystem constants/structures */ /* This part borrowed from the bsd386 isofs */ #define ISODCL(from, to) (to - from + 1) struct iso_volume_descriptor { char type[ISODCL(1,1)]; /* 711 */ char id[ISODCL(2,6)]; char version[ISODCL(7,7)]; char data[ISODCL(8,2048)]; }; /* volume descriptor types */ #define ISO_VD_BOOT 0 #define ISO_VD_PRIMARY 1 #define ISO_VD_SUPPLEMENTARY 2 #define ISO_VD_END 255 #define ISO_STANDARD_ID "CD001" struct iso_primary_descriptor { char type [ISODCL(1, 1)]; /* 711 */ char id [ISODCL(2, 6)]; char version [ISODCL(7, 7)]; /* 711 */ char unused1 [ISODCL(8, 8)]; char system_id [ISODCL(9, 40)]; /* achars */ char volume_id [ISODCL(41, 72)]; /* dchars */ char unused2 [ISODCL(73, 80)]; char volume_space_size [ISODCL(81, 88)]; /* 733 */ char unused3 [ISODCL(89, 120)]; char volume_set_size [ISODCL(121, 124)]; /* 723 */ char volume_sequence_number [ISODCL(125, 128)]; /* 723 */ char logical_block_size [ISODCL(129, 132)]; /* 723 */ char path_table_size [ISODCL(133, 140)]; /* 733 */ char type_l_path_table [ISODCL(141, 144)]; /* 731 */ char opt_type_l_path_table [ISODCL(145, 148)]; /* 731 */ char type_m_path_table [ISODCL(149, 152)]; /* 732 */ char opt_type_m_path_table [ISODCL(153, 156)]; /* 732 */ char root_directory_record [ISODCL(157, 190)]; /* 9.1 */ char volume_set_id [ISODCL(191, 318)]; /* dchars */ char publisher_id [ISODCL(319, 446)]; /* achars */ char preparer_id [ISODCL(447, 574)]; /* achars */ char application_id [ISODCL(575, 702)]; /* achars */ char copyright_file_id [ISODCL(703, 739)]; /* 7.5 dchars */ char abstract_file_id [ISODCL(740, 776)]; /* 7.5 dchars */ char bibliographic_file_id [ISODCL(777, 813)]; /* 7.5 dchars */ char creation_date [ISODCL(814, 830)]; /* 8.4.26.1 */ char modification_date [ISODCL(831, 847)]; /* 8.4.26.1 */ char expiration_date [ISODCL(848, 864)]; /* 8.4.26.1 */ char effective_date [ISODCL(865, 881)]; /* 8.4.26.1 */ char file_structure_version [ISODCL(882, 882)]; /* 711 */ char unused4 [ISODCL(883, 883)]; char application_data [ISODCL(884, 1395)]; char unused5 [ISODCL(1396, 2048)]; }; /* Almost the same as the primary descriptor but two fields are specified */ struct iso_supplementary_descriptor { char type [ISODCL(1, 1)]; /* 711 */ char id [ISODCL(2, 6)]; char version [ISODCL(7, 7)]; /* 711 */ char flags [ISODCL(8, 8)]; /* 853 */ char system_id [ISODCL(9, 40)]; /* achars */ char volume_id [ISODCL(41, 72)]; /* dchars */ char unused2 [ISODCL(73, 80)]; char volume_space_size [ISODCL(81, 88)]; /* 733 */ char escape [ISODCL(89, 120)]; /* 856 */ char volume_set_size [ISODCL(121, 124)]; /* 723 */ char volume_sequence_number [ISODCL(125, 128)]; /* 723 */ char logical_block_size [ISODCL(129, 132)]; /* 723 */ char path_table_size [ISODCL(133, 140)]; /* 733 */ char type_l_path_table [ISODCL(141, 144)]; /* 731 */ char opt_type_l_path_table [ISODCL(145, 148)]; /* 731 */ char type_m_path_table [ISODCL(149, 152)]; /* 732 */ char opt_type_m_path_table [ISODCL(153, 156)]; /* 732 */ char root_directory_record [ISODCL(157, 190)]; /* 9.1 */ char volume_set_id [ISODCL(191, 318)]; /* dchars */ char publisher_id [ISODCL(319, 446)]; /* achars */ char preparer_id [ISODCL(447, 574)]; /* achars */ char application_id [ISODCL(575, 702)]; /* achars */ char copyright_file_id [ISODCL(703, 739)]; /* 7.5 dchars */ char abstract_file_id [ISODCL(740, 776)]; /* 7.5 dchars */ char bibliographic_file_id [ISODCL(777, 813)]; /* 7.5 dchars */ char creation_date [ISODCL(814, 830)]; /* 8.4.26.1 */ char modification_date [ISODCL(831, 847)]; /* 8.4.26.1 */ char expiration_date [ISODCL(848, 864)]; /* 8.4.26.1 */ char effective_date [ISODCL(865, 881)]; /* 8.4.26.1 */ char file_structure_version [ISODCL(882, 882)]; /* 711 */ char unused4 [ISODCL(883, 883)]; char application_data [ISODCL(884, 1395)]; char unused5 [ISODCL(1396, 2048)]; }; #define HS_STANDARD_ID "CDROM" struct hs_volume_descriptor { char foo [ISODCL(1, 8)]; /* 733 */ char type [ISODCL(9, 9)]; /* 711 */ char id [ISODCL(10, 14)]; char version [ISODCL(15, 15)]; /* 711 */ char data[ISODCL(16,2048)]; }; struct hs_primary_descriptor { char foo [ISODCL(1, 8)]; /* 733 */ char type [ISODCL(9, 9)]; /* 711 */ char id [ISODCL(10, 14)]; char version [ISODCL(15, 15)]; /* 711 */ char unused1 [ISODCL(16, 16)]; /* 711 */ char system_id [ISODCL(17, 48)]; /* achars */ char volume_id [ISODCL(49, 80)]; /* dchars */ char unused2 [ISODCL(81, 88)]; /* 733 */ char volume_space_size [ISODCL(89, 96)]; /* 733 */ char unused3 [ISODCL(97, 128)]; /* 733 */ char volume_set_size [ISODCL(129, 132)]; /* 723 */ char volume_sequence_number [ISODCL(133, 136)]; /* 723 */ char logical_block_size [ISODCL(137, 140)]; /* 723 */ char path_table_size [ISODCL(141, 148)]; /* 733 */ char type_l_path_table [ISODCL(149, 152)]; /* 731 */ char unused4 [ISODCL(153, 180)]; /* 733 */ char root_directory_record [ISODCL(181, 214)]; /* 9.1 */ }; /* We use this to help us look up the parent inode numbers. */ struct iso_path_table { char name_len[1]; /* 711 */ char ext_attr_length[1]; /* 711 */ char extent[4]; /* 731 */ char parent[2]; /* 721 */ char name[1]; }; /* high sierra is identical to iso, except that the date is only 6 bytes, and there is an extra reserved byte after the flags */ struct iso_directory_record { char length [ISODCL(1, 1)]; /* 711 */ char ext_attr_length [ISODCL(2, 2)]; /* 711 */ char extent [ISODCL(3, 10)]; /* 733 */ char size [ISODCL(11, 18)]; /* 733 */ char date [ISODCL(19, 25)]; /* 7 by 711 */ char flags [ISODCL(26, 26)]; char file_unit_size [ISODCL(27, 27)]; /* 711 */ char interleave [ISODCL(28, 28)]; /* 711 */ char volume_sequence_number [ISODCL(29, 32)]; /* 723 */ char name_len [ISODCL(33, 33)]; /* 711 */ char name [1]; }; /* 8 bit numbers */ static __inline unsigned char isonum_711(char *p); static __inline char isonum_712(char *p); /* 16 bit numbers */ static __inline unsigned short isonum_721(char *p); static __inline unsigned short isonum_722(char *p); static __inline unsigned short isonum_723(char *p); /* 32 bit numbers */ static __inline unsigned int isonum_731(char *p); static __inline unsigned int isonum_732(char *p); static __inline unsigned int isonum_733(char *p); /* 8 bit numbers */ static __inline unsigned char isonum_711(char *p) { return *(unsigned char *)p; } static __inline char isonum_712(char *p) { return *p; } /* 16 bit numbers */ static __inline unsigned short isonum_721(char *p) { return le2me_16(*(unsigned short *)p); } static __inline unsigned short isonum_722(char *p) { return be2me_16(*(unsigned short *)p); } static __inline unsigned short isonum_723(char *p) { /* Ignore bigendian datum due to broken mastering programs */ return le2me_16(*(unsigned short *)p); } /* 32 bit numbers */ static __inline unsigned int isonum_731(char *p) { return le2me_32(*(unsigned int *)p); } static __inline unsigned int isonum_732(char *p) { return be2me_32(*(unsigned int *)p); } static __inline unsigned int isonum_733(char *p) { /* Ignore bigendian datum due to broken mastering programs */ return le2me_32(*(unsigned int *)p); } #endif /*_ISOFS_H*/ diff --git a/iso/libisofs/isofs.c b/iso/libisofs/isofs.c index c4ef0526..9c9650ff 100644 --- a/iso/libisofs/isofs.c +++ b/iso/libisofs/isofs.c @@ -1,914 +1,914 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.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 package 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 package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "isofs.h" #include #include #include "iso_fs.h" /**************************************************************/ /* internal function from the linux kernel (isofs fs) */ static time_t getisotime(int year, int month, int day, int hour, int minute, int second, int tz) { int days, i; time_t crtime; year -= 1970; if (year < 0) { crtime = 0; } else { int monlen[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; days = year * 365; if (year > 2) days += (year + 1) / 4; for (i = 1; i < month; i++) days += monlen[i-1]; if (((year + 2) % 4) == 0 && month > 2) days++; days += day - 1; crtime = ((((days * 24) + hour) * 60 + minute) * 60) + second; /* sign extend */ if (tz & 0x80) tz |= (-1 << 8); /* * The timezone offset is unreliable on some disks, * so we make a sanity check. In no case is it ever * more than 13 hours from GMT, which is 52*15min. * The time is always stored in localtime with the * timezone offset being what get added to GMT to * get to localtime. Thus we need to subtract the offset * to get to true GMT, which is what we store the time * as internally. On the local system, the user may set * their timezone any way they wish, of course, so GMT * gets converted back to localtime on the receiving * system. * * NOTE: mkisofs in versions prior to mkisofs-1.10 had * the sign wrong on the timezone offset. This has now * been corrected there too, but if you are getting screwy * results this may be the explanation. If enough people * complain, a user configuration option could be added * to add the timezone offset in with the wrong sign * for 'compatibility' with older discs, but I cannot see how * it will matter that much. * * Thanks to kuhlmav@elec.canterbury.ac.nz (Volker Kuhlmann) * for pointing out the sign error. */ if (-52 <= tz && tz <= 52) crtime -= tz * 15 * 60; } return crtime; } /** * Returns the Unix from the ISO9660 9.1.5 time format */ time_t isodate_915(char * p, int hs) { return getisotime(1900 + p[0], p[1], p[2], p[3], p[4], p[5], hs == 0 ? p[6] : 0); } /** * Returns the Unix from the ISO9660 8.4.26.1 time format * BUG: hundredth of seconds are ignored, because Unix time_t has one second * resolution (I think it's no problem at all) */ time_t isodate_84261(char * p, int hs) { int year, month, day, hour, minute, second; year = (p[0] - '0') * 1000 + (p[1] - '0') * 100 + (p[2] - '0') * 10 + p[3] - '0'; month = (p[4] - '0') * 10 + (p[5] - '0'); day = (p[6] - '0') * 10 + (p[7] - '0'); hour = (p[8] - '0') * 10 + (p[9] - '0'); minute = (p[10] - '0') * 10 + (p[11] - '0'); second = (p[12] - '0') * 10 + (p[13] - '0'); return getisotime(year, month, day, hour, minute, second, hs == 0 ? p[16] : 0); } void FreeBootTable(boot_head *boot) { boot_entry *be, *next; be = boot->defentry; while (be) { next = be->next; free(be); be = next; } boot->defentry = NULL; } long long BootImageSize(int media, unsigned int len) { long long ret; switch (media & 0xf) { case 0: ret = len; /* No emulation */ break; case 1: ret = 80 * 2 * 15; /* 1.2 MB */ break; case 2: ret = 80 * 2 * 18; /* 1.44 MB */ break; case 3: ret = 80 * 2 * 36; /* 2.88 MB */ break; case 4: /* FIXME!!! */ ret = len; /* Hard Disk */ break; default: ret = len; } return ret; } static boot_entry *CreateBootEntry(char *be) { boot_entry *entry; entry = (boot_entry*) malloc(sizeof(boot_entry)); if (!entry) return NULL; memset(entry, 0, sizeof(boot_entry)); memcpy(&(entry->data), be, sizeof(entry->data)); return entry; } int ReadBootTable(readfunc *read, unsigned int sector, boot_head *head, void *udata) { char buf[2048], *c, *be; int i, end = 0; unsigned short sum; boot_entry *defcur = NULL, *deflast = NULL; register struct validation_entry *ventry = NULL; head->sections = NULL; head->defentry = NULL; while (1) { be = (char*) & buf; if (read(be, sector, 1, udata) != 1) goto err; /* first entry needs to be a validation entry */ if (!ventry) { ventry = (struct validation_entry *) be; if (isonum_711(ventry->type) != 1) goto err; sum = 0; c = (char*) ventry; for (i = 0;i < 16;i++) { sum += isonum_721(c); c += 2; } if (sum) goto err; memcpy(&head->ventry, be, 0x20); be += 0x20; } while (!end && (be < (char *)(&buf + 1))) { switch (isonum_711(be)) { case 0x88: defcur = CreateBootEntry(be); if (!defcur) goto err; if (deflast) deflast->next = defcur; else head->defentry = defcur; defcur->prev = deflast; deflast = defcur; break; case 0x90: case 0x91: break; default: end = 1; break; } be += 0x20; } if (end) break; sector ++; } return 0; err: FreeBootTable(head); return -1; } /** * Creates the linked list of the volume descriptors */ iso_vol_desc *ReadISO9660(readfunc *read, unsigned int sector, void *udata) { int i; struct iso_volume_descriptor buf; iso_vol_desc *first = NULL, *current = NULL, *prev = NULL; for (i = 0;i < 100;i++) { if (read((char*) &buf, sector + i + 16, 1, udata) != 1) { FreeISO9660(first); return NULL; } if (!memcmp(ISO_STANDARD_ID, &buf.id, 5)) { switch (isonum_711(&buf.type[0])) { case ISO_VD_BOOT: case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: current = (iso_vol_desc*) malloc(sizeof(iso_vol_desc)); if (!current) { FreeISO9660(first); return NULL; } current->prev = prev; current->next = NULL; if (prev) prev->next = current; memcpy(&(current->data), &buf, 2048); if (!first) first = current; prev = current; break; case ISO_VD_END: return first; break; } } else if (!memcmp(HS_STANDARD_ID, (struct hs_volume_descriptor*) &buf, 5)) { /* High Sierra format not supported (yet) */ } } return first; } /** * Frees the linked list of volume descriptors */ void FreeISO9660(iso_vol_desc *data) { iso_vol_desc *current; while (data) { current = data; data = current->next; free(current); } } /** * Frees the strings in 'rrentry' */ void FreeRR(rr_entry *rrentry) { if (rrentry->name) { free(rrentry->name); rrentry->name = NULL; } if (rrentry->sl) { free(rrentry->sl); rrentry->name = NULL; } } static int str_nappend(char **d, char *s, int n) { int i = 0; char *c; /* i=strnlen(s,n)+1; */ while (i < n && s[i]) i++; i++; if (*d) i += (strlen(*d) + 1); c = (char*) malloc(i); if (!c) return -ENOMEM; if (*d) { strcpy(c, *d); strncat(c, s, n); free(*d); } else strncpy(c, s, n); c[i-1] = 0; *d = c; return 0; } static int str_append(char **d, char *s) { int i; char *c; i = strlen(s) + 1; if (*d) i += (strlen(*d) + 1); c = (char*) malloc(i); if (!c) return -ENOMEM; if (*d) { strcpy(c, *d); strcat(c, s); free(*d); } else strcpy(c, s); c[i-1] = 0; *d = c; return 0; } #define rrtlen(c) (((unsigned char) c & 0x80) ? 17 : 7) #define rrctime(f,c) ((unsigned char) f & 0x80) ? isodate_84261(c,0) : isodate_915(c,0) /** * Parses the System Use area and fills rr_entry with values */ int ParseRR(struct iso_directory_record *idr, rr_entry *rrentry) { int suspoffs, susplen, i, f, ret = 0; char *r, *c; struct rock_ridge *rr; suspoffs = 33 + isonum_711(idr->name_len); if (!(isonum_711(idr->name_len) & 1)) suspoffs++; susplen = isonum_711(idr->length) - suspoffs; r = & (((char*) idr)[suspoffs]); rr = (struct rock_ridge*) r; memset(rrentry, 0, sizeof(rr_entry)); rrentry->len = sizeof(rr_entry); while (susplen > 0) { if (isonum_711(&rr->len) > susplen || rr->len == 0) break; if (rr->signature[0] == 'N' && rr->signature[1] == 'M') { if (!(rr->u.NM.flags & 0x26) && rr->len > 5 && !rrentry->name) { if (str_nappend(&rrentry->name, rr->u.NM.name, isonum_711(&rr->len) - 5)) { FreeRR(rrentry); return -ENOMEM; } ret++; } } else if (rr->signature[0] == 'P' && rr->signature[1] == 'X' && (isonum_711(&rr->len) == 44 || isonum_711(&rr->len) == 36)) { rrentry->mode = isonum_733(rr->u.PX.mode); rrentry->nlink = isonum_733(rr->u.PX.n_links); rrentry->uid = isonum_733(rr->u.PX.uid); rrentry->gid = isonum_733(rr->u.PX.gid); if (isonum_711(&rr->len) == 44) rrentry->serno = isonum_733(rr->u.PX.serno); ret++; } else if (rr->signature[0] == 'P' && rr->signature[1] == 'N' && isonum_711(&rr->len) == 20) { rrentry->dev_major = isonum_733(rr->u.PN.dev_high); rrentry->dev_minor = isonum_733(rr->u.PN.dev_low); ret++; } else if (rr->signature[0] == 'P' && rr->signature[1] == 'L' && isonum_711(&rr->len) == 12) { rrentry->pl = isonum_733(rr->u.PL.location); ret++; } else if (rr->signature[0] == 'C' && rr->signature[1] == 'L' && isonum_711(&rr->len) == 12) { rrentry->cl = isonum_733(rr->u.CL.location); ret++; } else if (rr->signature[0] == 'R' && rr->signature[1] == 'E' && isonum_711(&rr->len) == 4) { rrentry->re = 1; ret++; } else if (rr->signature[0] == 'S' && rr->signature[1] == 'L' && isonum_711(&rr->len) > 7) { i = isonum_711(&rr->len) - 5; c = (char*) rr; c += 5; while (i > 0) { switch (c[0] & ~1) { case 0x2: if (str_append(&rrentry->sl, (char *)".")) { FreeRR(rrentry); return -ENOMEM; } break; case 0x4: if (str_append(&rrentry->sl, (char *)"..")) { FreeRR(rrentry); return -ENOMEM; } break; } if ((c[0] & 0x08) == 0x08 || (c[1] && rrentry->sl && strlen(rrentry->sl) > 1)) { if (str_append(&rrentry->sl, (char *) DIR_SEPARATOR)) { FreeRR(rrentry); return -ENOMEM; } } if ((unsigned char)c[1] > 0) { if (str_nappend(&rrentry->sl, c + 2, (unsigned char)c[1])) { FreeRR(rrentry); return -ENOMEM; } } i -= ((unsigned char)c[1] + 2); c += ((unsigned char)c[1] + 2); } ret++; } else if (rr->signature[0] == 'T' && rr->signature[1] == 'F' && isonum_711(&rr->len) > 5) { i = isonum_711(&rr->len) - 5; f = rr->u.TF.flags; c = (char*) rr; c += 5; while (i >= rrtlen(f)) { if (f & 1) { rrentry->t_creat = rrctime(f, c); f &= ~1; } else if (f & 2) { rrentry->t_mtime = rrctime(f, c); f &= ~2; } else if (f & 4) { rrentry->t_atime = rrctime(f, c); f &= ~4; } else if (f & 8) { rrentry->t_ctime = rrctime(f, c); f &= ~8; } else if (f & 16) { rrentry->t_backup = rrctime(f, c); f &= ~16; } else if (f & 32) { rrentry->t_expire = rrctime(f, c); f &= ~32; } else if (f & 64) { rrentry->t_effect = rrctime(f, c); f &= ~64; } i -= rrtlen(f); c += rrtlen(f); } ret++; } else if (rr->signature[0] == 'Z' && rr->signature[1] == 'F' && isonum_711(&rr->len) == 16) { /* Linux-specific extension: transparent decompression */ rrentry->z_algo[0] = rr->u.ZF.algorithm[0]; rrentry->z_algo[1] = rr->u.ZF.algorithm[1]; rrentry->z_params[0] = rr->u.ZF.parms[0]; rrentry->z_params[1] = rr->u.ZF.parms[1]; rrentry->z_size = isonum_733(rr->u.ZF.real_size); ret++; } else { /* printf("SUSP sign: %c%c\n",rr->signature[0],rr->signature[1]); */ } susplen -= isonum_711(&rr->len); r += isonum_711(&rr->len); rr = (struct rock_ridge*) r; } return ret; } /** * Iterates over the directory entries. The directory is in 'buf', * the size of the directory is 'size'. 'callback' is called for each * directory entry with the parameter 'udata'. */ int ProcessDir(readfunc *read, int extent, int size, dircallback *callback, void *udata) { int pos = 0, ret = 0, siz; char *buf; struct iso_directory_record *idr; if (size & 2047) { siz = ((size >> 11) + 1) << 11; } else { siz = size; } buf = (char*) malloc(siz); if (!buf) return -ENOMEM; if (read(buf, extent, siz >> 11, udata) != siz >> 11) { free(buf); return -EIO; } while (size > 0) { idr = (struct iso_directory_record*) & buf[pos]; if (isonum_711(idr->length) == 0) { size -= (2048 - (pos & 0x7ff)); if (size <= 2) break; pos += 0x800; pos &= 0xfffff800; idr = (struct iso_directory_record*) & buf[pos]; } pos += isonum_711(idr->length); pos += isonum_711(idr->ext_attr_length); size -= isonum_711(idr->length); size -= isonum_711(idr->ext_attr_length); if (size < 0) break; if (isonum_711(idr->length) < 33 || isonum_711(idr->length) < 33 + isonum_711(idr->name_len)) { /* Invalid directory entry */ continue; } if ((ret = callback(idr, udata))) break; } free(buf); return ret; } /** * returns the joliet level from the volume descriptor */ int JolietLevel(struct iso_volume_descriptor *ivd) { int ret = 0; register struct iso_supplementary_descriptor *isd; isd = (struct iso_supplementary_descriptor *) ivd; if (isonum_711(ivd->type) == ISO_VD_SUPPLEMENTARY) { if (isd->escape[0] == 0x25 && isd->escape[1] == 0x2f) { switch (isd->escape[2]) { case 0x40: ret = 1; break; case 0x43: ret = 2; break; case 0x45: ret = 3; break; } } } return ret; } /********************************************************************/ #ifdef ISOFS_MAIN #include #include #include #include #include #include #include int level = 0, joliet = 0, dirs, files; iconv_t iconv_d; int fd; int readf(char *buf, unsigned int start, unsigned int len, void *udata) { int ret; if ((ret = lseek64(fd, (long long)start << (long long)11, SEEK_SET)) < 0) return ret; ret = read(fd, buf, len << 11u); if (ret < 0) return ret; return (ret >> 11u); } void dumpchars(char *c, int len) { while (len > 0) { printf("%c", *c); len--; c++; } } void sp(int num) { int i; for (i = 0;i < num*5;i++) { printf(" "); }; } void dumpflags(char flags) { if (flags & 1) printf("HIDDEN "); if (flags & 2) printf("DIR "); if (flags & 4) printf("ASF "); } void dumpjoliet(char *c, int len) { char outbuf[255]; size_t out; int ret; char *outptr; outptr = (char*) & outbuf; out = 255; if ((iconv(iconv_d, &c, &len, &outptr, &out)) < 0) { printf("conversion error=%d", errno); return; } ret = 255 - out; dumpchars((char*) &outbuf, ret); } void dumpchardesc(char *c, int len) { if (joliet) dumpjoliet(c, len); else { dumpchars(c, len); } } void dumpiso915time(char *t, int hs) { time_t time; char *c; time = isodate_915(t, hs); c = (char*) ctime(&time); if (c && c[strlen(c)-1] == 0x0a) c[strlen(c)-1] = 0; if (c) printf("%s", c); } void dumpiso84261time(char *t, int hs) { time_t time; char *c; time = isodate_84261(t, hs); c = (char*) ctime(&time); if (c && c[strlen(c)-1] == 0x0a) c[strlen(c)-1] = 0; if (c) printf("%s", c); } void dumpdirrec(struct iso_directory_record *dir) { if (isonum_711(dir->name_len) == 1) { switch (dir->name[0]) { case 0: printf("."); break; case 1: printf(".."); break; default: printf("%c", dir->name[0]); break; } } dumpchardesc(dir->name, isonum_711(dir->name_len)); printf(" size=%d", isonum_733(dir->size)); printf(" extent=%d ", isonum_733(dir->extent)); dumpflags(isonum_711(dir->flags)); dumpiso915time((char*) &(dir->date), 0); } void dumprrentry(rr_entry *rr) { printf(" NM=[%s] uid=%d gid=%d nlink=%d mode=%o ", rr->name, rr->uid, rr->gid, rr->nlink, rr->mode); if (S_ISCHR(rr->mode) || S_ISBLK(rr->mode)) printf("major=%d minor=%d ", rr->dev_major, rr->dev_minor); if (rr->mode & S_IFLNK && rr->sl) printf("slink=%s ", rr->sl); /* printf("\n"); if (rr->t_creat) printf("t_creat: %s",ctime(&rr->t_creat)); if (rr->st_mtime) printf("st_mtime: %s",ctime(&rr->st_mtime)); if (rr->st_atime) printf("st_atime: %s",ctime(&rr->st_atime)); if (rr->st_ctime) printf("st_ctime: %s",ctime(&rr->st_ctime)); if (rr->t_backup) printf("t_backup: %s",ctime(&rr->t_backup)); if (rr->t_expire) printf("t_expire: %s",ctime(&rr->t_expire)); if (rr->t_effect) printf("t_effect: %s",ctime(&rr->t_effect)); */ } void dumpsusp(char *c, int len) { dumpchars(c, len); } void dumpboot(struct el_torito_boot_descriptor *ebd) { printf("version: %d\n", isonum_711(ebd->version)); printf("system id: ");dumpchars(ebd->system_id, ISODCL(8, 39));printf("\n"); printf("boot catalog start: %d\n", isonum_731(ebd->boot_catalog)); } void dumpdefentry(struct default_entry *de) { printf("Default entry: \n"); printf(" bootid=%x\n", isonum_711(de->bootid)); printf(" media emulation=%d (", isonum_711(de->media)); switch (isonum_711(de->media) & 0xf) { case 0: printf("No emulation"); break; case 1: printf("1.2 Mb floppy"); break; case 2: printf("1.44 Mb floppy"); break; case 3: printf("2.88 Mb floppy"); break; case 4: printf("Hard Disk"); break; default: printf("Unknown/Invalid"); break; } printf(")\n"); printf(" loadseg=%d\n", isonum_721(de->loadseg)); printf(" systype=%d\n", isonum_711(de->systype)); printf(" start lba=%d count=%d\n", isonum_731(de->start), isonum_721(de->seccount)); } void dumpbootcat(boot_head *bh) { boot_entry *be; printf("System id: ");dumpchars(bh->ventry.id, ISODCL(28, 5));printf("\n"); be = bh->defentry; while (be) { dumpdefentry(be->data); be = be->next; } } void dumpdesc(struct iso_primary_descriptor *ipd) { printf("system id: ");dumpchardesc(ipd->system_id, ISODCL(9, 40));printf("\n"); printf("volume id: ");dumpchardesc(ipd->volume_id, ISODCL(41, 72));printf("\n"); printf("volume space size: %d\n", isonum_733(ipd->volume_space_size)); printf("volume set size: %d\n", isonum_723(ipd->volume_set_size)); printf("volume seq num: %d\n", isonum_723(ipd->volume_set_size)); printf("logical block size: %d\n", isonum_723(ipd->logical_block_size)); printf("path table size: %d\n", isonum_733(ipd->path_table_size)); printf("location of type_l path table: %d\n", isonum_731(ipd->type_l_path_table)); printf("location of optional type_l path table: %d\n", isonum_731(ipd->opt_type_l_path_table)); printf("location of type_m path table: %d\n", isonum_732(ipd->type_m_path_table)); printf("location of optional type_m path table: %d\n", isonum_732(ipd->opt_type_m_path_table)); /* printf("Root dir record:\n");dumpdirrec((struct iso_directory_record*) &ipd->root_directory_record); */ printf("Volume set id: ");dumpchardesc(ipd->volume_set_id, ISODCL(191, 318));printf("\n"); printf("Publisher id: ");dumpchardesc(ipd->publisher_id, ISODCL(319, 446));printf("\n"); printf("Preparer id: ");dumpchardesc(ipd->preparer_id, ISODCL(447, 574));printf("\n"); printf("Application id: ");dumpchardesc(ipd->application_id, ISODCL(575, 702));printf("\n"); printf("Copyright id: ");dumpchardesc(ipd->copyright_file_id, ISODCL(703, 739));printf("\n"); printf("Abstract file id: ");dumpchardesc(ipd->abstract_file_id, ISODCL(740, 776));printf("\n"); printf("Bibliographic file id: ");dumpchardesc(ipd->bibliographic_file_id, ISODCL(777, 813));printf("\n"); printf("Volume creation date: ");dumpiso84261time(ipd->creation_date, 0);printf("\n"); printf("Volume modification date: ");dumpiso84261time(ipd->modification_date, 0);printf("\n"); printf("Volume expiration date: ");dumpiso84261time(ipd->expiration_date, 0);printf("\n"); printf("Volume effective date: ");dumpiso84261time(ipd->effective_date, 0);printf("\n"); printf("File structure version: %d\n", isonum_711(ipd->file_structure_version)); } int mycallb(struct iso_directory_record *idr, void *udata) { rr_entry rrentry; sp(level);dumpdirrec(idr); if (level == 0) printf(" (Root directory) "); printf("\n"); if (ParseRR(idr, &rrentry) > 0) { sp(level);printf(" ");dumprrentry(&rrentry);printf("\n"); } FreeRR(&rrentry); if (!(idr->flags[0] & 2)) files++; if ((idr->flags[0] & 2) && (level == 0 || isonum_711(idr->name_len) > 1)) { level++; dirs++; ProcessDir(&readf, isonum_733(idr->extent), isonum_733(idr->size), &mycallb, udata); level--; } return 0; } /************************************************/ int main(int argc, char *argv[]) { int i = 1, sector = 0; iso_vol_desc *desc; boot_head boot; if (argc < 2) { fprintf(stderr, "\nUsage: %s iso-file-name or device [starting sector]\n\n", argv[0]); return 0; } if (argc >= 3) { sector = atoi(argv[2]); printf("Using starting sector number %d\n", sector); } fd = open(argv[1], O_RDONLY); if (fd < 0) { fprintf(stderr, "open error\n"); return -1; } iconv_d = iconv_open("ISO8859-2", "UTF16BE"); if (iconv_d == 0) { fprintf(stderr, "iconv open error\n"); return -1; } desc = ReadISO9660(&readf, sector, NULL); if (!desc) { printf("No volume descriptors\n"); return -1; } while (desc) { printf("\n\n--------------- Volume descriptor (%d.) type %d: ---------------\n\n", i, isonum_711(desc->data.type)); switch (isonum_711(desc->data.type)) { case ISO_VD_BOOT: { struct el_torito_boot_descriptor* bootdesc; bootdesc = &(desc->data); dumpboot(bootdesc); if (!memcmp(EL_TORITO_ID, bootdesc->system_id, ISODCL(8, 39))) { if (ReadBootTable(&readf, isonum_731(bootdesc->boot_catalog), &boot, NULL)) { printf("Boot Catalog Error\n"); } else { dumpbootcat(&boot); FreeBootTable(&boot); } } } break; case ISO_VD_PRIMARY: case ISO_VD_SUPPLEMENTARY: joliet = 0; joliet = JolietLevel(&desc->data); printf("Joliet level: %d\n", joliet); dumpdesc((struct iso_primary_descriptor*) &desc->data); printf("\n\n--------------- Directory structure: -------------------\n\n"); dirs = 0;files = 0; mycallb(&(((struct iso_primary_descriptor*) &desc->data)->root_directory_record), NULL); printf("\nnumber of directories: %d\n", dirs); printf("\nnumber of files: %d\n", files); break; } desc = desc->next; i++; } iconv_close(iconv_d); close(fd); FreeISO9660(desc); return 0; } #endif /* ISOFS_MAIN */ diff --git a/iso/libisofs/isofs.h b/iso/libisofs/isofs.h index 52635398..326edb73 100644 --- a/iso/libisofs/isofs.h +++ b/iso/libisofs/isofs.h @@ -1,171 +1,171 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef ISOFS_H #define ISOFS_H #ifdef __cplusplus extern "C" { #endif #include #include #include "iso_fs.h" #include "el_torito.h" #include "rock.h" typedef struct _rr_entry { int len; /* length of structure */ char *name; /* Name from 'NM' */ char *sl; /* symbolic link data */ time_t t_creat; time_t t_mtime; time_t t_atime; time_t t_ctime; time_t t_backup; time_t t_expire; time_t t_effect; int mode; /* POSIX file modes */ int nlink; int uid; int gid; int serno; int dev_major; int dev_minor; int pl; /* parent location */ int cl; /* child location */ int re; /* relocated */ char z_algo[2]; /* zizofs algorithm */ char z_params[2]; /* zizofs parameters */ unsigned int z_size; /* zizofs real_size */ } rr_entry; typedef struct _iso_vol_desc { struct _iso_vol_desc *next; struct _iso_vol_desc *prev; struct iso_volume_descriptor data; } iso_vol_desc; union default_entry_as_data { char c[32]; struct default_entry d_e; }; typedef struct _boot_entry { struct _boot_entry *next; struct _boot_entry *prev; struct _boot_entry *parent; struct _boot_entry *child; union default_entry_as_data data; } boot_entry; typedef struct _boot_head { struct validation_entry ventry; struct _boot_entry *defentry; struct _boot_entry *sections; } boot_head; /** * this callback function needs to read 'len' sectors from 'start' into 'buf' */ typedef int readfunc(char *buf, unsigned int start, unsigned int len, void *); /** * ProcessDir uses this callback */ typedef int dircallback(struct iso_directory_record *, void *); /** * Returns the Unix from the ISO9660 9.1.5 (7 bytes) time format * This function is from the linux kernel. * Set 'hs' to non-zero if it's a HighSierra volume */ time_t isodate_915(char * p, int hs); /** * Returns the Unix time from the ISO9660 8.4.26.1 (17 bytes) time format * BUG: hundredth of seconds are ignored, because time_t has one second * resolution (I think it's no problem at all) * Set 'hs' to non-zero if it's a HighSierra volume */ time_t isodate_84261(char * p, int hs); /** * Creates the linked list of the volume descriptors * 'sector' is the starting sector number of where the filesystem start * (starting sector of a session on a CD-ROM) * If the function fails, returns NULL * Don't forget to call FreeISO9660 after using the volume descriptor list! */ iso_vol_desc *ReadISO9660(readfunc *read, unsigned int sector, void *udata); /** * Frees the linked list of volume descriptors. */ void FreeISO9660(iso_vol_desc *data); /** * Iterates over the directory entries. The directory is in 'buf', * the size of the directory is 'size'. 'callback' is called for each * directory entry with the parameter 'udata'. */ int ProcessDir(readfunc *read, int extent, int size, dircallback *callback, void *udata); /** * Parses the System Use area and fills rr_entry with values */ int ParseRR(struct iso_directory_record *idr, rr_entry *rrentry); /** * Frees the strings in 'rrentry' */ void FreeRR(rr_entry *rrentry); /** * returns the joliet level from the volume descriptor */ int JolietLevel(struct iso_volume_descriptor *ivd); /** * Returns the size of the boot image (in 512 byte sectors) */ long long BootImageSize(int media, unsigned int len); /** * Frees the boot catalog entries in 'boot'. If you ever called ReadBootTable, * then don't forget to call FreeBootTable! */ void FreeBootTable(boot_head *boot); /** * Reads the boot catalog into 'head'. Don't forget to call FreeBootTable! */ int ReadBootTable(readfunc *read, unsigned int sector, boot_head *head, void *udata); #ifdef __cplusplus } //extern "C" #endif #endif diff --git a/iso/libisofs/rock.h b/iso/libisofs/rock.h index 232daff7..03b294fb 100644 --- a/iso/libisofs/rock.h +++ b/iso/libisofs/rock.h @@ -1,148 +1,148 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * From the linux kernel * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef ROCK_H #define ROCK_H 1 /* These structs are used by the system-use-sharing protocol, in which the Rock Ridge extensions are embedded. It is quite possible that other extensions are present on the disk, and this is fine as long as they all use SUSP */ struct SU_SP { unsigned char magic[2]; unsigned char skip; }; struct SU_CE { char extent[8]; char offset[8]; char size[8]; }; struct SU_ER { unsigned char len_id; unsigned char len_des; unsigned char len_src; unsigned char ext_ver; char data[1]; }; struct RR_RR { char flags[1]; }; struct RR_PX { char mode[8]; char n_links[8]; char uid[8]; char gid[8]; char serno[8]; }; struct RR_PN { char dev_high[8]; char dev_low[8]; }; struct SL_component { unsigned char flags; unsigned char len; char text[1]; }; struct RR_SL { unsigned char flags; struct SL_component link; }; struct RR_NM { unsigned char flags; char name[1]; }; struct RR_CL { char location[8]; }; struct RR_PL { char location[8]; }; struct stamp { char time[7]; }; struct RR_TF { char flags; struct stamp times[1]; /* Variable number of these beasts */ }; /* Linux-specific extension for transparent decompression */ struct RR_ZF { char algorithm[2]; char parms[2]; char real_size[8]; }; /* These are the bits and their meanings for flags in the TF structure. */ #define TF_CREATE 1 #define TF_MODIFY 2 #define TF_ACCESS 4 #define TF_ATTRIBUTES 8 #define TF_BACKUP 16 #define TF_EXPIRATION 32 #define TF_EFFECTIVE 64 #define TF_LONG_FORM 128 struct rock_ridge { char signature[2]; char len; /* 711 */ char version; /* 711 */ union { struct SU_SP SP; struct SU_CE CE; struct SU_ER ER; struct RR_RR RR; struct RR_PX PX; struct RR_PN PN; struct RR_SL SL; struct RR_NM NM; struct RR_CL CL; struct RR_PL PL; struct RR_TF TF; struct RR_ZF ZF; } u; }; #define RR_PX 1 /* POSIX attributes */ #define RR_PN 2 /* POSIX devices */ #define RR_SL 4 /* Symbolic link */ #define RR_NM 8 /* Alternate Name */ #define RR_CL 16 /* Child link */ #define RR_PL 32 /* Parent link */ #define RR_RE 64 /* Relocation directory */ #define RR_TF 128 /* Timestamps */ #endif /* ROCK_H */ diff --git a/iso/qfilehack.cpp b/iso/qfilehack.cpp index 1d6b708b..ae26ce77 100644 --- a/iso/qfilehack.cpp +++ b/iso/qfilehack.cpp @@ -1,45 +1,45 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "qfilehack.h" QFileHack::QFileHack() = default; QFileHack::QFileHack(const QString & name) : QFile(name) { } QFileHack::~QFileHack() = default; bool QFileHack::open(QFile::OpenMode m) { bool ret; #ifdef Q_OS_UNIX // m |= IO_Async; // On linux, set O_NONBLOCK, opens CD-ROMs faster #endif ret = QFile::open(m); // if (ret && isSequential() ) { // setOpenMode(m | (QFile::OpenMode)IO_Direct); // } return ret; } diff --git a/iso/qfilehack.h b/iso/qfilehack.h index 20fbb048..dc723be6 100644 --- a/iso/qfilehack.h +++ b/iso/qfilehack.h @@ -1,41 +1,41 @@ /***************************************************************************** * Copyright (C) 2002 Szombathelyi György * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef QFILEHACK_H #define QFILEHACK_H // QtCore #include #include /** * Qt thinks if a file is not S_IFREG, you cannot seek in it. * It's false (what about block devices for example ?) */ class QFileHack : public QFile { public: QFileHack(); explicit QFileHack(const QString & name); ~QFileHack(); virtual bool open(QFile::OpenMode m) Q_DECL_OVERRIDE; }; #endif diff --git a/krArc/krarc.cpp b/krArc/krarc.cpp index 802e538b..f5245b2e 100644 --- a/krArc/krarc.cpp +++ b/krArc/krarc.cpp @@ -1,1936 +1,1936 @@ /***************************************************************************** * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Shie Erlich * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krarc.h" #include "../krusader/defaults.h" // QtCore #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../krusader/compat.h" #define MAX_IPC_SIZE (1024*32) #define TRIES_WITH_PASSWORDS 3 using namespace KIO; extern "C" { #ifdef KRARC_ENABLED /* This codec is for being able to handle files which encoding differs from the current locale. * * Unfortunately QProcess requires QString parameters for arguments which are encoded to Local8Bit * If we want to use unzip with ISO-8852-2 when the current locale is UTF-8, it will cause problems. * * Workaround: * 1. encode the QString to QByteArray ( according to the selected remote encoding, ISO-8852-2 ) * 2. encode QByteArray to QString again * unicode 0xE000-0xF7FF is for private use * the byte array is mapped to 0xE000-0xE0FF unicodes * 3. KrArcCodec maps 0xE000-0xE0FF to 0x0000-0x00FF, while calls the default encoding routine * for other unicodes. */ class KrArcCodec : public QTextCodec { public: KrArcCodec(QTextCodec * codec) : originalCodec(codec) {} ~KrArcCodec() override = default; QByteArray name() const Q_DECL_OVERRIDE { return originalCodec->name(); } QList aliases() const Q_DECL_OVERRIDE { return originalCodec->aliases(); } int mibEnum() const Q_DECL_OVERRIDE { return originalCodec->mibEnum(); } protected: QString convertToUnicode(const char *in, int length, ConverterState *state) const Q_DECL_OVERRIDE { return originalCodec->toUnicode(in, length, state); } QByteArray convertFromUnicode(const QChar *in, int length, ConverterState *state) const Q_DECL_OVERRIDE { // the QByteArray is embedded into the unicode charset (QProcess hell) QByteArray result; for (int i = 0; i != length; i++) { if (((in[ i ].unicode()) & 0xFF00) == 0xE000) // map 0xE000-0xE0FF to 0x0000-0x00FF result.append((char)(in[ i ].unicode() & 0xFF)); else result.append(originalCodec->fromUnicode(in + i, 1, state)); } return result; } private: QTextCodec * originalCodec; } *krArcCodec; #define SET_KRCODEC QTextCodec *origCodec = QTextCodec::codecForLocale(); \ QTextCodec::setCodecForLocale( krArcCodec ); #define RESET_KRCODEC QTextCodec::setCodecForLocale( origCodec ); #endif // KRARC_ENABLED class DummySlave : public KIO::SlaveBase { public: DummySlave(const QByteArray &pool_socket, const QByteArray &app_socket) : SlaveBase("kio_krarc", pool_socket, app_socket) { error((int)ERR_SLAVE_DEFINED, QString("krarc is disabled.")); } }; int Q_DECL_EXPORT kdemain(int argc, char **argv) { if (argc != 4) { qWarning() << "Usage: kio_krarc protocol domain-socket1 domain-socket2" << endl; exit(-1); } QCoreApplication app(argc, argv); app.setApplicationName(QStringLiteral("kio_krarc")); #ifdef KRARC_ENABLED kio_krarcProtocol slave(argv[2], argv[3]); #else DummySlave slave(argv[2], argv[3]); #endif slave.dispatchLoop(); return 0; } } // extern "C" #ifdef KRARC_ENABLED kio_krarcProtocol::kio_krarcProtocol(const QByteArray &pool_socket, const QByteArray &app_socket) : SlaveBase("kio_krarc", pool_socket, app_socket), archiveChanged(true), arcFile(nullptr), extArcReady(false), password(QString()), krConf("krusaderrc"), codec(nullptr) { KRFUNC; confGrp = KConfigGroup(&krConf, "Dependencies"); KConfigGroup group(&krConf, "General"); QString tmpDirPath = group.readEntry("Temp Directory", _TempDirectory); QDir tmpDir(tmpDirPath); if(!tmpDir.exists()) { for (int i = 1 ; i != -1 ; i = tmpDirPath.indexOf('/', i + 1)) QDir().mkdir(tmpDirPath.left(i)); QDir().mkdir(tmpDirPath); } arcTempDir = tmpDirPath + DIR_SEPARATOR; QString dirName = "krArc" + QDateTime::currentDateTime().toString(Qt::ISODate); dirName.replace(QRegExp(":"), "_"); tmpDir.mkdir(dirName); arcTempDir = arcTempDir + dirName + DIR_SEPARATOR; krArcCodec = new KrArcCodec(QTextCodec::codecForLocale()); } /* ---------------------------------------------------------------------------------- */ kio_krarcProtocol::~kio_krarcProtocol() { KRFUNC; // delete the temp directory KProcess proc; proc << fullPathName("rm") << "-rf" << arcTempDir; proc.start(); proc.waitForFinished(); } /* ---------------------------------------------------------------------------------- */ bool kio_krarcProtocol::checkWriteSupport() { KRFUNC; krConf.reparseConfiguration(); if (KConfigGroup(&krConf, "kio_krarc").readEntry("EnableWrite", false)) return true; else { error(ERR_UNSUPPORTED_ACTION, i18n("krarc: write support is disabled.\n" "You can enable it on the 'Archives' page in Konfigurator.")); return false; } } void kio_krarcProtocol::receivedData(KProcess *, QByteArray &d) { KRFUNC; const QByteArray& buf(d); data(buf); processedSize(d.length()); decompressedLen += d.length(); } void kio_krarcProtocol::mkdir(const QUrl &url, int permissions) { KRFUNC; const QString path = getPath(url); KRDEBUG(path); if (!checkWriteSupport()) return; // In case of KIO::mkpath call there is a mkdir call for every path element. // Therefore the path all the way up to our archive needs to be checked for existence // and reported as success. if (QDir().exists(path)) { finished(); return; } if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, path); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, path); return; } if (putCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Creating directories is not supported with %1 archives", arcType)); return; } const QString arcFilePath = getPath(arcFile->url()); if (arcType == "arj" || arcType == "lha") { QString arcDir = path.mid(arcFilePath.length()); if (arcDir.right(1) != DIR_SEPARATOR) arcDir = arcDir + DIR_SEPARATOR; if (dirDict.find(arcDir) == dirDict.end()) addNewDir(arcDir); finished(); return; } QString arcDir = findArcDirectory(url); QString tempDir = arcDir.mid(1) + path.mid(path.lastIndexOf(DIR_SEPARATOR) + 1); if (tempDir.right(1) != DIR_SEPARATOR) tempDir = tempDir + DIR_SEPARATOR; if (permissions == -1) permissions = 0777; //set default permissions QByteArray arcTempDirEnc = arcTempDir.toLocal8Bit(); for (int i = 0;i < tempDir.length() && i >= 0; i = tempDir.indexOf(DIR_SEPARATOR, i + 1)) { QByteArray newDirs = encodeString(tempDir.left(i)); newDirs.prepend(arcTempDirEnc); QT_MKDIR(newDirs, permissions); } if (tempDir.endsWith(DIR_SEPARATOR)) tempDir.truncate(tempDir.length() - 1); // pack the directory KrLinecountingProcess proc; proc << putCmd << arcFilePath << localeEncodedString(tempDir); infoMessage(i18n("Creating %1...", url.fileName())); QDir::setCurrent(arcTempDir); SET_KRCODEC proc.start(); RESET_KRCODEC proc.waitForFinished(); // delete the temp directory QDir().rmdir(arcTempDir); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(ERR_COULD_NOT_WRITE, path + "\n\n" + proc.getErrorMsg()); return; } // force a refresh of archive information initDirDict(url, true); finished(); } void kio_krarcProtocol::put(const QUrl &url, int permissions, KIO::JobFlags flags) { KRFUNC; KRDEBUG(getPath(url)); if (!checkWriteSupport()) return; bool overwrite = !!(flags & KIO::Overwrite); bool resume = !!(flags & KIO::Resume); if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (putCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Writing to %1 archives is not supported", arcType)); return; } if (!overwrite && findFileEntry(url)) { error(ERR_FILE_ALREADY_EXIST, getPath(url)); return; } QString arcDir = findArcDirectory(url); if (arcDir.isEmpty()) KRDEBUG("arcDir is empty."); QString tempFile = arcDir.mid(1) + getPath(url).mid(getPath(url).lastIndexOf(DIR_SEPARATOR) + 1); QString tempDir = arcDir.mid(1); if (tempDir.right(1) != DIR_SEPARATOR) tempDir = tempDir + DIR_SEPARATOR; if (permissions == -1) permissions = 0777; //set default permissions QByteArray arcTempDirEnc = arcTempDir.toLocal8Bit(); for (int i = 0;i < tempDir.length() && i >= 0; i = tempDir.indexOf(DIR_SEPARATOR, i + 1)) { QByteArray newDirs = encodeString(tempDir.left(i)); newDirs.prepend(arcTempDirEnc); QT_MKDIR(newDirs, 0755); } int fd; if (resume) { QByteArray ba = encodeString(tempFile); ba.prepend(arcTempDirEnc); fd = QT_OPEN(ba, O_RDWR); // append if resuming QT_LSEEK(fd, 0, SEEK_END); // Seek to end } else { // WABA: Make sure that we keep writing permissions ourselves, // otherwise we can be in for a surprise on NFS. mode_t initialMode; if (permissions != -1) initialMode = permissions | S_IWUSR | S_IRUSR; else initialMode = 0666; QByteArray ba = encodeString(tempFile); ba.prepend(arcTempDirEnc); fd = QT_OPEN(ba, O_CREAT | O_TRUNC | O_WRONLY, initialMode); } QByteArray buffer; int readResult; bool isIncomplete = false; do { dataReq(); readResult = readData(buffer); auto bytesWritten = ::write(fd, buffer.data(), buffer.size()); if (bytesWritten < buffer.size()) { isIncomplete = true; break; } } while (readResult > 0); ::close(fd); if (isIncomplete) { error(ERR_COULD_NOT_WRITE, getPath(url)); return; } // pack the file KrLinecountingProcess proc; proc << putCmd << getPath(arcFile->url()) << localeEncodedString(tempFile); infoMessage(i18n("Packing %1...", url.fileName())); QDir::setCurrent(arcTempDir); SET_KRCODEC proc.start(); RESET_KRCODEC proc.waitForFinished(); // remove the files QDir().rmdir(arcTempDir); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(ERR_COULD_NOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg()); return; } // force a refresh of archive information initDirDict(url, true); finished(); } void kio_krarcProtocol::get(const QUrl &url) { KRFUNC; get(url, TRIES_WITH_PASSWORDS); } void kio_krarcProtocol::get(const QUrl &url, int tries) { KRFUNC; KRDEBUG(getPath(url)); bool decompressToFile = false; if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (getCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Retrieving data from %1 archives is not supported", arcType)); return; } UDSEntry* entry = findFileEntry(url); if (!entry) { error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } if (KFileItem(*entry, url).isDir()) { error(KIO::ERR_IS_DIRECTORY, getPath(url)); return; } KIO::filesize_t expectedSize = KFileItem(*entry, url).size(); // for RPM files extract the cpio file first if (!extArcReady && arcType == "rpm") { KrLinecountingProcess cpio; cpio << "rpm2cpio" << getPath(arcFile->url(), QUrl::StripTrailingSlash); cpio.setStandardOutputFile(arcTempDir + "contents.cpio"); cpio.start(); cpio.waitForFinished(); if (cpio.exitStatus() != QProcess::NormalExit || !checkStatus(cpio.exitCode())) { error(ERR_COULD_NOT_READ, getPath(url) + "\n\n" + cpio.getErrorMsg()); return; } extArcReady = true; } // for DEB files extract the tar file first if (!extArcReady && arcType == "deb") { KrLinecountingProcess dpkg; dpkg << cmd << "--fsys-tarfile" << getPath(arcFile->url(), QUrl::StripTrailingSlash); dpkg.setStandardOutputFile(arcTempDir + "contents.cpio"); dpkg.start(); dpkg.waitForFinished(); if (dpkg.exitStatus() != QProcess::NormalExit || !checkStatus(dpkg.exitCode())) { error(ERR_COULD_NOT_READ, getPath(url) + "\n\n" + dpkg.getErrorMsg()); return; } extArcReady = true; } // Use the external unpacker to unpack the file QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1); KrLinecountingProcess proc; if (extArcReady) { proc << getCmd << arcTempDir + "contents.cpio" << '*' + localeEncodedString(file); } else if (arcType == "arj" || arcType == "ace" || arcType == "7z") { proc << getCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash) << localeEncodedString(file); if (arcType == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); file = url.fileName(); decompressToFile = true; } else { decompressedLen = 0; // Determine the mimetype of the file to be retrieved, and emit it. // This is mandatory in all slaves (for KRun/BrowserRun to work). QMimeDatabase db; QMimeType mt = db.mimeTypeForFile(arcTempDir + file); if (mt.isValid()) emit mimeType(mt.name()); QString escapedFilename = file; if(arcType == "zip") // left bracket needs to be escaped escapedFilename.replace('[', "[[]"); proc << getCmd << getPath(arcFile->url()); if (arcType != "gzip" && arcType != "bzip2" && arcType != "lzma" && arcType != "xz") proc << localeEncodedString(escapedFilename); connect(&proc, &KrLinecountingProcess::newOutputData, this, &kio_krarcProtocol::receivedData); proc.setMerge(false); } infoMessage(i18n("Unpacking %1...", url.fileName())); // change the working directory to our arcTempDir QDir::setCurrent(arcTempDir); SET_KRCODEC proc.setTextModeEnabled(false); proc.start(); RESET_KRCODEC proc.waitForFinished(); if (!extArcReady && !decompressToFile) { if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()) || (arcType != "bzip2" && arcType != "lzma" && arcType != "xz" && expectedSize != decompressedLen)) { if (encrypted && tries) { invalidatePassword(); get(url, tries - 1); return; } error(KIO::ERR_ACCESS_DENIED, getPath(url) + "\n\n" + proc.getErrorMsg()); return; } } else { if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode()) || !QFileInfo(arcTempDir + file).exists()) { if (decompressToFile) QFile(arcTempDir + file).remove(); if (encrypted && tries) { invalidatePassword(); get(url, tries - 1); return; } error(KIO::ERR_ACCESS_DENIED, getPath(url)); return; } // the following block is ripped from KDE file KIO::Slave // $Id: krarc.cpp,v 1.43 2007/01/13 13:39:51 ckarai Exp $ QByteArray _path(QFile::encodeName(arcTempDir + file)); QT_STATBUF buff; if (QT_LSTAT(_path.data(), &buff) == -1) { if (errno == EACCES) error(KIO::ERR_ACCESS_DENIED, getPath(url)); else error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } if (S_ISDIR(buff.st_mode)) { error(KIO::ERR_IS_DIRECTORY, getPath(url)); return; } if (!S_ISREG(buff.st_mode)) { error(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url)); return; } int fd = QT_OPEN(_path.data(), O_RDONLY); if (fd < 0) { error(KIO::ERR_CANNOT_OPEN_FOR_READING, getPath(url)); return; } // Determine the mimetype of the file to be retrieved, and emit it. // This is mandatory in all slaves (for KRun/BrowserRun to work). QMimeDatabase db; QMimeType mt = db.mimeTypeForFile(arcTempDir + file); if (mt.isValid()) emit mimeType(mt.name()); KIO::filesize_t processed_size = 0; QString resumeOffset = metaData("resume"); if (!resumeOffset.isEmpty()) { bool ok; KIO::fileoffset_t offset = resumeOffset.toLongLong(&ok); if (ok && (offset > 0) && (offset < buff.st_size)) { if (QT_LSEEK(fd, offset, SEEK_SET) == offset) { canResume(); processed_size = offset; } } } totalSize(buff.st_size); char buffer[ MAX_IPC_SIZE ]; while (1) { int n = ::read(fd, buffer, MAX_IPC_SIZE); if (n == -1) { if (errno == EINTR) continue; error(KIO::ERR_COULD_NOT_READ, getPath(url)); ::close(fd); return; } if (n == 0) break; // Finished { QByteArray array = QByteArray::fromRawData(buffer, n); data(array); } processed_size += n; } data(QByteArray()); ::close(fd); processedSize(buff.st_size); finished(); if (decompressToFile) QFile(arcTempDir + file).remove(); return; } // send empty buffer to mark EOF data(QByteArray()); finished(); } void kio_krarcProtocol::del(QUrl const & url, bool isFile) { KRFUNC; KRDEBUG(getPath(url)); if (!checkWriteSupport()) return; if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (delCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Deleting files from %1 archives is not supported", arcType)); return; } if (!findFileEntry(url)) { if ((arcType != "arj" && arcType != "lha") || isFile) { error(KIO::ERR_DOES_NOT_EXIST, getPath(url)); return; } } QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1); if (!isFile && file.right(1) != DIR_SEPARATOR) { if (arcType == "zip") file = file + DIR_SEPARATOR; } KrLinecountingProcess proc; proc << delCmd << getPath(arcFile->url()) << localeEncodedString(file); infoMessage(i18n("Deleting %1...", url.fileName())); SET_KRCODEC proc.start(); RESET_KRCODEC proc.waitForFinished(); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(ERR_COULD_NOT_WRITE, getPath(url) + "\n\n" + proc.getErrorMsg()); return; } // force a refresh of archive information initDirDict(url, true); finished(); } void kio_krarcProtocol::stat(const QUrl &url) { KRFUNC; KRDEBUG(getPath(url)); if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (listCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Accessing files is not supported with %1 archives", arcType)); return; } QString path = getPath(url, QUrl::StripTrailingSlash); QUrl newUrl = url; // but treat the archive itself as the archive root if (path == getPath(arcFile->url(), QUrl::StripTrailingSlash)) { newUrl.setPath(path + DIR_SEPARATOR); path = getPath(newUrl); } // we might be stating a real file if (QFileInfo(path).exists()) { QT_STATBUF buff; QT_STAT(path.toLocal8Bit(), &buff); QString mime; QMimeDatabase db; QMimeType result = db.mimeTypeForFile(path); if (result.isValid()) mime = result.name(); statEntry(KFileItem(QUrl::fromLocalFile(path), mime, buff.st_mode).entry()); finished(); return; } UDSEntry* entry = findFileEntry(newUrl); if (entry) { statEntry(*entry); finished(); } else error(KIO::ERR_DOES_NOT_EXIST, path); } void kio_krarcProtocol::copy(const QUrl &url, const QUrl &dest, int, KIO::JobFlags flags) { KRDEBUG("source: " << url.path() << " dest: " << dest.path()); if (!checkWriteSupport()) return; bool overwrite = !!(flags & KIO::Overwrite); // KDE HACK: opening the password dlg in copy causes error for the COPY, and further problems // that's why encrypted files are not allowed to copy if (!encrypted && dest.isLocalFile()) do { if (url.fileName() != dest.fileName()) break; if (QTextCodec::codecForLocale()->name() != codec->name()) break; //the file exists and we don't want to overwrite if ((!overwrite) && (QFile(getPath(dest)).exists())) { error((int)ERR_FILE_ALREADY_EXIST, QString(QFile::encodeName(getPath(dest)))); return; }; if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (newArchiveURL && !initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } UDSEntry* entry = findFileEntry(url); if (copyCmd.isEmpty() || !entry) break; QString file = getPath(url).mid(getPath(arcFile->url()).length() + 1); QString destDir = getPath(dest, QUrl::StripTrailingSlash); if (!QDir(destDir).exists()) { int ndx = destDir.lastIndexOf(DIR_SEPARATOR_CHAR); if (ndx != -1) destDir.truncate(ndx + 1); } QDir::setCurrent(destDir); QString escapedFilename = file; if(arcType == "zip") { // left bracket needs to be escaped escapedFilename.replace('[', "[[]"); } KrLinecountingProcess proc; proc << copyCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash) << escapedFilename; if (arcType == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); proc.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect infoMessage(i18n("Unpacking %1...", url.fileName())); proc.start(); proc.waitForFinished(); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(KIO::ERR_COULD_NOT_WRITE, getPath(dest, QUrl::StripTrailingSlash) + "\n\n" + proc.getErrorMsg()); return; } if (!QFileInfo(getPath(dest, QUrl::StripTrailingSlash)).exists()) { error(KIO::ERR_COULD_NOT_WRITE, getPath(dest, QUrl::StripTrailingSlash)); return; } processedSize(KFileItem(*entry, url).size()); finished(); QDir::setCurrent(QDir::rootPath()); /* for being able to umount devices after copying*/ return; } while (0); if (encrypted) KRDEBUG("ERROR: " << url.path() << " is encrypted."); if (!dest.isLocalFile()) KRDEBUG("ERROR: " << url.path() << " is not a local file."); // CMD_COPY is no more in KF5 - replaced with 74 value (as stated in kio/src/core/commands_p.h, which could be found in cgit.kde.org/kio.git/tree) error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, 74)); } void kio_krarcProtocol::rename(const QUrl& src, const QUrl& dest, KIO::JobFlags flags) { Q_UNUSED(flags); KRDEBUG("renaming from: " << src.path() << " to: " << dest.path()); KRDEBUG("command: " << arcPath); if (!checkWriteSupport()) { return; } if (renCmd.isEmpty()) { error(KIO::ERR_CANNOT_RENAME, src.fileName()); return; } if (src.fileName() == dest.fileName()) { return; } KrLinecountingProcess proc; proc << renCmd << arcPath << src.path().remove(arcPath + '/') << dest.path().remove(arcPath + '/'); proc.start(); proc.waitForFinished(); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) { error(KIO::ERR_CANNOT_RENAME, src.fileName()); return; } finished(); } void kio_krarcProtocol::listDir(const QUrl &url) { KRFUNC; KRDEBUG(getPath(url)); if (!setArcFile(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } if (listCmd.isEmpty()) { error(ERR_UNSUPPORTED_ACTION, i18n("Listing directories is not supported for %1 archives", arcType)); return; } QString path = getPath(url); if (path.right(1) != DIR_SEPARATOR) path = path + DIR_SEPARATOR; // it might be a real dir ! if (QFileInfo(path).exists()) { if (QFileInfo(path).isDir()) { QUrl redir; redir.setPath(getPath(url)); redirection(redir); finished(); } else { // maybe it's an archive ! error(ERR_IS_FILE, path); } return; } if (!initDirDict(url)) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } QString arcDir = path.mid(getPath(arcFile->url()).length()); arcDir.truncate(arcDir.lastIndexOf(DIR_SEPARATOR)); if (arcDir.right(1) != DIR_SEPARATOR) arcDir = arcDir + DIR_SEPARATOR; if (dirDict.find(arcDir) == dirDict.end()) { error(ERR_CANNOT_ENTER_DIRECTORY, getPath(url)); return; } UDSEntryList* dirList = dirDict[ arcDir ]; totalSize(dirList->size()); listEntries(*dirList); finished(); } bool kio_krarcProtocol::setArcFile(const QUrl &url) { KRFUNC; KRDEBUG(url.fileName()); QString path = getPath(url); time_t currTime = time(nullptr); archiveChanged = true; newArchiveURL = true; // is the file already set ? if (arcFile && getPath(arcFile->url(), QUrl::StripTrailingSlash) == path.left(getPath(arcFile->url(), QUrl::StripTrailingSlash).length())) { newArchiveURL = false; // Has it changed ? KFileItem* newArcFile = new KFileItem(arcFile->url(), QString(), arcFile->mode()); if (metaData("Charset") != currentCharset || !newArcFile->cmp(*arcFile)) { currentCharset = metaData("Charset"); codec = QTextCodec::codecForName(currentCharset.toLatin1()); if (codec == nullptr) codec = QTextCodec::codecForMib(4 /* latin-1 */); delete arcFile; password.clear(); extArcReady = false; arcFile = newArcFile; } else { // same old file delete newArcFile; archiveChanged = false; if (encrypted && password.isNull()) initArcParameters(); } } else { // it's a new file... extArcReady = false; // new archive file means new dirDict, too dirDict.clear(); if (arcFile) { delete arcFile; password.clear(); arcFile = nullptr; } QString newPath = path; if (newPath.right(1) != DIR_SEPARATOR) newPath = newPath + DIR_SEPARATOR; for (int pos = 0; pos >= 0; pos = newPath.indexOf(DIR_SEPARATOR, pos + 1)) { QFileInfo qfi(newPath.left(pos)); if (qfi.exists() && !qfi.isDir()) { QT_STATBUF stat_p; QT_LSTAT(newPath.left(pos).toLocal8Bit(), &stat_p); arcFile = new KFileItem(QUrl::fromLocalFile(newPath.left(pos)), QString(), stat_p.st_mode); break; } } if (!arcFile) { // KRDEBUG("ERROR: " << path << " does not exist."); error(ERR_DOES_NOT_EXIST, path); return false; // file not found } currentCharset = metaData("Charset"); codec = QTextCodec::codecForName(currentCharset.toLatin1()); if (codec == nullptr) codec = QTextCodec::codecForMib(4 /* latin-1 */); } /* FIX: file change can only be detected if the timestamp between the two consequent changes is more than 1s. If the archive is continuously changing (check: move files inside the archive), krarc may errorneusly think, that the archive file is unchanged, because the timestamp is the same as the previous one. This situation can only occur if the modification time equals with the current time. While this condition is true, we can say, that the archive is changing, so content reread is always necessary during that period. */ if (archiveChanging) archiveChanged = true; archiveChanging = (currTime == (time_t)arcFile->time(KFileItem::ModificationTime).toTime_t()); arcPath = getPath(arcFile->url(), QUrl::StripTrailingSlash); arcType = detectArchive(encrypted, arcPath); if (arcType == "tbz") arcType = "bzip2"; else if (arcType == "tgz") arcType = "gzip"; else if (arcType == "tlz") arcType = "lzma"; else if (arcType == "txz") arcType = "xz"; if (arcType.isEmpty()) { arcType = arcFile->mimetype(); arcType = getShortTypeFromMime(arcType); if (arcType == "jar") arcType = "zip"; } return initArcParameters(); } bool kio_krarcProtocol::initDirDict(const QUrl &url, bool forced) { KRFUNC; KRDEBUG(getPath(url)); // set the archive location //if( !setArcFile(getPath(url)) ) return false; // no need to rescan the archive if it's not changed // KRDEBUG("achiveChanged: " << archiveChanged << " forced: " << forced); if (!archiveChanged && !forced) { // KRDEBUG("doing nothing."); return true; } extArcReady = false; if (!setArcFile(url)) return false; /* if the archive was changed refresh the file information */ // write the temp file KrLinecountingProcess proc; QTemporaryFile temp; // parse the temp file if (!temp.open()) { error(ERR_COULD_NOT_READ, temp.fileName()); return false; } if (arcType != "bzip2" && arcType != "lzma" && arcType != "xz") { if (arcType == "rpm") { proc << listCmd << arcPath; proc.setStandardOutputFile(temp.fileName()); } else { proc << listCmd << getPath(arcFile->url(), QUrl::StripTrailingSlash); proc.setStandardOutputFile(temp.fileName()); } if (arcType == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); proc.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect proc.start(); proc.waitForFinished(); if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(proc.exitCode())) return false; } // clear the dir dictionary QHashIterator< QString, KIO::UDSEntryList *> lit(dirDict); while (lit.hasNext()) delete lit.next().value(); dirDict.clear(); // add the "/" directory auto* root = new UDSEntryList(); dirDict.insert(DIR_SEPARATOR, root); // and the "/" UDSEntry UDSEntry entry; entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_NAME, "."); mode_t mode = parsePermString("drwxr-xr-x"); entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT); // keep file type only entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // keep permissions only root->append(entry); if (arcType == "bzip2" || arcType == "lzma" || arcType == "xz") abort(); char buf[1000]; QString line; int lineNo = 0; bool invalidLine = false; // the rar list is started with a ------ line. if (arcType == "rar" || arcType == "arj" || arcType == "lha" || arcType == "7z") { while (temp.readLine(buf, 1000) != -1) { line = decodeString(buf); if (line.startsWith(QLatin1String("----------"))) break; } } while (temp.readLine(buf, 1000) != -1) { line = decodeString(buf); if (arcType == "rar") { // the rar list is ended with a ------ line. if (line.startsWith(QLatin1String("----------"))) { invalidLine = !invalidLine; break; } if (invalidLine) continue; else { if (line[0] == '*') // encrypted archives starts with '*' line[0] = ' '; } } if (arcType == "ace") { // the ace list begins with a number. if (!line[0].isDigit()) continue; } if (arcType == "arj") { // the arj list is ended with a ------ line. if (line.startsWith(QLatin1String("----------"))) { invalidLine = !invalidLine; continue; } if (invalidLine) continue; else { temp.readLine(buf, 1000); line = line + decodeString(buf); temp.readLine(buf, 1000); line = line + decodeString(buf); temp.readLine(buf, 1000); line = line + decodeString(buf); } } if (arcType == "lha" || arcType == "7z") { // the arj list is ended with a ------ line. if (line.startsWith(QLatin1String("----------"))) break; } parseLine(lineNo++, line.trimmed()); } // close and delete our file temp.close(); archiveChanged = false; // KRDEBUG("done."); return true; } QString kio_krarcProtocol::findArcDirectory(const QUrl &url) { KRFUNC; KRDEBUG(url.fileName()); QString path = getPath(url); if (path.right(1) == DIR_SEPARATOR) path.truncate(path.length() - 1); if (!initDirDict(url)) { return QString(); } QString arcDir = path.mid(getPath(arcFile->url()).length()); arcDir.truncate(arcDir.lastIndexOf(DIR_SEPARATOR)); if (arcDir.right(1) != DIR_SEPARATOR) arcDir = arcDir + DIR_SEPARATOR; return arcDir; } UDSEntry* kio_krarcProtocol::findFileEntry(const QUrl &url) { KRFUNC; QString arcDir = findArcDirectory(url); if (arcDir.isEmpty()) return nullptr; QHash::iterator itef = dirDict.find(arcDir); if (itef == dirDict.end()) return nullptr; UDSEntryList* dirList = itef.value(); QString name = getPath(url); if (getPath(arcFile->url(), QUrl::StripTrailingSlash) == getPath(url, QUrl::StripTrailingSlash)) name = '.'; // the '/' case else { if (name.right(1) == DIR_SEPARATOR) name.truncate(name.length() - 1); name = name.mid(name.lastIndexOf(DIR_SEPARATOR) + 1); } UDSEntryList::iterator entry; for (entry = dirList->begin(); entry != dirList->end(); ++entry) { if ((entry->contains(KIO::UDSEntry::UDS_NAME)) && (entry->stringValue(KIO::UDSEntry::UDS_NAME) == name)) return &(*entry); } return nullptr; } QString kio_krarcProtocol::nextWord(QString &s, char d) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information s = s.trimmed(); int j = s.indexOf(d, 0); QString temp = s.left(j); // find the leftmost word. s.remove(0, j); return temp; } mode_t kio_krarcProtocol::parsePermString(QString perm) { KRFUNC; mode_t mode = 0; // file type if (perm[0] == 'd') mode |= S_IFDIR; #ifndef Q_WS_WIN if (perm[0] == 'l') mode |= S_IFLNK; #endif if (perm[0] == '-') mode |= S_IFREG; // owner permissions if (perm[1] != '-') mode |= S_IRUSR; if (perm[2] != '-') mode |= S_IWUSR; if (perm[3] != '-') mode |= S_IXUSR; #ifndef Q_WS_WIN // group permissions if (perm[4] != '-') mode |= S_IRGRP; if (perm[5] != '-') mode |= S_IWGRP; if (perm[6] != '-') mode |= S_IXGRP; // other permissions if (perm[7] != '-') mode |= S_IROTH; if (perm[8] != '-') mode |= S_IWOTH; if (perm[9] != '-') mode |= S_IXOTH; #endif return mode; } UDSEntryList* kio_krarcProtocol::addNewDir(const QString& path) { KRFUNC; UDSEntryList* dir; // check if the current dir exists QHash::iterator itef = dirDict.find(path); if (itef != dirDict.end()) return itef.value(); // set dir to the parent dir dir = addNewDir(path.left(path.lastIndexOf(DIR_SEPARATOR, -2) + 1)); // add a new entry in the parent dir QString name = path.mid(path.lastIndexOf(DIR_SEPARATOR, -2) + 1); name = name.left(name.length() - 1); if (name == "." || name == "..") { // entries with these names wouldn't be displayed // don't translate since this is an internal error QString err = QString("Cannot handle path: ") + path; // KRDEBUG("ERROR: " << err); error(KIO::ERR_INTERNAL, err); exit(); } UDSEntry entry; entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_NAME, name); mode_t mode = parsePermString("drwxr-xr-x"); entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT); // keep file type only entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // keep permissions only entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_SIZE, 0); entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_MODIFICATION_TIME, arcFile->time(KFileItem::ModificationTime).toTime_t()); dir->append(entry); // create a new directory entry and add it.. dir = new UDSEntryList(); dirDict.insert(path, dir); return dir; } void kio_krarcProtocol::parseLine(int lineNo, QString line) { KRFUNC; UDSEntryList* dir; UDSEntry entry; QString owner; QString group; QString symlinkDest; QString perm; mode_t mode = 0666; size_t size = 0; time_t time = ::time(nullptr); QString fullName; if (arcType == "zip") { // permissions perm = nextWord(line); // ignore the next 2 fields nextWord(line); nextWord(line); // size size = nextWord(line).toLong(); // ignore the next 2 fields nextWord(line);nextWord(line); // date & time QString d = nextWord(line); QDate qdate(d.mid(0, 4).toInt(), d.mid(4, 2).toInt(), d.mid(6, 2).toInt()); QTime qtime(d.mid(9, 2).toInt(), d.mid(11, 2).toInt(), d.mid(13, 2).toInt()); time = QDateTime(qdate, qtime).toTime_t(); // full name fullName = nextWord(line, '\n'); if (perm.length() != 10) perm = (perm.at(0) == 'd' || fullName.endsWith(DIR_SEPARATOR)) ? "drwxr-xr-x" : "-rw-r--r--" ; mode = parsePermString(perm); } if (arcType == "rar") { // permissions perm = nextWord(line); // size size = nextWord(line).toLong(); // ignore the next 2 fields : packed size and compression ration nextWord(line); nextWord(line); // date & time QString d = nextWord(line); int year = 1900 + d.mid(6, 2).toInt(); if (year < 1930) year += 100; QDate qdate(year, d.mid(3, 2).toInt(), d.mid(0, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0); time = QDateTime(qdate, qtime).toTime_t(); // checksum : ignored nextWord(line); // full name fullName = nextWord(line, '\n'); if (perm.length() == 7) { // windows rar permission format bool isDir = (perm.at(1).toLower() == 'd'); bool isReadOnly = (perm.at(2).toLower() == 'r'); perm = isDir ? "drwxr-xr-x" : "-rw-r--r--"; if (isReadOnly) perm[ 2 ] = '-'; } if (perm.length() != 10) perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--" ; mode = parsePermString(perm); } if (arcType == "arj") { nextWord(line); // full name fullName = nextWord(line, '\n'); // ignore the next 2 fields nextWord(line); nextWord(line); // size size = nextWord(line).toLong(); // ignore the next 2 fields nextWord(line); nextWord(line); // date & time QString d = nextWord(line); int year = 1900 + d.mid(0, 2).toInt(); if (year < 1930) year += 100; QDate qdate(year, d.mid(3, 2).toInt(), d.mid(6, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0); time = QDateTime(qdate, qtime).toTime_t(); // permissions perm = nextWord(line); if (perm.length() != 10) perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--" ; mode = parsePermString(perm); } if (arcType == "rpm") { // full name fullName = nextWord(line); // size size = nextWord(line).toULong(); // date & time time = nextWord(line).toULong(); // next field is md5sum, ignore it nextWord(line); // permissions mode = nextWord(line).toULong(nullptr, 8); // Owner & Group owner = nextWord(line); group = nextWord(line); // symlink destination #ifndef Q_WS_WIN if (S_ISLNK(mode)) { // ignore the next 3 fields nextWord(line); nextWord(line); nextWord(line); symlinkDest = nextWord(line); } #endif } if (arcType == "gzip") { if (!lineNo) return; //ignore the first line // first field is uncompressed size - ignore it nextWord(line); // size size = nextWord(line).toULong(); // ignore the next field nextWord(line); // full name fullName = nextWord(line); fullName = fullName.mid(fullName.lastIndexOf(DIR_SEPARATOR) + 1); } if (arcType == "lzma") { fullName = arcFile->name(); if (fullName.endsWith(QLatin1String("lzma"))) { fullName.truncate(fullName.length() - 5); } mode = arcFile->mode(); size = arcFile->size(); } if (arcType == "xz") { fullName = arcFile->name(); if (fullName.endsWith(QLatin1String("xz"))) { fullName.truncate(fullName.length() - 3); } mode = arcFile->mode(); size = arcFile->size(); } if (arcType == "bzip2") { // There is no way to list bzip2 files, so we take our information from // the archive itself... fullName = arcFile->name(); if (fullName.endsWith(QLatin1String("bz2"))) { fullName.truncate(fullName.length() - 4); } mode = arcFile->mode(); size = arcFile->size(); } if (arcType == "lha") { // permissions perm = nextWord(line); if (perm.length() != 10) perm = (perm.at(0) == 'd') ? "drwxr-xr-x" : "-rw-r--r--" ; mode = parsePermString(perm); // ignore the next field nextWord(line); // size size = nextWord(line).toLong(); // ignore the next field nextWord(line); // date & time int month = (QString("Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec").split(',')).indexOf(nextWord(line)) + 1; int day = nextWord(line).toInt(); int year = QDate::currentDate().year(); QString third = nextWord(line); QTime qtime; if (third.contains(":")) qtime = QTime::fromString(third); else year = third.toInt(); QDate qdate(year, month, day); time = QDateTime(qdate, qtime).toTime_t(); // full name fullName = nextWord(line, '\n'); } if (arcType == "ace") { // date & time QString d = nextWord(line); int year = 1900 + d.mid(6, 2).toInt(); if (year < 1930) year += 100; QDate qdate(year, d.mid(3, 2).toInt(), d.mid(0, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0); time = QDateTime(qdate, qtime).toTime_t(); // ignore the next field nextWord(line); // size size = nextWord(line).toLong(); // ignore the next field nextWord(line); // full name fullName = nextWord(line, '\n'); if (fullName[ 0 ] == '*') // encrypted archives starts with '*' fullName = fullName.mid(1); } if (arcType == "deb") { // permissions perm = nextWord(line); mode = parsePermString(perm); // Owner & Group owner = nextWord(line, DIR_SEPARATOR_CHAR); group = nextWord(line).mid(1); // size size = nextWord(line).toLong(); // date & time QString d = nextWord(line); QDate qdate(d.mid(0, 4).toInt(), d.mid(5, 2).toInt(), d.mid(8, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), 0); time = QDateTime(qdate, qtime).toTime_t(); // full name fullName = nextWord(line, '\n').mid(1); //if ( fullName.right( 1 ) == "/" ) return; if (fullName.contains("->")) { symlinkDest = fullName.mid(fullName.indexOf("->") + 2); fullName = fullName.left(fullName.indexOf("->") - 1); } } if (arcType == "7z") { // date & time QString d = nextWord(line); QDate qdate(d.mid(0, 4).toInt(), d.mid(5, 2).toInt(), d.mid(8, 2).toInt()); QString t = nextWord(line); QTime qtime(t.mid(0, 2).toInt(), t.mid(3, 2).toInt(), t.mid(6, 2).toInt()); time = QDateTime(qdate, qtime).toTime_t(); // permissions perm = nextWord(line); bool isDir = (perm.at(0).toLower() == 'd'); bool isReadOnly = (perm.at(1).toLower() == 'r'); perm = isDir ? "drwxr-xr-x" : "-rw-r--r--"; if (isReadOnly) perm[ 2 ] = '-'; mode = parsePermString(perm); // size size = nextWord(line).toLong(); // ignore the next 15 characters line = line.mid(15); // full name fullName = nextWord(line, '\n'); } if (fullName.right(1) == DIR_SEPARATOR) fullName = fullName.left(fullName.length() - 1); if (!fullName.startsWith(DIR_SEPARATOR)) fullName = DIR_SEPARATOR + fullName; QString path = fullName.left(fullName.lastIndexOf(DIR_SEPARATOR) + 1); // set/create the directory UDSEntryList QHash::iterator itef = dirDict.find(path); if (itef == dirDict.end()) dir = addNewDir(path); else dir = itef.value(); QString name = fullName.mid(fullName.lastIndexOf(DIR_SEPARATOR) + 1); // file name entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_NAME, name); // file type entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_FILE_TYPE, mode & S_IFMT); // keep file type only // file permissions entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_ACCESS, mode & 07777); // keep permissions only // file size entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_SIZE, size); // modification time entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_MODIFICATION_TIME, time); // link destination if (!symlinkDest.isEmpty()) { entry.UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_LINK_DEST, symlinkDest); } if (S_ISDIR(mode)) { fullName = fullName + DIR_SEPARATOR; if (dirDict.find(fullName) == dirDict.end()) dirDict.insert(fullName, new UDSEntryList()); else { // try to overwrite an existing entry UDSEntryList::iterator entryIt; for (entryIt = dir->begin(); entryIt != dir->end(); ++entryIt) { if (entryIt->contains(KIO::UDSEntry::UDS_NAME) && entryIt->stringValue(KIO::UDSEntry::UDS_NAME) == name) { entryIt->UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_MODIFICATION_TIME, time); entryIt->UDS_ENTRY_INSERT(KIO::UDSEntry::UDS_ACCESS, mode); return; } } return; // there is already an entry for this directory } } // multi volume archives can add a file twice, use only one UDSEntryList::iterator dirEntryIt; for (dirEntryIt = dir->begin(); dirEntryIt != dir->end(); ++dirEntryIt) if (dirEntryIt->contains(KIO::UDSEntry::UDS_NAME) && dirEntryIt->stringValue(KIO::UDSEntry::UDS_NAME) == name) return; dir->append(entry); } bool kio_krarcProtocol::initArcParameters() { KRFUNC; KRDEBUG("arcType: " << arcType); noencoding = false; cmd.clear(); listCmd = QStringList(); getCmd = QStringList(); copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); renCmd = QStringList(); if (arcType == "zip") { noencoding = true; cmd = fullPathName("unzip"); listCmd << fullPathName("unzip") << "-ZTs-z-t-h"; getCmd << fullPathName("unzip") << "-p"; copyCmd << fullPathName("unzip") << "-jo"; if (QStandardPaths::findExecutable(QStringLiteral("zip")).isEmpty()) { delCmd = QStringList(); putCmd = QStringList(); } else { delCmd << fullPathName("zip") << "-d"; putCmd << fullPathName("zip") << "-ry"; } if (!QStandardPaths::findExecutable(QStringLiteral("7za")).isEmpty()) { renCmd << fullPathName("7za") << "rn"; } if (!getPassword().isEmpty()) { getCmd << "-P" << password; copyCmd << "-P" << password; putCmd << "-P" << password; } } else if (arcType == "rar") { noencoding = true; if (QStandardPaths::findExecutable(QStringLiteral("rar")).isEmpty()) { cmd = fullPathName("unrar"); listCmd << fullPathName("unrar") << "-c-" << "-v" << "v"; getCmd << fullPathName("unrar") << "p" << "-ierr" << "-idp" << "-c-" << "-y"; copyCmd << fullPathName("unrar") << "e" << "-y"; delCmd = QStringList(); putCmd = QStringList(); } else { cmd = fullPathName("rar"); listCmd << fullPathName("rar") << "-c-" << "-v" << "v"; getCmd << fullPathName("rar") << "p" << "-ierr" << "-idp" << "-c-" << "-y"; copyCmd << fullPathName("rar") << "e" << "-y"; delCmd << fullPathName("rar") << "d"; putCmd << fullPathName("rar") << "-r" << "a"; } if (!getPassword().isEmpty()) { getCmd << QString("-p%1").arg(password); listCmd << QString("-p%1").arg(password); copyCmd << QString("-p%1").arg(password); if (!putCmd.isEmpty()) { putCmd << QString("-p%1").arg(password); delCmd << QString("-p%1").arg(password); } } } else if (arcType == "rpm") { cmd = fullPathName("rpm"); listCmd << fullPathName("rpm") << "--dump" << "-lpq"; getCmd << fullPathName("cpio") << "--force-local" << "--no-absolute-filenames" << "-iuvdF"; delCmd = QStringList(); putCmd = QStringList(); copyCmd = QStringList(); } else if (arcType == "gzip") { cmd = fullPathName("gzip"); listCmd << fullPathName("gzip") << "-l"; getCmd << fullPathName("gzip") << "-dc"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "bzip2") { cmd = fullPathName("bzip2"); listCmd << fullPathName("bzip2"); getCmd << fullPathName("bzip2") << "-dc"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "lzma") { cmd = fullPathName("lzma"); listCmd << fullPathName("lzma"); getCmd << fullPathName("lzma") << "-dc"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "xz") { cmd = fullPathName("xz"); listCmd << fullPathName("xz"); getCmd << fullPathName("xz") << "-dc"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "arj") { cmd = fullPathName("arj"); listCmd << fullPathName("arj") << "v" << "-y" << "-v"; getCmd << fullPathName("arj") << "-jyov" << "-v" << "e"; copyCmd << fullPathName("arj") << "-jyov" << "-v" << "e"; delCmd << fullPathName("arj") << "d"; putCmd << fullPathName("arj") << "-r" << "a"; if (!getPassword().isEmpty()) { getCmd << QString("-g%1").arg(password); copyCmd << QString("-g%1").arg(password); putCmd << QString("-g%1").arg(password); } } else if (arcType == "lha") { cmd = fullPathName("lha"); listCmd << fullPathName("lha") << "l"; getCmd << fullPathName("lha") << "pq"; copyCmd << fullPathName("lha") << "eif"; delCmd << fullPathName("lha") << "d"; putCmd << fullPathName("lha") << "a"; } else if (arcType == "ace") { cmd = fullPathName("unace"); listCmd << fullPathName("unace") << "v"; getCmd << fullPathName("unace") << "e" << "-o"; copyCmd << fullPathName("unace") << "e" << "-o"; delCmd = QStringList(); putCmd = QStringList(); if (!getPassword().isEmpty()) { getCmd << QString("-p%1").arg(password); copyCmd << QString("-p%1").arg(password); } } else if (arcType == "deb") { cmd = fullPathName("dpkg"); listCmd << fullPathName("dpkg") << "-c"; getCmd << fullPathName("tar") << "xvf"; copyCmd = QStringList(); delCmd = QStringList(); putCmd = QStringList(); } else if (arcType == "7z") { noencoding = true; cmd = fullPathName("7z"); if (QStandardPaths::findExecutable(cmd).isEmpty()) cmd = fullPathName("7za"); listCmd << cmd << "l" << "-y"; getCmd << cmd << "e" << "-y"; copyCmd << cmd << "e" << "-y"; delCmd << cmd << "d" << "-y"; putCmd << cmd << "a" << "-y"; renCmd << cmd << "rn"; if (!getPassword().isEmpty()) { getCmd << QString("-p%1").arg(password); listCmd << QString("-p%1").arg(password); copyCmd << QString("-p%1").arg(password); if (!putCmd.isEmpty()) { putCmd << QString("-p%1").arg(password); delCmd << QString("-p%1").arg(password); } } } // checking if it's an absolute path #ifdef Q_WS_WIN if (cmd.length() > 2 && cmd[ 0 ].isLetter() && cmd[ 1 ] == ':') return true; #else if (cmd.startsWith(DIR_SEPARATOR)) return true; #endif if (QStandardPaths::findExecutable(cmd).isEmpty()) { error(KIO::ERR_CANNOT_LAUNCH_PROCESS, cmd + i18n("\nMake sure that the %1 binary is installed properly on your system.", cmd)); KRDEBUG("Failed to find cmd: " << cmd); return false; } return true; } bool kio_krarcProtocol::checkStatus(int exitCode) { KRFUNC; KRDEBUG(exitCode); return KrArcBaseManager::checkStatus(arcType, exitCode); } void kio_krarcProtocol::checkIf7zIsEncrypted(bool &encrypted, QString fileName) { KRFUNC; if (encryptedArchPath == fileName) encrypted = true; else { // we try to find whether the 7z archive is encrypted // this is hard as the headers are also compressed QString tester = fullPathName("7z"); if (QStandardPaths::findExecutable(tester).isEmpty()) { KRDEBUG("A 7z program was not found"); tester = fullPathName("7za"); if (QStandardPaths::findExecutable(tester).isEmpty()) { KRDEBUG("A 7za program was not found"); return; } } QString testCmd = tester + " t -y "; lastData = encryptedArchPath = ""; KrLinecountingProcess proc; proc << testCmd << fileName; connect(&proc, &KrLinecountingProcess::newOutputData, this, &kio_krarcProtocol::checkOutputForPassword); proc.start(); proc.waitForFinished(); encrypted = this->encrypted; if (encrypted) encryptedArchPath = fileName; } } void kio_krarcProtocol::checkOutputForPassword(KProcess * proc, QByteArray & buf) { KRFUNC; QString data = QString(buf); QString checkable = lastData + data; QStringList lines = checkable.split('\n'); lastData = lines[ lines.count() - 1 ]; for (int i = 0; i != lines.count(); i++) { QString line = lines[ i ].trimmed().toLower(); int ndx = line.indexOf("testing"); if (ndx >= 0) line.truncate(ndx); if (line.isEmpty()) continue; if (line.contains("password") && line.contains("enter")) { KRDEBUG("Encrypted 7z archive found!"); encrypted = true; proc->kill(); return; } } } void kio_krarcProtocol::invalidatePassword() { KRFUNC; KRDEBUG(getPath(arcFile->url(), QUrl::StripTrailingSlash) + DIR_SEPARATOR); if (!encrypted) return; KIO::AuthInfo authInfo; authInfo.caption = i18n("Krarc Password Dialog"); authInfo.username = "archive"; authInfo.readOnly = true; authInfo.keepPassword = true; authInfo.verifyPath = true; QString fileName = getPath(arcFile->url(), QUrl::StripTrailingSlash); authInfo.url = QUrl::fromLocalFile(ROOT_DIR); authInfo.url.setHost(fileName /*.replace('/','_')*/); authInfo.url.setScheme("krarc"); password.clear(); cacheAuthentication(authInfo); } QString kio_krarcProtocol::getPassword() { KRFUNC; KRDEBUG("Encrypted: " << encrypted); if (!password.isNull()) return password; if (!encrypted) return (password = ""); KIO::AuthInfo authInfo; authInfo.caption = i18n("Krarc Password Dialog"); authInfo.username = "archive"; authInfo.readOnly = true; authInfo.keepPassword = true; authInfo.verifyPath = true; QString fileName = getPath(arcFile->url(), QUrl::StripTrailingSlash); authInfo.url = QUrl::fromLocalFile(ROOT_DIR); authInfo.url.setHost(fileName /*.replace('/','_')*/); authInfo.url.setScheme("krarc"); if (checkCachedAuthentication(authInfo) && !authInfo.password.isNull()) { KRDEBUG(authInfo.password); return (password = authInfo.password); } authInfo.password.clear(); #if KIO_VERSION_MINOR >= 24 int errCode = openPasswordDialogV2(authInfo, i18n("Accessing the file requires a password.")); if (!errCode && !authInfo.password.isNull()) { #else if (openPasswordDialog(authInfo, i18n("Accessing the file requires a password.")) && !authInfo.password.isNull()) { #endif KRDEBUG(authInfo.password); return (password = authInfo.password); #if KIO_VERSION_MINOR >= 24 } else { error(errCode, QString()); #endif } KRDEBUG(password); return password; } QString kio_krarcProtocol::detectFullPathName(QString name) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information KRDEBUG(name); name = name + EXEC_SUFFIX; QStringList path = QString::fromLocal8Bit(qgetenv("PATH")).split(':'); for (auto & it : path) { if (QDir(it).exists(name)) { QString dir = it; if (!dir.endsWith(DIR_SEPARATOR)) dir += DIR_SEPARATOR; return dir + name; } } return name; } QString kio_krarcProtocol::fullPathName(const QString& name) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information KRDEBUG(name); QString supposedName = confGrp.readEntry(name, QString()); if (supposedName.isEmpty()) supposedName = detectFullPathName(name); return supposedName; } QString kio_krarcProtocol::localeEncodedString(QString str) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information if (noencoding) return str; QByteArray array = codec->fromUnicode(str); // encoding the byte array to QString, mapping 0x0000-0x00FF to 0xE000-0xE0FF // see KrArcCodec for more explanation int size = array.size(); QString result; const char *data = array.data(); for (int i = 0; i != size; i++) { unsigned short ch = (((int)data[ i ]) & 0xFF) + 0xE000; // user defined character result.append(QChar(ch)); } return result; } QByteArray kio_krarcProtocol::encodeString(const QString& str) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information if (noencoding) return QTextCodec::codecForLocale()->fromUnicode(str); return codec->fromUnicode(str); } QString kio_krarcProtocol::decodeString(char * buf) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information if (noencoding) return QTextCodec::codecForLocale()->toUnicode(buf); return codec->toUnicode(buf); } QString kio_krarcProtocol::getPath(const QUrl &url, QUrl::FormattingOptions options) { // Note: KRFUNC was not used here in order to avoid filling the log with too much information QString path = url.adjusted(options).path(); REPLACE_DIR_SEP2(path); #ifdef Q_WS_WIN if (path.startsWith(DIR_SEPARATOR)) { int p = 1; while (p < path.length() && path[ p ] == DIR_SEPARATOR_CHAR) p++; /* /C:/Folder */ if (p + 2 <= path.length() && path[ p ].isLetter() && path[ p + 1 ] == ':') { path = path.mid(p); } } #endif return path; } #endif // KRARC_ENABLED diff --git a/krArc/krarc.h b/krArc/krarc.h index db5faa96..001f639f 100644 --- a/krArc/krarc.h +++ b/krArc/krarc.h @@ -1,145 +1,145 @@ /***************************************************************************** * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Shie Erlich * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRARC_H #define KRARC_H // QtCore #include #include #include #include #include #include #include #include #include #include "krarcbasemanager.h" #include "krlinecountingprocess.h" #include "../krusader/krdebuglogger.h" class KFileItem; class QByteArray; class QTextCodec; class kio_krarcProtocol : public QObject, public KIO::SlaveBase, public KrArcBaseManager { Q_OBJECT public: kio_krarcProtocol(const QByteArray &pool_socket, const QByteArray &app_socket); ~kio_krarcProtocol() override; void stat(const QUrl &url) Q_DECL_OVERRIDE; void get(const QUrl &url) Q_DECL_OVERRIDE; void put(const QUrl &url, int permissions, KIO::JobFlags flags) Q_DECL_OVERRIDE; void mkdir(const QUrl &url, int permissions) Q_DECL_OVERRIDE; void listDir(const QUrl &url) Q_DECL_OVERRIDE; void del(QUrl const & url, bool isFile) Q_DECL_OVERRIDE; void copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) Q_DECL_OVERRIDE; void rename(const QUrl &src, const QUrl & dest, KIO::JobFlags flags) Q_DECL_OVERRIDE; public slots: void receivedData(KProcess *, QByteArray &); void checkOutputForPassword(KProcess *, QByteArray &); protected: virtual bool initDirDict(const QUrl &url, bool forced = false); virtual bool initArcParameters(); void checkIf7zIsEncrypted(bool &, QString) Q_DECL_OVERRIDE; virtual void parseLine(int lineNo, QString line); virtual bool setArcFile(const QUrl &url); virtual QString getPassword(); virtual void invalidatePassword(); QString getPath(const QUrl &url, QUrl::FormattingOptions options = nullptr); QString localeEncodedString(QString str); QByteArray encodeString(const QString&); QString decodeString(char *); // archive specific commands QString cmd; ///< the archiver name. QStringList listCmd; ///< list files. QStringList getCmd; ///< unpack files command. QStringList delCmd; ///< delete files command. QStringList putCmd; ///< add file command. QStringList copyCmd; ///< copy to file command. QStringList renCmd; ///< rename file command. private: void get(const QUrl &url, int tries); /** checks if a returned status ("exit code") of an archiving-related process is OK. */ bool checkStatus(int exitCode); /** service function for parseLine. */ QString nextWord(QString &s, char d = ' '); /** translate permission string to mode_t. */ mode_t parsePermString(QString perm); /** return the name of the directory inside the archive. */ QString findArcDirectory(const QUrl &url); /** find the UDSEntry of a file in a directory. */ KIO::UDSEntry* findFileEntry(const QUrl &url); /** add a new directory (file list container). */ KIO::UDSEntryList* addNewDir(const QString& path); QString fullPathName(const QString& name); static QString detectFullPathName(QString name); bool checkWriteSupport(); QHash dirDict; //< the directories data structure. bool encrypted; //< tells whether the archive is encrypted bool archiveChanged; //< true if the archive was changed. bool archiveChanging; //< true if the archive is currently changing. bool newArchiveURL; //< true if new archive was entered for the protocol bool noencoding; //< 7z files use UTF-16, so encoding is unnecessary KIO::filesize_t decompressedLen; //< the number of the decompressed bytes KFileItem* arcFile; //< the archive file item. QString arcPath; //< the archive location QString arcTempDir; //< the currently used temp directory. QString arcType; //< the archive type. bool extArcReady; //< Used for RPM & DEB files. QString password; //< Password for the archives KConfig krConf; //< The configuration file for krusader KConfigGroup confGrp; //< the 'Dependencies' config group QString lastData; QString encryptedArchPath; QString currentCharset; QTextCodec * codec; }; #ifdef Q_WS_WIN #define DIR_SEPARATOR "/" #define DIR_SEPARATOR2 "\\" #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_CHAR2 '\\' #define REPLACE_DIR_SEP2(x) x = x.replace( DIR_SEPARATOR2, DIR_SEPARATOR ); #define ROOT_DIR "C:\\" #define EXEC_SUFFIX ".exe" #else #define DIR_SEPARATOR "/" #define DIR_SEPARATOR2 "/" #define DIR_SEPARATOR_CHAR '/' #define DIR_SEPARATOR_CHAR2 '/' #define REPLACE_DIR_SEP2(x) #define ROOT_DIR "/" #define EXEC_SUFFIX "" #endif #endif diff --git a/krArc/krarcbasemanager.cpp b/krArc/krarcbasemanager.cpp index 8bec551f..5f5f4b85 100644 --- a/krArc/krarcbasemanager.cpp +++ b/krArc/krarcbasemanager.cpp @@ -1,234 +1,234 @@ /***************************************************************************** * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Shie Erlich * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krarcbasemanager.h" #include KrArcBaseManager::AutoDetectParams KrArcBaseManager::autoDetectParams[] = {{"zip", 0, "PK\x03\x04"}, {"rar", 0, "Rar!\x1a" }, {"arj", 0, "\x60\xea" }, {"rpm", 0, "\xed\xab\xee\xdb"}, {"ace", 7, "**ACE**" }, {"bzip2", 0, "\x42\x5a\x68\x39\x31" }, {"gzip", 0, "\x1f\x8b"}, {"deb", 0, "!\ndebian-binary " }, {"7z", 0, "7z\xbc\xaf\x27\x1c" }/*, {"xz", 0, "\xfd\x37\x7a\x58\x5a\x00"}*/ }; int KrArcBaseManager::autoDetectElems = sizeof(autoDetectParams) / sizeof(AutoDetectParams); const int KrArcBaseManager::maxLenType = 5; //! Checks if a returned status ("exit code") of an archiving-related process is OK /*! \param arcType A short QString which contains an identifier of the type of the archive. \param exitCode The returned status ("exit code") of an archiving-related process. \return If the exit code means that the archiving-related process ended without errors. */ bool KrArcBaseManager::checkStatus(const QString &arcType, int exitCode) { if (arcType == "zip" || arcType == "rar" || arcType == "7z") return exitCode == 0 || exitCode == 1; else if (arcType == "ace" || arcType == "bzip2" || arcType == "lha" || arcType == "rpm" || arcType == "cpio" || arcType == "tar" || arcType == "tarz" || arcType == "tbz" || arcType == "tgz" || arcType == "arj" || arcType == "deb" || arcType == "tlz" || arcType == "txz") return exitCode == 0; else if (arcType == "gzip" || arcType == "lzma" || arcType == "xz") return exitCode == 0 || exitCode == 2; else return exitCode == 0; } QString KrArcBaseManager::detectArchive(bool &encrypted, const QString& fileName, bool checkEncrypted, bool fast) { encrypted = false; QFile arcFile(fileName); if (arcFile.open(QIODevice::ReadOnly)) { char buffer[ 1024 ]; long sizeMax = arcFile.read(buffer, sizeof(buffer)); arcFile.close(); for (int i = 0; i < autoDetectElems; i++) { QByteArray detectionString = autoDetectParams[ i ].detectionString; int location = autoDetectParams[ i ].location; int endPtr = detectionString.length() + location; if (endPtr > sizeMax) continue; int j = 0; for (; j != detectionString.length(); j++) { if (detectionString[ j ] == '?') continue; if (buffer[ location + j ] != detectionString[ j ]) break; } if (j == detectionString.length()) { QString type = autoDetectParams[ i ].type; if (type == "bzip2" || type == "gzip") { if (fast) { if (fileName.endsWith(QLatin1String(".tar.gz"))) type = "tgz"; else if (fileName.endsWith(QLatin1String(".tar.bz2"))) type = "tbz"; } else { KTar tapeArchive(fileName); if (tapeArchive.open(QIODevice::ReadOnly)) { tapeArchive.close(); if (type == "gzip") type = "tgz"; else if (type == "bzip2") type = "tbz"; } } } else if (type == "zip") encrypted = (buffer[6] & 1); else if (type == "arj") { if (sizeMax > 4) { long headerSize = ((unsigned char *)buffer)[ 2 ] + 256 * ((unsigned char *)buffer)[ 3 ]; long fileHeader = headerSize + 10; if (fileHeader + 9 < sizeMax && buffer[ fileHeader ] == (char)0x60 && buffer[ fileHeader + 1 ] == (char)0xea) encrypted = (buffer[ fileHeader + 8 ] & 1); } } else if (type == "rar") { if (sizeMax > 13 && buffer[ 9 ] == (char)0x73) { if (buffer[ 10 ] & 0x80) { // the header is encrypted? encrypted = true; } else { long offset = 7; long mainHeaderSize = ((unsigned char *)buffer)[ offset+5 ] + 256 * ((unsigned char *)buffer)[ offset+6 ]; offset += mainHeaderSize; while (offset + 10 < sizeMax) { long headerSize = ((unsigned char *)buffer)[ offset+5 ] + 256 * ((unsigned char *)buffer)[ offset+6 ]; bool isDir = (buffer[ offset+7 ] == '\0') && (buffer[ offset+8 ] == '\0') && (buffer[ offset+9 ] == '\0') && (buffer[ offset+10 ] == '\0'); if (buffer[ offset + 2 ] != (char)0x74) break; if (!isDir) { encrypted = (buffer[ offset + 3 ] & 4) != 0; break; } offset += headerSize; } } } } else if (type == "ace") { long offset = 0; long mainHeaderSize = ((unsigned char *)buffer)[ offset+2 ] + 256 * ((unsigned char *)buffer)[ offset+3 ] + 4; offset += mainHeaderSize; while (offset + 10 < sizeMax) { long headerSize = ((unsigned char *)buffer)[ offset+2 ] + 256 * ((unsigned char *)buffer)[ offset+3 ] + 4; bool isDir = (buffer[ offset+11 ] == '\0') && (buffer[ offset+12 ] == '\0') && (buffer[ offset+13 ] == '\0') && (buffer[ offset+14 ] == '\0'); if (buffer[ offset + 4 ] != (char)0x01) break; if (!isDir) { encrypted = (buffer[ offset + 6 ] & 64) != 0; break; } offset += headerSize; } } else if (type == "7z") { // encryption check is expensive, check only if it's necessary if (checkEncrypted) checkIf7zIsEncrypted(encrypted, fileName); } return type; } } if (sizeMax >= 512) { /* checking if it's a tar file */ unsigned checksum = 32 * 8; char chksum[ 9 ]; for (int i = 0; i != 512; i++) checksum += ((unsigned char *)buffer)[ i ]; for (int i = 148; i != 156; i++) checksum -= ((unsigned char *)buffer)[ i ]; sprintf(chksum, "0%o", checksum); if (!memcmp(buffer + 148, chksum, strlen(chksum))) { int k = strlen(chksum); for (; k < 8; k++) if (buffer[148+k] != 0 && buffer[148+k] != 32) break; if (k == 8) return "tar"; } } } if (fileName.endsWith(QLatin1String(".tar.lzma")) || fileName.endsWith(QLatin1String(".tlz"))) { return "tlz"; } if (fileName.endsWith(QLatin1String(".lzma"))) { return "lzma"; } if (fileName.endsWith(QLatin1String(".tar.xz")) || fileName.endsWith(QLatin1String(".txz"))) { return "txz"; } if (fileName.endsWith(QLatin1String(".xz"))) { return "xz"; } return QString(); } //! Returns a short identifier of the type of a file, obtained from the mime type of the file /*! \param mime The mime type of the file. \return A short QString which contains an identifier of the type of the file. */ QString KrArcBaseManager::getShortTypeFromMime(const QString &mime) { // 7zip files are a not a normal case because their mimetype does not // follow the norm of other types: zip, tar, lha, ace, arj, etc. if (mime == "application/x-7z-compressed") return "7z"; // If it's a rar file but its mimetype isn't "application/x-rar" if (mime == "application/x-rar-compressed") return "rar"; // The short type that will be returned QString sType = mime; int lastHyphen = sType.lastIndexOf('-'); if (lastHyphen != -1) sType = sType.mid(lastHyphen + 1); else { int lastSlash = sType.lastIndexOf('/'); if (lastSlash != -1) sType = sType.mid(lastSlash + 1); } // The identifier kept short if (sType.length() > maxLenType) sType = sType.right(maxLenType); return sType; } diff --git a/krArc/krarcbasemanager.h b/krArc/krarcbasemanager.h index 833f1cf3..55bf199f 100644 --- a/krArc/krarcbasemanager.h +++ b/krArc/krarcbasemanager.h @@ -1,59 +1,59 @@ /***************************************************************************** * Copyright (C) 2003 Rafi Yanai * * Copyright (C) 2003 Shie Erlich * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRARCBASEMANAGER_H #define KRARCBASEMANAGER_H // QtCore #include /*! * \brief An abstract base class for managing archives. */ class KrArcBaseManager { private: //! Information about a type of archive and the bytes that are used to detect it. struct AutoDetectParams { QString type; int location; QByteArray detectionString; }; static AutoDetectParams autoDetectParams[]; //! Information used to detect if a file is an archive static int autoDetectElems; //!< The size of autoDetectParams[] protected: //! The maximum length of a short QString that represents the type of a file static const int maxLenType; static bool checkStatus(const QString &, int); public: KrArcBaseManager() {} QString detectArchive(bool &, const QString&, bool = true, bool = false); virtual void checkIf7zIsEncrypted(bool &, QString) = 0; static QString getShortTypeFromMime(const QString &); virtual ~KrArcBaseManager() {} }; #endif // KRARCBASEMANAGER_H diff --git a/krArc/krlinecountingprocess.cpp b/krArc/krlinecountingprocess.cpp index 4a94c649..4890a002 100644 --- a/krArc/krlinecountingprocess.cpp +++ b/krArc/krlinecountingprocess.cpp @@ -1,66 +1,66 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krlinecountingprocess.h" KrLinecountingProcess::KrLinecountingProcess() { setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect! connect(this, &KrLinecountingProcess::readyReadStandardError, this, &KrLinecountingProcess::receivedError); connect(this, &KrLinecountingProcess::readyReadStandardOutput, [=]() { receivedOutput(); }); mergedOutput = true; } void KrLinecountingProcess::setMerge(bool value) { mergedOutput = value; } QString KrLinecountingProcess::getErrorMsg() { if (errorData.trimmed().isEmpty()) return QString::fromLocal8Bit(outputData); else return QString::fromLocal8Bit(errorData); } void KrLinecountingProcess::receivedError() { QByteArray newData(this->readAllStandardError()); emit newErrorLines(newData.count('\n')); errorData += newData; if (errorData.length() > 500) errorData = errorData.right(500); if (mergedOutput) receivedOutput(newData); } void KrLinecountingProcess::receivedOutput(QByteArray newData) { if (newData.isEmpty()) newData = this->readAllStandardOutput(); emit newOutputLines(newData.count('\n')); emit newOutputData(this, newData); outputData += newData; if (outputData.length() > 500) outputData = outputData.right(500); } diff --git a/krArc/krlinecountingprocess.h b/krArc/krlinecountingprocess.h index 43c9a3cc..b7dd4004 100644 --- a/krArc/krlinecountingprocess.h +++ b/krArc/krlinecountingprocess.h @@ -1,54 +1,54 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRLINECOUNTINGPROCESS_H #define KRLINECOUNTINGPROCESS_H #include /** * A KProcess which emits how many lines it is writing to stdout or stderr. */ class KrLinecountingProcess : public KProcess { Q_OBJECT public: KrLinecountingProcess(); void setMerge(bool); QString getErrorMsg(); public slots: void receivedError(); void receivedOutput(QByteArray = QByteArray()); signals: void newOutputLines(int); void newErrorLines(int); void newOutputData(KProcess *, QByteArray &); private: QByteArray errorData; QByteArray outputData; bool mergedOutput; }; #endif // KRLINECOUNTINGPROCESS_H diff --git a/krusader/ActionMan/actionman.cpp b/krusader/ActionMan/actionman.cpp index 5e97c9f4..1473cfa3 100644 --- a/krusader/ActionMan/actionman.cpp +++ b/krusader/ActionMan/actionman.cpp @@ -1,86 +1,86 @@ /***************************************************************************** * Copyright (C) 2006 Jonas Bähr * - * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2006-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "actionman.h" // QtWidgets #include #include #include #include #include #include "useractionpage.h" #include "../krusader.h" #include "../UserAction/useraction.h" ActionMan::ActionMan(QWidget * parent) : QDialog(parent) { setWindowModality(Qt::WindowModal); setWindowTitle(i18n("ActionMan - Manage Your Useractions")); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); userActionPage = new UserActionPage(this); mainLayout->addWidget(userActionPage); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close|QDialogButtonBox::Apply); mainLayout->addWidget(buttonBox); applyButton = buttonBox->button(QDialogButtonBox::Apply); applyButton->setEnabled(false); connect(buttonBox, &QDialogButtonBox::rejected, this, &ActionMan::slotClose); connect(applyButton, &QPushButton::clicked, this, &ActionMan::slotApply); connect(userActionPage, &UserActionPage::changed, this, &ActionMan::slotEnableApplyButton); connect(userActionPage, &UserActionPage::applied, this, &ActionMan::slotDisableApplyButton); exec(); krApp->updateUserActions(); } ActionMan::~ActionMan() = default; void ActionMan::slotClose() { if (userActionPage->readyToQuit()) reject(); } void ActionMan::slotApply() { userActionPage->applyChanges(); } void ActionMan::slotEnableApplyButton() { applyButton->setEnabled(true); } void ActionMan::slotDisableApplyButton() { applyButton->setEnabled(false); } diff --git a/krusader/ActionMan/actionman.h b/krusader/ActionMan/actionman.h index 8aeaab0a..c2314d94 100644 --- a/krusader/ActionMan/actionman.h +++ b/krusader/ActionMan/actionman.h @@ -1,50 +1,50 @@ /***************************************************************************** * Copyright (C) 2006 Jonas Bähr * - * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2006-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef ACTIONMAN_H #define ACTIONMAN_H // QtWidgets #include class UserActionPage; /** * This manages all useractions */ class ActionMan : public QDialog { Q_OBJECT public: explicit ActionMan(QWidget* parent = nullptr); ~ActionMan() override; protected slots: void slotClose(); void slotApply(); void slotEnableApplyButton(); void slotDisableApplyButton(); private: UserActionPage* userActionPage; QPushButton *applyButton; }; #endif // ifndef ACTIONMAN_H diff --git a/krusader/ActionMan/actionproperty.cpp b/krusader/ActionMan/actionproperty.cpp index a38469fb..08336443 100644 --- a/krusader/ActionMan/actionproperty.cpp +++ b/krusader/ActionMan/actionproperty.cpp @@ -1,517 +1,517 @@ /***************************************************************************** * Copyright (C) 2004-2007 Jonas Bähr * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "actionproperty.h" #include "addplaceholderpopup.h" #include "../UserAction/useraction.h" #include "../UserAction/kraction.h" #include "../krusader.h" #include "../krglobal.h" #include "../icon.h" // QtWidgets #include #include #include #include #include ActionProperty::ActionProperty(QWidget *parent, KrAction *action) : QWidget(parent), _modified(false) { setupUi(this); if (action) { _action = action; updateGUI(_action); } ButtonAddPlaceholder->setIcon(Icon("list-add")); ButtonAddStartpath->setIcon(Icon("document-open")); // fill with all existing categories cbCategory->addItems(krUserAction->allCategories()); connect(ButtonAddPlaceholder, &QToolButton::clicked, this, &ActionProperty::addPlaceholder); connect(ButtonAddStartpath, &QToolButton::clicked, this, &ActionProperty::addStartpath); connect(ButtonNewProtocol, &QToolButton::clicked, this, &ActionProperty::newProtocol); connect(ButtonEditProtocol, &QToolButton::clicked, this, &ActionProperty::editProtocol); connect(ButtonRemoveProtocol, &QToolButton::clicked, this, &ActionProperty::removeProtocol); connect(ButtonAddPath, &QToolButton::clicked, this, &ActionProperty::addPath); connect(ButtonEditPath, &QToolButton::clicked, this, &ActionProperty::editPath); connect(ButtonRemovePath, &QToolButton::clicked, this, &ActionProperty::removePath); connect(ButtonAddMime, &QToolButton::clicked, this, &ActionProperty::addMime); connect(ButtonEditMime, &QToolButton::clicked, this, &ActionProperty::editMime); connect(ButtonRemoveMime, &QToolButton::clicked, this, &ActionProperty::removeMime); connect(ButtonNewFile, &QToolButton::clicked, this, &ActionProperty::newFile); connect(ButtonEditFile, &QToolButton::clicked, this, &ActionProperty::editFile); connect(ButtonRemoveFile, &QToolButton::clicked, this, &ActionProperty::removeFile); connect(KeyButtonShortcut, &KKeySequenceWidget::keySequenceChanged, this, &ActionProperty::changedShortcut); // track modifications: connect(leDistinctName, &KLineEdit::textChanged, this, [=]() { setModified(true); }); connect(leTitle, &KLineEdit::textChanged, this, [=]() {setModified(true); }); connect(ButtonIcon, &KIconButton::iconChanged, this, [=]() {setModified(true); }); connect(cbCategory, &KComboBox::currentTextChanged, this, [=]() {setModified(true); }); connect(leTooltip, &KLineEdit::textChanged, this, [=]() {setModified(true); }); connect(textDescription, &KTextEdit::textChanged, this, [=]() {setModified(true); }); connect(leCommandline, &KLineEdit::textChanged, this, [=]() {setModified(true); }); connect(leStartpath, &KLineEdit::textChanged, this, [=]() {setModified(true); }); connect(chkSeparateStdError, &QCheckBox::clicked, this, &ActionProperty::setModified); connect(radioCollectOutput, &QRadioButton::clicked, this, &ActionProperty::setModified); connect(radioNormal, &QRadioButton::clicked, this, &ActionProperty::setModified); connect(radioTE, &QRadioButton::clicked, this, &ActionProperty::setModified); connect(radioTerminal, &QRadioButton::clicked, this, &ActionProperty::setModified); connect(radioLocal, &QRadioButton::clicked, this, &ActionProperty::setModified); connect(radioUrl, &QRadioButton::clicked, this, &ActionProperty::setModified); connect(KeyButtonShortcut, &KKeySequenceWidget::keySequenceChanged, this, [=]() {setModified(true); }); connect(chkEnabled, &QCheckBox::clicked, this, &ActionProperty::setModified); connect(leDifferentUser, &KLineEdit::textChanged, this, [=]() {setModified(true); }); connect(chkDifferentUser, &QCheckBox::clicked, this, &ActionProperty::setModified); connect(chkConfirmExecution, &QCheckBox::clicked, this, &ActionProperty::setModified); connect(chkSeparateStdError, &QCheckBox::clicked, this, &ActionProperty::setModified); // The modified-state of the ShowOnly-lists is tracked in the access-functions below } ActionProperty::~ActionProperty() = default; void ActionProperty::changedShortcut(const QKeySequence& shortcut) { KeyButtonShortcut->setKeySequence(shortcut); } void ActionProperty::clear() { _action = nullptr; // This prevents the changed-signal from being emitted during the GUI-update _modified = true; // The real state is set at the end of this function. leDistinctName->clear(); cbCategory->clearEditText(); leTitle->clear(); leTooltip->clear(); textDescription->clear(); leCommandline->clear(); leStartpath->clear(); KeyButtonShortcut->clearKeySequence(); lbShowonlyProtocol->clear(); lbShowonlyPath->clear(); lbShowonlyMime->clear(); lbShowonlyFile->clear(); chkSeparateStdError->setChecked(false); radioNormal->setChecked(true); radioLocal->setChecked(true); chkEnabled->setChecked(true); chkConfirmExecution->setChecked(false); ButtonIcon->resetIcon(); leDifferentUser->clear(); chkDifferentUser->setChecked(false); setModified(false); } void ActionProperty::updateGUI(KrAction *action) { if (action) _action = action; if (! _action) return; // This prevents the changed-signal from being emitted during the GUI-update. _modified = true; // The real state is set at the end of this function. leDistinctName->setText(_action->objectName()); cbCategory->lineEdit()->setText(_action->category()); leTitle->setText(_action->text()); leTooltip->setText(_action->toolTip()); textDescription->setText(_action->whatsThis()); leCommandline->setText(_action->command()); leCommandline->home(false); leStartpath->setText(_action->startpath()); KeyButtonShortcut->setKeySequence(_action->shortcut()); lbShowonlyProtocol->clear(); lbShowonlyProtocol->addItems(_action->showonlyProtocol()); lbShowonlyPath->clear(); lbShowonlyPath->addItems(_action->showonlyPath()); lbShowonlyMime->clear(); lbShowonlyMime->addItems(_action->showonlyMime()); lbShowonlyFile->clear(); lbShowonlyFile->addItems(_action->showonlyFile()); chkSeparateStdError->setChecked(false); switch (_action->execType()) { case KrAction::CollectOutputSeparateStderr: chkSeparateStdError->setChecked(true); radioCollectOutput->setChecked(true); break; case KrAction::CollectOutput: radioCollectOutput->setChecked(true); break; case KrAction::Terminal: radioTerminal->setChecked(true); break; case KrAction::RunInTE: radioTE->setChecked(true); break; default: // case KrAction::Normal: radioNormal->setChecked(true); break; } if (_action->acceptURLs()) radioUrl->setChecked(true); else radioLocal->setChecked(true); chkEnabled->setChecked(_action->isVisible()); chkConfirmExecution->setChecked(_action->confirmExecution()); if (! _action->icon().isNull()) ButtonIcon->setIcon(_action->icon()); else ButtonIcon->resetIcon(); leDifferentUser->setText(_action->user()); if (_action->user().isEmpty()) chkDifferentUser->setChecked(false); else chkDifferentUser->setChecked(true); setModified(false); } void ActionProperty::updateAction(KrAction *action) { if (action) _action = action; if (! _action) return; if (_action->category() != cbCategory->currentText()) { _action->setCategory(cbCategory->currentText()); // Update the category-list cbCategory->clear(); cbCategory->addItems(krUserAction->allCategories()); cbCategory->lineEdit()->setText(_action->category()); } _action->setObjectName(leDistinctName->text()); _action->setText(leTitle->text()); _action->setToolTip(leTooltip->text()); _action->setWhatsThis(textDescription->toPlainText()); _action->setCommand(leCommandline->text()); _action->setStartpath(leStartpath->text()); _action->setShortcut(KeyButtonShortcut->keySequence()); QStringList list; for (int i1 = 0; i1 != lbShowonlyProtocol->count(); i1++) { QListWidgetItem* lbi = lbShowonlyProtocol->item(i1); list << lbi->text(); } _action->setShowonlyProtocol(list); list = QStringList(); for (int i1 = 0; i1 != lbShowonlyPath->count(); i1++) { QListWidgetItem* lbi = lbShowonlyPath->item(i1); list << lbi->text(); } _action->setShowonlyPath(list); list = QStringList(); for (int i1 = 0; i1 != lbShowonlyMime->count(); i1++) { QListWidgetItem* lbi = lbShowonlyMime->item(i1); list << lbi->text(); } _action->setShowonlyMime(list); list = QStringList(); for (int i1 = 0; i1 != lbShowonlyFile->count(); i1++) { QListWidgetItem* lbi = lbShowonlyFile->item(i1); list << lbi->text(); } _action->setShowonlyFile(list); if (radioCollectOutput->isChecked() && chkSeparateStdError->isChecked()) _action->setExecType(KrAction::CollectOutputSeparateStderr); else if (radioCollectOutput->isChecked() && ! chkSeparateStdError->isChecked()) _action->setExecType(KrAction::CollectOutput); else if (radioTerminal->isChecked()) _action->setExecType(KrAction::Terminal); else if (radioTE->isChecked()) _action->setExecType(KrAction::RunInTE); else _action->setExecType(KrAction::Normal); if (radioUrl->isChecked()) _action->setAcceptURLs(true); else _action->setAcceptURLs(false); _action->setEnabled(chkEnabled->isChecked()); _action->setVisible(chkEnabled->isChecked()); _action->setConfirmExecution(chkConfirmExecution->isChecked()); _action->setIcon(Icon(ButtonIcon->icon())); _action->setIconName(ButtonIcon->icon()); _action->setUser(leDifferentUser->text()); setModified(false); } void ActionProperty::addPlaceholder() { AddPlaceholderPopup popup(this); QString exp = popup.getPlaceholder(mapToGlobal( QPoint( ButtonAddPlaceholder->pos().x() + ButtonAddPlaceholder->width() + 6, // 6 is the default margin ButtonAddPlaceholder->pos().y() ) )); leCommandline->insert(exp); } void ActionProperty::addStartpath() { QString folder = QFileDialog::getExistingDirectory(this); if (!folder.isEmpty()) { leStartpath->setText(folder); } } void ActionProperty::newProtocol() { bool ok; QString currentText; if (lbShowonlyProtocol->currentItem()) currentText = lbShowonlyProtocol->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("New protocol"), i18n("Set a protocol:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyProtocol->addItems(text.split(';')); setModified(); } } void ActionProperty::editProtocol() { if (lbShowonlyProtocol->currentItem() == nullptr) return; bool ok; QString currentText = lbShowonlyProtocol->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("Edit Protocol"), i18n("Set another protocol:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyProtocol->currentItem()->setText(text); setModified(); } } void ActionProperty::removeProtocol() { if (lbShowonlyProtocol->currentItem() != nullptr) { delete lbShowonlyProtocol->currentItem(); setModified(); } } void ActionProperty::addPath() { QString folder = QFileDialog::getExistingDirectory(this); if (!folder.isEmpty()) { lbShowonlyPath->addItem(folder); setModified(); } } void ActionProperty::editPath() { if (lbShowonlyPath->currentItem() == nullptr) return; bool ok; QString currentText = lbShowonlyPath->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("Edit Path"), i18n("Set another path:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyPath->currentItem()->setText(text); setModified(); } } void ActionProperty::removePath() { if (lbShowonlyPath->currentItem() != nullptr) { delete lbShowonlyPath->currentItem(); setModified(); } } void ActionProperty::addMime() { bool ok; QString currentText; if (lbShowonlyMime->currentItem()) currentText = lbShowonlyMime->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("New MIME Type"), i18n("Set a MIME type:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyMime->addItems(text.split(';')); setModified(); } } void ActionProperty::editMime() { if (lbShowonlyMime->currentItem() == nullptr) return; bool ok; QString currentText = lbShowonlyMime->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("Edit MIME Type"), i18n("Set another MIME type:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyMime->currentItem()->setText(text); setModified(); } } void ActionProperty::removeMime() { if (lbShowonlyMime->currentItem() != nullptr) { delete lbShowonlyMime->currentItem(); setModified(); } } void ActionProperty::newFile() { bool ok; QString currentText; if (lbShowonlyFile->currentItem()) currentText = lbShowonlyFile->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("New File Name"), i18n("Set a file name:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyFile->addItems(text.split(';')); setModified(); } } void ActionProperty::editFile() { if (lbShowonlyFile->currentItem() == nullptr) return; bool ok; QString currentText = lbShowonlyFile->currentItem()->text(); QString text = QInputDialog::getText(this, i18n("Edit File Name"), i18n("Set another file name:"), QLineEdit::Normal, currentText, &ok); if (ok && !text.isEmpty()) { lbShowonlyFile->currentItem()->setText(text); setModified(); } } void ActionProperty::removeFile() { if (lbShowonlyFile->currentItem() != nullptr) { delete lbShowonlyFile->currentItem(); setModified(); } } bool ActionProperty::validProperties() { if (leDistinctName->text().simplified().isEmpty()) { KMessageBox::error(this, i18n("Please set a unique name for the useraction")); leDistinctName->setFocus(); return false; } if (leTitle->text().simplified().isEmpty()) { KMessageBox::error(this, i18n("Please set a title for the menu entry")); leTitle->setFocus(); return false; } if (leCommandline->text().simplified().isEmpty()) { KMessageBox::error(this, i18n("Command line is empty")); leCommandline->setFocus(); return false; } if (leDistinctName->isEnabled()) if (krApp->actionCollection()->action(leDistinctName->text())) { KMessageBox::error(this, i18n("There already is an action with this name.\n" "If you do not have such a useraction the name is used by Krusader for an internal action.") ); leDistinctName->setFocus(); return false; } return true; } void ActionProperty::setModified(bool m) { if (m && !_modified) { // emit only when the state _changes_to_true_, emit changed(); } _modified = m; } diff --git a/krusader/ActionMan/actionproperty.h b/krusader/ActionMan/actionproperty.h index 612ead87..0abeac33 100644 --- a/krusader/ActionMan/actionproperty.h +++ b/krusader/ActionMan/actionproperty.h @@ -1,157 +1,157 @@ /***************************************************************************** * Copyright (C) 2004-2007 Jonas Bähr * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef ACTIONPROPERTY_H #define ACTIONPROPERTY_H #include "ui_actionproperty.h" class KrAction; /** * Use this widget where ever you need to manipulate a UserAction */ class ActionProperty : public QWidget, public Ui::ActionProperty { Q_OBJECT public: explicit ActionProperty(QWidget *parent = nullptr, KrAction *action = nullptr); ~ActionProperty() override; /** * @return the currently displayed action */ KrAction* action() { return _action; }; /** * This inits the widget with the actions properties. * If no action is provided, the last used will be taken! * It also resets the changed() state. * @param action the action which should be displayd */ void updateGUI(KrAction *action = nullptr); /** * This writes the displayed properties back into the action. * If no action is provided, the last used will be taken! * It also resets the changed() state. * @param action the action which should be manipulated */ void updateAction(KrAction *action = nullptr); /** * clears everything */ void clear(); /** * @return true if all properties got valid values */ bool validProperties(); /** * @return true if any property got changed */ bool isModified() { return _modified; }; signals: /** * emitted when any actionproperty changed. This signal is only emitted when * the _modified attribute changes to true. If there are changes made and * _modified is already true, no signal is emitted! */ void changed(); protected slots: void setModified(bool m = true); /** * executes the AddPlaceholderPopup */ void addPlaceholder(); /** * asks for an existing path */ void addStartpath(); /** * (availability) asks for a new protocol */ void newProtocol(); /** * (availability) changes a protocol of the list */ void editProtocol(); /** * (availability) removes a protocol from the list */ void removeProtocol(); /** * (availability) asks for a new path */ void addPath(); /** * (availability) edits a path of the list */ void editPath(); /** * (availability) removes a path from the list */ void removePath(); /** * (availability) asks for a new mime-type */ void addMime(); /** * (availability) changes a mime-type of the list */ void editMime(); /** * (availability) removes a mime-type from the list */ void removeMime(); /** * (availability) asks for a new file-filter */ void newFile(); /** * (availability) edits a file-filter of the list */ void editFile(); /** * (availability) removes a file-filter from the lsit */ void removeFile(); private: KrAction *_action; bool _modified; private slots: /** * This updates the ShortcutButton * @internal */ void changedShortcut(const QKeySequence& shortcut); }; #endif diff --git a/krusader/ActionMan/addplaceholderpopup.cpp b/krusader/ActionMan/addplaceholderpopup.cpp index 406b5c51..e8b6d7bc 100644 --- a/krusader/ActionMan/addplaceholderpopup.cpp +++ b/krusader/ActionMan/addplaceholderpopup.cpp @@ -1,703 +1,703 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * * Copyright (C) 2004 Rafi Yanai * * Copyright (C) 2004 Jonas Bähr * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "addplaceholderpopup.h" // for ParameterDialog #include "../krglobal.h" // for konfig-access #include "../icon.h" #include "../BookMan/krbookmarkbutton.h" #include "../GUI/profilemanager.h" // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ACTIVE_MASK 0x0100 #define OTHER_MASK 0x0200 #define LEFT_MASK 0x0400 #define RIGHT_MASK 0x0800 #define INDEPENDENT_MASK 0x1000 #define EXECUTABLE_ID 0xFFFF AddPlaceholderPopup::AddPlaceholderPopup(QWidget *parent) : QMenu(parent) { _activeSub = new QMenu(i18n("Active panel"), this); _otherSub = new QMenu(i18n("Other panel"), this); _leftSub = new QMenu(i18n("Left panel"), this); _rightSub = new QMenu(i18n("Right panel"), this); _independentSub = new QMenu(i18n("Panel independent"), this); addMenu(_activeSub); addMenu(_otherSub); addMenu(_leftSub); addMenu(_rightSub); addMenu(_independentSub); QAction *chooseExecAct = _independentSub->addAction(i18n("Choose executable...")); chooseExecAct->setData(QVariant(EXECUTABLE_ID)); _independentSub->addSeparator(); // read the expressions array from the user menu and populate menus Expander expander; for (int i = 0; i < expander.placeholderCount(); ++i) { if (expander.placeholder(i)->expression().isEmpty()) { if (expander.placeholder(i)->needPanel()) { _activeSub->addSeparator(); _otherSub->addSeparator(); _leftSub->addSeparator(); _rightSub->addSeparator(); } else _independentSub->addSeparator(); } else { QAction * action; if (expander.placeholder(i)->needPanel()) { action = _activeSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | ACTIVE_MASK)); action = _otherSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | OTHER_MASK)); action = _leftSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | LEFT_MASK)); action = _rightSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | RIGHT_MASK)); } else { action = _independentSub->addAction(i18n(expander.placeholder(i)->description().toUtf8())); action->setData(QVariant(i | INDEPENDENT_MASK)); } } } } QString AddPlaceholderPopup::getPlaceholder(const QPoint& pos) { QAction *res = exec(pos); if (res == nullptr) return QString(); // add the selected flag to the command line if (res->data().toInt() == EXECUTABLE_ID) { // did the user need an executable ? // select an executable QString filename = QFileDialog::getOpenFileName(this); if (!filename.isEmpty()) { return filename + ' '; // with extra space // return filename; // without extra space } } else { // user selected something from the menus Expander expander; const exp_placeholder* currentPlaceholder = expander.placeholder(res->data().toInt() & ~(ACTIVE_MASK | OTHER_MASK | LEFT_MASK | RIGHT_MASK | INDEPENDENT_MASK)); // if ( ¤tPlaceholder->expFunc == 0 ) { // KMessageBox::sorry( this, "BOFH Excuse #93:\nFeature not yet implemented" ); // return QString(); // } auto* parameterDialog = new ParameterDialog(currentPlaceholder, this); QString panel, parameter = parameterDialog->getParameter(); delete parameterDialog; // indicate the panel with 'a' 'o', 'l', 'r' or '_'. if (res->data().toInt() & ACTIVE_MASK) panel = 'a'; else if (res->data().toInt() & OTHER_MASK) panel = 'o'; else if (res->data().toInt() & LEFT_MASK) panel = 'l'; else if (res->data().toInt() & RIGHT_MASK) panel = 'r'; else if (res->data().toInt() & INDEPENDENT_MASK) panel = '_'; //return '%' + panel + currentPlaceholder->expression() + parameter + "% "; // with extra space return '%' + panel + currentPlaceholder->expression() + parameter + '%'; // without extra space } return QString(); } //////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////// ParameterDialog //////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// ParameterDialog::ParameterDialog(const exp_placeholder* currentPlaceholder, QWidget *parent) : QDialog(parent) { setWindowTitle(i18n("User Action Parameter Dialog")); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); _parameter.clear(); _parameterCount = currentPlaceholder->parameterCount(); QWidget *page = new QWidget(this); mainLayout->addWidget(page); auto* layout = new QVBoxLayout(page); layout->setSpacing(11); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n("This placeholder allows some parameter:"), page)); for (int i = 0; i < _parameterCount; ++i) { if (currentPlaceholder->parameter(i).preset() == "__placeholder") _parameter.append(new ParameterPlaceholder(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__yes") _parameter.append(new ParameterYes(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__no") _parameter.append(new ParameterNo(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__file") _parameter.append(new ParameterFile(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset().indexOf("__choose") != -1) _parameter.append(new ParameterChoose(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__select") _parameter.append(new ParameterSelect(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__goto") _parameter.append(new ParameterGoto(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__syncprofile") _parameter.append(new ParameterSyncprofile(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__searchprofile") _parameter.append(new ParameterSearch(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset() == "__panelprofile") _parameter.append(new ParameterPanelprofile(currentPlaceholder->parameter(i), page)); else if (currentPlaceholder->parameter(i).preset().indexOf("__int") != -1) _parameter.append(new ParameterInt(currentPlaceholder->parameter(i), page)); else _parameter.append(new ParameterText(currentPlaceholder->parameter(i), page)); layout->addWidget(_parameter.last()); } QFrame * line = new QFrame(page); line->setFrameShape(QFrame::HLine); line->setFrameShadow(QFrame::Sunken); layout->addWidget(line); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::RestoreDefaults); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(okButton, &QPushButton:: clicked, this, &ParameterDialog::slotOk); connect(buttonBox, &QDialogButtonBox::accepted, this, &ParameterDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &ParameterDialog::reject); connect(buttonBox->button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &ParameterDialog::reset); } QString ParameterDialog::getParameter() { if (_parameterCount == 0) // meaning no parameters return QString(); if (exec() == -1) return QString(); int lastParameter = _parameterCount; while (--lastParameter > -1) { if (_parameter[ lastParameter ]->text() != _parameter[ lastParameter ]->preset() || _parameter[ lastParameter ]->necessary()) break; } if (lastParameter < 0) // all parameters have default-values return QString(); QString parameter; for (int i = 0; i <= lastParameter; ++i) { if (i > 0) parameter += ", "; parameter += '\"' + _parameter[ i ]->text().replace('\"', "\\\"") + '\"'; } return '(' + parameter + ')'; } void ParameterDialog::reset() { for (int i = 0; i < _parameterCount; ++i) _parameter[ i ]->reset(); } void ParameterDialog::slotOk() { bool valid = true; for (int i = 0; i < _parameterCount; ++i) { if (_parameter[ i ]->necessary() && ! _parameter[ i ]->valid()) valid = false; } if (valid) accept(); } ///////////// ParameterText ParameterText::ParameterText(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_lineEdit = new KLineEdit(parameter.preset(), this)); _preset = parameter.preset(); } QString ParameterText::text() { return _lineEdit->text(); } QString ParameterText::preset() { return _preset; } void ParameterText::reset() { _lineEdit->setText(_preset); } bool ParameterText::valid() { if (_lineEdit->text().isEmpty()) return false; else return true; } ///////////// ParameterPlaceholder ParameterPlaceholder::ParameterPlaceholder(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); QWidget * hboxWidget = new QWidget(this); layout->addWidget(hboxWidget); auto * hbox = new QHBoxLayout(hboxWidget); hbox->setContentsMargins(0, 0, 0, 0); hbox->setSpacing(6); _lineEdit = new KLineEdit(hboxWidget); hbox->addWidget(_lineEdit); _button = new QToolButton(hboxWidget); _button->setIcon(Icon("list-add")); hbox->addWidget(_button); connect(_button, &QToolButton::clicked, this, &ParameterPlaceholder::addPlaceholder); } QString ParameterPlaceholder::text() { return _lineEdit->text(); } QString ParameterPlaceholder::preset() { return QString(); } void ParameterPlaceholder::reset() { _lineEdit->setText(QString()); } bool ParameterPlaceholder::valid() { if (_lineEdit->text().isEmpty()) return false; else return true; } void ParameterPlaceholder::addPlaceholder() { auto* popup = new AddPlaceholderPopup(this); QString exp = popup->getPlaceholder(mapToGlobal(QPoint(_button->pos().x() + _button->width() + 6, _button->pos().y() + _button->height() / 2))); _lineEdit->insert(exp); delete popup; } ///////////// ParameterYes ParameterYes::ParameterYes(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(_checkBox = new QCheckBox(i18n(parameter.description().toUtf8()), this)); _checkBox->setChecked(true); } QString ParameterYes::text() { if (_checkBox->isChecked()) return QString(); else return "No"; } QString ParameterYes::preset() { return QString(); } void ParameterYes::reset() { _checkBox->setChecked(true); } bool ParameterYes::valid() { return true; } ///////////// ParameterNo ParameterNo::ParameterNo(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(_checkBox = new QCheckBox(i18n(parameter.description().toUtf8()), this)); _checkBox->setChecked(false); } QString ParameterNo::text() { if (_checkBox->isChecked()) return "Yes"; else return QString(); } QString ParameterNo::preset() { return QString(); } void ParameterNo::reset() { _checkBox->setChecked(false); } bool ParameterNo::valid() { return true; } ///////////// ParameterFile ParameterFile::ParameterFile(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); QWidget * hboxWidget = new QWidget(this); layout->addWidget(hboxWidget); auto * hbox = new QHBoxLayout(hboxWidget); hbox->setContentsMargins(0, 0, 0, 0); hbox->setSpacing(6); _lineEdit = new KLineEdit(hboxWidget); hbox->addWidget(_lineEdit); _button = new QToolButton(hboxWidget); hbox->addWidget(_button); _button->setIcon(Icon("document-open")); connect(_button, &QToolButton::clicked, this, &ParameterFile::addFile); } QString ParameterFile::text() { return _lineEdit->text(); } QString ParameterFile::preset() { return QString(); } void ParameterFile::reset() { _lineEdit->setText(QString()); } bool ParameterFile::valid() { if (_lineEdit->text().isEmpty()) return false; else return true; } void ParameterFile::addFile() { QString filename = QFileDialog::getOpenFileName(this); _lineEdit->insert(filename); } ///////////// ParameterChoose ParameterChoose::ParameterChoose(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->addItems(parameter.preset().section(':', 1).split(';')); } QString ParameterChoose::text() { return _combobox->currentText(); } QString ParameterChoose::preset() { return _combobox->itemText(0); } void ParameterChoose::reset() { _combobox->setCurrentIndex(0); } bool ParameterChoose::valid() { return true; } ///////////// ParameterSelect ParameterSelect::ParameterSelect(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->setEditable(true); KConfigGroup group(krConfig, "Private"); QStringList lst = group.readEntry("Predefined Selections", QStringList()); if (lst.size() > 0) _combobox->addItems(lst); _combobox->lineEdit()->setText("*"); } QString ParameterSelect::text() { return _combobox->currentText(); } QString ParameterSelect::preset() { return "*"; } void ParameterSelect::reset() { _combobox->lineEdit()->setText("*"); } bool ParameterSelect::valid() { return true; } ///////////// ParameterGoto ParameterGoto::ParameterGoto(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); QWidget * hboxWidget = new QWidget(this); auto * hbox = new QHBoxLayout(hboxWidget); hbox->setContentsMargins(0, 0, 0, 0); hbox->setSpacing(6); _lineEdit = new KLineEdit(hboxWidget); _lineEdit->setCompletionObject(new KUrlCompletion(KUrlCompletion::DirCompletion)); hbox->addWidget(_lineEdit); _dirButton = new QToolButton(hboxWidget); hbox->addWidget(_dirButton); _dirButton->setIcon(Icon("document-open")); connect(_dirButton, &QToolButton::clicked, this, &ParameterGoto::setDir); _placeholderButton = new QToolButton(hboxWidget); _placeholderButton->setIcon(Icon("list-add")); hbox->addWidget(_placeholderButton); connect(_placeholderButton, &QToolButton::clicked, this, &ParameterGoto::addPlaceholder); layout->addWidget(hboxWidget); } QString ParameterGoto::text() { return _lineEdit->text(); } QString ParameterGoto::preset() { return QString(); } void ParameterGoto::reset() { _lineEdit->setText(QString()); } bool ParameterGoto::valid() { if (_lineEdit->text().isEmpty()) return false; else return true; } void ParameterGoto::setDir() { QString folder = QFileDialog::getExistingDirectory(this); _lineEdit->setText(folder); } void ParameterGoto::addPlaceholder() { auto* popup = new AddPlaceholderPopup(this); QString exp = popup->getPlaceholder(mapToGlobal(QPoint(_placeholderButton->pos().x() + _placeholderButton->width() + 6, _placeholderButton->pos().y() + _placeholderButton->height() / 2))); _lineEdit->insert(exp); delete popup; } ///////////// ParameterSyncprofile ParameterSyncprofile::ParameterSyncprofile(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->addItems(ProfileManager::availableProfiles("SynchronizerProfile")); } QString ParameterSyncprofile::text() { return _combobox->currentText(); } QString ParameterSyncprofile::preset() { return _combobox->itemText(0); } void ParameterSyncprofile::reset() { _combobox->setCurrentIndex(0); } bool ParameterSyncprofile::valid() { return true; } ///////////// ParameterSearch ParameterSearch::ParameterSearch(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->addItems(ProfileManager::availableProfiles("SearcherProfile")); } QString ParameterSearch::text() { return _combobox->currentText(); } QString ParameterSearch::preset() { return _combobox->itemText(0); } void ParameterSearch::reset() { _combobox->setCurrentIndex(0); } bool ParameterSearch::valid() { return true; } ///////////// ParameterPanelprofile ParameterPanelprofile::ParameterPanelprofile(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QVBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_combobox = new KComboBox(this)); _combobox->addItems(ProfileManager::availableProfiles("Panel")); } QString ParameterPanelprofile::text() { return _combobox->currentText(); } QString ParameterPanelprofile::preset() { return _combobox->itemText(0); } void ParameterPanelprofile::reset() { _combobox->setCurrentIndex(0); } bool ParameterPanelprofile::valid() { return true; } ///////////// ParameterInt ParameterInt::ParameterInt(const exp_parameter& parameter, QWidget* parent) : ParameterBase(parameter, parent) { auto* layout = new QHBoxLayout(this); layout->setSpacing(6); layout->setContentsMargins(0, 0, 0, 0); layout->addWidget(new QLabel(i18n(parameter.description().toUtf8()), this)); layout->addWidget(_spinbox = new QSpinBox(this)); QStringList para = parameter.preset().section(':', 1).split(';'); _spinbox->setMinimum(para[0].toInt()); _spinbox->setMaximum(para[1].toInt()); _spinbox->setSingleStep(para[2].toInt()); _spinbox->setValue(para[3].toInt()); _default = _spinbox->value(); } QString ParameterInt::text() { return _spinbox->text(); } QString ParameterInt::preset() { return QString("%1").arg(_default); } void ParameterInt::reset() { return _spinbox->setValue(_default); } bool ParameterInt::valid() { return true; } diff --git a/krusader/ActionMan/addplaceholderpopup.h b/krusader/ActionMan/addplaceholderpopup.h index c051fd85..bf175a18 100644 --- a/krusader/ActionMan/addplaceholderpopup.h +++ b/krusader/ActionMan/addplaceholderpopup.h @@ -1,348 +1,348 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * * Copyright (C) 2004 Rafi Yanai * * Copyright (C) 2004 Jonas Bähr * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef ADDPLACEHOLDERPOPUP_H #define ADDPLACEHOLDERPOPUP_H // QtCore #include #include // QtWidgets #include #include #include "../UserAction/expander.h" class KLineEdit; class QToolButton; class QCheckBox; class KComboBox; class QSpinBox; /** * This reads Expander::placeholder[] and * fills a popup for easy access to the UserAction Placeholder */ class AddPlaceholderPopup : public QMenu { public: explicit AddPlaceholderPopup(QWidget *parent); /** * Use this to exec the popup. * @param pos Position where the popup should appear * @return the expression which can be placed in the UserAction commandline */ QString getPlaceholder(const QPoint& pos); protected: /** * This is called when a Placeholder got parameter. * @param currentPlaceholder A pointer to the Placeholder the user has chosen * @return a parameter-string */ QString getParameter(exp_placeholder* currentPlaceholder); private: QMenu *_activeSub, *_otherSub, *_leftSub, *_rightSub, *_independentSub; }; //////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////// Parameter Widgets /////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// /** * abstract baseclass for all Parameter widgets */ class ParameterBase : public QWidget { public: inline ParameterBase(const exp_parameter& parameter, QWidget* parent) : QWidget(parent) { _necessary = parameter.necessary(); } /** * @return the text for the parameter */ virtual QString text() = 0; /** * @return the default of the parameter */ virtual QString preset() = 0; /** * re-init the parameter with the default */ virtual void reset() = 0; /** * @return true if the Parameter as a valid value */ virtual bool valid() = 0; /** * @return true if the Placeholder really needs this parameter */ inline bool necessary() { return _necessary; } private: bool _necessary; }; /** * The simple Parameter widgets: a line-edit with the description above * used by default */ class ParameterText : public ParameterBase { public: ParameterText(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: KLineEdit * _lineEdit; QString _preset; }; /** * A line-edit with the "addPlaceholder"-button * used with default = "__placeholder" */ class ParameterPlaceholder : public ParameterBase { Q_OBJECT public: ParameterPlaceholder(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: KLineEdit * _lineEdit; QToolButton* _button; private slots: void addPlaceholder(); }; /** * A Checkbox, default: checked; retuns "No" if unchecked * used with default = "__yes" */ class ParameterYes : public ParameterBase { public: ParameterYes(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: QCheckBox* _checkBox; }; /** * A Checkbox, default: unchecked; retuns "Yes" if checked * used with default = "__no" */ class ParameterNo : public ParameterBase { public: ParameterNo(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: QCheckBox* _checkBox; }; /** * A line-edit with the "file open"-button * used with default = "__file" */ class ParameterFile : public ParameterBase { Q_OBJECT public: ParameterFile(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: KLineEdit * _lineEdit; QToolButton* _button; private slots: void addFile(); }; /** * A ComboBox with the description above * used with default = "__choose:item1;item2;..." */ class ParameterChoose : public ParameterBase { public: ParameterChoose(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: KComboBox * _combobox; }; /** * An editable ComboBox with the predefined selections * used with default = "__select" */ class ParameterSelect : public ParameterBase { public: ParameterSelect(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: KComboBox * _combobox; }; /** * A line-edit with a "choose dir"- and a bookmark-button * used with default = "__goto" */ class ParameterGoto : public ParameterBase { Q_OBJECT public: ParameterGoto(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: KLineEdit * _lineEdit; QToolButton* _dirButton, *_placeholderButton; private slots: void setDir(); void addPlaceholder(); }; /** * A ComboBox with all profiles available for the Synchronizer * used with default = "__syncprofile" */ class ParameterSyncprofile : public ParameterBase { public: ParameterSyncprofile(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: KComboBox * _combobox; }; /** * A ComboBox with all profiles available for the panels * used with default = "__panelprofile" */ class ParameterPanelprofile : public ParameterBase { public: ParameterPanelprofile(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: KComboBox * _combobox; }; /** * A ComboBox with all profiles available for the Searchmodule * used with default = "__searchprofile" */ class ParameterSearch : public ParameterBase { public: ParameterSearch(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: KComboBox * _combobox; }; /** * A SpinBox for integer * used with default = "__int:min;max;step;value" */ class ParameterInt : public ParameterBase { public: ParameterInt(const exp_parameter& parameter, QWidget* parent); QString text() Q_DECL_OVERRIDE; QString preset() Q_DECL_OVERRIDE; void reset() Q_DECL_OVERRIDE; bool valid() Q_DECL_OVERRIDE; private: QSpinBox * _spinbox; int _default; }; //////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////// ParameterDialog //////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////// /** * Opens a dialog for the parameter. Depending on the default (preset) a different widget is used. * See Parameter-Classes for details */ class ParameterDialog : public QDialog { Q_OBJECT public: ParameterDialog(const exp_placeholder* currentPlaceholder, QWidget *parent); /** * Use this to execute the dialog. * @return a QString with all parameters; omitting the optional ones if they have the default-value. */ QString getParameter(); private: typedef QList ParameterList; ParameterList _parameter; int _parameterCount; private slots: void reset(); void slotOk(); }; #endif // ADDPLACEHOLDERPOPUP_H diff --git a/krusader/ActionMan/useractionlistview.cpp b/krusader/ActionMan/useractionlistview.cpp index 3e3a3216..0406ee45 100644 --- a/krusader/ActionMan/useractionlistview.cpp +++ b/krusader/ActionMan/useractionlistview.cpp @@ -1,251 +1,251 @@ /***************************************************************************** * Copyright (C) 2006 Jonas Bähr * - * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2006-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "useractionlistview.h" // QtCore #include // QtXml #include #include #include "../krglobal.h" #include "../icon.h" #include "../UserAction/kraction.h" #include "../UserAction/useraction.h" #define COL_TITLE 0 // UserActionListView UserActionListView::UserActionListView(QWidget * parent) : KrTreeWidget(parent) { setHeaderLabel(i18n("Title")); setRootIsDecorated(true); setSelectionMode(QAbstractItemView::ExtendedSelection); // normally select single items but one may use Ctrl or Shift to select multiple setSortingEnabled(true); sortItems(COL_TITLE, Qt::AscendingOrder); connect(this, &UserActionListView::currentItemChanged, this, &UserActionListView::slotCurrentItemChanged); update(); } UserActionListView::~UserActionListView() = default; QSize UserActionListView::sizeHint() const { return QSize(200, 400); } void UserActionListView::update() { clear(); UserAction::KrActionList list = krUserAction->actionList(); QListIterator it(list); while (it.hasNext()) insertAction(it.next()); } void UserActionListView::update(KrAction* action) { UserActionListViewItem* item = findActionItem(action); if (item) { // deleting & re-inserting is _much_easier then tracking all possible cases of category changes! bool current = (item == currentItem()); bool selected = item->isSelected(); delete item; item = insertAction(action); if (current) setCurrentItem(item); if (selected) item->setSelected(true); } } UserActionListViewItem* UserActionListView::insertAction(KrAction* action) { if (! action) return nullptr; UserActionListViewItem* item; if (action->category().isEmpty()) item = new UserActionListViewItem(this, action); else { QTreeWidgetItem* categoryItem = findCategoryItem(action->category()); if (! categoryItem) { categoryItem = new QTreeWidgetItem(this); // create the new category item it not already present categoryItem->setText(0, action->category()); categoryItem->setFlags(Qt::ItemIsEnabled); } item = new UserActionListViewItem(categoryItem, action); } item->setAction(action); return item; } QTreeWidgetItem* UserActionListView::findCategoryItem(const QString& category) { QTreeWidgetItemIterator it(this); while (*it) { if ((*it)->text(COL_TITLE) == category && !((*it)->flags() & Qt::ItemIsSelectable)) return *it; it++; } return nullptr; } UserActionListViewItem* UserActionListView::findActionItem(const KrAction* action) { QTreeWidgetItemIterator it(this); while (*it) { if (auto* item = dynamic_cast(*it)) { if (item->action() == action) return item; } it++; } return nullptr; } KrAction * UserActionListView::currentAction() const { if (auto* item = dynamic_cast(currentItem())) return item->action(); else return nullptr; } void UserActionListView::setCurrentAction(const KrAction* action) { UserActionListViewItem* item = findActionItem(action); if (item) { setCurrentItem(item); } } void UserActionListView::setFirstActionCurrent() { QTreeWidgetItemIterator it(this); while (*it) { if (auto* item = dynamic_cast(*it)) { setCurrentItem(item); break; } it++; } } void UserActionListView::slotCurrentItemChanged(QTreeWidgetItem* item) { if (! item) return; scrollTo(indexOf(item)); } QDomDocument UserActionListView::dumpSelectedActions(QDomDocument* mergeDoc) const { QList list = selectedItems(); QDomDocument doc; if (mergeDoc) doc = *mergeDoc; else doc = UserAction::createEmptyDoc(); QDomElement root = doc.documentElement(); for (auto item : list) { if (auto* actionItem = dynamic_cast(item)) root.appendChild(actionItem->action()->xmlDump(doc)); } return doc; } void UserActionListView::removeSelectedActions() { QList list = selectedItems(); for (auto item : list) { if (auto* actionItem = dynamic_cast(item)) { delete actionItem->action(); // remove the action itself delete actionItem; // remove the action from the list } // if } } // UserActionListViewItem UserActionListViewItem::UserActionListViewItem(QTreeWidget* view, KrAction* action) : QTreeWidgetItem(view) { setAction(action); } UserActionListViewItem::UserActionListViewItem(QTreeWidgetItem* item, KrAction * action) : QTreeWidgetItem(item) { setAction(action); } UserActionListViewItem::~UserActionListViewItem() = default; void UserActionListViewItem::setAction(KrAction * action) { if (! action) return; _action = action; update(); } KrAction * UserActionListViewItem::action() const { return _action; } void UserActionListViewItem::update() { if (! _action) return; if (! _action->icon().isNull()) setIcon(COL_TITLE, Icon(_action->iconName())); setText(COL_TITLE, _action->text()); } bool UserActionListViewItem::operator<(const QTreeWidgetItem &other) const { // FIXME some how this only produces bullshit :-/ // if ( i->text( COL_NAME ).isEmpty() ) { // categories only have titles // //qDebug() << "this->title: " << text(COL_TITLE) << " |=| i->title: " << i->text(COL_TITLE); // return ( ascending ? -1 : 1 ); // <0 means this is smaller then i // } // else return QTreeWidgetItem::operator<(other); } diff --git a/krusader/ActionMan/useractionlistview.h b/krusader/ActionMan/useractionlistview.h index 1f79339f..c6eda59c 100644 --- a/krusader/ActionMan/useractionlistview.h +++ b/krusader/ActionMan/useractionlistview.h @@ -1,87 +1,87 @@ /***************************************************************************** * Copyright (C) 2006 Jonas Bähr * - * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2006-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef USERACTIONLISTVIEW_H #define USERACTIONLISTVIEW_H #include "../GUI/krtreewidget.h" class KrAction; class QString; class UserActionListViewItem; class QDomDocument; class UserActionListView : public KrTreeWidget { Q_OBJECT public: explicit UserActionListView(QWidget* parent = nullptr); ~UserActionListView() override; QSize sizeHint() const Q_DECL_OVERRIDE; void update(); void update(KrAction* action); UserActionListViewItem* insertAction(KrAction* action); KrAction* currentAction() const; void setCurrentAction(const KrAction*); QDomDocument dumpSelectedActions(QDomDocument* mergeDoc = nullptr) const; void removeSelectedActions(); /** * makes the first action in the list current */ void setFirstActionCurrent(); /** * makes @e item current and ensures its visibility */ protected slots: void slotCurrentItemChanged(QTreeWidgetItem*); protected: QTreeWidgetItem* findCategoryItem(const QString& category); UserActionListViewItem* findActionItem(const KrAction* action); }; class UserActionListViewItem : public QTreeWidgetItem { public: UserActionListViewItem(QTreeWidget* view, KrAction* action); UserActionListViewItem(QTreeWidgetItem* item, KrAction* action); ~UserActionListViewItem() override; void setAction(KrAction* action); KrAction* action() const; void update(); /** * This reimplements qt's compare-function in order to have categories on the top of the list */ bool operator<(const QTreeWidgetItem &other) const Q_DECL_OVERRIDE; private: KrAction* _action; }; #endif diff --git a/krusader/ActionMan/useractionpage.cpp b/krusader/ActionMan/useractionpage.cpp index e0c9000e..d3ab88e8 100644 --- a/krusader/ActionMan/useractionpage.cpp +++ b/krusader/ActionMan/useractionpage.cpp @@ -1,355 +1,355 @@ /***************************************************************************** * Copyright (C) 2006 Shie Erlich * * Copyright (C) 2006 Rafi Yanai * - * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2006-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "useractionpage.h" // QtWidgets #include #include #include #include #include #include // QtGui #include // QtXml #include #include #include #include #include #include "actionproperty.h" #include "useractionlistview.h" #include "../UserAction/useraction.h" #include "../UserAction/kraction.h" #include "../krusader.h" #include "../krglobal.h" #include "../icon.h" //This is the filter in the QFileDialog of Import/Export: static const char* FILE_FILTER = I18N_NOOP("*.xml|XML files\n*|All files"); UserActionPage::UserActionPage(QWidget* parent) : QWidget(parent) { auto* layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(6); // 0px margin, 6px item-spacing // ======== pseudo-toolbar start ======== auto* toolbarLayout = new QHBoxLayout; // neither margin nor spacing for the toolbar with autoRaise toolbarLayout->setSpacing(0); toolbarLayout->setContentsMargins(0, 0, 0, 0); newButton = new QToolButton(this); newButton->setIcon(Icon("document-new")); newButton->setAutoRaise(true); newButton->setToolTip(i18n("Create new useraction")); importButton = new QToolButton(this); importButton->setIcon(Icon("document-import")); importButton->setAutoRaise(true); importButton->setToolTip(i18n("Import useractions")); exportButton = new QToolButton(this); exportButton->setIcon(Icon("document-export")); exportButton->setAutoRaise(true); exportButton->setToolTip(i18n("Export useractions")); copyButton = new QToolButton(this); copyButton->setIcon(Icon("edit-copy")); copyButton->setAutoRaise(true); copyButton->setToolTip(i18n("Copy useractions to clipboard")); pasteButton = new QToolButton(this); pasteButton->setIcon(Icon("edit-paste")); pasteButton->setAutoRaise(true); pasteButton->setToolTip(i18n("Paste useractions from clipboard")); removeButton = new QToolButton(this); removeButton->setIcon(Icon("edit-delete")); removeButton->setAutoRaise(true); removeButton->setToolTip(i18n("Delete selected useractions")); toolbarLayout->addWidget(newButton); toolbarLayout->addWidget(importButton); toolbarLayout->addWidget(exportButton); toolbarLayout->addWidget(copyButton); toolbarLayout->addWidget(pasteButton); toolbarLayout->addSpacing(6); // 6 pixel nothing toolbarLayout->addWidget(removeButton); toolbarLayout->addStretch(1000); // some very large stretch-factor // ======== pseudo-toolbar end ======== /* This seems obsolete now! // Display some help KMessageBox::information( this, // parent i18n( "When you apply changes to an action, the modifications " "become available in the current session immediately.\n" "When closing ActionMan, you will be asked to save the changes permanently." ), QString(), // caption "show UserAction help" //dontShowAgainName for the config ); */ layout->addLayout(toolbarLayout); auto *split = new QSplitter(this); layout->addWidget(split, 1000); // again a very large stretch-factor to fix the height of the toolbar actionTree = new UserActionListView(split); actionTree->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); actionProperties = new ActionProperty(split); actionProperties->setEnabled(false); // if there are any actions in the list, the first is displayed and this widget is enabled connect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); connect(newButton, &QToolButton::clicked, this, &UserActionPage::slotNewAction); connect(removeButton, &QToolButton::clicked, this, &UserActionPage::slotRemoveAction); connect(importButton, &QToolButton::clicked, this, &UserActionPage::slotImport); connect(exportButton, &QToolButton::clicked, this, &UserActionPage::slotExport); connect(copyButton, &QToolButton::clicked, this, &UserActionPage::slotToClip); connect(pasteButton, &QToolButton::clicked, this, &UserActionPage::slotFromClip); // forwards the changed signal of the properties connect(actionProperties, SIGNAL(changed()), SIGNAL(changed())); actionTree->setFirstActionCurrent(); actionTree->setFocus(); } UserActionPage::~UserActionPage() = default; bool UserActionPage::continueInSpiteOfChanges() { if (! actionProperties->isModified()) return true; int answer = KMessageBox::questionYesNoCancel(this, i18n("The current action has been modified. Do you want to apply these changes?") ); if (answer == KMessageBox::Cancel) { disconnect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); actionTree->setCurrentAction(actionProperties->action()); connect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); return false; } if (answer == KMessageBox::Yes) { if (! actionProperties->validProperties()) { disconnect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); actionTree->setCurrentAction(actionProperties->action()); connect(actionTree, &UserActionListView::currentItemChanged, this, &UserActionPage::slotChangeCurrent); return false; } slotUpdateAction(); } // if Yes return true; } void UserActionPage::slotChangeCurrent() { if (! continueInSpiteOfChanges()) return; KrAction* action = actionTree->currentAction(); if (action) { actionProperties->setEnabled(true); // the distinct name is used as ID it is not allowed to change it afterwards because it is may referenced anywhere else actionProperties->leDistinctName->setEnabled(false); actionProperties->updateGUI(action); } else { // If the current item in the tree is no action (i.e. a category), disable the properties actionProperties->clear(); actionProperties->setEnabled(false); } emit applied(); // to disable the apply-button } void UserActionPage::slotUpdateAction() { // check that we have a command line, title and a name if (! actionProperties->validProperties()) return; if (actionProperties->leDistinctName->isEnabled()) { // := new entry KrAction* action = new KrAction(krApp->actionCollection(), actionProperties->leDistinctName->text()); krUserAction->addKrAction(action); actionProperties->updateAction(action); UserActionListViewItem* item = actionTree->insertAction(action); actionTree->setCurrentItem(item); } else { // := edit an existing actionProperties->updateAction(); actionTree->update(actionProperties->action()); // update the listviewitem as well... } apply(); } void UserActionPage::slotNewAction() { if (continueInSpiteOfChanges()) { actionTree->clearSelection(); // else the user may think that he is overwriting the selected action actionProperties->clear(); actionProperties->setEnabled(true); // it may be disabled because the tree has the focus on a category actionProperties->leDistinctName->setEnabled(true); actionProperties->leDistinctName->setFocus(); } } void UserActionPage::slotRemoveAction() { if (! dynamic_cast(actionTree->currentItem())) return; int messageDelete = KMessageBox::warningContinueCancel(this, //parent i18n("Are you sure that you want to remove all selected actions?"), //text i18n("Remove Selected Actions?"), //caption KStandardGuiItem::remove(), //Label for the continue-button KStandardGuiItem::cancel(), "Confirm Remove UserAction", //dontAskAgainName (for the config-file) KMessageBox::Dangerous | KMessageBox::Notify); if (messageDelete != KMessageBox::Continue) return; actionProperties->clear(); actionProperties->setEnabled(false); actionTree->removeSelectedActions(); apply(); } void UserActionPage::slotImport() { QString filename = QFileDialog::getOpenFileName(this, QString(), QString(), i18n(FILE_FILTER)); if (filename.isEmpty()) return; UserAction::KrActionList newActions; krUserAction->readFromFile(filename, UserAction::renameDoublicated, &newActions); QListIterator it(newActions); while (it.hasNext()) actionTree->insertAction(it.next()); if (newActions.count() > 0) { apply(); } } void UserActionPage::slotExport() { if (! dynamic_cast(actionTree->currentItem())) return; QString filename = QFileDialog::getSaveFileName(this, QString(), QString(), i18n(FILE_FILTER)); if (filename.isEmpty()) return; QDomDocument doc = QDomDocument(ACTION_DOCTYPE); QFile file(filename); int answer = 0; if (file.open(QIODevice::ReadOnly)) { // getting here, means the file already exists an can be read if (doc.setContent(&file)) // getting here means the file exists and already contains an UserAction-XML-tree answer = KMessageBox::warningYesNoCancel(this, //parent i18n("This file already contains some useractions.\nDo you want to overwrite it or should it be merged with the selected actions?"), //text i18n("Overwrite or Merge?"), //caption KStandardGuiItem::overwrite(), //label for Yes-Button KGuiItem(i18n("Merge")) //label for No-Button ); file.close(); } if (answer == 0 && file.exists()) answer = KMessageBox::warningContinueCancel(this, //parent i18n("This file already exists. Do you want to overwrite it?"), //text i18n("Overwrite Existing File?"), //caption KStandardGuiItem::overwrite() //label for Continue-Button ); if (answer == KMessageBox::Cancel) return; if (answer == KMessageBox::No) // that means the merge-button doc = actionTree->dumpSelectedActions(&doc); // merge else // Yes or Continue means overwrite doc = actionTree->dumpSelectedActions(); bool success = UserAction::writeToFile(doc, filename); if (! success) KMessageBox::error(this, i18n("Cannot open %1 for writing.\nNothing exported.", filename), i18n("Export Failed") ); } void UserActionPage::slotToClip() { if (! dynamic_cast(actionTree->currentItem())) return; QDomDocument doc = actionTree->dumpSelectedActions(); QApplication::clipboard()->setText(doc.toString()); } void UserActionPage::slotFromClip() { QDomDocument doc(ACTION_DOCTYPE); if (doc.setContent(QApplication::clipboard()->text())) { QDomElement root = doc.documentElement(); UserAction::KrActionList newActions; krUserAction->readFromElement(root, UserAction::renameDoublicated, &newActions); QListIterator it(newActions); while (it.hasNext()) actionTree->insertAction(it.next()); if (newActions.count() > 0) { apply(); } } // if ( doc.setContent ) } bool UserActionPage::readyToQuit() { // Check if the current UserAction has changed if (! continueInSpiteOfChanges()) return false; krUserAction->writeActionFile(); return true; } void UserActionPage::apply() { krUserAction->writeActionFile(); emit applied(); } void UserActionPage::applyChanges() { slotUpdateAction(); } diff --git a/krusader/ActionMan/useractionpage.h b/krusader/ActionMan/useractionpage.h index 57a598e3..582b32bd 100644 --- a/krusader/ActionMan/useractionpage.h +++ b/krusader/ActionMan/useractionpage.h @@ -1,82 +1,82 @@ /***************************************************************************** * Copyright (C) 2006 Shie Erlich * * Copyright (C) 2006 Rafi Yanai * - * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2006-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef USERACTIONPAGE_H #define USERACTIONPAGE_H // QtWidgets #include class UserActionListView; class ActionProperty; class QToolButton; class UserActionPage : public QWidget { Q_OBJECT public: explicit UserActionPage(QWidget* parent); ~UserActionPage() override; /** * Be sure to call this function before you delete this page!! * @return true if this page can be closed */ bool readyToQuit(); void applyChanges(); signals: void changed(); ///< emitted on changes to an action (used to enable the apply-button) void applied(); ///< emitted when changes are applied to an action (used to disable the apply-button) private: /** * If there are modifications in the property-widget, the user is asked * what to do. Apply, discard or continue editing. In the first case, * saving is done in this function. * @return true if a new action can be loaded in the property-widget. */ bool continueInSpiteOfChanges(); /** * applies all changes by writing the actionfile and emits "applied" */ void apply(); //bool _modified; ///< true if the action-tree was changed (= changes were applied to an action) UserActionListView *actionTree; ActionProperty *actionProperties; QToolButton *importButton, *exportButton; QToolButton *copyButton, *pasteButton; QToolButton *removeButton, *newButton; private slots: void slotChangeCurrent(); //loads a new action into the detail-view void slotUpdateAction(); //updates the action to the xml-file void slotNewAction(); void slotRemoveAction(); void slotImport(); void slotExport(); void slotToClip(); void slotFromClip(); }; #endif diff --git a/krusader/Archive/abstractthreadedjob.cpp b/krusader/Archive/abstractthreadedjob.cpp index 1e22ec8e..4c8558d2 100644 --- a/krusader/Archive/abstractthreadedjob.cpp +++ b/krusader/Archive/abstractthreadedjob.cpp @@ -1,664 +1,664 @@ /***************************************************************************** * Copyright (C) 2009 Csaba Karai * - * Copyright (C) 2009-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2009-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "abstractthreadedjob.h" // QtCore #include #include #include #include #include #include // QtWidgets #include #include #include #include #include "krarchandler.h" #include "../krglobal.h" #include "../krservices.h" #include "../FileSystem/filesystemprovider.h" extern KRarcHandler arcHandler; AbstractThreadedJob::AbstractThreadedJob() : _maxProgressValue(0), _currentProgress(0), _exiting(false), _jobThread(nullptr) { } void AbstractThreadedJob::startAbstractJobThread(AbstractJobThread * jobThread) { _jobThread = jobThread; _jobThread->setJob(this); _jobThread->moveToThread(_jobThread); _jobThread->start(); } AbstractThreadedJob::~AbstractThreadedJob() { _exiting = true; if (_jobThread) { _jobThread->abort(); _locker.lock(); _waiter.wakeAll(); _locker.unlock(); _jobThread->wait(); delete _jobThread; } } bool AbstractThreadedJob::event(QEvent *e) { if (e->type() == QEvent::User) { auto *event = (UserEvent*) e; switch (event->command()) { case CMD_SUCCESS: { emitResult(); } break; case CMD_ERROR: { auto error = event->args()[ 0 ].value(); QString errorText = event->args()[ 1 ].value(); setError(error); setErrorText(errorText); emitResult(); } break; case CMD_INFO: { QString info = event->args()[ 0 ].value(); QString arg1 = event->args()[ 1 ].value(); QString arg2 = event->args()[ 2 ].value(); QString arg3 = event->args()[ 3 ].value(); QString arg4 = event->args()[ 4 ].value(); _title = info; emit description(this, info, qMakePair(arg1, arg2), qMakePair(arg3, arg4)); } break; case CMD_RESET: { QString info = event->args()[ 0 ].value(); QString arg1 = event->args()[ 1 ].value(); QString arg2 = event->args()[ 2 ].value(); QString arg3 = event->args()[ 3 ].value(); QString arg4 = event->args()[ 4 ].value(); _title = info; setProcessedAmount(KJob::Bytes, 0); setTotalAmount(KJob::Bytes, 0); emitSpeed(0); emit description(this, info, qMakePair(arg1, arg2), qMakePair(arg3, arg4)); } break; case CMD_UPLOAD_FILES: case CMD_DOWNLOAD_FILES: { QList sources = KrServices::toUrlList(event->args()[ 0 ].value()); QUrl dest = event->args()[ 1 ].value(); KIO::Job *job = KIO::copy(sources, dest, KIO::HideProgressInfo); addSubjob(job); job->setUiDelegate(new KIO::JobUiDelegate()); connect(job, &KIO::Job::result, this, &AbstractThreadedJob::slotDownloadResult); connect(job, SIGNAL(processedAmount(KJob*,KJob::Unit,qulonglong)), this, SLOT(slotProcessedAmount(KJob*,KJob::Unit,qulonglong))); connect(job, SIGNAL(totalAmount(KJob*,KJob::Unit,qulonglong)), this, SLOT(slotTotalAmount(KJob*,KJob::Unit,qulonglong))); connect(job, SIGNAL(speed(KJob*,ulong)), this, SLOT(slotSpeed(KJob*,ulong))); connect(job, SIGNAL(description(KJob*,QString,QPair,QPair)), this, SLOT(slotDescription(KJob*,QString,QPair,QPair))); } break; case CMD_MAXPROGRESSVALUE: { auto maxValue = event->args()[ 0 ].value(); _maxProgressValue = maxValue; _currentProgress = 0; } break; case CMD_ADD_PROGRESS: { auto progress = event->args()[ 0 ].value(); _currentProgress += progress; if (_maxProgressValue != 0) { setPercent(100 * _currentProgress / _maxProgressValue); int elapsed = _time.isNull() ? 1 : _time.secsTo(QTime::currentTime()); if (elapsed != 0 && event->args().count() > 1) { _time = QTime::currentTime(); QString progressString = (event->args()[ 1 ].value()); emit description(this, _title, qMakePair(progressString, QString("%1/%2").arg(_currentProgress).arg(_maxProgressValue)), qMakePair(QString(), QString()) ); } } } break; case CMD_GET_PASSWORD: { QString path = event->args()[ 0 ].value(); QString password = KRarcHandler::getPassword(path); auto *resultResp = new QList (); (*resultResp) << password; addEventResponse(resultResp); } break; case CMD_MESSAGE: { QString message = event->args()[ 0 ].value(); auto *ui = dynamic_cast(uiDelegate()); KMessageBox::information(ui ? ui->window() : nullptr, message); auto *resultResp = new QList (); addEventResponse(resultResp); } break; } return true; } else { return KIO::Job::event(e); } } void AbstractThreadedJob::addEventResponse(QList * obj) { _locker.lock(); _stack.push(obj); _waiter.wakeOne(); _locker.unlock(); } QList * AbstractThreadedJob::getEventResponse(UserEvent * event) { _locker.lock(); QApplication::postEvent(this, event); _waiter.wait(&_locker); if (_exiting) return nullptr; QList *resp = _stack.pop(); _locker.unlock(); return resp; } void AbstractThreadedJob::sendEvent(UserEvent * event) { QApplication::postEvent(this, event); } void AbstractThreadedJob::slotDownloadResult(KJob* job) { auto *resultResp = new QList (); if (job) { (*resultResp) << QVariant(job->error()); (*resultResp) << QVariant(job->errorText()); } else { (*resultResp) << QVariant(KJob::UserDefinedError); (*resultResp) << QVariant(QString(i18n("Internal error, undefined in result signal"))); } addEventResponse(resultResp); } void AbstractThreadedJob::slotProcessedAmount(KJob *, KJob::Unit unit, qulonglong xu) { setProcessedAmount(unit, xu); } void AbstractThreadedJob::slotTotalAmount(KJob *, KJob::Unit unit, qulonglong xu) { setTotalAmount(unit, xu); } void AbstractThreadedJob::slotSpeed(KJob *, unsigned long spd) { emitSpeed(spd); } void AbstractThreadedJob::slotDescription(KJob *, const QString &title, const QPair &field1, const QPair &field2) { QString mytitle = title; if (!_title.isNull()) mytitle = _title; emit description(this, mytitle, field1, field2); } class AbstractJobObserver : public KRarcObserver { protected: AbstractJobThread * _jobThread; public: explicit AbstractJobObserver(AbstractJobThread * thread): _jobThread(thread) {} ~AbstractJobObserver() override = default; void processEvents() Q_DECL_OVERRIDE { usleep(1000); qApp->processEvents(); } void subJobStarted(const QString & jobTitle, int count) Q_DECL_OVERRIDE { _jobThread->sendReset(jobTitle); _jobThread->sendMaxProgressValue(count); } void subJobStopped() Q_DECL_OVERRIDE { } bool wasCancelled() Q_DECL_OVERRIDE { return _jobThread->_exited; } void error(const QString & error) Q_DECL_OVERRIDE { _jobThread->sendError(KIO::ERR_NO_CONTENT, error); } void detailedError(const QString & error, const QString & details) Q_DECL_OVERRIDE { _jobThread->sendError(KIO::ERR_NO_CONTENT, error + '\n' + details); } void incrementProgress(int c) Q_DECL_OVERRIDE { _jobThread->sendAddProgress(c, _jobThread->_progressTitle); } }; AbstractJobThread::AbstractJobThread() : _job(nullptr), _downloadTempDir(nullptr), _observer(nullptr), _tempFile(nullptr), _tempDir(nullptr), _exited(false) { } AbstractJobThread::~AbstractJobThread() { if (_downloadTempDir) { delete _downloadTempDir; _downloadTempDir = nullptr; } if (_observer) { delete _observer; _observer = nullptr; } if (_tempFile) { delete _tempFile; _tempFile = nullptr; } } void AbstractJobThread::run() { QTimer::singleShot(0, this, &AbstractJobThread::slotStart); QPointer threadLoop = new QEventLoop(this); _loop = threadLoop; threadLoop->exec(); _loop = nullptr; delete threadLoop; } void AbstractJobThread::terminate() { if (_loop && !_exited) { _loop->quit(); _exited = true; } } void AbstractJobThread::abort() { terminate(); } QList AbstractJobThread::remoteUrls(const QUrl &baseUrl, const QStringList & files) { QList urlList; foreach(const QString &name, files) { QUrl url = baseUrl; url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + (name)); urlList << url; } return urlList; } QUrl AbstractJobThread::downloadIfRemote(const QUrl &baseUrl, const QStringList & files) { // download remote URL-s if necessary if (!baseUrl.isLocalFile()) { sendInfo(i18n("Downloading remote files")); _downloadTempDir = new QTemporaryDir(); QList urlList = remoteUrls(baseUrl, files); QUrl dest(_downloadTempDir->path()); QList args; args << KrServices::toStringList(urlList); args << dest; auto * downloadEvent = new UserEvent(CMD_DOWNLOAD_FILES, args); QList * result = _job->getEventResponse(downloadEvent); if (result == nullptr) return QUrl(); auto errorCode = (*result)[ 0 ].value(); QString errorText = (*result)[ 1 ].value(); delete result; if (errorCode) { sendError(errorCode, errorText); return QUrl(); } else { return dest; } } else { return baseUrl; } } QString AbstractJobThread::tempFileIfRemote(const QUrl &kurl, const QString &type) { if (kurl.isLocalFile()) { return kurl.path(); } _tempFile = new QTemporaryFile(QDir::tempPath() + QLatin1String("/krusader_XXXXXX.") + type); _tempFile->open(); _tempFileName = _tempFile->fileName(); _tempFile->close(); // necessary to create the filename QFile::remove(_tempFileName); _tempFileTarget = kurl; return _tempFileName; } QString AbstractJobThread::tempDirIfRemote(const QUrl &kurl) { if (kurl.isLocalFile()) { return kurl.adjusted(QUrl::StripTrailingSlash).path(); } _tempDir = new QTemporaryDir(); _tempDirTarget = kurl; return _tempDirName = _tempDir->path(); } void AbstractJobThread::sendSuccess() { terminate(); QList args; auto * errorEvent = new UserEvent(CMD_SUCCESS, args); _job->sendEvent(errorEvent); } void AbstractJobThread::sendError(int errorCode, const QString& message) { terminate(); QList args; args << errorCode; args << message; auto * errorEvent = new UserEvent(CMD_ERROR, args); _job->sendEvent(errorEvent); } void AbstractJobThread::sendInfo(const QString& message, const QString& a1, const QString& a2, const QString& a3, const QString& a4) { QList args; args << message; args << a1; args << a2; args << a3; args << a4; auto * infoEvent = new UserEvent(CMD_INFO, args); _job->sendEvent(infoEvent); } void AbstractJobThread::sendReset(const QString& message, const QString& a1, const QString& a2, const QString& a3, const QString& a4) { QList args; args << message; args << a1; args << a2; args << a3; args << a4; auto * infoEvent = new UserEvent(CMD_RESET, args); _job->sendEvent(infoEvent); } void AbstractJobThread::sendMaxProgressValue(qulonglong value) { QList args; args << value; auto * infoEvent = new UserEvent(CMD_MAXPROGRESSVALUE, args); _job->sendEvent(infoEvent); } void AbstractJobThread::sendAddProgress(qulonglong value, const QString &progress) { QList args; args << value; if (!progress.isNull()) args << progress; auto * infoEvent = new UserEvent(CMD_ADD_PROGRESS, args); _job->sendEvent(infoEvent); } void countFiles(const QString &path, unsigned long &totalFiles, bool &stop) { const QDir dir(path); if (!dir.exists()) { totalFiles++; // assume it's a file return; } for (const QString& name : dir.entryList()) { if (stop) return; if (name == QStringLiteral(".") || name == QStringLiteral("..")) continue; countFiles(dir.absoluteFilePath(name), totalFiles, stop); } } void AbstractJobThread::countLocalFiles(const QUrl &baseUrl, const QStringList &names, unsigned long &totalFiles) { sendReset(i18n("Counting files")); FileSystem *calcSpaceFileSystem = FileSystemProvider::instance().getFilesystem(baseUrl); calcSpaceFileSystem->scanDir(baseUrl); for (const QString& name : names) { if (_exited) return; const QString path = calcSpaceFileSystem->getUrl(name).toLocalFile(); if (!QFileInfo(path).exists()) return; countFiles(path, totalFiles, _exited); } delete calcSpaceFileSystem; } KRarcObserver * AbstractJobThread::observer() { if (_observer) return _observer; _observer = new AbstractJobObserver(this); return _observer; } bool AbstractJobThread::uploadTempFiles() { if (_tempFile != nullptr || _tempDir != nullptr) { sendInfo(i18n("Uploading to remote destination")); if (_tempFile) { QList urlList; urlList << QUrl::fromLocalFile(_tempFileName); QList args; args << KrServices::toStringList(urlList); args << _tempFileTarget; auto * uploadEvent = new UserEvent(CMD_UPLOAD_FILES, args); QList * result = _job->getEventResponse(uploadEvent); if (result == nullptr) return false; auto errorCode = (*result)[ 0 ].value(); QString errorText = (*result)[ 1 ].value(); delete result; if (errorCode) { sendError(errorCode, errorText); return false; } } if (_tempDir) { QList urlList; QDir tempDir(_tempDirName); QStringList list = tempDir.entryList(); foreach(const QString &name, list) { if (name == "." || name == "..") continue; QUrl url = QUrl::fromLocalFile(_tempDirName).adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + (name)); urlList << url; } QList args; args << KrServices::toStringList(urlList); args << _tempDirTarget; auto * uploadEvent = new UserEvent(CMD_UPLOAD_FILES, args); QList * result = _job->getEventResponse(uploadEvent); if (result == nullptr) return false; auto errorCode = (*result)[ 0 ].value(); QString errorText = (*result)[ 1 ].value(); delete result; if (errorCode) { sendError(errorCode, errorText); return false; } } } return true; } QString AbstractJobThread::getPassword(const QString &path) { QList args; args << path; auto * getPasswdEvent = new UserEvent(CMD_GET_PASSWORD, args); QList * result = _job->getEventResponse(getPasswdEvent); if (result == nullptr) return QString(); QString password = (*result)[ 0 ].value(); if (password.isNull()) password = QString(""); delete result; return password; } void AbstractJobThread::sendMessage(const QString &message) { QList args; args << message; auto * getPasswdEvent = new UserEvent(CMD_MESSAGE, args); QList * result = _job->getEventResponse(getPasswdEvent); if (result == nullptr) return; delete result; } //! Gets some archive information that is needed in several cases. /*! \param path A path to the archive. \param type The type of the archive. \param password The password of the archive. \param arcName The name of the archive. \param sourceFolder A QUrl, which may be remote, of the folder where the archive is. \return If the archive information has been obtained. */ bool AbstractJobThread::getArchiveInformation(QString &path, QString &type, QString &password, QString &arcName, const QUrl &sourceFolder) { // Safety checks (though the user shouldn't have been able to select something named "" or "..") if (arcName.isEmpty()) return false; if (arcName == "..") return false; QUrl url = sourceFolder.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + arcName); path = url.adjusted(QUrl::StripTrailingSlash).path(); QMimeDatabase db; QMimeType mt = db.mimeTypeForUrl(url); QString mime = mt.isValid() ? mt.name() : QString(); bool encrypted = false; type = arcHandler.getType(encrypted, path, mime); // Check that the archive is supported if (!KRarcHandler::arcSupported(type)) { sendError(KIO::ERR_NO_CONTENT, i18nc("%1=archive filename", "%1, unsupported archive type.", arcName)); return false; } password = encrypted ? getPassword(path) : QString(); return true; } diff --git a/krusader/Archive/abstractthreadedjob.h b/krusader/Archive/abstractthreadedjob.h index 85af4e5c..b14a986a 100644 --- a/krusader/Archive/abstractthreadedjob.h +++ b/krusader/Archive/abstractthreadedjob.h @@ -1,188 +1,188 @@ /***************************************************************************** * Copyright (C) 2009 Csaba Karai * - * Copyright (C) 2009-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2009-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef ABSTRACTTHREADEDJOB_H #define ABSTRACTTHREADEDJOB_H // QtCore #include #include #include #include #include #include #include #include #include #include #include #include class AbstractJobThread; class QTemporaryDir; class UserEvent; class KRarcObserver; class QTemporaryFile; class AbstractThreadedJob : public KIO::Job { friend class AbstractJobThread; Q_OBJECT protected: AbstractThreadedJob(); void addEventResponse(QList * obj); QList * getEventResponse(UserEvent * event); void sendEvent(UserEvent * event); virtual ~AbstractThreadedJob(); virtual bool event(QEvent *) Q_DECL_OVERRIDE; virtual void startAbstractJobThread(AbstractJobThread*); virtual bool doSuspend() Q_DECL_OVERRIDE { return false; } protected slots: void slotDownloadResult(KJob*); void slotProcessedAmount(KJob *, KJob::Unit, qulonglong); void slotTotalAmount(KJob *, KJob::Unit, qulonglong); void slotSpeed(KJob *, unsigned long); void slotDescription(KJob *job, const QString &title, const QPair &field1, const QPair &field2); public: QMutex _locker; QWaitCondition _waiter; QStack *> _stack; QString _title; qulonglong _maxProgressValue; qulonglong _currentProgress; QTime _time; bool _exiting; private: AbstractJobThread * _jobThread; }; class AbstractJobThread : public QThread { friend class AbstractThreadedJob; friend class AbstractJobObserver; Q_OBJECT public: AbstractJobThread(); virtual ~AbstractJobThread(); void abort(); KRarcObserver * observer(); protected slots: virtual void slotStart() = 0; protected: virtual void run() Q_DECL_OVERRIDE; void setJob(AbstractThreadedJob * job) { _job = job; } QList remoteUrls(const QUrl &baseUrl, const QStringList & files); QUrl downloadIfRemote(const QUrl &baseUrl, const QStringList & files); void countLocalFiles(const QUrl &baseUrl, const QStringList &names, unsigned long &totalFiles); void sendError(int errorCode, const QString& message); void sendInfo(const QString& message, const QString& a1 = QString(), const QString& a2 = QString(), const QString& a3 = QString(), const QString& a4 = QString()); void sendReset(const QString& message, const QString& a1 = QString(""), const QString& a2 = QString(""), const QString& a3 = QString(""), const QString& a4 = QString("")); void sendSuccess(); void sendMessage(const QString &message); void sendMaxProgressValue(qulonglong value); void sendAddProgress(qulonglong value, const QString &progress = QString()); void setProgressTitle(const QString &title) { _progressTitle = title; } QString tempFileIfRemote(const QUrl &kurl, const QString &type); QString tempDirIfRemote(const QUrl &kurl); bool uploadTempFiles(); bool isExited() { return _exited; } void terminate(); QString getPassword(const QString &path); bool getArchiveInformation(QString &, QString &, QString &, QString &, const QUrl &); AbstractThreadedJob *_job; QEventLoop *_loop; QTemporaryDir *_downloadTempDir; KRarcObserver *_observer; QTemporaryFile *_tempFile; QString _tempFileName; QUrl _tempFileTarget; QTemporaryDir *_tempDir; QString _tempDirName; QUrl _tempDirTarget; bool _exited; QString _progressTitle; }; enum PossibleCommands { CMD_ERROR = 1, CMD_INFO = 2, CMD_RESET = 3, CMD_DOWNLOAD_FILES = 4, CMD_UPLOAD_FILES = 5, CMD_SUCCESS = 6, CMD_MAXPROGRESSVALUE = 7, CMD_ADD_PROGRESS = 8, CMD_GET_PASSWORD = 9, CMD_MESSAGE = 10 }; class UserEvent : public QEvent { public: UserEvent(int command, const QList &args) : QEvent(QEvent::User), _command(command), _args(args) {} inline int command() { return _command; } inline const QList & args() { return _args; } protected: int _command; QList _args; }; #endif // __ABSTRACTTHREADED_JOB_H__ diff --git a/krusader/Archive/kr7zencryptionchecker.cpp b/krusader/Archive/kr7zencryptionchecker.cpp index 27c72a6a..72cc85e2 100644 --- a/krusader/Archive/kr7zencryptionchecker.cpp +++ b/krusader/Archive/kr7zencryptionchecker.cpp @@ -1,63 +1,63 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "kr7zencryptionchecker.h" Kr7zEncryptionChecker::Kr7zEncryptionChecker() : encrypted(false), lastData() { setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect! connect(this, &Kr7zEncryptionChecker::readyReadStandardOutput, this, [=]() {receivedOutput(); }); } void Kr7zEncryptionChecker::setupChildProcess() { // This function is called after the fork but for the exec. We create a process group // to work around a broken wrapper script of 7z. Without this only the wrapper is killed. setsid(); // make this process leader of a new process group } void Kr7zEncryptionChecker::receivedOutput() { QString data = QString::fromLocal8Bit(this->readAllStandardOutput()); QString checkable = lastData + data; QStringList lines = checkable.split('\n'); lastData = lines[ lines.count() - 1 ]; for (int i = 0; i != lines.count(); i++) { QString line = lines[ i ].trimmed().toLower(); int ndx = line.indexOf("testing"); if (ndx >= 0) line.truncate(ndx); if (line.isEmpty()) continue; if (line.contains("password") && line.contains("enter")) { encrypted = true; ::kill(- pid(), SIGKILL); // kill the whole process group by giving the negative PID } } } bool Kr7zEncryptionChecker::isEncrypted() { return encrypted; } diff --git a/krusader/Archive/kr7zencryptionchecker.h b/krusader/Archive/kr7zencryptionchecker.h index 160ec3da..0c7ea9d3 100644 --- a/krusader/Archive/kr7zencryptionchecker.h +++ b/krusader/Archive/kr7zencryptionchecker.h @@ -1,53 +1,53 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KR7ZENCRYPTIONCHECKER_H #define KR7ZENCRYPTIONCHECKER_H #include // for setsid, see Kr7zEncryptionChecker::setupChildProcess #include // for kill #include /** * Used by ArcHandler. */ class Kr7zEncryptionChecker : public KProcess { Q_OBJECT public: Kr7zEncryptionChecker(); protected: void setupChildProcess() Q_DECL_OVERRIDE; public slots: void receivedOutput(); bool isEncrypted(); private: QString fileName; bool encrypted; QString lastData; }; #endif // KR7ZENCRYPTIONCHECKER_H diff --git a/krusader/Archive/krarchandler.cpp b/krusader/Archive/krarchandler.cpp index 3648e57e..6cbeabc9 100644 --- a/krusader/Archive/krarchandler.cpp +++ b/krusader/Archive/krarchandler.cpp @@ -1,669 +1,669 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krarchandler.h" // QtCore #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include "kr7zencryptionchecker.h" #include "../krglobal.h" #include "../defaults.h" #include "../krservices.h" #include "../Dialogs/krpleasewait.h" #include "../../krArc/krlinecountingprocess.h" #if 0 class DefaultKRarcObserver : public KRarcObserver { public: DefaultKRarcObserver() {} virtual ~DefaultKRarcObserver() {} virtual void processEvents() Q_DECL_OVERRIDE { usleep(1000); qApp->processEvents(); } virtual void subJobStarted(const QString & jobTitle, int count) Q_DECL_OVERRIDE { krApp->startWaiting(jobTitle, count, true); } virtual void subJobStopped() Q_DECL_OVERRIDE { krApp->stopWait(); } virtual bool wasCancelled() Q_DECL_OVERRIDE { return krApp->wasWaitingCancelled(); } virtual void error(const QString & error) Q_DECL_OVERRIDE { KMessageBox::error(krApp, error, i18n("Error")); } virtual void detailedError(const QString & error, const QString & details) Q_DECL_OVERRIDE { KMessageBox::detailedError(krApp, error, details, i18n("Error")); } virtual void incrementProgress(int c) Q_DECL_OVERRIDE { krApp->plzWait->incProgress(c); } }; #endif static QStringList arcProtocols = QString("tar;bzip;bzip2;lzma;xz;gzip;krarc;zip").split(';'); KWallet::Wallet * KRarcHandler::wallet = nullptr; QStringList KRarcHandler::supportedPackers() { QStringList packers; // we will simply try to find the packers here.. if (KrServices::cmdExist("tar")) packers.append("tar"); if (KrServices::cmdExist("gzip")) packers.append("gzip"); if (KrServices::cmdExist("bzip2")) packers.append("bzip2"); if (KrServices::cmdExist("lzma")) packers.append("lzma"); if (KrServices::cmdExist("xz")) packers.append("xz"); if (KrServices::cmdExist("unzip")) packers.append("unzip"); if (KrServices::cmdExist("zip")) packers.append("zip"); if (KrServices::cmdExist("zip")) packers.append("cbz"); if (KrServices::cmdExist("lha")) packers.append("lha"); if (KrServices::cmdExist("cpio")) packers.append("cpio"); if (KrServices::cmdExist("unrar")) packers.append("unrar"); if (KrServices::cmdExist("rar")) packers.append("rar"); if (KrServices::cmdExist("rar")) packers.append("cbr"); if (KrServices::cmdExist("arj")) packers.append("arj"); if (KrServices::cmdExist("unarj")) packers.append("unarj"); if (KrServices::cmdExist("unace")) packers.append("unace"); if (KrServices::cmdExist("dpkg")) packers.append("dpkg"); if (KrServices::cmdExist("7z") || KrServices::cmdExist("7za")) packers.append("7z"); if (KrServices::cmdExist("rpm") && KrServices::cmdExist("rpm2cpio")) packers.append("rpm"); // qDebug() << "Supported Packers:"; //QStringList::Iterator it; //for( it = packers.begin(); it != packers.end(); ++it ) // qDebug() << *it; return packers; } bool KRarcHandler::arcSupported(QString type) { // lst will contain the supported unpacker list... const KConfigGroup group(krConfig, "Archives"); const QStringList lst = group.readEntry("Supported Packers", QStringList()); // Let's notice that in some cases the QString `type` that arrives here // represents a mimetype, and in some other cases it represents // a short identifier. // If `type` is not a short identifier then it's supposed that `type` is a mime type if (type.length() > maxLenType) { type = getShortTypeFromMime(type); } return (type == "zip" && lst.contains("unzip")) || (type == "tar" && lst.contains("tar")) || (type == "tbz" && lst.contains("tar")) || (type == "tgz" && lst.contains("tar")) || (type == "tlz" && lst.contains("tar")) || (type == "txz" && lst.contains("tar")) || (type == "tarz" && lst.contains("tar")) || (type == "gzip" && lst.contains("gzip")) || (type == "bzip2" && lst.contains("bzip2")) || (type == "lzma" && lst.contains("lzma")) || (type == "xz" && lst.contains("xz")) || (type == "lha" && lst.contains("lha")) || (type == "ace" && lst.contains("unace")) || (type == "rpm" && lst.contains("cpio")) || (type == "cpio" && lst.contains("cpio")) || (type == "rar" && (lst.contains("unrar") || lst.contains("rar"))) || (type == "arj" && (lst.contains("unarj") || lst.contains("arj"))) || (type == "deb" && (lst.contains("dpkg") && lst.contains("tar"))) || (type == "7z" && lst.contains("7z")); } long KRarcHandler::arcFileCount(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer) { int divideWith = 1; // first check if supported if (!arcSupported(type)) return 0; // bzip2, gzip, etc. archives contain only one file if (type == "bzip2" || type == "gzip" || type == "lzma" || type == "xz") return 1L; // set the right lister to do the job QStringList lister; if (type == "zip") lister << KrServices::fullPathName("unzip") << "-ZTs"; else if (type == "tar") lister << KrServices::fullPathName("tar") << "-tvf"; else if (type == "tgz") lister << KrServices::fullPathName("tar") << "-tvzf"; else if (type == "tarz") lister << KrServices::fullPathName("tar") << "-tvzf"; else if (type == "tbz") lister << KrServices::fullPathName("tar") << "-tjvf"; else if (type == "tlz") lister << KrServices::fullPathName("tar") << "--lzma" << "-tvf"; else if (type == "txz") lister << KrServices::fullPathName("tar") << "--xz" << "-tvf"; else if (type == "lha") lister << KrServices::fullPathName("lha") << "l"; else if (type == "rar") lister << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "l" << "-v"; else if (type == "ace") lister << KrServices::fullPathName("unace") << "l"; else if (type == "arj") { if (KrServices::cmdExist("arj")) lister << KrServices::fullPathName("arj") << "v" << "-y" << "-v", divideWith = 4; else lister << KrServices::fullPathName("unarj") << "l"; } else if (type == "rpm") lister << KrServices::fullPathName("rpm") << "--dump" << "-lpq"; else if (type == "deb") lister << KrServices::fullPathName("dpkg") << "-c"; else if (type == "7z") lister << KrServices::fullPathName("7z") << "-y" << "l"; else return 0L; if (!password.isNull()) { if (type == "arj") lister << QString("-g%1").arg(password); if (type == "ace" || type == "rar" || type == "7z") lister << QString("-p%1").arg(password); } // tell the user to wait observer->subJobStarted(i18n("Counting files in archive"), 0); // count the number of files in the archive long count = 1; KProcess list; list << lister << archive; if (type == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! list.setStandardInputFile("/dev/ptmx"); list.setOutputChannelMode(KProcess::SeparateChannels); // without this output redirection has no effect list.start(); // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking // it would be better to connect to started(), error() and finished() if (list.waitForStarted()) while (list.state() == QProcess::Running) { observer->processEvents(); if (observer->wasCancelled()) list.kill(); } ; // busy wait - need to find something better... observer->subJobStopped(); if (list.exitStatus() != QProcess::NormalExit || !checkStatus(type, list.exitCode())) { observer->detailedError(i18n("Failed to list the content of the archive (%1).", archive), QString::fromLocal8Bit(list.readAllStandardError())); return 0; } count = list.readAllStandardOutput().count('\n'); //make sure you call stopWait after this function return... // observer->subJobStopped(); return count / divideWith; } bool KRarcHandler::unpack(QString archive, const QString& type, const QString& password, const QString& dest, KRarcObserver *observer) { KConfigGroup group(krConfig, "Archives"); if (group.readEntry("Test Before Unpack", _TestBeforeUnpack)) { // test first - or be sorry later... if (type != "rpm" && type != "deb" && !test(archive, type, password, observer, 0)) { observer->error(i18n("Failed to unpack %1.", archive)); return false; } } // count the files in the archive long count = arcFileCount(archive, type, password, observer); if (count == 0) return false; // not supported if (count == 1) count = 0; // choose the right packer for the job QString cpioName; QStringList packer; // set the right packer to do the job if (type == "zip") packer << KrServices::fullPathName("unzip") << "-o"; else if (type == "tar") packer << KrServices::fullPathName("tar") << "-xvf"; else if (type == "tgz") packer << KrServices::fullPathName("tar") << "-xvzf"; else if (type == "tarz") packer << KrServices::fullPathName("tar") << "-xvzf"; else if (type == "tbz") packer << KrServices::fullPathName("tar") << "-xjvf"; else if (type == "tlz") packer << KrServices::fullPathName("tar") << "--lzma" << "-xvf"; else if (type == "txz") packer << KrServices::fullPathName("tar") << "--xz" << "-xvf"; else if (type == "gzip") packer << KrServices::fullPathName("gzip") << "-cd"; else if (type == "bzip2") packer << KrServices::fullPathName("bzip2") << "-cdk"; else if (type == "lzma") packer << KrServices::fullPathName("lzma") << "-cdk"; else if (type == "xz") packer << KrServices::fullPathName("xz") << "-cdk"; else if (type == "lha") packer << KrServices::fullPathName("lha") << "xf"; else if (type == "rar") packer << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "-y" << "x"; else if (type == "ace") packer << KrServices::fullPathName("unace") << "x"; else if (type == "arj") { if (KrServices::cmdExist("arj")) packer << KrServices::fullPathName("arj") << "-y" << "-v" << "x"; else packer << KrServices::fullPathName("unarj") << "x"; } else if (type == "7z") packer << KrServices::fullPathName("7z") << "-y" << "x"; else if (type == "rpm") { // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone) cpioName = QDir::tempPath() + QStringLiteral("/contents.cpio"); KrLinecountingProcess cpio; cpio << KrServices::fullPathName("rpm2cpio") << archive; cpio.setStandardOutputFile(cpioName); // TODO maybe no tmpfile but a pipe (setStandardOutputProcess(packer)) cpio.start(); if (!cpio.waitForFinished() || cpio.exitStatus() != QProcess::NormalExit || !checkStatus("cpio", cpio.exitCode())) { observer->detailedError(i18n("Failed to convert rpm (%1) to cpio.", archive), cpio.getErrorMsg()); return 0; } archive = cpioName; packer << KrServices::fullPathName("cpio") << "--force-local" << "--no-absolute-filenames" << "-iuvdF"; } else if (type == "deb") { // TODO use QTemporaryFile (setAutoRemove(false) when asynchrone) cpioName = QDir::tempPath() + QStringLiteral("/contents.tar"); KrLinecountingProcess dpkg; dpkg << KrServices::fullPathName("dpkg") << "--fsys-tarfile" << archive; dpkg.setStandardOutputFile(cpioName); // TODO maybe no tmpfile but a pipe (setStandardOutputProcess(packer)) dpkg.start(); if (!dpkg.waitForFinished() || dpkg.exitStatus() != QProcess::NormalExit || !checkStatus("deb", dpkg.exitCode())) { observer->detailedError(i18n("Failed to convert deb (%1) to tar.", archive), dpkg.getErrorMsg()); return 0; } archive = cpioName; packer << KrServices::fullPathName("tar") << "xvf"; } else return false; if (!password.isNull()) { if (type == "zip") packer << "-P" << password; if (type == "arj") packer << QString("-g%1").arg(password); if (type == "ace" || type == "rar" || type == "7z") packer << QString("-p%1").arg(password); } // unpack the files KrLinecountingProcess proc; proc << packer << archive; if (type == "bzip2" || type == "gzip" || type == "lzma" || type == "xz") { QString arcname = archive.mid(archive.lastIndexOf("/") + 1); if (arcname.contains(".")) arcname = arcname.left(arcname.lastIndexOf(".")); proc.setStandardOutputFile(dest + '/' + arcname); } if (type == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); proc.setWorkingDirectory(dest); // tell the user to wait observer->subJobStarted(i18n("Unpacking File(s)"), count); if (count != 0) { connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KRarcObserver::incrementProgress); if (type == "rpm") connect(&proc, &KrLinecountingProcess::newErrorLines, observer, &KRarcObserver::incrementProgress); } // start the unpacking process proc.start(); // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking // it would be better to connect to started(), error() and finished() if (proc.waitForStarted()) while (proc.state() == QProcess::Running) { observer->processEvents(); if (observer->wasCancelled()) proc.kill(); } ; // busy wait - need to find something better... observer->subJobStopped(); if (!cpioName.isEmpty()) QFile(cpioName).remove(); /* remove the cpio file */ // check the return value if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) { observer->detailedError(i18n("Failed to unpack %1.", archive), observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg()); return false; } return true; // SUCCESS } bool KRarcHandler::test(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer, long count) { // choose the right packer for the job QStringList packer; // set the right packer to do the job if (type == "zip") packer << KrServices::fullPathName("unzip") << "-t"; else if (type == "tar") packer << KrServices::fullPathName("tar") << "-tvf"; else if (type == "tgz") packer << KrServices::fullPathName("tar") << "-tvzf"; else if (type == "tarz") packer << KrServices::fullPathName("tar") << "-tvzf"; else if (type == "tbz") packer << KrServices::fullPathName("tar") << "-tjvf"; else if (type == "tlz") packer << KrServices::fullPathName("tar") << "--lzma" << "-tvf"; else if (type == "txz") packer << KrServices::fullPathName("tar") << "--xz" << "-tvf"; else if (type == "gzip") packer << KrServices::fullPathName("gzip") << "-tv"; else if (type == "bzip2") packer << KrServices::fullPathName("bzip2") << "-tv"; else if (type == "lzma") packer << KrServices::fullPathName("lzma") << "-tv"; else if (type == "xz") packer << KrServices::fullPathName("xz") << "-tv"; else if (type == "rar") packer << KrServices::fullPathName(KrServices::cmdExist("rar") ? "rar" : "unrar") << "t"; else if (type == "ace") packer << KrServices::fullPathName("unace") << "t"; else if (type == "lha") packer << KrServices::fullPathName("lha") << "t"; else if (type == "arj") packer << KrServices::fullPathName(KrServices::cmdExist("arj") ? "arj" : "unarj") << "t"; else if (type == "cpio") packer << KrServices::fullPathName("cpio") << "--only-verify-crc" << "-tvF"; else if (type == "7z") packer << KrServices::fullPathName("7z") << "-y" << "t"; else return false; if (!password.isNull()) { if (type == "zip") packer << "-P" << password; if (type == "arj") packer << QString("-g%1").arg(password); if (type == "ace" || type == "rar" || type == "7z") packer << QString("-p%1").arg(password); } // unpack the files KrLinecountingProcess proc; proc << packer << archive; if (type == "ace" && QFile("/dev/ptmx").exists()) // Don't remove, unace crashes if missing!!! proc.setStandardInputFile("/dev/ptmx"); // tell the user to wait observer->subJobStarted(i18n("Testing Archive"), count); if (count != 0) connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KRarcObserver::incrementProgress); // start the unpacking process proc.start(); // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking // it would be better to connect to started(), error() and finished() if (proc.waitForStarted()) while (proc.state() == QProcess::Running) { observer->processEvents(); if (observer->wasCancelled()) proc.kill(); } ; // busy wait - need to find something better... observer->subJobStopped(); // check the return value if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) return false; return true; // SUCCESS } bool KRarcHandler::pack(QStringList fileNames, QString type, const QString& dest, long count, QMap extraProps, KRarcObserver *observer) { // set the right packer to do the job QStringList packer; if (type == "zip") { packer << KrServices::fullPathName("zip") << "-ry"; } else if (type == "cbz") { packer << KrServices::fullPathName("zip") << "-ry"; type = "zip"; } else if (type == "tar") { packer << KrServices::fullPathName("tar") << "-cvf"; } else if (type == "tar.gz") { packer << KrServices::fullPathName("tar") << "-cvzf"; type = "tgz"; } else if (type == "tar.bz2") { packer << KrServices::fullPathName("tar") << "-cvjf"; type = "tbz"; } else if (type == "tar.lzma") { packer << KrServices::fullPathName("tar") << "--lzma" << "-cvf"; type = "tlz"; } else if (type == "tar.xz") { packer << KrServices::fullPathName("tar") << "--xz" << "-cvf"; type = "txz"; } else if (type == "rar") { packer << KrServices::fullPathName("rar") << "-r" << "a"; } else if (type == "cbr") { packer << KrServices::fullPathName("rar") << "-r" << "a"; type = "rar"; } else if (type == "lha") { packer << KrServices::fullPathName("lha") << "a"; } else if (type == "arj") { packer << KrServices::fullPathName("arj") << "-r" << "-y" << "a"; } else if (type == "7z") { packer << KrServices::fullPathName("7z") << "-y" << "a"; } else return false; QString password; if (extraProps.count("Password") > 0) { password = extraProps[ "Password" ]; if (!password.isNull()) { if (type == "zip") packer << "-P" << password; else if (type == "arj") packer << QString("-g%1").arg(password); else if (type == "ace" || type == "7z") packer << QString("-p%1").arg(password); else if (type == "rar") { if (extraProps.count("EncryptHeaders") > 0) packer << QString("-hp%1").arg(password); else packer << QString("-p%1").arg(password); } else password.clear(); } } if (extraProps.count("VolumeSize") > 0) { QString sizeStr = extraProps[ "VolumeSize" ]; KIO::filesize_t size = sizeStr.toLongLong(); if (size >= 10000) { if (type == "arj" || type == "rar") packer << QString("-v%1b").arg(sizeStr); } } if (extraProps.count("CompressionLevel") > 0) { int level = extraProps[ "CompressionLevel" ].toInt() - 1; if (level < 0) level = 0; if (level > 8) level = 8; if (type == "rar") { static const int rarLevels[] = { 0, 1, 2, 2, 3, 3, 4, 4, 5 }; packer << QString("-m%1").arg(rarLevels[ level ]); } else if (type == "arj") { static const int arjLevels[] = { 0, 4, 4, 3, 3, 2, 2, 1, 1 }; packer << QString("-m%1").arg(arjLevels[ level ]); } else if (type == "zip") { static const int zipLevels[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9 }; packer << QString("-%1").arg(zipLevels[ level ]); } else if (type == "7z") { static const int sevenZipLevels[] = { 0, 1, 2, 4, 5, 6, 7, 8, 9 }; packer << QString("-mx%1").arg(sevenZipLevels[ level ]); } } if (extraProps.count("CommandLineSwitches") > 0) packer << QString("%1").arg(extraProps[ "CommandLineSwitches" ]); // prepare to pack KrLinecountingProcess proc; proc << packer << dest; for (auto & fileName : fileNames) { proc << fileName; } // tell the user to wait observer->subJobStarted(i18n("Packing File(s)"), count); if (count != 0) connect(&proc, &KrLinecountingProcess::newOutputLines, observer, &KRarcObserver::incrementProgress); // start the packing process proc.start(); // TODO make use of asynchronous process starting. waitForStarted(int msec = 30000) is blocking // it would be better to connect to started(), error() and finished() if (proc.waitForStarted()) while (proc.state() == QProcess::Running) { observer->processEvents(); if (observer->wasCancelled()) proc.kill(); } ; // busy wait - need to find something better... observer->subJobStopped(); // check the return value if (proc.exitStatus() != QProcess::NormalExit || !checkStatus(type, proc.exitCode())) { observer->detailedError(i18n("Failed to pack %1.", dest), observer->wasCancelled() ? i18n("User cancelled.") : proc.getErrorMsg()); return false; } KConfigGroup group(krConfig, "Archives"); if (group.readEntry("Test Archives", _TestArchives) && !test(dest, type, password, observer, count)) { observer->error(i18n("Failed to pack %1.", dest)); return false; } return true; // SUCCESS } bool KRarcHandler::openWallet() { if (!wallet) { // find a suitable parent window QWidget *actWindow = QApplication::activeWindow(); if (!actWindow) actWindow = (QWidget*) QApplication::desktop(); wallet = KWallet::Wallet::openWallet(KWallet::Wallet::NetworkWallet(), actWindow->effectiveWinId()); } return (wallet != nullptr); } QString KRarcHandler::getPassword(const QString& path) { QString password; QString key = "krarc-" + path; if (!KWallet::Wallet::keyDoesNotExist(KWallet::Wallet::NetworkWallet(), KWallet::Wallet::PasswordFolder(), key)) { if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet()) && wallet != nullptr) { delete wallet; wallet = nullptr; } if (openWallet() && wallet->hasFolder(KWallet::Wallet::PasswordFolder())) { wallet->setFolder(KWallet::Wallet::PasswordFolder()); QMap map; if (wallet->readMap(key, map) == 0) { QMap::const_iterator it = map.constFind("password"); if (it != map.constEnd()) password = it.value(); } } } bool keep = true; QString user = "archive"; QPointer passDlg = new KPasswordDialog(nullptr, KPasswordDialog::ShowKeepPassword); passDlg->setPrompt(i18n("This archive is encrypted, please supply the password:") ), passDlg->setUsername(user); passDlg->setPassword(password); if (passDlg->exec() == KPasswordDialog::Accepted) { password = passDlg->password(); if (keep) { if (!KWallet::Wallet::isOpen(KWallet::Wallet::NetworkWallet()) && wallet != nullptr) { delete wallet; wallet = nullptr; } if (openWallet()) { bool ok = true; if (!wallet->hasFolder(KWallet::Wallet::PasswordFolder())) ok = wallet->createFolder(KWallet::Wallet::PasswordFolder()); if (ok) { wallet->setFolder(KWallet::Wallet::PasswordFolder()); QMap map; map.insert("login", "archive"); map.insert("password", password); wallet->writeMap(key, map); } } } delete passDlg; return password; } delete passDlg; return ""; } bool KRarcHandler::isArchive(const QUrl &url) { QString protocol = url.scheme(); if (arcProtocols.indexOf(protocol) != -1) return true; else return false; } QString KRarcHandler::getType(bool &encrypted, QString fileName, const QString& mime, bool checkEncrypted, bool fast) { QString result = detectArchive(encrypted, std::move(fileName), checkEncrypted, fast); if (result.isNull()) { // Then the type is based on the mime type return getShortTypeFromMime(mime); } return result; } bool KRarcHandler::checkStatus(const QString& type, int exitCode) { return KrArcBaseManager::checkStatus(type, exitCode); } void KRarcHandler::checkIf7zIsEncrypted(bool &encrypted, QString fileName) { Kr7zEncryptionChecker proc; // TODO incorporate all this in Kr7zEncryptionChecker proc << KrServices::fullPathName("7z") << "-y" << "t"; proc << fileName; proc.start(); proc.waitForFinished(); encrypted = proc.isEncrypted(); } diff --git a/krusader/Archive/krarchandler.h b/krusader/Archive/krarchandler.h index 7e641bd6..cdd81470 100644 --- a/krusader/Archive/krarchandler.h +++ b/krusader/Archive/krarchandler.h @@ -1,87 +1,87 @@ /***************************************************************************** * Copyright (C) 2001 Shie Erlich * * Copyright (C) 2001 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRARCHANDLER_H #define KRARCHANDLER_H // QtCore #include #include #include #include #include "../../krArc/krarcbasemanager.h" namespace KWallet { class Wallet; } class KRarcObserver : public QObject { Q_OBJECT public: ~KRarcObserver() override = default; virtual void processEvents() = 0; virtual void subJobStarted(const QString & jobTitle, int count) = 0; virtual void subJobStopped() = 0; virtual bool wasCancelled() = 0; virtual void error(const QString & error) = 0; virtual void detailedError(const QString & error, const QString & details) = 0; public slots: virtual void incrementProgress(int) = 0; }; class KRarcHandler: public QObject, public KrArcBaseManager { Q_OBJECT public: // return the number of files in the archive static long arcFileCount(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer); // unpack an archive to destination directory static bool unpack(QString archive, const QString& type, const QString& password, const QString& dest, KRarcObserver *observer ); // pack an archive to destination directory static bool pack(QStringList fileNames, QString type, const QString& dest, long count, QMap extraProps, KRarcObserver *observer ); // test an archive static bool test(const QString& archive, const QString& type, const QString& password, KRarcObserver *observer, long count = 0L ); // returns `true` if the right unpacker exist in the system static bool arcSupported(QString type); // return the list of supported packers static QStringList supportedPackers(); // returns `true` if the url is an archive (ie: tar:/home/test/file.tar.bz2) static bool isArchive(const QUrl &url); // used to determine the type of the archive QString getType(bool &encrypted, QString fileName, const QString& mime, bool checkEncrypted = true, bool fast = false); // queries the password from the user static QString getPassword(const QString& path); // detects the archive type void checkIf7zIsEncrypted(bool &, QString) Q_DECL_OVERRIDE; private: //! checks if a returned status ("exit code") of an archiving-related process is OK static bool checkStatus(const QString& type, int exitCode); static bool openWallet(); static KWallet::Wallet * wallet; }; #endif diff --git a/krusader/Archive/packjob.cpp b/krusader/Archive/packjob.cpp index 4e4f413a..8f15a2cc 100644 --- a/krusader/Archive/packjob.cpp +++ b/krusader/Archive/packjob.cpp @@ -1,169 +1,169 @@ /***************************************************************************** * Copyright (C) 2009 Csaba Karai * - * Copyright (C) 2009-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2009-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "packjob.h" #include "krarchandler.h" // QtCore #include #include #include #include #include extern KRarcHandler arcHandler; PackJob::PackJob(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames, const QString &type, const QMap &packProps) { startAbstractJobThread(new PackThread(srcUrl, destUrl, fileNames, type, packProps)); } PackJob * PackJob::createPacker(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames, const QString &type, const QMap &packProps) { return new PackJob(srcUrl, destUrl, fileNames, type, packProps); } PackThread::PackThread(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames, const QString &type, const QMap &packProps) : _sourceUrl(srcUrl), _destUrl(destUrl), _fileNames(fileNames), _type(type), _packProperties(packProps) { } void PackThread::slotStart() { QUrl newSource = downloadIfRemote(_sourceUrl, _fileNames); if (newSource.isEmpty()) return; unsigned long totalFiles = 0; countLocalFiles(newSource, _fileNames, totalFiles); QString arcFile = tempFileIfRemote(_destUrl, _type); QString arcDir = newSource.adjusted(QUrl::StripTrailingSlash).path(); setProgressTitle(i18n("Processed files")); QString save = QDir::currentPath(); QDir::setCurrent(arcDir); bool result = KRarcHandler::pack(_fileNames, _type, arcFile, totalFiles, _packProperties, observer()); QDir::setCurrent(save); if (isExited()) return; if (!result) { sendError(KIO::ERR_INTERNAL, i18n("Error while packing")); return; } if (!uploadTempFiles()) return; sendSuccess(); } TestArchiveJob::TestArchiveJob(const QUrl &srcUrl, const QStringList & fileNames) { startAbstractJobThread(new TestArchiveThread(srcUrl, fileNames)); } TestArchiveJob * TestArchiveJob::testArchives(const QUrl &srcUrl, const QStringList & fileNames) { return new TestArchiveJob(srcUrl, fileNames); } TestArchiveThread::TestArchiveThread(const QUrl &srcUrl, const QStringList & fileNames) : _sourceUrl(srcUrl), _fileNames(fileNames) { } void TestArchiveThread::slotStart() { // Gets a QUrl of the source folder, which may be remote QUrl newSource = downloadIfRemote(_sourceUrl, _fileNames); if (newSource.isEmpty()) return; for (int i = 0; i < _fileNames.count(); ++i) { QString path, type, password, arcName = _fileNames[i]; if (!getArchiveInformation(path, type, password, arcName, newSource)) return; // test the archive if (!KRarcHandler::test(path, type, password, observer(), 0)) { sendError(KIO::ERR_NO_CONTENT, i18nc("%1=archive filename", "%1, test failed.", arcName)); return; } } sendMessage(i18n("Archive tests passed.")); sendSuccess(); } UnpackJob::UnpackJob(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames) { startAbstractJobThread(new UnpackThread(srcUrl, destUrl, fileNames)); } UnpackJob * UnpackJob::createUnpacker(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames) { return new UnpackJob(srcUrl, destUrl, fileNames); } UnpackThread::UnpackThread(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames) : _sourceUrl(srcUrl), _destUrl(destUrl), _fileNames(fileNames) { } void UnpackThread::slotStart() { // Gets a QUrl of the source folder, which may be remote QUrl newSource = downloadIfRemote(_sourceUrl, _fileNames); if (newSource.isEmpty()) return; QString localDest = tempDirIfRemote(_destUrl); for (int i = 0; i < _fileNames.count(); ++i) { QString path, type, password, arcName = _fileNames[i]; if (!getArchiveInformation(path, type, password, arcName, newSource)) return; setProgressTitle(i18n("Processed files")); // unpack the files bool result = KRarcHandler::unpack(path, type, password, localDest, observer()); if (isExited()) return; if (!result) { sendError(KIO::ERR_INTERNAL, i18n("Error while unpacking")); return; } } if (!uploadTempFiles()) return; sendSuccess(); } diff --git a/krusader/Archive/packjob.h b/krusader/Archive/packjob.h index 4c40e4be..358dfa4f 100644 --- a/krusader/Archive/packjob.h +++ b/krusader/Archive/packjob.h @@ -1,125 +1,125 @@ /***************************************************************************** * Copyright (C) 2009 Csaba Karai * - * Copyright (C) 2009-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2009-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef PACKJOB_H #define PACKJOB_H // QtCore #include #include "abstractthreadedjob.h" class PackThread; class TestArchiveThread; class UnpackThread; class PackJob : public AbstractThreadedJob { Q_OBJECT private: PackJob(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames, const QString &type, const QMap &packProps); public: static PackJob * createPacker(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames, const QString &type, const QMap &packProps); }; class PackThread : public AbstractJobThread { Q_OBJECT public: PackThread(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames, const QString &type, const QMap &packProps); ~PackThread() override = default; protected slots: void slotStart() Q_DECL_OVERRIDE; private: QUrl _sourceUrl; QUrl _destUrl; QStringList _fileNames; QString _type; QMap _packProperties; }; class TestArchiveJob : public AbstractThreadedJob { Q_OBJECT private: TestArchiveJob(const QUrl &srcUrl, const QStringList & fileNames); public: static TestArchiveJob * testArchives(const QUrl &srcUrl, const QStringList & fileNames); }; class TestArchiveThread : public AbstractJobThread { Q_OBJECT public: TestArchiveThread(const QUrl &srcUrl, const QStringList & fileNames); ~TestArchiveThread() override = default; protected slots: void slotStart() Q_DECL_OVERRIDE; private: QUrl _sourceUrl; QStringList _fileNames; }; class UnpackJob : public AbstractThreadedJob { Q_OBJECT private: UnpackJob(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames); public: static UnpackJob * createUnpacker(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames); }; class UnpackThread : public AbstractJobThread { Q_OBJECT public: UnpackThread(const QUrl &srcUrl, const QUrl &destUrl, const QStringList & fileNames); ~UnpackThread() override = default; protected slots: void slotStart() Q_DECL_OVERRIDE; private: QUrl _sourceUrl; QUrl _destUrl; QStringList _fileNames; }; #endif // __PACK_JOB_H__ diff --git a/krusader/BookMan/kraddbookmarkdlg.cpp b/krusader/BookMan/kraddbookmarkdlg.cpp index 21b69c60..7cc6f902 100644 --- a/krusader/BookMan/kraddbookmarkdlg.cpp +++ b/krusader/BookMan/kraddbookmarkdlg.cpp @@ -1,186 +1,186 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "kraddbookmarkdlg.h" #include "../krglobal.h" #include "../icon.h" #include "krbookmarkhandler.h" // QtWidgets #include #include #include #include #include #include #include KrAddBookmarkDlg::KrAddBookmarkDlg(QWidget *parent, const QUrl& url): QDialog(parent) { setWindowModality(Qt::WindowModal); setWindowTitle(i18n("Add Bookmark")); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); auto *layout = new QGridLayout; // expanding // name and url QLabel *lb1 = new QLabel(i18n("Name:"), this); _name = new KLineEdit(this); _name->setText(url.toDisplayString()); // default name is the url _name->selectAll(); // make the text selected layout->addWidget(lb1, 0, 0); layout->addWidget(_name, 0, 1); QLabel *lb2 = new QLabel(i18n("URL:"), this); _url = new KLineEdit(this); layout->addWidget(lb2, 1, 0); layout->addWidget(_url, 1, 1); _url->setText(url.toDisplayString()); // set the url in the field // create in linedit and button QLabel *lb3 = new QLabel(i18n("Create in:"), this); _folder = new KLineEdit(this); layout->addWidget(lb3, 2, 0); layout->addWidget(_folder, 2, 1); _folder->setReadOnly(true); _createInBtn = new QToolButton(this); _createInBtn->setIcon(Icon("go-down")); _createInBtn->setCheckable(true); connect(_createInBtn, &QToolButton::toggled, this, &KrAddBookmarkDlg::toggleCreateIn); layout->addWidget(_createInBtn, 2, 2); mainLayout->addLayout(layout); detailsWidget = createInWidget(); detailsWidget->setVisible(false); mainLayout->addWidget(detailsWidget); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); newFolderButton = new QPushButton(i18n("New Folder")); buttonBox->addButton(newFolderButton, QDialogButtonBox::ActionRole); newFolderButton->setVisible(false);// hide it until _createIn is shown connect(newFolderButton, &QPushButton::clicked, this, &KrAddBookmarkDlg::newFolder); connect(buttonBox, &QDialogButtonBox::accepted, this, &KrAddBookmarkDlg::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &KrAddBookmarkDlg::reject); _name->setFocus(); resize(sizeHint().width() * 2, sizeHint().height()); } void KrAddBookmarkDlg::toggleCreateIn(bool show) { _createInBtn->setIcon(Icon(show ? "go-up" : "go-down")); newFolderButton->setVisible(show); detailsWidget->setVisible(show); } // creates the widget that lets you decide where to put the new bookmark QWidget *KrAddBookmarkDlg::createInWidget() { _createIn = new KrTreeWidget(this); _createIn->setHeaderLabel(i18n("Folders")); _createIn->header()->hide(); _createIn->setRootIsDecorated(true); _createIn->setAlternatingRowColors(false); // disable alternate coloring auto *item = new QTreeWidgetItem(_createIn); item->setText(0, i18n("Bookmarks")); _createIn->expandItem(item); item->setSelected(true); _xr[item] = krBookMan->_root; populateCreateInWidget(krBookMan->_root, item); _createIn->setCurrentItem(item); slotSelectionChanged(); connect(_createIn, &KrTreeWidget::itemSelectionChanged, this, &KrAddBookmarkDlg::slotSelectionChanged); return _createIn; } void KrAddBookmarkDlg::slotSelectionChanged() { QList items = _createIn->selectedItems(); if (items.count() > 0) { _folder->setText(_xr[ items[ 0 ] ]->text()); } } void KrAddBookmarkDlg::populateCreateInWidget(KrBookmark *root, QTreeWidgetItem *parent) { QListIterator it(root->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) { auto *item = new QTreeWidgetItem(parent); item->setText(0, bm->text()); item->treeWidget()->expandItem(item); _xr[item] = bm; populateCreateInWidget(bm, item); } } } void KrAddBookmarkDlg::newFolder() { // get the name QString newFolder = QInputDialog::getText(this, i18n("New Folder"), i18n("Folder name:")); if (newFolder.isEmpty()) { return; } QList items = _createIn->selectedItems(); if (items.count() == 0) return; // add to the list in bookman KrBookmark *bm = new KrBookmark(newFolder); krBookMan->addBookmark(bm, _xr[ items[ 0 ]]); // fix the gui auto *item = new QTreeWidgetItem(items[ 0 ]); item->setText(0, bm->text()); _xr[item] = bm; _createIn->setCurrentItem(item); item->setSelected(true); } KrBookmark * KrAddBookmarkDlg::folder() const { QList items = _createIn->selectedItems(); if (items.count() == 0) return nullptr; return _xr[ items[ 0 ] ]; } diff --git a/krusader/BookMan/kraddbookmarkdlg.h b/krusader/BookMan/kraddbookmarkdlg.h index 730eebe9..3d730650 100644 --- a/krusader/BookMan/kraddbookmarkdlg.h +++ b/krusader/BookMan/kraddbookmarkdlg.h @@ -1,70 +1,70 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRADDBOOKMARKDLG_H #define KRADDBOOKMARKDLG_H #include "krbookmark.h" #include "../GUI/krtreewidget.h" // QtCore #include #include // QtWidgets #include #include #include class KrAddBookmarkDlg: public QDialog { Q_OBJECT public: explicit KrAddBookmarkDlg(QWidget *parent, const QUrl& url = QUrl()); QUrl url() const { return QUrl::fromUserInput(_url->text(), QString(), QUrl::AssumeLocalFile); } QString name() const { return _name->text(); } KrBookmark *folder() const; protected: QWidget *createInWidget(); void populateCreateInWidget(KrBookmark *root, QTreeWidgetItem *parent); protected slots: void toggleCreateIn(bool show); void slotSelectionChanged(); void newFolder(); private: KLineEdit *_name; KLineEdit *_url; KLineEdit *_folder; KrTreeWidget *_createIn; QMap _xr; QToolButton *_createInBtn; QPushButton *newFolderButton; QWidget *detailsWidget; }; #endif // KRADDBOOKMARKDLG_H diff --git a/krusader/BookMan/krbookmark.cpp b/krusader/BookMan/krbookmark.cpp index 5575927f..5a77e38c 100644 --- a/krusader/BookMan/krbookmark.cpp +++ b/krusader/BookMan/krbookmark.cpp @@ -1,152 +1,152 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krbookmark.h" #include "../krglobal.h" #include "../icon.h" #include "../Archive/krarchandler.h" #include "../FileSystem/krtrashhandler.h" #include "../Panel/listpanelactions.h" #include #include #include #define BM_NAME(X) (QString("Bookmark:")+X) static const char* NAME_TRASH = I18N_NOOP("Trash bin"); static const char* NAME_VIRTUAL = I18N_NOOP("Virtual Filesystem"); static const char* NAME_LAN = I18N_NOOP("Local Network"); KrBookmark::KrBookmark(const QString& name, QUrl url, KActionCollection *parent, const QString& iconName, const QString& actionName) : QAction(parent), _url(std::move(url)), _iconName(iconName), _folder(false), _separator(false), _autoDelete(true) { QString actName = actionName.isNull() ? BM_NAME(name) : BM_NAME(actionName); setText(name); parent->addAction(actName, this); connect(this, &KrBookmark::triggered, this, &KrBookmark::activatedProxy); setIconName(iconName); } KrBookmark::KrBookmark(const QString& name, const QString& iconName) : QAction(Icon(iconName), name, nullptr), _iconName(iconName), _folder(true), _separator(false), _autoDelete(false) { setIcon(Icon(iconName == "" ? "folder" : iconName)); } KrBookmark::~KrBookmark() { if (_autoDelete) { QListIterator it(_children); while (it.hasNext()) delete it.next(); _children.clear(); } } void KrBookmark::setIconName(const QString& iconName) { if (!iconName.isEmpty()) { setIcon(Icon(iconName)); } else if (_url.isLocalFile()) { setIcon(Icon("folder")); } else if (KRarcHandler::isArchive(_url)) { setIcon(Icon("application-x-tar")); } else { setIcon(Icon("folder-html")); } } KrBookmark * KrBookmark::getExistingBookmark(const QString& actionName, KActionCollection *collection) { return dynamic_cast(collection->action(BM_NAME(actionName))); } KrBookmark * KrBookmark::trash(KActionCollection *collection) { KrBookmark *bm = getExistingBookmark(i18n(NAME_TRASH), collection); if (!bm) bm = new KrBookmark(i18n(NAME_TRASH), QUrl("trash:/"), collection); bm->setIcon(Icon(KrTrashHandler::trashIconName())); return bm; } KrBookmark * KrBookmark::virt(KActionCollection *collection) { KrBookmark *bm = getExistingBookmark(i18n(NAME_VIRTUAL), collection); if (!bm) { bm = new KrBookmark(i18n(NAME_VIRTUAL), QUrl("virt:/"), collection); bm->setIcon(Icon("document-open-remote")); } return bm; } KrBookmark * KrBookmark::lan(KActionCollection *collection) { KrBookmark *bm = getExistingBookmark(i18n(NAME_LAN), collection); if (!bm) { bm = new KrBookmark(i18n(NAME_LAN), QUrl("remote:/"), collection); bm->setIcon(Icon("network-workgroup")); } return bm; } QAction * KrBookmark::jumpBackAction(KActionCollection *collection, bool isSetter, ListPanelActions *sourceActions) { auto actionName = isSetter ? QString("setJumpBack") : QString("jumpBack"); auto action = collection->action(actionName); if (action) { return action; } if (!sourceActions) { return nullptr; } // copy essential part of source action auto sourceAction = isSetter ? sourceActions->actSetJumpBack : sourceActions->actJumpBack; action = new QAction(sourceAction->icon(), sourceAction->text(), sourceAction); action->setShortcut(sourceAction->shortcut()); action->setShortcutContext(Qt::WidgetShortcut); connect(action, &QAction::triggered, sourceAction, &QAction::trigger); // ensure there are no accelerator keys coming from another menu action->setText(KLocalizedString::removeAcceleratorMarker(action->text())); collection->addAction(actionName, action); return action; } KrBookmark * KrBookmark::separator() { KrBookmark *bm = new KrBookmark(""); bm->_separator = true; bm->_folder = false; return bm; } void KrBookmark::activatedProxy() { emit activated(url()); } diff --git a/krusader/BookMan/krbookmark.h b/krusader/BookMan/krbookmark.h index 561c68f2..3ee7145e 100644 --- a/krusader/BookMan/krbookmark.h +++ b/krusader/BookMan/krbookmark.h @@ -1,93 +1,93 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRBOOKMARK_H #define KRBOOKMARK_H // QtCore #include #include // QtWidgets #include class KActionCollection; class ListPanelActions; class KrBookmark: public QAction { Q_OBJECT public: KrBookmark(const QString& name, QUrl url, KActionCollection *parent, const QString& iconName = "", const QString& actionName = QString()); explicit KrBookmark(const QString& name, const QString& iconName = ""); // creates a folder ~KrBookmark() override; // text() and setText() to change the name of the bookmark // icon() and setIcon() to change icons void setIconName(const QString& iconName); inline const QString& iconName() const { return _iconName; } inline const QUrl &url() const { return _url; } inline void setURL(const QUrl &url) { _url = url; } inline bool isFolder() const { return _folder; } inline bool isSeparator() const { return _separator; } QList& children() { return _children; } static KrBookmark * getExistingBookmark(const QString& actionName, KActionCollection *collection); // ----- special bookmarks static KrBookmark * trash(KActionCollection *collection); static KrBookmark * virt(KActionCollection *collection); static KrBookmark * lan(KActionCollection *collection); static QAction * jumpBackAction(KActionCollection *collection, bool isSetter = false, ListPanelActions *sourceActions = nullptr); static KrBookmark * separator(); signals: void activated(const QUrl &url); protected slots: void activatedProxy(); private: QUrl _url; QString _iconName; bool _folder; bool _separator; bool _autoDelete; QList _children; }; #endif // KRBOOKMARK_H diff --git a/krusader/BookMan/krbookmarkbutton.cpp b/krusader/BookMan/krbookmarkbutton.cpp index d0fdd829..183a1763 100644 --- a/krusader/BookMan/krbookmarkbutton.cpp +++ b/krusader/BookMan/krbookmarkbutton.cpp @@ -1,63 +1,63 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krbookmarkbutton.h" #include "krbookmarkhandler.h" #include "../krglobal.h" #include "../icon.h" // QtGui #include // QtWidgets #include #include #include KrBookmarkButton::KrBookmarkButton(QWidget *parent): QToolButton(parent) { setAutoRaise(true); setIcon(Icon("bookmarks")); setText(i18n("BookMan II")); setToolTip(i18n("BookMan II")); setPopupMode(QToolButton::InstantPopup); setAcceptDrops(false); acmBookmarks = new KActionMenu(Icon("bookmarks"), i18n("Bookmarks"), this); acmBookmarks->setDelayed(false); setMenu(acmBookmarks->menu()); connect(acmBookmarks->menu(), &QMenu::aboutToShow, this, &KrBookmarkButton::populate); connect(acmBookmarks->menu(), &QMenu::aboutToShow, this, &KrBookmarkButton::aboutToShow); } void KrBookmarkButton::populate() { krBookMan->populate(static_cast(menu())); } void KrBookmarkButton::showMenu() { populate(); menu()->exec(mapToGlobal(QPoint(0, height()))); } diff --git a/krusader/BookMan/krbookmarkbutton.h b/krusader/BookMan/krbookmarkbutton.h index 66aa3f0e..5d9c507b 100644 --- a/krusader/BookMan/krbookmarkbutton.h +++ b/krusader/BookMan/krbookmarkbutton.h @@ -1,48 +1,48 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRBOOKMARKBUTTON_H #define KRBOOKMARKBUTTON_H // QtWidgets #include #include class KrBookmarkButton: public QToolButton { Q_OBJECT public: explicit KrBookmarkButton(QWidget *parent); void showMenu(); signals: void openUrl(const QUrl &url); void aboutToShow(); protected slots: void populate(); private: KActionMenu *acmBookmarks; }; #endif // KRBOOKMARK_BUTTON_H diff --git a/krusader/BookMan/krbookmarkhandler.cpp b/krusader/BookMan/krbookmarkhandler.cpp index 5c7d16e9..4168ee03 100644 --- a/krusader/BookMan/krbookmarkhandler.cpp +++ b/krusader/BookMan/krbookmarkhandler.cpp @@ -1,881 +1,881 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krbookmarkhandler.h" #include "kraddbookmarkdlg.h" #include "../krglobal.h" #include "../icon.h" #include "../krslots.h" #include "../kractions.h" #include "../krmainwindow.h" #include "../Dialogs/popularurls.h" #include "../FileSystem/filesystem.h" #include "../Panel/krpanel.h" #include "../Panel/listpanelactions.h" // QtCore #include #include #include #include #include #include // QtGui #include #include #include #include #include #include #include #include #define SPECIAL_BOOKMARKS true // ------------------------ for internal use #define BOOKMARKS_FILE "krusader/krbookmarks.xml" #define CONNECT_BM(X) { disconnect(X, SIGNAL(activated(QUrl)), 0, 0); connect(X, SIGNAL(activated(QUrl)), this, SLOT(slotActivated(QUrl))); } KrBookmarkHandler::KrBookmarkHandler(KrMainWindow *mainWindow) : QObject(mainWindow->widget()), _mainWindow(mainWindow), _middleClick(false), _mainBookmarkPopup(nullptr), _quickSearchAction(nullptr), _quickSearchBar(nullptr), _quickSearchMenu(nullptr) { // create our own action collection and make the shortcuts apply only to parent _privateCollection = new KActionCollection(this); _collection = _mainWindow->actions(); // create _root: father of all bookmarks. it is a dummy bookmark and never shown _root = new KrBookmark(i18n("Bookmarks")); _root->setParent(this); // load bookmarks importFromFile(); // create bookmark manager QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; manager = KBookmarkManager::managerForFile(filename, QStringLiteral("krusader")); connect(manager, &KBookmarkManager::changed, this, &KrBookmarkHandler::bookmarksChanged); // create the quick search bar and action _quickSearchAction = new QWidgetAction(this); _quickSearchBar = new QLineEdit(); _quickSearchBar->setPlaceholderText(i18n("Type to search...")); _quickSearchAction->setDefaultWidget(_quickSearchBar); // ownership of the bar is transferred to the action _quickSearchAction->setEnabled(false); _setQuickSearchText(""); // fill a dummy menu to properly init actions (allows toolbar bookmark buttons to work properly) auto menu = new QMenu(mainWindow->widget()); populate(menu); menu->deleteLater(); } KrBookmarkHandler::~KrBookmarkHandler() { delete manager; delete _privateCollection; } void KrBookmarkHandler::bookmarkCurrent(QUrl url) { QPointer dlg = new KrAddBookmarkDlg(_mainWindow->widget(), std::move(url)); if (dlg->exec() == QDialog::Accepted) { KrBookmark *bm = new KrBookmark(dlg->name(), dlg->url(), _collection); addBookmark(bm, dlg->folder()); } delete dlg; } void KrBookmarkHandler::addBookmark(KrBookmark *bm, KrBookmark *folder) { if (folder == nullptr) folder = _root; // add to the list (bottom) folder->children().append(bm); exportToFile(); } void KrBookmarkHandler::deleteBookmark(KrBookmark *bm) { if (bm->isFolder()) clearBookmarks(bm); // remove the child bookmarks removeReferences(_root, bm); foreach(QWidget *w, bm->associatedWidgets()) w->removeAction(bm); delete bm; exportToFile(); } void KrBookmarkHandler::removeReferences(KrBookmark *root, KrBookmark *bmToRemove) { int index = root->children().indexOf(bmToRemove); if (index >= 0) root->children().removeAt(index); QListIterator it(root->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) removeReferences(bm, bmToRemove); } } void KrBookmarkHandler::exportToFileBookmark(QDomDocument &doc, QDomElement &where, KrBookmark *bm) { if (bm->isSeparator()) { QDomElement bookmark = doc.createElement("separator"); where.appendChild(bookmark); } else { QDomElement bookmark = doc.createElement("bookmark"); // url bookmark.setAttribute("href", bm->url().toDisplayString()); // icon bookmark.setAttribute("icon", bm->iconName()); // title QDomElement title = doc.createElement("title"); title.appendChild(doc.createTextNode(bm->text())); bookmark.appendChild(title); where.appendChild(bookmark); } } void KrBookmarkHandler::exportToFileFolder(QDomDocument &doc, QDomElement &parent, KrBookmark *folder) { QListIterator it(folder->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) { QDomElement newFolder = doc.createElement("folder"); newFolder.setAttribute("icon", bm->iconName()); parent.appendChild(newFolder); QDomElement title = doc.createElement("title"); title.appendChild(doc.createTextNode(bm->text())); newFolder.appendChild(title); exportToFileFolder(doc, newFolder, bm); } else { exportToFileBookmark(doc, parent, bm); } } } // export to file using the xbel standard // // // Developer Web Site // // Title of this folder // KDE Web Site // // My own bookmarks // KOffice Web Site // // KDevelop Web Site // // // void KrBookmarkHandler::exportToFile() { QDomDocument doc("xbel"); QDomElement root = doc.createElement("xbel"); doc.appendChild(root); exportToFileFolder(doc, root, _root); if (!doc.firstChild().isProcessingInstruction()) { // adding: if not already present QDomProcessingInstruction instr = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\" "); doc.insertBefore(instr, doc.firstChild()); } QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; QFile file(filename); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream.setCodec("UTF-8"); stream << doc.toString(); file.close(); } else { KMessageBox::error(_mainWindow->widget(), i18n("Unable to write to %1", filename), i18n("Error")); } } bool KrBookmarkHandler::importFromFileBookmark(QDomElement &e, KrBookmark *parent, const QString& path, QString *errorMsg) { QString url, name, iconName; // verify tag if (e.tagName() != "bookmark") { *errorMsg = i18n("%1 instead of %2", e.tagName(), QLatin1String("bookmark")); return false; } // verify href if (!e.hasAttribute("href")) { *errorMsg = i18n("missing tag %1", QLatin1String("href")); return false; } else url = e.attribute("href"); // verify title QDomElement te = e.firstChild().toElement(); if (te.tagName() != "title") { *errorMsg = i18n("missing tag %1", QLatin1String("title")); return false; } else name = te.text(); // do we have an icon? if (e.hasAttribute("icon")) { iconName = e.attribute("icon"); } // ok: got name and url, let's add a bookmark KrBookmark *bm = KrBookmark::getExistingBookmark(path + name, _collection); if (!bm) { bm = new KrBookmark(name, QUrl(url), _collection, iconName, path + name); } else { bm->setURL(QUrl(url)); bm->setIconName(iconName); } parent->children().append(bm); return true; } bool KrBookmarkHandler::importFromFileFolder(QDomNode &first, KrBookmark *parent, const QString& path, QString *errorMsg) { QString name; QDomNode n = first; while (!n.isNull()) { QDomElement e = n.toElement(); if (e.tagName() == "bookmark") { if (!importFromFileBookmark(e, parent, path, errorMsg)) return false; } else if (e.tagName() == "folder") { QString iconName = ""; if (e.hasAttribute("icon")) iconName = e.attribute("icon"); // the title is the first child of the folder QDomElement tmp = e.firstChild().toElement(); if (tmp.tagName() != "title") { *errorMsg = i18n("missing tag %1", QLatin1String("title")); return false; } else name = tmp.text(); KrBookmark *folder = new KrBookmark(name, iconName); parent->children().append(folder); QDomNode nextOne = tmp.nextSibling(); if (!importFromFileFolder(nextOne, folder, path + name + '/', errorMsg)) return false; } else if (e.tagName() == "separator") { parent->children().append(KrBookmark::separator()); } n = n.nextSibling(); } return true; } void KrBookmarkHandler::importFromFile() { clearBookmarks(_root, false); QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; QFile file(filename); if (!file.open(QIODevice::ReadOnly)) return; // no bookmarks file QString errorMsg; QDomNode n; QDomElement e; QDomDocument doc("xbel"); if (!doc.setContent(&file, &errorMsg)) { goto BM_ERROR; } // iterate through the document: first child should be "xbel" (skip all until we find it) n = doc.firstChild(); while (!n.isNull() && n.toElement().tagName() != "xbel") n = n.nextSibling(); if (n.isNull() || n.toElement().tagName() != "xbel") { errorMsg = i18n("%1 does not seem to be a valid bookmarks file", filename); goto BM_ERROR; } else n = n.firstChild(); // skip the xbel part importFromFileFolder(n, _root, "", &errorMsg); goto BM_SUCCESS; BM_ERROR: KMessageBox::error(_mainWindow->widget(), i18n("Error reading bookmarks file: %1", errorMsg), i18n("Error")); BM_SUCCESS: file.close(); } void KrBookmarkHandler::_setQuickSearchText(const QString &text) { bool isEmptyQuickSearchBarVisible = KConfigGroup(krConfig, "Look&Feel").readEntry("Always show search bar", true); _quickSearchBar->setText(text); auto length = text.length(); bool isVisible = isEmptyQuickSearchBarVisible || length > 0; _quickSearchAction->setVisible(isVisible); _quickSearchBar->setVisible(isVisible); if (length == 0) { qDebug() << "Bookmark search: reset"; _resetActionTextAndHighlighting(); } else { qDebug() << "Bookmark search: query =" << text; } } QString KrBookmarkHandler::_quickSearchText() const { return _quickSearchBar->text(); } void KrBookmarkHandler::_highlightAction(QAction *action, bool isMatched) { auto font = action->font(); font.setBold(isMatched); action->setFont(font); } void KrBookmarkHandler::populate(QMenu *menu) { // removing action from previous menu is necessary // otherwise it won't be displayed in the currently populating menu if (_mainBookmarkPopup) { _mainBookmarkPopup->removeAction(_quickSearchAction); } _mainBookmarkPopup = menu; menu->clear(); _specialBookmarks.clear(); buildMenu(_root, menu); } void KrBookmarkHandler::buildMenu(KrBookmark *parent, QMenu *menu, int depth) { // add search bar widget to the top of the menu if (depth == 0) { menu->addAction(_quickSearchAction); } // run the loop twice, in order to put the folders on top. stupid but easy :-) // note: this code drops the separators put there by the user QListIterator it(parent->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (!bm->isFolder()) continue; auto *newMenu = new QMenu(menu); newMenu->setIcon(Icon(bm->iconName())); newMenu->setTitle(bm->text()); QAction *menuAction = menu->addMenu(newMenu); QVariant v; v.setValue(bm); menuAction->setData(v); buildMenu(bm, newMenu, depth + 1); } it.toFront(); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) continue; if (bm->isSeparator()) { menu->addSeparator(); continue; } menu->addAction(bm); CONNECT_BM(bm); } if (depth == 0) { KConfigGroup group(krConfig, "Private"); bool hasPopularURLs = group.readEntry("BM Popular URLs", true); bool hasTrash = group.readEntry("BM Trash", true); bool hasLan = group.readEntry("BM Lan", true); bool hasVirtualFS = group.readEntry("BM Virtual FS", true); bool hasJumpback = group.readEntry("BM Jumpback", true); if (hasPopularURLs) { menu->addSeparator(); // add the popular links submenu auto *newMenu = new QMenu(menu); newMenu->setTitle(i18n("Popular URLs")); newMenu->setIcon(Icon("folder-bookmark")); QAction *bmfAct = menu->addMenu(newMenu); _specialBookmarks.append(bmfAct); // add the top 15 urls #define MAX 15 QList list = _mainWindow->popularUrls()->getMostPopularUrls(MAX); QList::Iterator it; for (it = list.begin(); it != list.end(); ++it) { QString name; if ((*it).isLocalFile()) name = (*it).path(); else name = (*it).toDisplayString(); // note: these bookmark are put into the private collection // as to not spam the general collection KrBookmark *bm = KrBookmark::getExistingBookmark(name, _privateCollection); if (!bm) bm = new KrBookmark(name, *it, _privateCollection); newMenu->addAction(bm); CONNECT_BM(bm); } newMenu->addSeparator(); newMenu->addAction(krPopularUrls); newMenu->installEventFilter(this); } // do we need to add special bookmarks? if (SPECIAL_BOOKMARKS) { if (hasTrash || hasLan || hasVirtualFS) menu->addSeparator(); KrBookmark *bm; // note: special bookmarks are not kept inside the _bookmarks list and added ad-hoc if (hasTrash) { bm = KrBookmark::trash(_collection); menu->addAction(bm); _specialBookmarks.append(bm); CONNECT_BM(bm); } if (hasLan) { bm = KrBookmark::lan(_collection); menu->addAction(bm); _specialBookmarks.append(bm); CONNECT_BM(bm); } if (hasVirtualFS) { bm = KrBookmark::virt(_collection); menu->addAction(bm); _specialBookmarks.append(bm); CONNECT_BM(bm); } if (hasJumpback) { menu->addSeparator(); ListPanelActions *actions = _mainWindow->listPanelActions(); auto slotTriggered = [=] { if (_mainBookmarkPopup && !_mainBookmarkPopup->isHidden()) { _mainBookmarkPopup->close(); } }; auto addJumpBackAction = [=](bool isSetter) { auto action = KrBookmark::jumpBackAction(_privateCollection, isSetter, actions); if (action) { menu->addAction(action); _specialBookmarks.append(action); // disconnecting from this as a receiver is important: // we don't want to break connections established by KrBookmark::jumpBackAction disconnect(action, &QAction::triggered, this, nullptr); connect(action, &QAction::triggered, this, slotTriggered); } }; addJumpBackAction(true); addJumpBackAction(false); } } menu->addSeparator(); menu->addAction(KrActions::actAddBookmark); _specialBookmarks.append(KrActions::actAddBookmark); QAction *bmAct = menu->addAction(Icon("bookmarks"), i18n("Manage Bookmarks"), manager, SLOT(slotEditBookmarks())); _specialBookmarks.append(bmAct); // make sure the menu is connected to us disconnect(menu, SIGNAL(triggered(QAction*)), nullptr, nullptr); } menu->installEventFilter(this); } void KrBookmarkHandler::clearBookmarks(KrBookmark *root, bool removeBookmarks) { for (auto it = root->children().begin(); it != root->children().end(); it = root->children().erase(it)) { KrBookmark *bm = *it; if (bm->isFolder()) { clearBookmarks(bm, removeBookmarks); delete bm; } else if (bm->isSeparator()) { delete bm; } else if (removeBookmarks) { foreach (QWidget *w, bm->associatedWidgets()) { w->removeAction(bm); } delete bm; } } } void KrBookmarkHandler::bookmarksChanged(const QString&, const QString&) { importFromFile(); } bool KrBookmarkHandler::eventFilter(QObject *obj, QEvent *ev) { auto eventType = ev->type(); auto *menu = qobject_cast(obj); if (eventType == QEvent::Show && menu) { _setQuickSearchText(""); _quickSearchMenu = menu; qDebug() << "Bookmark search: menu" << menu << "is shown"; return QObject::eventFilter(obj, ev); } if (eventType == QEvent::Close && menu && _quickSearchMenu) { if (_quickSearchMenu == menu) { qDebug() << "Bookmark search: stopped on menu" << menu; _setQuickSearchText(""); _quickSearchMenu = nullptr; } else { qDebug() << "Bookmark search: active action =" << _quickSearchMenu->activeAction(); // fix automatic deactivation of current action due to spurious close event from submenu auto quickSearchMenu = _quickSearchMenu; auto activeAction = _quickSearchMenu->activeAction(); QTimer::singleShot(0, this, [=]() { qDebug() << "Bookmark search: active action =" << quickSearchMenu->activeAction(); if (!quickSearchMenu->activeAction() && activeAction) { quickSearchMenu->setActiveAction(activeAction); qDebug() << "Bookmark search: restored active action =" << quickSearchMenu->activeAction(); } }); } return QObject::eventFilter(obj, ev); } // Having it occur on keypress is consistent with other shortcuts, // such as Ctrl+W and accelerator keys if (eventType == QEvent::KeyPress && menu) { auto *kev = dynamic_cast(ev); QList acts = menu->actions(); bool quickSearchStarted = false; bool searchInSpecialItems = KConfigGroup(krConfig, "Look&Feel").readEntry("Search in special items", false); if (kev->key() == Qt::Key_Left && kev->modifiers() == Qt::NoModifier) { menu->close(); return true; } if ((kev->modifiers() != Qt::ShiftModifier && kev->modifiers() != Qt::NoModifier) || kev->text().isEmpty() || kev->key() == Qt::Key_Delete || kev->key() == Qt::Key_Return || kev->key() == Qt::Key_Escape) { return QObject::eventFilter(obj, ev); } // update quick search text if (kev->key() == Qt::Key_Backspace) { auto newSearchText = _quickSearchText(); newSearchText.chop(1); _setQuickSearchText(newSearchText); if (_quickSearchText().length() == 0) { return QObject::eventFilter(obj, ev); } } else { quickSearchStarted = _quickSearchText().length() == 0; _setQuickSearchText(_quickSearchText().append(kev->text())); } if (quickSearchStarted) { _quickSearchMenu = menu; qDebug() << "Bookmark search: started on menu" << menu; } // match actions QAction *matchedAction = nullptr; int nMatches = 0; const Qt::CaseSensitivity matchCase = _quickSearchText() == _quickSearchText().toLower() ? Qt::CaseInsensitive : Qt::CaseSensitive; for (auto act : acts) { if (act->isSeparator() || act->text().isEmpty()) { continue; } if (!searchInSpecialItems && _specialBookmarks.contains(act)) { continue; } if (quickSearchStarted) { // if the first key press is an accelerator key, let the accelerator handler process this event if (act->text().contains('&' + kev->text(), Qt::CaseInsensitive)) { qDebug() << "Bookmark search: hit accelerator key of" << act; _setQuickSearchText(""); return QObject::eventFilter(obj, ev); } // strip accelerator keys from actions so they don't interfere with the search key press events auto text = act->text(); _quickSearchOriginalActionTitles.insert(act, text); act->setText(KLocalizedString::removeAcceleratorMarker(text)); } // match prefix of the action text to the query if (act->text().left(_quickSearchText().length()).compare(_quickSearchText(), matchCase) == 0) { _highlightAction(act); if (!matchedAction || matchedAction->menu()) { // Can't highlight menus (see comment below), hopefully pick something we can matchedAction = act; } nMatches++; } else { _highlightAction(act, false); } } if (matchedAction) { qDebug() << "Bookmark search: primary match =" << matchedAction->text() << ", number of matches =" << nMatches; } else { qDebug() << "Bookmark search: no matches"; } // trigger the matched menu item or set an active item accordingly if (nMatches == 1) { _setQuickSearchText(""); if ((bool) matchedAction->menu()) { menu->setActiveAction(matchedAction); } else { matchedAction->activate(QAction::Trigger); } } else if (nMatches > 1) { // Because of a bug submenus cannot be highlighted // https://bugreports.qt.io/browse/QTBUG-939 if (!matchedAction->menu()) { menu->setActiveAction(matchedAction); } else { menu->setActiveAction(nullptr); } } else { menu->setActiveAction(nullptr); } return true; } if (eventType == QEvent::MouseButtonRelease) { switch (dynamic_cast(ev)->button()) { case Qt::RightButton: _middleClick = false; if (obj->inherits("QMenu")) { auto *menu = dynamic_cast(obj); QAction *act = menu->actionAt(dynamic_cast(ev)->pos()); if (obj == _mainBookmarkPopup && _specialBookmarks.contains(act)) { rightClickOnSpecialBookmark(); return true; } auto *bm = qobject_cast(act); if (bm != nullptr) { rightClicked(menu, bm); return true; } else if (act && act->data().canConvert()) { auto *bm = act->data().value(); rightClicked(menu, bm); } } break; case Qt::LeftButton: _middleClick = false; break; case Qt::MidButton: _middleClick = true; break; default: break; } } return QObject::eventFilter(obj, ev); } void KrBookmarkHandler::_resetActionTextAndHighlighting() { for (QHash::const_iterator i = _quickSearchOriginalActionTitles.constBegin(); i != _quickSearchOriginalActionTitles.constEnd(); ++i) { QAction *action = i.key(); action->setText(i.value()); _highlightAction(action, false); } _quickSearchOriginalActionTitles.clear(); } #define POPULAR_URLS_ID 100100 #define TRASH_ID 100101 #define LAN_ID 100103 #define VIRTUAL_FS_ID 100102 #define JUMP_BACK_ID 100104 void KrBookmarkHandler::rightClickOnSpecialBookmark() { KConfigGroup group(krConfig, "Private"); bool hasPopularURLs = group.readEntry("BM Popular URLs", true); bool hasTrash = group.readEntry("BM Trash", true); bool hasLan = group.readEntry("BM Lan", true); bool hasVirtualFS = group.readEntry("BM Virtual FS", true); bool hasJumpback = group.readEntry("BM Jumpback", true); QMenu menu(_mainBookmarkPopup); menu.setTitle(i18n("Enable special bookmarks")); QAction *act; act = menu.addAction(i18n("Popular URLs")); act->setData(QVariant(POPULAR_URLS_ID)); act->setCheckable(true); act->setChecked(hasPopularURLs); act = menu.addAction(i18n("Trash bin")); act->setData(QVariant(TRASH_ID)); act->setCheckable(true); act->setChecked(hasTrash); act = menu.addAction(i18n("Local Network")); act->setData(QVariant(LAN_ID)); act->setCheckable(true); act->setChecked(hasLan); act = menu.addAction(i18n("Virtual Filesystem")); act->setData(QVariant(VIRTUAL_FS_ID)); act->setCheckable(true); act->setChecked(hasVirtualFS); act = menu.addAction(i18n("Jump back")); act->setData(QVariant(JUMP_BACK_ID)); act->setCheckable(true); act->setChecked(hasJumpback); connect(_mainBookmarkPopup, SIGNAL(highlighted(int)), &menu, SLOT(close())); connect(_mainBookmarkPopup, SIGNAL(activated(int)), &menu, SLOT(close())); int result = -1; QAction *res = menu.exec(QCursor::pos()); if (res && res->data().canConvert()) result = res->data().toInt(); bool doCloseMain = true; switch (result) { case POPULAR_URLS_ID: group.writeEntry("BM Popular URLs", !hasPopularURLs); break; case TRASH_ID: group.writeEntry("BM Trash", !hasTrash); break; case LAN_ID: group.writeEntry("BM Lan", !hasLan); break; case VIRTUAL_FS_ID: group.writeEntry("BM Virtual FS", !hasVirtualFS); break; case JUMP_BACK_ID: group.writeEntry("BM Jumpback", !hasJumpback); break; default: doCloseMain = false; break; } menu.close(); if (doCloseMain && _mainBookmarkPopup) _mainBookmarkPopup->close(); } #define OPEN_ID 100200 #define OPEN_NEW_TAB_ID 100201 #define DELETE_ID 100202 void KrBookmarkHandler::rightClicked(QMenu *menu, KrBookmark * bm) { QMenu popup(_mainBookmarkPopup); QAction * act; if (!bm->isFolder()) { act = popup.addAction(Icon("document-open"), i18n("Open")); act->setData(QVariant(OPEN_ID)); act = popup.addAction(Icon("tab-new"), i18n("Open in a new tab")); act->setData(QVariant(OPEN_NEW_TAB_ID)); popup.addSeparator(); } act = popup.addAction(Icon("edit-delete"), i18n("Delete")); act->setData(QVariant(DELETE_ID)); connect(menu, SIGNAL(highlighted(int)), &popup, SLOT(close())); connect(menu, SIGNAL(activated(int)), &popup, SLOT(close())); int result = -1; QAction *res = popup.exec(QCursor::pos()); if (res && res->data().canConvert ()) result = res->data().toInt(); popup.close(); if (_mainBookmarkPopup && result >= OPEN_ID && result <= DELETE_ID) { _mainBookmarkPopup->close(); } switch (result) { case OPEN_ID: SLOTS->refresh(bm->url()); break; case OPEN_NEW_TAB_ID: _mainWindow->activeManager()->newTab(bm->url()); break; case DELETE_ID: deleteBookmark(bm); break; } } // used to monitor middle clicks. if mid is found, then the // bookmark is opened in a new tab. ugly, but easier than overloading // KAction and KActionCollection. void KrBookmarkHandler::slotActivated(const QUrl &url) { if (_mainBookmarkPopup && !_mainBookmarkPopup->isHidden()) _mainBookmarkPopup->close(); if (_middleClick) _mainWindow->activeManager()->newTab(url); else SLOTS->refresh(url); } diff --git a/krusader/BookMan/krbookmarkhandler.h b/krusader/BookMan/krbookmarkhandler.h index 657e21d7..86350545 100644 --- a/krusader/BookMan/krbookmarkhandler.h +++ b/krusader/BookMan/krbookmarkhandler.h @@ -1,101 +1,101 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRBOOKMARKHANDLER_H #define KRBOOKMARKHANDLER_H // QtCore #include #include #include #include #include // QtXml #include // QtWidgets #include #include #include #include "krbookmark.h" class KActionCollection; class KBookmarkManager; class KrMainWindow; class KrBookmarkHandler: public QObject { Q_OBJECT friend class KrAddBookmarkDlg; enum Actions { BookmarkCurrent = 0, ManageBookmarks }; public: explicit KrBookmarkHandler(KrMainWindow *mainWindow); ~KrBookmarkHandler() override; void populate(QMenu *menu); void addBookmark(KrBookmark *bm, KrBookmark *parent = nullptr); void bookmarkCurrent(QUrl url); protected: void deleteBookmark(KrBookmark *bm); void importFromFile(); bool importFromFileBookmark(QDomElement &e, KrBookmark *parent, const QString& path, QString *errorMsg); bool importFromFileFolder(QDomNode &first, KrBookmark *parent, const QString& path, QString *errorMsg); void exportToFile(); void exportToFileFolder(QDomDocument &doc, QDomElement &parent, KrBookmark *folder); void exportToFileBookmark(QDomDocument &doc, QDomElement &where, KrBookmark *bm); void clearBookmarks(KrBookmark *root, bool removeBookmarks = true); void buildMenu(KrBookmark *parent, QMenu *menu, int depth = 0); bool eventFilter(QObject *obj, QEvent *ev) Q_DECL_OVERRIDE; void rightClicked(QMenu *menu, KrBookmark *bm); void rightClickOnSpecialBookmark(); void removeReferences(KrBookmark *root, KrBookmark *bmToRemove); protected slots: void bookmarksChanged(const QString&, const QString&); void slotActivated(const QUrl &url); private: KrMainWindow *_mainWindow; KActionCollection *_collection, *_privateCollection; KrBookmark *_root; // the whole KBookmarkManager is an ugly hack. use it until we have our own KBookmarkManager *manager; bool _middleClick; // if true, the user clicked the middle button to open the bookmark QPointer _mainBookmarkPopup; // main bookmark popup menu QList _specialBookmarks; // the action list of the special bookmarks QWidgetAction *_quickSearchAction; ///< Search bar container action QLineEdit *_quickSearchBar; ///< Search bar containing current query QMenu *_quickSearchMenu; ///< The menu where the search is performed QHash _quickSearchOriginalActionTitles; ///< Saved original action text values to restore after search void _setQuickSearchText(const QString &text); QString _quickSearchText() const; static void _highlightAction(QAction *action, bool isMatched = true); void _resetActionTextAndHighlighting(); }; Q_DECLARE_METATYPE(KrBookmark *) #endif // KRBOOKMARK_HANDLER_H diff --git a/krusader/Dialogs/checksumdlg.cpp b/krusader/Dialogs/checksumdlg.cpp index fa16fdca..f7733b5e 100644 --- a/krusader/Dialogs/checksumdlg.cpp +++ b/krusader/Dialogs/checksumdlg.cpp @@ -1,574 +1,574 @@ /***************************************************************************** * Copyright (C) 2005 Shie Erlich * * Copyright (C) 2007-2008 Csaba Karai * * Copyright (C) 2008 Jonas Bähr * - * Copyright (C) 2005-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2005-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "checksumdlg.h" #include "../krglobal.h" #include "../icon.h" #include "../krservices.h" #include "../krusader.h" #include "../GUI/krlistwidget.h" #include "../GUI/krtreewidget.h" // QtCore #include #include #include #include #include #include // QtWidgets #include #include #include #include #include #include // krazy:exclude=includes #include #include #include void Checksum::startCreationWizard(const QString &path, const QStringList &files) { if (files.isEmpty()) return; QDialog *dialog = new CHECKSUM_::CreateWizard(path, files); dialog->show(); } void Checksum::startVerifyWizard(const QString &path, const QString &checksumFile) { QDialog *dialog = new CHECKSUM_::VerifyWizard(path, checksumFile); dialog->show(); } namespace CHECKSUM_ { bool stopListFiles; // async operation invoked by QtConcurrent::run in creation wizard QStringList listFiles(const QString &path, const QStringList &fileNames) { const QDir baseDir(path); QStringList allFiles; for (const QString& fileName : fileNames) { if (stopListFiles) return QStringList(); QDir subDir = QDir(baseDir.filePath(fileName)); if (subDir.exists()) { subDir.setFilter(QDir::Files); QDirIterator it(subDir, QDirIterator::Subdirectories | QDirIterator::FollowSymlinks); while (it.hasNext()) { if (stopListFiles) return QStringList(); allFiles << baseDir.relativeFilePath(it.next()); } } else { // assume this is a file allFiles << fileName; } } return allFiles; } // ------------- Checksum Process ChecksumProcess::ChecksumProcess(QObject *parent, const QString &path) : KProcess(parent), m_tmpOutFile(QDir::tempPath() + QLatin1String("/krusader_XXXXXX.stdout")), m_tmpErrFile(QDir::tempPath() + QLatin1String("/krusader_XXXXXX.stderr")) { m_tmpOutFile.open(); // necessary to create the filename m_tmpErrFile.open(); // necessary to create the filename setOutputChannelMode(KProcess::SeparateChannels); // without this the next 2 lines have no effect! setStandardOutputFile(m_tmpOutFile.fileName()); setStandardErrorFile(m_tmpErrFile.fileName()); setWorkingDirectory(path); connect(this, &ChecksumProcess::errorOccurred, this, &ChecksumProcess::slotError); connect(this, static_cast(&QProcess::finished), this, &ChecksumProcess::slotFinished); } ChecksumProcess::~ChecksumProcess() { disconnect(this, nullptr, this, nullptr); // QProcess emits finished() on destruction close(); } void ChecksumProcess::slotError(QProcess::ProcessError error) { if (error == QProcess::FailedToStart) { KMessageBox::error(nullptr, i18n("Could not start %1.", program().join(" "))); } } void ChecksumProcess::slotFinished(int, QProcess::ExitStatus exitStatus) { if (exitStatus != QProcess::NormalExit) { KMessageBox::error(nullptr, i18n("There was an error while running %1.", program().join(" "))); return; } // parse result files if (!KrServices::fileToStringList(&m_tmpOutFile, m_outputLines) || !KrServices::fileToStringList(&m_tmpErrFile, m_errorLines)) { KMessageBox::error(nullptr, i18n("Error reading stdout or stderr")); return; } emit resultReady(); } // ------------- Generic Checksum Wizard ChecksumWizard::ChecksumWizard(const QString &path) : QWizard(krApp), m_path(path), m_process(nullptr) { setAttribute(Qt::WA_DeleteOnClose); // init the dictionary - pity it has to be manually m_checksumTools.insert("md5", "md5sum"); m_checksumTools.insert("sha1", "sha1sum"); m_checksumTools.insert("sha256", "sha256sum"); m_checksumTools.insert("sha224", "sha224sum"); m_checksumTools.insert("sha384", "sha384sum"); m_checksumTools.insert("sha512", "sha512sum"); connect(this, &QWizard::currentIdChanged, this, &ChecksumWizard::slotCurrentIdChanged); } ChecksumWizard::~ChecksumWizard() { if (m_process) { delete m_process; } } void ChecksumWizard::slotCurrentIdChanged(int id) { if (id == m_introId) { onIntroPage(); } else if (id == m_progressId) { if (m_process) { // we are coming from the result page; delete m_process; m_process = nullptr; restart(); } else { button(QWizard::BackButton)->hide(); button(QWizard::NextButton)->hide(); onProgressPage(); } } else if (id == m_resultId) { onResultPage(); } } QWizardPage *ChecksumWizard::createProgressPage(const QString &title) { auto *page = new QWizardPage; page->setTitle(title); page->setPixmap(QWizard::LogoPixmap, Icon("process-working").pixmap(32)); page->setSubTitle(i18n("Please wait...")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); // "busy" indicator auto *bar = new QProgressBar(); bar->setRange(0,0); mainLayout->addWidget(bar); return page; } bool ChecksumWizard::checkExists(const QString& type) { if (!KrServices::cmdExist(m_checksumTools[type])) { KMessageBox::error( this, i18n("Krusader cannot find a checksum tool that handles %1 on your system. " "Please check the Dependencies page in Krusader's settings.", type)); return false; } return true; } void ChecksumWizard::runProcess(const QString &type, const QStringList &args) { Q_ASSERT(m_process == nullptr); m_process = new ChecksumProcess(this, m_path); m_process->setProgram(KrServices::fullPathName(m_checksumTools[type]), args); // show next page (with results) (only) when process is done connect(m_process, &ChecksumProcess::resultReady, this, &QWizard::next); // run the process m_process->start(); } void ChecksumWizard::addChecksumLine(KrTreeWidget *tree, const QString &line) { auto *item = new QTreeWidgetItem(tree); const int hashLength = line.indexOf(' '); // delimiter is either " " or " *" item->setText(0, line.left(hashLength)); QString fileName = line.mid(hashLength + 2); if (fileName.endsWith('\n')) fileName.chop(1); item->setText(1, fileName); } // ------------- Create Wizard CreateWizard::CreateWizard(const QString &path, const QStringList &_files) : ChecksumWizard(path), m_fileNames(_files) { m_introId = addPage(createIntroPage()); m_progressId = addPage(createProgressPage(i18n("Creating Checksums"))); m_resultId = addPage(createResultPage()); setButton(QWizard::FinishButton, QDialogButtonBox(QDialogButtonBox::Save).button(QDialogButtonBox::Save)); connect(&m_listFilesWatcher, &QFutureWatcher::resultReadyAt, this, &CreateWizard::createChecksums); } QWizardPage *CreateWizard::createIntroPage() { auto *page = new QWizardPage; page->setTitle(i18n("Create Checksums")); page->setPixmap(QWizard::LogoPixmap, Icon("document-edit-sign").pixmap(32)); page->setSubTitle(i18n("About to calculate checksum for the following files or directories:")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); // file list auto *listWidget = new KrListWidget; listWidget->addItems(m_fileNames); mainLayout->addWidget(listWidget); // checksum method auto *hLayout = new QHBoxLayout; QLabel *methodLabel = new QLabel(i18n("Select the checksum method:")); hLayout->addWidget(methodLabel); m_methodBox = new KComboBox; // -- fill the combo with available methods for (const QString& type: m_checksumTools.keys()) m_methodBox->addItem(type); m_methodBox->setFocus(); hLayout->addWidget(m_methodBox); mainLayout->addLayout(hLayout); return page; } QWizardPage *CreateWizard::createResultPage() { auto *page = new QWizardPage; page->setTitle(i18n("Checksum Results")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); m_hashesTreeWidget = new KrTreeWidget(this); m_hashesTreeWidget->setAllColumnsShowFocus(true); m_hashesTreeWidget->setHeaderLabels(QStringList() << i18n("Hash") << i18n("File")); mainLayout->addWidget(m_hashesTreeWidget); m_errorLabel = new QLabel(i18n("Errors received:")); mainLayout->addWidget(m_errorLabel); m_errorListWidget = new KrListWidget; mainLayout->addWidget(m_errorListWidget); m_onePerFileBox = new QCheckBox(i18n("Save one checksum file for each source file")); m_onePerFileBox->setChecked(false); mainLayout->addWidget(m_onePerFileBox); return page; } void CreateWizard::onIntroPage() { button(QWizard::NextButton)->show(); } void CreateWizard::onProgressPage() { // first, get all files (recurse in directories) - async stopListFiles = false; // QFuture cannot cancel QtConcurrent::run connect(this, &CreateWizard::finished, this, [=]() { stopListFiles = true; }); QFuture listFuture = QtConcurrent::run(listFiles, m_path, m_fileNames); m_listFilesWatcher.setFuture(listFuture); } void CreateWizard::createChecksums() { const QString type = m_methodBox->currentText(); if (!checkExists(type)) { button(QWizard::BackButton)->show(); return; } const QStringList &allFiles = m_listFilesWatcher.result(); if (allFiles.isEmpty()) { KMessageBox::error(this, i18n("No files found")); button(QWizard::BackButton)->show(); return; } runProcess(type, allFiles); // set suggested filename m_suggestedFilePath = QDir(m_path).filePath( (m_fileNames.count() > 1 ? "checksum." : (m_fileNames[0] + '.')) + type); } void CreateWizard::onResultPage() { // hash tools display errors into stderr, so we'll use that to determine the result of the job const QStringList outputLines = m_process->stdOutput(); const QStringList errorLines = m_process->errOutput(); bool errors = !errorLines.isEmpty(); bool successes = !outputLines.isEmpty(); QWizardPage *page = currentPage(); page->setPixmap(QWizard::LogoPixmap, Icon(errors || !successes ? "dialog-error" : "dialog-information").pixmap(32)); page->setSubTitle(errors || !successes ? i18n("Errors were detected while creating the checksums") : i18n("Checksums were created successfully")); m_hashesTreeWidget->clear(); m_hashesTreeWidget->setVisible(successes); if (successes) { for (const QString& line : outputLines) addChecksumLine(m_hashesTreeWidget, line); //m_hashesTreeWidget->sortItems(1, Qt::AscendingOrder); } m_errorLabel->setVisible(errors); m_errorListWidget->setVisible(errors); m_errorListWidget->clear(); m_errorListWidget->addItems(errorLines); m_onePerFileBox->setEnabled(outputLines.size() > 1); button(QWizard::FinishButton)->setEnabled(successes); } bool CreateWizard::savePerFile() { const QString type = m_suggestedFilePath.mid(m_suggestedFilePath.lastIndexOf('.')); krApp->startWaiting(i18n("Saving checksum files..."), 0); for (const QString& line : m_process->stdOutput()) { const QString filename = line.mid(line.indexOf(' ') + 2) + type; if (!saveChecksumFile(QStringList() << line, filename)) { KMessageBox::error(this, i18n("Errors occurred while saving multiple checksums. Stopping")); krApp->stopWait(); return false; } } krApp->stopWait(); return true; } bool CreateWizard::saveChecksumFile(const QStringList &data, const QString &filename) { QString filePath = filename.isEmpty() ? m_suggestedFilePath : filename; if (filename.isEmpty() || QFile::exists(filePath)) { filePath = QFileDialog::getSaveFileName(this, QString(), filePath); if (filePath.isEmpty()) return false; // user pressed cancel } QFile file(filePath); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); for (const QString& line : data) stream << line << "\n"; file.close(); } if (file.error() != QFile::NoError) { KMessageBox::detailedError(this, i18n("Error saving file %1", filePath), file.errorString()); return false; } return true; } void CreateWizard::accept() { const bool saved = m_onePerFileBox->isChecked() ? savePerFile() : saveChecksumFile(m_process->stdOutput()); if (saved) QWizard::accept(); } // ------------- Verify Wizard VerifyWizard::VerifyWizard(const QString &path, const QString &inputFile) : ChecksumWizard(path) { m_checksumFile = isSupported(inputFile) ? inputFile : path; m_introId = addPage(createIntroPage()); // m_checksumFile must already be set m_progressId = addPage(createProgressPage(i18n("Verifying Checksums"))); m_resultId = addPage(createResultPage()); } void VerifyWizard::slotChecksumPathChanged(const QString &path) { m_hashesTreeWidget->clear(); button(QWizard::NextButton)->setEnabled(false); if (!isSupported(path)) return; m_checksumFile = path; // parse and display checksum file content; only for the user, parsed values are not used m_hashesTreeWidget->clear(); QFile file(m_checksumFile); if (file.open(QFile::ReadOnly)) { QTextStream inStream(&file); while (!inStream.atEnd()) { addChecksumLine(m_hashesTreeWidget, file.readLine()); } } file.close(); button(QWizard::NextButton)->setEnabled(true); } QWizardPage *VerifyWizard::createIntroPage() { auto *page = new QWizardPage; page->setTitle(i18n("Verify Checksum File")); page->setPixmap(QWizard::LogoPixmap, Icon("document-edit-verify").pixmap(32)); page->setSubTitle(i18n("About to verify the following checksum file")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); // checksum file auto *hLayout = new QHBoxLayout; QLabel *checksumFileLabel = new QLabel(i18n("Checksum file:")); hLayout->addWidget(checksumFileLabel); auto *checksumFileReq = new KUrlRequester; QString typesFilter; for (const QString& ext: m_checksumTools.keys()) typesFilter += ("*." + ext + ' '); checksumFileReq->setFilter(typesFilter); checksumFileReq->setText(m_checksumFile); checksumFileReq->setFocus(); connect(checksumFileReq, &KUrlRequester::textChanged, this, &VerifyWizard::slotChecksumPathChanged); hLayout->addWidget(checksumFileReq); mainLayout->addLayout(hLayout); // content of checksum file m_hashesTreeWidget = new KrTreeWidget(page); m_hashesTreeWidget->setAllColumnsShowFocus(true); m_hashesTreeWidget->setHeaderLabels(QStringList() << i18n("Hash") << i18n("File")); mainLayout->addWidget(m_hashesTreeWidget); return page; } QWizardPage *VerifyWizard::createResultPage() { auto *page = new QWizardPage; page->setTitle(i18n("Verify Result")); auto *mainLayout = new QVBoxLayout; page->setLayout(mainLayout); m_outputLabel = new QLabel(i18n("Result output:")); mainLayout->addWidget(m_outputLabel); m_outputListWidget = new KrListWidget; mainLayout->addWidget(m_outputListWidget); return page; } void VerifyWizard::onIntroPage() { // cannot do this in constructor: NextButton->hide() is overridden slotChecksumPathChanged(m_checksumFile); } void VerifyWizard::onProgressPage() { // verify checksum file... const QString extension = QFileInfo(m_checksumFile).suffix(); if (!checkExists(extension)) { button(QWizard::BackButton)->show(); return; } runProcess(extension, QStringList() << "--strict" << "-c" << m_checksumFile); } void VerifyWizard::onResultPage() { // better not only trust error output const bool errors = m_process->exitCode() != 0 || !m_process->errOutput().isEmpty(); QWizardPage *page = currentPage(); page->setPixmap(QWizard::LogoPixmap, Icon(errors ? "dialog-error" : "dialog-information").pixmap(32)); page->setSubTitle(errors ? i18n("Errors were detected while verifying the checksums") : i18n("Checksums were verified successfully")); // print everything, errors first m_outputListWidget->clear(); m_outputListWidget->addItems(m_process->errOutput() + m_process->stdOutput()); button(QWizard::FinishButton)->setEnabled(!errors); } bool VerifyWizard::isSupported(const QString &path) { const QFileInfo fileInfo(path); return fileInfo.isFile() && m_checksumTools.keys().contains(fileInfo.suffix()); } } // NAMESPACE CHECKSUM_ diff --git a/krusader/Dialogs/checksumdlg.h b/krusader/Dialogs/checksumdlg.h index 095c1a0e..da1d5ee7 100644 --- a/krusader/Dialogs/checksumdlg.h +++ b/krusader/Dialogs/checksumdlg.h @@ -1,180 +1,180 @@ /***************************************************************************** * Copyright (C) 2005 Shie Erlich * * Copyright (C) 2007-2008 Csaba Karai * * Copyright (C) 2008 Jonas Bähr * - * Copyright (C) 2005-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2005-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef CHECKSUMDLG_H #define CHECKSUMDLG_H // QtCore #include #include #include // QtWidgets #include #include #include #include #include class KrListWidget; class KrTreeWidget; /** * Perform checksum operations: Creation of checksums or verifying files with a checksum file. * * The dialogs are not modal. The used checksum tools only support local files which are expected to * be in one directory (specified by 'path'). */ class Checksum { public: static void startCreationWizard(const QString &path, const QStringList &fileNames); static void startVerifyWizard(const QString &path, const QString &checksumFile = QString()); }; namespace CHECKSUM_ { // private namespace /** Wrapper for KProcess to handle errors and output. */ class ChecksumProcess : public KProcess { Q_OBJECT public: ChecksumProcess(QObject *parent, const QString &path); ~ChecksumProcess() override; QStringList stdOutput() const { return m_outputLines; } QStringList errOutput() const { return m_errorLines; } signals: void resultReady(); private slots: void slotError(QProcess::ProcessError error); void slotFinished(int, QProcess::ExitStatus exitStatus); private: QStringList m_outputLines; QStringList m_errorLines; QTemporaryFile m_tmpOutFile; QTemporaryFile m_tmpErrFile; }; /** Base class for common code in creation and verify wizard. */ class ChecksumWizard : public QWizard { Q_OBJECT public: explicit ChecksumWizard(const QString &path); ~ChecksumWizard() override; private slots: void slotCurrentIdChanged(int id); protected: virtual QWizardPage *createIntroPage() = 0; virtual QWizardPage *createResultPage() = 0; virtual void onIntroPage() = 0; virtual void onProgressPage() = 0; virtual void onResultPage() = 0; QWizardPage *createProgressPage(const QString &title); bool checkExists(const QString& type); void runProcess(const QString &type, const QStringList &args); void addChecksumLine(KrTreeWidget *tree, const QString &line); const QString m_path; ChecksumProcess *m_process; QMap m_checksumTools; // extension/typ-name -> binary name int m_introId, m_progressId, m_resultId; }; class CreateWizard : public ChecksumWizard { Q_OBJECT public: CreateWizard(const QString &path, const QStringList &_files); public slots: void accept() Q_DECL_OVERRIDE; private: QWizardPage *createIntroPage() Q_DECL_OVERRIDE; QWizardPage *createResultPage() Q_DECL_OVERRIDE; void onIntroPage() Q_DECL_OVERRIDE; void onProgressPage() Q_DECL_OVERRIDE; void onResultPage() Q_DECL_OVERRIDE; void createChecksums(); bool savePerFile(); bool saveChecksumFile(const QStringList &data, const QString &filename = QString()); const QStringList m_fileNames; QFutureWatcher m_listFilesWatcher; QString m_suggestedFilePath; // intro page KComboBox *m_methodBox; // result page KrTreeWidget *m_hashesTreeWidget; QLabel *m_errorLabel; KrListWidget *m_errorListWidget; QCheckBox *m_onePerFileBox; }; class VerifyWizard : public ChecksumWizard { Q_OBJECT public: VerifyWizard(const QString &path, const QString &inputFile); private slots: void slotChecksumPathChanged(const QString &path); private: QWizardPage *createIntroPage() Q_DECL_OVERRIDE; QWizardPage *createResultPage() Q_DECL_OVERRIDE; void onIntroPage() Q_DECL_OVERRIDE; void onProgressPage() Q_DECL_OVERRIDE; void onResultPage() Q_DECL_OVERRIDE; bool isSupported(const QString &path); QString m_checksumFile; // intro page KrTreeWidget *m_hashesTreeWidget; // result page QLabel *m_outputLabel; KrListWidget *m_outputListWidget; }; } // NAMESPACE CHECKSUM_ #endif // CHECKSUMDLG_H diff --git a/krusader/Dialogs/krdialogs.cpp b/krusader/Dialogs/krdialogs.cpp index 3ca28c84..92ca21bc 100644 --- a/krusader/Dialogs/krdialogs.cpp +++ b/krusader/Dialogs/krdialogs.cpp @@ -1,206 +1,206 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krdialogs.h" // QtCore #include #include // QtGui #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include "../krglobal.h" #include "../FileSystem/filesystem.h" #include "../defaults.h" #include "../JobMan/jobman.h" QUrl KChooseDir::getFile(const QString &text, const QUrl& url, const QUrl& cwd) { return get(text, url, cwd, KFile::File); } QUrl KChooseDir::getDir(const QString &text, const QUrl& url, const QUrl& cwd) { return get(text, url, cwd, KFile::Directory); } QUrl KChooseDir::get(const QString &text, const QUrl &url, const QUrl &cwd, KFile::Modes mode) { QScopedPointer dlg(new KUrlRequesterDialog(url, text, krMainWindow)); dlg->urlRequester()->setStartDir(cwd); dlg->urlRequester()->setMode(mode); dlg->exec(); QUrl u = dlg->selectedUrl(); // empty if cancelled if (u.scheme() == "zip" || u.scheme() == "krarc" || u.scheme() == "tar" || u.scheme() == "iso") { if (QDir(u.path()).exists()) { u.setScheme("file"); } } return u; } KChooseDir::ChooseResult KChooseDir::getCopyDir(const QString &text, const QUrl &url, const QUrl &cwd) { QScopedPointer dlg(new KUrlRequesterDlgForCopy(url, text, krMainWindow, true)); dlg->urlRequester()->setStartDir(cwd); dlg->urlRequester()->setMode(KFile::Directory); dlg->exec(); QUrl u = dlg->selectedURL(); if (u.scheme() == "zip" || u.scheme() == "krarc" || u.scheme() == "tar" || u.scheme() == "iso") { if (QDir(u.path()).exists()) { u.setScheme("file"); } } ChooseResult result; result.url = u; result.enqueue = dlg->isQueued(); return result; } KUrlRequesterDlgForCopy::KUrlRequesterDlgForCopy(const QUrl &urlName, const QString &_text, QWidget *parent, bool modal) : QDialog(parent) { setWindowModality(modal ? Qt::WindowModal : Qt::NonModal); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); QLabel *label = new QLabel; label->setWordWrap(true); label->setText(_text); mainLayout->addWidget(label); urlRequester_ = new KUrlRequester(urlName, this); urlRequester_->setMinimumWidth(urlRequester_->sizeHint().width() * 3); mainLayout->addWidget(urlRequester_); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); QPushButton *queueButton = new QPushButton( krJobMan->isQueueModeEnabled() ? i18n("F2 Delay Job Start") : i18n("F2 Queue"), this); queueButton->setToolTip(krJobMan->isQueueModeEnabled() ? i18n("Do not start the job now.") : i18n("Enqueue the job if another job is running. Otherwise start immediately.")); buttonBox->addButton(queueButton, QDialogButtonBox::ActionRole); connect(buttonBox, &QDialogButtonBox::accepted, this, &KUrlRequesterDlgForCopy::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &KUrlRequesterDlgForCopy::reject); connect(queueButton, &QPushButton::clicked, this, &KUrlRequesterDlgForCopy::slotQueueButtonClicked); connect(urlRequester_, &KUrlRequester::textChanged, this, &KUrlRequesterDlgForCopy::slotTextChanged); urlRequester_->setFocus(); bool state = !urlName.isEmpty(); okButton->setEnabled(state); } void KUrlRequesterDlgForCopy::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_F2: slotQueueButtonClicked(); return; default: QDialog::keyPressEvent(e); } } void KUrlRequesterDlgForCopy::slotQueueButtonClicked() { queueStart = true; accept(); } void KUrlRequesterDlgForCopy::slotTextChanged(const QString & text) { bool state = !text.trimmed().isEmpty(); okButton->setEnabled(state); } QUrl KUrlRequesterDlgForCopy::selectedURL() const { if (result() == QDialog::Accepted) { QUrl url = urlRequester_->url(); qDebug() << "requester returned URL=" << url.toDisplayString(); if (url.isValid()) KRecentDocument::add(url); return url; } else return QUrl(); } KUrlRequester * KUrlRequesterDlgForCopy::urlRequester() { return urlRequester_; } KRGetDate::KRGetDate(QDate date, QWidget *parent) : QDialog(parent, Qt::MSWindowsFixedSizeDialogHint) { setWindowModality(Qt::WindowModal); dateWidget = new KDatePicker(this); dateWidget->setDate(date); dateWidget->resize(dateWidget->sizeHint()); setMinimumSize(dateWidget->sizeHint()); setMaximumSize(dateWidget->sizeHint()); resize(minimumSize()); connect(dateWidget, &KDatePicker::dateSelected, this, &KRGetDate::setDate); connect(dateWidget, &KDatePicker::dateEntered, this, &KRGetDate::setDate); // keep the original date - incase ESC is pressed originalDate = date; } QDate KRGetDate::getDate() { if (exec() == QDialog::Rejected) chosenDate = QDate(); hide(); return chosenDate; } void KRGetDate::setDate(QDate date) { chosenDate = date; accept(); } diff --git a/krusader/Dialogs/krdialogs.h b/krusader/Dialogs/krdialogs.h index d9c89b41..f6915ec1 100644 --- a/krusader/Dialogs/krdialogs.h +++ b/krusader/Dialogs/krdialogs.h @@ -1,119 +1,119 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRDIALOGS_H #define KRDIALOGS_H // QtCore #include #include // QtWidgets #include #include #include #include #include #include #include // QtGui #include #include #include #include #include /** \class KChooseDir * Used for asking the user for a folder. * example: * \code * QUrl u = KChooseDir::getDir("target folder", "/suggested/path", ACTIVE_PANEL->virtualPath()); * if (u.isEmpty()) { * // user canceled (either by pressing cancel, or esc * } else { * // do you thing here: you've got a safe url to use * } * \endcode */ class KChooseDir { public: struct ChooseResult { QUrl url; bool enqueue; }; /** * \param text - description of the info requested from the user * \param url - a suggested url to appear in the box as a default choice * \param cwd - a path which is the current working directory (usually ACTIVE_PANEL->virtualPath()). * this is used for completion of partial urls */ static QUrl getFile(const QString &text, const QUrl &url, const QUrl &cwd); static QUrl getDir(const QString &text, const QUrl &url, const QUrl &cwd); static ChooseResult getCopyDir(const QString &text, const QUrl &url, const QUrl &cwd); private: static QUrl get(const QString &text, const QUrl &url, const QUrl &cwd, KFile::Modes mode); }; class KUrlRequesterDlgForCopy : public QDialog { Q_OBJECT public: KUrlRequesterDlgForCopy(const QUrl& url, const QString& text, QWidget *parent, bool modal = true); QUrl selectedURL() const; bool isQueued() { return queueStart; } KUrlRequester *urlRequester(); protected: void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; private slots: void slotQueueButtonClicked(); void slotTextChanged(const QString &); private: KUrlRequester *urlRequester_; QPushButton *okButton; bool queueStart = false; }; class KRGetDate : public QDialog { Q_OBJECT public: explicit KRGetDate(QDate date = QDate::currentDate(), QWidget *parent = nullptr); QDate getDate(); private slots: void setDate(QDate); private: KDatePicker *dateWidget; QDate chosenDate, originalDate; }; #endif diff --git a/krusader/Dialogs/krmaskchoice.cpp b/krusader/Dialogs/krmaskchoice.cpp index 1c5bf590..168124db 100644 --- a/krusader/Dialogs/krmaskchoice.cpp +++ b/krusader/Dialogs/krmaskchoice.cpp @@ -1,151 +1,151 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krmaskchoice.h" // QtCore #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include "../GUI/krlistwidget.h" /** * Constructs a KRMaskChoice which is a child of 'parent', with the * name 'name' and widget flags set to 'f' * * The dialog will by default be modeless, unless you set 'modal' to * TRUE to construct a modal dialog. */ KRMaskChoice::KRMaskChoice(QWidget* parent) : QDialog(parent) { setModal(true); resize(401, 314); setWindowTitle(i18n("Choose Files")); auto* MainLayout = new QVBoxLayout(this); auto* HeaderLayout = new QHBoxLayout(); MainLayout->addLayout(HeaderLayout); PixmapLabel1 = new QLabel(this); PixmapLabel1->setScaledContents(true); PixmapLabel1->setMaximumSize(QSize(31, 31)); HeaderLayout->addWidget(PixmapLabel1); label = new QLabel(this); label->setText(i18n("Select the following files:")); HeaderLayout->addWidget(label); selection = new KComboBox(this); selection->setEditable(true); selection->setInsertPolicy(QComboBox::InsertAtTop); selection->setAutoCompletion(true); MainLayout->addWidget(selection); auto* GroupBox1 = new QGroupBox(this); GroupBox1->setTitle(i18n("Predefined Selections")); MainLayout->addWidget(GroupBox1); auto* gbLayout = new QHBoxLayout(GroupBox1); preSelections = new KrListWidget(GroupBox1); preSelections->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); preSelections->setWhatsThis(i18n("A predefined selection is a file-mask which you often use.\nSome examples are: \"*.c, *.h\", \"*.c, *.o\", etc.\nYou can add these masks to the list by typing them and pressing the Add button.\nDelete removes a predefined selection and Clear removes all of them.\nNotice that the line in which you edit the mask has its own history, you can scroll it, if needed.")); gbLayout->addWidget(preSelections); auto* vbox = new QVBoxLayout(); gbLayout->addLayout(vbox); PushButton7 = new QPushButton(GroupBox1); PushButton7->setText(i18n("Add")); PushButton7->setToolTip(i18n("Adds the selection in the line-edit to the list")); vbox->addWidget(PushButton7); PushButton7_2 = new QPushButton(GroupBox1); PushButton7_2->setText(i18n("Delete")); PushButton7_2->setToolTip(i18n("Delete the marked selection from the list")); vbox->addWidget(PushButton7_2); PushButton7_3 = new QPushButton(GroupBox1); PushButton7_3->setText(i18n("Clear")); PushButton7_3->setToolTip(i18n("Clears the entire list of selections")); vbox->addWidget(PushButton7_3); vbox->addItem(new QSpacerItem(5, 5, QSizePolicy::Fixed, QSizePolicy::Expanding)); QDialogButtonBox* ButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); MainLayout->addWidget(ButtonBox); // signals and slots connections connect(ButtonBox, &QDialogButtonBox::rejected, this, &KRMaskChoice::reject); connect(ButtonBox, &QDialogButtonBox::accepted, this, &KRMaskChoice::accept); connect(PushButton7, &QPushButton::clicked, this, &KRMaskChoice::addSelection); connect(PushButton7_2, &QPushButton::clicked, this, &KRMaskChoice::deleteSelection); connect(PushButton7_3, &QPushButton::clicked, this, &KRMaskChoice::clearSelections); connect(selection, QOverload::of(&KComboBox::activated), selection, &KComboBox::setEditText); connect(selection->lineEdit(), &QLineEdit::returnPressed, this, &KRMaskChoice::accept); connect(preSelections, &KrListWidget::currentItemChanged, this, &KRMaskChoice::currentItemChanged); connect(preSelections, &KrListWidget::itemActivated, this, &KRMaskChoice::acceptFromList); } /* * Destroys the object and frees any allocated resources */ KRMaskChoice::~KRMaskChoice() { // no need to delete child widgets, Qt does it all for us } void KRMaskChoice::addSelection() { qWarning("KRMaskChoice::addSelection(): Not implemented yet!"); } void KRMaskChoice::clearSelections() { qWarning("KRMaskChoice::clearSelections(): Not implemented yet!"); } void KRMaskChoice::deleteSelection() { qWarning("KRMaskChoice::deleteSelection(): Not implemented yet!"); } void KRMaskChoice::acceptFromList(QListWidgetItem *) { qWarning("KRMaskChoice::acceptFromList(QListWidgetItem *): Not implemented yet!"); } void KRMaskChoice::currentItemChanged(QListWidgetItem *) { qWarning("KRMaskChoice::currentItemChanged(QListWidgetItem *): Not implemented yet!"); } diff --git a/krusader/Dialogs/krmaskchoice.h b/krusader/Dialogs/krmaskchoice.h index 7b52778a..11a5e58c 100644 --- a/krusader/Dialogs/krmaskchoice.h +++ b/krusader/Dialogs/krmaskchoice.h @@ -1,57 +1,57 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRMASKCHOICE_H #define KRMASKCHOICE_H // QtWidgets #include class QLabel; class QListWidgetItem; class QPushButton; class KComboBox; class KrListWidget; class KRMaskChoice : public QDialog { Q_OBJECT public: explicit KRMaskChoice(QWidget* parent = nullptr); ~KRMaskChoice() override; KComboBox* selection; QLabel* PixmapLabel1; QLabel* label; KrListWidget* preSelections; QPushButton* PushButton7; QPushButton* PushButton7_2; QPushButton* PushButton7_3; public slots: virtual void addSelection(); virtual void clearSelections(); virtual void deleteSelection(); virtual void acceptFromList(QListWidgetItem *); virtual void currentItemChanged(QListWidgetItem *); }; #endif // KRMASKCHOICE_H diff --git a/krusader/Dialogs/krpleasewait.cpp b/krusader/Dialogs/krpleasewait.cpp index 48479731..17152971 100644 --- a/krusader/Dialogs/krpleasewait.cpp +++ b/krusader/Dialogs/krpleasewait.cpp @@ -1,156 +1,156 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krpleasewait.h" // QtCore #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include "../krglobal.h" KRPleaseWait::KRPleaseWait(const QString& msg, QWidget *parent, int count, bool cancel): QProgressDialog(cancel ? nullptr : parent) , inc(true) { setModal(!cancel); timer = new QTimer(this); setWindowTitle(i18n("Krusader::Wait")); setMinimumDuration(500); setAutoClose(false); setAutoReset(false); connect(timer, &QTimer::timeout, this, &KRPleaseWait::cycleProgress); auto* progress = new QProgressBar(this); progress->setMaximum(count); progress->setMinimum(0); setBar(progress); QLabel* label = new QLabel(this); setLabel(label); QPushButton* btn = new QPushButton(i18n("&Cancel"), this); setCancelButton(btn); btn->setEnabled(canClose = cancel); setLabelText(msg); show(); } void KRPleaseWait::closeEvent(QCloseEvent * e) { if (canClose) { emit canceled(); e->accept(); } else /* if cancel is not allowed, we disable */ e->ignore(); /* the window closing [x] also */ } void KRPleaseWait::incProgress(int howMuch) { setValue(value() + howMuch); } void KRPleaseWait::cycleProgress() { if (inc) setValue(value() + 1); else setValue(value() - 1); if (value() >= 9) inc = false; if (value() <= 0) inc = true; } KRPleaseWaitHandler::KRPleaseWaitHandler(QWidget *parentWindow) : QObject(parentWindow), _parentWindow(parentWindow), dlg(nullptr) { } void KRPleaseWaitHandler::stopWait() { if (dlg != nullptr) delete dlg; dlg = nullptr; cycleMutex = incMutex = false; // return cursor to normal arrow _parentWindow->setCursor(Qt::ArrowCursor); } void KRPleaseWaitHandler::startWaiting(const QString& msg, int count , bool cancel) { if (dlg == nullptr) { dlg = new KRPleaseWait(msg , _parentWindow, count, cancel); connect(dlg, &KRPleaseWait::canceled, this, &KRPleaseWaitHandler::killJob); } incMutex = cycleMutex = _wasCancelled = false; dlg->setValue(0); dlg->setLabelText(msg); if (count == 0) { dlg->setMaximum(10); cycle = true; cycleProgress(); } else { dlg->setMaximum(count); cycle = false; } } void KRPleaseWaitHandler::cycleProgress() { if (cycleMutex) return; cycleMutex = true; if (dlg) dlg->cycleProgress(); if (cycle) QTimer::singleShot(2000, this, &KRPleaseWaitHandler::cycleProgress); cycleMutex = false; } void KRPleaseWaitHandler::killJob() { if (!job.isNull()) job->kill(KJob::EmitResult); stopWait(); _wasCancelled = true; } void KRPleaseWaitHandler::setJob(KIO::Job* j) { job = j; } void KRPleaseWaitHandler::incProgress(int i) { if (incMutex) return; incMutex = true; if (dlg) dlg->incProgress(i); incMutex = false; } diff --git a/krusader/Dialogs/krpleasewait.h b/krusader/Dialogs/krpleasewait.h index febceda4..0319a579 100644 --- a/krusader/Dialogs/krpleasewait.h +++ b/krusader/Dialogs/krpleasewait.h @@ -1,81 +1,81 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRPLEASEWAIT_H #define KRPLEASEWAIT_H // QtCore #include #include // QtGui #include // QtWidgets #include #include class KRPleaseWait; class KRPleaseWaitHandler : public QObject { Q_OBJECT public: explicit KRPleaseWaitHandler(QWidget *parentWindow); public slots: void startWaiting(const QString& msg, int count = 0, bool cancel = false); void stopWait(); void cycleProgress(); void incProgress(int i); void killJob(); void setJob(KIO::Job* j); bool wasCancelled() const { return _wasCancelled; } private: QWidget *_parentWindow; QPointer job; KRPleaseWait * dlg; bool cycle, cycleMutex, incMutex, _wasCancelled; }; class KRPleaseWait : public QProgressDialog { Q_OBJECT public: KRPleaseWait(const QString& msg, QWidget *parent, int count = 0 , bool cancel = false); public slots: void incProgress(int howMuch); void cycleProgress(); protected: bool inc; QTimer* timer; void closeEvent(QCloseEvent * e) Q_DECL_OVERRIDE; bool canClose; }; #endif diff --git a/krusader/Dialogs/krspecialwidgets.cpp b/krusader/Dialogs/krspecialwidgets.cpp index a4cd2e04..a38b3eff 100644 --- a/krusader/Dialogs/krspecialwidgets.cpp +++ b/krusader/Dialogs/krspecialwidgets.cpp @@ -1,219 +1,219 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krspecialwidgets.h" #include "krmaskchoice.h" #include "newftpgui.h" #include "../krglobal.h" // QtGui #include #include #include ///////////////////////////////////////////////////////////////////////////// /////////////////////// Pie related widgets ///////////////////////////////// ///////////////////////////////////////////////////////////////////////////// // The pie-related widgets use hard-coded coordinates to create the look. // This is ok since the whole widget is fitted into an existing view and thus // no re-alignments are needed. #define LEFT 10 #define BOTTOM 150 #define WIDTH 120 #define HEIGHT 40 #define Z_HEIGHT 10 #define STARTANGLE 0 #define DEG(x) (16*(x)) QColor KRPie::colors[ 12 ] = {Qt::red, Qt::blue, Qt::green, Qt::cyan, Qt::magenta, Qt::gray, Qt::black, Qt::white, Qt::darkRed, Qt::darkBlue, Qt::darkMagenta, Qt::darkCyan }; ////////////////////////////////////////////////////////////////////////////// /////////////// KRFSDisplay - Filesystem / Freespace Display ///////////////// ////////////////////////////////////////////////////////////////////////////// // This is the full constructor: use it for a mounted filesystem KRFSDisplay::KRFSDisplay(QWidget *parent, QString _alias, QString _realName, KIO::filesize_t _total, KIO::filesize_t _free) : QWidget(parent), totalSpace(_total), freeSpace(_free), alias(std::move(_alias)), realName(std::move(_realName)), mounted(true), empty(false), supermount(false) { int leftMargin, topMargin, rightMargin, bottomMargin; getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); resize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); setMinimumSize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); show(); } // Use this one for an unmounted filesystem KRFSDisplay::KRFSDisplay(QWidget *parent, QString _alias, QString _realName, bool sm) : QWidget(parent), alias(std::move(_alias)), realName(std::move(_realName)), mounted(false), empty(false), supermount(sm) { int leftMargin, topMargin, rightMargin, bottomMargin; getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); resize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); setMinimumSize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); show(); } // This is used only when an empty widget needs to be displayed (for example: // when filesystem statistics haven't been calculated yet) KRFSDisplay::KRFSDisplay(QWidget *parent) : QWidget(parent), empty(true) { int leftMargin, topMargin, rightMargin, bottomMargin; getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); resize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); setMinimumSize(150 + leftMargin + rightMargin, 200 + topMargin + bottomMargin); show(); } // The main painter! void KRFSDisplay::paintEvent(QPaintEvent *) { QPainter paint(this); if (!empty) { int leftMargin, topMargin, rightMargin, bottomMargin; getContentsMargins(&leftMargin, &topMargin, &rightMargin, &bottomMargin); // create the text // first, name and location QFont font = paint.font(); font.setWeight(QFont::Bold); paint.setFont(font); paint.drawText(leftMargin + 10, topMargin + 20, alias); font.setWeight(QFont::Normal); paint.setFont(font); paint.drawText(leftMargin + 10, topMargin + 37, '(' + realName + ')'); if (mounted) { // incase the filesystem is already mounted // second, the capacity paint.drawText(leftMargin + 10, topMargin + 70, i18n("Capacity: %1", KIO::convertSizeFromKiB(totalSpace))); // third, the 2 boxes (used, free) QPen systemPen = paint.pen(); paint.setPen(Qt::black); paint.drawRect(leftMargin + 10, topMargin + 90, 10, 10); paint.fillRect(leftMargin + 11, topMargin + 91, 8, 8, QBrush(Qt::gray)); paint.drawRect(leftMargin + 10, topMargin + 110, 10, 10); paint.fillRect(leftMargin + 11, topMargin + 111, 8, 8, QBrush(Qt::white)); // now, the text for the boxes paint.setPen(systemPen); paint.drawText(leftMargin + 25, topMargin + 100, i18n("Used: %1", KIO::convertSizeFromKiB(totalSpace - freeSpace))); paint.drawText(leftMargin + 25, topMargin + 120, i18n("Free: %1", KIO::convertSizeFromKiB(freeSpace))); // first, create the empty pie // bottom... paint.setPen(Qt::black); paint.setBrush(Qt::white); paint.drawPie(leftMargin + LEFT, topMargin + BOTTOM, WIDTH, HEIGHT, STARTANGLE, DEG(360)); // body... paint.setPen(Qt::lightGray); for (int i = 1; i < Z_HEIGHT; ++i) paint.drawPie(leftMargin + LEFT, topMargin + BOTTOM - i, WIDTH, HEIGHT, STARTANGLE, DEG(360)); // side lines... paint.setPen(Qt::black); paint.drawLine(leftMargin + LEFT, topMargin + BOTTOM + HEIGHT / 2, LEFT, BOTTOM + HEIGHT / 2 - Z_HEIGHT); paint.drawLine(leftMargin + LEFT + WIDTH, topMargin + BOTTOM + HEIGHT / 2, LEFT + WIDTH, BOTTOM + HEIGHT / 2 - Z_HEIGHT); // top of the pie paint.drawPie(leftMargin + LEFT, topMargin + BOTTOM - Z_HEIGHT, WIDTH, HEIGHT, STARTANGLE, DEG(360)); // the "used space" slice float i = ((float)(totalSpace - freeSpace) / (totalSpace)) * 360.0; paint.setBrush(Qt::gray); paint.drawPie(leftMargin + LEFT, topMargin + BOTTOM - Z_HEIGHT, WIDTH, HEIGHT, STARTANGLE, (int) DEG(i)); // if we need to draw a 3d stripe ... if (i > 180.0) { for (int j = 1; j < Z_HEIGHT; ++j) paint.drawArc(leftMargin + LEFT, topMargin + BOTTOM - j, WIDTH, HEIGHT, STARTANGLE - 16 * 180, (int)(DEG(i - 180.0))); } } else { // if the filesystem is unmounted... font.setWeight(QFont::Bold); paint.setFont(font); paint.drawText(leftMargin + 10, topMargin + 60, i18n("Not mounted.")); } } else { // if the widget is in empty situation... } } //////////////////////////////////////////////////////////////////////////////// KRPie::KRPie(KIO::filesize_t _totalSize, QWidget *parent) : QWidget(parent), totalSize(_totalSize) { slices.push_back(KRPieSlice(100, Qt::yellow, "DEFAULT")); sizeLeft = totalSize; resize(300, 300); } void KRPie::paintEvent(QPaintEvent *) { QPainter paint(this); // now create the slices float sAngle = STARTANGLE; for (int ndx = 0; ndx != slices.count(); ndx++) { paint.setBrush(slices[ndx].getColor()); paint.setPen(slices[ndx].getColor()); // angles are negative to create a clock-wise drawing of slices float angle = -(slices[ndx].getPerct() / 100 * 360) * 16; for (int i = 1; i < Z_HEIGHT; ++i) paint.drawPie(LEFT, BOTTOM + i, WIDTH, HEIGHT, (int) sAngle, (int) angle); sAngle += angle; } paint.setPen(Qt::yellow); // pen paint.setBrush(Qt::yellow); // fill // for (int i=1; i * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRSPECIALWIDGETS_H #define KRSPECIALWIDGETS_H // QtCore #include // QtGui #include #include #include #include // QtWidgets #include #include #include #include class KRPieSlice; class KRPie : public QWidget { Q_OBJECT public: explicit KRPie(KIO::filesize_t _totalSize, QWidget *parent = nullptr); void addSlice(KIO::filesize_t size, QString label); protected: void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; private: QList slices; KIO::filesize_t totalSize, sizeLeft; static QColor colors[ 12 ]; }; class KRFSDisplay : public QWidget { Q_OBJECT public: // this constructor is used for a mounted filesystem KRFSDisplay(QWidget *parent, QString _alias, QString _realName, KIO::filesize_t _total, KIO::filesize_t _free); // this one is for an unmounted/supermount file system KRFSDisplay(QWidget *parent, QString _alias, QString _realName, bool sm = false); // the last one is used inside MountMan(R), when no filesystem is selected explicit KRFSDisplay(QWidget *parent); inline void setTotalSpace(KIO::filesize_t t) { totalSpace = t; } inline void setFreeSpace(KIO::filesize_t t) { freeSpace = t; } inline void setAlias(QString a) { alias = std::move(a); } inline void setRealName(QString r) { realName = std::move(r); } inline void setMounted(bool m) { mounted = m; } inline void setEmpty(bool e) { empty = e; } inline void setSupermount(bool s) { supermount = s; } protected: void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE; private: KIO::filesize_t totalSpace, freeSpace; QString alias, realName; bool mounted, empty, supermount; }; class KRPieSlice { public: KRPieSlice(float _perct, QColor _color, QString _label) : perct(_perct), color(std::move(_color)), label(std::move(_label)) {} inline QColor getColor() { return color; } inline float getPerct() { return perct; } inline QString getLabel() { return label; } inline void setPerct(float _perct) { perct = _perct; } inline void setLabel(QString _label) { label = std::move(_label); } private: float perct; QColor color; QString label; }; #endif diff --git a/krusader/Dialogs/krspwidgets.cpp b/krusader/Dialogs/krspwidgets.cpp index 9017ad0a..667dd3b3 100644 --- a/krusader/Dialogs/krspwidgets.cpp +++ b/krusader/Dialogs/krspwidgets.cpp @@ -1,258 +1,258 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krspwidgets.h" #include "../krglobal.h" #include "../icon.h" #include "../Filter/filtertabs.h" #include "../GUI/krlistwidget.h" // QtCore #include // QtGui #include // QtWidgets #include #include #include #include #include #include // missing ? #include #include #include #include #include ///////////////////// initiation of the static members //////////////////////// QStringList KRSpWidgets::maskList; /////////////////////////////////////////////////////////////////////////////// KRSpWidgets::KRSpWidgets() = default; KRQuery KRSpWidgets::getMask(const QString& caption, bool nameOnly, QWidget * parent) { if (!nameOnly) { return FilterTabs::getQuery(parent); } else { QPointer p = new KRMaskChoiceSub(parent); p->setWindowTitle(caption); p->exec(); QString selection = p->selection->currentText(); delete p; if (selection.isEmpty()) { return KRQuery(); } else { return KRQuery(selection); } } } /////////////////////////// newFTP //////////////////////////////////////// QUrl KRSpWidgets::newFTP() { QPointer p = new newFTPSub(); p->exec(); QString uri = p->url->currentText(); if (uri.isEmpty()) { delete p; return QUrl(); // empty url } QString protocol = p->prefix->currentText(); protocol.truncate(protocol.length() - 3); // remove the trailing :// QString username = p->username->text().simplified(); QString password = p->password->text().simplified(); int uriStart = uri.lastIndexOf('@'); /* lets the user enter user and password in the URI field */ if (uriStart != -1) { QString uriUser = uri.left(uriStart); QString uriPsw; uri = uri.mid(uriStart + 1); int pswStart = uriUser.indexOf(':'); /* getting the password name from the URL */ if (pswStart != -1) { uriPsw = uriUser.mid(pswStart + 1); uriUser = uriUser.left(pswStart); } if (!uriUser.isEmpty()) { /* handling the ftp proxy username and password also */ username = username.isEmpty() ? uriUser : username + '@' + uriUser; } if (!uriPsw.isEmpty()) { /* handling the ftp proxy username and password also */ password = password.isEmpty() ? uriPsw : password + '@' + uriPsw; } } QString host = uri; /* separating the hostname and path from the uri */ QString path; int pathStart = uri.indexOf("/"); if (pathStart != -1) { path = host.mid(pathStart); host = host.left(pathStart); } /* setting the parameters of the URL */ QUrl url; url.setScheme(protocol); url.setHost(host); url.setPath(path); if (protocol == "ftp" || protocol == "fish" || protocol == "sftp") { url.setPort(p->port->cleanText().toInt()); } if (!username.isEmpty()) { url.setUserName(username); } if (!password.isEmpty()) { url.setPassword(password); } delete p; return url; } newFTPSub::newFTPSub() : newFTPGUI(nullptr) { url->setFocus(); setGeometry(krMainWindow->x() + krMainWindow->width() / 2 - width() / 2, krMainWindow->y() + krMainWindow->height() / 2 - height() / 2, width(), height()); } void newFTPSub::accept() { url->addToHistory(url->currentText()); // save the history and completion list when the history combo is // destroyed KConfigGroup group(krConfig, "Private"); QStringList list = url->completionObject()->items(); group.writeEntry("newFTP Completion list", list); list = url->historyItems(); group.writeEntry("newFTP History list", list); QString protocol = prefix->currentText(); group.writeEntry("newFTP Protocol", protocol); newFTPGUI::accept(); } void newFTPSub::reject() { url->lineEdit()->setText(""); newFTPGUI::reject(); } /////////////////////////// KRMaskChoiceSub /////////////////////////////// KRMaskChoiceSub::KRMaskChoiceSub(QWidget * parent) : KRMaskChoice(parent) { PixmapLabel1->setPixmap(Icon("edit-select").pixmap(32)); label->setText(i18n("Enter a selection:")); // the predefined selections list KConfigGroup group(krConfig, "Private"); QStringList lst = group.readEntry("Predefined Selections", QStringList()); if (lst.size() > 0) preSelections->addItems(lst); // the combo-box tweaks selection->setDuplicatesEnabled(false); selection->addItems(KRSpWidgets::maskList); selection->lineEdit()->setText("*"); selection->lineEdit()->selectAll(); selection->setFocus(); } void KRMaskChoiceSub::reject() { selection->clear(); KRMaskChoice::reject(); } void KRMaskChoiceSub::accept() { bool add = true; // make sure we don't have that already for (int i = 0; i != KRSpWidgets::maskList.count(); i++) if (KRSpWidgets::maskList[ i ].simplified() == selection->currentText().simplified()) { // break if we found one such as this add = false; break; } if (add) KRSpWidgets::maskList.insert(0, selection->currentText().toLocal8Bit()); // write down the predefined selections list QStringList list; for (int j = 0; j != preSelections->count(); j++) { QListWidgetItem *i = preSelections->item(j); list.append(i->text()); } KConfigGroup group(krConfig, "Private"); group.writeEntry("Predefined Selections", list); KRMaskChoice::accept(); } void KRMaskChoiceSub::addSelection() { QString temp = selection->currentText(); bool itemExists = false; // check if the selection already exists for (int j = 0; j != preSelections->count(); j++) { QListWidgetItem *i = preSelections->item(j); if (i->text() == temp) { itemExists = true; break; } } if (!temp.isEmpty() && !itemExists) { preSelections->addItem(selection->currentText()); preSelections->update(); } } void KRMaskChoiceSub::deleteSelection() { delete preSelections->currentItem(); preSelections->update(); } void KRMaskChoiceSub::clearSelections() { preSelections->clear(); preSelections->update(); } void KRMaskChoiceSub::acceptFromList(QListWidgetItem *i) { selection->addItem(i->text(), 0); accept(); } void KRMaskChoiceSub::currentItemChanged(QListWidgetItem *i) { if (i) selection->setEditText(i->text()); } diff --git a/krusader/Dialogs/krspwidgets.h b/krusader/Dialogs/krspwidgets.h index 3de8cd38..d53bb4eb 100644 --- a/krusader/Dialogs/krspwidgets.h +++ b/krusader/Dialogs/krspwidgets.h @@ -1,83 +1,83 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRSPWIDGETS_H #define KRSPWIDGETS_H // QtCore #include // QtGui #include #include #include #include "krmaskchoice.h" #include "newftpgui.h" #include "../FileSystem/krquery.h" class KRMaskChoiceSub; class KRSpWidgets { friend class KRMaskChoiceSub; public: KRSpWidgets(); static KRQuery getMask(const QString& caption, bool nameOnly = false, QWidget * parent = 0); // get file-mask for (un)selecting files static QUrl newFTP(); private: static QStringList maskList; // used by KRMaskChoiceSub }; /////////////////////////// newFTPSub /////////////////////////////////////// class newFTPSub : public newFTPGUI { public: newFTPSub(); protected: void reject() Q_DECL_OVERRIDE; void accept() Q_DECL_OVERRIDE; }; /////////////////////////// KRMaskChoiceSub ///////////////////////////////// // Inherits KRMaskChoice's generated code to fully implement the functions // ///////////////////////////////////////////////////////////////////////////// class KRMaskChoiceSub : public KRMaskChoice { public: explicit KRMaskChoiceSub(QWidget * parent = 0); public slots: void addSelection() Q_DECL_OVERRIDE; void deleteSelection() Q_DECL_OVERRIDE; void clearSelections() Q_DECL_OVERRIDE; void acceptFromList(QListWidgetItem *i) Q_DECL_OVERRIDE; void currentItemChanged(QListWidgetItem *i) Q_DECL_OVERRIDE; protected: void reject() Q_DECL_OVERRIDE; void accept() Q_DECL_OVERRIDE; }; #endif diff --git a/krusader/Dialogs/krsqueezedtextlabel.cpp b/krusader/Dialogs/krsqueezedtextlabel.cpp index 100f9bf6..2a1464b9 100644 --- a/krusader/Dialogs/krsqueezedtextlabel.cpp +++ b/krusader/Dialogs/krsqueezedtextlabel.cpp @@ -1,102 +1,102 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "krsqueezedtextlabel.h" // QtGui #include #include // QtWidgets #include #include #include #include KrSqueezedTextLabel::KrSqueezedTextLabel(QWidget *parent): KSqueezedTextLabel(parent), _index(-1), _length(-1) { setAutoFillBackground(true); } KrSqueezedTextLabel::~KrSqueezedTextLabel() = default; void KrSqueezedTextLabel::mousePressEvent(QMouseEvent *e) { e->ignore(); emit clicked(e); } void KrSqueezedTextLabel::squeezeTextToLabel(int index, int length) { if (index == -1 || length == -1) KSqueezedTextLabel::squeezeTextToLabel(); else { QString sqtext = fullText; QFontMetrics fm(fontMetrics()); int labelWidth = size().width(); int textWidth = fm.width(sqtext); if (textWidth > labelWidth) { int avgCharSize = textWidth / sqtext.length(); int numOfExtraChars = (textWidth - labelWidth) / avgCharSize; int delta; // remove as much as possible from the left, and then from the right if (index > 3) { delta = qMin(index, numOfExtraChars); numOfExtraChars -= delta; sqtext.replace(0, delta, "..."); } if (numOfExtraChars > 0 && ((int)sqtext.length() > length + 3)) { delta = qMin(numOfExtraChars, (int)sqtext.length() - (length + 3)); sqtext.replace(sqtext.length() - delta, delta, "..."); } QLabel::setText(sqtext); setToolTip(QString()); setToolTip(fullText); } else { QLabel::setText(fullText); setToolTip(QString()); QToolTip::hideText(); } } } void KrSqueezedTextLabel::setText(const QString &text, int index, int length) { _index = index; _length = length; fullText = text; KSqueezedTextLabel::setText(fullText); squeezeTextToLabel(_index, _length); } void KrSqueezedTextLabel::paintEvent(QPaintEvent * e) { KSqueezedTextLabel::paintEvent(e); } diff --git a/krusader/Dialogs/krsqueezedtextlabel.h b/krusader/Dialogs/krsqueezedtextlabel.h index a55a2970..e753d755 100644 --- a/krusader/Dialogs/krsqueezedtextlabel.h +++ b/krusader/Dialogs/krsqueezedtextlabel.h @@ -1,70 +1,70 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KRSQUEEZEDTEXTLABEL_H #define KRSQUEEZEDTEXTLABEL_H // QtGui #include #include #include #include class QMouseEvent; class QDragEnterEvent; class QPaintEvent; /** This class overloads KSqueezedTextLabel and simply adds a clicked signal, so that users will be able to click the label and switch focus between panels. NEW: a special setText() method allows to choose which part of the string should be displayed (example: make sure that search results won't be cut out) */ class KrSqueezedTextLabel : public KSqueezedTextLabel { Q_OBJECT public: explicit KrSqueezedTextLabel(QWidget *parent = nullptr); ~KrSqueezedTextLabel() override; public slots: void setText(const QString &text, int index = -1, int length = -1); signals: void clicked(QMouseEvent *); /**< emitted when someone clicks on the label */ protected: void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE { squeezeTextToLabel(_index, _length); } void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE; void paintEvent(QPaintEvent * e) Q_DECL_OVERRIDE; void squeezeTextToLabel(int index = -1, int length = -1); QString fullText; private: int _index, _length; }; #endif diff --git a/krusader/Dialogs/kurllistrequester.cpp b/krusader/Dialogs/kurllistrequester.cpp index f5dfacbe..9ec3d14d 100644 --- a/krusader/Dialogs/kurllistrequester.cpp +++ b/krusader/Dialogs/kurllistrequester.cpp @@ -1,187 +1,187 @@ /***************************************************************************** * Copyright (C) 2005 Csaba Karai * - * Copyright (C) 2005-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2005-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "kurllistrequester.h" #include "../FileSystem/filesystem.h" #include "../icon.h" // QtGui #include #include #include // QtWidgets #include #include #include #include #include #include #define DELETE_ITEM_ID 100 KURLListRequester::KURLListRequester(Mode requestMode, QWidget *parent) : QWidget(parent), mode(requestMode) { // Creating the widget auto *urlListRequesterGrid = new QGridLayout(this); urlListRequesterGrid->setSpacing(0); urlListRequesterGrid->setContentsMargins(0, 0, 0, 0); urlLineEdit = new KLineEdit(this); urlListRequesterGrid->addWidget(urlLineEdit, 0, 0); urlAddBtn = new QToolButton(this); urlAddBtn->setText(""); urlAddBtn->setIcon(Icon("arrow-down")); urlListRequesterGrid->addWidget(urlAddBtn, 0, 1); urlBrowseBtn = new QToolButton(this); urlBrowseBtn->setText(""); urlBrowseBtn->setIcon(Icon("folder")); urlListRequesterGrid->addWidget(urlBrowseBtn, 0, 2); urlListBox = new KrListWidget(this); urlListBox->setSelectionMode(QAbstractItemView::ExtendedSelection); urlListRequesterGrid->addWidget(urlListBox, 1, 0, 1, 3); // add shell completion completion.setMode(KUrlCompletion::FileCompletion); urlLineEdit->setCompletionObject(&completion); // connection table connect(urlAddBtn, &QToolButton::clicked, this, &KURLListRequester::slotAdd); connect(urlBrowseBtn, &QToolButton::clicked, this, &KURLListRequester::slotBrowse); connect(urlLineEdit, &KLineEdit::returnPressed, this, &KURLListRequester::slotAdd); connect(urlListBox, &KrListWidget::itemRightClicked, this, &KURLListRequester::slotRightClicked); connect(urlLineEdit, &KLineEdit::textChanged, this, &KURLListRequester::changed); } void KURLListRequester::slotAdd() { QString text = urlLineEdit->text().simplified(); if (text.length()) { QString error; emit checkValidity(text, error); if (!error.isNull()) KMessageBox::error(this, error); else { urlListBox->addItem(text); urlLineEdit->clear(); emit changed(); } } } void KURLListRequester::slotBrowse() { QUrl url; switch (mode) { case RequestFiles: url = QFileDialog::getOpenFileUrl(this); break; case RequestDirs: url = QFileDialog::getExistingDirectoryUrl(this); break; } if (!url.isEmpty()) urlLineEdit->setText(url.toDisplayString(QUrl::PreferLocalFile)); urlLineEdit->setFocus(); } void KURLListRequester::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Delete) { if (urlListBox->hasFocus()) { deleteSelectedItems(); return; } } QWidget::keyPressEvent(e); } void KURLListRequester::deleteSelectedItems() { QList selectedItems = urlListBox->selectedItems(); for (QListWidgetItem *item : selectedItems) delete item; emit changed(); } void KURLListRequester::slotRightClicked(QListWidgetItem *item, const QPoint &pos) { if (item == nullptr) return; QMenu popupMenu(this); QAction * menuAction = popupMenu.addAction(i18n("Delete")); if (menuAction == popupMenu.exec(pos)) { if (item->isSelected()) deleteSelectedItems(); else { delete item; emit changed(); } } } QList KURLListRequester::urlList() { QStringList lines; const QString lineEditText = urlLineEdit->text().simplified(); if (!lineEditText.isEmpty()) { lines.append(lineEditText); } for (int i = 0; i != urlListBox->count(); i++) { QListWidgetItem *item = urlListBox->item(i); lines.append(item->text().simplified()); } QList urls; for (QString text : lines) { QString error; emit checkValidity(text, error); if (error.isNull()) urls.append(QUrl::fromUserInput(text, QString(), QUrl::AssumeLocalFile)); } return urls; } void KURLListRequester::setUrlList(const QList &urlList) { urlLineEdit->clear(); urlListBox->clear(); for (const QUrl& url : urlList) { urlListBox->addItem(url.toDisplayString(QUrl::PreferLocalFile)); } emit changed(); } diff --git a/krusader/Dialogs/kurllistrequester.h b/krusader/Dialogs/kurllistrequester.h index 5499e413..af43d446 100644 --- a/krusader/Dialogs/kurllistrequester.h +++ b/krusader/Dialogs/kurllistrequester.h @@ -1,78 +1,78 @@ /***************************************************************************** * Copyright (C) 2005 Csaba Karai * - * Copyright (C) 2005-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2005-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef KURLLISTREQUESTER_H #define KURLLISTREQUESTER_H // QtGui #include // QtWidgets #include #include #include #include #include "../GUI/krlistwidget.h" /** * Widget for letting the user define a list of URLs. */ class KURLListRequester : public QWidget { Q_OBJECT public: enum Mode { RequestFiles, RequestDirs }; explicit KURLListRequester(Mode requestMode, QWidget *parent = nullptr); QList urlList(); void setUrlList(const QList &); KLineEdit *lineEdit() { return urlLineEdit; } KrListWidget *listBox() { return urlListBox; } void setCompletionDir(const QUrl &dir) { completion.setDir(dir); } signals: void checkValidity(QString &text, QString &error); void changed(); protected slots: void slotAdd(); void slotBrowse(); void slotRightClicked(QListWidgetItem *, const QPoint &); protected: void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; void deleteSelectedItems(); Mode mode; KLineEdit *urlLineEdit; KrListWidget *urlListBox; QToolButton *urlAddBtn; QToolButton *urlBrowseBtn; KUrlCompletion completion; }; #endif /* __KURLLISTREQUESTER_H__ */ diff --git a/krusader/Dialogs/newftpgui.cpp b/krusader/Dialogs/newftpgui.cpp index 24a306d6..af597a0e 100644 --- a/krusader/Dialogs/newftpgui.cpp +++ b/krusader/Dialogs/newftpgui.cpp @@ -1,203 +1,203 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2009 Fathi Boudra * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "newftpgui.h" // QtCore #include #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include #include #include #include "../krglobal.h" #include "../icon.h" #define SIZE_MINIMUM QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed) const QStringList sProtocols = QStringList() << QStringLiteral("ftp") << QStringLiteral("ftps") << QStringLiteral("sftp") << QStringLiteral("fish") << QStringLiteral("nfs") << QStringLiteral("smb") << QStringLiteral("webdav") << QStringLiteral("svn") << QStringLiteral("svn+file") << QStringLiteral("svn+http") << QStringLiteral("svn+https") << QStringLiteral("svn+ssh"); /** * Constructs a newFTPGUI which is a child of 'parent', * with the name 'name' and widget flags set to 'f' */ newFTPGUI::newFTPGUI(QWidget* parent) : QDialog(parent) { setModal(true); setWindowTitle(i18n("New Network Connection")); resize(500, 240); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); QSizePolicy policy(QSizePolicy::Preferred, QSizePolicy::Preferred); policy.setHeightForWidth(sizePolicy().hasHeightForWidth()); setSizePolicy(policy); iconLabel = new QLabel(this); iconLabel->setPixmap(Icon("network-wired").pixmap(32)); iconLabel->setSizePolicy(SIZE_MINIMUM); aboutLabel = new QLabel(i18n("About to connect to..."), this); QFont font(aboutLabel->font()); font.setBold(true); aboutLabel->setFont(font); protocolLabel = new QLabel(i18n("Protocol:"), this); hostLabel = new QLabel(i18n("Host:"), this); portLabel = new QLabel(i18n("Port:"), this); prefix = new KComboBox(this); prefix->setObjectName(QString::fromUtf8("protocol")); prefix->setSizePolicy(SIZE_MINIMUM); url = new KHistoryComboBox(this); url->setMaxCount(50); url->setMinimumContentsLength(10); const QStringList availableProtocols = KProtocolInfo::protocols(); for (const QString& protocol : sProtocols) { if (availableProtocols.contains(protocol)) prefix->addItem(protocol + QStringLiteral("://")); } // load the history and completion list after creating the history combo KConfigGroup group(krConfig, "Private"); QStringList list = group.readEntry("newFTP Completion list", QStringList()); url->completionObject()->setItems(list); list = group.readEntry("newFTP History list", QStringList()); url->setHistoryItems(list); // Select last used protocol const QString lastUsedProtocol = group.readEntry("newFTP Protocol", QString()); if(!lastUsedProtocol.isEmpty()) { prefix->setCurrentItem(lastUsedProtocol); } port = new QSpinBox(this); port->setMaximum(65535); port->setValue(21); port->setSizePolicy(SIZE_MINIMUM); usernameLabel = new QLabel(i18n("Username:"), this); username = new KLineEdit(this); passwordLabel = new QLabel(i18n("Password:"), this); password = new KLineEdit(this); password->setEchoMode(QLineEdit::Password); auto *horizontalLayout = new QHBoxLayout(); horizontalLayout->addWidget(iconLabel); horizontalLayout->addWidget(aboutLabel); auto *gridLayout = new QGridLayout(); gridLayout->addWidget(protocolLabel, 0, 0, 1, 1); gridLayout->addWidget(hostLabel, 0, 1, 1, 1); gridLayout->addWidget(portLabel, 0, 2, 1, 1); gridLayout->addWidget(prefix, 1, 0, 1, 1); gridLayout->addWidget(url, 1, 1, 1, 1); gridLayout->addWidget(port, 1, 2, 1, 1); gridLayout->addWidget(usernameLabel, 2, 0, 1, 1); gridLayout->addWidget(username, 3, 0, 1, 3); gridLayout->addWidget(passwordLabel, 4, 0, 1, 1); gridLayout->addWidget(password, 5, 0, 1, 3); auto *widgetLayout = new QGridLayout(); widgetLayout->addLayout(horizontalLayout, 0, 0, 1, 1); widgetLayout->addLayout(gridLayout, 1, 0, 1, 1); mainLayout->addLayout(widgetLayout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setText(i18n("&Connect")); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &newFTPGUI::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &newFTPGUI::reject); connect(prefix, QOverload::of(&KComboBox::activated), this, &newFTPGUI::slotTextChanged); connect(url, QOverload::of(&KHistoryComboBox::activated), url, &KHistoryComboBox::addToHistory); if(!lastUsedProtocol.isEmpty()) { // update the port field slotTextChanged(lastUsedProtocol); } setTabOrder(url, username); setTabOrder(username, password); setTabOrder(password, prefix); } /** * Destroys the object and frees any allocated resources */ newFTPGUI::~newFTPGUI() { // no need to delete child widgets, Qt does it all for us } void newFTPGUI::slotTextChanged(const QString &string) { if (string.startsWith(QLatin1String("ftp")) || string.startsWith(QLatin1String("sftp")) || string.startsWith(QLatin1String("fish"))) { if (port->value() == 21 || port->value() == 22) { port->setValue(string.startsWith(QLatin1String("ftp")) ? 21 : 22); } port->setEnabled(true); } else { port->setEnabled(false); } } /** * Main event handler. Reimplemented to handle application font changes */ bool newFTPGUI::event(QEvent *ev) { bool ret = QDialog::event(ev); if (ev->type() == QEvent::ApplicationFontChange) { QFont font(aboutLabel->font()); font.setBold(true); aboutLabel->setFont(font); } return ret; } diff --git a/krusader/Dialogs/newftpgui.h b/krusader/Dialogs/newftpgui.h index 4e20c40e..ddbeef6b 100644 --- a/krusader/Dialogs/newftpgui.h +++ b/krusader/Dialogs/newftpgui.h @@ -1,68 +1,68 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * Copyright (C) 2009 Fathi Boudra * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef NEWFTPGUI_H #define NEWFTPGUI_H // QtWidgets #include #include #include #include #include #include /** * The "New Network Connection" dialog */ class newFTPGUI : public QDialog { Q_OBJECT public: explicit newFTPGUI(QWidget *parent = nullptr); ~newFTPGUI() override; KComboBox* prefix; KHistoryComboBox* url; QSpinBox* port; KLineEdit* username; KLineEdit* password; protected: bool event(QEvent *) Q_DECL_OVERRIDE; private slots: void slotTextChanged(const QString &); private: QLabel* iconLabel; QLabel* aboutLabel; QLabel* protocolLabel; QLabel* passwordLabel; QLabel* hostLabel; QLabel* usernameLabel; QLabel* portLabel; }; #endif diff --git a/krusader/Dialogs/packgui.cpp b/krusader/Dialogs/packgui.cpp index 61c2ea51..059b5e0a 100644 --- a/krusader/Dialogs/packgui.cpp +++ b/krusader/Dialogs/packgui.cpp @@ -1,127 +1,127 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "packgui.h" #include "../krglobal.h" #include "../defaults.h" // QtCore #include // QtWidgets #include #include #include #include #include #include #include #include #include #define PS(x) lst.contains(x)>0 // clear the statics first QString PackGUI::filename = nullptr; QString PackGUI::destination = nullptr; QString PackGUI::type = nullptr; QMap PackGUI::extraProps; PackGUI::PackGUI(const QString& defaultName, const QString& defaultPath, int noOfFiles, const QString& filename) : PackGUIBase(nullptr) { // first, fill the WhatToPack textfield with information if (noOfFiles == 1) TextLabel1->setText(i18n("Pack %1", filename)); else TextLabel1->setText(i18np("Pack %1 file", "Pack %1 files", noOfFiles)); // now, according to the Konfigurator, fill the combobox with the information // about what kind of packing we can do KConfigGroup group(krConfig, "Archives"); QStringList lst = group.readEntry("Supported Packers", QStringList()); // now, clear the type combo and begin... typeData->clear(); if (PS("tar")) typeData->addItem("tar"); if (PS("tar") && PS("gzip")) typeData->addItem("tar.gz"); if (PS("tar") && PS("bzip2")) typeData->addItem("tar.bz2"); if (PS("tar") && PS("lzma")) typeData->addItem("tar.lzma"); if (PS("tar") && PS("xz")) typeData->addItem("tar.xz"); if (PS("zip")) typeData->addItem("zip"); if (PS("zip")) typeData->addItem("cbz"); if (PS("rar")) typeData->addItem("rar"); if (PS("rar")) typeData->addItem("cbr"); if (PS("lha")) typeData->addItem("lha"); if (PS("arj")) typeData->addItem("arj"); if (PS("7z")) typeData->addItem("7z"); // set the last used packer as the top one QString tmp = group.readEntry("lastUsedPacker", QString()); if (!tmp.isEmpty()) { for (int i = 0; i < typeData->count(); ++i) if (typeData->itemText(i) == tmp) { typeData->removeItem(i); typeData->insertItem(0, tmp); typeData->setCurrentIndex(0); break; } } checkConsistency(); // and go on with the normal stuff dirData->setText(defaultPath); nameData->setText(defaultName); nameData->setFocus(); if (typeData->count() == 0) // if no packers are available okButton->setEnabled(false); setGeometry(krMainWindow->x() + krMainWindow->width() / 2 - width() / 2, krMainWindow->y() + krMainWindow->height() / 2 - height() / 2, width(), height()); exec(); } void PackGUI::browse() { QString temp = QFileDialog::getExistingDirectory(nullptr, i18n("Please select a folder"), dirData->text()); if (!temp.isEmpty()) { dirData->setText(temp); } } void PackGUI::accept() { if (!extraProperties(extraProps)) return; filename = nameData->text(); destination = dirData->text(); type = typeData->currentText(); // write down the packer chosen, to be lastUsedPacker KConfigGroup group(krConfig, "Archives"); group.writeEntry("lastUsedPacker", type); krConfig->sync(); PackGUIBase::accept(); } void PackGUI::reject() { filename.clear(); destination.clear(); type.clear(); PackGUIBase::reject(); } diff --git a/krusader/Dialogs/packgui.h b/krusader/Dialogs/packgui.h index ed4ef376..b1d06b47 100644 --- a/krusader/Dialogs/packgui.h +++ b/krusader/Dialogs/packgui.h @@ -1,45 +1,45 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef PACKGUI_H #define PACKGUI_H #include "packguibase.h" class PackGUI : public PackGUIBase { Q_OBJECT public: PackGUI(const QString& defaultName, const QString& defaultPath, int noOfFiles, const QString& filename = ""); public slots: void browse() Q_DECL_OVERRIDE; protected slots: void accept() Q_DECL_OVERRIDE; void reject() Q_DECL_OVERRIDE; public: static QString filename, destination, type; static QMap extraProps; }; #endif diff --git a/krusader/Dialogs/packguibase.cpp b/krusader/Dialogs/packguibase.cpp index 8535484e..76d836aa 100644 --- a/krusader/Dialogs/packguibase.cpp +++ b/krusader/Dialogs/packguibase.cpp @@ -1,496 +1,496 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "packguibase.h" // QtCore #include // QtGui #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../defaults.h" #include "../krglobal.h" #include "../icon.h" /* * Constructs a PackGUIBase which is a child of 'parent', with the * name 'name' and widget flags set to 'f' * * The dialog will by default be modeless, unless you set 'modal' to * TRUE to construct a modal dialog. */ PackGUIBase::PackGUIBase(QWidget* parent) : QDialog(parent), expanded(false) { KConfigGroup group(krConfig, "Archives"); setModal(true); resize(430, 140); setWindowTitle(i18n("Pack")); grid = new QGridLayout(this); grid->setSpacing(6); grid->setContentsMargins(11, 11, 11, 11); hbox = new QHBoxLayout; hbox->setSpacing(6); hbox->setContentsMargins(0, 0, 0, 0); TextLabel3 = new QLabel(this); TextLabel3->setText(i18n("To archive")); hbox->addWidget(TextLabel3); nameData = new QLineEdit(this); hbox->addWidget(nameData); typeData = new QComboBox(this); typeData->setSizePolicy(QSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed)); connect(typeData, QOverload::of(&QComboBox::activated), this, &PackGUIBase::checkConsistency); connect(typeData, QOverload::of(&QComboBox::highlighted), this, &PackGUIBase::checkConsistency); hbox->addWidget(typeData); grid->addLayout(hbox, 1, 0); hbox_2 = new QHBoxLayout; hbox_2->setSpacing(6); hbox_2->setContentsMargins(0, 0, 0, 0); TextLabel5 = new QLabel(this); TextLabel5->setText(i18n("In folder")); hbox_2->addWidget(TextLabel5); dirData = new QLineEdit(this); hbox_2->addWidget(dirData); browseButton = new QToolButton(this); browseButton->setIcon(Icon("document-open")); hbox_2->addWidget(browseButton); auto* spacer = new QSpacerItem(48, 20, QSizePolicy::Fixed, QSizePolicy::Fixed); hbox_2->addItem(spacer); grid->addLayout(hbox_2, 2, 0); hbox_3 = new QHBoxLayout; hbox_3->setSpacing(6); hbox_3->setContentsMargins(0, 0, 0, 0); PixmapLabel1 = new QLabel(this); PixmapLabel1->setPixmap(Icon("package-x-generic").pixmap(32)); PixmapLabel1->setScaledContents(true); PixmapLabel1->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed)); hbox_3->addWidget(PixmapLabel1); TextLabel1 = new QLabel(this); TextLabel1->setText(i18n("Pack")); hbox_3->addWidget(TextLabel1); grid->addLayout(hbox_3, 0, 0); hbox_4 = new QHBoxLayout; hbox_4->setSpacing(6); hbox_4->setContentsMargins(0, 0, 0, 0); auto* spacer_3 = new QSpacerItem(20, 26, QSizePolicy::Fixed, QSizePolicy::Expanding); hbox_4->addItem(spacer_3); grid->addLayout(hbox_4, 3, 0); advancedWidget = new QWidget(this); hbox_5 = new QGridLayout(advancedWidget); hbox_5->setSpacing(6); hbox_5->setContentsMargins(0, 0, 0, 0); auto *compressLayout = new QVBoxLayout; compressLayout->setSpacing(6); compressLayout->setContentsMargins(0, 0, 0, 0); multipleVolume = new QCheckBox(i18n("Multiple volume archive"), advancedWidget); connect(multipleVolume, &QCheckBox::toggled, this, &PackGUIBase::checkConsistency); compressLayout->addWidget(multipleVolume, 0, nullptr); auto * volumeHbox = new QHBoxLayout; auto* spacer_5 = new QSpacerItem(20, 26, QSizePolicy::Fixed, QSizePolicy::Fixed); volumeHbox->addItem(spacer_5); TextLabel7 = new QLabel(i18n("Size:"), advancedWidget); volumeHbox->addWidget(TextLabel7); volumeSpinBox = new QSpinBox(advancedWidget); volumeSpinBox->setMinimum(1); volumeSpinBox->setMaximum(9999); volumeSpinBox->setValue(1440); volumeHbox->addWidget(volumeSpinBox); volumeUnitCombo = new QComboBox(advancedWidget); volumeUnitCombo->addItem("B"); volumeUnitCombo->addItem("KB"); volumeUnitCombo->addItem("MB"); volumeUnitCombo->setCurrentIndex(1); volumeHbox->addWidget(volumeUnitCombo); compressLayout->addLayout(volumeHbox); int level = group.readEntry("Compression level", _defaultCompressionLevel); setCompressionLevel = new QCheckBox(i18n("Set compression level"), advancedWidget); if (level != _defaultCompressionLevel) setCompressionLevel->setChecked(true); connect(setCompressionLevel, &QCheckBox::toggled, this, &PackGUIBase::checkConsistency); compressLayout->addWidget(setCompressionLevel, 0, nullptr); auto * sliderHbox = new QHBoxLayout; auto* spacer_6 = new QSpacerItem(20, 26, QSizePolicy::Fixed, QSizePolicy::Fixed); sliderHbox->addItem(spacer_6); QWidget * sliderVBoxWidget = new QWidget(advancedWidget); auto *sliderVBox = new QVBoxLayout(sliderVBoxWidget); compressionSlider = new QSlider(Qt::Horizontal, sliderVBoxWidget); compressionSlider->setMinimum(1); compressionSlider->setMaximum(9); compressionSlider->setPageStep(1); compressionSlider->setValue(level); compressionSlider->setTickPosition(QSlider::TicksBelow); sliderVBox->addWidget(compressionSlider); QWidget * minmaxWidget = new QWidget(sliderVBoxWidget); sliderVBox->addWidget(minmaxWidget); auto * minmaxHbox = new QHBoxLayout(minmaxWidget); minLabel = new QLabel(i18n("MIN"), minmaxWidget); maxLabel = new QLabel(i18n("MAX"), minmaxWidget); maxLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); minmaxHbox->addWidget(minLabel); minmaxHbox->addWidget(maxLabel); sliderHbox->addWidget(sliderVBoxWidget); compressLayout->addLayout(sliderHbox); compressLayout->addStretch(0); hbox_5->addLayout(compressLayout, 0, 0); QFrame *vline = new QFrame(advancedWidget); vline->setFrameStyle(QFrame::VLine | QFrame::Sunken); vline->setMinimumWidth(20); hbox_5->addWidget(vline, 0, 1); auto * passwordGrid = new QGridLayout; passwordGrid->setSpacing(6); passwordGrid->setContentsMargins(0, 0, 0, 0); TextLabel4 = new QLabel(advancedWidget); TextLabel4->setText(i18n("Password")); passwordGrid->addWidget(TextLabel4, 0, 0); password = new QLineEdit(advancedWidget); password->setEchoMode(QLineEdit::Password); connect(password, &QLineEdit::textChanged, this, &PackGUIBase::checkConsistency); passwordGrid->addWidget(password, 0, 1); TextLabel6 = new QLabel(advancedWidget); TextLabel6->setText(i18n("Again")); passwordGrid->addWidget(TextLabel6, 1, 0); passwordAgain = new QLineEdit(advancedWidget); passwordAgain->setEchoMode(QLineEdit::Password); connect(passwordAgain, &QLineEdit::textChanged, this, &PackGUIBase::checkConsistency); passwordGrid->addWidget(passwordAgain, 1, 1); auto *consistencyHbox = new QHBoxLayout; auto* spacer_cons = new QSpacerItem(48, 20, QSizePolicy::Expanding, QSizePolicy::Fixed); consistencyHbox->addItem(spacer_cons); passwordConsistencyLabel = new QLabel(advancedWidget); consistencyHbox->addWidget(passwordConsistencyLabel); passwordGrid->addLayout(consistencyHbox, 2, 0, 1, 2); encryptHeaders = new QCheckBox(i18n("Encrypt headers"), advancedWidget); passwordGrid->addWidget(encryptHeaders, 3, 0, 1, 2); auto* spacer_psw = new QSpacerItem(20, 20, QSizePolicy::Fixed, QSizePolicy::Expanding); passwordGrid->addItem(spacer_psw, 4, 0); hbox_5->addLayout(passwordGrid, 0, 2); hbox_7 = new QHBoxLayout; hbox_7->setSpacing(6); hbox_7->setContentsMargins(0, 0, 0, 0); TextLabel8 = new QLabel(i18n("Command line switches:"), advancedWidget); TextLabel8->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); hbox_7->addWidget(TextLabel8); commandLineSwitches = new KHistoryComboBox(advancedWidget); commandLineSwitches->setMaxCount(25); // remember 25 items commandLineSwitches->setDuplicatesEnabled(false); commandLineSwitches->setMinimumContentsLength(10); QStringList list = group.readEntry("Command Line Switches", QStringList()); commandLineSwitches->setHistoryItems(list); hbox_7->addWidget(commandLineSwitches); hbox_5->addLayout(hbox_7, 1, 0, 1, 3); advancedWidget->hide(); checkConsistency(); grid->addWidget(advancedWidget, 4, 0); hbox_6 = new QHBoxLayout; hbox_6->setSpacing(6); hbox_6->setContentsMargins(0, 0, 0, 0); advancedButton = new QPushButton(this); advancedButton->setText(i18n("&Advanced >>")); hbox_6->addWidget(advancedButton); auto* spacer_2 = new QSpacerItem(140, 20, QSizePolicy::Expanding, QSizePolicy::Fixed); hbox_6->addItem(spacer_2); okButton = new QPushButton(this); KStandardGuiItem::assign(okButton, KStandardGuiItem::Ok); okButton->setDefault(true); hbox_6->addWidget(okButton); cancelButton = new QPushButton(this); KStandardGuiItem::assign(cancelButton, KStandardGuiItem::Cancel); hbox_6->addWidget(cancelButton); grid->addLayout(hbox_6, 6, 0); // signals and slots connections connect(okButton, &QPushButton::clicked, this, &PackGUIBase::accept); connect(advancedButton, &QPushButton::clicked, this, &PackGUIBase::expand); connect(cancelButton, &QPushButton::clicked, this, &PackGUIBase::reject); connect(browseButton, &QToolButton::clicked, this, &PackGUIBase::browse); } /* * Destroys the object and frees any allocated resources */ PackGUIBase::~PackGUIBase() { // no need to delete child widgets, Qt does it all for us } void PackGUIBase::browse() { qWarning("PackGUIBase::browse(): Not implemented yet!"); } void PackGUIBase::expand() { expanded = !expanded; advancedButton->setText(expanded ? i18n("&Advanced <<") : i18n("&Advanced >>")); if (expanded) advancedWidget->show(); else { advancedWidget->hide(); layout()->activate(); QSize minSize = minimumSize(); resize(width(), minSize.height()); } show(); } void PackGUIBase::checkConsistency() { QPalette p = QGuiApplication::palette(); QPalette pal = passwordConsistencyLabel->palette(); if (password->text().isEmpty() && passwordAgain->text().isEmpty()) { pal.setColor(passwordConsistencyLabel->foregroundRole(), p.color(QPalette::Active, QPalette::Text)); passwordConsistencyLabel->setText(i18n("No password specified")); } else if (password->text() == passwordAgain->text()) { pal.setColor(passwordConsistencyLabel->foregroundRole(), p.color(QPalette::Active, QPalette::Text)); passwordConsistencyLabel->setText(i18n("The passwords are equal")); } else { pal.setColor(passwordConsistencyLabel->foregroundRole(), Qt::red); passwordConsistencyLabel->setText(i18n("The passwords are different")); } passwordConsistencyLabel->setPalette(pal); QString packer = typeData->currentText(); bool passworded = false; if (packer == "7z" || packer == "rar" || packer == "zip" || packer == "arj") passworded = true; passwordConsistencyLabel->setEnabled(passworded); password->setEnabled(passworded); passwordAgain->setEnabled(passworded); TextLabel4->setEnabled(passworded); TextLabel6->setEnabled(passworded); encryptHeaders->setEnabled(packer == "rar"); multipleVolume->setEnabled(packer == "rar" || packer == "arj"); bool volumeEnabled = multipleVolume->isEnabled() && multipleVolume->isChecked(); volumeSpinBox->setEnabled(volumeEnabled); volumeUnitCombo->setEnabled(volumeEnabled); TextLabel7->setEnabled(volumeEnabled); /* TODO */ setCompressionLevel->setEnabled(packer == "rar" || packer == "arj" || packer == "zip" || packer == "7z"); bool sliderEnabled = setCompressionLevel->isEnabled() && setCompressionLevel->isChecked(); compressionSlider->setEnabled(sliderEnabled); minLabel->setEnabled(sliderEnabled); maxLabel->setEnabled(sliderEnabled); } bool PackGUIBase::extraProperties(QMap & inMap) { inMap.clear(); KConfigGroup group(krConfig, "Archives"); if (password->isEnabled() && passwordAgain->isEnabled()) { if (password->text() != passwordAgain->text()) { KMessageBox::error(this, i18n("Cannot pack, the passwords are different.")); return false; } if (!password->text().isEmpty()) { inMap[ "Password" ] = password->text(); if (encryptHeaders->isEnabled() && encryptHeaders->isChecked()) inMap[ "EncryptHeaders" ] = '1'; } } if (multipleVolume->isEnabled() && multipleVolume->isChecked()) { KIO::filesize_t size = volumeSpinBox->value(); switch (volumeUnitCombo->currentIndex()) { case 2: size *= 1000; #if __GNUC__ >= 7 [[gnu::fallthrough]]; #endif case 1: size *= 1000; default: break; } if (size < 10000) { KMessageBox::error(this, i18n("Invalid volume size.")); return false; } QString sbuffer; sbuffer.sprintf("%llu", size); inMap[ "VolumeSize" ] = sbuffer; } if (setCompressionLevel->isEnabled() && setCompressionLevel->isChecked()) { inMap[ "CompressionLevel" ] = QString("%1").arg(compressionSlider->value()); int level = compressionSlider->value(); group.writeEntry("Compression level", level); } QString cmdArgs = commandLineSwitches->currentText().trimmed(); if (!cmdArgs.isEmpty()) { bool firstChar = true; QChar quote = QChar::Null; for (int i = 0; i < cmdArgs.length(); i++) { QChar ch(cmdArgs[ i ]); if (ch.isSpace()) continue; if (ch == quote) { quote = QChar::Null; continue; } if (firstChar && ch != QLatin1Char('-')) { KMessageBox::error(this, i18n("Invalid command line switch.\nA switch must start with '-'.")); return false; } firstChar = false; if (quote == QLatin1Char('"')) continue; if (quote == QChar::Null && (ch == QLatin1Char('\'') || ch == QLatin1Char('"'))) quote = ch; if (ch == QLatin1Char('\\')) { if (i == cmdArgs.length() - 1) { KMessageBox::error(this, i18n("Invalid command line switch.\nBackslashes cannot be the last character.")); return false; } i++; } } if (quote != QChar::Null) { KMessageBox::error(this, i18n("Invalid command line switch.\nUnclosed quotation mark.")); return false; } commandLineSwitches->addToHistory(cmdArgs); QStringList list = commandLineSwitches->historyItems(); group.writeEntry("Command Line Switches", list); inMap[ "CommandLineSwitches" ] = cmdArgs; } return true; } diff --git a/krusader/Dialogs/packguibase.h b/krusader/Dialogs/packguibase.h index daa297c8..6e2e03af 100644 --- a/krusader/Dialogs/packguibase.h +++ b/krusader/Dialogs/packguibase.h @@ -1,103 +1,103 @@ /***************************************************************************** * Copyright (C) 2000 Shie Erlich * * Copyright (C) 2000 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef PACKGUIBASE_H #define PACKGUIBASE_H // QtCore #include // QtWidgets #include #include #include #include #include class QHBoxLayout; class QGridLayout; class QCheckBox; class QComboBox; class QLabel; class QLineEdit; class QPushButton; class QToolButton; class QSpinBox; class QSlider; class KHistoryComboBox; class PackGUIBase : public QDialog { Q_OBJECT public: explicit PackGUIBase(QWidget* parent = nullptr); ~PackGUIBase() override; QLabel* TextLabel3; QLineEdit* nameData; QComboBox* typeData; QLabel* TextLabel5; QLineEdit* dirData; QToolButton* browseButton; QWidget* advancedWidget; QLabel* PixmapLabel1; QLabel* TextLabel1; QLabel* TextLabel4; QLabel* TextLabel6; QLabel* TextLabel7; QLabel* TextLabel8; QLabel* minLabel; QLabel* maxLabel; QLineEdit* password; QLineEdit* passwordAgain; QLabel* passwordConsistencyLabel; QPushButton* okButton; QPushButton* cancelButton; QPushButton* advancedButton; QCheckBox* encryptHeaders; QCheckBox* multipleVolume; QSpinBox* volumeSpinBox; QComboBox* volumeUnitCombo; QCheckBox* setCompressionLevel; QSlider* compressionSlider; KHistoryComboBox *commandLineSwitches; public slots: virtual void browse(); virtual bool extraProperties(QMap &); void expand(); void checkConsistency(); protected: QHBoxLayout* hbox; QHBoxLayout* hbox_2; QHBoxLayout* hbox_3; QHBoxLayout* hbox_4; QGridLayout* hbox_5; QHBoxLayout* hbox_6; QHBoxLayout* hbox_7; QGridLayout* grid; private: bool expanded; }; #endif // PACKGUIBASE_H diff --git a/krusader/Dialogs/percentalsplitter.cpp b/krusader/Dialogs/percentalsplitter.cpp index c4e40f59..61f01c63 100644 --- a/krusader/Dialogs/percentalsplitter.cpp +++ b/krusader/Dialogs/percentalsplitter.cpp @@ -1,82 +1,82 @@ /***************************************************************************** * Copyright (C) 2006 Csaba Karai * - * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2006-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "percentalsplitter.h" // QtCore #include // QtGui #include #include // QtWidgets #include #include #include #include PercentalSplitter::PercentalSplitter(QWidget * parent) : QSplitter(parent), label(nullptr), opaqueOldPos(-1) { connect(this, &PercentalSplitter::splitterMoved, this, &PercentalSplitter::slotSplitterMoved); } PercentalSplitter::~PercentalSplitter() = default; QString PercentalSplitter::toolTipString(int p) { QList values = sizes(); if (values.count() != 0) { int sum = 0; QListIterator it(values); while (it.hasNext()) sum += it.next(); if (sum == 0) sum++; auto percent = (int)(((double)p / (double)(sum)) * 10000. + 0.5); return QString("%1.%2%3").arg(percent / 100).arg((percent / 10) % 10).arg(percent % 10) + '%'; } return QString(); } void PercentalSplitter::slotSplitterMoved(int p, int index) { handle(index) -> setToolTip(toolTipString(p)); QToolTip::showText(QCursor::pos(), toolTipString(p) , this); } void PercentalSplitter::showEvent(QShowEvent * event) { QList values = sizes(); for (int i = 0; i != count(); i++) { int p = 0; for (int j = 0; j < i; j++) p += values[ j ]; handle(i) -> setToolTip(toolTipString(p)); } QSplitter::showEvent(event); } diff --git a/krusader/Dialogs/percentalsplitter.h b/krusader/Dialogs/percentalsplitter.h index 1bbe6bba..12e4dbe6 100644 --- a/krusader/Dialogs/percentalsplitter.h +++ b/krusader/Dialogs/percentalsplitter.h @@ -1,50 +1,50 @@ /***************************************************************************** * Copyright (C) 2006 Csaba Karai * - * Copyright (C) 2006-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2006-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef PERCENTALSPLITTER_H #define PERCENTALSPLITTER_H // QtWidgets #include #include class PercentalSplitter : public QSplitter { Q_OBJECT public: explicit PercentalSplitter(QWidget * parent = nullptr); ~PercentalSplitter() override; QString toolTipString(int p); protected: void showEvent(QShowEvent * event) Q_DECL_OVERRIDE; protected slots: void slotSplitterMoved(int pos, int index); private: QLabel * label; int opaqueOldPos; QPoint labelLocation; }; #endif /* __PERCENTAL_SPLITTER__ */ diff --git a/krusader/Dialogs/popularurls.cpp b/krusader/Dialogs/popularurls.cpp index 26ab7299..e46388b8 100644 --- a/krusader/Dialogs/popularurls.cpp +++ b/krusader/Dialogs/popularurls.cpp @@ -1,373 +1,373 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "popularurls.h" #include // QtCore #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include "../krglobal.h" #include "../icon.h" #include "../krslots.h" #include "../GUI/krtreewidget.h" #define STARTING_RANK 20 #define INCREASE 2 #define DECREASE 1 PopularUrls::PopularUrls(QObject *parent) : QObject(parent), head(nullptr), tail(nullptr), count(0) { dlg = new PopularUrlsDlg(); } PopularUrls::~PopularUrls() { clearList(); delete dlg; } void PopularUrls::clearList() { if (head) { UrlNodeP p = head, tmp; while (p) { tmp = p; p = p->next; delete tmp; } } ranks.clear(); head = tail = nullptr; } void PopularUrls::save() { KConfigGroup svr(krConfig, "Private"); // prepare the string list containing urls and int list with ranks QStringList urlList; QList rankList; UrlNodeP p = head; while (p) { urlList << p->url.toDisplayString(); rankList << p->rank; p = p->next; } svr.writeEntry("PopularUrls", urlList); svr.writeEntry("PopularUrlsRank", rankList); } void PopularUrls::load() { KConfigGroup svr(krConfig, "Private"); QStringList urlList = svr.readEntry("PopularUrls", QStringList()); QList rankList = svr.readEntry("PopularUrlsRank", QList()); if (urlList.count() != rankList.count()) { KMessageBox::error(krMainWindow, i18n("The saved 'Popular URLs' are invalid. The list will be cleared.")); return; } clearList(); count = 0; // iterate through both lists and QStringList::Iterator uit; QList::Iterator rit; for (uit = urlList.begin(), rit = rankList.begin(); uit != urlList.end() && rit != rankList.end(); ++uit, ++rit) { auto node = new UrlNode; node->url = QUrl(*uit); node->rank = *rit; appendNode(node); ranks.insert(*uit, node); } } // returns a url list with the 'max' top popular urls QList PopularUrls::getMostPopularUrls(int max) { // get at most 'max' urls QList list; UrlNodeP p = head; int tmp = 0; if (maxUrls < max) max = maxUrls; // don't give more than maxUrls while (p && tmp < max) { list << p->url; p = p->next; ++tmp; } return list; } // adds a url to the list, or increase rank of an existing url, making // sure to bump it up the list if needed void PopularUrls::addUrl(const QUrl& url) { QUrl tmpurl = url; tmpurl.setPassword(QString()); // make sure no passwords are permanently stored if (!tmpurl.path().endsWith('/')) // make a uniform trailing slash policy tmpurl.setPath(tmpurl.path() + '/'); UrlNodeP pnode; decreaseRanks(); if (!head) { // if the list is empty ... (assumes dict to be empty as well) pnode = new UrlNode; pnode->rank = STARTING_RANK; pnode->url = tmpurl; appendNode(pnode); ranks.insert(tmpurl.url(), head); } else { if (ranks.find(tmpurl.url()) == ranks.end()) { // is the added url new? if so, append it pnode = new UrlNode; pnode->rank = STARTING_RANK; pnode->url = tmpurl; appendNode(pnode); ranks.insert(tmpurl.url(), pnode); } else { pnode = ranks[ tmpurl.url()]; pnode->rank += INCREASE; } } // do we need to change location for this one? relocateIfNeeded(pnode); // too many urls? if (count > maxUrls) removeNode(tail); //dumpList(); } // checks if 'node' needs to be bumped-up the ranking list and does it if needed void PopularUrls::relocateIfNeeded(UrlNodeP node) { if (node->prev && (node->prev->rank < node->rank)) { // iterate until we find the correct place to put it UrlNodeP tmp = node->prev->prev; while (tmp) { if (tmp->rank >= node->rank) break; // found it! else tmp = tmp->prev; } // now, if tmp isn't null, we need to move node to tmp->next // else move it to become head removeNode(node); insertNode(node, tmp); } } // iterate over the list, decreasing each url's rank // this is very naive, but a 1..30 for loop is acceptable (i hope) void PopularUrls::decreaseRanks() { if (head) { UrlNodeP p = head; while (p) { if (p->rank - DECREASE >= 0) p->rank -= DECREASE; else p->rank = 0; p = p->next; } } } // removes a node from the list, but doesn't free memory! // note: this will be buggy in case the list becomes empty (which should never happen) void PopularUrls::removeNode(UrlNodeP node) { if (node->prev) { if (tail == node) tail = node->prev; node->prev->next = node->next; } if (node->next) { if (head == node) head = node->next; node->next->prev = node->prev; } --count; } void PopularUrls::insertNode(UrlNodeP node, UrlNodeP after) { if (!after) { // make node head node->next = head; node->prev = nullptr; head->prev = node; head = node; } else { if (tail == after) tail = node; node->prev = after; node->next = after->next; if (node->next) { after->next->prev = node; after->next = node; } } ++count; } // appends 'node' to the end of the list, collecting garbage if needed void PopularUrls::appendNode(UrlNodeP node) { if (!tail) { // creating the first element head = tail = node; node->prev = node->next = nullptr; } else { node->next = nullptr; node->prev = tail; tail->next = node; tail = node; } ++count; } void PopularUrls::dumpList() { UrlNodeP p = head; printf("====start %d====\n", count); while (p) { printf("%d : %s\n", p->rank, p->url.url().toLatin1().data()); p = p->next; } fflush(stdout); } void PopularUrls::showDialog() { QList list = getMostPopularUrls(maxUrls); dlg->run(list); if (dlg->result() == -1) return; SLOTS->refresh(list[dlg->result()]); //printf("running %s\n", list[dlg->result()].url().toLatin1());fflush(stdout); } // ===================================== PopularUrlsDlg ====================================== PopularUrlsDlg::PopularUrlsDlg(): QDialog(krMainWindow) { setWindowTitle(i18n("Popular URLs")); setWindowModality(Qt::WindowModal); auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); auto *layout = new QGridLayout; layout->setContentsMargins(0, 0, 0, 0); // listview to contain the urls urls = new KrTreeWidget(this); urls->header()->hide(); urls->setSortingEnabled(false); urls->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); // quick search search = new KTreeWidgetSearchLine(this, urls); QLabel *lbl = new QLabel(i18n("&Search:"), this); lbl->setBuddy(search); layout->addWidget(lbl, 0, 0); layout->addWidget(search, 0, 1); layout->addWidget(urls, 1, 0, 1, 2); mainLayout->addLayout(layout); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); setTabOrder(search, urls); setTabOrder((QWidget *)urls, buttonBox->button(QDialogButtonBox::Close)); connect(buttonBox, &QDialogButtonBox::rejected, this, &PopularUrlsDlg::reject); connect(urls, &KrTreeWidget::activated, this, &PopularUrlsDlg::slotItemSelected); connect(search, &KTreeWidgetSearchLine::hiddenChanged, this, &PopularUrlsDlg::slotVisibilityChanged); } void PopularUrlsDlg::slotItemSelected(const QModelIndex & ndx) { selection = ndx.row(); accept(); } void PopularUrlsDlg::slotVisibilityChanged() { // select the first visible item QList list = urls->selectedItems(); if (list.count() > 0 && !list[0]->isHidden()) return; urls->clearSelection(); urls->setCurrentItem(nullptr); QTreeWidgetItemIterator it(urls); while (*it) { if (!(*it)->isHidden()) { (*it)->setSelected(true); urls->setCurrentItem(*it); break; } it++; } } PopularUrlsDlg::~PopularUrlsDlg() { delete search; delete urls; } void PopularUrlsDlg::run(QList list) { // populate the listview urls->clear(); QList::Iterator it; QTreeWidgetItem * lastItem = nullptr; for (it = list.begin(); it != list.end(); ++it) { auto *item = new QTreeWidgetItem(urls, lastItem); lastItem = item; item->setText(0, (*it).isLocalFile() ? (*it).path() : (*it).toDisplayString()); item->setIcon(0, (*it).isLocalFile() ? Icon("folder") : Icon("folder-html")); } setMinimumSize(urls->sizeHint().width() + 45, 400); search->clear(); search->setFocus(); selection = -1; slotVisibilityChanged(); exec(); } diff --git a/krusader/Dialogs/popularurls.h b/krusader/Dialogs/popularurls.h index 67149375..6dad889e 100644 --- a/krusader/Dialogs/popularurls.h +++ b/krusader/Dialogs/popularurls.h @@ -1,111 +1,111 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef POPULARURLS_H #define POPULARURLS_H // QtCore #include #include #include // QtWidgets #include // the class holds a list of most popular links in a dual data structure // * linked list, with head and tail: for fast append/prepend support // * dictionary that maps urls to list nodes: to save the need to iterate // over the list in order to find the correct node for each new url // // also, the class holds a maximum number of urls. two variables affect this: // * maxUrls - the num. of urls the user can see // * hardLimit - the actual number of urls kept. // when the number of urls reaches hardLimit, a garbage collection is done and // the bottom (hardLimit-maxUrls) entries are removed from the list typedef struct _UrlNode* UrlNodeP; typedef struct _UrlNode { UrlNodeP prev; QUrl url; int rank; UrlNodeP next; } UrlNode; class PopularUrlsDlg; class PopularUrls : public QObject { Q_OBJECT public: explicit PopularUrls(QObject *parent = nullptr); ~PopularUrls() override; void save(); void load(); void addUrl(const QUrl& url); QList getMostPopularUrls(int max); public slots: void showDialog(); protected: // NOTE: the following methods append/insert/remove a node to the list // but NEVER free memory or allocate memory! void appendNode(UrlNodeP node); void insertNode(UrlNodeP node, UrlNodeP after); void removeNode(UrlNodeP node); void relocateIfNeeded(UrlNodeP node); void clearList(); void dumpList(); void decreaseRanks(); private: UrlNodeP head, tail; QHash ranks; // actually holds UrlNode* int count; static const int maxUrls = 30; PopularUrlsDlg *dlg; }; class KrTreeWidget; class KTreeWidgetSearchLine; class QModelIndex; class PopularUrlsDlg: public QDialog { Q_OBJECT public: PopularUrlsDlg(); ~PopularUrlsDlg() override; void run(QList list); // use this to open the dialog inline int result() const { return selection; } // returns index 0 - topmost, or -1 protected slots: void slotVisibilityChanged(); void slotItemSelected(const QModelIndex &); private: KrTreeWidget *urls; KTreeWidgetSearchLine *search; int selection; }; #endif diff --git a/krusader/DiskUsage/diskusage.cpp b/krusader/DiskUsage/diskusage.cpp index efdb2229..78f73828 100644 --- a/krusader/DiskUsage/diskusage.cpp +++ b/krusader/DiskUsage/diskusage.cpp @@ -1,1134 +1,1134 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "diskusage.h" // QtCore #include #include #include #include #include #include // QtGui #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dufilelight.h" #include "dulines.h" #include "dulistview.h" #include "filelightParts/Config.h" #include "../FileSystem/fileitem.h" #include "../FileSystem/filesystemprovider.h" #include "../FileSystem/krpermhandler.h" #include "../Panel/krpanel.h" #include "../Panel/panelfunc.h" #include "../defaults.h" #include "../krglobal.h" #include "../filelisticon.h" // these are the values that will exist in the menu #define DELETE_ID 90 #define EXCLUDE_ID 91 #define PARENT_DIR_ID 92 #define NEW_SEARCH_ID 93 #define REFRESH_ID 94 #define STEP_INTO_ID 95 #define INCLUDE_ALL_ID 96 #define VIEW_POPUP_ID 97 #define LINES_VIEW_ID 98 #define DETAILED_VIEW_ID 99 #define FILELIGHT_VIEW_ID 100 #define NEXT_VIEW_ID 101 #define PREVIOUS_VIEW_ID 102 #define ADDITIONAL_POPUP_ID 103 #define MAX_FILENUM 100 LoaderWidget::LoaderWidget(QWidget *parent) : QScrollArea(parent), cancelled(false) { QPalette palette = viewport()->palette(); palette.setColor(viewport()->backgroundRole(), Qt::white); viewport()->setPalette(palette); widget = new QWidget(parent); auto *loaderLayout = new QGridLayout(widget); loaderLayout->setSpacing(0); loaderLayout->setContentsMargins(0, 0, 0, 0); QFrame *loaderBox = new QFrame(widget); loaderBox->setFrameShape(QFrame::Box); loaderBox->setFrameShadow(QFrame::Sunken); loaderBox->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); loaderBox->setFrameStyle(QFrame::Panel + QFrame::Raised); loaderBox->setLineWidth(2); auto *synchGrid = new QGridLayout(loaderBox); synchGrid->setSpacing(6); synchGrid->setContentsMargins(11, 11, 11, 11); QLabel *titleLabel = new QLabel(i18n("Loading Usage Information"), loaderBox); titleLabel->setAlignment(Qt::AlignHCenter); synchGrid->addWidget(titleLabel, 0, 0, 1, 2); QLabel *filesLabel = new QLabel(i18n("Files:"), loaderBox); filesLabel->setFrameShape(QLabel::StyledPanel); filesLabel->setFrameShadow(QLabel::Sunken); synchGrid->addWidget(filesLabel, 1, 0); QLabel *directoriesLabel = new QLabel(i18n("Directories:"), loaderBox); directoriesLabel->setFrameShape(QLabel::StyledPanel); directoriesLabel->setFrameShadow(QLabel::Sunken); synchGrid->addWidget(directoriesLabel, 2, 0); QLabel *totalSizeLabel = new QLabel(i18n("Total Size:"), loaderBox); totalSizeLabel->setFrameShape(QLabel::StyledPanel); totalSizeLabel->setFrameShadow(QLabel::Sunken); synchGrid->addWidget(totalSizeLabel, 3, 0); files = new QLabel(loaderBox); files->setFrameShape(QLabel::StyledPanel); files->setFrameShadow(QLabel::Sunken); files->setAlignment(Qt::AlignRight); synchGrid->addWidget(files, 1, 1); directories = new QLabel(loaderBox); directories->setFrameShape(QLabel::StyledPanel); directories->setFrameShadow(QLabel::Sunken); directories->setAlignment(Qt::AlignRight); synchGrid->addWidget(directories, 2, 1); totalSize = new QLabel(loaderBox); totalSize->setFrameShape(QLabel::StyledPanel); totalSize->setFrameShadow(QLabel::Sunken); totalSize->setAlignment(Qt::AlignRight); synchGrid->addWidget(totalSize, 3, 1); int width; searchedDirectory = new KSqueezedTextLabel(loaderBox); searchedDirectory->setFrameShape(QLabel::StyledPanel); searchedDirectory->setFrameShadow(QLabel::Sunken); searchedDirectory->setMinimumWidth(width = QFontMetrics(searchedDirectory->font()).width("W") * 30); searchedDirectory->setMaximumWidth(width); synchGrid->addWidget(searchedDirectory, 4, 0, 1, 2); QFrame *line = new QFrame(loaderBox); line->setFrameStyle(QFrame::HLine | QFrame::Sunken); synchGrid->addWidget(line, 5, 0, 1, 2); QWidget *hboxWidget = new QWidget(loaderBox); auto * hbox = new QHBoxLayout(hboxWidget); auto* spacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding); hbox->addItem(spacer); auto *cancelButton = new QPushButton(hboxWidget); KStandardGuiItem::assign(cancelButton, KStandardGuiItem::Cancel); hbox->addWidget(cancelButton); synchGrid->addWidget(hboxWidget, 6, 1); loaderLayout->addWidget(loaderBox, 0, 0); setWidget(widget); setAlignment(Qt::AlignCenter); connect(cancelButton, &QPushButton::clicked, this, &LoaderWidget::slotCancelled); } void LoaderWidget::init() { cancelled = false; } void LoaderWidget::setCurrentURL(const QUrl &url) { searchedDirectory->setText(FileSystem::ensureTrailingSlash(url).toDisplayString(QUrl::PreferLocalFile)); } void LoaderWidget::setValues(int fileNum, int dirNum, KIO::filesize_t total) { files->setText(QString("%1").arg(fileNum)); directories->setText(QString("%1").arg(dirNum)); totalSize->setText(QString("%1").arg(KRpermHandler::parseSize(total).trimmed())); } void LoaderWidget::slotCancelled() { cancelled = true; } DiskUsage::DiskUsage(QString confGroup, QWidget *parent) : QStackedWidget(parent), currentDirectory(nullptr), root(nullptr), configGroup(std::move(confGroup)), loading(false), abortLoading(false), clearAfterAbort(false), deleting(false), searchFileSystem(nullptr) { listView = new DUListView(this); lineView = new DULines(this); filelightView = new DUFilelight(this); loaderView = new LoaderWidget(this); addWidget(listView); addWidget(lineView); addWidget(filelightView); addWidget(loaderView); setView(VIEW_LINES); Filelight::Config::read(); connect(&loadingTimer, &QTimer::timeout, this, &DiskUsage::slotLoadDirectory); } DiskUsage::~DiskUsage() { if (listView) // don't remove these lines. The module will crash at exit if removed delete listView; if (lineView) delete lineView; if (filelightView) delete filelightView; if (root) delete root; QHashIterator< File *, Properties * > lit(propertyMap); while (lit.hasNext()) delete lit.next().value(); } void DiskUsage::load(const QUrl &baseDir) { fileNum = dirNum = 0; currentSize = 0; emit status(i18n("Loading the disk usage information...")); clear(); baseURL = baseDir.adjusted(QUrl::StripTrailingSlash); root = new Directory(baseURL.fileName(), baseDir.toDisplayString(QUrl::PreferLocalFile)); directoryStack.clear(); parentStack.clear(); directoryStack.push(""); parentStack.push(root); if (searchFileSystem) { delete searchFileSystem; searchFileSystem = nullptr; } searchFileSystem = FileSystemProvider::instance().getFilesystem(baseDir); if (searchFileSystem == nullptr) { qWarning() << "could not get filesystem for directory=" << baseDir; loading = abortLoading = clearAfterAbort = false; emit loadFinished(false); return; } currentFileItem = nullptr; if (!loading) { viewBeforeLoad = activeView; setView(VIEW_LOADER); } loading = true; loaderView->init(); loaderView->setCurrentURL(baseURL); loaderView->setValues(fileNum, dirNum, currentSize); loadingTimer.setSingleShot(true); loadingTimer.start(0); } void DiskUsage::slotLoadDirectory() { if ((currentFileItem == nullptr && directoryStack.isEmpty()) || loaderView->wasCancelled() || abortLoading) { if (searchFileSystem) delete searchFileSystem; searchFileSystem = nullptr; currentFileItem = nullptr; setView(viewBeforeLoad); if (clearAfterAbort) clear(); else { calculateSizes(); changeDirectory(root); } emit loadFinished(!(loaderView->wasCancelled() || abortLoading)); loading = abortLoading = clearAfterAbort = false; } else if (loading) { for (int counter = 0; counter != MAX_FILENUM; counter ++) { if (currentFileItem == nullptr) { if (directoryStack.isEmpty()) break; dirToCheck = directoryStack.pop(); currentParent = parentStack.pop(); contentMap.insert(dirToCheck, currentParent); QUrl url = baseURL; if (!dirToCheck.isEmpty()) { url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + (dirToCheck)); } #ifdef BSD if (url.isLocalFile() && url.path().left(7) == "/procfs") break; #else if (url.isLocalFile() && url.path().left(5) == "/proc") break; #endif loaderView->setCurrentURL(url); if (!searchFileSystem->scanDir(url)) break; fileItems = searchFileSystem->fileItems(); dirNum++; currentFileItem = fileItems.isEmpty() ? 0 : fileItems.takeFirst(); } else { fileNum++; File *newItem = nullptr; QString mime = currentFileItem->getMime(); // fast == not using mimetype magic if (currentFileItem->isDir() && !currentFileItem->isSymLink()) { newItem = new Directory(currentParent, currentFileItem->getName(), dirToCheck, currentFileItem->getSize(), currentFileItem->getMode(), currentFileItem->getOwner(), currentFileItem->getGroup(), currentFileItem->getPerm(), currentFileItem->getModificationTime(), currentFileItem->isSymLink(), mime); directoryStack.push((dirToCheck.isEmpty() ? "" : dirToCheck + '/') + currentFileItem->getName()); parentStack.push(dynamic_cast(newItem)); } else { newItem = new File(currentParent, currentFileItem->getName(), dirToCheck, currentFileItem->getSize(), currentFileItem->getMode(), currentFileItem->getOwner(), currentFileItem->getGroup(), currentFileItem->getPerm(), currentFileItem->getModificationTime(), currentFileItem->isSymLink(), mime); currentSize += currentFileItem->getSize(); } currentParent->append(newItem); currentFileItem = fileItems.isEmpty() ? 0 : fileItems.takeFirst(); } } loaderView->setValues(fileNum, dirNum, currentSize); loadingTimer.setSingleShot(true); loadingTimer.start(0); } } void DiskUsage::stopLoad() { abortLoading = true; } void DiskUsage::close() { if (loading) { abortLoading = true; clearAfterAbort = true; } } void DiskUsage::dirUp() { if (currentDirectory != nullptr) { if (currentDirectory->parent() != nullptr) changeDirectory((Directory *)(currentDirectory->parent())); else { QUrl up = KIO::upUrl(baseURL); if (KMessageBox::questionYesNo(this, i18n("Stepping into the parent folder requires " "loading the content of the \"%1\" URL. Do you wish " "to continue?", up.toDisplayString(QUrl::PreferLocalFile)), i18n("Krusader::DiskUsage"), KStandardGuiItem::yes(), KStandardGuiItem::no(), "DiskUsageLoadParentDir" ) == KMessageBox::Yes) load(up); } } } Directory * DiskUsage::getDirectory(QString dir) { while (dir.endsWith('/')) dir.truncate(dir.length() - 1); if (dir.isEmpty()) return root; if (contentMap.find(dir) == contentMap.end()) return nullptr; return contentMap[ dir ]; } File * DiskUsage::getFile(const QString& path) { if (path.isEmpty()) return root; QString dir = path; int ndx = path.lastIndexOf('/'); QString file = path.mid(ndx + 1); if (ndx == -1) dir = ""; else dir.truncate(ndx); Directory *dirEntry = getDirectory(dir); if (dirEntry == nullptr) return nullptr; for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) if ((*it)->name() == file) return *it; return nullptr; } void DiskUsage::clear() { baseURL = QUrl(); emit clearing(); QHashIterator< File *, Properties * > lit(propertyMap); while (lit.hasNext()) delete lit.next().value(); propertyMap.clear(); contentMap.clear(); if (root) delete root; root = currentDirectory = nullptr; } int DiskUsage::calculateSizes(Directory *dirEntry, bool emitSig, int depth) { int changeNr = 0; if (dirEntry == nullptr) dirEntry = root; KIO::filesize_t own = 0, total = 0; for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) { File * item = *it; if (!item->isExcluded()) { if (item->isDir()) changeNr += calculateSizes(dynamic_cast(item), emitSig, depth + 1); else own += item->size(); total += item->size(); } } KIO::filesize_t oldOwn = dirEntry->ownSize(), oldTotal = dirEntry->size(); dirEntry->setSizes(total, own); if (dirEntry == currentDirectory) currentSize = total; if (emitSig && (own != oldOwn || total != oldTotal)) { emit changed(dirEntry); changeNr++; } if (depth == 0 && changeNr != 0) emit changeFinished(); return changeNr; } int DiskUsage::exclude(File *file, bool calcPercents, int depth) { int changeNr = 0; if (!file->isExcluded()) { file->exclude(true); emit changed(file); changeNr++; if (file->isDir()) { auto *dir = dynamic_cast(file); for (Iterator it = dir->iterator(); it != dir->end(); ++it) changeNr += exclude(*it, false, depth + 1); } } if (calcPercents) { calculateSizes(root, true); calculatePercents(true); createStatus(); } if (depth == 0 && changeNr != 0) emit changeFinished(); return changeNr; } int DiskUsage::include(Directory *dir, int depth) { int changeNr = 0; if (dir == nullptr) return 0; for (Iterator it = dir->iterator(); it != dir->end(); ++it) { File *item = *it; if (item->isDir()) changeNr += include(dynamic_cast(item), depth + 1); if (item->isExcluded()) { item->exclude(false); emit changed(item); changeNr++; } } if (depth == 0 && changeNr != 0) emit changeFinished(); return changeNr; } void DiskUsage::includeAll() { include(root); calculateSizes(root, true); calculatePercents(true); createStatus(); } int DiskUsage::del(File *file, bool calcPercents, int depth) { int deleteNr = 0; if (file == root) return 0; KConfigGroup gg(krConfig, "General"); bool trash = gg.readEntry("Move To Trash", _MoveToTrash); QUrl url = QUrl::fromLocalFile(file->fullPath()); if (calcPercents) { // now ask the user if he want to delete: KConfigGroup ga(krConfig, "Advanced"); if (ga.readEntry("Confirm Delete", _ConfirmDelete)) { QString s; KGuiItem b; if (trash && url.isLocalFile()) { s = i18nc("singularOnly", "Do you really want to move this item to the trash?"); b = KGuiItem(i18n("&Trash")); } else { s = i18nc("singularOnly", "Do you really want to delete this item?"); b = KStandardGuiItem::del(); } QStringList name; name.append(file->fullPath()); // show message // note: i'm using continue and not yes/no because the yes/no has cancel as default button if (KMessageBox::warningContinueCancelList(krMainWindow, s, name, i18n("Warning"), b) != KMessageBox::Continue) return 0; } emit status(i18n("Deleting %1...", file->name())); } if (file == currentDirectory) dirUp(); if (file->isDir()) { auto *dir = dynamic_cast(file); Iterator it; while ((it = dir->iterator()) != dir->end()) deleteNr += del(*it, false, depth + 1); QString path; for (const Directory *d = (Directory*)file; d != root && d && d->parent() != nullptr; d = d->parent()) { if (!path.isEmpty()) path = '/' + path; path = d->name() + path; } contentMap.remove(path); } emit deleted(file); deleteNr++; KIO::Job *job; if (trash) { job = KIO::trash(url); } else { job = KIO::del(QUrl::fromLocalFile(file->fullPath()), KIO::HideProgressInfo); } deleting = true; // during qApp->processEvent strange things can occur grabMouse(); // that's why we disable the mouse and keyboard events grabKeyboard(); job->exec(); delete job; releaseMouse(); releaseKeyboard(); deleting = false; ((Directory *)(file->parent()))->remove(file); delete file; if (depth == 0) createStatus(); if (calcPercents) { calculateSizes(root, true); calculatePercents(true); createStatus(); emit enteringDirectory(currentDirectory); } if (depth == 0 && deleteNr != 0) emit deleteFinished(); return deleteNr; } void * DiskUsage::getProperty(File *item, const QString& key) { QHash< File *, Properties *>::iterator itr = propertyMap.find(item); if (itr == propertyMap.end()) return nullptr; QHash::iterator it = (*itr)->find(key); if (it == (*itr)->end()) return nullptr; return it.value(); } void DiskUsage::addProperty(File *item, const QString& key, void * prop) { Properties *props; QHash< File *, Properties *>::iterator itr = propertyMap.find(item); if (itr == propertyMap.end()) { props = new Properties(); propertyMap.insert(item, props); } else props = *itr; props->insert(key, prop); } void DiskUsage::removeProperty(File *item, const QString& key) { QHash< File *, Properties *>::iterator itr = propertyMap.find(item); if (itr == propertyMap.end()) return; (*itr)->remove(key); if ((*itr)->count() == 0) propertyMap.remove(item); } void DiskUsage::createStatus() { Directory *dirEntry = currentDirectory; if (dirEntry == nullptr) return; QUrl url = baseURL; if (dirEntry != root) { url = url.adjusted(QUrl::StripTrailingSlash); url.setPath(url.path() + '/' + (dirEntry->directory())); } emit status(i18n("Current folder:%1, Total size:%2, Own size:%3", url.toDisplayString(QUrl::PreferLocalFile | QUrl::StripTrailingSlash), ' ' + KRpermHandler::parseSize(dirEntry->size()), ' ' + KRpermHandler::parseSize(dirEntry->ownSize()))); } void DiskUsage::changeDirectory(Directory *dir) { currentDirectory = dir; currentSize = dir->size(); calculatePercents(true, dir); createStatus(); emit enteringDirectory(dir); } Directory* DiskUsage::getCurrentDir() { return currentDirectory; } void DiskUsage::rightClickMenu(const QPoint & pos, File *fileItem, QMenu *addPopup, const QString& addPopupName) { QMenu popup(this); popup.setTitle(i18n("Disk Usage")); QHash actionHash; if (fileItem != nullptr) { QAction * actDelete = popup.addAction(i18n("Delete")); actionHash[ actDelete ] = DELETE_ID; actDelete->setShortcut(Qt::Key_Delete); QAction * actExclude = popup.addAction(i18n("Exclude")); actionHash[ actExclude ] = EXCLUDE_ID; actExclude->setShortcut(Qt::CTRL + Qt::Key_E); popup.addSeparator(); } QAction * myAct = popup.addAction(i18n("Up one folder")); actionHash[ myAct ] = PARENT_DIR_ID; myAct->setShortcut(Qt::SHIFT + Qt::Key_Up); myAct = popup.addAction(i18n("New search")); actionHash[ myAct ] = NEW_SEARCH_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_N); myAct = popup.addAction(i18n("Refresh")); actionHash[ myAct ] = REFRESH_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_R); myAct = popup.addAction(i18n("Include all")); actionHash[ myAct ] = INCLUDE_ALL_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_I); myAct = popup.addAction(i18n("Step into")); actionHash[ myAct ] = STEP_INTO_ID; myAct->setShortcut(Qt::SHIFT + Qt::Key_Down); popup.addSeparator(); if (addPopup != nullptr) { QAction * menu = popup.addMenu(addPopup); menu->setText(addPopupName); } QMenu viewPopup; myAct = viewPopup.addAction(i18n("Lines")); actionHash[ myAct ] = LINES_VIEW_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_L); myAct = viewPopup.addAction(i18n("Detailed")); actionHash[ myAct ] = DETAILED_VIEW_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_D); myAct = viewPopup.addAction(i18n("Filelight")); actionHash[ myAct ] = FILELIGHT_VIEW_ID; myAct->setShortcut(Qt::CTRL + Qt::Key_F); viewPopup.addSeparator(); myAct = viewPopup.addAction(i18n("Next")); actionHash[ myAct ] = NEXT_VIEW_ID; myAct->setShortcut(Qt::SHIFT + Qt::Key_Right); myAct = viewPopup.addAction(i18n("Previous")); actionHash[ myAct ] = PREVIOUS_VIEW_ID; myAct->setShortcut(Qt::SHIFT + Qt::Key_Left); QAction * menu = popup.addMenu(&viewPopup); menu->setText(i18n("View")); QAction * res = popup.exec(pos); if (actionHash.contains(res)) executeAction(actionHash[ res ], fileItem); } void DiskUsage::executeAction(int action, File * fileItem) { // check out the user's option switch (action) { case DELETE_ID: if (fileItem) del(fileItem); break; case EXCLUDE_ID: if (fileItem) exclude(fileItem); break; case PARENT_DIR_ID: dirUp(); break; case NEW_SEARCH_ID: emit newSearch(); break; case REFRESH_ID: load(baseURL); break; case INCLUDE_ALL_ID: includeAll(); break; case STEP_INTO_ID: { QString uri; if (fileItem && fileItem->isDir()) uri = fileItem->fullPath(); else uri = currentDirectory->fullPath(); ACTIVE_FUNC->openUrl(QUrl::fromLocalFile(uri)); } break; case LINES_VIEW_ID: setView(VIEW_LINES); break; case DETAILED_VIEW_ID: setView(VIEW_DETAILED); break; case FILELIGHT_VIEW_ID: setView(VIEW_FILELIGHT); break; case NEXT_VIEW_ID: setView((activeView + 1) % 3); break; case PREVIOUS_VIEW_ID: setView((activeView + 2) % 3); break; } // currentWidget()->setFocus(); } void DiskUsage::keyPressEvent(QKeyEvent *e) { if (activeView != VIEW_LOADER) { switch (e->key()) { case Qt::Key_E: if (e->modifiers() == Qt::ControlModifier) { executeAction(EXCLUDE_ID, getCurrentFile()); return; } break; case Qt::Key_D: if (e->modifiers() == Qt::ControlModifier) { executeAction(DETAILED_VIEW_ID); return; } break; case Qt::Key_F: if (e->modifiers() == Qt::ControlModifier) { executeAction(FILELIGHT_VIEW_ID); return; } break; case Qt::Key_I: if (e->modifiers() == Qt::ControlModifier) { executeAction(INCLUDE_ALL_ID); return; } break; case Qt::Key_L: if (e->modifiers() == Qt::ControlModifier) { executeAction(LINES_VIEW_ID); return; } break; case Qt::Key_N: if (e->modifiers() == Qt::ControlModifier) { executeAction(NEW_SEARCH_ID); return; } break; case Qt::Key_R: if (e->modifiers() == Qt::ControlModifier) { executeAction(REFRESH_ID); return; } break; case Qt::Key_Up: if (e->modifiers() == Qt::ShiftModifier) { executeAction(PARENT_DIR_ID); return; } break; case Qt::Key_Down: if (e->modifiers() == Qt::ShiftModifier) { executeAction(STEP_INTO_ID); return; } break; case Qt::Key_Left: if (e->modifiers() == Qt::ShiftModifier) { executeAction(PREVIOUS_VIEW_ID); return; } break; case Qt::Key_Right: if (e->modifiers() == Qt::ShiftModifier) { executeAction(NEXT_VIEW_ID); return; } break; case Qt::Key_Delete: if (!e->modifiers()) { executeAction(DELETE_ID, getCurrentFile()); return; } break; case Qt::Key_Plus: if (activeView == VIEW_FILELIGHT) { filelightView->zoomIn(); return; } break; case Qt::Key_Minus: if (activeView == VIEW_FILELIGHT) { filelightView->zoomOut(); return; } break; } } QStackedWidget::keyPressEvent(e); } QPixmap DiskUsage::getIcon(const QString& mime) { QPixmap icon; if (!QPixmapCache::find(mime, &icon)) { // get the icon. if (mime == "Broken Link !") // FIXME: this doesn't work anymore - the reported mimetype for a broken link is now "unknown" icon = FileListIcon("file-broken").pixmap(); else { QMimeDatabase db; QMimeType mt = db.mimeTypeForName(mime); if (mt.isValid()) icon = FileListIcon(mt.iconName()).pixmap(); else icon = FileListIcon("file-broken").pixmap(); } // insert it into the cache QPixmapCache::insert(mime, icon); } return icon; } int DiskUsage::calculatePercents(bool emitSig, Directory *dirEntry, int depth) { int changeNr = 0; if (dirEntry == nullptr) dirEntry = root; for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) { File *item = *it; if (!item->isExcluded()) { int newPerc; if (dirEntry->size() == 0 && item->size() == 0) newPerc = 0; else if (dirEntry->size() == 0) newPerc = -1; else newPerc = (int)((double)item->size() / (double)currentSize * 10000. + 0.5); int oldPerc = item->intPercent(); item->setPercent(newPerc); if (emitSig && newPerc != oldPerc) { emit changed(item); changeNr++; } if (item->isDir()) changeNr += calculatePercents(emitSig, dynamic_cast(item), depth + 1); } } if (depth == 0 && changeNr != 0) emit changeFinished(); return changeNr; } QString DiskUsage::getToolTip(File *item) { QMimeDatabase db; QMimeType mt = db.mimeTypeForName(item->mime()); QString mime; if (mt.isValid()) mime = mt.comment(); time_t tma = item->time(); struct tm* t = localtime((time_t *) & tma); QDateTime tmp(QDate(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday), QTime(t->tm_hour, t->tm_min)); QString date = QLocale().toString(tmp, QLocale::ShortFormat); QString str = "
" + "" + ""; if (item->isDir()) str += ""; str += "" + "" + "" + "
" + i18n("Name:") + "" + item->name() + "
" + i18n("Type:") + "" + mime + "
" + i18n("Size:") + "" + KRpermHandler::parseSize(item->size()) + "
" + i18n("Own size:") + "" + KRpermHandler::parseSize(item->ownSize()) + "
" + i18n("Last modified:") + "" + date + "
" + i18n("Permissions:") + "" + item->perm() + "
" + i18n("Owner:") + "" + item->owner() + " - " + item->group() + "
"; str.replace(' ', " "); return str; } void DiskUsage::setView(int view) { switch (view) { case VIEW_LINES: setCurrentWidget(lineView); break; case VIEW_DETAILED: setCurrentWidget(listView); break; case VIEW_FILELIGHT: setCurrentWidget(filelightView); break; case VIEW_LOADER: setCurrentWidget(loaderView); break; } // currentWidget()->setFocus(); emit viewChanged(activeView = view); } File * DiskUsage::getCurrentFile() { File * file = nullptr; switch (activeView) { case VIEW_LINES: file = lineView->getCurrentFile(); break; case VIEW_DETAILED: file = listView->getCurrentFile(); break; case VIEW_FILELIGHT: file = filelightView->getCurrentFile(); break; } return file; } bool DiskUsage::event(QEvent * e) { if (deleting) { // if we are deleting, disable the mouse and switch (e->type()) { // keyboard events case QEvent::MouseButtonPress: case QEvent::MouseButtonRelease: case QEvent::MouseButtonDblClick: case QEvent::MouseMove: case QEvent::KeyPress: case QEvent::KeyRelease: return true; default: break; } } if (e->type() == QEvent::ShortcutOverride) { auto* ke = (QKeyEvent*) e; if (ke->modifiers() == Qt::NoModifier || ke->modifiers() == Qt::KeypadModifier) { switch (ke->key()) { case Qt::Key_Delete: case Qt::Key_Plus: case Qt::Key_Minus: ke->accept(); break; } } else if (ke->modifiers() == Qt::ShiftModifier) { switch (ke->key()) { case Qt::Key_Left: case Qt::Key_Right: case Qt::Key_Up: case Qt::Key_Down: ke->accept(); break; } } else if (ke->modifiers() & Qt::ControlModifier) { switch (ke->key()) { case Qt::Key_D: case Qt::Key_E: case Qt::Key_F: case Qt::Key_I: case Qt::Key_L: case Qt::Key_N: case Qt::Key_R: ke->accept(); break; } } } return QStackedWidget::event(e); } diff --git a/krusader/DiskUsage/diskusage.h b/krusader/DiskUsage/diskusage.h index 3e7696db..aee3bd7f 100644 --- a/krusader/DiskUsage/diskusage.h +++ b/krusader/DiskUsage/diskusage.h @@ -1,209 +1,209 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef DISKUSAGE_H #define DISKUSAGE_H // QtCore #include #include #include #include #include // QtGui #include #include #include // QtWidgets #include #include #include #include #include #include "filelightParts/fileTree.h" #define VIEW_LINES 0 #define VIEW_DETAILED 1 #define VIEW_FILELIGHT 2 #define VIEW_LOADER 3 typedef QHash Properties; class DUListView; class DULines; class DUFilelight; class QMenu; class LoaderWidget; class FileItem; class FileSystem; class DiskUsage : public QStackedWidget { Q_OBJECT public: explicit DiskUsage(QString confGroup, QWidget *parent = 0); ~DiskUsage(); void load(const QUrl &dirName); void close(); void stopLoad(); bool isLoading() { return loading; } void setView(int view); int getActiveView() { return activeView; } Directory* getDirectory(QString path); File * getFile(const QString& path); QString getConfigGroup() { return configGroup; } void * getProperty(File *, const QString&); void addProperty(File *, const QString&, void *); void removeProperty(File *, const QString&); int exclude(File *file, bool calcPercents = true, int depth = 0); void includeAll(); int del(File *file, bool calcPercents = true, int depth = 0); QString getToolTip(File *); void rightClickMenu(const QPoint &, File *, QMenu * = 0, const QString& = QString()); void changeDirectory(Directory *dir); Directory* getCurrentDir(); File* getCurrentFile(); QPixmap getIcon(const QString& mime); QUrl getBaseURL() { return baseURL; } public slots: void dirUp(); void clear(); signals: void enteringDirectory(Directory *); void clearing(); void changed(File *); void changeFinished(); void deleted(File *); void deleteFinished(); void status(QString); void viewChanged(int); void loadFinished(bool); void newSearch(); protected slots: void slotLoadDirectory(); protected: QHash< QString, Directory * > contentMap; QHash< File *, Properties *> propertyMap; Directory* currentDirectory; KIO::filesize_t currentSize; virtual void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE; virtual bool event(QEvent *) Q_DECL_OVERRIDE; int calculateSizes(Directory *dir = 0, bool emitSig = false, int depth = 0); int calculatePercents(bool emitSig = false, Directory *dir = 0 , int depth = 0); int include(Directory *dir, int depth = 0); void createStatus(); void executeAction(int, File * = 0); QUrl baseURL; //< the base URL of loading DUListView *listView; DULines *lineView; DUFilelight *filelightView; LoaderWidget *loaderView; Directory *root; int activeView; QString configGroup; bool first; bool loading; bool abortLoading; bool clearAfterAbort; bool deleting; QStack directoryStack; QStack parentStack; FileSystem *searchFileSystem; FileItem *currentFileItem; QList fileItems; Directory *currentParent; QString dirToCheck; int fileNum; int dirNum; int viewBeforeLoad; QTimer loadingTimer; }; class LoaderWidget : public QScrollArea { Q_OBJECT public: explicit LoaderWidget(QWidget *parent = 0); void init(); void setCurrentURL(const QUrl &url); void setValues(int fileNum, int dirNum, KIO::filesize_t total); bool wasCancelled() { return cancelled; } public slots: void slotCancelled(); protected: QLabel *totalSize; QLabel *files; QLabel *directories; KSqueezedTextLabel *searchedDirectory; QWidget *widget; bool cancelled; }; #endif /* __DISK_USAGE_GUI_H__ */ diff --git a/krusader/DiskUsage/diskusagegui.cpp b/krusader/DiskUsage/diskusagegui.cpp index 9a38de44..f122bae3 100644 --- a/krusader/DiskUsage/diskusagegui.cpp +++ b/krusader/DiskUsage/diskusagegui.cpp @@ -1,242 +1,242 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "diskusagegui.h" // QtCore #include // QtGui #include // QtWidgets #include #include #include #include #include #include #include "../krglobal.h" #include "../icon.h" #include "../FileSystem/filesystem.h" #include "../Dialogs/krdialogs.h" DiskUsageGUI::DiskUsageGUI(const QUrl &openDir) : QDialog(nullptr), exitAtFailure(true) { setWindowTitle(i18n("Krusader::Disk Usage")); setAttribute(Qt::WA_DeleteOnClose); baseDirectory = openDir; auto *mainLayout = new QVBoxLayout; setLayout(mainLayout); auto *duGrid = new QGridLayout(); duGrid->setSpacing(6); duGrid->setContentsMargins(11, 11, 11, 11); QWidget *duTools = new QWidget(this); auto *duHBox = new QHBoxLayout(duTools); duHBox->setContentsMargins(0, 0, 0, 0); duTools->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); btnNewSearch = new QToolButton(duTools); btnNewSearch->setIcon(Icon("document-open")); duHBox->addWidget(btnNewSearch); btnNewSearch->setToolTip(i18n("Start new disk usage search")); btnRefresh = new QToolButton(duTools); btnRefresh->setIcon(Icon("view-refresh")); duHBox->addWidget(btnRefresh); btnRefresh->setToolTip(i18n("Refresh")); btnDirUp = new QToolButton(duTools); btnDirUp->setIcon(Icon("go-up")); duHBox->addWidget(btnDirUp); btnDirUp->setToolTip(i18n("Parent folder")); QWidget *separatorWidget = new QWidget(duTools); separatorWidget->setMinimumWidth(10); duHBox->addWidget(separatorWidget); btnLines = new QToolButton(duTools); btnLines->setIcon(Icon("view-list-details")); btnLines->setCheckable(true); duHBox->addWidget(btnLines); btnLines->setToolTip(i18n("Line view")); btnDetailed = new QToolButton(duTools); btnDetailed->setIcon(Icon("view-list-tree")); btnDetailed->setCheckable(true); duHBox->addWidget(btnDetailed); btnDetailed->setToolTip(i18n("Detailed view")); btnFilelight = new QToolButton(duTools); btnFilelight->setIcon(Icon("kr_diskusage")); btnFilelight->setCheckable(true); duHBox->addWidget(btnFilelight); btnFilelight->setToolTip(i18n("Filelight view")); QWidget *spacerWidget = new QWidget(duTools); duHBox->addWidget(spacerWidget); auto *hboxlayout = new QHBoxLayout(spacerWidget); auto* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Fixed); hboxlayout->addItem(spacer); duGrid->addWidget(duTools, 0, 0); diskUsage = new DiskUsage("DiskUsage", this); duGrid->addWidget(diskUsage, 1, 0); status = new KSqueezedTextLabel(this); duGrid->addWidget(status, 2, 0); mainLayout->addLayout(duGrid); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close); mainLayout->addWidget(buttonBox); connect(buttonBox, &QDialogButtonBox::rejected, this, &DiskUsageGUI::close); connect(diskUsage, &DiskUsage::status, this, &DiskUsageGUI::slotStatus); connect(diskUsage, &DiskUsage::viewChanged, this, &DiskUsageGUI::slotViewChanged); connect(diskUsage, &DiskUsage::newSearch, this, &DiskUsageGUI::askDir); connect(diskUsage, &DiskUsage::loadFinished, this, &DiskUsageGUI::slotLoadFinished); connect(btnNewSearch, &QToolButton::clicked, this, &DiskUsageGUI::askDir); connect(btnRefresh, &QToolButton::clicked, this, &DiskUsageGUI::slotLoadUsageInfo); connect(btnDirUp, &QToolButton::clicked, diskUsage, &DiskUsage::dirUp); connect(btnLines, &QToolButton::clicked, this, &DiskUsageGUI::slotSelectLinesView); connect(btnDetailed, &QToolButton::clicked, this, &DiskUsageGUI::slotSelectListView); connect(btnFilelight, &QToolButton::clicked, this, &DiskUsageGUI::slotSelectFilelightView); KConfigGroup group(krConfig, "DiskUsage"); int view = group.readEntry("View", VIEW_LINES); if (view < VIEW_LINES || view > VIEW_FILELIGHT) view = VIEW_LINES; diskUsage->setView(view); sizeX = group.readEntry("Window Width", QFontMetrics(font()).width("W") * 70); sizeY = group.readEntry("Window Height", QFontMetrics(font()).height() * 25); resize(sizeX, sizeY); if (group.readEntry("Window Maximized", false)) { setWindowState(windowState() | Qt::WindowMaximized); } } void DiskUsageGUI::askDirAndShow() { if (askDir()) { show(); } } void DiskUsageGUI::slotLoadFinished(bool result) { if (exitAtFailure && !result) { close(); } else { exitAtFailure = false; } } void DiskUsageGUI::enableButtons(bool isOn) { btnNewSearch->setEnabled(isOn); btnRefresh->setEnabled(isOn); btnDirUp->setEnabled(isOn); btnLines->setEnabled(isOn); btnDetailed->setEnabled(isOn); btnFilelight->setEnabled(isOn); } void DiskUsageGUI::resizeEvent(QResizeEvent *e) { if (!isMaximized()) { sizeX = e->size().width(); sizeY = e->size().height(); } QDialog::resizeEvent(e); } void DiskUsageGUI::closeEvent(QCloseEvent *event) { KConfigGroup group(krConfig, "DiskUsage"); group.writeEntry("Window Width", sizeX); group.writeEntry("Window Height", sizeY); group.writeEntry("Window Maximized", isMaximized()); group.writeEntry("View", diskUsage->getActiveView()); event->accept(); } void DiskUsageGUI::slotLoadUsageInfo() { diskUsage->load(baseDirectory); } void DiskUsageGUI::slotStatus(const QString& stat) { status->setText(stat); } void DiskUsageGUI::slotViewChanged(int view) { if (view == VIEW_LOADER) { enableButtons(false); return; } enableButtons(true); btnLines->setChecked(false); btnDetailed->setChecked(false); btnFilelight->setChecked(false); switch (view) { case VIEW_LINES: btnLines->setChecked(true); break; case VIEW_DETAILED: btnDetailed->setChecked(true); break; case VIEW_FILELIGHT: btnFilelight->setChecked(true); break; case VIEW_LOADER: break; } } bool DiskUsageGUI::askDir() { // ask the user for the copy destX const QUrl newDir = KChooseDir::getDir(i18n("Viewing the usage of folder:"), baseDirectory, baseDirectory); if (newDir.isEmpty()) return false; baseDirectory = newDir; QTimer::singleShot(0, this, &DiskUsageGUI::slotLoadUsageInfo); return true; } diff --git a/krusader/DiskUsage/diskusagegui.h b/krusader/DiskUsage/diskusagegui.h index 8313bc94..ca1ef302 100644 --- a/krusader/DiskUsage/diskusagegui.h +++ b/krusader/DiskUsage/diskusagegui.h @@ -1,87 +1,87 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef DISKUSAGEGUI_H #define DISKUSAGEGUI_H // QtCore #include // QtGui #include // QtWidgets #include #include #include #include #include "diskusage.h" class DiskUsageGUI : public QDialog { Q_OBJECT public: explicit DiskUsageGUI(const QUrl &openDir); ~DiskUsageGUI() override = default; void askDirAndShow(); protected slots: void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; protected: void resizeEvent(QResizeEvent *e) Q_DECL_OVERRIDE; private slots: bool askDir(); void slotLoadUsageInfo(); void slotStatus(const QString&); void slotSelectLinesView() { diskUsage->setView(VIEW_LINES); } void slotSelectListView() { diskUsage->setView(VIEW_DETAILED); } void slotSelectFilelightView() { diskUsage->setView(VIEW_FILELIGHT); } void slotViewChanged(int view); void slotLoadFinished(bool); private: void enableButtons(bool); DiskUsage *diskUsage; QUrl baseDirectory; KSqueezedTextLabel *status; QToolButton *btnNewSearch; QToolButton *btnRefresh; QToolButton *btnDirUp; QToolButton *btnLines; QToolButton *btnDetailed; QToolButton *btnFilelight; int sizeX; int sizeY; bool exitAtFailure; }; #endif /* __DISK_USAGE_GUI_H__ */ diff --git a/krusader/DiskUsage/dufilelight.cpp b/krusader/DiskUsage/dufilelight.cpp index 249bf6b9..5530570a 100644 --- a/krusader/DiskUsage/dufilelight.cpp +++ b/krusader/DiskUsage/dufilelight.cpp @@ -1,225 +1,225 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "dufilelight.h" #include "radialMap/radialMap.h" // QtGui #include #include // QtWidgets #include #include #include #define SCHEME_POPUP_ID 6730 DUFilelight::DUFilelight(DiskUsage *usage) : RadialMap::Widget(usage), diskUsage(usage), currentDir(nullptr), refreshNeeded(true) { // setFocusPolicy(Qt::StrongFocus); connect(diskUsage, &DiskUsage::enteringDirectory, this, &DUFilelight::slotDirChanged); connect(diskUsage, &DiskUsage::clearing, this, &DUFilelight::clear); connect(diskUsage, &DiskUsage::changed, this, &DUFilelight::slotChanged); connect(diskUsage, &DiskUsage::deleted, this, &DUFilelight::slotChanged); connect(diskUsage, &DiskUsage::changeFinished, this, &DUFilelight::slotRefresh); connect(diskUsage, &DiskUsage::deleteFinished, this, &DUFilelight::slotRefresh); connect(diskUsage, &DiskUsage::currentChanged, this, &DUFilelight::slotAboutToShow); } void DUFilelight::slotDirChanged(Directory *dir) { if (diskUsage->currentWidget() != this) return; if (currentDir != dir) { currentDir = dir; invalidate(false); create(dir); refreshNeeded = false; } } void DUFilelight::clear() { invalidate(false); currentDir = nullptr; } File * DUFilelight::getCurrentFile() { const RadialMap::Segment * focus = focusSegment(); if (!focus || focus->isFake() || focus->file() == currentDir) return nullptr; return (File *)focus->file(); } void DUFilelight::mousePressEvent(QMouseEvent *event) { if (event->button() == Qt::RightButton) { File * file = nullptr; const RadialMap::Segment * focus = focusSegment(); if (focus && !focus->isFake() && focus->file() != currentDir) file = (File *)focus->file(); QMenu filelightPopup; filelightPopup.addAction(i18n("Zoom In"), this, SLOT(zoomIn()), Qt::Key_Plus); filelightPopup.addAction(i18n("Zoom Out"), this, SLOT(zoomOut()), Qt::Key_Minus); QMenu schemePopup; schemePopup.addAction(i18n("Rainbow"), this, SLOT(schemeRainbow())); schemePopup.addAction(i18n("High Contrast"), this, SLOT(schemeHighContrast())); schemePopup.addAction(i18n("KDE"), this, SLOT(schemeKDE())); filelightPopup.addMenu(&schemePopup); schemePopup.setTitle(i18n("Scheme")); filelightPopup.addAction(i18n("Increase contrast"), this, SLOT(increaseContrast())); filelightPopup.addAction(i18n("Decrease contrast"), this, SLOT(decreaseContrast())); QAction * act = filelightPopup.addAction(i18n("Use anti-aliasing"), this, SLOT(changeAntiAlias())); act->setCheckable(true); act->setChecked(Filelight::Config::antiAliasFactor > 1); act = filelightPopup.addAction(i18n("Show small files"), this, SLOT(showSmallFiles())); act->setCheckable(true); act->setChecked(Filelight::Config::showSmallFiles); act = filelightPopup.addAction(i18n("Vary label font sizes"), this, SLOT(varyLabelFontSizes())); act->setCheckable(true); act->setChecked(Filelight::Config::varyLabelFontSizes); filelightPopup.addAction(i18n("Minimum font size"), this, SLOT(minFontSize())); diskUsage->rightClickMenu(event->globalPos(), file, &filelightPopup, i18n("Filelight")); return; } else if (event->button() == Qt::LeftButton) { const RadialMap::Segment * focus = focusSegment(); if (focus && !focus->isFake() && focus->file() == currentDir) { diskUsage->dirUp(); return; } else if (focus && !focus->isFake() && focus->file()->isDir()) { diskUsage->changeDirectory((Directory *)focus->file()); return; } } RadialMap::Widget::mousePressEvent(event); } void DUFilelight::setScheme(Filelight::MapScheme scheme) { Filelight::Config::scheme = scheme; Filelight::Config::write(); slotRefresh(); } void DUFilelight::increaseContrast() { if ((Filelight::Config::contrast += 10) > 100) Filelight::Config::contrast = 100; Filelight::Config::write(); slotRefresh(); } void DUFilelight::decreaseContrast() { if ((Filelight::Config::contrast -= 10) > 100) Filelight::Config::contrast = 0; Filelight::Config::write(); slotRefresh(); } void DUFilelight::changeAntiAlias() { Filelight::Config::antiAliasFactor = 1 + (Filelight::Config::antiAliasFactor == 1); Filelight::Config::write(); slotRefresh(); } void DUFilelight::showSmallFiles() { Filelight::Config::showSmallFiles = !Filelight::Config::showSmallFiles; Filelight::Config::write(); slotRefresh(); } void DUFilelight::varyLabelFontSizes() { Filelight::Config::varyLabelFontSizes = !Filelight::Config::varyLabelFontSizes; Filelight::Config::write(); slotRefresh(); } void DUFilelight::minFontSize() { bool ok = false; int result = QInputDialog::getInt(this, i18n("Krusader::Filelight"), i18n("Minimum font size"), (int)Filelight::Config::minFontPitch, 1, 100, 1, &ok); if (ok) { Filelight::Config::minFontPitch = (uint)result; Filelight::Config::write(); slotRefresh(); } } void DUFilelight::slotAboutToShow(int ndx) { QWidget *widget = diskUsage->widget(ndx); if (widget == this && (diskUsage->getCurrentDir() != currentDir || refreshNeeded)) { refreshNeeded = false; if ((currentDir = diskUsage->getCurrentDir()) != nullptr) { invalidate(false); create(currentDir); } } } void DUFilelight::slotRefresh() { if (diskUsage->currentWidget() != this) return; refreshNeeded = false; if (currentDir && currentDir == diskUsage->getCurrentDir()) { invalidate(false); create(currentDir); } } void DUFilelight::slotChanged(File *) { if (!refreshNeeded) refreshNeeded = true; } diff --git a/krusader/DiskUsage/dufilelight.h b/krusader/DiskUsage/dufilelight.h index bc5eef0c..17b2b74f 100644 --- a/krusader/DiskUsage/dufilelight.h +++ b/krusader/DiskUsage/dufilelight.h @@ -1,79 +1,79 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef DUFILELIGHT_H #define DUFILELIGHT_H // QtGui #include #include "diskusage.h" #include "radialMap/widget.h" #include "filelightParts/Config.h" class DUFilelight : public RadialMap::Widget { Q_OBJECT public: explicit DUFilelight(DiskUsage *usage); File * getCurrentFile(); public slots: void slotDirChanged(Directory *); void clear(); void slotChanged(File *); void slotRefresh(); protected slots: void slotAboutToShow(int); void schemeRainbow() { setScheme(Filelight::Rainbow); } void schemeHighContrast() { setScheme(Filelight::HighContrast); } void schemeKDE() { setScheme(Filelight::KDE); } void increaseContrast(); void decreaseContrast(); void changeAntiAlias(); void showSmallFiles(); void varyLabelFontSizes(); void minFontSize(); protected: void mousePressEvent(QMouseEvent*) Q_DECL_OVERRIDE; void setScheme(Filelight::MapScheme); DiskUsage *diskUsage; Directory *currentDir; private: bool refreshNeeded; }; #endif /* __DU_FILELIGHT_H__ */ diff --git a/krusader/DiskUsage/dulines.cpp b/krusader/DiskUsage/dulines.cpp index b40b2f70..63e19e44 100644 --- a/krusader/DiskUsage/dulines.cpp +++ b/krusader/DiskUsage/dulines.cpp @@ -1,534 +1,534 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "dulines.h" #include "../icon.h" #include "../krglobal.h" #include "../FileSystem/krpermhandler.h" // QtCore #include // QtGui #include #include #include #include #include #include // QtWidgets #include #include #include #include #include #include #include class DULinesItemDelegate : public QItemDelegate { public: explicit DULinesItemDelegate(QObject *parent = nullptr) : QItemDelegate(parent) {} void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE { QItemDelegate::paint(painter, option, index); QVariant value = index.data(Qt::UserRole); if (value.isValid()) { QString text = value.toString(); value = index.data(Qt::DisplayRole); QString display; if (value.isValid()) display = value.toString(); QSize iconSize; value = index.data(Qt::DecorationRole); if (value.isValid()) iconSize = qvariant_cast(value).actualSize(option.decorationSize); painter->save(); painter->setClipRect(option.rect); QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; if (cg == QPalette::Normal && !(option.state & QStyle::State_Active)) cg = QPalette::Inactive; if (option.state & QStyle::State_Selected) { painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); } else { painter->setPen(option.palette.color(cg, QPalette::Text)); } QFont fnt = option.font; fnt.setItalic(true); painter->setFont(fnt); QFontMetrics fm(fnt); QString renderedText = text; int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin); int pos = 3 * textMargin + option.fontMetrics.width(display) + iconSize.width(); bool truncd = false; QRect rct = option.rect; if (rct.width() > pos) { rct.setX(rct.x() + pos); if (fm.width(renderedText) > rct.width()) { truncd = true; int points = fm.width("..."); while (!renderedText.isEmpty() && (fm.width(renderedText) + points > rct.width())) renderedText.truncate(renderedText.length() - 1); renderedText += "..."; } painter->drawText(rct, Qt::AlignLeft, renderedText); } else truncd = true; if (truncd) ((QAbstractItemModel *)index.model())->setData(index, QVariant(display + " " + text), Qt::ToolTipRole); else ((QAbstractItemModel *)index.model())->setData(index, QVariant(), Qt::ToolTipRole); painter->restore(); } } }; class DULinesItem : public QTreeWidgetItem { public: DULinesItem(DiskUsage *diskUsageIn, File *fileItem, QTreeWidget * parent, const QString& label1, const QString& label2, const QString& label3) : QTreeWidgetItem(parent), diskUsage(diskUsageIn), file(fileItem) { setText(0, label1); setText(1, label2); setText(2, label3); setTextAlignment(1, Qt::AlignRight); } DULinesItem(DiskUsage *diskUsageIn, File *fileItem, QTreeWidget * parent, QTreeWidgetItem * after, const QString& label1, const QString& label2, const QString& label3) : QTreeWidgetItem(parent, after), diskUsage(diskUsageIn), file(fileItem) { setText(0, label1); setText(1, label2); setText(2, label3); setTextAlignment(1, Qt::AlignRight); } bool operator<(const QTreeWidgetItem &other) const Q_DECL_OVERRIDE { int column = treeWidget() ? treeWidget()->sortColumn() : 0; if (text(0) == "..") return true; const auto *compWith = dynamic_cast< const DULinesItem * >(&other); if (compWith == nullptr) return false; switch (column) { case 0: case 1: return file->size() > compWith->file->size(); default: return text(column) < other.text(column); } } inline File * getFile() { return file; } private: DiskUsage *diskUsage; File *file; }; DULines::DULines(DiskUsage *usage) : KrTreeWidget(usage), diskUsage(usage), refreshNeeded(false), started(false) { setItemDelegate(itemDelegate = new DULinesItemDelegate()); setAllColumnsShowFocus(true); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setIndentation(10); int defaultSize = QFontMetrics(font()).width("W"); QStringList labels; labels << i18n("Line View"); labels << i18n("Percent"); labels << i18n("Name"); setHeaderLabels(labels); header()->setSectionResizeMode(QHeaderView::Interactive); KConfigGroup group(krConfig, diskUsage->getConfigGroup()); showFileSize = group.readEntry("L Show File Size", true); if (group.hasKey("L State")) header()->restoreState(group.readEntry("L State", QByteArray())); else { setColumnWidth(0, defaultSize * 20); setColumnWidth(1, defaultSize * 6); setColumnWidth(2, defaultSize * 20); } setStretchingColumn(0); header()->setSortIndicatorShown(true); sortItems(1, Qt::AscendingOrder); // toolTip = new DULinesToolTip( diskUsage, viewport(), this ); connect(diskUsage, &DiskUsage::enteringDirectory, this, &DULines::slotDirChanged); connect(diskUsage, &DiskUsage::clearing, this, &DULines::clear); connect(header(), &QHeaderView::sectionResized, this, &DULines::sectionResized); connect(this, &DULines::itemRightClicked, this, &DULines::slotRightClicked); connect(diskUsage, &DiskUsage::changed, this, &DULines::slotChanged); connect(diskUsage, &DiskUsage::deleted, this, &DULines::slotDeleted); started = true; } DULines::~DULines() { KConfigGroup group(krConfig, diskUsage->getConfigGroup()); group.writeEntry("L State", header()->saveState()); delete itemDelegate; } bool DULines::event(QEvent * event) { switch (event->type()) { case QEvent::ToolTip: { auto *he = dynamic_cast(event); if (viewport()) { QPoint pos = viewport()->mapFromGlobal(he->globalPos()); QTreeWidgetItem * item = itemAt(pos); int column = columnAt(pos.x()); if (item && column == 1) { File *fileItem = ((DULinesItem *)item)->getFile(); QToolTip::showText(he->globalPos(), diskUsage->getToolTip(fileItem), this); return true; } } } break; default: break; } return KrTreeWidget::event(event); } void DULines::slotDirChanged(Directory *dirEntry) { clear(); QTreeWidgetItem * lastItem = nullptr; if (!(dirEntry->parent() == nullptr)) { lastItem = new QTreeWidgetItem(this); lastItem->setText(0, ".."); lastItem->setIcon(0, Icon("go-up")); lastItem->setFlags(lastItem->flags() & (~Qt::ItemIsSelectable)); } int maxPercent = -1; for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) { File *item = *it; if (!item->isExcluded() && item->intPercent() > maxPercent) maxPercent = item->intPercent(); } for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) { File *item = *it; QString fileName = item->name(); if (lastItem == nullptr) lastItem = new DULinesItem(diskUsage, item, this, "", item->percent() + " ", fileName); else lastItem = new DULinesItem(diskUsage, item, this, lastItem, "", item->percent() + " ", fileName); if (item->isExcluded()) lastItem->setHidden(true); int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; lastItem->setIcon(2, diskUsage->getIcon(item->mime())); lastItem->setData(0, Qt::DecorationRole, createPixmap(item->intPercent(), maxPercent, header()->sectionSize(0) - 2 * textMargin)); if (showFileSize) lastItem->setData(2, Qt::UserRole, " [" + KIO::convertSize(item->size()) + ']'); QSize size = lastItem->sizeHint(0); size.setWidth(16); lastItem->setSizeHint(0, size); } if (topLevelItemCount() > 0) { setCurrentItem(topLevelItem(0)); } } QPixmap DULines::createPixmap(int percent, int maxPercent, int maxWidth) { if (percent < 0 || percent > maxPercent || maxWidth < 2 || maxPercent == 0) return QPixmap(); maxWidth -= 2; int actualWidth = maxWidth * percent / maxPercent; if (actualWidth == 0) return QPixmap(); QPen pen; pen.setColor(Qt::black); QPainter painter; int size = QFontMetrics(font()).height() - 2; QRect rect(0, 0, actualWidth, size); QRect frameRect(0, 0, actualWidth - 1, size - 1); QPixmap pixmap(rect.width(), rect.height()); painter.begin(&pixmap); painter.setPen(pen); for (int i = 1; i < actualWidth - 1; i++) { int color = (511 * i / (maxWidth - 1)); if (color < 256) pen.setColor(QColor(255 - color, 255, 0)); else pen.setColor(QColor(color - 256, 511 - color, 0)); painter.setPen(pen); painter.drawLine(i, 1, i, size - 1); } pen.setColor(Qt::black); painter.setPen(pen); if (actualWidth != 1) painter.drawRect(frameRect); else painter.drawLine(0, 0, 0, size); painter.end(); pixmap.detach(); return pixmap; } void DULines::resizeEvent(QResizeEvent * re) { KrTreeWidget::resizeEvent(re); if (started && (re->oldSize() != re->size())) sectionResized(0); } void DULines::sectionResized(int column) { if (topLevelItemCount() == 0 || column != 0) return; Directory * currentDir = diskUsage->getCurrentDir(); if (currentDir == nullptr) return; int maxPercent = -1; for (Iterator it = currentDir->iterator(); it != currentDir->end(); ++it) { File *item = *it; if (!item->isExcluded() && item->intPercent() > maxPercent) maxPercent = item->intPercent(); } QTreeWidgetItemIterator it2(this); while (*it2) { QTreeWidgetItem *lvitem = *it2; if (lvitem->text(0) != "..") { auto *duItem = dynamic_cast< DULinesItem *>(lvitem); if (duItem) { int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; duItem->setData(0, Qt::DecorationRole, createPixmap(duItem->getFile()->intPercent(), maxPercent, header()->sectionSize(0) - 2 * textMargin)); QSize size = duItem->sizeHint(0); size.setWidth(16); duItem->setSizeHint(0, size); } } it2++; } } bool DULines::doubleClicked(QTreeWidgetItem * item) { if (item) { if (item->text(0) != "..") { File *fileItem = ((DULinesItem *)item)->getFile(); if (fileItem->isDir()) diskUsage->changeDirectory(dynamic_cast(fileItem)); return true; } else { auto *upDir = (Directory *)diskUsage->getCurrentDir()->parent(); if (upDir) diskUsage->changeDirectory(upDir); return true; } } return false; } void DULines::mouseDoubleClickEvent(QMouseEvent * e) { if (e || e->button() == Qt::LeftButton) { QPoint vp = viewport()->mapFromGlobal(e->globalPos()); QTreeWidgetItem * item = itemAt(vp); if (doubleClicked(item)) return; } KrTreeWidget::mouseDoubleClickEvent(e); } void DULines::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Return : case Qt::Key_Enter : if (doubleClicked(currentItem())) return; break; case Qt::Key_Left : case Qt::Key_Right : case Qt::Key_Up : case Qt::Key_Down : if (e->modifiers() == Qt::ShiftModifier) { e->ignore(); return; } break; case Qt::Key_Delete : e->ignore(); return; } KrTreeWidget::keyPressEvent(e); } void DULines::slotRightClicked(QTreeWidgetItem *item, const QPoint &pos) { File * file = nullptr; if (item && item->text(0) != "..") file = ((DULinesItem *)item)->getFile(); QMenu linesPopup; QAction *act = linesPopup.addAction(i18n("Show file sizes"), this, SLOT(slotShowFileSizes())); act->setChecked(showFileSize); diskUsage->rightClickMenu(pos, file, &linesPopup, i18n("Lines")); } void DULines::slotShowFileSizes() { showFileSize = !showFileSize; slotDirChanged(diskUsage->getCurrentDir()); } File * DULines::getCurrentFile() { QTreeWidgetItem *item = currentItem(); if (item == nullptr || item->text(0) == "..") return nullptr; return ((DULinesItem *)item)->getFile(); } void DULines::slotChanged(File * item) { QTreeWidgetItemIterator it(this); while (*it) { QTreeWidgetItem *lvitem = *it; it++; if (lvitem->text(0) != "..") { auto *duItem = (DULinesItem *)(lvitem); if (duItem->getFile() == item) { setSortingEnabled(false); duItem->setHidden(item->isExcluded()); duItem->setText(1, item->percent()); if (!refreshNeeded) { refreshNeeded = true; QTimer::singleShot(0, this, &DULines::slotRefresh); } break; } } } } void DULines::slotDeleted(File * item) { QTreeWidgetItemIterator it(this); while (*it) { QTreeWidgetItem *lvitem = *it; it++; if (lvitem->text(0) != "..") { auto *duItem = (DULinesItem *)(lvitem); if (duItem->getFile() == item) { delete duItem; break; } } } } void DULines::slotRefresh() { if (refreshNeeded) { refreshNeeded = false; setSortingEnabled(true); sortItems(1, Qt::AscendingOrder); } } diff --git a/krusader/DiskUsage/dulines.h b/krusader/DiskUsage/dulines.h index 73440996..a565176d 100644 --- a/krusader/DiskUsage/dulines.h +++ b/krusader/DiskUsage/dulines.h @@ -1,78 +1,78 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef DULINES_H #define DULINES_H // QtGui #include #include #include #include #include "diskusage.h" #include "../GUI/krtreewidget.h" class DULinesToolTip; class DULinesItemDelegate; class DULines : public KrTreeWidget { Q_OBJECT public: explicit DULines(DiskUsage *usage); ~DULines() override; File * getCurrentFile(); public slots: void slotDirChanged(Directory *dirEntry); void sectionResized(int); void slotRightClicked(QTreeWidgetItem *, const QPoint &); void slotChanged(File *); void slotDeleted(File *); void slotShowFileSizes(); void slotRefresh(); protected: DiskUsage *diskUsage; bool event(QEvent * event) Q_DECL_OVERRIDE; void mouseDoubleClickEvent(QMouseEvent * e) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *) Q_DECL_OVERRIDE; private: QPixmap createPixmap(int percent, int maxPercent, int maxWidth); bool doubleClicked(QTreeWidgetItem * item); bool refreshNeeded; bool started; bool showFileSize; DULinesToolTip *toolTip; DULinesItemDelegate *itemDelegate; }; #endif /* __DU_LINES_H__ */ diff --git a/krusader/DiskUsage/dulistview.cpp b/krusader/DiskUsage/dulistview.cpp index b779969f..95bbf828 100644 --- a/krusader/DiskUsage/dulistview.cpp +++ b/krusader/DiskUsage/dulistview.cpp @@ -1,271 +1,271 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "dulistview.h" #include "../krglobal.h" #include "../icon.h" #include "../FileSystem/krpermhandler.h" // QtCore #include #include // QtGui #include #include #include // QtWidgets #include #include #include DUListView::DUListView(DiskUsage *usage) : KrTreeWidget(usage), diskUsage(usage) { setAllColumnsShowFocus(true); setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); setRootIsDecorated(true); setIndentation(10); setItemsExpandable(true); QStringList labels; labels << i18n("Name"); labels << i18n("Percent"); labels << i18n("Total size"); labels << i18n("Own size"); labels << i18n("Type"); labels << i18n("Date"); labels << i18n("Permissions"); labels << i18n("Owner"); labels << i18n("Group"); setHeaderLabels(labels); header()->setSectionResizeMode(QHeaderView::Interactive); KConfigGroup group(krConfig, diskUsage->getConfigGroup()); if (group.hasKey("D State")) header()->restoreState(group.readEntry("D State", QByteArray())); else { int defaultSize = QFontMetrics(font()).width("W"); setColumnWidth(0, defaultSize * 20); setColumnWidth(1, defaultSize * 5); setColumnWidth(2, defaultSize * 10); setColumnWidth(3, defaultSize * 10); setColumnWidth(4, defaultSize * 10); setColumnWidth(5, defaultSize * 10); setColumnWidth(6, defaultSize * 6); setColumnWidth(7, defaultSize * 5); setColumnWidth(8, defaultSize * 5); } header()->setSortIndicatorShown(true); sortItems(2, Qt::AscendingOrder); connect(diskUsage, &DiskUsage::enteringDirectory, this, &DUListView::slotDirChanged); connect(diskUsage, &DiskUsage::clearing, this, &DUListView::clear); connect(diskUsage, &DiskUsage::changed, this, &DUListView::slotChanged); connect(diskUsage, &DiskUsage::deleted, this, &DUListView::slotDeleted); connect(this, &DUListView::itemRightClicked, this, &DUListView::slotRightClicked); connect(this, &DUListView::itemExpanded, this, &DUListView::slotExpanded); } DUListView::~ DUListView() { KConfigGroup group(krConfig, diskUsage->getConfigGroup()); group.writeEntry("D State", header()->saveState()); } void DUListView::addDirectory(Directory *dirEntry, QTreeWidgetItem *parent) { QTreeWidgetItem * lastItem = nullptr; if (parent == nullptr && !(dirEntry->parent() == nullptr)) { lastItem = new QTreeWidgetItem(this); lastItem->setText(0, ".."); lastItem->setIcon(0, Icon("go-up")); lastItem->setFlags(Qt::ItemIsEnabled); } for (Iterator it = dirEntry->iterator(); it != dirEntry->end(); ++it) { File *item = *it; QMimeDatabase db; QMimeType mt = db.mimeTypeForName(item->mime()); QString mime; if (mt.isValid()) mime = mt.comment(); time_t tma = item->time(); struct tm* t = localtime((time_t *) & tma); QDateTime tmp(QDate(t->tm_year + 1900, t->tm_mon + 1, t->tm_mday), QTime(t->tm_hour, t->tm_min)); QString date = QLocale().toString(tmp, QLocale::ShortFormat); QString totalSize = KRpermHandler::parseSize(item->size()) + ' '; QString ownSize = KRpermHandler::parseSize(item->ownSize()) + ' '; QString percent = item->percent(); if (lastItem == nullptr && parent == nullptr) lastItem = new DUListViewItem(diskUsage, item, this, item->name(), percent, totalSize, ownSize, mime, date, item->perm(), item->owner(), item->group()); else if (lastItem == nullptr) lastItem = new DUListViewItem(diskUsage, item, parent, item->name(), percent, totalSize, ownSize, mime, date, item->perm(), item->owner(), item->group()); else if (parent == nullptr) lastItem = new DUListViewItem(diskUsage, item, this, lastItem, item->name(), percent, totalSize, ownSize, mime, date, item->perm(), item->owner(), item->group()); else lastItem = new DUListViewItem(diskUsage, item, parent, lastItem, item->name(), percent, totalSize, ownSize, mime, date, item->perm(), item->owner(), item->group()); if (item->isExcluded()) lastItem->setHidden(true); lastItem->setIcon(0, diskUsage->getIcon(item->mime())); if (item->isDir() && !item->isSymLink()) lastItem->setChildIndicatorPolicy(QTreeWidgetItem::ShowIndicator); } if (topLevelItemCount() > 0) { setCurrentItem(topLevelItem(0)); } } void DUListView::slotDirChanged(Directory *dirEntry) { clear(); addDirectory(dirEntry, nullptr); } File * DUListView::getCurrentFile() { QTreeWidgetItem *item = currentItem(); if (item == nullptr || item->text(0) == "..") return nullptr; return ((DUListViewItem *)item)->getFile(); } void DUListView::slotChanged(File * item) { void * itemPtr = diskUsage->getProperty(item, "ListView-Ref"); if (itemPtr == nullptr) return; auto *duItem = (DUListViewItem *)itemPtr; duItem->setHidden(item->isExcluded()); duItem->setText(1, item->percent()); duItem->setText(2, KRpermHandler::parseSize(item->size()) + ' '); duItem->setText(3, KRpermHandler::parseSize(item->ownSize()) + ' '); } void DUListView::slotDeleted(File * item) { void * itemPtr = diskUsage->getProperty(item, "ListView-Ref"); if (itemPtr == nullptr) return; auto *duItem = (DUListViewItem *)itemPtr; delete duItem; } void DUListView::slotRightClicked(QTreeWidgetItem *item, const QPoint & pos) { File * file = nullptr; if (item && item->text(0) != "..") file = ((DUListViewItem *)item)->getFile(); diskUsage->rightClickMenu(pos, file); } bool DUListView::doubleClicked(QTreeWidgetItem * item) { if (item) { if (item->text(0) != "..") { File *fileItem = ((DUListViewItem *)item)->getFile(); if (fileItem->isDir()) diskUsage->changeDirectory(dynamic_cast(fileItem)); return true; } else { auto *upDir = (Directory *)diskUsage->getCurrentDir()->parent(); if (upDir) diskUsage->changeDirectory(upDir); return true; } } return false; } void DUListView::mouseDoubleClickEvent(QMouseEvent * e) { if (e || e->button() == Qt::LeftButton) { QPoint vp = viewport()->mapFromGlobal(e->globalPos()); QTreeWidgetItem * item = itemAt(vp); if (doubleClicked(item)) return; } KrTreeWidget::mouseDoubleClickEvent(e); } void DUListView::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Return : case Qt::Key_Enter : if (doubleClicked(currentItem())) return; break; case Qt::Key_Left : case Qt::Key_Right : case Qt::Key_Up : case Qt::Key_Down : if (e->modifiers() == Qt::ShiftModifier) { e->ignore(); return; } break; case Qt::Key_Delete : e->ignore(); return; } KrTreeWidget::keyPressEvent(e); } void DUListView::slotExpanded(QTreeWidgetItem * item) { if (item == nullptr || item->text(0) == "..") return; if (item->childCount() == 0) { File *fileItem = ((DUListViewItem *)item)->getFile(); if (fileItem->isDir()) addDirectory(dynamic_cast(fileItem), item); } } diff --git a/krusader/DiskUsage/dulistview.h b/krusader/DiskUsage/dulistview.h index 60c6cd2f..2473e18c 100644 --- a/krusader/DiskUsage/dulistview.h +++ b/krusader/DiskUsage/dulistview.h @@ -1,184 +1,184 @@ /***************************************************************************** * Copyright (C) 2004 Csaba Karai * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef DULISTVIEW_H #define DULISTVIEW_H // QtGui #include #include #include "../GUI/krtreewidget.h" #include "diskusage.h" class DUListViewItem : public QTreeWidgetItem { public: DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidget * parent, const QString& label1, const QString& label2, const QString& label3, const QString& label4, const QString& label5, const QString& label6, const QString& label7, const QString& label8, const QString& label9) : QTreeWidgetItem(parent), diskUsage(diskUsageIn), file(fileIn) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setText(8, label9); setTextAlignment(1, Qt::AlignRight); setTextAlignment(2, Qt::AlignRight); setTextAlignment(3, Qt::AlignRight); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); diskUsage->addProperty(file, "ListView-Ref", this); } DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidgetItem * parent, const QString& label1, const QString& label2, const QString& label3, const QString& label4, const QString& label5, const QString& label6, const QString& label7, const QString& label8, const QString& label9) : QTreeWidgetItem(parent), diskUsage(diskUsageIn), file(fileIn) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setText(8, label9); setTextAlignment(1, Qt::AlignRight); setTextAlignment(2, Qt::AlignRight); setTextAlignment(3, Qt::AlignRight); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); diskUsage->addProperty(file, "ListView-Ref", this); } DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidget * parent, QTreeWidgetItem * after, const QString& label1, const QString& label2, const QString& label3, const QString& label4, const QString& label5, const QString& label6, const QString& label7, const QString& label8, const QString& label9) : QTreeWidgetItem(parent, after), diskUsage(diskUsageIn), file(fileIn) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setText(8, label9); setTextAlignment(1, Qt::AlignRight); setTextAlignment(2, Qt::AlignRight); setTextAlignment(3, Qt::AlignRight); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); diskUsage->addProperty(file, "ListView-Ref", this); } DUListViewItem(DiskUsage *diskUsageIn, File *fileIn, QTreeWidgetItem * parent, QTreeWidgetItem * after, const QString& label1, const QString& label2, const QString& label3, const QString& label4, const QString& label5, const QString& label6, const QString& label7, const QString& label8, const QString& label9) : QTreeWidgetItem(parent, after), diskUsage(diskUsageIn), file(fileIn) { setText(0, label1); setText(1, label2); setText(2, label3); setText(3, label4); setText(4, label5); setText(5, label6); setText(6, label7); setText(7, label8); setText(8, label9); setTextAlignment(1, Qt::AlignRight); setTextAlignment(2, Qt::AlignRight); setTextAlignment(3, Qt::AlignRight); setChildIndicatorPolicy(QTreeWidgetItem::DontShowIndicatorWhenChildless); diskUsage->addProperty(file, "ListView-Ref", this); } ~DUListViewItem() override { diskUsage->removeProperty(file, "ListView-Ref"); } bool operator<(const QTreeWidgetItem &other) const Q_DECL_OVERRIDE { int column = treeWidget() ? treeWidget()->sortColumn() : 0; if (text(0) == "..") return true; const auto *compWith = dynamic_cast< const DUListViewItem * >(&other); if (compWith == nullptr) return false; switch (column) { case 1: case 2: return file->size() > compWith->file->size(); case 3: return file->ownSize() > compWith->file->ownSize(); case 5: return file->time() < compWith->file->time(); default: return text(column) < other.text(column); } } inline File * getFile() { return file; } private: DiskUsage *diskUsage; File *file; }; class DUListView : public KrTreeWidget { Q_OBJECT public: explicit DUListView(DiskUsage *usage); ~DUListView() override; File * getCurrentFile(); public slots: void slotDirChanged(Directory *); void slotChanged(File *); void slotDeleted(File *); void slotRightClicked(QTreeWidgetItem *, const QPoint &); void slotExpanded(QTreeWidgetItem *); protected: DiskUsage *diskUsage; void mouseDoubleClickEvent(QMouseEvent * e) Q_DECL_OVERRIDE; void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE; private: void addDirectory(Directory *dirEntry, QTreeWidgetItem *parent); bool doubleClicked(QTreeWidgetItem * item); }; #endif /* __DU_LISTVIEW_H__ */ diff --git a/krusader/DiskUsage/filelightParts/Config.cpp b/krusader/DiskUsage/filelightParts/Config.cpp index 313ec5bc..d51f10bc 100644 --- a/krusader/DiskUsage/filelightParts/Config.cpp +++ b/krusader/DiskUsage/filelightParts/Config.cpp @@ -1,71 +1,71 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "Config.h" // QtGui #include #include #include #include bool Config::varyLabelFontSizes = true; bool Config::showSmallFiles = false; uint Config::contrast = 50; uint Config::antiAliasFactor = 2; uint Config::minFontPitch = 10; uint Config::defaultRingDepth = 4; Filelight::MapScheme Config::scheme; inline KConfigGroup Filelight::Config::kconfig() { KSharedConfigPtr config = KSharedConfig::openConfig(); return KConfigGroup(config, "DiskUsage"); } void Filelight::Config::read() { KConfigGroup group = kconfig(); varyLabelFontSizes = group.readEntry("varyLabelFontSizes", true); showSmallFiles = group.readEntry("showSmallFiles", false); contrast = group.readEntry("contrast", 50); antiAliasFactor = group.readEntry("antiAliasFactor", 2); minFontPitch = group.readEntry("minFontPitch", QFont().pointSize() - 3); scheme = (MapScheme) group.readEntry("scheme", 0); defaultRingDepth = 4; } void Filelight::Config::write() { KConfigGroup group = kconfig(); group.writeEntry("varyLabelFontSizes", varyLabelFontSizes); group.writeEntry("showSmallFiles", showSmallFiles); group.writeEntry("contrast", contrast); group.writeEntry("antiAliasFactor", antiAliasFactor); group.writeEntry("minFontPitch", minFontPitch); group.writeEntry("scheme", (int)scheme); } diff --git a/krusader/DiskUsage/filelightParts/Config.h b/krusader/DiskUsage/filelightParts/Config.h index d6aa07c0..e20b79be 100644 --- a/krusader/DiskUsage/filelightParts/Config.h +++ b/krusader/DiskUsage/filelightParts/Config.h @@ -1,56 +1,56 @@ /***************************************************************************** * Copyright (C) 2004 Shie Erlich * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef CONFIG_H #define CONFIG_H // QtCore #include class KConfigGroup; namespace Filelight { enum MapScheme { Rainbow, HighContrast, KDE, FileDensity, ModTime }; class Config { static KConfigGroup kconfig(); public: static void read(); static void write(); //keep everything positive, avoid using DON'T, NOT or NO static bool varyLabelFontSizes; static bool showSmallFiles; static uint contrast; static uint antiAliasFactor; static uint minFontPitch; static uint defaultRingDepth; static MapScheme scheme; }; } using Filelight::Config; #endif diff --git a/krusader/DiskUsage/filelightParts/fileTree.cpp b/krusader/DiskUsage/filelightParts/fileTree.cpp index 36934814..3c9e4a7e 100644 --- a/krusader/DiskUsage/filelightParts/fileTree.cpp +++ b/krusader/DiskUsage/filelightParts/fileTree.cpp @@ -1,91 +1,91 @@ /***************************************************************************** * Copyright (C) 2004 Max Howell * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "fileTree.h" // QtCore #include #include //static definitions const FileSize File::DENOMINATOR[4] = { 1ull, 1ull << 10, 1ull << 20, 1ull << 30 }; const char File::PREFIX[5][2] = { "", "K", "M", "G", "T" }; QString File::fullPath(const Directory *root /*= 0*/) const { QString path; if (root == this) root = nullptr; //prevent returning empty string when there is something we could return const File *d; for (d = this; d != root && d && d->parent() != nullptr; d = d->parent()) { if (!path.isEmpty()) path = '/' + path; path = d->name() + path; } if (d) { while (d->parent()) d = d->parent(); if (d->directory().endsWith('/')) return d->directory() + path; else return d->directory() + '/' + path; } else return path; } QString File::humanReadableSize(UnitPrefix key /*= mega*/) const //FIXME inline { return humanReadableSize(m_size, key); } QString File::humanReadableSize(FileSize size, UnitPrefix key /*= mega*/) //static { QString s; double prettySize = (double)size / (double)DENOMINATOR[key]; const QLocale locale; if (prettySize >= 0.01) { if (prettySize < 1) s = locale.toString(prettySize, 'f', 2); else if (prettySize < 100) s = locale.toString(prettySize, 'f', 1); else s = locale.toString(prettySize, 'f', 0); s += ' '; s += PREFIX[key]; s += 'B'; } if (prettySize < 0.1) { s += " ("; s += locale.toString(size / DENOMINATOR[ key ? key - 1 : 0 ]); s += ' '; s += PREFIX[key]; s += "B)"; } return s; } diff --git a/krusader/DiskUsage/filelightParts/fileTree.h b/krusader/DiskUsage/filelightParts/fileTree.h index 019da56f..660ce50b 100644 --- a/krusader/DiskUsage/filelightParts/fileTree.h +++ b/krusader/DiskUsage/filelightParts/fileTree.h @@ -1,385 +1,385 @@ /***************************************************************************** * Copyright (C) 2004 Max Howell * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef FILETREE_H #define FILETREE_H #include #include #include #include #include // TODO these are pointlessly general purpose now, make them incredibly specific typedef KIO::filesize_t FileSize; template class Iterator; template class ConstIterator; template class Chain; template class Link { public: explicit Link(T* const t) : prev(this), next(this), data(t) {} Link() : prev(this), next(this), data(0) {} //TODO unlinking is slow and you don't use it very much in this context. // ** Perhaps you can make a faster deletion system that doesn't bother tidying up first // ** and then you MUST call some kind of detach() function when you remove elements otherwise ~Link() { delete data; unlink(); } friend class Iterator; friend class ConstIterator; friend class Chain; private: void unlink() { prev->next = next; next->prev = prev; prev = next = this; } Link* prev; Link* next; T* data; //ensure only iterators have access to this }; template class Iterator { public: Iterator() : link(0) { } //**** remove this, remove this REMOVE THIS!!! dangerous as your implementation doesn't test for null links, always assumes they can be dereferenced explicit Iterator(Link *p) : link(p) { } bool operator==(const Iterator& it) const { return link == it.link; } bool operator!=(const Iterator& it) const { return link != it.link; } bool operator!=(const Link *p) const { return p != link; } //here we have a choice, really I should make two classes one const the other not const T* operator*() const { return link->data; } T* operator*() { return link->data; } Iterator& operator++() { link = link->next; return *this; } //**** does it waste time returning in places where we don't use the retval? bool isNull() const { return (link == 0); } //REMOVE WITH ABOVE REMOVAL you don't want null iterators to be possible void transferTo(Chain &chain) { chain.append(remove()); } T* remove() { //remove from list, delete Link, data is returned NOT deleted T* const d = link->data; Link* const p = link->prev; link->data = 0; delete link; link = p; //make iterator point to previous element, YOU must check this points to an element return d; } private: Link *link; }; template class ConstIterator { public: explicit ConstIterator(Link *p) : link(p) { } bool operator==(const Iterator& it) const { return link == it.link; } bool operator!=(const Iterator& it) const { return link != it.link; } bool operator!=(const Link *p) const { return p != link; } const T* operator*() const { return link->data; } ConstIterator& operator++() { link = link->next; return *this; } private: const Link *link; }; //**** try to make a generic list class and then a brief full list template that inlines // thus reducing code bloat template class Chain { public: Chain() { } virtual ~Chain() { empty(); } void append(T* const data) { Link* const link = new Link(data); link->prev = head.prev; link->next = &head; head.prev->next = link; head.prev = link; } void transferTo(Chain &c) { if (isEmpty()) return; Link* const first = head.next; Link* const last = head.prev; head.unlink(); first->prev = c.head.prev; c.head.prev->next = first; last->next = &c.head; c.head.prev = last; } void empty() { while (head.next != &head) { delete head.next; } } Iterator iterator() const { return Iterator(head.next); } ConstIterator constIterator() const { return ConstIterator(head.next); } const Link *end() const { return &head; } bool isEmpty() const { return (head.next == &head); } private: Link head; void operator=(const Chain&) {} }; class Directory; class File { protected: Directory *m_parent; //0 if this is treeRoot QString m_name; //< file name QString m_directory;//< the directory of the file FileSize m_size; //< size with subdirectories FileSize m_ownSize; //< size without subdirectories mode_t m_mode; //< file mode QString m_owner; //< file owner name QString m_group; //< file group name QString m_perm; //< file permissions string time_t m_time; //< file modification in time_t format bool m_symLink; //< true if the file is a symlink QString m_mimeType; //< file mimetype bool m_excluded; //< flag if the file is excluded from du int m_percent; //< percent flag public: File(Directory *parentIn, const QString &nameIn, const QString &dir, FileSize sizeIn, mode_t modeIn, const QString &ownerIn, const QString &groupIn, const QString &permIn, time_t timeIn, bool symLinkIn, const QString &mimeTypeIn) : m_parent(parentIn), m_name(nameIn), m_directory(dir), m_size(sizeIn), m_ownSize(sizeIn), m_mode(modeIn), m_owner(ownerIn), m_group(groupIn), m_perm(permIn), m_time(timeIn), m_symLink(symLinkIn), m_mimeType(mimeTypeIn), m_excluded(false), m_percent(-1) {} File(const QString &nameIn, FileSize sizeIn) : m_parent(0), m_name(nameIn), m_directory(QString()), m_size(sizeIn), m_ownSize(sizeIn), m_mode(0), m_owner(QString()), m_group(QString()), m_perm(QString()), m_time(-1), m_symLink(false), m_mimeType(QString()), m_excluded(false), m_percent(-1) { } virtual ~File() {} inline const QString & name() const { return m_name; } inline const QString & directory() const { return m_directory; } inline FileSize size() const { return m_excluded ? 0 : m_size; } inline FileSize ownSize() const { return m_excluded ? 0 : m_ownSize; } inline mode_t mode() const { return m_mode; } inline const QString & owner() const { return m_owner; } inline const QString & group() const { return m_group; } inline const QString & perm() const { return m_perm; } inline time_t time() const { return m_time; } inline const QString & mime() const { return m_mimeType; } inline bool isSymLink() const { return m_symLink; } virtual bool isDir() const { return false; } inline bool isExcluded() const { return m_excluded; } inline void exclude(bool flag) { m_excluded = flag; } inline int intPercent() const { return m_percent; } inline const QString percent() const { if (m_percent < 0) return "INV"; QString buf; buf.sprintf("%d.%02d%%", m_percent / 100, m_percent % 100); return buf; } inline void setPercent(int p) { m_percent = p; } inline const Directory* parent() const { return m_parent; } inline void setSizes(KIO::filesize_t totalSize, KIO::filesize_t ownSize) { m_ownSize = ownSize; m_size = totalSize; } enum UnitPrefix { kilo, mega, giga, tera }; static const FileSize DENOMINATOR[4]; static const char PREFIX[5][2]; QString fullPath(const Directory* = 0) const; QString humanReadableSize(UnitPrefix key = mega) const; static QString humanReadableSize(FileSize size, UnitPrefix Key = mega); friend class Directory; }; //TODO when you modify this to take into account hardlinks you should make the Chain layered not inherited class Directory : public Chain, public File { public: Directory(Directory *parentIn, const QString &nameIn, const QString &dir, FileSize sizeIn, mode_t modeIn, const QString &ownerIn, const QString &groupIn, const QString &permIn, time_t timeIn, bool symLinkIn, const QString &mimeTypeIn) : File(parentIn, nameIn, dir, sizeIn, modeIn, ownerIn, groupIn, permIn, timeIn, symLinkIn, mimeTypeIn), m_fileCount(0) {} Directory(const QString &name, QString url) : File(name, 0), m_fileCount(0) { m_directory = url; } virtual ~Directory() {} virtual bool isDir() const Q_DECL_OVERRIDE { return true; } void append(File *p) { ++m_fileCount; Directory *parent = m_parent; while (parent) { parent->m_fileCount++; parent = parent->m_parent; } Chain::append(p); p->m_parent = this; } void remove(File *p) { for (Iterator it = Chain::iterator(); it != Chain::end(); ++it) if ((*it) == p) { --m_fileCount; Directory *parent = m_parent; while (parent) { parent->m_fileCount--; parent = parent->m_parent; } it.remove(); break; } } uint fileCount() const { return m_fileCount; } private: Directory(const Directory&); void operator=(const Directory&); uint m_fileCount; }; #endif diff --git a/krusader/DiskUsage/radialMap/builder.cpp b/krusader/DiskUsage/radialMap/builder.cpp index a12ba42d..892aa17f 100644 --- a/krusader/DiskUsage/radialMap/builder.cpp +++ b/krusader/DiskUsage/radialMap/builder.cpp @@ -1,149 +1,149 @@ /***************************************************************************** * Copyright (C) 2003-2004 Max Howell * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #include "builder.h" #include "Config.h" #include "widget.h" // QtCore #include #include //**** REMOVE NEED FOR the +1 with MAX_RING_DEPTH uses //**** add some angle bounds checking (possibly in Segment ctor? can I delete in a ctor?) //**** this class is a mess RadialMap::Builder::Builder(RadialMap::Map *m, const Directory* const d, bool fast) : m_map(m) , m_root(d) , m_minSize(static_cast((d->size() * 3) / (PI * m->height() - m->MAP_2MARGIN))) , m_depth(&m->m_visibleDepth) { m_signature = new Chain [*m_depth + 1]; if (!fast) { //|| *m_depth == 0 ) //depth 0 is special case usability-wise //**** WHY?! //determine depth rather than use old one findVisibleDepth(d); //sets m_depth } m_map->setRingBreadth(); setLimits(m_map->m_ringBreadth); build(d); m_map->m_signature = m_signature; delete []m_limits; } void RadialMap::Builder::findVisibleDepth(const Directory* const dir, const unsigned int depth) { //**** because I don't use the same minimumSize criteria as in the visual function // this can lead to incorrect visual representation //**** BUT, you can't set those limits until you know m_depth! //**** also this function doesn't check to see if anything is actually visible // it just assumes that when it reaches a new level everything in it is visible // automatically. This isn't right especially as there might be no files in the // dir provided to this function! static uint stopDepth = 0; if (dir == m_root) { stopDepth = *m_depth; *m_depth = 0; } if (*m_depth < depth) *m_depth = depth; if (*m_depth >= stopDepth) return; for (ConstIterator it = dir->constIterator(); it != dir->end(); ++it) if ((*it)->isDir() && (*it)->size() > m_minSize) findVisibleDepth((Directory *)*it, depth + 1); //if no files greater than min size the depth is still recorded } void RadialMap::Builder::setLimits(const uint &b) //b = breadth? { double size3 = m_root->size() * 3; double pi2B = PI * 2 * b; m_limits = new FileSize [*m_depth + 1]; //FIXME delete! for (unsigned int d = 0; d <= *m_depth; ++d) m_limits[d] = (FileSize)(size3 / (double)(pi2B * (d + 1))); //min is angle that gives 3px outer diameter for that depth } //**** segments currently overlap at edges (i.e. end of first is start of next) bool RadialMap::Builder::build(const Directory* const dir, const unsigned int depth, unsigned int a_start, const unsigned int a_end) { //first iteration: dir == m_root if (dir->fileCount() == 0) //we do fileCount rather than size to avoid chance of divide by zero later return false; FileSize hiddenSize = 0; uint hiddenFileCount = 0; for (ConstIterator it = dir->constIterator(); it != dir->end(); ++it) { if ((*it)->size() > m_limits[depth]) { auto a_len = (unsigned int)(5760 * ((double)(*it)->size() / (double)m_root->size())); auto *s = new Segment(*it, a_start, a_len); (m_signature + depth)->append(s); if ((*it)->isDir()) { if (depth != *m_depth) { //recurse s->m_hasHiddenChildren = build((Directory*) * it, depth + 1, a_start, a_start + a_len); } else s->m_hasHiddenChildren = true; } a_start += a_len; //**** should we add 1? } else { hiddenSize += (*it)->size(); if ((*it)->isDir()) //**** considered virtual, but dir wouldn't count itself! hiddenFileCount += dynamic_cast(*it)->fileCount(); //need to add one to count the dir as well ++hiddenFileCount; } } if (hiddenFileCount == dir->fileCount() && !Config::showSmallFiles) return true; else if ((Config::showSmallFiles && hiddenSize > m_limits[depth]) || (depth == 0 && (hiddenSize > dir->size() / 8)) /*|| > size() * 0.75*/) { //append a segment for unrepresented space - a "fake" segment const QString s = i18np("%1 file: ~ %2", "%1 files: ~ %2", QLocale().toString(hiddenFileCount), File::humanReadableSize(hiddenSize / hiddenFileCount)); (m_signature + depth)->append(new Segment(new File(s, hiddenSize), a_start, a_end - a_start, true)); } return false; } diff --git a/krusader/DiskUsage/radialMap/builder.h b/krusader/DiskUsage/radialMap/builder.h index 716a207c..3e993e45 100644 --- a/krusader/DiskUsage/radialMap/builder.h +++ b/krusader/DiskUsage/radialMap/builder.h @@ -1,54 +1,54 @@ /***************************************************************************** * Copyright (C) 2003-2004 Max Howell * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ #ifndef BUILDER_H #define BUILDER_H #include "radialMap.h" //Segment, defines #include "fileTree.h" template class Chain; namespace RadialMap { class Map; //temporary class that builds the Map signature class Builder { public: Builder(Map*, const Directory* const, bool fast = false); private: void findVisibleDepth(const Directory* const dir, const uint = 0); void setLimits(const uint&); bool build(const Directory* const, const uint = 0, uint = 0, const uint = 5760); Map *m_map; const Directory* const m_root; const FileSize m_minSize; uint *m_depth; Chain *m_signature; FileSize *m_limits; }; } #endif diff --git a/krusader/DiskUsage/radialMap/labels.cpp b/krusader/DiskUsage/radialMap/labels.cpp index fb1237bb..e418b96a 100644 --- a/krusader/DiskUsage/radialMap/labels.cpp +++ b/krusader/DiskUsage/radialMap/labels.cpp @@ -1,366 +1,366 @@ /***************************************************************************** * Copyright (C) 2003-2004 Max Howell * - * Copyright (C) 2004-2018 Krusader Krew [https://krusader.org] * + * Copyright (C) 2004-2019 Krusader Krew [https://krusader.org] * * * * This file is part of Krusader [https://krusader.org]. * * * * Krusader 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. * * * * Krusader 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 Krusader. If not, see [http://www.gnu.org/licenses/]. * *****************************************************************************/ // QtCore #include // QtGui #include #include #include #include #include "Config.h" #include "fileTree.h" #include "radialMap.h" #include "sincos.h" #include "widget.h" namespace RadialMap { struct Label { Label(const RadialMap::Segment *s, int l) : segment(s), lvl(l), a(segment->start() + (segment->length() / 2)) { } bool tooClose(const int &aa) const { return (a > aa - LABEL_ANGLE_MARGIN && a < aa + LABEL_ANGLE_MARGIN); } const RadialMap::Segment *segment; const unsigned int lvl; const int a; int x1, y1, x2, y2, x3; int tx, ty; QString qs; }; class LabelList : public QList