diff --git a/doc/index.docbook b/doc/index.docbook index f2b5c70a3..d5363f2c5 100644 --- a/doc/index.docbook +++ b/doc/index.docbook @@ -1,2159 +1,2170 @@ ]> The &dolphin; Handbook Peter Penz
peter.penz@gmx.at
Orville Bennett
&Orville.Bennett.mail;
Michael Austin
tuxedup@users.sourceforge.net
David Edmundson
kde@davidedmundson.co.uk
Alan Blanchflower Frank Reininghaus
frank78ac@googlemail.com
2006 Peter Penz 2006 &Orville.Bennett; Michael Austin 2009 Frank Reininghaus &FDLNotice; 2019-06-26 Applications 19.08 &dolphin; is the default file manager by &kde;, designed with usability as a primary focus. KDE Dolphin Filemanager file management
Introduction &dolphin; is &plasma;'s default file manager. It aims to improve usability at the user interface level. &dolphin; focuses only on being a file manager whereas &konqueror;, which was &kde;'s default file manager in &kde; 3 and can still be used for file management, is a universal viewer for many file types. This design approach allows the developers to concentrate on optimizing the user interface for the specific task of file management. Please report any problems or feature requests to the &dolphin; author via the bug report dialog. This is accessible either from the Control button at the right of the toolbar in the default mode without menubar; or from HelpReport Bug... menu of the application, if the menubar is shown. Using &dolphin; &dolphin; User Interface The screenshot below shows &dolphin;'s default user interface: Screenshot of &dolphin;'s default user interface &dolphin;'s default user interface. &dolphin;'s default user interface. The elements of the default user interface are: The toolbar, which can be used for quick access to frequently used actions. The toolbar can be customized by clicking it with the &RMB; and choosing Configure Toolbars... from the context menu, with Configure Toolbars... from the Control button at the right of the toolbar or via Settings Configure Toolbars... from the main menu. Screenshot of &dolphin;'s toolbar The default toolbar. Toolbar items where the icon is sufficient for knowing the command do not have text alongside the icons. This can be changed by a &RMB; click and selecting the item text below Show Text. The Control button at the right side of the toolbar is only displayed if the menubar is hidden. The location bar, which always displays the path to the current folder. It can be switched between two different modes, see the section on the location bar for details. The view, which shows all files and folders in the current folder. The Places panel, which provides quick access to bookmarked locations and disks or other media. If desktop search and file indexing are enabled in the Desktop Search module in the &systemsettings; the panel provides Recently Saved items and allows you to search for Documents, Images, Audio Files and Video. The status bar. It displays the name, size and type of the file which is currently hovered over with the mouse, or the number and size of the files which are selected. At the right, there is a zoom slider that allows you to adjust the size of the icons in the view. The menubar (hidden by default), which provides access to all commands and configuration options. See the Command Reference for a list of all menu commands. The menubar can be toggled with Show Menubar (&Ctrl;M) from the Settings menu or the Control button. If the menubar is hidden, all its actions are available from the Control button. The additional Information, Folders and Terminal panels are hidden by default, for more information see Panels. The &dolphin; View Using the View The view displays all files and folders in the current folder. These items can be accessed or manipulated in different ways: A file or folder can be opened by clicking it with the &LMB; (or double-clicking, if Double-click to open files and folders is enabled in the &systemsettings; in the Input Devices Mouse module. Clicking any item or the white area around the items with the &RMB; opens a context menu which provides access to many frequently used actions for the item or the current folder, respectively. If the &LMB; is pressed on an item, but not immediately released, the item can be dragged and dropped in another folder in the current view or in another &dolphin; view (in another &dolphin; window or in the same window if the view is split, see below) to move or copy it or to create a symbolic link. Items can even be dropped in another application to open them in that application. &dolphin; remembers the history of visited folders. To navigate backward or forward in the history, the corresponding buttons in the toolbar can be used: Screenshot of &dolphin;'s toolbar The Back and Forward buttons in the toolbar. The Back and Forward buttons in the toolbar can be used to navigate in the history. If you click with the &MMB; the item in the history is opened in a new tab thus keeping the current tab with its content. &dolphin; View Appearance The toolbar contains buttons to control the appearance of the view: Screenshot of &dolphin;'s toolbar The buttons in the toolbar which control the appearance of the view. The buttons in the toolbar which control the appearance of the view. All the settings discussed below and other options concerning, ⪚ the sorting of the files in the current folder, can also be modified in the View menu and in the View Properties dialog. By default, these settings are remembered for each folder separately. This behavior can be changed in the General section of the settings. View Modes The first three buttons in the above screenshot switch between &dolphin;'s view modes. In the Icons view, which is the default, files will be represented by an icon that visualizes the file type, and a folder icon will be shown for subfolders. The names of folders and files, and the items enabled in ViewAdditional Information, are displayed below the icons. The Compact view shows the folder contents as icons with the name beside it and the items enabled in ViewAdditional Information below the name. The items are grouped in columns similar to the Short View in the &kde; file dialog. In the Details view, the folder contents are displayed as a detailed list which contains the name, size and last modification time of each item. Additional columns can be added by clicking a column header with the &RMB;. In the context menu of the header line you can choose between custom or automatic column width. Automatic width adjusts the width of all columns once to display the longest item in the column completely, except for the Name column where the extension is replaced by ... The order of columns can be changed by drag and drop of column headers, except for the Name header, which is always the first column in this view. The details view allows you to view the current folder in a tree-like fashion if Expandable folders are enabled: Each subfolder of the current folder can be expanded or collapsed by clicking on the > or v icon next to it. Grouped View Grouped View All view modes support grouping by the sort type selected in ViewSort by Information in the View In all view modes &dolphin; shows at least an icon and a name for each item. Using Additional Information in the View menu or the context menu of the header in Details mode, you can select more information for each item to be shown: Size, Date, Type, Rating, Tags or Comment. Depending on the file type, additionally, sorting criteria can be selected: Document: Number of words and lines Image: Size and orientation Audio: Artist, album, duration and track The Other submenu allows you to select Path, Link Destination, Copied From, Permissions, Owner or User Group. Preview If Preview is enabled, the icons are based on the actual file or folder contents; ⪚ for images a scaled down preview of the image is shown. Split If Split is clicked, two views are shown which can display the contents of different folders. This can be convenient for moving or copying files. Selecting Items in the View There are several ways to select items in the view. Once a group of items is selected, all actions, such as Cut, Copy, Move to Trash, and drag and drop operations, affect all selected items. Selecting Items Using the Mouse You can press the &LMB; somewhere in the view and draw a rectangle around a group of items before releasing the button. This will select all items in the rectangle and clear the previous selection. If the &Shift; key is pressed during the selection process, the previous selection is kept. If the &Ctrl; key is pressed while an item is clicked with the &LMB;, the selection state of this item is toggled. If the &Ctrl; key is pressed while a rectangle is drawn around a group of items as described above, the selection state of all items in the rectangle will be toggled. If the &Shift; key is pressed while an item is clicked with the &LMB;, all items between the previous current item and the clicked item will be selected. If Show selection marker is enabled in the Behavior tab of the General section of the settings, a small + or - button appears in the top left corner of the item which is currently hovered over with the mouse. Clicking this sign selects or deselects the item, respectively. Selecting Items Using the Keyboard If an arrow key, Page Up, Page Down, Home, or End is pressed, the new current item is selected, and the previous selection is cleared. If the &Ctrl; key is held while one of the above keys is pressed, the selection remains unchanged. If the &Shift; key is held while one of the above keys is pressed, all items between the previous current item and the new current item will be selected. If &Ctrl;Space is pressed, the selection state of the current item is toggled. &Ctrl;A selects all items in the view. &Ctrl;&Shift;A toggles the selection state of all items in the view. Select a file or folder by typing the first few letters of its name and the first matching item is selected. To clear the selection and cancel the keyboard search press &Esc; or wait longer than the timeout of 1 second. Location Bar The location bar, which can be found above &dolphin;'s view, displays the path to the current folder. The location bar has two modes. Bread Crumb Mode In the bread crumb mode, which is the default, each folder name in the path to the current folder is a button which can be clicked to quickly open that folder. Moreover, clicking the > sign to the right of a folder opens a menu which allows you to quickly open a subfolder of that folder. Screenshot of the location bar in bread crumb mode Location bar in bread crumb mode. Location bar in bread crumb mode. Editable Mode When in bread crumb mode, clicking in the gray area to the right of the path with the &LMB; switches the location bar to editable mode, in which the path can be edited using the keyboard. To switch back to bread crumb mode, click the check mark at the right of the location bar with the &LMB;. Screenshot of the location bar in editable mode Location bar in editable mode. Location bar in editable mode. Using Kioslaves If the location bar is empty in editable mode, a drop down box appears in front of the bar listing all available kioslaves on your system. Kioslaves are programs built into &kde; which add support for many different protocols to &dolphin; and other &kde; applications. For example with the fish kioslave &dolphin; can be used to manage files and folders on a remote host that is accessible via SSH. To do this you would type fish://username@remotehost into the location bar. Similar remote file management can be done on remote hosts accessible via the &FTP;, NFS, SFTP, SMB (CIFS) or webdav protocols. It is also possible to use the kioslaves drop down list to access &systemsettings;, fonts, trash, other programs and devices attached to your computer. See the drop down list for the full list of capabilities available from kioslaves on your system. Screenshot of the list of kioslaves Location bar showing list of available kioslaves. List of available kioslaves. Places and Context If the Places panel is hidden; in both modes an additional icon in front of the path is displayed. This icon can be clicked with the &LMB; to open a menu which offers quick access to places and storage media. See the section about the Places Panel for details. Location bar with Places icon Location bar with Places icon The context menu of the location bar offers actions to switch between the modes and to copy and paste the path using the clipboard. Check the last option in this context menu to display either the full path starting with the root folder of the file system or to display the path starting with the current places entry. Location bar context menu Location bar context menu Panels &dolphin; allows a number of panels to be placed next to the view. These can be enabled in ViewPanels. By unlocking the panels and clicking and dragging a panel title, the panel can be moved to a different position, even outside the window. Places The Places panel is located at the left of the window by default. The Places panel shows any locations you have bookmarked. It also shows any disk or media attached to the computer, recently accessed items and allows you to search for certain type of files. The order of these entries can be changed by drag and drop. The easiest way to add a folder to the Places panel is to drag it and drop it in the panel. Moreover, you can click inside the panel with the &RMB; and choose Add Entry... from the context menu. The first procedure creates a system wide bookmark, the second procedure can be used to add the current path of the location bar or any desired folder or device. A dialog opens where label, location and icon can be edited and the usage of this entry can be restricted to &dolphin;. A &RMB; click opens the context menu to edit, add, hide or remove entries and change the icon size to one of the predefined values or lock/unlock the panels. The context menu has an action to open the entry in a new tab. Devices can be unmounted using the context menu. Information The Information panel shows extended information about the selected items(s) or about the current folder or the file which is currently hovered over with the mouse, including size, type, and date of last modification. It also features a large preview of the selected item and allows you to assign a rating, tags, and comments to it. Folders The Folders panel shows a tree view structure of the file system. It only shows folders. Clicking a folder with the &LMB; opens this folder in the &dolphin; view. Use Limit to Home Directory to hide all folders from the tree view except your Home. Terminal This panel contains a terminal. The terminal will open at the folder currently shown in the &dolphin; view. Changing the folder in the active &dolphin; view will update the working folder of the terminal. Changing the directory in the terminal will update the working folder in the &dolphin; view. The terminal only works with local media. Quick Tips The following are a number of tips to save time when using &dolphin;. Quick Bookmarking To quickly create a bookmark in the Places panel for the current folder, &RMB; click in the work space and click Add to Places in the context menu. Finding Files and Searching in Files &dolphin; is capable of searching for files and for content in files. If &Ctrl;F is pressed or Edit Search... is used, the Search bar will open already set up to search for files within the current folder and any sub-folders. Start to type into the find input box and the search starts immediately. Search files and for content in files Search files and for content in files &dolphin; searching files and for content in files. The search is case insensitive, and does not require surrounding wildcards (*foo* and foo are equivalent), but you can use wildcards inside the search term. * will match zero or more characters, ? only one single character. This feature can be used with running Baloo services; without these services a KIOSlave is launched to provide the search results. The option from Everywhere with activated Baloo services searches in all indexed folders, without Baloo this option starts the search from the user's Home folder. Search with More Options Search with More Options &dolphin; searching with More Options. Use the More Options button to extend the Search bar. This provides a very comfortable way for the user to shrink the number of search results. To start a search select one or more file types (Documents, Audio, Video, Images), a time period and rating Alternatively you can use these options in the Places panel together with the Filter bar to find files using Baloo or limit the search to files matching the filter expression. Use the Save icon to save a search to the Search For section in the Places panel to quickly access it again in the future. Mounting Storage Media A quick way to mount Storage Media is to click on the device in the Places panel. This will mount and open the device in &dolphin;. Undo Actions &dolphin; is capable of undoing changes you have made to files. For example if you moved a file to the Trash, &dolphin; can undo this and move it back to its original location. To undo an action, press &Ctrl;Z or select Edit Undo: (action name) in the menu, ⪚ Undo: Rename. Renaming A Batch Of Files &dolphin; is capable of renaming a number of files at the same time. Each file will have the file name specified, including a number, ⪚, Image1.jpg, Image2.jpg, Image3.jpg. This can be useful, ⪚, for pictures taken with a digital camera. If you wish to rename a batch of files, first select the files to be renamed. This can be done by pressing the &LMB; and drawing a rectangle around the files to be renamed before releasing it, or by holding &Ctrl; and clicking each file to be renamed (see Selecting Items in the View for more details on item selection). Then open the batch-rename dialog by pressing F2 or via the File menu: File Rename... Then enter the name you wish to give the files. The # character must be present within the name. The files will then be renamed, where the # character is replaced by a different consecutive number for each file. If all file extensions in your selection are different, the name of all files can be changed without using a # placeholder while preserving the file extensions. This is ⪚ useful to rename a video file and all associated subtitle files, which have the same filename, but different extensions. Comparing A Selection Of Files or Folders If the &kompare; application is installed, you can use it to see the differences between two files or folders. First select the two files or folders to be compared. Then launch the &kompare; application via the Tools menu: Tools Compare Files . &kompare; will then open showing the differences between the files or folders. Filtering Files &dolphin; is capable of filtering files, &ie; showing only those items in the view whose name contains a given text. For example, if you wish to show only the MP3 files within a folder, you could filter for .mp3. This would then filter out all files whose name does not contain .mp3. To filter files, first enable the filter bar, either by pressing &Ctrl;I or via the menu: Tools Show Filter Bar . You can then enter the text to be filtered for in the filter bar. The filter bar can be disabled either by pressing &Esc;, or with a &LMB; click on the Hide Filter Bar icon. Configuring &dolphin; &dolphin; distinguishes two different kinds of settings: Settings which affect the general behavior of &dolphin;. These can be configured using the Preferences Dialog. Settings which determine how the contents of a folder are displayed in &dolphin;. These settings are called View Properties and can be controlled with toolbar buttons, via the View menu, and with the View Properties Dialog. In the default configuration, the view properties are remembered for each folder, but &dolphin; can also be configured to use common view properties for all folders in the General section of the settings. The &dolphin; Preferences Dialog The Preferences Dialog is opened via Settings Configure &dolphin;... in the menu in &dolphin;'s main window. The settings are divided into several groups which can be accessed by clicking the corresponding icon on the left of the dialog. All settings except for the Startup page and the Status Bar tab on the General page are shared with &konqueror; in filemanager mode. General This group contains settings which control the general behavior of &dolphin;. The group is divided further into four subgroups which can be accessed using the tab bar at the top. Screenshot of the General settings in &dolphin;'s preferences dialog General Settings. General Settings in &dolphin;'s Preferences Dialog. Behavior Tab In the View section, you can configure whether the view properties are stored for each folder or if common view properties are to be used for all folders. Sorting Mode controls how items are sorted in the view. If Natural sorting is enabled, the sort order of three example files will be File1, File2, File10. If this option is disabled, the normal alphabetical sorting case sensitive or case insensitive will be used, which leads to the sort order File1, File10, File2. When hovering over a file or folder with the mouse, a small window with relevant information is shown if Show tooltips is enabled. Show selection marker shows a small + or - button above an item's icon if the item is hovered over with the mouse. These can be used to select or deselect the item. Enable Rename inline to use this mode if only one item is currently selected. If this option is disabled or several items are selected, a dialog will be displayed for renaming. Enabling Switch between split panes with tab key allows to switch split views with the key. Disable Turning off split view closes active pane to close the inactive pane when you are turning off the split view mode, ⪚ pressing F3. Previews Tab In this tab, you can configure for which file types previews are shown. Moreover, the maximum size of remote files for which previews are generated can be chosen. If previews are enabled for folders, previews of some files in the folder will be shown inside a folder's icon. Confirmations Tab In the ask for confirmation section, you can enable warning dialogs that are shown before potentially harmful actions . The confirmation settings for Moving files or folders to trash and Deleting files or folders affect file operations in &dolphin;, &konqueror;, Gwenview and all &kde; applications using the default &kde; file dialog, whereas Closing Dolphin windows with multiple tabs is a &dolphin; specific setting. Status Bar Tab In this tab, some additional items can be enabled for the status bar, provided the status bar is wide enough: A zoom slider which can be used to change the icon size quickly. A bar that shows how much space is free on the current drive. Startup This group contains settings which control the appearance of &dolphin; on startup. Screenshot of the Startup settings in &dolphin;'s preferences dialog Startup Settings. Startup Settings in &dolphin;'s Preferences Dialog. The Start in folder is the folder which is opened on startup. The location of the folder can be entered directly or chosen in a dialog which can be opened by clicking the button showing a folder icon. Moreover, the current location or the default location (which is the user's home folder) can be used as the startup folder by clicking the corresponding button. Split view mode controls if the &dolphin; view is split on startup or not. Editable location bar controls if the location bar is in editable mode on startup. The bread crumb mode of the location bar is used otherwise. See the section about the location bar for details about the two modes. If Show full path inside location bar is enabled, the full path of the current location is shown in the bread crumb mode of the location bar. Otherwise, a shortened version of the path is shown if it begins with the path of one of the places in the Places panel. Show filter bar controls if the filter bar is shown on startup or not. See the section on the filter bar for details. Show full path in title bar makes it easy to distinguish between files or folders with the same name in different folders. Open new folders in tabs controls whether &dolphin; should open a new folder in a new tab of the current instance when called externally. If not enabled, the new folders will be opened in new instances of &dolphin;. By default this option is enabled. View Modes This group contains settings which control the behavior of &dolphin;'s view modes. The three view modes (Icons, Compact, and Details) are accessible via the tab bar at the top. Screenshot of the Icons View settings in &dolphin;'s preferences dialog View Modes Settings. View Modes Settings in &dolphin;'s Preferences Dialog. Common settings for all view modes All three view modes have some common settings: Sliders which control the size of the icons. The Default or Preview sizes are used if previews are disabled or enabled, respectively. Note that the icon size can be changed easily with the zoom slider in the status bar if the corresponding option is enabled in the General section of the settings. A setting for the font used in the view mode: either the system font or a custom font can be chosen. The other settings in the Text section which apply to only one of the view modes are discussed below. Icons Width controls the minimum width that is reserved for the text of a file item. Maximum lines means maximum number of text lines below the icon. Compact Maximum width controls the maximum width that is reserved for the text of a file item. Details Expandable folders determines whether any folders that have subfolders are displayed in a tree view, where the sub items can be expanded by &LMB; clicking the > icon and collapsed by clicking the v icon. Navigation This group contains settings which control how navigation in the folder structure and in archives works. Screenshot of the Navigation settings in &dolphin;'s preferences dialog Navigation Settings. Navigation Settings in &dolphin;'s Preferences Dialog. The option to open items with a single or double mouse click is a system wide setting and can be changed in the &systemsettings; in the Input Devices Mouse module. Archives will be opened inside &dolphin;, and not in an external application, if Open Archives as folder is enabled. If Open folders during drag operations is enabled, dragging an item with the mouse and hovering over a folder with it for a short time will open that folder. This allows you to move or copy items quickly to folders which are several levels deeper in the folder hierarchy. Services This group offers a selection of services that can be shown in the Actions submenu of &dolphin;'s context menu which appears when clicking a file or folder with the &RMB;. Screenshot of the Services settings in &dolphin;'s preferences dialog Services Settings. Services Settings in &dolphin;'s Preferences Dialog. Using the Download New Services you can fetch additional services for the context menu. If you have installed &dolphin;'s plugins for Bazaar, Mercurial, Git or Subversion from the kdesdk module these services are shown in the list. If these plugins are enabled and you enter a folder which is under version control, the version state (locally changed, up to date &etc;) is indicated by icons and you have additional entries in the context menu like commit, update, add, remove &etc; In the service list you can also choose if the Delete, Copy To, and Move To commands are shown in the context menu. &dolphin; has to be restarted to activate the changes for some of these settings. Trash This group contains settings which control the behavior of the trash. Screenshot of the Trash settings in &dolphin;'s preferences dialog Trash Settings. Trash Settings in &dolphin;'s Preferences Dialog. Files which are older than a configurable number of days can be deleted automatically. The size of the trash can be limited to a configurable percentage of the disk size. If this limit is reached, a warning can be issued, or the oldest or largest files can be deleted automatically. Folder View Properties The following settings control how the contents of a folder are displayed in the &dolphin; view, and are stored on a per-folder basis by default: The view mode (Icons, Compact, Details) The sorting of items, which is determined by the sort order (ascending, descending) and the attribute (such as name, size,...) that the items are sorted by Sorting of folders and files – are folders shown first or not? Previews – are they shown instead of icons (based on the settings made in Previews tab of &dolphin;'s General settings) or not? Are items shown in groups in the views? Are hidden files shown? What additional information (besides the name) is shown in the Icons or Details view? The view properties can be configured in the View menu, some (such as the view mode) can also be changed using toolbar buttons. The View Properties Dialog Screenshot of the View Properties dialog The View Properties dialog. The View Properties Dialog. The View Properties dialog can be used to quickly modify several view properties at once. This is done for the current folder, for the current folder including all subfolders, or even for all folders, depending on the choice made in the Apply to section. If Use as default view settings is enabled, the chosen view properties will also be used for all folders which do not have customized view properties yet. Command Reference By default the menubar is not shown. All actions described here either can be accessed with toolbar buttons or with items in the menu of the Control toolbar button. The Menubar in &dolphin;'s Main Window The File Menu File Create New Creates a new object (such as a folder or a text file) in the current folder. You will find an explanation of all available objects in &konqueror;'s handbook in the chapter Create New. &Ctrl;N File New Window Opens a new &dolphin; window. &Ctrl;T File New Tab Opens a new tab. &Ctrl;W File Close Tab Closes the current tab. &Ctrl;&Shift;T File Undo close tab Reopens the last closed tab. F2 File Rename Renames one currently selected item inline. Opens the batch rename dialog if several items are selected. Del File Move to Trash Moves the currently selected item(s) to the trash. &Shift;Del File Delete Permanently deletes the currently selected item(s). The items are not moved to the trash and cannot be restored. File Show Target This action highlights a link target in a new &dolphin; window. &Alt;Return File Properties Shows the properties dialog for the currently selected item(s). &Ctrl;Q File Quit Exits &dolphin;. The Edit Menu &Ctrl;Z Edit Undo Undoes the last action performed by &dolphin;. &Ctrl;X Edit Cut Cuts the currently selected item(s). &Ctrl;C Edit Copy Copies the currently selected item(s). &Ctrl;V Edit Paste Clipboard Contents... or EditPaste one File or EditPaste one Folder or EditPaste x Items Pastes the currently copied/cut items to the current folder. If the clipboard does not contain files or folders, the clipboard contents (such as text or image data) will be pasted into a new file. The name of this file has to be entered in a dialog. &Ctrl;F Edit Search... Opens the find bar. Enter a search term into the edit box and select to search for filename or in contents of files starting from the current folder or everywhere. &Ctrl;A Edit Select All Selects all files and folders in the current folder. &Ctrl;&Shift;A Edit Invert Selection Selects all unselected items and deselects all selected items in the current folder. The View Menu &Ctrl;+ View Zoom In Increases the size of icons in the view. &Ctrl;- View Zoom Out Decreases the size of icons in the view. + + + +&Ctrl;0 + +View +Zoom Reset + +Resets the size of icons in the view to default. + + View View Mode Changes the view mode to Icons (&Ctrl;1), Compact (&Ctrl;2) or Details (&Ctrl;3). View Sort By Changes whether items are sorted by Name or other criteria described in Information in the View. Descending reverses the sort order. Folders First sorts folders before files. View Additional Information Displays additional information described in Information in the View. View Preview Displays a symbolic preview of the file contents in the different view modes. View Show in Groups Displays the content of the current folder grouped by the option selected in Sort By. &Alt;. View Hidden Files Shows all the hidden files and sub-folders within the current folder.There is an alternate shortcut &Ctrl;H for this action. F3 View Split Enables and disables the split view mode. F5 View Reload Reloads the current folder. View Stop Stops loading/reading the contents of the current folder. View Panels Enables and disables the different panels: Places (F9), Information (F11), Folders (F7), Terminal (F4). With Lock Panels the panel header with caption and two buttons is hidden to save space and the panels are immutable, with Unlock Panels the header is visible and the panel can be moved to the right or left or even outside the main window. F6 View Location Bar Editable Location Changes the location bar between the two modes; the bread crumb mode and the editable mode. &Ctrl;L View Location Bar Replace Location Switches the location bar to editable mode, if necessary, and selects the location such that it can be replaced quickly. View Adjust View Properties... Opens the View Properties Dialog. The Go Menu &Alt;Up Go Up Changes to the folder above the current folder. &Alt;Left Go Back Changes to the previously viewed folder. &Alt;Right Go Forward Undoes a Go Back action. &Alt;Home Go Home Changes to the users home folder, ⪚ /home/Peter/. Go Recently Closed Tabs Shows a list of recently closed tabs which can be reopened. The Tools Menu &Ctrl;I Tools Show Filter Bar Enables and disables the filter bar. You can also use the alternate shortcut &Shift;/ for this action. &Shift;F4 Tools Open Terminal Opens &konsole; within the current folder. Tools Compare Files Compare the currently selected files or folders with &kompare;. This action is only enabled if two files or folders are selected. Tools Select Remote Charset Allows you to choose the charset used by a remote connection manually. The Settings and Help Menu &dolphin; has the common &kde; Settings and Help menu items, for more information read the sections about the Settings Menu and Help Menu of the &kde; Fundamentals. Miscellaneous Questions Has &dolphin; replaced &konqueror;? &dolphin; is not intended to be a competitor to &konqueror;: &konqueror; acts as a universal viewer being able to show &HTML; pages, text documents, folders and a lot more, whereas &dolphin; focuses on being only a file manager. This approach allows the optimization of the user interface for the task of file management. How can I get involved with the development of &dolphin;? The easiest way to get involved with &dolphin; is to subscribe to the developer mailing list kfm-devel and drop an email to the developer mailing list. Email what you can do, how much time you can devote &etc;, the developers will let you know what you can do in the project. If you wish to contribute to the documentation please email the &kde; Documentation Team list. How can I submit bug reports? The official channel for submitting bug reports is via the &kde; bug tracking system. The &kde; bug tracker can be found at http://bugs.kde.org. How can I submit feature requests? The official channel for submitting feature requests is via the &kde; bug tracking system. The &kde; bug tracker can be found at http://bugs.kde.org. Credits and License &dolphin; Program copyright 2006–2014 Peter Penz peter.penz@gmx.at, Frank Reininghaus frank78ac@googlemail.com and Emmanuel Pescosta emmanuelpescosta099@gmail.com Contributors: Cvetoslav Ludmiloff ludmiloff@gmail.com Stefan Monov logixoul@gmail.com Michael Austin tuxedup@users.sourceforge.net &Orville.Bennett; &Orville.Bennett.mail; Documentation copyright 2005 Peter Penz peter.penz@gmx.at Documentation copyright 2006 &Orville.Bennett; &Orville.Bennett.mail; Documentation copyright 2006 Michael Austin tuxedup@users.sourceforge.net Documentation copyright 2009 Frank Reininghaus frank78ac@googlemail.com &underFDL; &underGPL; &documentation.index;
diff --git a/src/dolphinmainwindow.cpp b/src/dolphinmainwindow.cpp index 87d041b87..ec42d33f7 100644 --- a/src/dolphinmainwindow.cpp +++ b/src/dolphinmainwindow.cpp @@ -1,2168 +1,2169 @@ /*************************************************************************** * Copyright (C) 2006 by Peter Penz * * Copyright (C) 2006 by Stefan Monov * * Copyright (C) 2006 by Cvetoslav Ludmiloff * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "dolphinmainwindow.h" #include "config-terminal.h" #include "global.h" #include "dolphinbookmarkhandler.h" #include "dolphindockwidget.h" #include "dolphincontextmenu.h" #include "dolphinnewfilemenu.h" #include "dolphinrecenttabsmenu.h" #include "dolphinviewcontainer.h" #include "dolphintabpage.h" #include "middleclickactioneventfilter.h" #include "panels/folders/folderspanel.h" #include "panels/places/placesitemmodel.h" #include "panels/places/placespanel.h" #include "panels/information/informationpanel.h" #include "panels/terminal/terminalpanel.h" #include "settings/dolphinsettingsdialog.h" #include "statusbar/dolphinstatusbar.h" #include "views/dolphinviewactionhandler.h" #include "views/dolphinremoteencoding.h" #include "views/draganddrophelper.h" #include "views/viewproperties.h" #include "views/dolphinnewfilemenuobserver.h" #include "dolphin_generalsettings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { // Used for GeneralSettings::version() to determine whether // an updated version of Dolphin is running. const int CurrentDolphinVersion = 200; } DolphinMainWindow::DolphinMainWindow() : KXmlGuiWindow(nullptr, Qt::WindowContextHelpButtonHint), m_newFileMenu(nullptr), m_helpMenu(nullptr), m_tabWidget(nullptr), m_activeViewContainer(nullptr), m_actionHandler(nullptr), m_remoteEncoding(nullptr), m_settingsDialog(), m_bookmarkHandler(nullptr), m_controlButton(nullptr), m_updateToolBarTimer(nullptr), m_lastHandleUrlStatJob(nullptr), m_terminalPanel(nullptr), m_placesPanel(nullptr), m_tearDownFromPlacesRequested(false) { Q_INIT_RESOURCE(dolphin); setComponentName(QStringLiteral("dolphin"), QGuiApplication::applicationDisplayName()); setObjectName(QStringLiteral("Dolphin#")); connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::errorMessage, this, &DolphinMainWindow::showErrorMessage); KIO::FileUndoManager* undoManager = KIO::FileUndoManager::self(); undoManager->setUiInterface(new UndoUiInterface()); connect(undoManager, QOverload::of(&KIO::FileUndoManager::undoAvailable), this, &DolphinMainWindow::slotUndoAvailable); connect(undoManager, &KIO::FileUndoManager::undoTextChanged, this, &DolphinMainWindow::slotUndoTextChanged); connect(undoManager, &KIO::FileUndoManager::jobRecordingStarted, this, &DolphinMainWindow::clearStatusBar); connect(undoManager, &KIO::FileUndoManager::jobRecordingFinished, this, &DolphinMainWindow::showCommand); GeneralSettings* generalSettings = GeneralSettings::self(); const bool firstRun = (generalSettings->version() < 200); if (firstRun) { generalSettings->setViewPropsTimestamp(QDateTime::currentDateTime()); } setAcceptDrops(true); m_tabWidget = new DolphinTabWidget(this); m_tabWidget->setObjectName("tabWidget"); connect(m_tabWidget, &DolphinTabWidget::activeViewChanged, this, &DolphinMainWindow::activeViewChanged); connect(m_tabWidget, &DolphinTabWidget::tabCountChanged, this, &DolphinMainWindow::tabCountChanged); connect(m_tabWidget, &DolphinTabWidget::currentUrlChanged, this, &DolphinMainWindow::updateWindowTitle); setCentralWidget(m_tabWidget); setupActions(); m_actionHandler = new DolphinViewActionHandler(actionCollection(), this); connect(m_actionHandler, &DolphinViewActionHandler::actionBeingHandled, this, &DolphinMainWindow::clearStatusBar); connect(m_actionHandler, &DolphinViewActionHandler::createDirectoryTriggered, this, &DolphinMainWindow::createDirectory); m_remoteEncoding = new DolphinRemoteEncoding(this, m_actionHandler); connect(this, &DolphinMainWindow::urlChanged, m_remoteEncoding, &DolphinRemoteEncoding::slotAboutToOpenUrl); setupDockWidgets(); setupGUI(Keys | Save | Create | ToolBar); stateChanged(QStringLiteral("new_file")); QClipboard* clipboard = QApplication::clipboard(); connect(clipboard, &QClipboard::dataChanged, this, &DolphinMainWindow::updatePasteAction); QAction* showFilterBarAction = actionCollection()->action(QStringLiteral("show_filter_bar")); showFilterBarAction->setChecked(generalSettings->filterBar()); if (firstRun) { menuBar()->setVisible(false); // Assure a proper default size if Dolphin runs the first time resize(750, 500); } const bool showMenu = !menuBar()->isHidden(); QAction* showMenuBarAction = actionCollection()->action(KStandardAction::name(KStandardAction::ShowMenubar)); showMenuBarAction->setChecked(showMenu); // workaround for bug #171080 if (!showMenu) { createControlButton(); } // enable middle-click on back/forward/up to open in a new tab auto *middleClickEventFilter = new MiddleClickActionEventFilter(this); connect(middleClickEventFilter, &MiddleClickActionEventFilter::actionMiddleClicked, this, &DolphinMainWindow::slotToolBarActionMiddleClicked); toolBar()->installEventFilter(middleClickEventFilter); setupWhatsThis(); } DolphinMainWindow::~DolphinMainWindow() { } QVector DolphinMainWindow::viewContainers() const { QVector viewContainers; viewContainers.reserve(m_tabWidget->count()); for (int i = 0; i < m_tabWidget->count(); ++i) { viewContainers << m_tabWidget->tabPageAt(i)->activeViewContainer(); } return viewContainers; } void DolphinMainWindow::openDirectories(const QList& dirs, bool splitView) { m_tabWidget->openDirectories(dirs, splitView); } void DolphinMainWindow::openDirectories(const QStringList& dirs, bool splitView) { openDirectories(QUrl::fromStringList(dirs), splitView); } void DolphinMainWindow::openFiles(const QList& files, bool splitView) { m_tabWidget->openFiles(files, splitView); } void DolphinMainWindow::openFiles(const QStringList& files, bool splitView) { openFiles(QUrl::fromStringList(files), splitView); } void DolphinMainWindow::activateWindow() { KStartupInfo::setNewStartupId(window(), KStartupInfo::startupId()); KWindowSystem::activateWindow(window()->effectiveWinId()); } void DolphinMainWindow::showCommand(CommandType command) { DolphinStatusBar* statusBar = m_activeViewContainer->statusBar(); switch (command) { case KIO::FileUndoManager::Copy: statusBar->setText(i18nc("@info:status", "Successfully copied.")); break; case KIO::FileUndoManager::Move: statusBar->setText(i18nc("@info:status", "Successfully moved.")); break; case KIO::FileUndoManager::Link: statusBar->setText(i18nc("@info:status", "Successfully linked.")); break; case KIO::FileUndoManager::Trash: statusBar->setText(i18nc("@info:status", "Successfully moved to trash.")); break; case KIO::FileUndoManager::Rename: statusBar->setText(i18nc("@info:status", "Successfully renamed.")); break; case KIO::FileUndoManager::Mkdir: statusBar->setText(i18nc("@info:status", "Created folder.")); break; default: break; } } void DolphinMainWindow::pasteIntoFolder() { m_activeViewContainer->view()->pasteIntoFolder(); } void DolphinMainWindow::changeUrl(const QUrl &url) { if (!KProtocolManager::supportsListing(url)) { // The URL navigator only checks for validity, not // if the URL can be listed. An error message is // shown due to DolphinViewContainer::restoreView(). return; } m_activeViewContainer->setUrl(url); updateFileAndEditActions(); updatePasteAction(); updateViewActions(); updateGoActions(); emit urlChanged(url); } void DolphinMainWindow::slotTerminalDirectoryChanged(const QUrl& url) { if (m_tearDownFromPlacesRequested && url == QUrl::fromLocalFile(QDir::homePath())) { m_placesPanel->proceedWithTearDown(); m_tearDownFromPlacesRequested = false; } m_activeViewContainer->setAutoGrabFocus(false); changeUrl(url); m_activeViewContainer->setAutoGrabFocus(true); } void DolphinMainWindow::slotEditableStateChanged(bool editable) { KToggleAction* editableLocationAction = static_cast(actionCollection()->action(QStringLiteral("editable_location"))); editableLocationAction->setChecked(editable); } void DolphinMainWindow::slotSelectionChanged(const KFileItemList& selection) { updateFileAndEditActions(); const int selectedUrlsCount = m_tabWidget->currentTabPage()->selectedItemsCount(); QAction* compareFilesAction = actionCollection()->action(QStringLiteral("compare_files")); if (selectedUrlsCount == 2) { compareFilesAction->setEnabled(isKompareInstalled()); } else { compareFilesAction->setEnabled(false); } emit selectionChanged(selection); } void DolphinMainWindow::updateHistory() { const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); const int index = urlNavigator->historyIndex(); QAction* backAction = actionCollection()->action(KStandardAction::name(KStandardAction::Back)); if (backAction) { backAction->setToolTip(i18nc("@info", "Go back")); backAction->setWhatsThis(i18nc("@info:whatsthis go back", "Return to the previously viewed folder.")); backAction->setEnabled(index < urlNavigator->historySize() - 1); } QAction* forwardAction = actionCollection()->action(KStandardAction::name(KStandardAction::Forward)); if (forwardAction) { forwardAction->setToolTip(i18nc("@info", "Go forward")); forwardAction->setWhatsThis(xi18nc("@info:whatsthis go forward", "This undoes a Go|Back action.")); forwardAction->setEnabled(index > 0); } } void DolphinMainWindow::updateFilterBarAction(bool show) { QAction* showFilterBarAction = actionCollection()->action(QStringLiteral("show_filter_bar")); showFilterBarAction->setChecked(show); } void DolphinMainWindow::openNewMainWindow() { Dolphin::openNewWindow({m_activeViewContainer->url()}, this); } void DolphinMainWindow::openNewActivatedTab() { m_tabWidget->openNewActivatedTab(); } void DolphinMainWindow::addToPlaces() { QUrl url; QString name; // If nothing is selected, act on the current dir if (m_activeViewContainer->view()->selectedItems().count() == 0) { url = m_activeViewContainer->url(); name = m_activeViewContainer->placesText(); } else { const auto dirToAdd = m_activeViewContainer->view()->selectedItems().first(); url = dirToAdd.url(); name = dirToAdd.name(); } if (url.isValid()) { PlacesItemModel model; QString icon; if (m_activeViewContainer->isSearchModeEnabled()) { icon = QStringLiteral("folder-saved-search-symbolic"); } else { icon = KIO::iconNameForUrl(url); } model.createPlacesItem(name, url, icon); } } void DolphinMainWindow::openNewTab(const QUrl& url, DolphinTabWidget::TabPlacement tabPlacement) { m_tabWidget->openNewTab(url, QUrl(), tabPlacement); } void DolphinMainWindow::openNewTabAfterCurrentTab(const QUrl& url) { m_tabWidget->openNewTab(url, QUrl(), DolphinTabWidget::AfterCurrentTab); } void DolphinMainWindow::openNewTabAfterLastTab(const QUrl& url) { m_tabWidget->openNewTab(url, QUrl(), DolphinTabWidget::AfterLastTab); } void DolphinMainWindow::openInNewTab() { const KFileItemList& list = m_activeViewContainer->view()->selectedItems(); bool tabCreated = false; foreach (const KFileItem& item, list) { const QUrl& url = DolphinView::openItemAsFolderUrl(item); if (!url.isEmpty()) { openNewTabAfterCurrentTab(url); tabCreated = true; } } // if no new tab has been created from the selection // open the current directory in a new tab if (!tabCreated) { openNewTabAfterCurrentTab(m_activeViewContainer->url()); } } void DolphinMainWindow::openInNewWindow() { QUrl newWindowUrl; const KFileItemList list = m_activeViewContainer->view()->selectedItems(); if (list.isEmpty()) { newWindowUrl = m_activeViewContainer->url(); } else if (list.count() == 1) { const KFileItem& item = list.first(); newWindowUrl = DolphinView::openItemAsFolderUrl(item); } if (!newWindowUrl.isEmpty()) { Dolphin::openNewWindow({newWindowUrl}, this); } } void DolphinMainWindow::showTarget() { const auto link = m_activeViewContainer->view()->selectedItems().at(0); const auto linkLocationDir = QFileInfo(link.localPath()).absoluteDir(); auto linkDestination = link.linkDest(); if (QFileInfo(linkDestination).isRelative()) { linkDestination = linkLocationDir.filePath(linkDestination); } if (QFileInfo::exists(linkDestination)) { KIO::highlightInFileManager({QUrl::fromLocalFile(linkDestination).adjusted(QUrl::StripTrailingSlash)}); } else { m_activeViewContainer->showMessage(xi18nc("@info", "Could not access %1.", linkDestination), DolphinViewContainer::Warning); } } void DolphinMainWindow::showEvent(QShowEvent* event) { KXmlGuiWindow::showEvent(event); if (!event->spontaneous()) { m_activeViewContainer->view()->setFocus(); } } void DolphinMainWindow::closeEvent(QCloseEvent* event) { // Find out if Dolphin is closed directly by the user or // by the session manager because the session is closed bool closedByUser = true; if (qApp->isSavingSession()) { closedByUser = false; } if (m_tabWidget->count() > 1 && GeneralSettings::confirmClosingMultipleTabs() && closedByUser) { // Ask the user if he really wants to quit and close all tabs. // Open a confirmation dialog with 3 buttons: // QDialogButtonBox::Yes -> Quit // QDialogButtonBox::No -> Close only the current tab // QDialogButtonBox::Cancel -> do nothing QDialog *dialog = new QDialog(this, Qt::Dialog); dialog->setWindowTitle(i18nc("@title:window", "Confirmation")); dialog->setModal(true); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Yes | QDialogButtonBox::No | QDialogButtonBox::Cancel); KGuiItem::assign(buttons->button(QDialogButtonBox::Yes), KGuiItem(i18nc("@action:button 'Quit Dolphin' button", "&Quit %1", QGuiApplication::applicationDisplayName()), QIcon::fromTheme(QStringLiteral("application-exit")))); KGuiItem::assign(buttons->button(QDialogButtonBox::No), KGuiItem(i18n("C&lose Current Tab"), QIcon::fromTheme(QStringLiteral("tab-close")))); KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); buttons->button(QDialogButtonBox::Yes)->setDefault(true); bool doNotAskAgainCheckboxResult = false; const auto result = KMessageBox::createKMessageBox(dialog, buttons, QMessageBox::Warning, i18n("You have multiple tabs open in this window, are you sure you want to quit?"), QStringList(), i18n("Do not ask again"), &doNotAskAgainCheckboxResult, KMessageBox::Notify); if (doNotAskAgainCheckboxResult) { GeneralSettings::setConfirmClosingMultipleTabs(false); } switch (result) { case QDialogButtonBox::Yes: // Quit break; case QDialogButtonBox::No: // Close only the current tab m_tabWidget->closeTab(); Q_FALLTHROUGH(); default: event->ignore(); return; } } if (m_terminalPanel->hasProgramRunning() && GeneralSettings::confirmClosingTerminalRunningProgram() && closedByUser) { // Ask if the user really wants to quit Dolphin with a program that is still running in the Terminal panel // Open a confirmation dialog with 3 buttons: // QDialogButtonBox::Yes -> Quit // QDialogButtonBox::No -> Show Terminal Panel // QDialogButtonBox::Cancel -> do nothing QDialog *dialog = new QDialog(this, Qt::Dialog); dialog->setWindowTitle(i18nc("@title:window", "Confirmation")); dialog->setModal(true); auto standardButtons = QDialogButtonBox::Yes | QDialogButtonBox::Cancel; if (!m_terminalPanel->isVisible()) { standardButtons |= QDialogButtonBox::No; } QDialogButtonBox *buttons = new QDialogButtonBox(standardButtons); KGuiItem::assign(buttons->button(QDialogButtonBox::Yes), KStandardGuiItem::quit()); if (!m_terminalPanel->isVisible()) { KGuiItem::assign( buttons->button(QDialogButtonBox::No), KGuiItem(i18n("Show &Terminal Panel"), QIcon::fromTheme(QStringLiteral("dialog-scripts")))); } KGuiItem::assign(buttons->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); bool doNotAskAgainCheckboxResult = false; const auto result = KMessageBox::createKMessageBox( dialog, buttons, QMessageBox::Warning, i18n("The program '%1' is still running in the Terminal panel. Are you sure you want to quit?", m_terminalPanel->runningProgramName()), QStringList(), i18n("Do not ask again"), &doNotAskAgainCheckboxResult, KMessageBox::Dangerous); if (doNotAskAgainCheckboxResult) { GeneralSettings::setConfirmClosingTerminalRunningProgram(false); } switch (result) { case QDialogButtonBox::Yes: // Quit break; case QDialogButtonBox::No: actionCollection()->action("show_terminal_panel")->trigger(); // Do not quit, ignore quit event Q_FALLTHROUGH(); default: event->ignore(); return; } } GeneralSettings::setVersion(CurrentDolphinVersion); GeneralSettings::self()->save(); KXmlGuiWindow::closeEvent(event); } void DolphinMainWindow::saveProperties(KConfigGroup& group) { m_tabWidget->saveProperties(group); } void DolphinMainWindow::readProperties(const KConfigGroup& group) { m_tabWidget->readProperties(group); } void DolphinMainWindow::updateNewMenu() { m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); m_newFileMenu->checkUpToDate(); m_newFileMenu->setPopupFiles(activeViewContainer()->url()); } void DolphinMainWindow::createDirectory() { m_newFileMenu->setViewShowsHiddenFiles(activeViewContainer()->view()->hiddenFilesShown()); m_newFileMenu->setPopupFiles(activeViewContainer()->url()); m_newFileMenu->createDirectory(); } void DolphinMainWindow::quit() { close(); } void DolphinMainWindow::showErrorMessage(const QString& message) { m_activeViewContainer->showMessage(message, DolphinViewContainer::Error); } void DolphinMainWindow::slotUndoAvailable(bool available) { QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); if (undoAction) { undoAction->setEnabled(available); } } void DolphinMainWindow::slotUndoTextChanged(const QString& text) { QAction* undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); if (undoAction) { undoAction->setText(text); } } void DolphinMainWindow::undo() { clearStatusBar(); KIO::FileUndoManager::self()->uiInterface()->setParentWidget(this); KIO::FileUndoManager::self()->undo(); } void DolphinMainWindow::cut() { m_activeViewContainer->view()->cutSelectedItems(); } void DolphinMainWindow::copy() { m_activeViewContainer->view()->copySelectedItems(); } void DolphinMainWindow::paste() { m_activeViewContainer->view()->paste(); } void DolphinMainWindow::find() { m_activeViewContainer->setSearchModeEnabled(true); } void DolphinMainWindow::updateSearchAction() { QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); toggleSearchAction->setChecked(m_activeViewContainer->isSearchModeEnabled()); } void DolphinMainWindow::updatePasteAction() { QAction* pasteAction = actionCollection()->action(KStandardAction::name(KStandardAction::Paste)); QPair pasteInfo = m_activeViewContainer->view()->pasteInfo(); pasteAction->setEnabled(pasteInfo.first); pasteAction->setText(pasteInfo.second); } void DolphinMainWindow::slotDirectoryLoadingCompleted() { updatePasteAction(); } void DolphinMainWindow::slotToolBarActionMiddleClicked(QAction *action) { if (action == actionCollection()->action(KStandardAction::name(KStandardAction::Back))) { goBackInNewTab(); } else if (action == actionCollection()->action(KStandardAction::name(KStandardAction::Forward))) { goForwardInNewTab(); } else if (action == actionCollection()->action(QStringLiteral("go_up"))) { goUpInNewTab(); } else if (action == actionCollection()->action(QStringLiteral("go_home"))) { goHomeInNewTab(); } } void DolphinMainWindow::selectAll() { clearStatusBar(); // if the URL navigator is editable and focused, select the whole // URL instead of all items of the view KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); QLineEdit* lineEdit = urlNavigator->editor()->lineEdit(); const bool selectUrl = urlNavigator->isUrlEditable() && lineEdit->hasFocus(); if (selectUrl) { lineEdit->selectAll(); } else { m_activeViewContainer->view()->selectAll(); } } void DolphinMainWindow::invertSelection() { clearStatusBar(); m_activeViewContainer->view()->invertSelection(); } void DolphinMainWindow::toggleSplitView() { DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); tabPage->setSplitViewEnabled(!tabPage->splitViewEnabled()); updateViewActions(); } void DolphinMainWindow::toggleSplitStash() { DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); tabPage->setSplitViewEnabled(false); tabPage->setSplitViewEnabled(true, QUrl("stash:/")); } void DolphinMainWindow::reloadView() { clearStatusBar(); m_activeViewContainer->reload(); m_activeViewContainer->statusBar()->updateSpaceInfo(); } void DolphinMainWindow::stopLoading() { m_activeViewContainer->view()->stopLoading(); } void DolphinMainWindow::enableStopAction() { actionCollection()->action(QStringLiteral("stop"))->setEnabled(true); } void DolphinMainWindow::disableStopAction() { actionCollection()->action(QStringLiteral("stop"))->setEnabled(false); } void DolphinMainWindow::showFilterBar() { m_activeViewContainer->setFilterBarVisible(true); } void DolphinMainWindow::toggleEditLocation() { clearStatusBar(); QAction* action = actionCollection()->action(QStringLiteral("editable_location")); KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); urlNavigator->setUrlEditable(action->isChecked()); } void DolphinMainWindow::replaceLocation() { KUrlNavigator* navigator = m_activeViewContainer->urlNavigator(); QLineEdit* lineEdit = navigator->editor()->lineEdit(); // If the text field currently has focus and everything is selected, // pressing the keyboard shortcut returns the whole thing to breadcrumb mode if (navigator->isUrlEditable() && lineEdit->hasFocus() && lineEdit->selectedText() == lineEdit->text() ) { navigator->setUrlEditable(false); } else { navigator->setUrlEditable(true); navigator->setFocus(); lineEdit->selectAll(); } } void DolphinMainWindow::togglePanelLockState() { const bool newLockState = !GeneralSettings::lockPanels(); foreach (QObject* child, children()) { DolphinDockWidget* dock = qobject_cast(child); if (dock) { dock->setLocked(newLockState); } } GeneralSettings::setLockPanels(newLockState); } void DolphinMainWindow::slotTerminalPanelVisibilityChanged() { if (m_terminalPanel->isHiddenInVisibleWindow() && m_activeViewContainer) { m_activeViewContainer->view()->setFocus(); } } void DolphinMainWindow::goBack() { KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); urlNavigator->goBack(); if (urlNavigator->locationState().isEmpty()) { // An empty location state indicates a redirection URL, // which must be skipped too urlNavigator->goBack(); } } void DolphinMainWindow::goForward() { m_activeViewContainer->urlNavigator()->goForward(); } void DolphinMainWindow::goUp() { m_activeViewContainer->urlNavigator()->goUp(); } void DolphinMainWindow::goHome() { m_activeViewContainer->urlNavigator()->goHome(); } void DolphinMainWindow::goBackInNewTab() { KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); const int index = urlNavigator->historyIndex() + 1; openNewTabAfterCurrentTab(urlNavigator->locationUrl(index)); } void DolphinMainWindow::goForwardInNewTab() { KUrlNavigator* urlNavigator = activeViewContainer()->urlNavigator(); const int index = urlNavigator->historyIndex() - 1; openNewTabAfterCurrentTab(urlNavigator->locationUrl(index)); } void DolphinMainWindow::goUpInNewTab() { const QUrl currentUrl = activeViewContainer()->urlNavigator()->locationUrl(); openNewTabAfterCurrentTab(KIO::upUrl(currentUrl)); } void DolphinMainWindow::goHomeInNewTab() { openNewTabAfterCurrentTab(Dolphin::homeUrl()); } void DolphinMainWindow::compareFiles() { const KFileItemList items = m_tabWidget->currentTabPage()->selectedItems(); if (items.count() != 2) { // The action is disabled in this case, but it could have been triggered // via D-Bus, see https://bugs.kde.org/show_bug.cgi?id=325517 return; } QUrl urlA = items.at(0).url(); QUrl urlB = items.at(1).url(); QString command(QStringLiteral("kompare -c \"")); command.append(urlA.toDisplayString(QUrl::PreferLocalFile)); command.append("\" \""); command.append(urlB.toDisplayString(QUrl::PreferLocalFile)); command.append('\"'); KRun::runCommand(command, QStringLiteral("Kompare"), QStringLiteral("kompare"), this); } void DolphinMainWindow::toggleShowMenuBar() { const bool visible = menuBar()->isVisible(); menuBar()->setVisible(!visible); if (visible) { createControlButton(); } else { deleteControlButton(); } } void DolphinMainWindow::openTerminal() { QString dir(QDir::homePath()); // If the given directory is not local, it can still be the URL of an // ioslave using UDS_LOCAL_PATH which to be converted first. KIO::StatJob* statJob = KIO::mostLocalUrl(m_activeViewContainer->url()); KJobWidgets::setWindow(statJob, this); statJob->exec(); QUrl url = statJob->mostLocalUrl(); //If the URL is local after the above conversion, set the directory. if (url.isLocalFile()) { dir = url.toLocalFile(); } KToolInvocation::invokeTerminal(QString(), dir); } void DolphinMainWindow::editSettings() { if (!m_settingsDialog) { DolphinViewContainer* container = activeViewContainer(); container->view()->writeSettings(); const QUrl url = container->url(); DolphinSettingsDialog* settingsDialog = new DolphinSettingsDialog(url, this); connect(settingsDialog, &DolphinSettingsDialog::settingsChanged, this, &DolphinMainWindow::refreshViews); settingsDialog->setAttribute(Qt::WA_DeleteOnClose); settingsDialog->show(); m_settingsDialog = settingsDialog; } else { m_settingsDialog.data()->raise(); } } void DolphinMainWindow::handleUrl(const QUrl& url) { delete m_lastHandleUrlStatJob; m_lastHandleUrlStatJob = nullptr; if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isDir()) { activeViewContainer()->setUrl(url); } else if (KProtocolManager::supportsListing(url)) { // stat the URL to see if it is a dir or not m_lastHandleUrlStatJob = KIO::stat(url, KIO::HideProgressInfo); if (m_lastHandleUrlStatJob->uiDelegate()) { KJobWidgets::setWindow(m_lastHandleUrlStatJob, this); } connect(m_lastHandleUrlStatJob, &KIO::Job::result, this, &DolphinMainWindow::slotHandleUrlStatFinished); } else { new KRun(url, this); // Automatically deletes itself after being finished } } void DolphinMainWindow::slotHandleUrlStatFinished(KJob* job) { m_lastHandleUrlStatJob = nullptr; const KIO::UDSEntry entry = static_cast(job)->statResult(); const QUrl url = static_cast(job)->url(); if (entry.isDir()) { activeViewContainer()->setUrl(url); } else { new KRun(url, this); // Automatically deletes itself after being finished } } void DolphinMainWindow::slotWriteStateChanged(bool isFolderWritable) { // trash:/ is writable but we don't want to create new items in it. // TODO: remove the trash check once https://phabricator.kde.org/T8234 is implemented newFileMenu()->setEnabled(isFolderWritable && m_activeViewContainer->url().scheme() != QLatin1String("trash")); } void DolphinMainWindow::openContextMenu(const QPoint& pos, const KFileItem& item, const QUrl& url, const QList& customActions) { QPointer contextMenu = new DolphinContextMenu(this, pos, item, url); contextMenu.data()->setCustomActions(customActions); const DolphinContextMenu::Command command = contextMenu.data()->open(); switch (command) { case DolphinContextMenu::OpenParentFolder: changeUrl(KIO::upUrl(item.url())); m_activeViewContainer->view()->markUrlsAsSelected({item.url()}); m_activeViewContainer->view()->markUrlAsCurrent(item.url()); break; case DolphinContextMenu::OpenParentFolderInNewWindow: Dolphin::openNewWindow({item.url()}, this, Dolphin::OpenNewWindowFlag::Select); break; case DolphinContextMenu::OpenParentFolderInNewTab: openNewTabAfterLastTab(KIO::upUrl(item.url())); break; case DolphinContextMenu::None: default: break; } // Delete the menu, unless it has been deleted in its own nested event loop already. if (contextMenu) { contextMenu->deleteLater(); } } void DolphinMainWindow::updateControlMenu() { QMenu* menu = qobject_cast(sender()); Q_ASSERT(menu); // All actions get cleared by QMenu::clear(). This includes the sub-menus // because 'menu' is their parent. menu->clear(); KActionCollection* ac = actionCollection(); // Add "Create New" menu menu->addMenu(m_newFileMenu->menu()); menu->addSeparator(); // Overwrite Find action to Search action QAction *searchAction = ac->action(KStandardAction::name(KStandardAction::Find)); searchAction->setText(i18n("Search...")); // Add "Edit" actions bool added = addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Undo)), menu) | addActionToMenu(searchAction, menu) | addActionToMenu(ac->action(KStandardAction::name(KStandardAction::SelectAll)), menu) | addActionToMenu(ac->action(QStringLiteral("invert_selection")), menu); if (added) { menu->addSeparator(); } // Add "View" actions if (!GeneralSettings::showZoomSlider()) { addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomIn)), menu); + addActionToMenu(ac->action(QStringLiteral("view_zoom_reset")), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ZoomOut)), menu); menu->addSeparator(); } added = addActionToMenu(ac->action(QStringLiteral("sort")), menu) | addActionToMenu(ac->action(QStringLiteral("view_mode")), menu) | addActionToMenu(ac->action(QStringLiteral("additional_info")), menu) | addActionToMenu(ac->action(QStringLiteral("show_preview")), menu) | addActionToMenu(ac->action(QStringLiteral("show_in_groups")), menu) | addActionToMenu(ac->action(QStringLiteral("show_hidden_files")), menu); if (added) { menu->addSeparator(); } added = addActionToMenu(ac->action(QStringLiteral("split_view")), menu) | addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Redisplay)), menu) | addActionToMenu(ac->action(QStringLiteral("view_properties")), menu); if (added) { menu->addSeparator(); } addActionToMenu(ac->action(QStringLiteral("panels")), menu); QMenu* locationBarMenu = new QMenu(i18nc("@action:inmenu", "Location Bar"), menu); locationBarMenu->addAction(ac->action(QStringLiteral("editable_location"))); locationBarMenu->addAction(ac->action(QStringLiteral("replace_location"))); menu->addMenu(locationBarMenu); menu->addSeparator(); // Add "Go" menu QMenu* goMenu = new QMenu(i18nc("@action:inmenu", "Go"), menu); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Back))); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Forward))); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Up))); goMenu->addAction(ac->action(KStandardAction::name(KStandardAction::Home))); goMenu->addAction(ac->action(QStringLiteral("closed_tabs"))); KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), goMenu); m_bookmarkHandler->fillControlMenu(bookmarkMenu->menu(), ac); goMenu->addAction(bookmarkMenu); menu->addMenu(goMenu); // Add "Tool" menu QMenu* toolsMenu = new QMenu(i18nc("@action:inmenu", "Tools"), menu); toolsMenu->addAction(ac->action(QStringLiteral("show_filter_bar"))); toolsMenu->addAction(ac->action(QStringLiteral("compare_files"))); toolsMenu->addAction(ac->action(QStringLiteral("open_terminal"))); toolsMenu->addAction(ac->action(QStringLiteral("change_remote_encoding"))); menu->addMenu(toolsMenu); // Add "Settings" menu entries addActionToMenu(ac->action(KStandardAction::name(KStandardAction::KeyBindings)), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ConfigureToolbars)), menu); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::Preferences)), menu); // Add "Help" menu menu->addMenu(m_helpMenu->menu()); menu->addSeparator(); addActionToMenu(ac->action(KStandardAction::name(KStandardAction::ShowMenubar)), menu); } void DolphinMainWindow::updateToolBar() { if (!menuBar()->isVisible()) { createControlButton(); } } void DolphinMainWindow::slotControlButtonDeleted() { m_controlButton = nullptr; m_updateToolBarTimer->start(); } void DolphinMainWindow::slotPlaceActivated(const QUrl& url) { DolphinViewContainer* view = activeViewContainer(); if (view->url() == url) { // We can end up here if the user clicked a device in the Places Panel // which had been unmounted earlier, see https://bugs.kde.org/show_bug.cgi?id=161385. reloadView(); } else { changeUrl(url); } } void DolphinMainWindow::closedTabsCountChanged(unsigned int count) { actionCollection()->action(QStringLiteral("undo_close_tab"))->setEnabled(count > 0); } void DolphinMainWindow::activeViewChanged(DolphinViewContainer* viewContainer) { DolphinViewContainer* oldViewContainer = m_activeViewContainer; Q_ASSERT(viewContainer); m_activeViewContainer = viewContainer; if (oldViewContainer) { const QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); toggleSearchAction->disconnect(oldViewContainer); // Disconnect all signals between the old view container (container, // view and url navigator) and main window. oldViewContainer->disconnect(this); oldViewContainer->view()->disconnect(this); oldViewContainer->urlNavigator()->disconnect(this); // except the requestItemInfo so that on hover the information panel can still be updated connect(oldViewContainer->view(), &DolphinView::requestItemInfo, this, &DolphinMainWindow::requestItemInfo); } connectViewSignals(viewContainer); m_actionHandler->setCurrentView(viewContainer->view()); updateHistory(); updateFileAndEditActions(); updatePasteAction(); updateViewActions(); updateGoActions(); updateSearchAction(); const QUrl url = viewContainer->url(); emit urlChanged(url); } void DolphinMainWindow::tabCountChanged(int count) { const bool enableTabActions = (count > 1); actionCollection()->action(QStringLiteral("activate_next_tab"))->setEnabled(enableTabActions); actionCollection()->action(QStringLiteral("activate_prev_tab"))->setEnabled(enableTabActions); } void DolphinMainWindow::updateWindowTitle() { const QString newTitle = m_activeViewContainer->caption(); if (windowTitle() != newTitle) { setWindowTitle(newTitle); } } void DolphinMainWindow::slotStorageTearDownFromPlacesRequested(const QString& mountPath) { if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { m_tearDownFromPlacesRequested = true; m_terminalPanel->goHome(); // m_placesPanel->proceedWithTearDown() will be called in slotTerminalDirectoryChanged } else { m_placesPanel->proceedWithTearDown(); } } void DolphinMainWindow::slotStorageTearDownExternallyRequested(const QString& mountPath) { if (m_terminalPanel->currentWorkingDirectory().startsWith(mountPath)) { m_tearDownFromPlacesRequested = false; m_terminalPanel->goHome(); } } void DolphinMainWindow::setupActions() { // setup 'File' menu m_newFileMenu = new DolphinNewFileMenu(actionCollection(), this); QMenu* menu = m_newFileMenu->menu(); menu->setTitle(i18nc("@title:menu Create new folder, file, link, etc.", "Create New")); menu->setIcon(QIcon::fromTheme(QStringLiteral("document-new"))); m_newFileMenu->setDelayed(false); connect(menu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateNewMenu); QAction* newWindow = KStandardAction::openNew(this, &DolphinMainWindow::openNewMainWindow, actionCollection()); newWindow->setText(i18nc("@action:inmenu File", "New &Window")); newWindow->setToolTip(i18nc("@info", "Open a new Dolphin window")); newWindow->setWhatsThis(xi18nc("@info:whatsthis", "This opens a new " "window just like this one with the current location and view." "You can drag and drop items between windows.")); newWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); QAction* newTab = actionCollection()->addAction(QStringLiteral("new_tab")); newTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); newTab->setText(i18nc("@action:inmenu File", "New Tab")); newTab->setWhatsThis(xi18nc("@info:whatsthis", "This opens a new " "Tab with the current location and view." "A tab is an additional view within this window. " "You can drag and drop items between tabs.")); actionCollection()->setDefaultShortcuts(newTab, {Qt::CTRL + Qt::Key_T, Qt::CTRL + Qt::SHIFT + Qt::Key_N}); connect(newTab, &QAction::triggered, this, &DolphinMainWindow::openNewActivatedTab); QAction* addToPlaces = actionCollection()->addAction(QStringLiteral("add_to_places")); addToPlaces->setIcon(QIcon::fromTheme(QStringLiteral("bookmark-new"))); addToPlaces->setWhatsThis(xi18nc("@info:whatsthis", "This adds the selected folder " "to the Places panel.")); connect(addToPlaces, &QAction::triggered, this, &DolphinMainWindow::addToPlaces); QAction* closeTab = KStandardAction::close(m_tabWidget, QOverload<>::of(&DolphinTabWidget::closeTab), actionCollection()); closeTab->setText(i18nc("@action:inmenu File", "Close Tab")); closeTab->setWhatsThis(i18nc("@info:whatsthis", "This closes the " "currently viewed tab. If no more tabs are left this window " "will close instead.")); QAction* quitAction = KStandardAction::quit(this, &DolphinMainWindow::quit, actionCollection()); quitAction->setWhatsThis(i18nc("@info:whatsthis quit", "This closes this window.")); // setup 'Edit' menu KStandardAction::undo(this, &DolphinMainWindow::undo, actionCollection()); // i18n: This will be the last paragraph for the whatsthis for all three: // Cut, Copy and Paste const QString cutCopyPastePara = xi18nc("@info:whatsthis", "Cut, " "Copy and Paste work between many " "applications and are among the most used commands. That's why their " "keyboard shortcuts are prominently placed right " "next to each other on the keyboard: Ctrl+X, " "Ctrl+C and Ctrl+V."); QAction* cutAction = KStandardAction::cut(this, &DolphinMainWindow::cut, actionCollection()); cutAction->setWhatsThis(xi18nc("@info:whatsthis cut", "This copies the items " "in your current selection to the clipboard." "Use the Paste action afterwards to copy them from " "the clipboard to a new location. The items will be removed from their " "initial location.") + cutCopyPastePara); QAction* copyAction = KStandardAction::copy(this, &DolphinMainWindow::copy, actionCollection()); copyAction->setWhatsThis(xi18nc("@info:whatsthis copy", "This copies the " "items in your current selection to the clipboard." "Use the Paste action afterwards to copy them " "from the clipboard to a new location.") + cutCopyPastePara); QAction* paste = KStandardAction::paste(this, &DolphinMainWindow::paste, actionCollection()); // The text of the paste-action is modified dynamically by Dolphin // (e. g. to "Paste One Folder"). To prevent that the size of the toolbar changes // due to the long text, the text "Paste" is used: paste->setIconText(i18nc("@action:inmenu Edit", "Paste")); paste->setWhatsThis(xi18nc("@info:whatsthis paste", "This copies the items from " "your clipboard to the currently viewed folder." "If the items were added to the clipboard by the Cut " "action they are removed from their old location.") + cutCopyPastePara); QAction *searchAction = KStandardAction::find(this, &DolphinMainWindow::find, actionCollection()); searchAction->setText(i18n("Search...")); searchAction->setToolTip(i18nc("@info:tooltip", "Search for files and folders")); searchAction->setWhatsThis(xi18nc("@info:whatsthis find", "This helps you " "find files and folders by opening a find bar. " "There you can enter search terms and specify settings to find the " "objects you are looking for.Use this help again on " "the find bar so we can have a look at it while the settings are " "explained.")); // toggle_search acts as a copy of the main searchAction to be used mainly // in the toolbar, with no default shortcut attached, to avoid messing with // existing workflows (search bar always open and Ctrl-F to focus) QAction *toggleSearchAction = actionCollection()->addAction(QStringLiteral("toggle_search")); toggleSearchAction->setText(i18nc("@action:inmenu", "Toggle Search Bar")); toggleSearchAction->setIconText(i18nc("@action:intoolbar", "Search")); toggleSearchAction->setIcon(searchAction->icon()); toggleSearchAction->setToolTip(searchAction->toolTip()); toggleSearchAction->setWhatsThis(searchAction->whatsThis()); toggleSearchAction->setCheckable(true); QAction* selectAllAction = KStandardAction::selectAll(this, &DolphinMainWindow::selectAll, actionCollection()); selectAllAction->setWhatsThis(xi18nc("@info:whatsthis", "This selects all " "files and folders in the current location.")); QAction* invertSelection = actionCollection()->addAction(QStringLiteral("invert_selection")); invertSelection->setText(i18nc("@action:inmenu Edit", "Invert Selection")); invertSelection->setWhatsThis(xi18nc("@info:whatsthis invert", "This selects all " "objects that you have currently not selected instead.")); invertSelection->setIcon(QIcon::fromTheme(QStringLiteral("edit-select-invert"))); actionCollection()->setDefaultShortcut(invertSelection, Qt::CTRL + Qt::SHIFT + Qt::Key_A); connect(invertSelection, &QAction::triggered, this, &DolphinMainWindow::invertSelection); // setup 'View' menu // (note that most of it is set up in DolphinViewActionHandler) QAction* split = actionCollection()->addAction(QStringLiteral("split_view")); split->setWhatsThis(xi18nc("@info:whatsthis find", "This splits " "the folder view below into two autonomous views.This " "way you can see two locations at once and move items between them " "quickly.Click this again afterwards to recombine the views.")); actionCollection()->setDefaultShortcut(split, Qt::Key_F3); connect(split, &QAction::triggered, this, &DolphinMainWindow::toggleSplitView); QAction* stashSplit = actionCollection()->addAction(QStringLiteral("split_stash")); actionCollection()->setDefaultShortcut(stashSplit, Qt::CTRL + Qt::Key_S); stashSplit->setText(i18nc("@action:intoolbar Stash", "Stash")); stashSplit->setToolTip(i18nc("@info", "Opens the stash virtual directory in a split window")); stashSplit->setIcon(QIcon::fromTheme(QStringLiteral("folder-stash"))); stashSplit->setCheckable(false); stashSplit->setVisible(KProtocolInfo::isKnownProtocol("stash")); connect(stashSplit, &QAction::triggered, this, &DolphinMainWindow::toggleSplitStash); KStandardAction::redisplay(this, &DolphinMainWindow::reloadView, actionCollection()); QAction* stop = actionCollection()->addAction(QStringLiteral("stop")); stop->setText(i18nc("@action:inmenu View", "Stop")); stop->setToolTip(i18nc("@info", "Stop loading")); stop->setWhatsThis(i18nc("@info", "This stops the loading of the contents of the current folder.")); stop->setIcon(QIcon::fromTheme(QStringLiteral("process-stop"))); connect(stop, &QAction::triggered, this, &DolphinMainWindow::stopLoading); KToggleAction* editableLocation = actionCollection()->add(QStringLiteral("editable_location")); editableLocation->setText(i18nc("@action:inmenu Navigation Bar", "Editable Location")); editableLocation->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the Location Bar to be " "editable so you can directly enter a location you want to go to." "You can also switch to editing by clicking to the right of the " "location and switch back by confirming the edited location.")); actionCollection()->setDefaultShortcut(editableLocation, Qt::Key_F6); connect(editableLocation, &KToggleAction::triggered, this, &DolphinMainWindow::toggleEditLocation); QAction* replaceLocation = actionCollection()->addAction(QStringLiteral("replace_location")); replaceLocation->setText(i18nc("@action:inmenu Navigation Bar", "Replace Location")); // i18n: "enter" is used both in the meaning of "writing" and "going to" a new location here. // Both meanings are useful but not necessary to understand the use of "Replace Location". // So you might want to be more verbose in your language to convey the meaning but it's up to you. replaceLocation->setWhatsThis(xi18nc("@info:whatsthis", "This switches to editing the location and selects it " "so you can quickly enter a different location.")); actionCollection()->setDefaultShortcut(replaceLocation, Qt::CTRL + Qt::Key_L); connect(replaceLocation, &QAction::triggered, this, &DolphinMainWindow::replaceLocation); // setup 'Go' menu QAction* backAction = KStandardAction::back(this, &DolphinMainWindow::goBack, actionCollection()); auto backShortcuts = backAction->shortcuts(); backShortcuts.append(QKeySequence(Qt::Key_Backspace)); actionCollection()->setDefaultShortcuts(backAction, backShortcuts); DolphinRecentTabsMenu* recentTabsMenu = new DolphinRecentTabsMenu(this); actionCollection()->addAction(QStringLiteral("closed_tabs"), recentTabsMenu); connect(m_tabWidget, &DolphinTabWidget::rememberClosedTab, recentTabsMenu, &DolphinRecentTabsMenu::rememberClosedTab); connect(recentTabsMenu, &DolphinRecentTabsMenu::restoreClosedTab, m_tabWidget, &DolphinTabWidget::restoreClosedTab); connect(recentTabsMenu, &DolphinRecentTabsMenu::closedTabsCountChanged, this, &DolphinMainWindow::closedTabsCountChanged); QAction* undoCloseTab = actionCollection()->addAction(QStringLiteral("undo_close_tab")); undoCloseTab->setText(i18nc("@action:inmenu File", "Undo close tab")); undoCloseTab->setWhatsThis(i18nc("@info:whatsthis undo close tab", "This returns you to the previously closed tab.")); actionCollection()->setDefaultShortcut(undoCloseTab, Qt::CTRL + Qt::SHIFT + Qt::Key_T); undoCloseTab->setIcon(QIcon::fromTheme(QStringLiteral("edit-undo"))); undoCloseTab->setEnabled(false); connect(undoCloseTab, &QAction::triggered, recentTabsMenu, &DolphinRecentTabsMenu::undoCloseTab); auto undoAction = actionCollection()->action(KStandardAction::name(KStandardAction::Undo)); undoAction->setWhatsThis(xi18nc("@info:whatsthis", "This undoes " "the last change you made to files or folders." "Such changes include creating, renaming " "and moving them to a different location " "or to the Trash. Changes that can't " "be undone will ask for your confirmation.")); undoAction->setEnabled(false); // undo should be disabled by default KStandardAction::forward(this, &DolphinMainWindow::goForward, actionCollection()); KStandardAction::up(this, &DolphinMainWindow::goUp, actionCollection()); QAction* homeAction = KStandardAction::home(this, &DolphinMainWindow::goHome, actionCollection()); homeAction->setWhatsThis(xi18nc("@info:whatsthis", "Go to your " "Home folder.Every user account " "has their own Home that contains their data " "including folders that contain personal application data.")); // setup 'Tools' menu QAction* showFilterBar = actionCollection()->addAction(QStringLiteral("show_filter_bar")); showFilterBar->setText(i18nc("@action:inmenu Tools", "Show Filter Bar")); showFilterBar->setWhatsThis(xi18nc("@info:whatsthis", "This opens the " "Filter Bar at the bottom of the window. " "There you can enter a text to filter the files and folders currently displayed. " "Only those that contain the text in their name will be kept in view.")); showFilterBar->setIcon(QIcon::fromTheme(QStringLiteral("view-filter"))); actionCollection()->setDefaultShortcuts(showFilterBar, {Qt::CTRL + Qt::Key_I, Qt::Key_Slash}); connect(showFilterBar, &QAction::triggered, this, &DolphinMainWindow::showFilterBar); QAction* compareFiles = actionCollection()->addAction(QStringLiteral("compare_files")); compareFiles->setText(i18nc("@action:inmenu Tools", "Compare Files")); compareFiles->setIcon(QIcon::fromTheme(QStringLiteral("kompare"))); compareFiles->setEnabled(false); connect(compareFiles, &QAction::triggered, this, &DolphinMainWindow::compareFiles); #ifdef HAVE_TERMINAL if (KAuthorized::authorize(QStringLiteral("shell_access"))) { QAction* openTerminal = actionCollection()->addAction(QStringLiteral("open_terminal")); openTerminal->setText(i18nc("@action:inmenu Tools", "Open Terminal")); openTerminal->setWhatsThis(xi18nc("@info:whatsthis", "This opens a terminal application for the viewed location." "To learn more about terminals use the help in the terminal application.")); openTerminal->setIcon(QIcon::fromTheme(QStringLiteral("dialog-scripts"))); actionCollection()->setDefaultShortcut(openTerminal, Qt::SHIFT + Qt::Key_F4); connect(openTerminal, &QAction::triggered, this, &DolphinMainWindow::openTerminal); } #endif // setup 'Bookmarks' menu KActionMenu *bookmarkMenu = new KActionMenu(i18nc("@title:menu", "&Bookmarks"), this); bookmarkMenu->setIcon(QIcon::fromTheme(QStringLiteral("bookmarks"))); // Make the toolbar button version work properly on click bookmarkMenu->setDelayed(false); m_bookmarkHandler = new DolphinBookmarkHandler(this, actionCollection(), bookmarkMenu->menu(), this); actionCollection()->addAction(QStringLiteral("bookmarks"), bookmarkMenu); // setup 'Settings' menu KToggleAction* showMenuBar = KStandardAction::showMenubar(nullptr, nullptr, actionCollection()); showMenuBar->setWhatsThis(xi18nc("@info:whatsthis", "This switches between having a Menubar " "and having a Control button. Both " "contain mostly the same commands and configuration options.")); connect(showMenuBar, &KToggleAction::triggered, // Fixes #286822 this, &DolphinMainWindow::toggleShowMenuBar, Qt::QueuedConnection); KStandardAction::preferences(this, &DolphinMainWindow::editSettings, actionCollection()); // setup 'Help' menu for the m_controlButton. The other one is set up in the base class. m_helpMenu = new KHelpMenu(nullptr); m_helpMenu->menu()->installEventFilter(this); // remove duplicate shortcuts m_helpMenu->action(KHelpMenu::menuHelpContents)->setShortcut(QKeySequence()); m_helpMenu->action(KHelpMenu::menuWhatsThis)->setShortcut(QKeySequence()); // not in menu actions QList nextTabKeys = KStandardShortcut::tabNext(); nextTabKeys.append(QKeySequence(Qt::CTRL + Qt::Key_Tab)); QList prevTabKeys = KStandardShortcut::tabPrev(); prevTabKeys.append(QKeySequence(Qt::CTRL + Qt::SHIFT + Qt::Key_Tab)); QAction* activateNextTab = actionCollection()->addAction(QStringLiteral("activate_next_tab")); activateNextTab->setIconText(i18nc("@action:inmenu", "Next Tab")); activateNextTab->setText(i18nc("@action:inmenu", "Activate Next Tab")); activateNextTab->setEnabled(false); connect(activateNextTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activateNextTab); actionCollection()->setDefaultShortcuts(activateNextTab, nextTabKeys); QAction* activatePrevTab = actionCollection()->addAction(QStringLiteral("activate_prev_tab")); activatePrevTab->setIconText(i18nc("@action:inmenu", "Previous Tab")); activatePrevTab->setText(i18nc("@action:inmenu", "Activate Previous Tab")); activatePrevTab->setEnabled(false); connect(activatePrevTab, &QAction::triggered, m_tabWidget, &DolphinTabWidget::activatePrevTab); actionCollection()->setDefaultShortcuts(activatePrevTab, prevTabKeys); // for context menu QAction* showTarget = actionCollection()->addAction(QStringLiteral("show_target")); showTarget->setText(i18nc("@action:inmenu", "Show Target")); showTarget->setIcon(QIcon::fromTheme(QStringLiteral("document-open-folder"))); showTarget->setEnabled(false); connect(showTarget, &QAction::triggered, this, &DolphinMainWindow::showTarget); QAction* openInNewTab = actionCollection()->addAction(QStringLiteral("open_in_new_tab")); openInNewTab->setText(i18nc("@action:inmenu", "Open in New Tab")); openInNewTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); connect(openInNewTab, &QAction::triggered, this, &DolphinMainWindow::openInNewTab); QAction* openInNewTabs = actionCollection()->addAction(QStringLiteral("open_in_new_tabs")); openInNewTabs->setText(i18nc("@action:inmenu", "Open in New Tabs")); openInNewTabs->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); connect(openInNewTabs, &QAction::triggered, this, &DolphinMainWindow::openInNewTab); QAction* openInNewWindow = actionCollection()->addAction(QStringLiteral("open_in_new_window")); openInNewWindow->setText(i18nc("@action:inmenu", "Open in New Window")); openInNewWindow->setIcon(QIcon::fromTheme(QStringLiteral("window-new"))); connect(openInNewWindow, &QAction::triggered, this, &DolphinMainWindow::openInNewWindow); } void DolphinMainWindow::setupDockWidgets() { const bool lock = GeneralSettings::lockPanels(); KDualAction* lockLayoutAction = actionCollection()->add(QStringLiteral("lock_panels")); lockLayoutAction->setActiveText(i18nc("@action:inmenu Panels", "Unlock Panels")); lockLayoutAction->setActiveIcon(QIcon::fromTheme(QStringLiteral("object-unlocked"))); lockLayoutAction->setInactiveText(i18nc("@action:inmenu Panels", "Lock Panels")); lockLayoutAction->setInactiveIcon(QIcon::fromTheme(QStringLiteral("object-locked"))); lockLayoutAction->setWhatsThis(xi18nc("@info:whatsthis", "This " "switches between having panels locked or " "unlocked.Unlocked panels can be " "dragged to the other side of the window and have a close " "button.Locked panels are embedded more cleanly.")); lockLayoutAction->setActive(lock); connect(lockLayoutAction, &KDualAction::triggered, this, &DolphinMainWindow::togglePanelLockState); // Setup "Information" DolphinDockWidget* infoDock = new DolphinDockWidget(i18nc("@title:window", "Information")); infoDock->setLocked(lock); infoDock->setObjectName(QStringLiteral("infoDock")); infoDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); #ifdef HAVE_BALOO InformationPanel* infoPanel = new InformationPanel(infoDock); infoPanel->setCustomContextMenuActions({lockLayoutAction}); connect(infoPanel, &InformationPanel::urlActivated, this, &DolphinMainWindow::handleUrl); infoDock->setWidget(infoPanel); QAction* infoAction = infoDock->toggleViewAction(); createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-information")), Qt::Key_F11, infoAction, QStringLiteral("show_information_panel")); addDockWidget(Qt::RightDockWidgetArea, infoDock); connect(this, &DolphinMainWindow::urlChanged, infoPanel, &InformationPanel::setUrl); connect(this, &DolphinMainWindow::selectionChanged, infoPanel, &InformationPanel::setSelection); connect(this, &DolphinMainWindow::requestItemInfo, infoPanel, &InformationPanel::requestDelayedItemInfo); #endif // i18n: This is the last paragraph for the "What's This"-texts of all four panels. const QString panelWhatsThis = xi18nc("@info:whatsthis", "To show or " "hide panels like this go to Control|Panels " "or View|Panels."); #ifdef HAVE_BALOO actionCollection()->action(QStringLiteral("show_information_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", " This toggles the " "information panel at the right side of the " "window.The panel provides in-depth information " "about the items your mouse is hovering over or about the selected " "items. Otherwise it informs you about the currently viewed folder." "For single items a preview of their contents is provided.")); #endif infoDock->setWhatsThis(xi18nc("@info:whatsthis", "This panel " "provides in-depth information about the items your mouse is " "hovering over or about the selected items. Otherwise it informs " "you about the currently viewed folder.For single items a " "preview of their contents is provided.You can configure " "which and how details are given here by right-clicking.") + panelWhatsThis); // Setup "Folders" DolphinDockWidget* foldersDock = new DolphinDockWidget(i18nc("@title:window", "Folders")); foldersDock->setLocked(lock); foldersDock->setObjectName(QStringLiteral("foldersDock")); foldersDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); FoldersPanel* foldersPanel = new FoldersPanel(foldersDock); foldersPanel->setCustomContextMenuActions({lockLayoutAction}); foldersDock->setWidget(foldersPanel); QAction* foldersAction = foldersDock->toggleViewAction(); createPanelAction(QIcon::fromTheme(QStringLiteral("folder")), Qt::Key_F7, foldersAction, QStringLiteral("show_folders_panel")); addDockWidget(Qt::LeftDockWidgetArea, foldersDock); connect(this, &DolphinMainWindow::urlChanged, foldersPanel, &FoldersPanel::setUrl); connect(foldersPanel, &FoldersPanel::folderActivated, this, &DolphinMainWindow::changeUrl); connect(foldersPanel, &FoldersPanel::folderMiddleClicked, this, &DolphinMainWindow::openNewTabAfterCurrentTab); connect(foldersPanel, &FoldersPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); actionCollection()->action(QStringLiteral("show_folders_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the " "folders panel at the left side of the window." "It shows the folders of the file system" " in a tree view.")); foldersDock->setWhatsThis(xi18nc("@info:whatsthis", "This panel " "shows the folders of the file system in a " "tree view.Click a folder to go " "there. Click the arrow to the left of a folder to see its subfolders. " "This allows quick switching between any folders.") + panelWhatsThis); // Setup "Terminal" #ifdef HAVE_TERMINAL if (KAuthorized::authorize(QStringLiteral("shell_access"))) { DolphinDockWidget* terminalDock = new DolphinDockWidget(i18nc("@title:window Shell terminal", "Terminal")); terminalDock->setLocked(lock); terminalDock->setObjectName(QStringLiteral("terminalDock")); m_terminalPanel = new TerminalPanel(terminalDock); m_terminalPanel->setCustomContextMenuActions({lockLayoutAction}); terminalDock->setWidget(m_terminalPanel); connect(m_terminalPanel, &TerminalPanel::hideTerminalPanel, terminalDock, &DolphinDockWidget::hide); connect(m_terminalPanel, &TerminalPanel::changeUrl, this, &DolphinMainWindow::slotTerminalDirectoryChanged); connect(terminalDock, &DolphinDockWidget::visibilityChanged, m_terminalPanel, &TerminalPanel::dockVisibilityChanged); connect(terminalDock, &DolphinDockWidget::visibilityChanged, this, &DolphinMainWindow::slotTerminalPanelVisibilityChanged); QAction* terminalAction = terminalDock->toggleViewAction(); createPanelAction(QIcon::fromTheme(QStringLiteral("dialog-scripts")), Qt::Key_F4, terminalAction, QStringLiteral("show_terminal_panel")); addDockWidget(Qt::BottomDockWidgetArea, terminalDock); connect(this, &DolphinMainWindow::urlChanged, m_terminalPanel, &TerminalPanel::setUrl); if (GeneralSettings::version() < 200) { terminalDock->hide(); } actionCollection()->action(QStringLiteral("show_terminal_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the " "terminal panel at the bottom of the window." "The location in the terminal will always match the folder " "view so you can navigate using either.The terminal " "panel is not needed for basic computer usage but can be useful " "for advanced tasks. To learn more about terminals use the help " "in a standalone terminal application like Konsole.")); terminalDock->setWhatsThis(xi18nc("@info:whatsthis", "This is " "the terminal panel. It behaves like a " "normal terminal but will match the location of the folder view " "so you can navigate using either.The terminal panel " "is not needed for basic computer usage but can be useful for " "advanced tasks. To learn more about terminals use the help in a " "standalone terminal application like Konsole.") + panelWhatsThis); } #endif if (GeneralSettings::version() < 200) { infoDock->hide(); foldersDock->hide(); } // Setup "Places" DolphinDockWidget* placesDock = new DolphinDockWidget(i18nc("@title:window", "Places")); placesDock->setLocked(lock); placesDock->setObjectName(QStringLiteral("placesDock")); placesDock->setAllowedAreas(Qt::LeftDockWidgetArea | Qt::RightDockWidgetArea); m_placesPanel = new PlacesPanel(placesDock); m_placesPanel->setCustomContextMenuActions({lockLayoutAction}); placesDock->setWidget(m_placesPanel); QAction *placesAction = placesDock->toggleViewAction(); createPanelAction(QIcon::fromTheme(QStringLiteral("bookmarks")), Qt::Key_F9, placesAction, QStringLiteral("show_places_panel")); addDockWidget(Qt::LeftDockWidgetArea, placesDock); connect(m_placesPanel, &PlacesPanel::placeActivated, this, &DolphinMainWindow::slotPlaceActivated); connect(m_placesPanel, &PlacesPanel::placeMiddleClicked, this, &DolphinMainWindow::openNewTabAfterCurrentTab); connect(m_placesPanel, &PlacesPanel::errorMessage, this, &DolphinMainWindow::showErrorMessage); connect(this, &DolphinMainWindow::urlChanged, m_placesPanel, &PlacesPanel::setUrl); connect(placesDock, &DolphinDockWidget::visibilityChanged, m_tabWidget, &DolphinTabWidget::slotPlacesPanelVisibilityChanged); connect(this, &DolphinMainWindow::settingsChanged, m_placesPanel, &PlacesPanel::readSettings); connect(m_placesPanel, &PlacesPanel::storageTearDownRequested, this, &DolphinMainWindow::slotStorageTearDownFromPlacesRequested); connect(m_placesPanel, &PlacesPanel::storageTearDownExternallyRequested, this, &DolphinMainWindow::slotStorageTearDownExternallyRequested); m_tabWidget->slotPlacesPanelVisibilityChanged(m_placesPanel->isVisible()); auto actionShowAllPlaces = new QAction(QIcon::fromTheme(QStringLiteral("view-hidden")), i18nc("@item:inmenu", "Show Hidden Places"), this); actionShowAllPlaces->setCheckable(true); actionShowAllPlaces->setDisabled(true); actionShowAllPlaces->setWhatsThis(i18nc("@info:whatsthis", "This displays " "all places in the places panel that have been hidden. They will " "appear semi-transparent unless you uncheck their hide property.")); connect(actionShowAllPlaces, &QAction::triggered, this, [actionShowAllPlaces, this](bool checked){ actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden"))); m_placesPanel->showHiddenEntries(checked); }); connect(m_placesPanel, &PlacesPanel::showHiddenEntriesChanged, this, [actionShowAllPlaces] (bool checked){ actionShowAllPlaces->setChecked(checked); actionShowAllPlaces->setIcon(QIcon::fromTheme(checked ? QStringLiteral("view-visible") : QStringLiteral("view-hidden"))); }); actionCollection()->action(QStringLiteral("show_places_panel")) ->setWhatsThis(xi18nc("@info:whatsthis", "This toggles the " "places panel at the left side of the window." "It allows you to go to locations you have " "bookmarked and to access disk or media attached to the computer " "or to the network. It also contains sections to find recently " "saved files or files of a certain type.")); placesDock->setWhatsThis(xi18nc("@info:whatsthis", "This is the " "Places panel. It allows you to go to locations " "you have bookmarked and to access disk or media attached to the " "computer or to the network. It also contains sections to find " "recently saved files or files of a certain type." "Click on an entry to go there. Click with the right mouse button " "instead to open any entry in a new tab or new window." "New entries can be added by dragging folders onto this panel. " "Right-click any section or entry to hide it. Right-click an empty " "space on this panel and select Show Hidden Places" " to display it again.") + panelWhatsThis); // Add actions into the "Panels" menu KActionMenu* panelsMenu = new KActionMenu(i18nc("@action:inmenu View", "Panels"), this); actionCollection()->addAction(QStringLiteral("panels"), panelsMenu); panelsMenu->setDelayed(false); const KActionCollection* ac = actionCollection(); panelsMenu->addAction(ac->action(QStringLiteral("show_places_panel"))); #ifdef HAVE_BALOO panelsMenu->addAction(ac->action(QStringLiteral("show_information_panel"))); #endif panelsMenu->addAction(ac->action(QStringLiteral("show_folders_panel"))); panelsMenu->addAction(ac->action(QStringLiteral("show_terminal_panel"))); panelsMenu->addSeparator(); panelsMenu->addAction(actionShowAllPlaces); panelsMenu->addAction(lockLayoutAction); connect(panelsMenu->menu(), &QMenu::aboutToShow, this, [actionShowAllPlaces, this]{ actionShowAllPlaces->setEnabled(m_placesPanel->hiddenListCount()); }); } void DolphinMainWindow::updateFileAndEditActions() { const KFileItemList list = m_activeViewContainer->view()->selectedItems(); const KActionCollection* col = actionCollection(); QAction* addToPlacesAction = col->action(QStringLiteral("add_to_places")); if (list.isEmpty()) { stateChanged(QStringLiteral("has_no_selection")); addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", m_activeViewContainer->placesText())); } else { stateChanged(QStringLiteral("has_selection")); QAction* renameAction = col->action(KStandardAction::name(KStandardAction::RenameFile)); QAction* moveToTrashAction = col->action(KStandardAction::name(KStandardAction::MoveToTrash)); QAction* deleteAction = col->action(KStandardAction::name(KStandardAction::DeleteFile)); QAction* cutAction = col->action(KStandardAction::name(KStandardAction::Cut)); QAction* deleteWithTrashShortcut = col->action(QStringLiteral("delete_shortcut")); // see DolphinViewActionHandler QAction* showTarget = col->action(QStringLiteral("show_target")); if (list.length() == 1 && list.first().isDir()) { addToPlacesAction->setEnabled(true); addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add '%1' to Places", list.first().name())); } else { addToPlacesAction->setEnabled(false); addToPlacesAction->setText(i18nc("@action:inmenu Add current folder to places", "Add to Places")); } KFileItemListProperties capabilities(list); const bool enableMoveToTrash = capabilities.isLocal() && capabilities.supportsMoving(); renameAction->setEnabled(capabilities.supportsMoving()); moveToTrashAction->setEnabled(enableMoveToTrash); deleteAction->setEnabled(capabilities.supportsDeleting()); deleteWithTrashShortcut->setEnabled(capabilities.supportsDeleting() && !enableMoveToTrash); cutAction->setEnabled(capabilities.supportsMoving()); showTarget->setEnabled(list.length() == 1 && list.at(0).isLink()); } } void DolphinMainWindow::updateViewActions() { m_actionHandler->updateViewActions(); QAction* showFilterBarAction = actionCollection()->action(QStringLiteral("show_filter_bar")); showFilterBarAction->setChecked(m_activeViewContainer->isFilterBarVisible()); updateSplitAction(); QAction* editableLocactionAction = actionCollection()->action(QStringLiteral("editable_location")); const KUrlNavigator* urlNavigator = m_activeViewContainer->urlNavigator(); editableLocactionAction->setChecked(urlNavigator->isUrlEditable()); } void DolphinMainWindow::updateGoActions() { QAction* goUpAction = actionCollection()->action(KStandardAction::name(KStandardAction::Up)); const QUrl currentUrl = m_activeViewContainer->url(); // I think this is one of the best places to firstly be confronted // with a file system and its hierarchy. Talking about the root // directory might seem too much here but it is the question that // naturally arises in this context. goUpAction->setWhatsThis(xi18nc("@info:whatsthis", "Go to " "the folder that contains the currently viewed one." "All files and folders are organized in a hierarchical " "file system. At the top of this hierarchy is " "a directory that contains all data connected to this computer" "—the root directory.")); goUpAction->setEnabled(KIO::upUrl(currentUrl) != currentUrl); } void DolphinMainWindow::createControlButton() { if (m_controlButton) { return; } Q_ASSERT(!m_controlButton); m_controlButton = new QToolButton(this); m_controlButton->setIcon(QIcon::fromTheme(QStringLiteral("application-menu"))); m_controlButton->setToolTip(i18nc("@action", "Show menu")); m_controlButton->setAttribute(Qt::WidgetAttribute::WA_CustomWhatsThis); m_controlButton->setPopupMode(QToolButton::InstantPopup); QMenu* controlMenu = new QMenu(m_controlButton); connect(controlMenu, &QMenu::aboutToShow, this, &DolphinMainWindow::updateControlMenu); controlMenu->installEventFilter(this); m_controlButton->setMenu(controlMenu); toolBar()->addWidget(m_controlButton); connect(toolBar(), &KToolBar::iconSizeChanged, m_controlButton, &QToolButton::setIconSize); // The added widgets are owned by the toolbar and may get deleted when e.g. the toolbar // gets edited. In this case we must add them again. The adding is done asynchronously by // m_updateToolBarTimer. connect(m_controlButton, &QToolButton::destroyed, this, &DolphinMainWindow::slotControlButtonDeleted); m_updateToolBarTimer = new QTimer(this); m_updateToolBarTimer->setInterval(500); connect(m_updateToolBarTimer, &QTimer::timeout, this, &DolphinMainWindow::updateToolBar); } void DolphinMainWindow::deleteControlButton() { delete m_controlButton; m_controlButton = nullptr; delete m_updateToolBarTimer; m_updateToolBarTimer = nullptr; } bool DolphinMainWindow::addActionToMenu(QAction* action, QMenu* menu) { Q_ASSERT(action); Q_ASSERT(menu); const KToolBar* toolBarWidget = toolBar(); foreach (const QWidget* widget, action->associatedWidgets()) { if (widget == toolBarWidget) { return false; } } menu->addAction(action); return true; } void DolphinMainWindow::refreshViews() { m_tabWidget->refreshViews(); if (GeneralSettings::modifiedStartupSettings()) { // The startup settings have been changed by the user (see bug #254947). // Synchronize the split-view setting with the active view: const bool splitView = GeneralSettings::splitView(); m_tabWidget->currentTabPage()->setSplitViewEnabled(splitView); updateSplitAction(); updateWindowTitle(); } emit settingsChanged(); } void DolphinMainWindow::clearStatusBar() { m_activeViewContainer->statusBar()->resetToDefaultText(); } void DolphinMainWindow::connectViewSignals(DolphinViewContainer* container) { connect(container, &DolphinViewContainer::showFilterBarChanged, this, &DolphinMainWindow::updateFilterBarAction); connect(container, &DolphinViewContainer::writeStateChanged, this, &DolphinMainWindow::slotWriteStateChanged); connect(container, &DolphinViewContainer::searchModeEnabledChanged, this, &DolphinMainWindow::updateSearchAction); const QAction* toggleSearchAction = actionCollection()->action(QStringLiteral("toggle_search")); connect(toggleSearchAction, &QAction::triggered, container, &DolphinViewContainer::setSearchModeEnabled); const DolphinView* view = container->view(); connect(view, &DolphinView::selectionChanged, this, &DolphinMainWindow::slotSelectionChanged); connect(view, &DolphinView::requestItemInfo, this, &DolphinMainWindow::requestItemInfo); connect(view, &DolphinView::tabRequested, this, &DolphinMainWindow::openNewTab); connect(view, &DolphinView::requestContextMenu, this, &DolphinMainWindow::openContextMenu); connect(view, &DolphinView::directoryLoadingStarted, this, &DolphinMainWindow::enableStopAction); connect(view, &DolphinView::directoryLoadingCompleted, this, &DolphinMainWindow::disableStopAction); connect(view, &DolphinView::directoryLoadingCompleted, this, &DolphinMainWindow::slotDirectoryLoadingCompleted); connect(view, &DolphinView::goBackRequested, this, &DolphinMainWindow::goBack); connect(view, &DolphinView::goForwardRequested, this, &DolphinMainWindow::goForward); connect(view, &DolphinView::urlActivated, this, &DolphinMainWindow::handleUrl); const KUrlNavigator* navigator = container->urlNavigator(); connect(navigator, &KUrlNavigator::urlChanged, this, &DolphinMainWindow::changeUrl); connect(navigator, &KUrlNavigator::historyChanged, this, &DolphinMainWindow::updateHistory); connect(navigator, &KUrlNavigator::editableStateChanged, this, &DolphinMainWindow::slotEditableStateChanged); connect(navigator, &KUrlNavigator::tabRequested, this, &DolphinMainWindow::openNewTabAfterLastTab); } void DolphinMainWindow::updateSplitAction() { QAction* splitAction = actionCollection()->action(QStringLiteral("split_view")); const DolphinTabPage* tabPage = m_tabWidget->currentTabPage(); if (tabPage->splitViewEnabled()) { if (GeneralSettings::closeActiveSplitView() ? tabPage->primaryViewActive() : !tabPage->primaryViewActive()) { splitAction->setText(i18nc("@action:intoolbar Close left view", "Close")); splitAction->setToolTip(i18nc("@info", "Close left view")); splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-left-close"))); } else { splitAction->setText(i18nc("@action:intoolbar Close right view", "Close")); splitAction->setToolTip(i18nc("@info", "Close right view")); splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-close"))); } } else { splitAction->setText(i18nc("@action:intoolbar Split view", "Split")); splitAction->setToolTip(i18nc("@info", "Split view")); splitAction->setIcon(QIcon::fromTheme(QStringLiteral("view-right-new"))); } } bool DolphinMainWindow::isKompareInstalled() const { static bool initialized = false; static bool installed = false; if (!initialized) { // TODO: maybe replace this approach later by using a menu // plugin like kdiff3plugin.cpp installed = !QStandardPaths::findExecutable(QStringLiteral("kompare")).isEmpty(); initialized = true; } return installed; } void DolphinMainWindow::createPanelAction(const QIcon& icon, const QKeySequence& shortcut, QAction* dockAction, const QString& actionName) { QAction* panelAction = actionCollection()->addAction(actionName); panelAction->setCheckable(true); panelAction->setChecked(dockAction->isChecked()); panelAction->setText(dockAction->text()); panelAction->setIcon(icon); actionCollection()->setDefaultShortcut(panelAction, shortcut); connect(panelAction, &QAction::triggered, dockAction, &QAction::trigger); connect(dockAction, &QAction::toggled, panelAction, &QAction::setChecked); } void DolphinMainWindow::setupWhatsThis() { // main widgets menuBar()->setWhatsThis(xi18nc("@info:whatsthis", "This is the " "Menubar. It provides access to commands and " "configuration options. Left-click on any of the menus on this " "bar to see its contents.The Menubar can be hidden " "by unchecking Settings|Show Menubar. Then " "most of its contents become available through a Control" " button on the Toolbar.")); toolBar()->setWhatsThis(xi18nc("@info:whatsthis", "This is the " "Toolbar. It allows quick access to " "frequently used actions.It is highly customizable. " "All items you see in the Control menu or " "in the Menubar can be placed on the " "Toolbar. Just right-click on it and select Configure " "Toolbars… or find this action in the " "Control or Settings menu." "The location of the bar and the style of its " "buttons can also be changed in the right-click menu. Right-click " "a button if you want to show or hide its text.")); m_tabWidget->setWhatsThis(xi18nc("@info:whatsthis main view", "Here you can see the folders and " "files that are at the location described in " "the Location Bar above. This area is the " "central part of this application where you navigate to the files " "you want to use.For an elaborate and general " "introduction to this application " "click here. This will open an introductory article from " "the KDE UserBase Wiki.For brief " "explanations of all the features of this view " "click here " "instead. This will open a page from the Handbook" " that covers the basics.")); // Settings menu actionCollection()->action(KStandardAction::name(KStandardAction::KeyBindings)) ->setWhatsThis(xi18nc("@info:whatsthis","This opens a window " "that lists the keyboard shortcuts." "There you can set up key combinations to trigger an action when " "they are pressed simultaneously. All commands in this application can " "be triggered this way.")); actionCollection()->action(KStandardAction::name(KStandardAction::ConfigureToolbars)) ->setWhatsThis(xi18nc("@info:whatsthis","This opens a window in which " "you can change which buttons appear on the Toolbar." "All items you see in the Control menu " "or in the Menubar can also be placed on the Toolbar.")); actionCollection()->action(KStandardAction::name(KStandardAction::Preferences)) ->setWhatsThis(xi18nc("@info:whatsthis","This opens a window where you can " "change a multitude of settings for this application. For an explanation " "of the various settings go to the chapter Configuring Dolphin" " in Help|Dolphin Handbook.")); // Help menu // The whatsthis has to be set for the m_helpMenu and for the // StandardAction separately because both are used in different locations. // m_helpMenu is only used for createControlButton() button. // Links do not work within the Menubar so texts without links are provided there. // i18n: If the external link isn't available in your language you should // probably state the external link language at least in brackets to not // frustrate the user. If there are multiple languages that the user might // know with a reasonable chance you might want to have 2 external links. // The same is in my opinion true for every external link you translate. const QString whatsThisHelpContents = xi18nc("@info:whatsthis handbook", "This opens the Handbook for this application. It provides " "explanations for every part of Dolphin."); actionCollection()->action(KStandardAction::name(KStandardAction::HelpContents)) ->setWhatsThis(whatsThisHelpContents + xi18nc("@info:whatsthis second half of handbook hb text without link", "If you want more elaborate introductions to the " "different features of Dolphin " "go to the KDE UserBase Wiki.")); m_helpMenu->action(KHelpMenu::menuHelpContents)->setWhatsThis(whatsThisHelpContents + xi18nc("@info:whatsthis second half of handbook text with link", "If you want more elaborate introductions to the " "different features of Dolphin " "click here. " "It will open the dedicated page in the KDE UserBase Wiki.")); const QString whatsThisWhatsThis = xi18nc("@info:whatsthis whatsthis button", "This is the button that invokes the help feature you are " "using right now! Click it, then click any component of this " "application to ask \"What's this?\" about it. The mouse cursor " "will change appearance if no help is available for a spot."); actionCollection()->action(KStandardAction::name(KStandardAction::WhatsThis)) ->setWhatsThis(whatsThisWhatsThis + xi18nc("@info:whatsthis second half of whatsthis button text without link", "There are two other ways to get help for this application: The " "Dolphin Handbook in the Help" " menu and the KDE UserBase Wiki " "article about File Management online." "The \"What's this?\" help is " "missing in most other windows so don't get too used to this.")); m_helpMenu->action(KHelpMenu::menuWhatsThis)->setWhatsThis(whatsThisWhatsThis + xi18nc("@info:whatsthis second half of whatsthis button text with link", "There are two other ways to get help: " "The Dolphin Handbook and " "the KDE " "UserBase Wiki.The \"What's this?\" help is " "missing in most other windows so don't get too used to this.")); const QString whatsThisReportBug = xi18nc("@info:whatsthis","This opens a " "window that will guide you through reporting errors or flaws " "in this application or in other KDE software."); actionCollection()->action(KStandardAction::name(KStandardAction::ReportBug)) ->setWhatsThis(whatsThisReportBug); m_helpMenu->action(KHelpMenu::menuReportBug)->setWhatsThis(whatsThisReportBug + xi18nc("@info:whatsthis second half of reportbug text with link", "High-quality bug reports are much appreciated. To learn " "how to make your bug report as effective as possible " "" "click here.")); const QString whatsThisDonate = xi18nc("@info:whatsthis","This opens a " "web page where you can donate to " "support the continued work on this application and many " "other projects by the KDE community." "Donating is the easiest and fastest way to efficiently " "support KDE and its projects. KDE projects are available for " "free therefore your donation is needed to cover things that " "require money like servers, contributor meetings, etc." "KDE e.V. is the non-profit " "organization behind the KDE community."); actionCollection()->action(KStandardAction::name(KStandardAction::Donate)) ->setWhatsThis(whatsThisDonate); m_helpMenu->action(KHelpMenu::menuDonate)->setWhatsThis(whatsThisDonate); const QString whatsThisSwitchLanguage = xi18nc("@info:whatsthis", "With this you can change the language this application uses." "You can even set secondary languages which will be used " "if texts are not available in your preferred language."); actionCollection()->action(KStandardAction::name(KStandardAction::SwitchApplicationLanguage)) ->setWhatsThis(whatsThisSwitchLanguage); m_helpMenu->action(KHelpMenu::menuSwitchLanguage)->setWhatsThis(whatsThisSwitchLanguage); const QString whatsThisAboutApp = xi18nc("@info:whatsthis","This opens a " "window that informs you about the version, license, " "used libraries and maintainers of this application."); actionCollection()->action(KStandardAction::name(KStandardAction::AboutApp)) ->setWhatsThis(whatsThisAboutApp); m_helpMenu->action(KHelpMenu::menuAboutApp)->setWhatsThis(whatsThisAboutApp); const QString whatsThisAboutKDE = xi18nc("@info:whatsthis","This opens a " "window with information about KDE. " "The KDE community are the people behind this free software." "If you like using this application but don't know " "about KDE or want to see a cute dragon have a look!"); actionCollection()->action(KStandardAction::name(KStandardAction::AboutKDE)) ->setWhatsThis(whatsThisAboutKDE); m_helpMenu->action(KHelpMenu::menuAboutKDE)->setWhatsThis(whatsThisAboutKDE); } bool DolphinMainWindow::event(QEvent *event) { if (event->type() == QEvent::WhatsThisClicked) { event->accept(); QWhatsThisClickedEvent* whatsThisEvent = dynamic_cast(event); QDesktopServices::openUrl(QUrl(whatsThisEvent->href())); return true; } return KXmlGuiWindow::event(event); } bool DolphinMainWindow::eventFilter(QObject* obj, QEvent* event) { Q_UNUSED(obj) if (event->type() == QEvent::WhatsThisClicked) { event->accept(); QWhatsThisClickedEvent* whatsThisEvent = dynamic_cast(event); QDesktopServices::openUrl(QUrl(whatsThisEvent->href())); return true; } return false; } DolphinMainWindow::UndoUiInterface::UndoUiInterface() : KIO::FileUndoManager::UiInterface() { } DolphinMainWindow::UndoUiInterface::~UndoUiInterface() { } void DolphinMainWindow::UndoUiInterface::jobError(KIO::Job* job) { DolphinMainWindow* mainWin= qobject_cast(parentWidget()); if (mainWin) { DolphinViewContainer* container = mainWin->activeViewContainer(); container->showMessage(job->errorString(), DolphinViewContainer::Error); } else { KIO::FileUndoManager::UiInterface::jobError(job); } } bool DolphinMainWindow::isUrlOpen(const QString& url) { if (m_tabWidget->getIndexByUrl(QUrl::fromUserInput((url))).first >= 0) { return true; } else { return false; } } diff --git a/src/dolphinui.rc b/src/dolphinui.rc index 754670a7e..d52e64edb 100644 --- a/src/dolphinui.rc +++ b/src/dolphinui.rc @@ -1,125 +1,130 @@ - + + + + + Location Bar Main Toolbar + diff --git a/src/views/dolphinview.cpp b/src/views/dolphinview.cpp index e6b232dcc..3597a2aa4 100644 --- a/src/views/dolphinview.cpp +++ b/src/views/dolphinview.cpp @@ -1,1843 +1,1858 @@ /*************************************************************************** * Copyright (C) 2006-2009 by Peter Penz * * Copyright (C) 2006 by Gregor Kališnik * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "dolphinview.h" #include "dolphin_detailsmodesettings.h" #include "dolphin_generalsettings.h" #include "dolphinitemlistview.h" #include "dolphinnewfilemenuobserver.h" #include "draganddrophelper.h" #include "kitemviews/kfileitemlistview.h" #include "kitemviews/kfileitemmodel.h" #include "kitemviews/kitemlistcontainer.h" #include "kitemviews/kitemlistcontroller.h" #include "kitemviews/kitemlistheader.h" #include "kitemviews/kitemlistselectionmanager.h" #include "renamedialog.h" #include "versioncontrol/versioncontrolobserver.h" #include "viewproperties.h" #include "views/tooltips/tooltipmanager.h" #include "zoomlevelinfo.h" #ifdef HAVE_BALOO #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include DolphinView::DolphinView(const QUrl& url, QWidget* parent) : QWidget(parent), m_active(true), m_tabsForFiles(false), m_assureVisibleCurrentIndex(false), m_isFolderWritable(true), m_dragging(false), m_url(url), m_viewPropertiesContext(), m_mode(DolphinView::IconsView), m_visibleRoles(), m_topLayout(nullptr), m_model(nullptr), m_view(nullptr), m_container(nullptr), m_toolTipManager(nullptr), m_selectionChangedTimer(nullptr), m_currentItemUrl(), m_scrollToCurrentItem(false), m_restoredContentsPosition(), m_selectedUrls(), m_clearSelectionBeforeSelectingNewItems(false), m_markFirstNewlySelectedItemAsCurrent(false), m_versionControlObserver(nullptr), m_twoClicksRenamingTimer(nullptr) { m_topLayout = new QVBoxLayout(this); m_topLayout->setSpacing(0); m_topLayout->setContentsMargins(0, 0, 0, 0); // When a new item has been created by the "Create New..." menu, the item should // get selected and it must be assured that the item will get visible. As the // creation is done asynchronously, several signals must be checked: connect(&DolphinNewFileMenuObserver::instance(), &DolphinNewFileMenuObserver::itemCreated, this, &DolphinView::observeCreatedItem); m_selectionChangedTimer = new QTimer(this); m_selectionChangedTimer->setSingleShot(true); m_selectionChangedTimer->setInterval(300); connect(m_selectionChangedTimer, &QTimer::timeout, this, &DolphinView::emitSelectionChangedSignal); m_model = new KFileItemModel(this); m_view = new DolphinItemListView(); m_view->setEnabledSelectionToggles(GeneralSettings::showSelectionToggle()); m_view->setVisibleRoles({"text"}); applyModeToView(); KItemListController* controller = new KItemListController(m_model, m_view, this); const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; controller->setAutoActivationDelay(delay); // The EnlargeSmallPreviews setting can only be changed after the model // has been set in the view by KItemListController. m_view->setEnlargeSmallPreviews(GeneralSettings::enlargeSmallPreviews()); m_container = new KItemListContainer(controller, this); m_container->installEventFilter(this); setFocusProxy(m_container); connect(m_container->horizontalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); }); connect(m_container->verticalScrollBar(), &QScrollBar::valueChanged, this, [=] { hideToolTip(); }); controller->setSelectionBehavior(KItemListController::MultiSelection); connect(controller, &KItemListController::itemActivated, this, &DolphinView::slotItemActivated); connect(controller, &KItemListController::itemsActivated, this, &DolphinView::slotItemsActivated); connect(controller, &KItemListController::itemMiddleClicked, this, &DolphinView::slotItemMiddleClicked); connect(controller, &KItemListController::itemContextMenuRequested, this, &DolphinView::slotItemContextMenuRequested); connect(controller, &KItemListController::viewContextMenuRequested, this, &DolphinView::slotViewContextMenuRequested); connect(controller, &KItemListController::headerContextMenuRequested, this, &DolphinView::slotHeaderContextMenuRequested); connect(controller, &KItemListController::mouseButtonPressed, this, &DolphinView::slotMouseButtonPressed); connect(controller, &KItemListController::itemHovered, this, &DolphinView::slotItemHovered); connect(controller, &KItemListController::itemUnhovered, this, &DolphinView::slotItemUnhovered); connect(controller, &KItemListController::itemDropEvent, this, &DolphinView::slotItemDropEvent); connect(controller, &KItemListController::escapePressed, this, &DolphinView::stopLoading); connect(controller, &KItemListController::modelChanged, this, &DolphinView::slotModelChanged); connect(controller, &KItemListController::selectedItemTextPressed, this, &DolphinView::slotSelectedItemTextPressed); connect(m_model, &KFileItemModel::directoryLoadingStarted, this, &DolphinView::slotDirectoryLoadingStarted); connect(m_model, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::slotDirectoryLoadingCompleted); connect(m_model, &KFileItemModel::directoryLoadingCanceled, this, &DolphinView::directoryLoadingCanceled); connect(m_model, &KFileItemModel::directoryLoadingProgress, this, &DolphinView::directoryLoadingProgress); connect(m_model, &KFileItemModel::directorySortingProgress, this, &DolphinView::directorySortingProgress); connect(m_model, &KFileItemModel::itemsChanged, this, &DolphinView::slotItemsChanged); connect(m_model, &KFileItemModel::itemsRemoved, this, &DolphinView::itemCountChanged); connect(m_model, &KFileItemModel::itemsInserted, this, &DolphinView::itemCountChanged); connect(m_model, &KFileItemModel::infoMessage, this, &DolphinView::infoMessage); connect(m_model, &KFileItemModel::errorMessage, this, &DolphinView::errorMessage); connect(m_model, &KFileItemModel::directoryRedirection, this, &DolphinView::slotDirectoryRedirection); connect(m_model, &KFileItemModel::urlIsFileError, this, &DolphinView::urlIsFileError); m_view->installEventFilter(this); connect(m_view, &DolphinItemListView::sortOrderChanged, this, &DolphinView::slotSortOrderChangedByHeader); connect(m_view, &DolphinItemListView::sortRoleChanged, this, &DolphinView::slotSortRoleChangedByHeader); connect(m_view, &DolphinItemListView::visibleRolesChanged, this, &DolphinView::slotVisibleRolesChangedByHeader); connect(m_view, &DolphinItemListView::roleEditingCanceled, this, &DolphinView::slotRoleEditingCanceled); connect(m_view->header(), &KItemListHeader::columnWidthChangeFinished, this, &DolphinView::slotHeaderColumnWidthChangeFinished); KItemListSelectionManager* selectionManager = controller->selectionManager(); connect(selectionManager, &KItemListSelectionManager::selectionChanged, this, &DolphinView::slotSelectionChanged); #ifdef HAVE_BALOO m_toolTipManager = new ToolTipManager(this); connect(m_toolTipManager, &ToolTipManager::urlActivated, this, &DolphinView::urlActivated); #endif m_versionControlObserver = new VersionControlObserver(this); m_versionControlObserver->setView(this); m_versionControlObserver->setModel(m_model); connect(m_versionControlObserver, &VersionControlObserver::infoMessage, this, &DolphinView::infoMessage); connect(m_versionControlObserver, &VersionControlObserver::errorMessage, this, &DolphinView::errorMessage); connect(m_versionControlObserver, &VersionControlObserver::operationCompletedMessage, this, &DolphinView::operationCompletedMessage); m_twoClicksRenamingTimer = new QTimer(this); m_twoClicksRenamingTimer->setSingleShot(true); connect(m_twoClicksRenamingTimer, &QTimer::timeout, this, &DolphinView::slotTwoClicksRenamingTimerTimeout); applyViewProperties(); m_topLayout->addWidget(m_container); loadDirectory(url); } DolphinView::~DolphinView() { } QUrl DolphinView::url() const { return m_url; } void DolphinView::setActive(bool active) { if (active == m_active) { return; } m_active = active; updatePalette(); if (active) { m_container->setFocus(); emit activated(); emit writeStateChanged(m_isFolderWritable); } } bool DolphinView::isActive() const { return m_active; } void DolphinView::setMode(Mode mode) { if (mode != m_mode) { ViewProperties props(viewPropertiesUrl()); props.setViewMode(mode); // We pass the new ViewProperties to applyViewProperties, rather than // storing them on disk and letting applyViewProperties() read them // from there, to prevent that changing the view mode fails if the // .directory file is not writable (see bug 318534). applyViewProperties(props); } } DolphinView::Mode DolphinView::mode() const { return m_mode; } void DolphinView::setPreviewsShown(bool show) { if (previewsShown() == show) { return; } ViewProperties props(viewPropertiesUrl()); props.setPreviewsShown(show); const int oldZoomLevel = m_view->zoomLevel(); m_view->setPreviewsShown(show); emit previewsShownChanged(show); const int newZoomLevel = m_view->zoomLevel(); if (newZoomLevel != oldZoomLevel) { emit zoomLevelChanged(newZoomLevel, oldZoomLevel); } } bool DolphinView::previewsShown() const { return m_view->previewsShown(); } void DolphinView::setHiddenFilesShown(bool show) { if (m_model->showHiddenFiles() == show) { return; } const KFileItemList itemList = selectedItems(); m_selectedUrls.clear(); m_selectedUrls = itemList.urlList(); ViewProperties props(viewPropertiesUrl()); props.setHiddenFilesShown(show); m_model->setShowHiddenFiles(show); emit hiddenFilesShownChanged(show); } bool DolphinView::hiddenFilesShown() const { return m_model->showHiddenFiles(); } void DolphinView::setGroupedSorting(bool grouped) { if (grouped == groupedSorting()) { return; } ViewProperties props(viewPropertiesUrl()); props.setGroupedSorting(grouped); props.save(); m_container->controller()->model()->setGroupedSorting(grouped); emit groupedSortingChanged(grouped); } bool DolphinView::groupedSorting() const { return m_model->groupedSorting(); } KFileItemList DolphinView::items() const { KFileItemList list; const int itemCount = m_model->count(); list.reserve(itemCount); for (int i = 0; i < itemCount; ++i) { list.append(m_model->fileItem(i)); } return list; } int DolphinView::itemsCount() const { return m_model->count(); } KFileItemList DolphinView::selectedItems() const { const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); KFileItemList selectedItems; const auto items = selectionManager->selectedItems(); selectedItems.reserve(items.count()); for (int index : items) { selectedItems.append(m_model->fileItem(index)); } return selectedItems; } int DolphinView::selectedItemsCount() const { const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); return selectionManager->selectedItems().count(); } void DolphinView::markUrlsAsSelected(const QList& urls) { m_selectedUrls = urls; } void DolphinView::markUrlAsCurrent(const QUrl &url) { m_currentItemUrl = url; m_scrollToCurrentItem = true; } void DolphinView::selectItems(const QRegExp& pattern, bool enabled) { const KItemListSelectionManager::SelectionMode mode = enabled ? KItemListSelectionManager::Select : KItemListSelectionManager::Deselect; KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); for (int index = 0; index < m_model->count(); index++) { const KFileItem item = m_model->fileItem(index); if (pattern.exactMatch(item.text())) { // An alternative approach would be to store the matching items in a KItemSet and // select them in one go after the loop, but we'd need a new function // KItemListSelectionManager::setSelected(KItemSet, SelectionMode mode) // for that. selectionManager->setSelected(index, 1, mode); } } } void DolphinView::setZoomLevel(int level) { const int oldZoomLevel = zoomLevel(); m_view->setZoomLevel(level); if (zoomLevel() != oldZoomLevel) { hideToolTip(); emit zoomLevelChanged(zoomLevel(), oldZoomLevel); } } int DolphinView::zoomLevel() const { return m_view->zoomLevel(); } void DolphinView::setSortRole(const QByteArray& role) { if (role != sortRole()) { updateSortRole(role); } } QByteArray DolphinView::sortRole() const { const KItemModelBase* model = m_container->controller()->model(); return model->sortRole(); } void DolphinView::setSortOrder(Qt::SortOrder order) { if (sortOrder() != order) { updateSortOrder(order); } } Qt::SortOrder DolphinView::sortOrder() const { return m_model->sortOrder(); } void DolphinView::setSortFoldersFirst(bool foldersFirst) { if (sortFoldersFirst() != foldersFirst) { updateSortFoldersFirst(foldersFirst); } } bool DolphinView::sortFoldersFirst() const { return m_model->sortDirectoriesFirst(); } void DolphinView::setVisibleRoles(const QList& roles) { const QList previousRoles = roles; ViewProperties props(viewPropertiesUrl()); props.setVisibleRoles(roles); m_visibleRoles = roles; m_view->setVisibleRoles(roles); emit visibleRolesChanged(m_visibleRoles, previousRoles); } QList DolphinView::visibleRoles() const { return m_visibleRoles; } void DolphinView::reload() { QByteArray viewState; QDataStream saveStream(&viewState, QIODevice::WriteOnly); saveState(saveStream); setUrl(url()); loadDirectory(url(), true); QDataStream restoreStream(viewState); restoreState(restoreStream); } void DolphinView::readSettings() { const int oldZoomLevel = m_view->zoomLevel(); GeneralSettings::self()->load(); m_view->readSettings(); applyViewProperties(); const int delay = GeneralSettings::autoExpandFolders() ? 750 : -1; m_container->controller()->setAutoActivationDelay(delay); const int newZoomLevel = m_view->zoomLevel(); if (newZoomLevel != oldZoomLevel) { emit zoomLevelChanged(newZoomLevel, oldZoomLevel); } } void DolphinView::writeSettings() { GeneralSettings::self()->save(); m_view->writeSettings(); } void DolphinView::setNameFilter(const QString& nameFilter) { m_model->setNameFilter(nameFilter); } QString DolphinView::nameFilter() const { return m_model->nameFilter(); } void DolphinView::setMimeTypeFilters(const QStringList& filters) { return m_model->setMimeTypeFilters(filters); } QStringList DolphinView::mimeTypeFilters() const { return m_model->mimeTypeFilters(); } QString DolphinView::statusBarText() const { QString summary; QString foldersText; QString filesText; int folderCount = 0; int fileCount = 0; KIO::filesize_t totalFileSize = 0; if (m_container->controller()->selectionManager()->hasSelection()) { // Give a summary of the status of the selected files const KFileItemList list = selectedItems(); foreach (const KFileItem& item, list) { if (item.isDir()) { ++folderCount; } else { ++fileCount; totalFileSize += item.size(); } } if (folderCount + fileCount == 1) { // If only one item is selected, show info about it return list.first().getStatusBarInfo(); } else { // At least 2 items are selected foldersText = i18ncp("@info:status", "1 Folder selected", "%1 Folders selected", folderCount); filesText = i18ncp("@info:status", "1 File selected", "%1 Files selected", fileCount); } } else { calculateItemCount(fileCount, folderCount, totalFileSize); foldersText = i18ncp("@info:status", "1 Folder", "%1 Folders", folderCount); filesText = i18ncp("@info:status", "1 File", "%1 Files", fileCount); } if (fileCount > 0 && folderCount > 0) { summary = i18nc("@info:status folders, files (size)", "%1, %2 (%3)", foldersText, filesText, KFormat().formatByteSize(totalFileSize)); } else if (fileCount > 0) { summary = i18nc("@info:status files (size)", "%1 (%2)", filesText, KFormat().formatByteSize(totalFileSize)); } else if (folderCount > 0) { summary = foldersText; } else { summary = i18nc("@info:status", "0 Folders, 0 Files"); } return summary; } QList DolphinView::versionControlActions(const KFileItemList& items) const { QList actions; if (items.isEmpty()) { const KFileItem item = m_model->rootItem(); if (!item.isNull()) { actions = m_versionControlObserver->actions(KFileItemList() << item); } } else { actions = m_versionControlObserver->actions(items); } return actions; } void DolphinView::setUrl(const QUrl& url) { if (url == m_url) { return; } clearSelection(); m_url = url; hideToolTip(); disconnect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished); // It is important to clear the items from the model before // applying the view properties, otherwise expensive operations // might be done on the existing items although they get cleared // anyhow afterwards by loadDirectory(). m_model->clear(); applyViewProperties(); loadDirectory(url); emit urlChanged(url); } void DolphinView::selectAll() { KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); selectionManager->setSelected(0, m_model->count()); } void DolphinView::invertSelection() { KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); selectionManager->setSelected(0, m_model->count(), KItemListSelectionManager::Toggle); } void DolphinView::clearSelection() { m_selectedUrls.clear(); m_container->controller()->selectionManager()->clearSelection(); } void DolphinView::renameSelectedItems() { const KFileItemList items = selectedItems(); if (items.isEmpty()) { return; } if (items.count() == 1 && GeneralSettings::renameInline()) { const int index = m_model->index(items.first()); m_view->editRole(index, "text"); hideToolTip(); connect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished); } else { RenameDialog* dialog = new RenameDialog(this, items); connect(dialog, &RenameDialog::renamingFinished, this, &DolphinView::slotRenameDialogRenamingFinished); dialog->open(); } // Assure that the current index remains visible when KFileItemModel // will notify the view about changed items (which might result in // a changed sorting). m_assureVisibleCurrentIndex = true; } void DolphinView::trashSelectedItems() { const QList list = simplifiedSelectedUrls(); KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(window()); if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Trash, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job* job = KIO::trash(list); KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Trash, list, QUrl(QStringLiteral("trash:/")), job); KJobWidgets::setWindow(job, this); connect(job, &KIO::Job::result, this, &DolphinView::slotTrashFileFinished); } } void DolphinView::deleteSelectedItems() { const QList list = simplifiedSelectedUrls(); KIO::JobUiDelegate uiDelegate; uiDelegate.setWindow(window()); if (uiDelegate.askDeleteConfirmation(list, KIO::JobUiDelegate::Delete, KIO::JobUiDelegate::DefaultConfirmation)) { KIO::Job* job = KIO::del(list); KJobWidgets::setWindow(job, this); connect(job, &KIO::Job::result, this, &DolphinView::slotDeleteFileFinished); } } void DolphinView::cutSelectedItems() { QMimeData* mimeData = selectionMimeData(); KIO::setClipboardDataCut(mimeData, true); QApplication::clipboard()->setMimeData(mimeData); } void DolphinView::copySelectedItems() { QMimeData* mimeData = selectionMimeData(); QApplication::clipboard()->setMimeData(mimeData); } void DolphinView::paste() { pasteToUrl(url()); } void DolphinView::pasteIntoFolder() { const KFileItemList items = selectedItems(); if ((items.count() == 1) && items.first().isDir()) { pasteToUrl(items.first().url()); } } void DolphinView::stopLoading() { m_model->cancelDirectoryLoading(); } void DolphinView::updatePalette() { QColor color = KColorScheme(isActiveWindow() ? QPalette::Active : QPalette::Inactive, KColorScheme::View).background().color(); if (!m_active) { color.setAlpha(150); } QWidget* viewport = m_container->viewport(); if (viewport) { QPalette palette; palette.setColor(viewport->backgroundRole(), color); viewport->setPalette(palette); } update(); } void DolphinView::abortTwoClicksRenaming() { m_twoClicksRenamingItemUrl.clear(); m_twoClicksRenamingTimer->stop(); } bool DolphinView::eventFilter(QObject* watched, QEvent* event) { switch (event->type()) { case QEvent::PaletteChange: updatePalette(); QPixmapCache::clear(); break; case QEvent::WindowActivate: case QEvent::WindowDeactivate: updatePalette(); break; case QEvent::KeyPress: hideToolTip(ToolTipManager::HideBehavior::Instantly); if (GeneralSettings::useTabForSwitchingSplitView()) { QKeyEvent* keyEvent = static_cast(event); if (keyEvent->key() == Qt::Key_Tab && keyEvent->modifiers() == Qt::NoModifier) { emit toggleActiveViewRequested(); return true; } } break; case QEvent::FocusIn: if (watched == m_container) { setActive(true); } break; case QEvent::GraphicsSceneDragEnter: if (watched == m_view) { m_dragging = true; abortTwoClicksRenaming(); } break; case QEvent::GraphicsSceneDragLeave: if (watched == m_view) { m_dragging = false; } break; case QEvent::GraphicsSceneDrop: if (watched == m_view) { m_dragging = false; } default: break; } return QWidget::eventFilter(watched, event); } void DolphinView::wheelEvent(QWheelEvent* event) { if (event->modifiers().testFlag(Qt::ControlModifier)) { const int numDegrees = event->delta() / 8; const int numSteps = numDegrees / 15; setZoomLevel(zoomLevel() + numSteps); event->accept(); } else { event->ignore(); } } void DolphinView::hideEvent(QHideEvent* event) { hideToolTip(); QWidget::hideEvent(event); } bool DolphinView::event(QEvent* event) { if (event->type() == QEvent::WindowDeactivate) { /* See Bug 297355 * Dolphin leaves file preview tooltips open even when is not visible. * * Hide tool-tip when Dolphin loses focus. */ hideToolTip(); abortTwoClicksRenaming(); } return QWidget::event(event); } void DolphinView::activate() { setActive(true); } void DolphinView::slotItemActivated(int index) { abortTwoClicksRenaming(); const KFileItem item = m_model->fileItem(index); if (!item.isNull()) { emit itemActivated(item); } } void DolphinView::slotItemsActivated(const KItemSet& indexes) { Q_ASSERT(indexes.count() >= 2); abortTwoClicksRenaming(); if (indexes.count() > 5) { QString question = i18np("Are you sure you want to open 1 item?", "Are you sure you want to open %1 items?", indexes.count()); const int answer = KMessageBox::warningYesNo(this, question); if (answer != KMessageBox::Yes) { return; } } KFileItemList items; items.reserve(indexes.count()); for (int index : indexes) { KFileItem item = m_model->fileItem(index); const QUrl& url = openItemAsFolderUrl(item); if (!url.isEmpty()) { // Open folders in new tabs emit tabRequested(url, DolphinTabWidget::AfterLastTab); } else { items.append(item); } } if (items.count() == 1) { emit itemActivated(items.first()); } else if (items.count() > 1) { emit itemsActivated(items); } } void DolphinView::slotItemMiddleClicked(int index) { const KFileItem& item = m_model->fileItem(index); const QUrl& url = openItemAsFolderUrl(item); if (!url.isEmpty()) { emit tabRequested(url, DolphinTabWidget::AfterCurrentTab); } else if (isTabsForFilesEnabled()) { emit tabRequested(item.url(), DolphinTabWidget::AfterCurrentTab); } } void DolphinView::slotItemContextMenuRequested(int index, const QPointF& pos) { // Force emit of a selection changed signal before we request the // context menu, to update the edit-actions first. (See Bug 294013) if (m_selectionChangedTimer->isActive()) { emitSelectionChangedSignal(); } const KFileItem item = m_model->fileItem(index); emit requestContextMenu(pos.toPoint(), item, url(), QList()); } void DolphinView::slotViewContextMenuRequested(const QPointF& pos) { emit requestContextMenu(pos.toPoint(), KFileItem(), url(), QList()); } void DolphinView::slotHeaderContextMenuRequested(const QPointF& pos) { ViewProperties props(viewPropertiesUrl()); QPointer menu = new QMenu(QApplication::activeWindow()); KItemListView* view = m_container->controller()->view(); const QSet visibleRolesSet = view->visibleRoles().toSet(); bool indexingEnabled = false; #ifdef HAVE_BALOO Baloo::IndexerConfig config; indexingEnabled = config.fileIndexingEnabled(); #endif QString groupName; QMenu* groupMenu = nullptr; // Add all roles to the menu that can be shown or hidden by the user const QList rolesInfo = KFileItemModel::rolesInformation(); foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { if (info.role == "text") { // It should not be possible to hide the "text" role continue; } const QString text = m_model->roleDescription(info.role); QAction* action = nullptr; if (info.group.isEmpty()) { action = menu->addAction(text); } else { if (!groupMenu || info.group != groupName) { groupName = info.group; groupMenu = menu->addMenu(groupName); } action = groupMenu->addAction(text); } action->setCheckable(true); action->setChecked(visibleRolesSet.contains(info.role)); action->setData(info.role); const bool enable = (!info.requiresBaloo && !info.requiresIndexer) || (info.requiresBaloo) || (info.requiresIndexer && indexingEnabled); action->setEnabled(enable); } menu->addSeparator(); QActionGroup* widthsGroup = new QActionGroup(menu); const bool autoColumnWidths = props.headerColumnWidths().isEmpty(); QAction* autoAdjustWidthsAction = menu->addAction(i18nc("@action:inmenu", "Automatic Column Widths")); autoAdjustWidthsAction->setCheckable(true); autoAdjustWidthsAction->setChecked(autoColumnWidths); autoAdjustWidthsAction->setActionGroup(widthsGroup); QAction* customWidthsAction = menu->addAction(i18nc("@action:inmenu", "Custom Column Widths")); customWidthsAction->setCheckable(true); customWidthsAction->setChecked(!autoColumnWidths); customWidthsAction->setActionGroup(widthsGroup); QAction* action = menu->exec(pos.toPoint()); if (menu && action) { KItemListHeader* header = view->header(); if (action == autoAdjustWidthsAction) { // Clear the column-widths from the viewproperties and turn on // the automatic resizing of the columns props.setHeaderColumnWidths(QList()); header->setAutomaticColumnResizing(true); } else if (action == customWidthsAction) { // Apply the current column-widths as custom column-widths and turn // off the automatic resizing of the columns QList columnWidths; columnWidths.reserve(view->visibleRoles().count()); foreach (const QByteArray& role, view->visibleRoles()) { columnWidths.append(header->columnWidth(role)); } props.setHeaderColumnWidths(columnWidths); header->setAutomaticColumnResizing(false); } else { // Show or hide the selected role const QByteArray selectedRole = action->data().toByteArray(); QList visibleRoles = view->visibleRoles(); if (action->isChecked()) { visibleRoles.append(selectedRole); } else { visibleRoles.removeOne(selectedRole); } view->setVisibleRoles(visibleRoles); props.setVisibleRoles(visibleRoles); QList columnWidths; if (!header->automaticColumnResizing()) { columnWidths.reserve(view->visibleRoles().count()); foreach (const QByteArray& role, view->visibleRoles()) { columnWidths.append(header->columnWidth(role)); } } props.setHeaderColumnWidths(columnWidths); } } delete menu; } void DolphinView::slotHeaderColumnWidthChangeFinished(const QByteArray& role, qreal current) { const QList visibleRoles = m_view->visibleRoles(); ViewProperties props(viewPropertiesUrl()); QList columnWidths = props.headerColumnWidths(); if (columnWidths.count() != visibleRoles.count()) { columnWidths.clear(); columnWidths.reserve(visibleRoles.count()); const KItemListHeader* header = m_view->header(); foreach (const QByteArray& role, visibleRoles) { const int width = header->columnWidth(role); columnWidths.append(width); } } const int roleIndex = visibleRoles.indexOf(role); Q_ASSERT(roleIndex >= 0 && roleIndex < columnWidths.count()); columnWidths[roleIndex] = current; props.setHeaderColumnWidths(columnWidths); } void DolphinView::slotItemHovered(int index) { const KFileItem item = m_model->fileItem(index); if (GeneralSettings::showToolTips() && !m_dragging) { QRectF itemRect = m_container->controller()->view()->itemContextRect(index); const QPoint pos = m_container->mapToGlobal(itemRect.topLeft().toPoint()); itemRect.moveTo(pos); #ifdef HAVE_BALOO m_toolTipManager->showToolTip(item, itemRect, nativeParentWidget()->windowHandle()); #endif } emit requestItemInfo(item); } void DolphinView::slotItemUnhovered(int index) { Q_UNUSED(index); hideToolTip(); emit requestItemInfo(KFileItem()); } void DolphinView::slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event) { QUrl destUrl; KFileItem destItem = m_model->fileItem(index); if (destItem.isNull() || (!destItem.isDir() && !destItem.isDesktopFile())) { // Use the URL of the view as drop target if the item is no directory // or desktop-file destItem = m_model->rootItem(); destUrl = url(); } else { // The item represents a directory or desktop-file destUrl = destItem.mostLocalUrl(); } QDropEvent dropEvent(event->pos().toPoint(), event->possibleActions(), event->mimeData(), event->buttons(), event->modifiers()); dropUrls(destUrl, &dropEvent, this); setActive(true); } void DolphinView::dropUrls(const QUrl &destUrl, QDropEvent *dropEvent, QWidget *dropWidget) { KIO::DropJob* job = DragAndDropHelper::dropUrls(destUrl, dropEvent, dropWidget); if (job) { connect(job, &KIO::DropJob::result, this, &DolphinView::slotPasteJobResult); if (destUrl == url()) { // Mark the dropped urls as selected. m_clearSelectionBeforeSelectingNewItems = true; m_markFirstNewlySelectedItemAsCurrent = true; connect(job, &KIO::DropJob::itemCreated, this, &DolphinView::slotItemCreated); } } } void DolphinView::slotModelChanged(KItemModelBase* current, KItemModelBase* previous) { if (previous != nullptr) { Q_ASSERT(qobject_cast(previous)); KFileItemModel* fileItemModel = static_cast(previous); disconnect(fileItemModel, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::slotDirectoryLoadingCompleted); m_versionControlObserver->setModel(nullptr); } if (current) { Q_ASSERT(qobject_cast(current)); KFileItemModel* fileItemModel = static_cast(current); connect(fileItemModel, &KFileItemModel::directoryLoadingCompleted, this, &DolphinView::slotDirectoryLoadingCompleted); m_versionControlObserver->setModel(fileItemModel); } } void DolphinView::slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons) { Q_UNUSED(itemIndex); hideToolTip(); if (buttons & Qt::BackButton) { emit goBackRequested(); } else if (buttons & Qt::ForwardButton) { emit goForwardRequested(); } } void DolphinView::slotSelectedItemTextPressed(int index) { if (GeneralSettings::renameInline() && !m_view->style()->styleHint(QStyle::SH_ItemView_ActivateItemOnSingleClick)) { const KFileItem item = m_model->fileItem(index); const KFileItemListProperties capabilities(KFileItemList() << item); if (capabilities.supportsMoving()) { m_twoClicksRenamingItemUrl = item.url(); m_twoClicksRenamingTimer->start(QApplication::doubleClickInterval()); } } } void DolphinView::slotItemCreated(const QUrl& url) { if (m_markFirstNewlySelectedItemAsCurrent) { markUrlAsCurrent(url); m_markFirstNewlySelectedItemAsCurrent = false; } m_selectedUrls << url; } void DolphinView::slotPasteJobResult(KJob *job) { if (job->error()) { emit errorMessage(job->errorString()); } if (!m_selectedUrls.isEmpty()) { m_selectedUrls << KDirModel::simplifiedUrlList(m_selectedUrls); } } void DolphinView::slotSelectionChanged(const KItemSet& current, const KItemSet& previous) { const int currentCount = current.count(); const int previousCount = previous.count(); const bool selectionStateChanged = (currentCount == 0 && previousCount > 0) || (currentCount > 0 && previousCount == 0); // If nothing has been selected before and something got selected (or if something // was selected before and now nothing is selected) the selectionChangedSignal must // be emitted asynchronously as fast as possible to update the edit-actions. m_selectionChangedTimer->setInterval(selectionStateChanged ? 0 : 300); m_selectionChangedTimer->start(); } void DolphinView::emitSelectionChangedSignal() { m_selectionChangedTimer->stop(); emit selectionChanged(selectedItems()); } void DolphinView::updateSortRole(const QByteArray& role) { ViewProperties props(viewPropertiesUrl()); props.setSortRole(role); KItemModelBase* model = m_container->controller()->model(); model->setSortRole(role); emit sortRoleChanged(role); } void DolphinView::updateSortOrder(Qt::SortOrder order) { ViewProperties props(viewPropertiesUrl()); props.setSortOrder(order); m_model->setSortOrder(order); emit sortOrderChanged(order); } void DolphinView::updateSortFoldersFirst(bool foldersFirst) { ViewProperties props(viewPropertiesUrl()); props.setSortFoldersFirst(foldersFirst); m_model->setSortDirectoriesFirst(foldersFirst); emit sortFoldersFirstChanged(foldersFirst); } QPair DolphinView::pasteInfo() const { const QMimeData *mimeData = QApplication::clipboard()->mimeData(); QPair info; info.second = KIO::pasteActionText(mimeData, &info.first, rootItem()); return info; } void DolphinView::setTabsForFilesEnabled(bool tabsForFiles) { m_tabsForFiles = tabsForFiles; } bool DolphinView::isTabsForFilesEnabled() const { return m_tabsForFiles; } bool DolphinView::itemsExpandable() const { return m_mode == DetailsView; } void DolphinView::restoreState(QDataStream& stream) { // Read the version number of the view state and check if the version is supported. quint32 version = 0; stream >> version; if (version != 1) { // The version of the view state isn't supported, we can't restore it. return; } // Restore the current item that had the keyboard focus stream >> m_currentItemUrl; // Restore the previously selected items stream >> m_selectedUrls; // Restore the view position stream >> m_restoredContentsPosition; // Restore expanded folders (only relevant for the details view - will be ignored by the view in other view modes) QSet urls; stream >> urls; m_model->restoreExpandedDirectories(urls); } void DolphinView::saveState(QDataStream& stream) { stream << quint32(1); // View state version // Save the current item that has the keyboard focus const int currentIndex = m_container->controller()->selectionManager()->currentItem(); if (currentIndex != -1) { KFileItem item = m_model->fileItem(currentIndex); Q_ASSERT(!item.isNull()); // If the current index is valid a item must exist QUrl currentItemUrl = item.url(); stream << currentItemUrl; } else { stream << QUrl(); } // Save the selected urls stream << selectedItems().urlList(); // Save view position const qreal x = m_container->horizontalScrollBar()->value(); const qreal y = m_container->verticalScrollBar()->value(); stream << QPoint(x, y); // Save expanded folders (only relevant for the details view - the set will be empty in other view modes) stream << m_model->expandedDirectories(); } KFileItem DolphinView::rootItem() const { return m_model->rootItem(); } void DolphinView::setViewPropertiesContext(const QString& context) { m_viewPropertiesContext = context; } QString DolphinView::viewPropertiesContext() const { return m_viewPropertiesContext; } QUrl DolphinView::openItemAsFolderUrl(const KFileItem& item, const bool browseThroughArchives) { if (item.isNull()) { return QUrl(); } QUrl url = item.targetUrl(); if (item.isDir()) { return url; } if (item.isMimeTypeKnown()) { const QString& mimetype = item.mimetype(); if (browseThroughArchives && item.isFile() && url.isLocalFile()) { // Generic mechanism for redirecting to tar:// when clicking on a tar file, // zip:// when clicking on a zip file, etc. // The .protocol file specifies the mimetype that the kioslave handles. // Note that we don't use mimetype inheritance since we don't want to // open OpenDocument files as zip folders... const QString& protocol = KProtocolManager::protocolForArchiveMimetype(mimetype); if (!protocol.isEmpty()) { url.setScheme(protocol); return url; } } if (mimetype == QLatin1String("application/x-desktop")) { // Redirect to the URL in Type=Link desktop files, unless it is a http(s) URL. KDesktopFile desktopFile(url.toLocalFile()); if (desktopFile.hasLinkType()) { const QString linkUrl = desktopFile.readUrl(); if (!linkUrl.startsWith(QLatin1String("http"))) { return QUrl::fromUserInput(linkUrl); } } } } return QUrl(); } +void DolphinView::resetZoomLevel() +{ + ViewModeSettings::ViewMode mode; + + switch (m_mode) { + case IconsView: mode = ViewModeSettings::IconsMode; break; + case CompactView: mode = ViewModeSettings::CompactMode; break; + case DetailsView: mode = ViewModeSettings::DetailsMode; break; + } + const ViewModeSettings settings(mode); + const QSize iconSize = QSize(settings.iconSize(), settings.iconSize()); + setZoomLevel(ZoomLevelInfo::zoomLevelForIconSize(iconSize)); +} + void DolphinView::observeCreatedItem(const QUrl& url) { if (m_active) { forceUrlsSelection(url, {url}); } } void DolphinView::slotDirectoryRedirection(const QUrl& oldUrl, const QUrl& newUrl) { if (oldUrl.matches(url(), QUrl::StripTrailingSlash)) { emit redirection(oldUrl, newUrl); m_url = newUrl; // #186947 } } void DolphinView::updateViewState() { if (m_currentItemUrl != QUrl()) { KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); // if there is a selection already, leave it that way if (!selectionManager->hasSelection()) { const int currentIndex = m_model->index(m_currentItemUrl); if (currentIndex != -1) { selectionManager->setCurrentItem(currentIndex); // scroll to current item and reset the state if (m_scrollToCurrentItem) { m_view->scrollToItem(currentIndex); m_scrollToCurrentItem = false; } } else { selectionManager->setCurrentItem(0); } } m_currentItemUrl = QUrl(); } if (!m_restoredContentsPosition.isNull()) { const int x = m_restoredContentsPosition.x(); const int y = m_restoredContentsPosition.y(); m_restoredContentsPosition = QPoint(); m_container->horizontalScrollBar()->setValue(x); m_container->verticalScrollBar()->setValue(y); } if (!m_selectedUrls.isEmpty()) { KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); // if there is a selection already, leave it that way if (!selectionManager->hasSelection()) { if (m_clearSelectionBeforeSelectingNewItems) { selectionManager->clearSelection(); m_clearSelectionBeforeSelectingNewItems = false; } KItemSet selectedItems = selectionManager->selectedItems(); QList::iterator it = m_selectedUrls.begin(); while (it != m_selectedUrls.end()) { const int index = m_model->index(*it); if (index >= 0) { selectedItems.insert(index); it = m_selectedUrls.erase(it); } else { ++it; } } selectionManager->beginAnchoredSelection(selectionManager->currentItem()); selectionManager->setSelectedItems(selectedItems); } } } void DolphinView::hideToolTip(const ToolTipManager::HideBehavior behavior) { #ifdef HAVE_BALOO if (GeneralSettings::showToolTips()) { m_toolTipManager->hideToolTip(behavior); } #endif } void DolphinView::calculateItemCount(int& fileCount, int& folderCount, KIO::filesize_t& totalFileSize) const { const int itemCount = m_model->count(); for (int i = 0; i < itemCount; ++i) { const KFileItem item = m_model->fileItem(i); if (item.isDir()) { ++folderCount; } else { ++fileCount; totalFileSize += item.size(); } } } void DolphinView::slotTwoClicksRenamingTimerTimeout() { const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); // verify that only one item is selected if (selectionManager->selectedItems().count() == 1) { const int index = selectionManager->currentItem(); const QUrl fileItemUrl = m_model->fileItem(index).url(); // check if the selected item was the same item that started the twoClicksRenaming if (fileItemUrl.isValid() && m_twoClicksRenamingItemUrl == fileItemUrl) { renameSelectedItems(); } } } void DolphinView::slotTrashFileFinished(KJob* job) { if (job->error() == 0) { emit operationCompletedMessage(i18nc("@info:status", "Trash operation completed.")); } else if (job->error() != KIO::ERR_USER_CANCELED) { emit errorMessage(job->errorString()); } } void DolphinView::slotDeleteFileFinished(KJob* job) { if (job->error() == 0) { emit operationCompletedMessage(i18nc("@info:status", "Delete operation completed.")); } else if (job->error() != KIO::ERR_USER_CANCELED) { emit errorMessage(job->errorString()); } } void DolphinView::slotRenamingResult(KJob* job) { if (job->error()) { KIO::CopyJob *copyJob = qobject_cast(job); Q_ASSERT(copyJob); const QUrl newUrl = copyJob->destUrl(); const int index = m_model->index(newUrl); if (index >= 0) { QHash data; const QUrl oldUrl = copyJob->srcUrls().at(0); data.insert("text", oldUrl.fileName()); m_model->setData(index, data); } } } void DolphinView::slotDirectoryLoadingStarted() { // Disable the writestate temporary until it can be determined in a fast way // in DolphinView::slotDirectoryLoadingCompleted() if (m_isFolderWritable) { m_isFolderWritable = false; emit writeStateChanged(m_isFolderWritable); } emit directoryLoadingStarted(); } void DolphinView::slotDirectoryLoadingCompleted() { // Update the view-state. This has to be done asynchronously // because the view might not be in its final state yet. QTimer::singleShot(0, this, &DolphinView::updateViewState); emit directoryLoadingCompleted(); updateWritableState(); } void DolphinView::slotItemsChanged() { m_assureVisibleCurrentIndex = false; } void DolphinView::slotSortOrderChangedByHeader(Qt::SortOrder current, Qt::SortOrder previous) { Q_UNUSED(previous); Q_ASSERT(m_model->sortOrder() == current); ViewProperties props(viewPropertiesUrl()); props.setSortOrder(current); emit sortOrderChanged(current); } void DolphinView::slotSortRoleChangedByHeader(const QByteArray& current, const QByteArray& previous) { Q_UNUSED(previous); Q_ASSERT(m_model->sortRole() == current); ViewProperties props(viewPropertiesUrl()); props.setSortRole(current); emit sortRoleChanged(current); } void DolphinView::slotVisibleRolesChangedByHeader(const QList& current, const QList& previous) { Q_UNUSED(previous); Q_ASSERT(m_container->controller()->view()->visibleRoles() == current); const QList previousVisibleRoles = m_visibleRoles; m_visibleRoles = current; ViewProperties props(viewPropertiesUrl()); props.setVisibleRoles(m_visibleRoles); emit visibleRolesChanged(m_visibleRoles, previousVisibleRoles); } void DolphinView::slotRoleEditingCanceled() { disconnect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished); } void DolphinView::slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value) { disconnect(m_view, &DolphinItemListView::roleEditingFinished, this, &DolphinView::slotRoleEditingFinished); if (index < 0 || index >= m_model->count()) { return; } if (role == "text") { const KFileItem oldItem = m_model->fileItem(index); const QString newName = value.toString(); if (!newName.isEmpty() && newName != oldItem.text() && newName != QLatin1Char('.') && newName != QLatin1String("..")) { const QUrl oldUrl = oldItem.url(); QUrl newUrl = oldUrl.adjusted(QUrl::RemoveFilename); newUrl.setPath(newUrl.path() + KIO::encodeFileName(newName)); #ifndef Q_OS_WIN //Confirm hiding file/directory by renaming inline if (!hiddenFilesShown() && newName.startsWith(QLatin1Char('.')) && !oldItem.name().startsWith(QLatin1Char('.'))) { KGuiItem yesGuiItem(KStandardGuiItem::yes()); yesGuiItem.setText(i18nc("@action:button", "Rename and Hide")); const auto code = KMessageBox::questionYesNo(this, oldItem.isFile() ? i18n("Adding a dot to the beginning of this file's name will hide it from view.\n" "Do you still want to rename it?") : i18n("Adding a dot to the beginning of this folder's name will hide it from view.\n" "Do you still want to rename it?"), oldItem.isFile() ? i18n("Hide this File?") : i18n("Hide this Folder?"), yesGuiItem, KStandardGuiItem::cancel(), QStringLiteral("ConfirmHide") ); if (code == KMessageBox::No) { return; } } #endif const bool newNameExistsAlready = (m_model->index(newUrl) >= 0); if (!newNameExistsAlready) { // Only change the data in the model if no item with the new name // is in the model yet. If there is an item with the new name // already, calling KIO::CopyJob will open a dialog // asking for a new name, and KFileItemModel will update the // data when the dir lister signals that the file name has changed. QHash data; data.insert(role, value); m_model->setData(index, data); } KIO::Job * job = KIO::moveAs(oldUrl, newUrl); KJobWidgets::setWindow(job, this); KIO::FileUndoManager::self()->recordJob(KIO::FileUndoManager::Rename, {oldUrl}, newUrl, job); job->uiDelegate()->setAutoErrorHandlingEnabled(true); forceUrlsSelection(newUrl, {newUrl}); if (!newNameExistsAlready) { // Only connect the result signal if there is no item with the new name // in the model yet, see bug 328262. connect(job, &KJob::result, this, &DolphinView::slotRenamingResult); } } } } void DolphinView::loadDirectory(const QUrl& url, bool reload) { if (!url.isValid()) { const QString location(url.toDisplayString(QUrl::PreferLocalFile)); if (location.isEmpty()) { emit errorMessage(i18nc("@info:status", "The location is empty.")); } else { emit errorMessage(i18nc("@info:status", "The location '%1' is invalid.", location)); } return; } if (reload) { m_model->refreshDirectory(url); } else { m_model->loadDirectory(url); } } void DolphinView::applyViewProperties() { const ViewProperties props(viewPropertiesUrl()); applyViewProperties(props); } void DolphinView::applyViewProperties(const ViewProperties& props) { m_view->beginTransaction(); const Mode mode = props.viewMode(); if (m_mode != mode) { const Mode previousMode = m_mode; m_mode = mode; // Changing the mode might result in changing // the zoom level. Remember the old zoom level so // that zoomLevelChanged() can get emitted. const int oldZoomLevel = m_view->zoomLevel(); applyModeToView(); emit modeChanged(m_mode, previousMode); if (m_view->zoomLevel() != oldZoomLevel) { emit zoomLevelChanged(m_view->zoomLevel(), oldZoomLevel); } } const bool hiddenFilesShown = props.hiddenFilesShown(); if (hiddenFilesShown != m_model->showHiddenFiles()) { m_model->setShowHiddenFiles(hiddenFilesShown); emit hiddenFilesShownChanged(hiddenFilesShown); } const bool groupedSorting = props.groupedSorting(); if (groupedSorting != m_model->groupedSorting()) { m_model->setGroupedSorting(groupedSorting); emit groupedSortingChanged(groupedSorting); } const QByteArray sortRole = props.sortRole(); if (sortRole != m_model->sortRole()) { m_model->setSortRole(sortRole); emit sortRoleChanged(sortRole); } const Qt::SortOrder sortOrder = props.sortOrder(); if (sortOrder != m_model->sortOrder()) { m_model->setSortOrder(sortOrder); emit sortOrderChanged(sortOrder); } const bool sortFoldersFirst = props.sortFoldersFirst(); if (sortFoldersFirst != m_model->sortDirectoriesFirst()) { m_model->setSortDirectoriesFirst(sortFoldersFirst); emit sortFoldersFirstChanged(sortFoldersFirst); } const QList visibleRoles = props.visibleRoles(); if (visibleRoles != m_visibleRoles) { const QList previousVisibleRoles = m_visibleRoles; m_visibleRoles = visibleRoles; m_view->setVisibleRoles(visibleRoles); emit visibleRolesChanged(m_visibleRoles, previousVisibleRoles); } const bool previewsShown = props.previewsShown(); if (previewsShown != m_view->previewsShown()) { const int oldZoomLevel = zoomLevel(); m_view->setPreviewsShown(previewsShown); emit previewsShownChanged(previewsShown); // Changing the preview-state might result in a changed zoom-level if (oldZoomLevel != zoomLevel()) { emit zoomLevelChanged(zoomLevel(), oldZoomLevel); } } KItemListView* itemListView = m_container->controller()->view(); if (itemListView->isHeaderVisible()) { KItemListHeader* header = itemListView->header(); const QList headerColumnWidths = props.headerColumnWidths(); const int rolesCount = m_visibleRoles.count(); if (headerColumnWidths.count() == rolesCount) { header->setAutomaticColumnResizing(false); QHash columnWidths; for (int i = 0; i < rolesCount; ++i) { columnWidths.insert(m_visibleRoles[i], headerColumnWidths[i]); } header->setColumnWidths(columnWidths); } else { header->setAutomaticColumnResizing(true); } } m_view->endTransaction(); } void DolphinView::applyModeToView() { switch (m_mode) { case IconsView: m_view->setItemLayout(KFileItemListView::IconsLayout); break; case CompactView: m_view->setItemLayout(KFileItemListView::CompactLayout); break; case DetailsView: m_view->setItemLayout(KFileItemListView::DetailsLayout); break; default: Q_ASSERT(false); break; } } void DolphinView::pasteToUrl(const QUrl& url) { KIO::PasteJob *job = KIO::paste(QApplication::clipboard()->mimeData(), url); KJobWidgets::setWindow(job, this); m_clearSelectionBeforeSelectingNewItems = true; m_markFirstNewlySelectedItemAsCurrent = true; connect(job, &KIO::PasteJob::itemCreated, this, &DolphinView::slotItemCreated); connect(job, &KIO::PasteJob::result, this, &DolphinView::slotPasteJobResult); } QList DolphinView::simplifiedSelectedUrls() const { QList urls; const KFileItemList items = selectedItems(); urls.reserve(items.count()); foreach (const KFileItem& item, items) { urls.append(item.url()); } if (itemsExpandable()) { // TODO: Check if we still need KDirModel for this in KDE 5.0 urls = KDirModel::simplifiedUrlList(urls); } return urls; } QMimeData* DolphinView::selectionMimeData() const { const KItemListSelectionManager* selectionManager = m_container->controller()->selectionManager(); const KItemSet selectedIndexes = selectionManager->selectedItems(); return m_model->createMimeData(selectedIndexes); } void DolphinView::updateWritableState() { const bool wasFolderWritable = m_isFolderWritable; m_isFolderWritable = false; KFileItem item = m_model->rootItem(); if (item.isNull()) { // Try to find out if the URL is writable even if the "root item" is // null, see https://bugs.kde.org/show_bug.cgi?id=330001 item = KFileItem(url()); item.setDelayedMimeTypes(true); } KFileItemListProperties capabilities(KFileItemList() << item); m_isFolderWritable = capabilities.supportsWriting(); if (m_isFolderWritable != wasFolderWritable) { emit writeStateChanged(m_isFolderWritable); } } QUrl DolphinView::viewPropertiesUrl() const { if (m_viewPropertiesContext.isEmpty()) { return m_url; } QUrl url; url.setScheme(m_url.scheme()); url.setPath(m_viewPropertiesContext); return url; } void DolphinView::slotRenameDialogRenamingFinished(const QList& urls) { forceUrlsSelection(urls.first(), urls); } void DolphinView::forceUrlsSelection(const QUrl& current, const QList& selected) { clearSelection(); m_clearSelectionBeforeSelectingNewItems = true; markUrlAsCurrent(current); markUrlsAsSelected(selected); } diff --git a/src/views/dolphinview.h b/src/views/dolphinview.h index a4da92f2d..ba38d3234 100644 --- a/src/views/dolphinview.h +++ b/src/views/dolphinview.h @@ -1,829 +1,834 @@ /*************************************************************************** * Copyright (C) 2006-2009 by Peter Penz * * Copyright (C) 2006 by Gregor Kališnik * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef DOLPHINVIEW_H #define DOLPHINVIEW_H #include "dolphintabwidget.h" #include "dolphin_export.h" #include "tooltips/tooltipmanager.h" #include #include #include #include #include #include #include #include typedef KIO::FileUndoManager::CommandType CommandType; class QVBoxLayout; class DolphinItemListView; class KFileItemModel; class KItemListContainer; class KItemModelBase; class KItemSet; class ToolTipManager; class VersionControlObserver; class ViewProperties; class QGraphicsSceneDragDropEvent; class QRegExp; /** * @short Represents a view for the directory content. * * View modes for icons, compact and details are supported. It's * possible to adjust: * - sort order * - sort type * - show hidden files * - show previews * - enable grouping */ class DOLPHIN_EXPORT DolphinView : public QWidget { Q_OBJECT public: /** * Defines the view mode for a directory. The * view mode is automatically updated if the directory itself * defines a view mode (see class ViewProperties for details). */ enum Mode { /** * The items are shown as icons with a name-label below. */ IconsView = 0, /** * The icon, the name and the size of the items are * shown per default as a table. */ DetailsView, /** * The items are shown as icons with the name-label aligned * to the right side. */ CompactView }; /** * @param url Specifies the content which should be shown. * @param parent Parent widget of the view. */ DolphinView(const QUrl& url, QWidget* parent); ~DolphinView() override; /** * Returns the current active URL, where all actions are applied. * The URL navigator is synchronized with this URL. */ QUrl url() const; /** * If \a active is true, the view will marked as active. The active * view is defined as view where all actions are applied to. */ void setActive(bool active); bool isActive() const; /** * Changes the view mode for the current directory to \a mode. * If the view properties should be remembered for each directory * (GeneralSettings::globalViewProps() returns false), then the * changed view mode will be stored automatically. */ void setMode(Mode mode); Mode mode() const; /** * Turns on the file preview for the all files of the current directory, * if \a show is true. * If the view properties should be remembered for each directory * (GeneralSettings::globalViewProps() returns false), then the * preview setting will be stored automatically. */ void setPreviewsShown(bool show); bool previewsShown() const; /** * Shows all hidden files of the current directory, * if \a show is true. * If the view properties should be remembered for each directory * (GeneralSettings::globalViewProps() returns false), then the * show hidden file setting will be stored automatically. */ void setHiddenFilesShown(bool show); bool hiddenFilesShown() const; /** * Turns on sorting by groups if \a enable is true. */ void setGroupedSorting(bool grouped); bool groupedSorting() const; /** * Returns the items of the view. */ KFileItemList items() const; /** * @return The number of items. itemsCount() is faster in comparison * to items().count(). */ int itemsCount() const; /** * Returns the selected items. The list is empty if no item has been * selected. */ KFileItemList selectedItems() const; /** * Returns the number of selected items (this is faster than * invoking selectedItems().count()). */ int selectedItemsCount() const; /** * Marks the items indicated by \p urls to get selected after the * directory DolphinView::url() has been loaded. Note that nothing * gets selected if no loading of a directory has been triggered * by DolphinView::setUrl() or DolphinView::reload(). */ void markUrlsAsSelected(const QList &urls); /** * Marks the item indicated by \p url to be scrolled to and as the * current item after directory DolphinView::url() has been loaded. */ void markUrlAsCurrent(const QUrl& url); /** * All items that match to the pattern \a pattern will get selected * if \a enabled is true and deselected if \a enabled is false. */ void selectItems(const QRegExp& pattern, bool enabled); /** * Sets the zoom level to \a level. It is assured that the used * level is adjusted to be inside the range ZoomLevelInfo::minimumLevel() and * ZoomLevelInfo::maximumLevel(). */ void setZoomLevel(int level); int zoomLevel() const; + /** + * Resets the view's icon size to the default value + */ + void resetZoomLevel(); + void setSortRole(const QByteArray& role); QByteArray sortRole() const; void setSortOrder(Qt::SortOrder order); Qt::SortOrder sortOrder() const; /** Sets a separate sorting with folders first (true) or a mixed sorting of files and folders (false). */ void setSortFoldersFirst(bool foldersFirst); bool sortFoldersFirst() const; /** Sets the additional information which should be shown for the items. */ void setVisibleRoles(const QList& roles); /** Returns the additional information which should be shown for the items. */ QList visibleRoles() const; void reload(); /** * Refreshes the view to get synchronized with the settings (e.g. icons size, * font, ...). */ void readSettings(); /** * Saves the current settings (e.g. icons size, font, ..). */ void writeSettings(); /** * Filters the currently shown items by \a nameFilter. All items * which contain the given filter string will be shown. */ void setNameFilter(const QString& nameFilter); QString nameFilter() const; /** * Filters the currently shown items by \a filters. All items * whose content-type matches those given by the list of filters * will be shown. */ void setMimeTypeFilters(const QStringList& filters); QStringList mimeTypeFilters() const; /** * Returns a textual representation of the state of the current * folder or selected items, suitable for use in the status bar. */ QString statusBarText() const; /** * Returns the version control actions that are provided for the items \p items. * Usually the actions are presented in the context menu. */ QList versionControlActions(const KFileItemList& items) const; /** * Returns the state of the paste action: * first is whether the action should be enabled * second is the text for the action */ QPair pasteInfo() const; /** * If \a tabsForFiles is true, the signal tabRequested() will also * emitted also for files. Per default tabs for files is disabled * and hence the signal tabRequested() will only be emitted for * directories. */ void setTabsForFilesEnabled(bool tabsForFiles); bool isTabsForFilesEnabled() const; /** * Returns true if the current view allows folders to be expanded, * i.e. presents a hierarchical view to the user. */ bool itemsExpandable() const; /** * Restores the view state (current item, contents position, details view expansion state) */ void restoreState(QDataStream& stream); /** * Saves the view state (current item, contents position, details view expansion state) */ void saveState(QDataStream& stream); /** * Returns the root item which represents the current URL. */ KFileItem rootItem() const; /** * Sets a context that is used for remembering the view-properties. * Per default the context is empty and the path of the currently set URL * is used for remembering the view-properties. Setting a custom context * makes sense if specific types of URLs (e.g. search-URLs) should * share common view-properties. */ void setViewPropertiesContext(const QString& context); QString viewPropertiesContext() const; /** * Checks if the given \a item can be opened as folder (e.g. archives). * This function will also adjust the \a url (e.g. change the protocol). * @return a valid and adjusted url if the item can be opened as folder, * otherwise return an empty url. */ static QUrl openItemAsFolderUrl(const KFileItem& item, const bool browseThroughArchives = true); public slots: /** * Changes the directory to \a url. If the current directory is equal to * \a url, nothing will be done (use DolphinView::reload() instead). */ void setUrl(const QUrl& url); /** * Selects all items. * @see DolphinView::selectedItems() */ void selectAll(); /** * Inverts the current selection: selected items get unselected, * unselected items get selected. * @see DolphinView::selectedItems() */ void invertSelection(); void clearSelection(); /** * Triggers the renaming of the currently selected items, where * the user must input a new name for the items. */ void renameSelectedItems(); /** * Moves all selected items to the trash. */ void trashSelectedItems(); /** * Deletes all selected items. */ void deleteSelectedItems(); /** * Copies all selected items to the clipboard and marks * the items as cut. */ void cutSelectedItems(); /** Copies all selected items to the clipboard. */ void copySelectedItems(); /** Pastes the clipboard data to this view. */ void paste(); /** * Pastes the clipboard data into the currently selected * folder. If the current selection is not exactly one folder, no * paste operation is done. */ void pasteIntoFolder(); /** * Handles a drop of @p dropEvent onto widget @p dropWidget and destination @p destUrl */ void dropUrls(const QUrl &destUrl, QDropEvent *dropEvent, QWidget *dropWidget); void stopLoading(); /** Activates the view if the item list container gets focus. */ bool eventFilter(QObject* watched, QEvent* event) override; signals: /** * Is emitted if the view has been activated by e. g. a mouse click. */ void activated(); /** Is emitted if the URL of the view has been changed to \a url. */ void urlChanged(const QUrl& url); /** * Is emitted when clicking on an item with the left mouse button. */ void itemActivated(const KFileItem& item); /** * Is emitted when multiple items have been activated by e. g. * context menu open with. */ void itemsActivated(const KFileItemList& items); /** * Is emitted if items have been added or deleted. */ void itemCountChanged(); /** * Is emitted if a new tab should be opened for the URL \a url. */ void tabRequested(const QUrl& url, DolphinTabWidget::TabPlacement tabPlacement); /** * Is emitted if the view mode (IconsView, DetailsView, * PreviewsView) has been changed. */ void modeChanged(DolphinView::Mode current, DolphinView::Mode previous); /** Is emitted if the 'show preview' property has been changed. */ void previewsShownChanged(bool shown); /** Is emitted if the 'show hidden files' property has been changed. */ void hiddenFilesShownChanged(bool shown); /** Is emitted if the 'grouped sorting' property has been changed. */ void groupedSortingChanged(bool groupedSorting); /** Is emitted if the sorting by name, size or date has been changed. */ void sortRoleChanged(const QByteArray& role); /** Is emitted if the sort order (ascending or descending) has been changed. */ void sortOrderChanged(Qt::SortOrder order); /** * Is emitted if the sorting of files and folders (separate with folders * first or mixed) has been changed. */ void sortFoldersFirstChanged(bool foldersFirst); /** Is emitted if the additional information shown for this view has been changed. */ void visibleRolesChanged(const QList& current, const QList& previous); /** Is emitted if the zoom level has been changed by zooming in or out. */ void zoomLevelChanged(int current, int previous); /** * Is emitted if information of an item is requested to be shown e. g. in the panel. * If item is null, no item information request is pending. */ void requestItemInfo(const KFileItem& item); /** * Is emitted whenever the selection has been changed. */ void selectionChanged(const KFileItemList& selection); /** * Is emitted if a context menu is requested for the item \a item, * which is part of \a url. If the item is null, the context menu * for the URL should be shown and the custom actions \a customActions * will be added. */ void requestContextMenu(const QPoint& pos, const KFileItem& item, const QUrl& url, const QList& customActions); /** * Is emitted if an information message with the content \a msg * should be shown. */ void infoMessage(const QString& msg); /** * Is emitted if an error message with the content \a msg * should be shown. */ void errorMessage(const QString& msg); /** * Is emitted if an "operation completed" message with the content \a msg * should be shown. */ void operationCompletedMessage(const QString& msg); /** * Is emitted after DolphinView::setUrl() has been invoked and * the current directory is loaded. If this signal is emitted, * it is assured that the view contains already the correct root * URL and property settings. */ void directoryLoadingStarted(); /** * Is emitted after the directory triggered by DolphinView::setUrl() * has been loaded. */ void directoryLoadingCompleted(); /** * Is emitted after the directory loading triggered by DolphinView::setUrl() * has been canceled. */ void directoryLoadingCanceled(); /** * Is emitted after DolphinView::setUrl() has been invoked and provides * the information how much percent of the current directory have been loaded. */ void directoryLoadingProgress(int percent); /** * Is emitted if the sorting is done asynchronously and provides the * progress information of the sorting. */ void directorySortingProgress(int percent); /** * Emitted when the file-item-model emits redirection. * Testcase: fish://localhost */ void redirection(const QUrl& oldUrl, const QUrl& newUrl); /** * Is emitted when the URL set by DolphinView::setUrl() represents a file. * In this case no signal errorMessage() will be emitted. */ void urlIsFileError(const QUrl& url); /** * Is emitted when the write state of the folder has been changed. The application * should disable all actions like "Create New..." that depend on the write * state. */ void writeStateChanged(bool isFolderWritable); /** * Is emitted if the URL should be changed to the previous URL of the * history (e.g. because the "back"-mousebutton has been pressed). */ void goBackRequested(); /** * Is emitted if the URL should be changed to the next URL of the * history (e.g. because the "next"-mousebutton has been pressed). */ void goForwardRequested(); /** * Is emitted when the user wants to move the focus to another view. */ void toggleActiveViewRequested(); /** * Is emitted when the user clicks a tag or a link * in the metadata widget of a tooltip. */ void urlActivated(const QUrl& url); protected: /** Changes the zoom level if Control is pressed during a wheel event. */ void wheelEvent(QWheelEvent* event) override; void hideEvent(QHideEvent* event) override; bool event(QEvent* event) override; private slots: /** * Marks the view as active (DolphinView:isActive() will return true) * and emits the 'activated' signal if it is not already active. */ void activate(); void slotItemActivated(int index); void slotItemsActivated(const KItemSet& indexes); void slotItemMiddleClicked(int index); void slotItemContextMenuRequested(int index, const QPointF& pos); void slotViewContextMenuRequested(const QPointF& pos); void slotHeaderContextMenuRequested(const QPointF& pos); void slotHeaderColumnWidthChangeFinished(const QByteArray& role, qreal current); void slotItemHovered(int index); void slotItemUnhovered(int index); void slotItemDropEvent(int index, QGraphicsSceneDragDropEvent* event); void slotModelChanged(KItemModelBase* current, KItemModelBase* previous); void slotMouseButtonPressed(int itemIndex, Qt::MouseButtons buttons); void slotRenameDialogRenamingFinished(const QList& urls); void slotSelectedItemTextPressed(int index); /* * Is called when new items get pasted or dropped. */ void slotItemCreated(const QUrl &url); /* * Is called after all pasted or dropped items have been copied to destination. */ void slotPasteJobResult(KJob *job); /** * Emits the signal \a selectionChanged() with a small delay. This is * because getting all file items for the selection can be an expensive * operation. Fast selection changes are collected in this case and * the signal is emitted only after no selection change has been done * within a small delay. */ void slotSelectionChanged(const KItemSet& current, const KItemSet& previous); /** * Is called by emitDelayedSelectionChangedSignal() and emits the * signal \a selectionChanged() with all selected file items as parameter. */ void emitSelectionChangedSignal(); /** * Updates the view properties of the current URL to the * sorting given by \a role. */ void updateSortRole(const QByteArray& role); /** * Updates the view properties of the current URL to the * sort order given by \a order. */ void updateSortOrder(Qt::SortOrder order); /** * Updates the view properties of the current URL to the * sorting of files and folders (separate with folders first or mixed) given by \a foldersFirst. */ void updateSortFoldersFirst(bool foldersFirst); /** * Indicates in the status bar that the delete operation * of the job \a job has been finished. */ void slotDeleteFileFinished(KJob* job); /** * Indicates in the status bar that the trash operation * of the job \a job has been finished. */ void slotTrashFileFinished(KJob* job); /** * Invoked when the rename job is done, for error handling. */ void slotRenamingResult(KJob* job); /** * Invoked when the file item model has started the loading * of the directory specified by DolphinView::url(). */ void slotDirectoryLoadingStarted(); /** * Invoked when the file item model indicates that the loading of a directory has * been completed. Assures that pasted items and renamed items get selected. */ void slotDirectoryLoadingCompleted(); /** * Is invoked when items of KFileItemModel have been changed. */ void slotItemsChanged(); /** * Is invoked when the sort order has been changed by the user by clicking * on a header item. The view properties of the directory will get updated. */ void slotSortOrderChangedByHeader(Qt::SortOrder current, Qt::SortOrder previous); /** * Is invoked when the sort role has been changed by the user by clicking * on a header item. The view properties of the directory will get updated. */ void slotSortRoleChangedByHeader(const QByteArray& current, const QByteArray& previous); /** * Is invoked when the visible roles have been changed by the user by dragging * a header item. The view properties of the directory will get updated. */ void slotVisibleRolesChangedByHeader(const QList& current, const QList& previous); void slotRoleEditingCanceled(); void slotRoleEditingFinished(int index, const QByteArray& role, const QVariant& value); /** * Observes the item with the URL \a url. As soon as the directory * model indicates that the item is available, the item will * get selected and it is assured that the item stays visible. */ void observeCreatedItem(const QUrl &url); /** * Called when a redirection happens. * Testcase: fish://localhost */ void slotDirectoryRedirection(const QUrl& oldUrl, const QUrl& newUrl); /** * Applies the state that has been restored by restoreViewState() * to the view. */ void updateViewState(); /** * Calculates the number of currently shown files into * \a fileCount and the number of folders into \a folderCount. * The size of all files is written into \a totalFileSize. * It is recommend using this method instead of asking the * directory lister or the model directly, as it takes * filtering and hierarchical previews into account. */ void calculateItemCount(int& fileCount, int& folderCount, KIO::filesize_t& totalFileSize) const; void slotTwoClicksRenamingTimerTimeout(); private: void loadDirectory(const QUrl& url, bool reload = false); /** * Applies the view properties which are defined by the current URL * to the DolphinView properties. The view properties are read from a * .directory file either in the current directory, or in the * share/apps/dolphin/view_properties/ subfolder of the user's .kde folder. */ void applyViewProperties(); /** * Applies the given view properties to the DolphinView. */ void applyViewProperties(const ViewProperties& props); /** * Applies the m_mode property to the corresponding * itemlayout-property of the KItemListView. */ void applyModeToView(); /** * Hides tooltip displayed over element. */ void hideToolTip(const ToolTipManager::HideBehavior behavior = ToolTipManager::HideBehavior::Later); /** * Helper method for DolphinView::paste() and DolphinView::pasteIntoFolder(). * Pastes the clipboard data into the URL \a url. */ void pasteToUrl(const QUrl& url); /** * Returns a list of URLs for all selected items. The list is * simplified, so that when the URLs are part of different tree * levels, only the parent is returned. */ QList simplifiedSelectedUrls() const; /** * Returns the MIME data for all selected items. */ QMimeData* selectionMimeData() const; /** * Updates m_isFolderWritable dependent on whether the folder represented by * the current URL is writable. If the state has changed, the signal * writeableStateChanged() will be emitted. */ void updateWritableState(); /** * @return The current URL if no viewproperties-context is given (see * DolphinView::viewPropertiesContext(), otherwise the context * is returned. */ QUrl viewPropertiesUrl() const; /** * Clears the selection and updates current item and selection according to the parameters * * @param current URL to be set as current * @param selected list of selected items */ void forceUrlsSelection(const QUrl& current, const QList& selected); void abortTwoClicksRenaming(); private: void updatePalette(); bool m_active; bool m_tabsForFiles; bool m_assureVisibleCurrentIndex; bool m_isFolderWritable; bool m_dragging; // True if a dragging is done. Required to be able to decide whether a // tooltip may be shown when hovering an item. QUrl m_url; QString m_viewPropertiesContext; Mode m_mode; QList m_visibleRoles; QVBoxLayout* m_topLayout; KFileItemModel* m_model; DolphinItemListView* m_view; KItemListContainer* m_container; ToolTipManager* m_toolTipManager; QTimer* m_selectionChangedTimer; QUrl m_currentItemUrl; // Used for making the view to remember the current URL after F5 bool m_scrollToCurrentItem; // Used for marking we need to scroll to current item or not QPoint m_restoredContentsPosition; QList m_selectedUrls; // Used for making the view to remember selections after F5 bool m_clearSelectionBeforeSelectingNewItems; bool m_markFirstNewlySelectedItemAsCurrent; VersionControlObserver* m_versionControlObserver; QTimer* m_twoClicksRenamingTimer; QUrl m_twoClicksRenamingItemUrl; // For unit tests friend class TestBase; friend class DolphinDetailsViewTest; friend class DolphinPart; // Accesses m_model }; /// Allow using DolphinView::Mode in QVariant Q_DECLARE_METATYPE(DolphinView::Mode) #endif // DOLPHINVIEW_H diff --git a/src/views/dolphinviewactionhandler.cpp b/src/views/dolphinviewactionhandler.cpp index 4aa59911b..a520d7e53 100644 --- a/src/views/dolphinviewactionhandler.cpp +++ b/src/views/dolphinviewactionhandler.cpp @@ -1,683 +1,697 @@ /*************************************************************************** * Copyright (C) 2008 by David Faure * * Copyright (C) 2012 by Peter Penz * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include "dolphinviewactionhandler.h" #include "dolphindebug.h" #include "kitemviews/kfileitemmodel.h" #include "settings/viewpropertiesdialog.h" #include "views/zoomlevelinfo.h" #ifdef HAVE_BALOO #include #endif #include #include #include #include #include #include #include #include DolphinViewActionHandler::DolphinViewActionHandler(KActionCollection* collection, QObject* parent) : QObject(parent), m_actionCollection(collection), m_currentView(nullptr), m_sortByActions(), m_visibleRoles() { Q_ASSERT(m_actionCollection); createActions(); } void DolphinViewActionHandler::setCurrentView(DolphinView* view) { Q_ASSERT(view); if (m_currentView) { disconnect(m_currentView, nullptr, this, nullptr); } m_currentView = view; connect(view, &DolphinView::modeChanged, this, &DolphinViewActionHandler::updateViewActions); connect(view, &DolphinView::previewsShownChanged, this, &DolphinViewActionHandler::slotPreviewsShownChanged); connect(view, &DolphinView::sortOrderChanged, this, &DolphinViewActionHandler::slotSortOrderChanged); connect(view, &DolphinView::sortFoldersFirstChanged, this, &DolphinViewActionHandler::slotSortFoldersFirstChanged); connect(view, &DolphinView::visibleRolesChanged, this, &DolphinViewActionHandler::slotVisibleRolesChanged); connect(view, &DolphinView::groupedSortingChanged, this, &DolphinViewActionHandler::slotGroupedSortingChanged); connect(view, &DolphinView::hiddenFilesShownChanged, this, &DolphinViewActionHandler::slotHiddenFilesShownChanged); connect(view, &DolphinView::sortRoleChanged, this, &DolphinViewActionHandler::slotSortRoleChanged); connect(view, &DolphinView::zoomLevelChanged, this, &DolphinViewActionHandler::slotZoomLevelChanged); connect(view, &DolphinView::writeStateChanged, this, &DolphinViewActionHandler::slotWriteStateChanged); } DolphinView* DolphinViewActionHandler::currentView() { return m_currentView; } void DolphinViewActionHandler::createActions() { // This action doesn't appear in the GUI, it's for the shortcut only. // KNewFileMenu takes care of the GUI stuff. QAction* newDirAction = m_actionCollection->addAction(QStringLiteral("create_dir")); newDirAction->setText(i18nc("@action", "Create Folder...")); m_actionCollection->setDefaultShortcut(newDirAction, Qt::Key_F10); newDirAction->setIcon(QIcon::fromTheme(QStringLiteral("folder-new"))); newDirAction->setEnabled(false); // Will be enabled in slotWriteStateChanged(bool) if the current URL is writable connect(newDirAction, &QAction::triggered, this, &DolphinViewActionHandler::createDirectoryTriggered); // File menu auto renameAction = KStandardAction::renameFile(this, &DolphinViewActionHandler::slotRename, m_actionCollection); renameAction->setWhatsThis(xi18nc("@info:whatsthis", "This renames the " "items in your current selection.Renaming multiple items " "at once amounts to their new names differing only in a number.")); auto trashAction = KStandardAction::moveToTrash(this, &DolphinViewActionHandler::slotTrashActivated, m_actionCollection); auto trashShortcuts = trashAction->shortcuts(); if (!trashShortcuts.contains(QKeySequence::Delete)) { trashShortcuts.append(QKeySequence::Delete); m_actionCollection->setDefaultShortcuts(trashAction, trashShortcuts); } trashAction->setWhatsThis(xi18nc("@info:whatsthis", "This moves the " "items in your current selection to the Trash" ".The trash is a temporary storage where " "items can be deleted from if disk space is needed.")); auto deleteAction = KStandardAction::deleteFile(this, &DolphinViewActionHandler::slotDeleteItems, m_actionCollection); auto deleteShortcuts = deleteAction->shortcuts(); if (!deleteShortcuts.contains(Qt::SHIFT | Qt::Key_Delete)) { deleteShortcuts.append(Qt::SHIFT | Qt::Key_Delete); m_actionCollection->setDefaultShortcuts(deleteAction, deleteShortcuts); } deleteAction->setWhatsThis(xi18nc("@info:whatsthis", "This deletes " "the items in your current selection completely. They can " "not be recovered by normal means.")); // This action is useful for being enabled when KStandardAction::MoveToTrash should be // disabled and KStandardAction::DeleteFile is enabled (e.g. non-local files), so that Key_Del // can be used for deleting the file (#76016). It needs to be a separate action // so that the Edit menu isn't affected. QAction* deleteWithTrashShortcut = m_actionCollection->addAction(QStringLiteral("delete_shortcut")); // The descriptive text is just for the shortcuts editor. deleteWithTrashShortcut->setText(i18nc("@action \"Move to Trash\" for non-local files, etc.", "Delete (using shortcut for Trash)")); m_actionCollection->setDefaultShortcuts(deleteWithTrashShortcut, KStandardShortcut::moveToTrash()); deleteWithTrashShortcut->setEnabled(false); connect(deleteWithTrashShortcut, &QAction::triggered, this, &DolphinViewActionHandler::slotDeleteItems); QAction *propertiesAction = m_actionCollection->addAction( QStringLiteral("properties") ); // Well, it's the File menu in dolphinmainwindow and the Edit menu in dolphinpart... :) propertiesAction->setText( i18nc("@action:inmenu File", "Properties") ); propertiesAction->setWhatsThis(xi18nc("@info:whatsthis properties", "This shows a complete list of properties of the currently " "selected items in a new window.If nothing is selected the " "window will be about the currently viewed folder instead." "You can configure advanced options there like managing " "read- and write-permissions.")); propertiesAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); m_actionCollection->setDefaultShortcuts(propertiesAction, {Qt::ALT + Qt::Key_Return, Qt::ALT + Qt::Key_Enter}); connect(propertiesAction, &QAction::triggered, this, &DolphinViewActionHandler::slotProperties); // View menu KToggleAction* iconsAction = iconsModeAction(); KToggleAction* compactAction = compactModeAction(); KToggleAction* detailsAction = detailsModeAction(); iconsAction->setWhatsThis(xi18nc("@info:whatsthis Icons view mode", "This switches to a view mode that focuses on the folder " "and file icons. This mode makes it easy to distinguish folders " "from files and to detect items with distinctive " "file types. This mode is handy to " "browse through pictures when the Preview" " option is enabled.")); compactAction->setWhatsThis(xi18nc("@info:whatsthis Compact view mode", "This switches to a compact view mode that lists the folders " "and files in columns with the names beside the icons." "This helps to keep the overview in folders with many items.")); detailsAction->setWhatsThis(xi18nc("@info:whatsthis Details view mode", "This switches to a list view mode that focuses on folder " "and file details.Click on a detail in the column " "header to sort the items by it. Click again to sort the other " "way around. To select which details should be displayed click " "the header with the right mouse button.You can " "view the contents of a folder without leaving the current " "location by clicking to the left of it. This way you can view " "the contents of multiple folders in the same list.")); KSelectAction* viewModeActions = m_actionCollection->add(QStringLiteral("view_mode")); viewModeActions->setText(i18nc("@action:intoolbar", "View Mode")); viewModeActions->addAction(iconsAction); viewModeActions->addAction(compactAction); viewModeActions->addAction(detailsAction); viewModeActions->setToolBarMode(KSelectAction::MenuMode); connect(viewModeActions, QOverload::of(&KSelectAction::triggered), this, &DolphinViewActionHandler::slotViewModeActionTriggered); QAction* zoomInAction = KStandardAction::zoomIn(this, &DolphinViewActionHandler::zoomIn, m_actionCollection); zoomInAction->setWhatsThis(i18nc("@info:whatsthis zoom in", "This increases the icon size.")); + QAction* zoomResetAction = m_actionCollection->addAction(QStringLiteral("view_zoom_reset")); + zoomResetAction->setText(i18nc("@action:inmenu View", "Reset Zoom Level")); + zoomResetAction->setToolTip(i18n("Zoom To Default")); + zoomResetAction->setWhatsThis(i18nc("@info:whatsthis zoom reset", "This resets the icon size to default.")); + zoomResetAction->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original"))); + m_actionCollection->setDefaultShortcuts(zoomResetAction, {Qt::CTRL + Qt::Key_0}); + connect(zoomResetAction, &QAction::triggered, this, &DolphinViewActionHandler::zoomReset); + QAction* zoomOutAction = KStandardAction::zoomOut(this, &DolphinViewActionHandler::zoomOut, m_actionCollection); zoomOutAction->setWhatsThis(i18nc("@info:whatsthis zoom out", "This reduces the icon size.")); KToggleAction* showPreview = m_actionCollection->add(QStringLiteral("show_preview")); showPreview->setText(i18nc("@action:intoolbar", "Preview")); showPreview->setToolTip(i18nc("@info", "Show preview of files and folders")); showPreview->setWhatsThis(xi18nc("@info:whatsthis", "When this is " "enabled, the icons are based on the actual file or folder " "contents.For example the icons of images become scaled " "down versions of the images.")); showPreview->setIcon(QIcon::fromTheme(QStringLiteral("view-preview"))); connect(showPreview, &KToggleAction::triggered, this, &DolphinViewActionHandler::togglePreview); KToggleAction* sortFoldersFirst = m_actionCollection->add(QStringLiteral("folders_first")); sortFoldersFirst->setText(i18nc("@action:inmenu Sort", "Folders First")); connect(sortFoldersFirst, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleSortFoldersFirst); // View -> Sort By QActionGroup* sortByActionGroup = createFileItemRolesActionGroup(QStringLiteral("sort_by_")); KActionMenu* sortByActionMenu = m_actionCollection->add(QStringLiteral("sort")); sortByActionMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sort"))); sortByActionMenu->setText(i18nc("@action:inmenu View", "Sort By")); sortByActionMenu->setDelayed(false); foreach (QAction* action, sortByActionGroup->actions()) { sortByActionMenu->addAction(action); } sortByActionMenu->addSeparator(); QActionGroup* group = new QActionGroup(sortByActionMenu); group->setExclusive(true); KToggleAction* ascendingAction = m_actionCollection->add(QStringLiteral("ascending")); ascendingAction->setActionGroup(group); connect(ascendingAction, &QAction::triggered, this, [this] { m_currentView->setSortOrder(Qt::AscendingOrder); }); KToggleAction* descendingAction = m_actionCollection->add(QStringLiteral("descending")); descendingAction->setActionGroup(group); connect(descendingAction, &QAction::triggered, this, [this] { m_currentView->setSortOrder(Qt::DescendingOrder); }); sortByActionMenu->addAction(ascendingAction); sortByActionMenu->addAction(descendingAction); sortByActionMenu->addSeparator(); sortByActionMenu->addAction(sortFoldersFirst); // View -> Additional Information QActionGroup* visibleRolesGroup = createFileItemRolesActionGroup(QStringLiteral("show_")); KActionMenu* visibleRolesMenu = m_actionCollection->add(QStringLiteral("additional_info")); visibleRolesMenu->setText(i18nc("@action:inmenu View", "Additional Information")); visibleRolesMenu->setDelayed(false); foreach (QAction* action, visibleRolesGroup->actions()) { visibleRolesMenu->addAction(action); } KToggleAction* showInGroups = m_actionCollection->add(QStringLiteral("show_in_groups")); showInGroups->setIcon(QIcon::fromTheme(QStringLiteral("view-group"))); showInGroups->setText(i18nc("@action:inmenu View", "Show in Groups")); showInGroups->setWhatsThis(i18nc("@info:whatsthis", "This groups files and folders by their first letter.")); connect(showInGroups, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleGroupedSorting); KToggleAction* showHiddenFiles = m_actionCollection->add(QStringLiteral("show_hidden_files")); showHiddenFiles->setText(i18nc("@action:inmenu View", "Hidden Files")); showHiddenFiles->setToolTip(i18nc("@info", "Visibility of hidden files and folders")); showHiddenFiles->setWhatsThis(xi18nc("@info:whatsthis", "When " "this is enabled hidden files and folders " "are visible. They will be displayed semi-transparent." "Hidden items only differ from other ones in that their " "name starts with a \".\". In general there is no need for " "users to access them which is why they are hidden.")); m_actionCollection->setDefaultShortcuts(showHiddenFiles, {Qt::ALT + Qt::Key_Period, Qt::CTRL + Qt::Key_H, Qt::Key_F8}); connect(showHiddenFiles, &KToggleAction::triggered, this, &DolphinViewActionHandler::toggleShowHiddenFiles); QAction* adjustViewProps = m_actionCollection->addAction(QStringLiteral("view_properties")); adjustViewProps->setText(i18nc("@action:inmenu View", "Adjust View Properties...")); adjustViewProps->setWhatsThis(i18nc("@info:whatsthis", "This opens a window " "in which all folder view properties can be adjusted.")); connect(adjustViewProps, &QAction::triggered, this, &DolphinViewActionHandler::slotAdjustViewProperties); } QActionGroup* DolphinViewActionHandler::createFileItemRolesActionGroup(const QString& groupPrefix) { const bool isSortGroup = (groupPrefix == QLatin1String("sort_by_")); Q_ASSERT(isSortGroup || groupPrefix == QLatin1String("show_")); QActionGroup* rolesActionGroup = new QActionGroup(m_actionCollection); rolesActionGroup->setExclusive(isSortGroup); if (isSortGroup) { connect(rolesActionGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotSortTriggered); } else { connect(rolesActionGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::toggleVisibleRole); } QString groupName; KActionMenu* groupMenu = nullptr; QActionGroup* groupMenuGroup = nullptr; bool indexingEnabled = false; #ifdef HAVE_BALOO Baloo::IndexerConfig config; indexingEnabled = config.fileIndexingEnabled(); #endif const QList rolesInfo = KFileItemModel::rolesInformation(); foreach (const KFileItemModel::RoleInfo& info, rolesInfo) { if (!isSortGroup && info.role == "text") { // It should not be possible to hide the "text" role continue; } KToggleAction* action = nullptr; const QString name = groupPrefix + info.role; if (info.group.isEmpty()) { action = m_actionCollection->add(name); action->setActionGroup(rolesActionGroup); } else { if (!groupMenu || info.group != groupName) { groupName = info.group; groupMenu = m_actionCollection->add(groupName); groupMenu->setText(groupName); groupMenu->setActionGroup(rolesActionGroup); groupMenuGroup = new QActionGroup(groupMenu); groupMenuGroup->setExclusive(isSortGroup); if (isSortGroup) { connect(groupMenuGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::slotSortTriggered); } else { connect(groupMenuGroup, &QActionGroup::triggered, this, &DolphinViewActionHandler::toggleVisibleRole); } } action = new KToggleAction(groupMenu); action->setActionGroup(groupMenuGroup); groupMenu->addAction(action); } action->setText(info.translation); action->setData(info.role); const bool enable = (!info.requiresBaloo && !info.requiresIndexer) || (info.requiresBaloo) || (info.requiresIndexer && indexingEnabled); action->setEnabled(enable); if (isSortGroup) { m_sortByActions.insert(info.role, action); } else { m_visibleRoles.insert(info.role, action); } } return rolesActionGroup; } void DolphinViewActionHandler::slotViewModeActionTriggered(QAction* action) { const DolphinView::Mode mode = action->data().value(); m_currentView->setMode(mode); QAction* viewModeMenu = m_actionCollection->action(QStringLiteral("view_mode")); viewModeMenu->setIcon(action->icon()); } void DolphinViewActionHandler::slotRename() { emit actionBeingHandled(); m_currentView->renameSelectedItems(); } void DolphinViewActionHandler::slotTrashActivated() { emit actionBeingHandled(); m_currentView->trashSelectedItems(); } void DolphinViewActionHandler::slotDeleteItems() { emit actionBeingHandled(); m_currentView->deleteSelectedItems(); } void DolphinViewActionHandler::togglePreview(bool show) { emit actionBeingHandled(); m_currentView->setPreviewsShown(show); } void DolphinViewActionHandler::slotPreviewsShownChanged(bool shown) { Q_UNUSED(shown); // It is not enough to update the 'Show Preview' action, also - // the 'Zoom In' and 'Zoom Out' actions must be adapted. + // the 'Zoom In', 'Zoom Out' and 'Zoom Reset' actions must be adapted. updateViewActions(); } QString DolphinViewActionHandler::currentViewModeActionName() const { switch (m_currentView->mode()) { case DolphinView::IconsView: return QStringLiteral("icons"); case DolphinView::DetailsView: return QStringLiteral("details"); case DolphinView::CompactView: return QStringLiteral("compact"); default: Q_ASSERT(false); break; } return QString(); // can't happen } KActionCollection* DolphinViewActionHandler::actionCollection() { return m_actionCollection; } void DolphinViewActionHandler::updateViewActions() { QAction* viewModeAction = m_actionCollection->action(currentViewModeActionName()); if (viewModeAction) { viewModeAction->setChecked(true); QAction* viewModeMenu = m_actionCollection->action(QStringLiteral("view_mode")); viewModeMenu->setIcon(viewModeAction->icon()); } QAction* showPreviewAction = m_actionCollection->action(QStringLiteral("show_preview")); showPreviewAction->setChecked(m_currentView->previewsShown()); slotSortOrderChanged(m_currentView->sortOrder()); slotSortFoldersFirstChanged(m_currentView->sortFoldersFirst()); slotVisibleRolesChanged(m_currentView->visibleRoles(), QList()); slotGroupedSortingChanged(m_currentView->groupedSorting()); slotSortRoleChanged(m_currentView->sortRole()); slotZoomLevelChanged(m_currentView->zoomLevel(), -1); // Updates the "show_hidden_files" action state and icon slotHiddenFilesShownChanged(m_currentView->hiddenFilesShown()); } void DolphinViewActionHandler::zoomIn() { const int level = m_currentView->zoomLevel(); m_currentView->setZoomLevel(level + 1); updateViewActions(); } void DolphinViewActionHandler::zoomOut() { const int level = m_currentView->zoomLevel(); m_currentView->setZoomLevel(level - 1); updateViewActions(); } +void DolphinViewActionHandler::zoomReset() +{ + m_currentView->resetZoomLevel(); + updateViewActions(); +} + void DolphinViewActionHandler::toggleSortFoldersFirst() { const bool sortFirst = m_currentView->sortFoldersFirst(); m_currentView->setSortFoldersFirst(!sortFirst); } void DolphinViewActionHandler::slotSortOrderChanged(Qt::SortOrder order) { QAction* descending = m_actionCollection->action(QStringLiteral("descending")); QAction* ascending = m_actionCollection->action(QStringLiteral("ascending")); const bool sortDescending = (order == Qt::DescendingOrder); descending->setChecked(sortDescending); ascending->setChecked(!sortDescending); } void DolphinViewActionHandler::slotSortFoldersFirstChanged(bool foldersFirst) { m_actionCollection->action(QStringLiteral("folders_first"))->setChecked(foldersFirst); } void DolphinViewActionHandler::toggleVisibleRole(QAction* action) { emit actionBeingHandled(); const QByteArray toggledRole = action->data().toByteArray(); QList roles = m_currentView->visibleRoles(); const bool show = action->isChecked(); const int index = roles.indexOf(toggledRole); const bool containsInfo = (index >= 0); if (show && !containsInfo) { roles.append(toggledRole); m_currentView->setVisibleRoles(roles); } else if (!show && containsInfo) { roles.removeAt(index); m_currentView->setVisibleRoles(roles); Q_ASSERT(roles.indexOf(toggledRole) < 0); } } void DolphinViewActionHandler::slotVisibleRolesChanged(const QList& current, const QList& previous) { Q_UNUSED(previous); const QSet checkedRoles = current.toSet(); QHashIterator it(m_visibleRoles); while (it.hasNext()) { it.next(); const QByteArray& role = it.key(); KToggleAction* action = it.value(); action->setChecked(checkedRoles.contains(role)); } } void DolphinViewActionHandler::toggleGroupedSorting(bool grouped) { m_currentView->setGroupedSorting(grouped); } void DolphinViewActionHandler::slotGroupedSortingChanged(bool groupedSorting) { QAction* showInGroupsAction = m_actionCollection->action(QStringLiteral("show_in_groups")); showInGroupsAction->setChecked(groupedSorting); } void DolphinViewActionHandler::toggleShowHiddenFiles(bool show) { emit actionBeingHandled(); m_currentView->setHiddenFilesShown(show); } void DolphinViewActionHandler::slotHiddenFilesShownChanged(bool shown) { QAction* showHiddenFilesAction = m_actionCollection->action(QStringLiteral("show_hidden_files")); showHiddenFilesAction->setChecked(shown); // #374508: don't overwrite custom icons. const QString iconName = showHiddenFilesAction->icon().name(); if (!iconName.isEmpty() && iconName != QLatin1String("view-visible") && iconName != QLatin1String("view-hidden")) { return; } showHiddenFilesAction->setIcon(QIcon::fromTheme(shown ? QStringLiteral("view-visible") : QStringLiteral("view-hidden"))); } void DolphinViewActionHandler::slotWriteStateChanged(bool isFolderWritable) { m_actionCollection->action(QStringLiteral("create_dir"))->setEnabled(isFolderWritable && KProtocolManager::supportsMakeDir(currentView()->url())); } KToggleAction* DolphinViewActionHandler::iconsModeAction() { KToggleAction* iconsView = m_actionCollection->add(QStringLiteral("icons")); iconsView->setText(i18nc("@action:inmenu View Mode", "Icons")); iconsView->setToolTip(i18nc("@info", "Icons view mode")); m_actionCollection->setDefaultShortcut(iconsView, Qt::CTRL + Qt::Key_1); iconsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-icons"))); iconsView->setData(QVariant::fromValue(DolphinView::IconsView)); return iconsView; } KToggleAction* DolphinViewActionHandler::compactModeAction() { KToggleAction* iconsView = m_actionCollection->add(QStringLiteral("compact")); iconsView->setText(i18nc("@action:inmenu View Mode", "Compact")); iconsView->setToolTip(i18nc("@info", "Compact view mode")); m_actionCollection->setDefaultShortcut(iconsView, Qt::CTRL + Qt::Key_2); iconsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-details"))); // TODO: discuss with Oxygen-team the wrong (?) name iconsView->setData(QVariant::fromValue(DolphinView::CompactView)); return iconsView; } KToggleAction* DolphinViewActionHandler::detailsModeAction() { KToggleAction* detailsView = m_actionCollection->add(QStringLiteral("details")); detailsView->setText(i18nc("@action:inmenu View Mode", "Details")); detailsView->setToolTip(i18nc("@info", "Details view mode")); m_actionCollection->setDefaultShortcut(detailsView, Qt::CTRL + Qt::Key_3); detailsView->setIcon(QIcon::fromTheme(QStringLiteral("view-list-tree"))); detailsView->setData(QVariant::fromValue(DolphinView::DetailsView)); return detailsView; } void DolphinViewActionHandler::slotSortRoleChanged(const QByteArray& role) { KToggleAction* action = m_sortByActions.value(role); if (action) { action->setChecked(true); if (!action->icon().isNull()) { QAction* sortByMenu = m_actionCollection->action(QStringLiteral("sort")); sortByMenu->setIcon(action->icon()); } } QAction* descending = m_actionCollection->action(QStringLiteral("descending")); QAction* ascending = m_actionCollection->action(QStringLiteral("ascending")); if (role == "text" || role == "type" || role == "tags" || role == "comment") { descending->setText(i18nc("Sort descending", "Z-A")); ascending->setText(i18nc("Sort ascending", "A-Z")); } else if (role == "size") { descending->setText(i18nc("Sort descending", "Largest first")); ascending->setText(i18nc("Sort ascending", "Smallest first")); } else if (role == "modificationtime" || role == "creationtime" || role == "accesstime") { descending->setText(i18nc("Sort descending", "Newest first")); ascending->setText(i18nc("Sort ascending", "Oldest first")); } else if (role == "rating") { descending->setText(i18nc("Sort descending", "Highest first")); ascending->setText(i18nc("Sort ascending", "Lowest first")); } else { descending->setText(i18nc("Sort descending", "Descending")); ascending->setText(i18nc("Sort ascending", "Ascending")); } slotSortOrderChanged(m_currentView->sortOrder()); } void DolphinViewActionHandler::slotZoomLevelChanged(int current, int previous) { Q_UNUSED(previous); QAction* zoomInAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomIn)); if (zoomInAction) { zoomInAction->setEnabled(current < ZoomLevelInfo::maximumLevel()); } QAction* zoomOutAction = m_actionCollection->action(KStandardAction::name(KStandardAction::ZoomOut)); if (zoomOutAction) { zoomOutAction->setEnabled(current > ZoomLevelInfo::minimumLevel()); } } void DolphinViewActionHandler::slotSortTriggered(QAction* action) { // The radiobuttons of the "Sort By"-menu are split between the main-menu // and several sub-menus. Because of this they don't have a common // action-group that assures an exclusive toggle-state between the main-menu // actions and the sub-menu-actions. If an action gets checked, it must // be assured that all other actions get unchecked, except the ascending/ // descending actions QAction* sortByMenu = m_actionCollection->action(QStringLiteral("sort")); for (QAction *groupAction : qAsConst(m_sortByActions)) { KActionMenu* actionMenu = qobject_cast(groupAction); if (actionMenu) { foreach (QAction* subAction, actionMenu->menu()->actions()) { subAction->setChecked(false); } } else if (groupAction->actionGroup()) { groupAction->setChecked(false); } } action->setChecked(true); // Apply the activated sort-role to the view const QByteArray role = action->data().toByteArray(); m_currentView->setSortRole(role); } void DolphinViewActionHandler::slotAdjustViewProperties() { emit actionBeingHandled(); QPointer dialog = new ViewPropertiesDialog(m_currentView); dialog->exec(); delete dialog; } void DolphinViewActionHandler::slotProperties() { KPropertiesDialog* dialog = nullptr; const KFileItemList list = m_currentView->selectedItems(); if (list.isEmpty()) { const QUrl url = m_currentView->url(); dialog = new KPropertiesDialog(url, m_currentView); } else { dialog = new KPropertiesDialog(list, m_currentView); } dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); dialog->raise(); dialog->activateWindow(); } diff --git a/src/views/dolphinviewactionhandler.h b/src/views/dolphinviewactionhandler.h index 7d675b7d4..f931b3b9c 100644 --- a/src/views/dolphinviewactionhandler.h +++ b/src/views/dolphinviewactionhandler.h @@ -1,256 +1,259 @@ /*************************************************************************** * Copyright (C) 2008 by David Faure * * Copyright (C) 2012 by Peter Penz * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #ifndef DOLPHINVIEWACTIONHANDLER_H #define DOLPHINVIEWACTIONHANDLER_H #include "dolphin_export.h" #include "views/dolphinview.h" #include class KToggleAction; class QAction; class QActionGroup; class DolphinView; class KActionCollection; /** * @short Handles all actions for DolphinView * * The action handler owns all the actions and slots related to DolphinView, * but the view that it acts upon can be switched to another one * (this is used in the case of split views). * * The purpose of this class is also to share this code between DolphinMainWindow * and DolphinPart. * * @see DolphinView * @see DolphinMainWindow * @see DolphinPart */ class DOLPHIN_EXPORT DolphinViewActionHandler : public QObject { Q_OBJECT public: explicit DolphinViewActionHandler(KActionCollection* collection, QObject* parent); /** * Sets the view that this action handler should work on. */ void setCurrentView(DolphinView* view); /** * Returns the view that this action handler should work on. */ DolphinView* currentView(); /** * Returns the name of the action for the current viewmode */ QString currentViewModeActionName() const; /** * Returns m_actionCollection */ KActionCollection* actionCollection(); public Q_SLOTS: /** * Update all actions in the 'View' menu, i.e. those that depend on the * settings in the current view. */ void updateViewActions(); Q_SIGNALS: /** * Emitted by DolphinViewActionHandler when the user triggered an action. * This is only used for clearing the statusbar in DolphinMainWindow. */ void actionBeingHandled(); /** * Emitted if the user requested creating a new directory by the F10 key. * The receiver of the signal (DolphinMainWindow or DolphinPart) invokes * the method createDirectory of their KNewFileMenu instance. */ void createDirectoryTriggered(); private Q_SLOTS: /** * Emitted when the user requested a change of view mode */ void slotViewModeActionTriggered(QAction*); /** * Let the user input a name for the selected item(s) and trigger * a renaming afterwards. */ void slotRename(); /** * Moves the selected items of the active view to the trash. * This methods adds "shift means del" handling. */ void slotTrashActivated(); /** * Deletes the selected items of the active view. */ void slotDeleteItems(); /** * Switches between showing a preview of the file content and showing the icon. */ void togglePreview(bool); /** Updates the state of the 'Show preview' menu action. */ void slotPreviewsShownChanged(bool shown); /** Increases the size of the current set view mode. */ void zoomIn(); /** Decreases the size of the current set view mode. */ void zoomOut(); + + /** Resets the size of the current set view mode to default. */ + void zoomReset(); /** Switches between a separate sorting and a mixed sorting of files and folders. */ void toggleSortFoldersFirst(); /** * Updates the state of the 'Sort Ascending/Descending' action. */ void slotSortOrderChanged(Qt::SortOrder order); /** * Updates the state of the 'Sort Folders First' action. */ void slotSortFoldersFirstChanged(bool foldersFirst); /** * Updates the state of the 'Sort by' actions. */ void slotSortRoleChanged(const QByteArray& role); /** * Updates the state of the 'Zoom In' and 'Zoom Out' actions. */ void slotZoomLevelChanged(int current, int previous); /** * Switches on or off the displaying of additional information * as specified by \a action. */ void toggleVisibleRole(QAction* action); /** * Changes the sorting of the current view. */ void slotSortTriggered(QAction*); /** * Updates the state of the 'Additional Information' actions. */ void slotVisibleRolesChanged(const QList& current, const QList& previous); /** * Switches between sorting by groups or not. */ void toggleGroupedSorting(bool); /** * Updates the state of the 'Categorized sorting' menu action. */ void slotGroupedSortingChanged(bool sortCategorized); /** * Switches between showing and hiding of hidden marked files */ void toggleShowHiddenFiles(bool); /** * Updates the state of the 'Show hidden files' menu action. */ void slotHiddenFilesShownChanged(bool shown); /** * Updates the state of the 'Create Folder...' action. */ void slotWriteStateChanged(bool isFolderWritable); /** * Opens the view properties dialog, which allows to modify the properties * of the currently active view. */ void slotAdjustViewProperties(); /** * Connected to the "properties" action. * Opens the properties dialog for the selected items of the * active view. The properties dialog shows information * like name, size and permissions. */ void slotProperties(); private: /** * Create all the actions. * This is called only once (by the constructor) */ void createActions(); /** * Creates an action-group out of all roles from KFileItemModel. * Dependent on the group-prefix either a radiobutton-group is * created for sorting (prefix is "sort_by_") or a checkbox-group * is created for additional information (prefix is "show_"). * The changes of actions are reported to slotSortTriggered() or * toggleAdditionalInfo(). */ QActionGroup* createFileItemRolesActionGroup(const QString& groupPrefix); /** * Returns the "switch to icons mode" action. * Helper method for createActions(); */ KToggleAction* iconsModeAction(); /** * Returns the "switch to compact mode" action. * Helper method for createActions(); */ KToggleAction* compactModeAction(); /** * Returns the "switch to details mode" action. * Helper method for createActions(); */ KToggleAction* detailsModeAction(); KActionCollection* m_actionCollection; DolphinView* m_currentView; QHash m_sortByActions; QHash m_visibleRoles; }; #endif /* DOLPHINVIEWACTIONHANDLER_H */