diff --git a/containments/desktop/package/contents/config/main.xml b/containments/desktop/package/contents/config/main.xml
index 2dc6a4eac..43a49358f 100644
--- a/containments/desktop/package/contents/config/main.xml
+++ b/containments/desktop/package/contents/config/main.xml
@@ -1,148 +1,152 @@
folder
false
true
desktop:/
1
0
0
false
0
false
true
false
true
true
true
0
4
+
+
+ 1
+
2
white
true
*
0
all/all
true
true
true
true
diff --git a/containments/desktop/package/contents/ui/ConfigIcons.qml b/containments/desktop/package/contents/ui/ConfigIcons.qml
index 99dda679a..6c47035ef 100644
--- a/containments/desktop/package/contents/ui/ConfigIcons.qml
+++ b/containments/desktop/package/contents/ui/ConfigIcons.qml
@@ -1,318 +1,332 @@
/***************************************************************************
* Copyright (C) 2014 by Eike Hein *
* Copyright (C) 2015 by Kai Uwe Broulik *
* *
* 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 . *
***************************************************************************/
import QtQuick 2.4
import QtQuick.Controls 1.0
import QtQuick.Dialogs 1.1
import QtQuick.Layouts 1.0
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.kquickcontrolsaddons 2.0
import org.kde.kconfig 1.0 // for KAuthorized
import org.kde.kirigami 2.4 as Kirigami
import org.kde.private.desktopcontainment.desktop 0.1 as Desktop
import org.kde.private.desktopcontainment.folder 0.1 as Folder
Item {
id: configIcons
width: childrenRect.width
height: childrenRect.height
property bool isPopup: (plasmoid.location !== PlasmaCore.Types.Floating)
property string cfg_icon: plasmoid.configuration.icon
property alias cfg_useCustomIcon: useCustomIcon.checked
property alias cfg_arrangement: arrangement.currentIndex
property alias cfg_alignment: alignment.currentIndex
property bool cfg_locked
property alias cfg_sortMode: sortMode.mode
property alias cfg_sortDesc: sortDesc.checked
property alias cfg_sortDirsFirst: sortDirsFirst.checked
property alias cfg_toolTips: toolTips.checked
property alias cfg_selectionMarkers: selectionMarkers.checked
property alias cfg_popups: popups.checked
property alias cfg_previews: previews.checked
property alias cfg_previewPlugins: previewPluginsDialog.previewPlugins
property alias cfg_viewMode: viewMode.currentIndex
property alias cfg_iconSize: iconSize.value
+ property alias cfg_labelWidth: labelWidth.currentIndex
property alias cfg_textLines: textLines.value
readonly property bool lockedByKiosk: !KAuthorized.authorize("editable_desktop_icons")
IconDialog {
id: iconDialog
onIconNameChanged: cfg_icon = iconName || "folder"
}
Kirigami.FormLayout {
anchors.horizontalCenter: parent.horizontalCenter
// Panel button
RowLayout {
spacing: units.smallSpacing
visible: isPopup
Kirigami.FormData.label: i18n("Panel button:")
CheckBox {
id: useCustomIcon
visible: isPopup
checked: cfg_useCustomIcon
text: i18n("Use a custom icon")
}
Button {
id: iconButton
Layout.minimumWidth: units.iconSizes.large + units.smallSpacing * 2
Layout.maximumWidth: Layout.minimumWidth
Layout.minimumHeight: Layout.minimumWidth
Layout.maximumHeight: Layout.minimumWidth
checkable: true
enabled: useCustomIcon.checked
onClicked: {
checked = Qt.binding(function() {
return iconMenu.status === PlasmaComponents.DialogStatus.Open;
})
iconMenu.open(0, height);
}
PlasmaCore.IconItem {
anchors.centerIn: parent
width: units.iconSizes.large
height: width
source: cfg_icon
}
}
PlasmaComponents.ContextMenu {
id: iconMenu
visualParent: iconButton
PlasmaComponents.MenuItem {
text: i18nc("@item:inmenu Open icon chooser dialog", "Choose...")
icon: "document-open-folder"
onClicked: iconDialog.open()
}
PlasmaComponents.MenuItem {
text: i18nc("@item:inmenu Reset icon to default", "Clear Icon")
icon: "edit-clear"
onClicked: cfg_icon = "folder"
}
}
}
Item {
visible: isPopup
Kirigami.FormData.isSection: true
}
// Arrangement section
ComboBox {
id: arrangement
Layout.fillWidth: true
Kirigami.FormData.label: i18n("Arrangement:")
model: [i18n("Rows"), i18n("Columns")]
}
ComboBox {
id: alignment
Layout.fillWidth: true
model: [i18n("Align Left"), i18n("Align Right")]
}
CheckBox {
id: locked
visible: ("containmentType" in plasmoid)
checked: cfg_locked || lockedByKiosk
enabled: !lockedByKiosk
onCheckedChanged: {
if (!lockedByKiosk) {
cfg_locked = checked;
}
}
text: i18n("Lock in place")
}
Item {
Kirigami.FormData.isSection: true
}
// Sorting section
ComboBox {
id: sortMode
Layout.fillWidth: true
Kirigami.FormData.label: i18n("Sorting:")
property int mode
// FIXME TODO HACK: This maps the combo box list model to the KDirModel::ModelColumns
// enum, which should be done in C++.
property variant indexToMode: [-1, 0, 1, 6, 2]
property variant modeToIndex: {'-1' : '0', '0' : '1', '1' : '2', '6' : '3', '2' : '4'}
model: [i18n("Manual"), i18n("Name"), i18n("Size"), i18n("Type"), i18n("Date")]
Component.onCompleted: currentIndex = modeToIndex[mode]
onActivated: mode = indexToMode[index]
}
CheckBox {
id: sortDesc
enabled: (sortMode.currentIndex != 0)
text: i18n("Descending")
}
CheckBox {
id: sortDirsFirst
enabled: (sortMode.currentIndex != 0)
text: i18n("Folders first")
}
Item {
Kirigami.FormData.isSection: true
}
// View Mode section (only if we're a pop-up)
ComboBox {
id: viewMode
visible: isPopup
Layout.fillWidth: true
Kirigami.FormData.label: i18nc("whether to use icon or list view", "View mode:")
model: [i18n("List"), i18n("Icons")]
}
// Size section
Slider {
id: iconSize
visible: !isPopup || viewMode.currentIndex === 1
- Kirigami.FormData.label: i18n("Size:")
+ Kirigami.FormData.label: i18n("Icon size:")
minimumValue: 0
maximumValue: 5
stepSize: 1
tickmarksEnabled: true
}
RowLayout {
Layout.fillWidth: true
Label {
Layout.alignment: Qt.AlignLeft
visible: !isPopup || viewMode.currentIndex === 1
text: i18n("Small")
}
Item {
Layout.fillWidth: true
}
Label {
Layout.alignment: Qt.AlignRight
visible: !isPopup || viewMode.currentIndex === 1
text: i18n("Large")
}
}
+ ComboBox {
+ id: labelWidth
+ visible: !isPopup || viewMode.currentIndex === 1
+
+ Kirigami.FormData.label: i18n("Label width:")
+
+ model: [
+ i18n("Narrow"),
+ i18n("Medium"),
+ i18n("Wide")
+ ]
+ }
+
SpinBox {
id: textLines
visible: !isPopup || viewMode.currentIndex === 1
Kirigami.FormData.label: i18n("Text lines:")
minimumValue: 1
maximumValue: 10
stepSize: 1
}
Item {
Kirigami.FormData.isSection: true
}
// Features section
CheckBox {
id: toolTips
Kirigami.FormData.label: i18n("Features:")
text: i18n("Tooltips")
}
CheckBox {
id: selectionMarkers
visible: Qt.styleHints.singleClickActivation
text: i18n("Selection markers")
}
CheckBox {
id: popups
visible: !isPopup
text: i18n("Folder preview popups")
}
CheckBox {
id: previews
text: i18n("Preview thumbnails")
}
Button {
id: previewSettings
text: i18n("More Preview Options...")
onClicked: {
previewPluginsDialog.visible = true;
}
}
}
FolderItemPreviewPluginsDialog {
id: previewPluginsDialog
}
}
diff --git a/containments/desktop/package/contents/ui/FolderView.qml b/containments/desktop/package/contents/ui/FolderView.qml
index 93dc77288..7d6136f6d 100644
--- a/containments/desktop/package/contents/ui/FolderView.qml
+++ b/containments/desktop/package/contents/ui/FolderView.qml
@@ -1,1396 +1,1396 @@
/***************************************************************************
* Copyright (C) 2014-2015 by Eike Hein *
* *
* 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 . *
***************************************************************************/
import QtQuick 2.4
import QtQuick.Layouts 1.1
import org.kde.plasma.plasmoid 2.0
import org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.plasma.extras 2.0 as PlasmaExtras
import org.kde.kquickcontrolsaddons 2.0
import org.kde.private.desktopcontainment.folder 0.1 as Folder
import "code/FolderTools.js" as FolderTools
FocusScope {
id: main
signal pressed
property QtObject model: dir
property Item rubberBand: null
property alias isRootView: gridView.isRootView
property alias currentIndex: gridView.currentIndex
property alias url: dir.url
property alias status: dir.status
property alias positions: positioner.positions
property alias errorString: dir.errorString
property alias dragging: dir.dragging
property alias locked: dir.locked
property alias sortMode: dir.sortMode
property alias filterMode: dir.filterMode
property alias filterPattern: dir.filterPattern
property alias filterMimeTypes: dir.filterMimeTypes
property alias flow: gridView.flow
property alias layoutDirection: gridView.layoutDirection
property alias cellWidth: gridView.cellWidth
property alias cellHeight: gridView.cellHeight
property alias overflowing: gridView.overflowing
property alias scrollLeft: gridView.scrollLeft
property alias scrollRight: gridView.scrollRight
property alias scrollUp: gridView.scrollUp
property alias scrollDown: gridView.scrollDown
property alias hoveredItem: gridView.hoveredItem
property var history: []
property var lastPosition: null
property bool goingBack: false
property Item backButton: null
property var dialog: null
property Item editor: null
function positionViewAtBeginning() {
gridView.positionViewAtBeginning();
}
function rename() {
if (gridView.currentIndex != -1) {
var renameAction = folderView.model.action("rename");
if (renameAction && !renameAction.enabled) {
return;
}
if (!editor) {
editor = editorComponent.createObject(listener);
}
editor.targetItem = gridView.currentItem;
}
}
function cancelRename() {
if (editor) {
editor.targetItem = null;
}
}
function linkHere(sourceUrl) {
dir.linkHere(sourceUrl);
}
function handleDragMove(x, y) {
var child = childAt(x, y);
if (child !== null && child === backButton) {
hoveredItem = null;
backButton.handleDragMove();
} else {
if (backButton && backButton.containsDrag) {
backButton.endDragMove();
}
var pos = mapToItem(gridView.contentItem, x, y);
var item = gridView.itemAt(pos.x, pos.y);
if (item && item.isDir) {
hoveredItem = item;
} else {
hoveredItem = null;
}
}
}
function endDragMove() {
if (backButton && backButton.active) {
backButton.endDragMove();
} else if (hoveredItem && !hoveredItem.popupDialog) {
hoveredItem = null;
}
}
function dropItemAt(pos) {
var item = gridView.itemAt(pos.x, pos.y);
if (item) {
if (item.blank) {
return -1;
}
var hOffset = Math.abs(Math.min(gridView.contentX, gridView.originX));
var hPos = mapToItem(item.hoverArea, pos.x + hOffset, pos.y);
if ((hPos.x < 0 || hPos.y < 0 || hPos.x > item.hoverArea.width || hPos.y > item.hoverArea.height)) {
return -1;
} else {
return positioner.map(item.index);
}
}
return -1;
}
function drop(target, event, pos) {
var dropPos = mapToItem(gridView.contentItem, pos.x, pos.y);
var dropIndex = gridView.indexAt(dropPos.x, dropPos.y);
var dragPos = mapToItem(gridView.contentItem, listener.dragX, listener.dragY);
var dragIndex = gridView.indexAt(dragPos.x, dragPos.y);
if (listener.dragX == -1 || dragIndex !== dropIndex) {
dir.drop(target, event, dropItemAt(dropPos));
}
}
Connections {
target: dir
onPopupMenuAboutToShow: {
if (!plasmoid.immutable) {
plasmoid.processMimeData(mimeData, x, y, dropJob);
}
}
}
Connections {
target: plasmoid
onExpandedChanged: {
if (plasmoid.expanded && dir.status === Folder.FolderModel.Ready && !gridView.model) {
gridView.model = positioner;
}
}
}
// Lower the toolBox when an item is hovered, so it doesn't interfere with
// its interaction (e.g. the selection button in the top left, cf. Bug 337060)
Binding {
target: toolBox
property: "z"
// 999 is the default "z" for desktop ToolBoxRoot
value: main.hoveredItem ? -100 : 999
when: toolBox
}
Binding {
target: plasmoid
property: "busy"
value: !gridView.model && dir.status === Folder.FolderModel.Listing
}
function makeBackButton() {
return Qt.createQmlObject("BackButtonItem {}", main);
}
function doCd(row) {
history.push({"url": url, "index": gridView.currentIndex, "yPosition": gridView.visibleArea.yPosition});
updateHistory();
dir.cd(row);
gridView.currentIndex = -1;
}
function doBack() {
goingBack = true;
gridView.currentIndex = -1;
lastPosition = history.pop();
url = lastPosition.url;
updateHistory();
}
// QML doesn't detect change in the array(history) property, so update it explicitly.
function updateHistory() {
history = history;
}
Connections {
target: root
onIsPopupChanged: {
if (backButton == null && root.useListViewMode) {
backButton = makeBackButton();
} else if (backButton != null) {
backButton.destroy();
}
}
}
MouseEventListener {
id: listener
anchors {
topMargin: backButton != null ? backButton.height : undefined
fill: parent
}
property alias hoveredItem: gridView.hoveredItem
property Item pressedItem: null
property int pressX: -1
property int pressY: -1
property int dragX: -1
property int dragY: -1
property variant cPress: null
property bool doubleClickInProgress: false
acceptedButtons: {
if (hoveredItem == null && main.isRootView) {
return root.isPopup ? (Qt.LeftButton | Qt.MiddleButton | Qt.BackButton) : Qt.LeftButton;
}
return root.isPopup ? (Qt.LeftButton | Qt.MiddleButton | Qt.RightButton | Qt.BackButton)
: (Qt.LeftButton | Qt.RightButton);
}
hoverEnabled: true
onPressXChanged: {
cPress = mapToItem(gridView.contentItem, pressX, pressY);
}
onPressYChanged: {
cPress = mapToItem(gridView.contentItem, pressX, pressY);
}
onPressed: {
// Ignore press events outside the viewport (i.e. on scrollbars).
if (!scrollArea.viewport.contains(Qt.point(mouse.x,mouse.y))) {
return;
}
scrollArea.focus = true;
if (mouse.buttons & Qt.BackButton) {
if (root.isPopup && dir.resolvedUrl !== dir.resolve(plasmoid.configuration.url)) {
doBack();
mouse.accepted = true;
}
return;
}
if (editor && childAt(mouse.x, mouse.y) !== editor) {
editor.commit();
}
pressX = mouse.x;
pressY = mouse.y;
if (!hoveredItem || hoveredItem.blank) {
if (!gridView.ctrlPressed) {
gridView.currentIndex = -1;
dir.clearSelection();
}
if (mouse.buttons & Qt.RightButton) {
clearPressState();
dir.openContextMenu(null, mouse.modifiers);
mouse.accepted = true;
}
} else {
pressedItem = hoveredItem;
var pos = mapToItem(hoveredItem.actionsOverlay, mouse.x, mouse.y);
if (!(pos.x <= hoveredItem.actionsOverlay.width && pos.y <= hoveredItem.actionsOverlay.height)) {
if (gridView.shiftPressed && gridView.currentIndex != -1) {
positioner.setRangeSelected(gridView.anchorIndex, hoveredItem.index);
} else {
// FIXME TODO: Clicking one item with others selected should deselect the others,
// which doesn't happen right now because initiating a drag after the press should
// still drag all of them: The deselect needs to happen on release instead so we
// can distinguish.
if (!gridView.ctrlPressed && !dir.isSelected(positioner.map(hoveredItem.index))) {
dir.clearSelection();
}
if (gridView.ctrlPressed) {
dir.toggleSelected(positioner.map(hoveredItem.index));
} else {
dir.setSelected(positioner.map(hoveredItem.index));
}
}
gridView.currentIndex = hoveredItem.index;
if (mouse.buttons & Qt.RightButton) {
if (pressedItem.toolTip && pressedItem.toolTip.active) {
pressedItem.toolTip.hideToolTip();
}
clearPressState();
dir.openContextMenu(null, mouse.modifiers);
mouse.accepted = true;
}
}
}
main.pressed();
}
onCanceled: pressCanceled()
onReleased: pressCanceled()
onClicked: {
clearPressState();
if (mouse.button === Qt.RightButton ||
(editor && childAt(mouse.x, mouse.y) === editor)) {
return;
}
if (!hoveredItem || hoveredItem.blank || gridView.currentIndex == -1 || gridView.ctrlPressed || gridView.shiftPressed) {
// Bug 357367: Replay mouse event, so containment actions assigned to left mouse button work.
eventGenerator.sendMouseEvent(plasmoid, EventGenerator.MouseButtonPress, mouse.x, mouse.y, mouse.button, mouse.buttons, mouse.modifiers);
return;
}
var pos = mapToItem(hoveredItem, mouse.x, mouse.y);
// Moving from an item to its preview popup dialog doesn't unset hoveredItem
// even though the cursor has left it, so we need to check whether the click
// actually occurred inside the item we expect it in before going ahead. If it
// didn't, clean up (e.g. dismissing the dialog as a side-effect of unsetting
// hoveredItem) and abort.
if (pos.x < 0 || pos.x > hoveredItem.width || pos.y < 0 || pos.y > hoveredItem.height) {
hoveredItem = null;
dir.clearSelection();
return;
// If the hoveredItem is clicked while having a preview popup dialog open,
// only dismiss the dialog and abort.
} else if (hoveredItem.popupDialog) {
hoveredItem.closePopup();
return;
}
pos = mapToItem(hoveredItem.actionsOverlay, mouse.x, mouse.y);
if (!(pos.x <= hoveredItem.actionsOverlay.width && pos.y <= hoveredItem.actionsOverlay.height)) {
if (Qt.styleHints.singleClickActivation || doubleClickInProgress) {
var func = root.useListViewMode && (mouse.button === Qt.LeftButton) && hoveredItem.isDir ? doCd : dir.run;
func(positioner.map(gridView.currentIndex));
hoveredItem = null;
} else {
doubleClickInProgress = true;
doubleClickTimer.interval = Qt.styleHints.mouseDoubleClickInterval;
doubleClickTimer.start();
}
}
}
onPositionChanged: {
gridView.ctrlPressed = (mouse.modifiers & Qt.ControlModifier);
gridView.shiftPressed = (mouse.modifiers & Qt.ShiftModifier);
var cPos = mapToItem(gridView.contentItem, mouse.x, mouse.y);
var item = gridView.itemAt(cPos.x, cPos.y);
var leftEdge = Math.min(gridView.contentX, gridView.originX);
if (!item || item.blank) {
if (gridView.hoveredItem && !root.containsDrag && (!dialog || !dialog.containsDrag) && !gridView.hoveredItem.popupDialog) {
gridView.hoveredItem = null;
}
} else {
var fPos = mapToItem(item.frame, mouse.x, mouse.y);
if (fPos.x < 0 || fPos.y < 0 || fPos.x > item.frame.width || fPos.y > item.frame.height) {
gridView.hoveredItem = null;
}
}
// Trigger autoscroll.
if (pressX != -1) {
gridView.scrollLeft = (mouse.x <= 0 && gridView.contentX > leftEdge);
gridView.scrollRight = (mouse.x >= gridView.width
&& gridView.contentX < gridView.contentItem.width - gridView.width);
gridView.scrollUp = (mouse.y <= 0 && gridView.contentY > 0);
gridView.scrollDown = (mouse.y >= gridView.height
&& gridView.contentY < gridView.contentItem.height - gridView.height);
}
// Update rubberband geometry.
if (main.rubberBand) {
var rB = main.rubberBand;
if (cPos.x < cPress.x) {
rB.x = Math.max(leftEdge, cPos.x);
rB.width = Math.abs(rB.x - cPress.x);
} else {
rB.x = cPress.x;
var ceil = Math.max(gridView.width, gridView.contentItem.width) + leftEdge;
rB.width = Math.min(ceil - rB.x, Math.abs(rB.x - cPos.x));
}
if (cPos.y < cPress.y) {
rB.y = Math.max(0, cPos.y);
rB.height = Math.abs(rB.y - cPress.y);
} else {
rB.y = cPress.y;
var ceil = Math.max(gridView.height, gridView.contentItem.height);
rB.height = Math.min(ceil - rB.y, Math.abs(rB.y - cPos.y));
}
// Ensure rubberband is at least 1px in size or else it will become
// invisible and not match any items.
rB.width = Math.max(1, rB.width);
rB.height = Math.max(1, rB.height);
gridView.rectangleSelect(rB.x, rB.y, rB.width, rB.height);
return;
}
// Drag initiation.
if (pressX != -1 && root.isDrag(pressX, pressY, mouse.x, mouse.y)) {
if (pressedItem != null && dir.isSelected(positioner.map(pressedItem.index))) {
pressedItem.toolTip.hideToolTip();
dragX = mouse.x;
dragY = mouse.y;
gridView.verticalDropHitscanOffset = pressedItem.iconArea.y + (pressedItem.iconArea.height / 2)
dir.dragSelected(mouse.x, mouse.y);
dragX = -1;
dragY = -1;
clearPressState();
} else {
// Disable rubberband in popup list view mode.
if (root.useListViewMode) {
return;
}
dir.pinSelection();
main.rubberBand = Qt.createQmlObject("import QtQuick 2.0; import org.kde.private.desktopcontainment.folder 0.1 as Folder;"
+ "Folder.RubberBand { x: " + cPress.x + "; y: " + cPress.y + "; width: 0; height: 0; z: 99999; }",
gridView.contentItem);
gridView.interactive = false;
}
}
}
onContainsMouseChanged: {
if (!containsMouse && !main.rubberBand) {
clearPressState();
if (gridView.hoveredItem && !gridView.hoveredItem.popupDialog) {
gridView.hoveredItem = null;
}
}
}
onHoveredItemChanged: {
doubleClickInProgress = false;
if (!hoveredItem) {
hoverActivateTimer.stop();
}
}
function pressCanceled() {
if (main.rubberBand) {
main.rubberBand.visible = false;
main.rubberBand.enabled = false;
main.rubberBand.destroy();
main.rubberBand = null;
gridView.interactive = true;
gridView.cachedRectangleSelection = null;
dir.unpinSelection();
}
clearPressState();
gridView.cancelAutoscroll();
}
function clearPressState() {
pressedItem = null;
pressX = -1;
pressY = -1;
}
Timer {
id: doubleClickTimer
onTriggered: {
listener.doubleClickInProgress = false;
}
}
Timer {
id: hoverActivateTimer
interval: root.hoverActivateDelay
onTriggered: {
if (!hoveredItem) {
return;
}
if (root.useListViewMode) {
doCd(index);
} else {
hoveredItem.openPopup();
}
}
}
PlasmaExtras.ScrollArea {
id: scrollArea
anchors.fill: parent
focus: true
property bool ready: false
readonly property int viewportWidth: scrollArea.ready && viewport ? Math.ceil(viewport.width) : 0
readonly property int viewportHeight: scrollArea.ready && viewport ? Math.ceil(viewport.height) : 0
Component.onCompleted: {
scrollArea.ready = true;
}
GridView {
id: gridView
property bool isRootView: false
property int iconSize: makeIconSize()
property int verticalDropHitscanOffset: 0
property Item hoveredItem: null
property int anchorIndex: 0
property bool ctrlPressed: false
property bool shiftPressed: false
property bool overflowing: (visibleArea.heightRatio < 1.0 || visibleArea.widthRatio < 1.0)
property bool scrollLeft: false
property bool scrollRight: false
property bool scrollUp: false
property bool scrollDown: false
property variant cachedRectangleSelection: null
currentIndex: -1
keyNavigationWraps: false
boundsBehavior: Flickable.StopAtBounds
function calcExtraSpacing(cellSize, containerSize) {
var availableColumns = Math.floor(containerSize / cellSize);
var extraSpacing = 0;
if (availableColumns > 0) {
var allColumnSize = availableColumns * cellSize;
var extraSpace = Math.max(containerSize - allColumnSize, 0);
extraSpacing = extraSpace / availableColumns;
}
return extraSpacing;
}
cellWidth: {
if (root.useListViewMode) {
return gridView.width;
} else {
var iconWidth = iconSize + (2 * units.largeSpacing) + (2 * units.smallSpacing);
if (root.isContainment && isRootView && scrollArea.viewportWidth > 0) {
- var minIconWidth = Math.max(iconWidth, units.iconSizes.small * 6);
+ var minIconWidth = Math.max(iconWidth, units.iconSizes.small * ((plasmoid.configuration.labelWidth * 2) + 4));
var extraWidth = calcExtraSpacing(minIconWidth, scrollArea.viewportWidth);
return minIconWidth + extraWidth;
} else {
return iconWidth;
}
}
}
cellHeight: {
if (root.useListViewMode) {
return Math.ceil((Math.max(theme.mSize(theme.defaultFont).height, iconSize)
+ Math.max(highlightItemSvg.margins.top + highlightItemSvg.margins.bottom,
listItemSvg.margins.top + listItemSvg.margins.bottom)) / 2) * 2;
} else {
var iconHeight = iconSize + (theme.mSize(theme.defaultFont).height * plasmoid.configuration.textLines) + (4 * units.smallSpacing);
if (root.isContainment && isRootView && scrollArea.viewportHeight > 0) {
var extraHeight = calcExtraSpacing(iconHeight, scrollArea.viewportHeight);
return iconHeight + extraHeight;
} else {
return iconHeight;
}
}
}
delegate: FolderItemDelegate {
width: gridView.cellWidth
height: gridView.cellHeight
}
onContentXChanged: {
if (hoveredItem) {
hoverActivateTimer.stop();
}
cancelRename();
dir.setDragHotSpotScrollOffset(contentX, contentY);
if (contentX == 0) {
scrollLeft = false;
}
if (contentX == contentItem.width - width) {
scrollRight = false;
}
// Update rubberband geometry.
if (main.rubberBand) {
var rB = main.rubberBand;
if (scrollLeft) {
rB.x = Math.min(gridView.contentX, gridView.originX);
rB.width = listener.cPress.x;
}
if (scrollRight) {
var lastCol = gridView.contentX + gridView.width;
rB.width = lastCol - rB.x;
}
gridView.rectangleSelect(rB.x, rB.y, rB.width, rB.height);
}
}
onContentYChanged: {
if (hoveredItem) {
hoverActivateTimer.stop();
}
cancelRename();
dir.setDragHotSpotScrollOffset(contentX, contentY);
if (contentY == 0) {
scrollUp = false;
}
if (contentY == contentItem.height - height) {
scrollDown = false;
}
// Update rubberband geometry.
if (main.rubberBand) {
var rB = main.rubberBand;
if (scrollUp) {
rB.y = 0;
rB.height = listener.cPress.y;
}
if (scrollDown) {
var lastRow = gridView.contentY + gridView.height;
rB.height = lastRow - rB.y;
}
gridView.rectangleSelect(rB.x, rB.y, rB.width, rB.height);
}
}
onScrollLeftChanged: {
if (scrollLeft && gridView.visibleArea.widthRatio < 1.0) {
smoothX.enabled = true;
contentX = (gridView.flow == GridView.FlowLeftToRight) ? gridView.contentX : gridView.originX;
} else {
contentX = contentX;
smoothX.enabled = false;
}
}
onScrollRightChanged: {
if (scrollRight && gridView.visibleArea.widthRatio < 1.0) {
smoothX.enabled = true;
contentX = ((gridView.flow == GridView.FlowLeftToRight) ? gridView.contentX : gridView.originX)
+ (contentItem.width - width);
} else {
contentX = contentX;
smoothX.enabled = false;
}
}
onScrollUpChanged: {
if (scrollUp && gridView.visibleArea.heightRatio < 1.0) {
smoothY.enabled = true;
contentY = 0;
} else {
contentY = contentY;
smoothY.enabled = false;
}
}
onScrollDownChanged: {
if (scrollDown && gridView.visibleArea.heightRatio < 1.0) {
smoothY.enabled = true;
contentY = contentItem.height - height;
} else {
contentY = contentY;
smoothY.enabled = false;
}
}
onFlowChanged: {
// FIXME TODO: Preserve positions.
if (positioner.enabled) {
positioner.reset();
}
}
onLayoutDirectionChanged: {
// FIXME TODO: Preserve positions.
if (positioner.enabled) {
positioner.reset();
}
}
onCurrentIndexChanged: {
positionViewAtIndex(currentIndex, GridView.Contain);
}
onCachedRectangleSelectionChanged: {
if (cachedRectangleSelection == null) {
return;
}
if (cachedRectangleSelection.length) {
// Set current index to start of selection.
// cachedRectangleSelection is pre-sorted.
currentIndex = cachedRectangleSelection[0];
}
dir.updateSelection(cachedRectangleSelection.map(positioner.map),
gridView.ctrlPressed);
}
function makeIconSize() {
if (root.useListViewMode) {
return units.iconSizes.small;
}
return FolderTools.iconSizeFromTheme(plasmoid.configuration.iconSize);
}
function updateSelection(modifier) {
if (modifier & Qt.ShiftModifier) {
positioner.setRangeSelected(anchorIndex, currentIndex);
} else {
dir.clearSelection();
dir.setSelected(positioner.map(currentIndex));
}
}
function cancelAutoscroll() {
scrollLeft = false;
scrollRight = false;
scrollUp = false;
scrollDown = false;
}
function rectangleSelect(x, y, width, height) {
var rows = (gridView.flow == GridView.FlowLeftToRight);
var axis = rows ? gridView.width : gridView.height;
var step = rows ? cellWidth : cellHeight;
var perStripe = Math.floor(axis / step);
var stripes = Math.ceil(gridView.count / perStripe);
var cWidth = gridView.cellWidth - (2 * units.smallSpacing);
var cHeight = gridView.cellHeight - (2 * units.smallSpacing);
var midWidth = gridView.cellWidth / 2;
var midHeight = gridView.cellHeight / 2;
var indices = [];
for (var s = 0; s < stripes; s++) {
for (var i = 0; i < perStripe; i++) {
var index = (s * perStripe) + i;
if (index >= gridView.count) {
break;
}
if (positioner.isBlank(index)) {
continue;
}
var itemX = ((rows ? i : s) * gridView.cellWidth);
var itemY = ((rows ? s : i) * gridView.cellHeight);
if (gridView.effectiveLayoutDirection == Qt.RightToLeft) {
itemX -= (rows ? gridView.contentX : gridView.originX);
itemX += cWidth;
itemX = (rows ? gridView.width : gridView.contentItem.width) - itemX;
}
// Check if the rubberband intersects this cell first to avoid doing more
// expensive work.
if (main.rubberBand.intersects(Qt.rect(itemX + units.smallSpacing, itemY + units.smallSpacing,
cWidth, cHeight))) {
var item = gridView.contentItem.childAt(itemX + midWidth, itemY + midHeight);
// If this is a visible item, check for intersection with the actual
// icon or label rects for better feel.
if (item && item.iconArea) {
var iconRect = Qt.rect(itemX + item.iconArea.x, itemY + item.iconArea.y,
item.iconArea.width, item.iconArea.height);
if (main.rubberBand.intersects(iconRect)) {
indices.push(index);
continue;
}
var labelRect = Qt.rect(itemX + item.labelArea.x, itemY + item.labelArea.y,
item.labelArea.width, item.labelArea.height);
if (main.rubberBand.intersects(labelRect)) {
indices.push(index);
continue;
}
} else {
// Otherwise be content with the cell intersection.
indices.push(index);
}
}
}
}
gridView.cachedRectangleSelection = indices;
}
function runOrCdSelected() {
if (currentIndex != -1 && dir.hasSelection()) {
if (root.useListViewMode && currentItem.isDir) {
doCd(positioner.map(currentIndex));
} else {
dir.runSelected();
}
}
}
Behavior on contentX { id: smoothX; enabled: false; SmoothedAnimation { velocity: 700 } }
Behavior on contentY { id: smoothY; enabled: false; SmoothedAnimation { velocity: 700 } }
Keys.onReturnPressed: {
if (event.modifiers === Qt.AltModifier) {
dir.openPropertiesDialog();
} else {
runOrCdSelected();
}
}
Keys.onEnterPressed: Keys.returnPressed(event)
Keys.onMenuPressed: {
if (currentIndex != -1 && dir.hasSelection() && currentItem) {
dir.setSelected(positioner.map(currentIndex));
dir.openContextMenu(currentItem.frame, event.modifiers);
} else {
// Otherwise let the containment handle it.
event.accepted = false;
}
}
Keys.onEscapePressed: {
if (!editor || !editor.targetItem) {
dir.clearSelection();
event.accepted = false;
}
}
Folder.ShortCut {
Component.onCompleted: {
installAsEventFilterFor(gridView);
}
onDeleteFile: {
dir.deleteSelected();
}
onRenameFile: {
rename();
}
}
Keys.onPressed: {
event.accepted = true;
if (event.matches(StandardKey.Delete)) {
if (dir.hasSelection()) {
dir.action("trash").trigger();
}
} else if (event.key === Qt.Key_Control) {
ctrlPressed = true;
} else if (event.key === Qt.Key_Shift) {
shiftPressed = true;
if (currentIndex != -1) {
anchorIndex = currentIndex;
}
} else if (event.key === Qt.Key_Home) {
currentIndex = 0;
updateSelection(event.modifiers);
} else if (event.key === Qt.Key_End) {
currentIndex = count - 1;
updateSelection(event.modifiers);
} else if (event.matches(StandardKey.Copy)) {
dir.copy();
} else if (event.matches(StandardKey.Paste)) {
dir.paste();
} else if (event.matches(StandardKey.Cut)) {
dir.cut();
} else if (event.matches(StandardKey.Undo)) {
dir.undo();
} else if (event.matches(StandardKey.Refresh)) {
dir.refresh();
} else if (event.matches(StandardKey.SelectAll)) {
positioner.setRangeSelected(0, count - 1);
} else {
event.accepted = false;
}
}
Keys.onReleased: {
if (event.key === Qt.Key_Control) {
ctrlPressed = false;
} else if (event.key === Qt.Key_Shift) {
shiftPressed = false;
anchorIndex = 0;
}
}
Keys.onLeftPressed: {
if (root.isPopup && root.useListViewMode) {
if (dir.resolvedUrl !== dir.resolve(plasmoid.configuration.url)) {
doBack();
}
} else if (positioner.enabled) {
var newIndex = positioner.nearestItem(currentIndex,
FolderTools.effectiveNavDirection(gridView.flow, gridView.effectiveLayoutDirection, Qt.LeftArrow));
if (newIndex !== -1) {
currentIndex = newIndex;
updateSelection(event.modifiers);
}
} else {
var oldIndex = currentIndex;
moveCurrentIndexLeft();
if (oldIndex === currentIndex) {
return;
}
updateSelection(event.modifiers);
}
}
Keys.onRightPressed: {
if (root.isPopup && root.useListViewMode) {
if (currentIndex != -1 && dir.hasSelection() && currentItem.isDir) {
doCd(positioner.map(currentIndex));
}
} else if (positioner.enabled) {
var newIndex = positioner.nearestItem(currentIndex,
FolderTools.effectiveNavDirection(gridView.flow, gridView.effectiveLayoutDirection, Qt.RightArrow));
if (newIndex !== -1) {
currentIndex = newIndex;
updateSelection(event.modifiers);
}
} else {
var oldIndex = currentIndex;
moveCurrentIndexRight();
if (oldIndex === currentIndex) {
return;
}
updateSelection(event.modifiers);
}
}
Keys.onUpPressed: {
if (positioner.enabled) {
var newIndex = positioner.nearestItem(currentIndex,
FolderTools.effectiveNavDirection(gridView.flow, gridView.effectiveLayoutDirection, Qt.UpArrow));
if (newIndex !== -1) {
currentIndex = newIndex;
updateSelection(event.modifiers);
}
} else {
var oldIndex = currentIndex;
moveCurrentIndexUp();
if (oldIndex === currentIndex) {
return;
}
updateSelection(event.modifiers);
}
}
Keys.onDownPressed: {
if (positioner.enabled) {
var newIndex = positioner.nearestItem(currentIndex,
FolderTools.effectiveNavDirection(gridView.flow, gridView.effectiveLayoutDirection, Qt.DownArrow));
if (newIndex !== -1) {
currentIndex = newIndex;
updateSelection(event.modifiers);
}
} else {
var oldIndex = currentIndex;
moveCurrentIndexDown();
if (oldIndex === currentIndex) {
return;
}
updateSelection(event.modifiers);
}
}
Keys.onBackPressed: {
if (root.isPopup && dir.resolvedUrl !== dir.resolve(plasmoid.configuration.url)) {
doBack();
}
}
Connections {
target: units
onIconSizesChanged: {
gridView.iconSize = gridView.makeIconSize();
}
}
Connections {
target: plasmoid.configuration
onIconSizeChanged: {
gridView.iconSize = gridView.makeIconSize();
}
}
Connections {
target: plasmoid.configuration
onUrlChanged: {
history = [];
updateHistory();
}
}
}
}
Folder.WheelInterceptor {
anchors.fill: parent
enabled: root.isContainment && !gridView.overflowing
destination: plasmoid
}
Folder.FolderModel {
id: dir
usedByContainment: root.isContainment && main.isRootView
sortDesc: plasmoid.configuration.sortDesc
sortDirsFirst: plasmoid.configuration.sortDirsFirst
parseDesktopFiles: (plasmoid.configuration.url === "desktop:/")
previews: plasmoid.configuration.previews
previewPlugins: plasmoid.configuration.previewPlugins
appletInterface: plasmoid
onListingCompleted: {
if (!gridView.model && plasmoid.expanded) {
gridView.model = positioner;
gridView.currentIndex = isPopup ? 0 : -1;
} else if (goingBack) {
goingBack = false;
gridView.currentIndex = Math.min(lastPosition.index, gridView.count - 1);
setSelected(positioner.map(gridView.currentIndex));
gridView.contentY = lastPosition.yPosition * gridView.contentHeight;
}
}
onMove: {
var rows = (gridView.flow == GridView.FlowLeftToRight);
var axis = rows ? gridView.width : gridView.height;
var step = rows ? cellWidth : cellHeight;
var perStripe = Math.floor(axis / step);
var dropPos = mapToItem(gridView.contentItem, x, y);
var leftEdge = Math.min(gridView.contentX, gridView.originX);
var moves = []
var itemX = -1;
var itemY = -1;
var col = -1;
var row = -1;
var from = -1;
var to = -1;
for (var i = 0; i < urls.length; i++) {
from = positioner.indexForUrl(urls[i]);
to = -1;
if (from === -1) {
continue;
}
var offset = dir.dragCursorOffset(positioner.map(from));
if (offset.x === -1) {
continue;
}
itemX = dropPos.x + offset.x + (listener.dragX % cellWidth) + (cellWidth / 2);
itemY = dropPos.y + offset.y + (listener.dragY % cellHeight) + gridView.verticalDropHitscanOffset;
if (gridView.effectiveLayoutDirection == Qt.RightToLeft) {
itemX -= (rows ? gridView.contentX : gridView.originX);
itemX = (rows ? gridView.width : gridView.contentItem.width) - itemX;
}
col = Math.floor(itemX / gridView.cellWidth);
row = Math.floor(itemY / gridView.cellHeight);
if ((rows ? col : row) < perStripe) {
to = ((rows ? row : col) * perStripe) + (rows ? col : row);
if (to < 0) {
return;
}
}
if (from !== to) {
moves.push(from);
moves.push(to);
}
}
if (moves.length) {
positioner.move(moves);
gridView.forceLayout();
}
dir.clearSelection();
}
}
Folder.Positioner {
id: positioner
enabled: isContainment && sortMode === -1
folderModel: dir
perStripe: Math.floor(((gridView.flow == GridView.FlowLeftToRight)
? gridView.width : gridView.height) / ((gridView.flow == GridView.FlowLeftToRight)
? gridView.cellWidth : gridView.cellHeight));
}
Folder.ItemViewAdapter {
id: viewAdapter
adapterView: gridView
adapterModel: positioner
adapterIconSize: gridView.iconSize * 2
adapterVisibleArea: Qt.rect(gridView.contentX, gridView.contentY, gridView.contentWidth, gridView.contentHeight)
Component.onCompleted: {
gridView.movementStarted.connect(viewAdapter.viewScrolled);
dir.viewAdapter = viewAdapter;
}
}
Component {
id: editorComponent
PlasmaComponents.TextArea {
id: editor
visible: false
wrapMode: root.useListViewMode ? TextEdit.NoWrap : TextEdit.Wrap
textMargin: 0
horizontalAlignment: root.useListViewMode ? TextEdit.AlignHLeft : TextEdit.AlignHCenter
property Item targetItem: null
onTargetItemChanged: {
if (targetItem != null) {
var xy = getXY();
x = xy[0];
y = xy[1];
width = getWidth();
height = getInitHeight();
text = targetItem.label.text;
adjustSize();
editor.select(0, dir.fileExtensionBoundary(positioner.map(targetItem.index)));
if(isPopup) {
flickableItem.contentX = Math.max(flickableItem.contentWidth - contentItem.width, 0);
} else {
flickableItem.contentY = Math.max(flickableItem.contentHeight - contentItem.height, 0);
}
visible = true;
} else {
x: 0
y: 0
visible = false;
}
}
onVisibleChanged: {
if (visible) {
focus = true;
} else {
scrollArea.focus = true;
}
}
Keys.onPressed: {
switch(event.key) {
case Qt.Key_Return:
case Qt.Key_Enter:
commit();
break;
case Qt.Key_Escape:
if (targetItem) {
targetItem = null;
event.accepted = true;
}
break;
case Qt.Key_Home:
if (event.modifiers & Qt.ShiftModifier) {
editor.select(0, cursorPosition);
} else {
editor.select(0, 0);
}
event.accepted = true;
break;
case Qt.Key_End:
if (event.modifiers & Qt.ShiftModifier) {
editor.select(cursorPosition, text.length);
} else {
editor.select(text.length, text.length);
}
event.accepted = true;
break;
default:
adjustSize();
break;
}
}
Keys.onReleased: {
adjustSize();
}
function getXY() {
var pos = main.mapFromItem(targetItem, targetItem.labelArea.x, targetItem.labelArea.y);
var _x, _y;
if (root.useListViewMode) {
_x = targetItem.labelArea.x - __style.padding.left;
_y = pos.y - __style.padding.top;
} else {
_x = targetItem.x + Math.abs(Math.min(gridView.contentX, gridView.originX));
_x += __style.padding.left;
_x += scrollArea.viewport.x;
if (verticalScrollBarPolicy == Qt.ScrollBarAlwaysOn
&& gridView.effectiveLayoutDirection == Qt.RightToLeft) {
_x -= __verticalScrollBar.parent.verticalScrollbarOffset;
}
_y = pos.y + units.smallSpacing - __style.padding.top;
}
return([ _x, _y ]);
}
function getWidth(addWidthVerticalScroller) {
return(targetItem.label.parent.width - units.smallSpacing +
(root.useListViewMode ? -(__style.padding.left + __style.padding.right + units.smallSpacing) : 0) +
(addWidthVerticalScroller ? __verticalScrollBar.parent.verticalScrollbarOffset : 0));
}
function getHeight(addWidthHoriozontalScroller, init) {
var _height;
if(isPopup || init) {
_height = targetItem.labelArea.height + __style.padding.top + __style.padding.bottom;
} else {
var realHeight = contentHeight + __style.padding.top + __style.padding.bottom;
var maxHeight = theme.mSize(theme.defaultFont).height * (plasmoid.configuration.textLines + 1) + __style.padding.top + __style.padding.bottom;
_height = Math.min(realHeight, maxHeight);
}
return(_height + (addWidthHoriozontalScroller ? __horizontalScrollBar.parent.horizontalScrollbarOffset : 0));
}
function getInitHeight() {
return(getHeight(false, true));
}
function adjustSize() {
if(isPopup) {
if(contentWidth + __style.padding.left + __style.padding.right > width) {
visible = true;
horizontalScrollBarPolicy = Qt.ScrollBarAlwaysOn;
height = getHeight(true);
} else {
horizontalScrollBarPolicy = Qt.ScrollBarAlwaysOff;
height = getHeight();
}
} else {
height = getHeight();
if(contentHeight + __style.padding.top + __style.padding.bottom > height) {
visible = true;
verticalScrollBarPolicy = Qt.ScrollBarAlwaysOn;
width = getWidth(true);
} else {
verticalScrollBarPolicy = Qt.ScrollBarAlwaysOff;
width = getWidth();
}
}
var xy = getXY();
x = xy[0];
y = xy[1];
}
function commit() {
if (targetItem) {
dir.rename(positioner.map(targetItem.index), text);
targetItem = null;
}
}
}
}
Component.onCompleted: {
dir.requestRename.connect(rename);
}
}
Component.onCompleted: {
if (backButton == null && root.useListViewMode) {
backButton = makeBackButton();
}
}
}