diff --git a/applets/kicker/plugin/actionlist.h b/applets/kicker/plugin/actionlist.h
index 780505b2a..db706d920 100644
--- a/applets/kicker/plugin/actionlist.h
+++ b/applets/kicker/plugin/actionlist.h
@@ -1,70 +1,75 @@
/***************************************************************************
* Copyright (C) 2013 by Aurélien Gâteau *
* 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 . *
***************************************************************************/
+#ifndef ACTIONLIST_H
+#define ACTIONLIST_H
+
#include
#include
class KFileItem;
namespace Kicker
{
enum {
DescriptionRole = Qt::UserRole + 1,
GroupRole,
FavoriteIdRole,
IsSeparatorRole,
IsDropPlaceholderRole,
IsParentRole,
HasChildrenRole,
HasActionListRole,
ActionListRole,
UrlRole
};
QVariantMap createActionItem(const QString &label, const QString &actionId, const QVariant &argument = QVariant());
QVariantMap createTitleActionItem(const QString &label);
QVariantMap createSeparatorActionItem();
QVariantList createActionListForFileItem(const KFileItem &fileItem);
bool handleFileItemAction(const KFileItem &fileItem, const QString &actionId, const QVariant &argument, bool *close);
QVariantList createAddLauncherActionList(QObject *appletInterface, const KService::Ptr &service);
bool handleAddLauncherAction(const QString &actionId, QObject *appletInterface, const KService::Ptr &service);
QVariantList jumpListActions(KService::Ptr service);
QVariantList recentDocumentActions(KService::Ptr service);
bool handleRecentDocumentAction(KService::Ptr service, const QString &actionId, const QVariant &argument);
bool canEditApplication(const QString &entryPath);
void editApplication(const QString &entryPath, const QString &menuId);
QVariantList editApplicationAction(const KService::Ptr &service);
bool handleEditApplicationAction(const QString &actionId, const KService::Ptr &service);
QVariantList appstreamActions(const KService::Ptr &service);
bool handleAppstreamActions(const QString &actionId, const QVariant &argument);
QString resolvedServiceEntryPath(const KService::Ptr &service);
}
+
+#endif
diff --git a/applets/kickoff/package/contents/ui/FullRepresentation.qml b/applets/kickoff/package/contents/ui/FullRepresentation.qml
index 7468153f2..1c22414ee 100644
--- a/applets/kickoff/package/contents/ui/FullRepresentation.qml
+++ b/applets/kickoff/package/contents/ui/FullRepresentation.qml
@@ -1,725 +1,725 @@
/*
Copyright (C) 2011 Martin Gräßlin
Copyright (C) 2012 Gregor Taetzner
Copyright (C) 2012 Marco Martin
Copyright (C) 2013 2014 David Edmundson
Copyright 2014 Sebastian Kügler
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.3
import org.kde.plasma.plasmoid 2.0
import QtQuick.Layouts 1.1
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.plasma.private.kicker 0.1 as Kicker
Item {
id: root
Layout.minimumWidth: units.gridUnit * 26
Layout.minimumHeight: units.gridUnit * 34
property string previousState
property bool switchTabsOnHover: plasmoid.configuration.switchTabsOnHover
property Item currentView: mainTabGroup.currentTab.decrementCurrentIndex ? mainTabGroup.currentTab : mainTabGroup.currentTab.item
property KickoffButton firstButton: null
property var configMenuItems
property QtObject globalFavorites: rootModelFavorites
state: "Normal"
onFocusChanged: {
header.input.forceActiveFocus();
}
function switchToInitial() {
if (firstButton != null) {
mainTabGroup.currentTab = firstButton.tab;
tabBar.currentTab = firstButton;
header.query = ""
header.state = "hint";
root.state = "Normal";
}
}
Kicker.DragHelper {
id: dragHelper
dragIconSize: units.iconSizes.medium
onDropped: kickoff.dragSource = null
}
Kicker.AppsModel {
id: rootModel
autoPopulate: false
appletInterface: plasmoid
appNameFormat: plasmoid.configuration.showAppsByName ? 0 : 1
flat: false
sorted: plasmoid.configuration.alphaSort
showSeparators: false
showTopLevelItems: true
favoritesModel: Kicker.KAStatsFavoritesModel {
id: rootModelFavorites
favorites: plasmoid.configuration.favorites
onFavoritesChanged: {
plasmoid.configuration.favorites = favorites;
}
}
Component.onCompleted: {
favoritesModel.initForClient("org.kde.plasma.kickoff.favorites.instance-" + plasmoid.id)
if (!plasmoid.configuration.favoritesPortedToKAstats) {
favoritesModel.portOldFavorites(plasmoid.configuration.favorites);
plasmoid.configuration.favoritesPortedToKAstats = true;
}
rootModel.refresh();
}
}
PlasmaCore.DataSource {
id: pmSource
engine: "powermanagement"
connectedSources: ["PowerDevil"]
}
PlasmaCore.Svg {
id: arrowsSvg
imagePath: "widgets/arrows"
size: "16x16"
}
Header {
id: header
}
Item {
id: mainArea
anchors.topMargin: mainTabGroup.state == "top" ? units.smallSpacing : 0
PlasmaComponents.TabGroup {
id: mainTabGroup
currentTab: favoritesPage
anchors {
fill: parent
}
//pages
FavoritesView {
id: favoritesPage
}
PlasmaExtras.ConditionalLoader {
id: applicationsPage
when: mainTabGroup.currentTab == applicationsPage
source: Qt.resolvedUrl("ApplicationsView.qml")
}
PlasmaExtras.ConditionalLoader {
id: systemPage
when: mainTabGroup.currentTab == systemPage
source: Qt.resolvedUrl("ComputerView.qml")
}
PlasmaExtras.ConditionalLoader {
id: recentlyUsedPage
when: mainTabGroup.currentTab == recentlyUsedPage
source: Qt.resolvedUrl("RecentlyUsedView.qml")
}
PlasmaExtras.ConditionalLoader {
id: oftenUsedPage
when: mainTabGroup.currentTab == oftenUsedPage
source: Qt.resolvedUrl("OftenUsedView.qml")
}
PlasmaExtras.ConditionalLoader {
id: leavePage
when: mainTabGroup.currentTab == leavePage
source: Qt.resolvedUrl("LeaveView.qml")
}
PlasmaExtras.ConditionalLoader {
id: searchPage
when: root.state == "Search"
//when: mainTabGroup.currentTab == searchPage || root.state == "Search"
source: Qt.resolvedUrl("SearchView.qml")
}
state: {
switch (plasmoid.location) {
case PlasmaCore.Types.LeftEdge:
return LayoutMirroring.enabled ? "right" : "left";
case PlasmaCore.Types.TopEdge:
return "top";
case PlasmaCore.Types.RightEdge:
return LayoutMirroring.enabled ? "left" : "right";
case PlasmaCore.Types.BottomEdge:
default:
return "bottom";
}
}
states: [
State {
name: "left"
AnchorChanges {
target: header
anchors {
left: root.left
top: undefined
right: root.right
bottom: root.bottom
}
}
PropertyChanges {
target: header
width: header.implicitWidth
}
AnchorChanges {
target: mainArea
anchors {
left: tabBar.right
top: root.top
right: root.right
bottom: header.top
}
}
AnchorChanges {
target: tabBar
anchors {
left: root.left
top: root.top
right: undefined
bottom: header.top
}
}
},
State {
name: "top"
AnchorChanges {
target: header
anchors {
left: root.left
top: undefined
right: root.right
bottom: root.bottom
}
}
PropertyChanges {
target: header
height: header.implicitHeight
}
AnchorChanges {
target: mainArea
anchors {
left: root.left
top: tabBar.bottom
right: root.right
bottom: header.top
}
}
AnchorChanges {
target: tabBar
anchors {
left: root.left
top: root.top
right: root.right
bottom: undefined
}
}
},
State {
name: "right"
AnchorChanges {
target: header
anchors {
left: root.left
top: undefined
right: root.right
bottom: root.bottom
}
}
PropertyChanges {
target: header
width: header.implicitWidth
}
AnchorChanges {
target: mainArea
anchors {
left: root.left
top: root.top
right: tabBar.left
bottom: header.top
}
}
AnchorChanges {
target: tabBar
anchors {
left: undefined
top: root.top
right: root.right
bottom: header.top
}
}
},
State {
name: "bottom"
AnchorChanges {
target: header
anchors {
left: root.left
top: root.top
right: root.right
bottom: undefined
}
}
PropertyChanges {
target: header
height: header.implicitHeight
}
AnchorChanges {
target: mainArea
anchors {
left: root.left
top: header.bottom
right: root.right
bottom: tabBar.top
}
}
AnchorChanges {
target: tabBar
anchors {
left: root.left
top: undefined
right: root.right
bottom: root.bottom
}
}
}
]
} // mainTabGroup
}
PlasmaComponents.TabBar {
id: tabBar
property int count: 5 // updated in createButtons()
width: {
if (opacity == 0) {
return 0;
}
switch (plasmoid.location) {
case PlasmaCore.Types.LeftEdge:
case PlasmaCore.Types.RightEdge:
return units.gridUnit * 5;
default:
return 0;
}
}
height: {
if (opacity == 0) {
return 0;
}
switch (plasmoid.location) {
case PlasmaCore.Types.LeftEdge:
case PlasmaCore.Types.RightEdge:
return 0;
default:
return units.gridUnit * 5;
}
}
Behavior on width {
NumberAnimation { duration: units.longDuration; easing.type: Easing.InQuad; }
enabled: plasmoid.expanded
}
Behavior on height {
NumberAnimation { duration: units.longDuration; easing.type: Easing.InQuad; }
enabled: plasmoid.expanded
}
tabPosition: {
switch (plasmoid.location) {
case PlasmaCore.Types.TopEdge:
return Qt.TopEdge;
case PlasmaCore.Types.LeftEdge:
return Qt.LeftEdge;
case PlasmaCore.Types.RightEdge:
return Qt.RightEdge;
default:
return Qt.BottomEdge;
}
}
onCurrentTabChanged: header.input.forceActiveFocus();
Connections {
target: plasmoid
onExpandedChanged: {
if(menuItemsChanged()) {
createButtons();
}
if (!expanded) {
switchToInitial();
}
}
}
} // tabBar
MouseArea {
anchors.fill: tabBar
property var oldPos: null
hoverEnabled: root.switchTabsOnHover
onExited: {
// Reset so we switch immediately when MouseArea is entered
// freshly, e.g. from the panel.
oldPos = null;
clickTimer.stop();
}
onPositionChanged: {
var button = tabBar.layout.childAt(mouse.x, mouse.y);
if (!button || button.objectName != "KickoffButton") {
clickTimer.stop();
return;
}
// Switch immediately when MouseArea was freshly entered, e.g.
// from the panel.
if (oldPos === null) {
oldPos = Qt.point(mouse.x, mouse.y);
clickTimer.stop();
button.clicked();
return;
}
var dx = (mouse.x - oldPos.x);
var dy = (mouse.y - oldPos.y);
// Check Manhattan length against drag distance to get a decent
// pointer motion vector.
if ((Math.abs(dx) + Math.abs(dy)) > Qt.styleHints.startDragDistance) {
if (tabBar.currentTab != button) {
var tabBarPos = mapToItem(tabBar, oldPos.x, oldPos.y);
oldPos = Qt.point(mouse.x, mouse.y);
var angleMouseMove = Math.atan2(dy, dx) * 180 / Math.PI;
var angleToCornerA = 0;
var angleToCornerB = 0;
switch (plasmoid.location) {
case PlasmaCore.Types.TopEdge: {
angleToCornerA = Math.atan2(tabBar.height - tabBarPos.y, 0 - tabBarPos.x);
angleToCornerB = Math.atan2(tabBar.height - tabBarPos.y, tabBar.width - tabBarPos.x);
break;
}
case PlasmaCore.Types.LeftEdge: {
angleToCornerA = Math.atan2(0 - tabBarPos.y, tabBar.width - tabBarPos.x);
angleToCornerB = Math.atan2(tabBar.height - tabBarPos.y, tabBar.width - tabBarPos.x);
break;
}
case PlasmaCore.Types.RightEdge: {
angleToCornerA = Math.atan2(0 - tabBarPos.y, 0 - tabBarPos.x);
angleToCornerB = Math.atan2(tabBar.height - tabBarPos.y, 0 - tabBarPos.x);
break;
}
// PlasmaCore.Types.BottomEdge
default: {
angleToCornerA = Math.atan2(0 - tabBarPos.y, 0 - tabBarPos.x);
angleToCornerB = Math.atan2(0 - tabBarPos.y, tabBar.width - tabBarPos.x);
}
}
// Degrees are nicer to debug than radians.
angleToCornerA = angleToCornerA * 180 / Math.PI;
angleToCornerB = angleToCornerB * 180 / Math.PI;
var lower = Math.min(angleToCornerA, angleToCornerB);
var upper = Math.max(angleToCornerA, angleToCornerB);
// If the motion vector is outside the angle range from oldPos to the
// relevant tab bar corners, switch immediately. Otherwise start the
// timer, which gets aborted should the pointer exit the the tab bar
// early.
var inRange = (lower < angleMouseMove == angleMouseMove < upper);
// Mirror-flip.
if (plasmoid.location == PlasmaCore.Types.RightEdge ? inRange : !inRange) {
clickTimer.stop();
button.clicked();
return;
} else {
clickTimer.pendingButton = button;
clickTimer.start();
}
} else {
oldPos = Qt.point(mouse.x, mouse.y);
}
}
}
onClicked: {
clickTimer.stop();
var button = tabBar.layout.childAt(mouse.x, mouse.y);
if (!button || button.objectName != "KickoffButton") {
return;
}
button.clicked();
}
Timer {
id: clickTimer
property Item pendingButton: null
interval: 250
onTriggered: {
if (pendingButton) {
pendingButton.clicked();
}
}
}
}
Keys.forwardTo: [tabBar.layout]
Keys.onPressed: {
if (mainTabGroup.currentTab == applicationsPage) {
if (event.key != Qt.Key_Tab) {
root.state = "Applications";
}
}
switch(event.key) {
case Qt.Key_Up: {
currentView.decrementCurrentIndex();
event.accepted = true;
break;
}
case Qt.Key_Down: {
currentView.incrementCurrentIndex();
event.accepted = true;
break;
}
case Qt.Key_Left: {
if (header.input.focus && header.state == "query") {
break;
}
if (!currentView.deactivateCurrentIndex()) {
if (root.state == "Applications") {
mainTabGroup.currentTab = firstButton.tab;
tabBar.currentTab = firstButton;
}
root.state = "Normal"
}
event.accepted = true;
break;
}
case Qt.Key_Right: {
if (header.input.focus && header.state == "query") {
break;
}
currentView.activateCurrentIndex();
event.accepted = true;
break;
}
case Qt.Key_Tab: {
root.state == "Applications" ? root.state = "Normal" : root.state = "Applications";
event.accepted = true;
break;
}
case Qt.Key_Enter:
case Qt.Key_Return: {
currentView.activateCurrentIndex(1);
event.accepted = true;
break;
}
case Qt.Key_Escape: {
if (header.state != "query") {
plasmoid.expanded = false;
} else {
header.query = "";
}
event.accepted = true;
break;
}
case Qt.Key_Menu: {
currentView.openContextMenu();
event.accepted = true;
break;
}
default:
if (!header.input.focus) {
header.input.forceActiveFocus();
}
}
}
states: [
State {
name: "Normal"
PropertyChanges {
target: root
Keys.forwardTo: [tabBar.layout]
}
PropertyChanges {
target: tabBar
//Set the opacity and NOT the visibility, as visibility is recursive
//and this binding would be executed also on popup show/hide
- //as recomended by the docs: http://doc.qt.io/qt-5/qml-qtquick-item.html#visible-prop
+ //as recommended by the docs: http://doc.qt.io/qt-5/qml-qtquick-item.html#visible-prop
//plus, it triggers https://bugreports.qt.io/browse/QTBUG-66907
//in which a mousearea may think it's under the mouse while it isn't
opacity: tabBar.count > 1 ? 1 : 0
}
},
State {
name: "Applications"
PropertyChanges {
target: root
Keys.forwardTo: [root]
}
PropertyChanges {
target: tabBar
opacity: tabBar.count > 1 ? 1 : 0
}
},
State {
name: "Search"
PropertyChanges {
target: tabBar
opacity: 0
}
PropertyChanges {
target: mainTabGroup
currentTab: searchPage
}
PropertyChanges {
target: root
Keys.forwardTo: [root]
}
}
] // states
function getButtonDefinition(name) {
switch(name) {
case "bookmark":
return {id: "bookmarkButton", tab: favoritesPage, iconSource: "bookmarks", text: i18n("Favorites")};
case "application":
return {id: "applicationButton", tab: applicationsPage, iconSource: "applications-other", text: i18n("Applications")};
case "computer":
return {id: "computerButton", tab: systemPage, iconSource: pmSource.data["PowerDevil"] && pmSource.data["PowerDevil"]["Is Lid Present"] ? "computer-laptop" : "computer", text: i18n("Computer")};
case "used":
return {id: "usedButton", tab: recentlyUsedPage, iconSource: "view-history", text: i18n("History")};
case "oftenUsed":
return {id: "usedButton", tab: oftenUsedPage, iconSource: "office-chart-pie", text: i18n("Often Used")};
case "leave":
return {id: "leaveButton", tab: leavePage, iconSource: "system-log-out", text: i18n("Leave")};
}
}
Component {
id: kickoffButton
KickoffButton {}
}
Component.onCompleted: {
createButtons();
}
function getEnabled(configuration) {
var res = [];
for(var i = 0; i < configuration.length; i++) {
var confItemName = configuration[i].substring(0, configuration[i].indexOf(":"));
var confItemEnabled = configuration[i].substring(configuration[i].length-1) == "t";
if(confItemEnabled) {
res.push(confItemName);
}
}
return res;
}
function createButtons() {
configMenuItems = plasmoid.configuration.menuItems;
var menuItems = getEnabled(plasmoid.configuration.menuItems);
tabBar.count = menuItems.length
// remove old menu items
for(var i = tabBar.layout.children.length -1; i >= 0; i--) {
if(tabBar.layout.children[i].objectName == "KickoffButton") {
tabBar.layout.children[i].destroy();
}
}
for (var i = 0; i < menuItems.length; i++) {
var props = getButtonDefinition(menuItems[i]);
var button = kickoffButton.createObject(tabBar.layout, props);
if(i == 0) {
firstButton = button;
switchToInitial();
}
}
}
function menuItemsChanged() {
if(configMenuItems.length != plasmoid.configuration.menuItems.length) {
return true;
}
for(var i = 0; i < configMenuItems.length; i++) {
if(configMenuItems[i] != plasmoid.configuration.menuItems[i]) {
return true;
}
}
return false;
}
}
diff --git a/applets/kickoff/package/contents/ui/KickoffHighlight.qml b/applets/kickoff/package/contents/ui/KickoffHighlight.qml
index cd97d2b7f..6331d7ae7 100644
--- a/applets/kickoff/package/contents/ui/KickoffHighlight.qml
+++ b/applets/kickoff/package/contents/ui/KickoffHighlight.qml
@@ -1,30 +1,30 @@
/*
* Copyright 2014 Sebastian Kügler
*
* 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.0
import org.kde.plasma.components 2.0 as PlasmaComponents
Item {
PlasmaComponents.Highlight {
anchors {
fill: parent
leftMargin: units.gridUnit
rightMargin: units.gridUnit
}
}
-}
\ No newline at end of file
+}
diff --git a/applets/kimpanel/backend/ibus/ibus15/gtkaccelparse_p.h b/applets/kimpanel/backend/ibus/ibus15/gtkaccelparse_p.h
index 8d179502c..69f6bc1fb 100644
--- a/applets/kimpanel/backend/ibus/ibus15/gtkaccelparse_p.h
+++ b/applets/kimpanel/backend/ibus/ibus15/gtkaccelparse_p.h
@@ -1,80 +1,80 @@
/* GDK - The GIMP Drawing Kit
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
* Copyright (C) 2005, 2006, 2007, 2009 GNOME Foundation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*/
-#ifndef GDKACCELPARSE_P_H
-#define GDKACCELPARSE_P_H
+#ifndef GTKACCELPARSE_P_H
+#define GTKACCELPARSE_P_H
#include
G_BEGIN_DECLS
typedef enum
{
GDK_SHIFT_MASK = 1 << 0,
GDK_LOCK_MASK = 1 << 1,
GDK_CONTROL_MASK = 1 << 2,
GDK_MOD1_MASK = 1 << 3,
GDK_MOD2_MASK = 1 << 4,
GDK_MOD3_MASK = 1 << 5,
GDK_MOD4_MASK = 1 << 6,
GDK_MOD5_MASK = 1 << 7,
GDK_BUTTON1_MASK = 1 << 8,
GDK_BUTTON2_MASK = 1 << 9,
GDK_BUTTON3_MASK = 1 << 10,
GDK_BUTTON4_MASK = 1 << 11,
GDK_BUTTON5_MASK = 1 << 12,
GDK_MODIFIER_RESERVED_13_MASK = 1 << 13,
GDK_MODIFIER_RESERVED_14_MASK = 1 << 14,
GDK_MODIFIER_RESERVED_15_MASK = 1 << 15,
GDK_MODIFIER_RESERVED_16_MASK = 1 << 16,
GDK_MODIFIER_RESERVED_17_MASK = 1 << 17,
GDK_MODIFIER_RESERVED_18_MASK = 1 << 18,
GDK_MODIFIER_RESERVED_19_MASK = 1 << 19,
GDK_MODIFIER_RESERVED_20_MASK = 1 << 20,
GDK_MODIFIER_RESERVED_21_MASK = 1 << 21,
GDK_MODIFIER_RESERVED_22_MASK = 1 << 22,
GDK_MODIFIER_RESERVED_23_MASK = 1 << 23,
GDK_MODIFIER_RESERVED_24_MASK = 1 << 24,
GDK_MODIFIER_RESERVED_25_MASK = 1 << 25,
/* The next few modifiers are used by XKB, so we skip to the end.
* Bits 15 - 25 are currently unused. Bit 29 is used internally.
*/
GDK_SUPER_MASK = 1 << 26,
GDK_HYPER_MASK = 1 << 27,
GDK_META_MASK = 1 << 28,
GDK_MODIFIER_RESERVED_29_MASK = 1 << 29,
GDK_RELEASE_MASK = 1 << 30,
/* Combination of GDK_SHIFT_MASK..GDK_BUTTON5_MASK + GDK_SUPER_MASK
+ GDK_HYPER_MASK + GDK_META_MASK + GDK_RELEASE_MASK */
GDK_MODIFIER_MASK = 0x5c001fff
} GdkModifierType;
void
_gtk_accelerator_parse (const gchar *accelerator,
guint *accelerator_key,
GdkModifierType *accelerator_mods);
G_END_DECLS
#endif // GDKACCELPARSE_P_H
diff --git a/applets/taskmanager/package/contents/ui/ToolTipInstance.qml b/applets/taskmanager/package/contents/ui/ToolTipInstance.qml
index 1a6ab8304..d776f0195 100644
--- a/applets/taskmanager/package/contents/ui/ToolTipInstance.qml
+++ b/applets/taskmanager/package/contents/ui/ToolTipInstance.qml
@@ -1,487 +1,487 @@
/*
* Copyright 2013 by Sebastian Kügler
* Copyright 2014 by Martin Gräßlin
* Copyright 2016 by Kai Uwe Broulik
* Copyright 2017 by Roman Gilg
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2, 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 Library General Public License for more details
*
* You should have received a copy of the GNU Library General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
*/
import QtQuick 2.6
import QtQuick.Layouts 1.1
import QtGraphicalEffects 1.0
import QtQml.Models 2.2
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 as KQuickControlsAddons
import org.kde.taskmanager 0.1 as TaskManager
Column {
property var submodelIndex
property int flatIndex: isGroup && index != undefined ? index : 0
spacing: units.smallSpacing
readonly property string mprisSourceName: mpris2Source.sourceNameForLauncherUrl(toolTipDelegate.launcherUrl, isGroup ? AppPid : pidParent)
readonly property var playerData: mprisSourceName != "" ? mpris2Source.data[mprisSourceName] : 0
readonly property bool hasPlayer: !!mprisSourceName && !!playerData
readonly property bool playing: hasPlayer && playerData.PlaybackStatus === "Playing"
readonly property bool canControl: hasPlayer && playerData.CanControl
readonly property bool canPlay: hasPlayer && playerData.CanPlay
readonly property bool canPause: hasPlayer && playerData.CanPause
readonly property bool canGoBack: hasPlayer && playerData.CanGoPrevious
readonly property bool canGoNext: hasPlayer && playerData.CanGoNext
readonly property bool canRaise: hasPlayer && playerData.CanRaise
readonly property var currentMetadata: hasPlayer ? playerData.Metadata : ({})
readonly property string track: {
var xesamTitle = currentMetadata["xesam:title"]
if (xesamTitle) {
return xesamTitle;
}
// if no track title is given, print out the file name
var xesamUrl = currentMetadata["xesam:url"] ? currentMetadata["xesam:url"].toString() : ""
if (!xesamUrl) {
return "";
}
var lastSlashPos = xesamUrl.lastIndexOf('/')
if (lastSlashPos < 0) {
return "";
}
var lastUrlPart = xesamUrl.substring(lastSlashPos + 1)
return decodeURIComponent(lastUrlPart);
}
readonly property string artist: currentMetadata["xesam:artist"] || ""
readonly property string albumArt: currentMetadata["mpris:artUrl"] || ""
//
// launcher icon + text labels + close button
RowLayout {
id: header
Layout.minimumWidth: childrenRect.width
Layout.maximumWidth: Layout.minimumWidth
Layout.minimumHeight: childrenRect.height
Layout.maximumHeight: Layout.minimumHeight
anchors.horizontalCenter: parent.horizontalCenter
// launcher icon
PlasmaCore.IconItem {
Layout.preferredWidth: units.iconSizes.medium
Layout.preferredHeight: units.iconSizes.medium
source: !isWin ? icon : ""
animated: false
usesPlasmaTheme: false
visible: !isWin
}
// all textlabels
Column {
PlasmaExtras.Heading {
level: 3
width: isWin ? textWidth : undefined
height: undefined
maximumLineCount: 1
elide: Text.ElideRight
text: appName
opacity: flatIndex == 0
textFormat: Text.PlainText
visible: text !== ""
}
// window title
PlasmaExtras.Heading {
level: 5
width: isWin ? textWidth : undefined
height: undefined
maximumLineCount: 1
elide: Text.ElideRight
text: generateTitle()
textFormat: Text.PlainText
opacity: 0.75
}
// subtext
PlasmaExtras.Heading {
level: 6
width: isWin ? textWidth : undefined
height: undefined
maximumLineCount: 1
elide: Text.ElideRight
text: isWin ? generateSubText() : ""
textFormat: Text.PlainText
opacity: 0.6
visible: text !== ""
}
}
// Count badge.
Badge {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
height: units.iconSizes.smallMedium
visible: flatIndex === 0 && smartLauncherCountVisible
number: smartLauncherCount
}
// close button
MouseArea {
Layout.alignment: Qt.AlignRight | Qt.AlignTop
height: units.iconSizes.smallMedium
width: height
visible: isWin
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onClicked: {
backend.cancelHighlightWindows();
tasksModel.requestClose(submodelIndex);
}
PlasmaCore.IconItem {
anchors.fill: parent
active: parent.containsMouse
source: "window-close"
animated: false
}
}
}
// thumbnail container
Item {
id: thumbnail
width: header.width
// similar to 0.5625 = 1 / (16:9) as most screens are
// round necessary, otherwise shadow mask for players has gap!
height: Math.round(0.5 * width)
anchors.horizontalCenter: parent.horizontalCenter
visible: isWin
Item {
id: thumbnailSourceItem
anchors.fill: parent
readonly property bool isMinimized: isGroup ? IsMinimized == true : isMinimizedParent
// TODO: this causes XCB error message when being visible the first time
property int winId: isWin && windows[flatIndex] != undefined ? windows[flatIndex] : 0
PlasmaCore.WindowThumbnail {
anchors.fill: parent
visible: !albumArtImage.visible && !thumbnailSourceItem.isMinimized
winId: thumbnailSourceItem.winId
ToolTipWindowMouseArea {
anchors.fill: parent
rootTask: parentTask
modelIndex: submodelIndex
winId: thumbnailSourceItem.winId
}
}
Image {
id: albumArtImage
// also Image.Loading to prevent loading thumbnails just because the album art takes a split second to load
readonly property bool available: status === Image.Ready || status === Image.Loading
anchors.fill: parent
sourceSize: Qt.size(parent.width, parent.height)
asynchronous: true
source: albumArt
fillMode: Image.PreserveAspectCrop
visible: available
ToolTipWindowMouseArea {
anchors.fill: parent
rootTask: parentTask
modelIndex: submodelIndex
winId: thumbnailSourceItem.winId
}
}
// when minimized, we don't have a preview, so show the icon
PlasmaCore.IconItem {
anchors.fill: parent
source: thumbnailSourceItem.isMinimized && !albumArtImage.visible ? icon : ""
animated: false
usesPlasmaTheme: false
visible: valid
ToolTipWindowMouseArea {
anchors.fill: parent
rootTask: parentTask
modelIndex: submodelIndex
winId: thumbnailSourceItem.winId
}
}
}
Loader {
anchors.fill: thumbnail
sourceComponent: hasPlayer ? playerControlsComp : undefined
}
Component {
id: playerControlsComp
Item {
anchors.fill: parent
// TODO: When could this really be the case? A not-launcher-task always has a window!?
// if there's no window associated with this task, we might still be able to raise the player
// MouseArea {
// id: raisePlayerArea
// anchors.fill: parent
// visible: !isWin || !windows[0] && canRaise
// onClicked: mpris2Source.raise(mprisSourceName)
// }
Item {
id: playerControlsFrostedGlass
anchors.fill: parent
visible: false // OpacityMask would render it
Rectangle {
width: parent.width
height: parent.height - playerControlsRow.height
opacity: 0
}
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: playerControlsRow.height
color: theme.backgroundColor
opacity: 0.8
}
}
OpacityMask {
id: playerControlsOpacityMask
anchors.fill: parent
source: playerControlsFrostedGlass
maskSource: thumbnailSourceItem
}
// prevent accidental click-through when a control is disabled
MouseArea {
anchors.fill: playerControlsRow
}
RowLayout {
id: playerControlsRow
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
}
width: parent.width
spacing: 0
enabled: canControl
ColumnLayout {
Layout.fillWidth: true
spacing: 0
PlasmaExtras.Heading {
Layout.fillWidth: true
level: 4
wrapMode: Text.NoWrap
elide: Text.ElideRight
text: track || ""
}
PlasmaExtras.Heading {
Layout.fillWidth: true
level: 5
wrapMode: Text.NoWrap
elide: Text.ElideRight
text: artist || ""
}
}
MouseArea {
height: units.iconSizes.smallMedium
width: height
enabled: canGoBack
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onClicked: mpris2Source.goPrevious(mprisSourceName)
PlasmaCore.IconItem {
anchors.fill: parent
enabled: canGoBack
active: parent.containsMouse
source: LayoutMirroring.enabled ? "media-skip-forward" : "media-skip-backward"
animated: false
}
}
MouseArea {
height: units.iconSizes.medium
width: height
enabled: playing ? canPause : canPlay
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onClicked: {
if (!playing) {
mpris2Source.play(mprisSourceName);
} else {
mpris2Source.pause(mprisSourceName);
}
}
PlasmaCore.IconItem {
anchors.fill: parent
enabled: playing ? canPause : canPlay
active: parent.containsMouse
source: playing ? "media-playback-pause" : "media-playback-start"
animated: false
}
}
MouseArea {
height: units.iconSizes.smallMedium
width: height
enabled: canGoNext
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onClicked: mpris2Source.goNext(mprisSourceName)
PlasmaCore.IconItem {
anchors.fill: parent
enabled: canGoNext
active: parent.containsMouse
source: LayoutMirroring.enabled ? "media-skip-backward" : "media-skip-forward"
animated: false
}
}
}
}
}
}
function generateTitle() {
if (!isWin) {
return genericName != undefined ? genericName : "";
}
var text;
if (isGroup) {
if (model.display == undefined) {
return "";
}
text = model.display.toString();
} else {
text = displayParent;
}
// KWin appends increasing integers in between pointy brackets to otherwise equal window titles.
// In this case save <#number> as counter and delete it at the end of text.
var counter = text.match(/<\d+>\W*$/);
text = text.replace(/\s*<\d+>\W*$/, "");
// Remove appName from the end of text.
var appNameRegex = new RegExp(appName + "$", "i");
text = text.replace(appNameRegex, "");
text = text.replace(/\s*(?:-|—)*\s*$/, "");
// Add counter back at the end.
if (counter != null) {
if (text == "") {
text = counter;
} else {
text = text + " " + counter;
}
}
- // In case the window title had only redundant informations (i.e. appName), text is now empty.
+ // In case the window title had only redundant information (i.e. appName), text is now empty.
// Add a hyphen to indicate that and avoid empty space.
if (text == "") {
text = "—";
}
return text.toString();
}
function generateSubText() {
if (activitiesParent == undefined) {
return "";
}
var subTextEntries = [];
var vd = isGroup ? VirtualDesktop : virtualDesktopParent;
if (!plasmoid.configuration.showOnlyCurrentDesktop
&& virtualDesktopInfo.numberOfDesktops > 1
&& (isGroup ? IsOnAllVirtualDesktops : isOnAllVirtualDesktopsParent) !== true
&& vd != -1
&& vd != undefined
&& virtualDesktopInfo.desktopNames[vd - 1] != undefined) {
subTextEntries.push(i18n("On %1", virtualDesktopInfo.desktopNames[vd - 1]));
}
var act = isGroup ? Activities : activitiesParent;
if (act == undefined) {
return subTextEntries.join("\n");
}
if (act.length == 0 && activityInfo.numberOfRunningActivities > 1) {
subTextEntries.push(i18nc("Which virtual desktop a window is currently on",
"Available on all activities"));
} else if (act.length > 0) {
var activityNames = [];
for (var i = 0; i < act.length; i++) {
var activity = act[i];
var activityName = activityInfo.activityName(act[i]);
if (activityName == "") {
continue;
}
if (plasmoid.configuration.showOnlyCurrentActivity) {
if (activity != activityInfo.currentActivity) {
activityNames.push(activityName);
}
} else if (activity != activityInfo.currentActivity) {
activityNames.push(activityName);
}
}
if (plasmoid.configuration.showOnlyCurrentActivity) {
if (activityNames.length > 0) {
subTextEntries.push(i18nc("Activities a window is currently on (apart from the current one)",
"Also available on %1", activityNames.join(", ")));
}
} else if (activityNames.length > 0) {
subTextEntries.push(i18nc("Which activities a window is currently on",
"Available on %1", activityNames.join(", ")));
}
}
return subTextEntries.join("\n");
}
}
diff --git a/applets/taskmanager/plugin/backend.cpp b/applets/taskmanager/plugin/backend.cpp
index 881dc5632..ebfe99f61 100644
--- a/applets/taskmanager/plugin/backend.cpp
+++ b/applets/taskmanager/plugin/backend.cpp
@@ -1,586 +1,586 @@
/***************************************************************************
* Copyright (C) 2012-2016 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 . *
***************************************************************************/
#include "backend.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace KAStats = KActivities::Stats;
using namespace KAStats;
using namespace KAStats::Terms;
Backend::Backend(QObject* parent) : QObject(parent)
, m_panelWinId(0)
, m_highlightWindows(false)
, m_actionGroup(new QActionGroup(this))
{
}
Backend::~Backend()
{
}
QQuickItem *Backend::taskManagerItem() const
{
return m_taskManagerItem;
}
void Backend::setTaskManagerItem(QQuickItem* item)
{
if (item != m_taskManagerItem) {
m_taskManagerItem = item;
emit taskManagerItemChanged();
}
}
QQuickItem *Backend::toolTipItem() const
{
return m_toolTipItem;
}
void Backend::setToolTipItem(QQuickItem *item)
{
if (item != m_toolTipItem) {
m_toolTipItem = item;
connect(item, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(toolTipWindowChanged(QQuickWindow*)));
emit toolTipItemChanged();
}
}
QQuickWindow *Backend::groupDialog() const
{
return m_groupDialog;
}
void Backend::setGroupDialog(QQuickWindow *dialog)
{
if (dialog != m_groupDialog) {
m_groupDialog = dialog;
emit groupDialogChanged();
}
}
bool Backend::highlightWindows() const
{
return m_highlightWindows;
}
void Backend::setHighlightWindows(bool highlight)
{
if (highlight != m_highlightWindows) {
m_highlightWindows = highlight;
updateWindowHighlight();
emit highlightWindowsChanged();
}
}
QUrl Backend::tryDecodeApplicationsUrl(const QUrl &launcherUrl)
{
if (launcherUrl.isValid() && launcherUrl.scheme() == QStringLiteral("applications")) {
const KService::Ptr service = KService::serviceByMenuId(launcherUrl.path());
if (service) {
return QUrl::fromLocalFile(service->entryPath());
}
}
return launcherUrl;
}
QVariantList Backend::jumpListActions(const QUrl &launcherUrl, QObject *parent)
{
if (!parent) {
return QVariantList();
}
QUrl desktopEntryUrl = tryDecodeApplicationsUrl(launcherUrl);
if (!desktopEntryUrl.isValid() || !desktopEntryUrl.isLocalFile()
|| !KDesktopFile::isDesktopFile(desktopEntryUrl.toLocalFile())) {
return QVariantList();
}
QVariantList actions;
KDesktopFile desktopFile(desktopEntryUrl.toLocalFile());
const QStringList &jumpListActions = desktopFile.readActions();
const QLatin1String kde("KDE");
foreach (const QString &actionName, jumpListActions) {
const KConfigGroup &actionGroup = desktopFile.actionGroup(actionName);
if (!actionGroup.isValid() || !actionGroup.exists()) {
continue;
}
const QStringList ¬ShowIn = actionGroup.readXdgListEntry(QStringLiteral("NotShowIn"));
if (notShowIn.contains(kde)) {
continue;
}
const QStringList &onlyShowIn = actionGroup.readXdgListEntry(QStringLiteral("OnlyShowIn"));
if (!onlyShowIn.isEmpty() && !onlyShowIn.contains(kde)) {
continue;
}
const QString &name = actionGroup.readEntry(QStringLiteral("Name"));
const QString &exec = actionGroup.readEntry(QStringLiteral("Exec"));
if (name.isEmpty() || exec.isEmpty()) {
continue;
}
QAction *action = new QAction(parent);
action->setText(name);
action->setIcon(QIcon::fromTheme(actionGroup.readEntry("Icon")));
action->setProperty("exec", exec);
// so we can show the proper application name and icon when it launches
action->setProperty("applicationName", desktopFile.readName());
action->setProperty("applicationIcon", desktopFile.readIcon());
connect(action, &QAction::triggered, this, &Backend::handleJumpListAction);
actions << QVariant::fromValue(action);
}
return actions;
}
QVariantList Backend::placesActions(const QUrl &launcherUrl, bool showAllPlaces, QObject *parent)
{
if (!parent) {
return QVariantList();
}
QUrl desktopEntryUrl = tryDecodeApplicationsUrl(launcherUrl);
if (!desktopEntryUrl.isValid() || !desktopEntryUrl.isLocalFile()
|| !KDesktopFile::isDesktopFile(desktopEntryUrl.toLocalFile())) {
return QVariantList();
}
QVariantList actions;
KDesktopFile desktopFile(desktopEntryUrl.toLocalFile());
// Since we can't have dynamic jump list actions, at least add the user's "Places" for file managers.
const QStringList &categories = desktopFile.desktopGroup().readXdgListEntry(QStringLiteral("Categories"));
if (!categories.contains(QLatin1String("FileManager"))) {
return actions;
}
QString previousGroup;
QMenu *subMenu = nullptr;
QScopedPointer placesModel(new KFilePlacesModel());
for (int i = 0; i < placesModel->rowCount(); ++i) {
QModelIndex idx = placesModel->index(i, 0);
if (placesModel->isHidden(idx)) {
continue;
}
const QString &title = idx.data(Qt::DisplayRole).toString();
const QIcon &icon = idx.data(Qt::DecorationRole).value();
const QUrl &url = idx.data(KFilePlacesModel::UrlRole).toUrl();
QAction *placeAction = new QAction(icon, title, parent);
connect(placeAction, &QAction::triggered, this, [this, url, desktopEntryUrl] {
KService::Ptr service = KService::serviceByDesktopPath(desktopEntryUrl.toLocalFile());
if (!service) {
return;
}
KRun::runService(*service, {url}, QApplication::activeWindow());
});
const QString &groupName = idx.data(KFilePlacesModel::GroupRole).toString();
if (previousGroup.isEmpty()) { // Skip first group heading.
previousGroup = groupName;
}
// Put all subsequent categories into a submenu.
if (previousGroup != groupName) {
QAction *subMenuAction = new QAction(groupName, parent);
subMenu = new QMenu();
// Cannot parent a QMenu to a QAction, need to delete it manually.
connect(parent, &QObject::destroyed, subMenu, &QObject::deleteLater);
subMenuAction->setMenu(subMenu);
actions << QVariant::fromValue(subMenuAction);
previousGroup = groupName;
}
if (subMenu) {
subMenu->addAction(placeAction);
} else {
actions << QVariant::fromValue(placeAction);
}
}
// There is nothing more frustrating than having a "More" entry that ends up showing just one or two
// additional entries. Therefore we truncate to max. 5 entries only if there are more than 7 in total.
if (!showAllPlaces && actions.count() > 7) {
const int totalActionCount = actions.count();
while (actions.count() > 5) {
actions.removeLast();
}
QAction *action = new QAction(parent);
action->setText(i18ncp("Show all user Places", "%1 more Place", "%1 more Places", totalActionCount - actions.count()));
connect(action, &QAction::triggered, this, &Backend::showAllPlaces);
actions << QVariant::fromValue(action);
}
return actions;
}
QVariantList Backend::recentDocumentActions(const QUrl &launcherUrl, QObject *parent)
{
if (!parent) {
return QVariantList();
}
QUrl desktopEntryUrl = tryDecodeApplicationsUrl(launcherUrl);
if (!desktopEntryUrl.isValid() || !desktopEntryUrl.isLocalFile()
|| !KDesktopFile::isDesktopFile(desktopEntryUrl.toLocalFile())) {
return QVariantList();
}
QVariantList actions;
QString desktopName = desktopEntryUrl.fileName();
QString storageId = desktopName;
if (storageId.endsWith(QLatin1String(".desktop"))) {
storageId = storageId.left(storageId.length() - 8);
}
auto query = UsedResources
| RecentlyUsedFirst
| Agent(storageId)
| Type::any()
| Activity::current()
| Url::file();
ResultSet results(query);
ResultSet::const_iterator resultIt = results.begin();
int actionCount = 0;
while (actionCount < 5 && resultIt != results.end()) {
const QString resource = (*resultIt).resource();
const QUrl url(resource);
if (!url.isValid()) {
continue;
}
const KFileItem fileItem(url);
if (!fileItem.isFile()) {
continue;
}
QAction *action = new QAction(parent);
action->setText(url.fileName());
action->setIcon(QIcon::fromTheme(fileItem.iconName(), QIcon::fromTheme(QStringLiteral("unknown"))));
action->setProperty("agent", storageId);
action->setProperty("entryPath", desktopEntryUrl);
action->setData(resource);
connect(action, &QAction::triggered, this, &Backend::handleRecentDocumentAction);
actions << QVariant::fromValue(action);
++resultIt;
++actionCount;
}
if (actionCount > 0) {
QAction *action = new QAction(parent);
action->setText(i18n("Forget Recent Documents"));
action->setProperty("agent", storageId);
connect(action, &QAction::triggered, this, &Backend::handleRecentDocumentAction);
actions << QVariant::fromValue(action);
}
return actions;
}
void Backend::toolTipWindowChanged(QQuickWindow *window)
{
Q_UNUSED(window)
updateWindowHighlight();
}
void Backend::handleJumpListAction() const
{
const QAction *action = qobject_cast(sender());
if (!action) {
return;
}
KRun::run(action->property("exec").toString(), {}, nullptr,
action->property("applicationName").toString(),
action->property("applicationIcon").toString());
}
void Backend::handleRecentDocumentAction() const
{
const QAction *action = qobject_cast(sender());
if (!action) {
return;
}
const QString agent = action->property("agent").toString();
if (agent.isEmpty()) {
return;
}
const QString desktopPath = action->property("entryPath").toUrl().toLocalFile();
const QString resource = action->data().toString();
if (desktopPath.isEmpty() || resource.isEmpty()) {
auto query = UsedResources
| Agent(agent)
| Type::any()
| Activity::current()
| Url::file();
KAStats::forgetResources(query);
return;
}
KService::Ptr service = KService::serviceByDesktopPath(desktopPath);
qDebug() << service;
if (!service) {
return;
}
KRun::runService(*service, QList() << QUrl(resource), QApplication::activeWindow());
}
void Backend::setActionGroup(QAction *action) const
{
if (action) {
action->setActionGroup(m_actionGroup);
}
}
QRect Backend::globalRect(QQuickItem *item) const
{
if (!item || !item->window()) {
return QRect();
}
QRect iconRect(item->x(), item->y(), item->width(), item->height());
iconRect.moveTopLeft(item->parentItem()->mapToScene(iconRect.topLeft()).toPoint());
iconRect.moveTopLeft(item->window()->mapToGlobal(iconRect.topLeft()));
return iconRect;
}
void Backend::ungrabMouse(QQuickItem *item) const
{
- //this is a workaround where Qt will fail to realise a mouse has been released
+ //this is a workaround where Qt will fail to realize a mouse has been released
// this happens if a window which does not accept focus spawns a new window that takes focus and X grab
// whilst the mouse is depressed
// https://bugreports.qt.io/browse/QTBUG-59044
// this causes the next click to go missing
//by releasing manually we avoid that situation
auto ungrabMouseHack = [item]() {
if (item && item->window() && item->window()->mouseGrabberItem()) {
item->window()->mouseGrabberItem()->ungrabMouse();
}
};
//pre 5.8.0 QQuickWindow code is "item->grabMouse(); sendEvent(item, mouseEvent)"
//post 5.8.0 QQuickWindow code is sendEvent(item, mouseEvent); item->grabMouse()
if (QVersionNumber::fromString(QString::fromLatin1(qVersion())) > QVersionNumber(5, 8, 0)) {
QTimer::singleShot(0, item, ungrabMouseHack);
}
else {
ungrabMouseHack();
}
//end workaround
}
bool Backend::canPresentWindows() const
{
return (KWindowSystem::compositingActive() && KWindowEffects::isEffectAvailable(KWindowEffects::PresentWindowsGroup));
}
void Backend::presentWindows(const QVariant &_winIds)
{
if (!m_taskManagerItem || !m_taskManagerItem->window()) {
return;
}
QList winIds;
const QVariantList &_winIdsList = _winIds.toList();
foreach(const QVariant &_winId, _winIdsList) {
bool ok = false;
qlonglong winId = _winId.toLongLong(&ok);
if (ok) {
winIds.append(winId);
}
}
if (!winIds.count()) {
return;
}
if (m_windowsToHighlight.count()) {
m_windowsToHighlight.clear();
updateWindowHighlight();
}
KWindowEffects::presentWindows(m_taskManagerItem->window()->winId(), winIds);
}
bool Backend::isApplication(const QUrl &url) const
{
if (!url.isValid() || !url.isLocalFile()) {
return false;
}
const QString &localPath = url.toLocalFile();
if (!KDesktopFile::isDesktopFile(localPath)) {
return false;
}
KDesktopFile desktopFile(localPath);
return desktopFile.hasApplicationType();
}
QList Backend::jsonArrayToUrlList(const QJsonArray &array) const
{
QList urls;
urls.reserve(array.count());
for (auto it = array.constBegin(), end = array.constEnd(); it != end; ++it) {
urls << QUrl(it->toString());
}
return urls;
}
void Backend::cancelHighlightWindows()
{
m_windowsToHighlight.clear();
updateWindowHighlight();
}
void Backend::windowsHovered(const QVariant &_winIds, bool hovered)
{
m_windowsToHighlight.clear();
if (hovered) {
const QVariantList &winIds = _winIds.toList();
foreach(const QVariant &_winId, winIds) {
bool ok = false;
qlonglong winId = _winId.toLongLong(&ok);
if (ok) {
m_windowsToHighlight.append(winId);
}
}
}
updateWindowHighlight();
}
void Backend::updateWindowHighlight()
{
if (!m_highlightWindows) {
if (m_panelWinId) {
KWindowEffects::highlightWindows(m_panelWinId, QList());
m_panelWinId = 0;
}
return;
}
if (m_taskManagerItem && m_taskManagerItem->window()) {
m_panelWinId = m_taskManagerItem->window()->winId();
} else {
return;
}
QList windows = m_windowsToHighlight;
if (windows.count() && m_toolTipItem && m_toolTipItem->window()) {
windows.append(m_toolTipItem->window()->winId());
}
if (windows.count() && m_groupDialog) {
windows.append(m_groupDialog->winId());
}
KWindowEffects::highlightWindows(m_panelWinId, windows);
}
diff --git a/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.cpp b/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.cpp
index d06b0b1bc..23d85eade 100644
--- a/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.cpp
+++ b/applets/taskmanager/plugin/smartlaunchers/smartlauncherbackend.cpp
@@ -1,353 +1,353 @@
/***************************************************************************
* Copyright (C) 2016 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 . *
***************************************************************************/
#include "smartlauncherbackend.h"
#include
#include
#include
#include
#include
#include
#include
#include
using namespace SmartLauncher;
Backend::Backend(QObject *parent)
: QObject(parent)
, m_watcher(new QDBusServiceWatcher(this))
, m_dataEngineConsumer(new Plasma::DataEngineConsumer)
, m_dataEngine(m_dataEngineConsumer->dataEngine(QStringLiteral("applicationjobs")))
{
m_available = setupUnity();
m_available = setupApplicationJobs() || m_available;
}
Backend::~Backend()
{
delete m_dataEngineConsumer;
}
bool Backend::setupUnity()
{
auto sessionBus = QDBusConnection::sessionBus();
if (!sessionBus.connect({}, {}, QStringLiteral("com.canonical.Unity.LauncherEntry"),
QStringLiteral("Update"), this, SLOT(update(QString,QMap)))) {
qWarning() << "failed to register Update signal";
return false;
}
if (!sessionBus.registerObject(QStringLiteral("/Unity"), this)) {
qWarning() << "Failed to register unity object";
return false;
}
if (!sessionBus.registerService(QStringLiteral("com.canonical.Unity"))) {
qWarning() << "Failed to register unity service";
return false;
}
KConfigGroup grp(KSharedConfig::openConfig(QStringLiteral("taskmanagerrulesrc")), QStringLiteral("Unity Launcher Mapping"));
foreach (const QString &key, grp.keyList()) {
const QString &value = grp.readEntry(key, QString());
if (value.isEmpty()) {
continue;
}
m_unityMappingRules.insert(key, value);
}
return true;
}
bool Backend::setupApplicationJobs()
{
if (!m_dataEngine->isValid()) {
qWarning() << "Failed to setup application jobs, data engine is not valid";
return false;
}
const QStringList &sources = m_dataEngine->sources();
for (const QString &source : sources) {
onApplicationJobAdded(source);
}
connect(m_dataEngine, &Plasma::DataEngine::sourceAdded, this, &Backend::onApplicationJobAdded);
connect(m_dataEngine, &Plasma::DataEngine::sourceRemoved, this, &Backend::onApplicationJobRemoved);
return true;
}
bool Backend::available() const
{
return m_available;
}
bool Backend::hasLauncher(const QString &storageId) const
{
return m_launchers.contains(storageId);
}
int Backend::count(const QString &uri) const
{
return m_launchers.value(uri).count;
}
bool Backend::countVisible(const QString &uri) const
{
return m_launchers.value(uri).countVisible;
}
int Backend::progress(const QString &uri) const
{
return m_launchers.value(uri).progress;
}
bool Backend::progressVisible(const QString &uri) const
{
return m_launchers.value(uri).progressVisible;
}
bool Backend::urgent(const QString &uri) const
{
return m_launchers.value(uri).urgent;
}
QHash Backend::unityMappingRules() const
{
return m_unityMappingRules;
}
void Backend::update(const QString &uri, const QMap &properties)
{
Q_ASSERT(calledFromDBus());
QString storageId;
auto foundStorageId = m_launcherUrlToStorageId.constFind(uri);
if (foundStorageId == m_launcherUrlToStorageId.constEnd()) { // we don't know this one, register
// According to Unity Launcher API documentation applications *should* send along their
// desktop file name with application:// prefix
const QString applicationSchemePrefix = QStringLiteral("application://");
QString normalizedUri = uri;
if (normalizedUri.startsWith(applicationSchemePrefix)) {
normalizedUri = uri.mid(applicationSchemePrefix.length());
}
KService::Ptr service = KService::serviceByStorageId(normalizedUri);
if (!service) {
qWarning() << "Failed to find service for Unity Launcher" << uri;
return;
}
storageId = service->storageId();
m_launcherUrlToStorageId.insert(uri, storageId);
m_dbusServiceToLauncherUrl.insert(message().service(), uri);
m_watcher->addWatchedService(message().service());
} else {
storageId = *foundStorageId;
}
auto foundEntry = m_launchers.find(storageId);
if (foundEntry == m_launchers.end()) { // we don't have it yet, create a new Entry
Entry entry;
foundEntry = m_launchers.insert(storageId, entry);
}
auto propertiesEnd = properties.constEnd();
auto foundCount = properties.constFind(QStringLiteral("count"));
if (foundCount != propertiesEnd) {
qint64 newCount = foundCount->toLongLong();
// 2 billion unread emails ought to be enough for anybody
if (newCount < std::numeric_limits::max()) {
int saneCount = static_cast(newCount);
if (saneCount != foundEntry->count) {
foundEntry->count = saneCount;
emit countChanged(storageId, saneCount);
}
}
}
updateLauncherProperty(storageId, properties, QStringLiteral("count"), &foundEntry->count, &Backend::countChanged);
updateLauncherProperty(storageId, properties, QStringLiteral("count-visible"), &foundEntry->countVisible, &Backend::countVisibleChanged);
- // the API gives us progress as 0..1 double but we'll use percent to avoid unneccessary
+ // the API gives us progress as 0..1 double but we'll use percent to avoid unnecessary
// changes when it just changed a fraction of a percent, hence not using our fancy updateLauncherProperty method
auto foundProgress = properties.constFind(QStringLiteral("progress"));
if (foundProgress != propertiesEnd) {
int newProgress = qRound(foundProgress->toDouble() * 100);
if (newProgress != foundEntry->progress) {
foundEntry->progress = newProgress;
emit progressChanged(storageId, newProgress);
}
}
updateLauncherProperty(storageId, properties, QStringLiteral("progress-visible"), &foundEntry->progressVisible, &Backend::progressVisibleChanged);
updateLauncherProperty(storageId, properties, QStringLiteral("urgent"), &foundEntry->urgent, &Backend::urgentChanged);
}
void Backend::onServiceUnregistered(const QString &service)
{
const QString &launcherUrl = m_dbusServiceToLauncherUrl.take(service);
if (launcherUrl.isEmpty()) {
return;
}
const QString &storageId = m_launcherUrlToStorageId.take(launcherUrl);
if (storageId.isEmpty()) {
return;
}
m_launchers.remove(storageId);
emit launcherRemoved(storageId);
}
void Backend::onApplicationJobAdded(const QString &source)
{
m_dataEngine->connectSource(source, this);
}
void Backend::onApplicationJobRemoved(const QString &source)
{
m_dataEngine->disconnectSource(source, this);
const QString &storageId = m_dataSourceToStorageId.take(source);
if (storageId.isEmpty()) {
return;
}
// remove job, calculate new percentage, or remove launcher if gone altogether
auto &jobs = m_storageIdToJobs[storageId];
jobs.removeOne(source);
if (jobs.isEmpty()) {
m_storageIdToJobs.remove(storageId);
}
m_jobProgress.remove(source);
auto foundEntry = m_launchers.find(storageId);
if (foundEntry == m_launchers.end()) {
qWarning() << "Cannot remove application job" << source << "as we don't know" << storageId;
return;
}
updateApplicationJobPercent(storageId, &*foundEntry);
if (!foundEntry->progressVisible && !foundEntry->progress) {
// no progress anymore whatsoever, remove entire launcher
m_launchers.remove(storageId);
emit launcherRemoved(storageId);
}
}
void Backend::dataUpdated(const QString &sourceName, const Plasma::DataEngine::Data &data)
{
QString storageId;
auto foundStorageId = m_dataSourceToStorageId.constFind(sourceName);
if (foundStorageId == m_dataSourceToStorageId.constEnd()) { // we don't know this one, register
QString appName = data.value(QStringLiteral("appName")).toString();
if (appName.isEmpty()) {
qWarning() << "Application jobs got update for" << sourceName << "without app name";
return;
}
KService::Ptr service = KService::serviceByStorageId(appName);
if (!service) {
appName.prepend(QLatin1String("org.kde."));
// HACK try to find a service with org.kde. notation
service = KService::serviceByStorageId(appName);
if (!service) {
qWarning() << "Could not find service for job" << sourceName << "with app name" << appName;
return;
}
}
storageId = service->storageId();
m_dataSourceToStorageId.insert(sourceName, storageId);
} else {
storageId = *foundStorageId;
}
auto foundEntry = m_launchers.find(storageId);
if (foundEntry == m_launchers.end()) { // we don't have it yet, create new Entry
Entry entry;
foundEntry = m_launchers.insert(storageId, entry);
}
int percent = data.value(QStringLiteral("percentage"), 0).toInt();
// setup everything and calculate new percentage
auto &jobs = m_storageIdToJobs[storageId];
if (!jobs.contains(sourceName)) {
jobs.append(sourceName);
}
m_jobProgress.insert(sourceName, percent); // insert() overrides if exist
updateApplicationJobPercent(storageId, &*foundEntry);
}
void Backend::updateApplicationJobPercent(const QString &storageId, Entry *entry)
{
// basically get all jobs for the given storageId and calculate an average progress
const auto &jobs = m_storageIdToJobs.value(storageId);
qreal jobCount = jobs.count();
int totalProgress = 0;
for (const QString &job : jobs) {
totalProgress += m_jobProgress.value(job, 0);
}
int progress = 0;
if (jobCount > 0) {
progress = qRound(totalProgress / jobCount);
}
bool visible = (jobCount > 0);
if (entry->count != jobCount) {
entry->count = jobCount;
emit countChanged(storageId, jobCount);
}
if (entry->countVisible != visible) {
entry->countVisible = visible;
emit countVisibleChanged(storageId, visible);
}
if (entry->progress != progress) {
entry->progress = progress;
emit progressChanged(storageId, progress);
}
if (entry->progressVisible != visible) {
entry->progressVisible = visible;
emit progressVisibleChanged(storageId, visible);
}
}
diff --git a/attica-kde/kdeplugin/kdeplatformdependent.cpp b/attica-kde/kdeplugin/kdeplatformdependent.cpp
index 36dff5c2a..ab10a96ee 100644
--- a/attica-kde/kdeplugin/kdeplatformdependent.cpp
+++ b/attica-kde/kdeplugin/kdeplatformdependent.cpp
@@ -1,264 +1,263 @@
/*
This file is part of KDE.
Copyright (c) 2009 Eckhart Wörner
Copyright (c) 2010 Frederik Gladhorn
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) version 3, or any
later version accepted by the membership of KDE e.V. (or its
successor approved by the membership of KDE e.V.), which shall
act as a proxy defined in Section 6 of version 3 of the license.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library. If not, see .
*/
#include "kdeplatformdependent.h"
#include "attica_plugin_debug.h"
#include
-#include "attica_plugin_debug.h"
#include
#include
#include
#include
#include
#include
#include
using namespace Attica;
KdePlatformDependent::KdePlatformDependent()
: m_config(KSharedConfig::openConfig(QStringLiteral("atticarc"))), m_accessManager(nullptr), m_wallet(nullptr)
{
// FIXME: Investigate how to not leak this instance witohut crashing.
m_accessManager = new QNetworkAccessManager(nullptr);
const QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/attica");
QNetworkDiskCache *cache = new QNetworkDiskCache(m_accessManager);
QStorageInfo storageInfo(cacheDir);
cache->setCacheDirectory(cacheDir);
cache->setMaximumCacheSize(storageInfo.bytesTotal() / 1000);
m_accessManager->setCache(cache);
}
KdePlatformDependent::~KdePlatformDependent()
{
delete m_wallet;
}
bool KdePlatformDependent::openWallet(bool force)
{
if (m_wallet) {
return true;
}
QString networkWallet = KWallet::Wallet::NetworkWallet();
// if not forced, or the folder doesn't exist, don't try to open the wallet
if (force || (!KWallet::Wallet::folderDoesNotExist(networkWallet, QStringLiteral("Attica")))) {
m_wallet = KWallet::Wallet::openWallet(networkWallet, 0);
}
if (m_wallet) {
m_wallet->createFolder(QStringLiteral("Attica"));
m_wallet->setFolder(QStringLiteral("Attica"));
return true;
}
return false;
}
QNetworkReply* KdePlatformDependent::post(const QNetworkRequest& request, const QByteArray& data)
{
return m_accessManager->post(removeAuthFromRequest(request), data);
}
QNetworkReply* KdePlatformDependent::post(const QNetworkRequest& request, QIODevice* data)
{
return m_accessManager->post(removeAuthFromRequest(request), data);
}
QNetworkReply* KdePlatformDependent::get(const QNetworkRequest& request)
{
return m_accessManager->get(removeAuthFromRequest(request));
}
QNetworkRequest KdePlatformDependent::removeAuthFromRequest(const QNetworkRequest& request)
{
const QStringList noauth = { QStringLiteral("no-auth-prompt"), QStringLiteral("true") };
QNetworkRequest notConstReq = const_cast(request);
notConstReq.setAttribute(QNetworkRequest::User, noauth);
return notConstReq;
}
bool KdePlatformDependent::saveCredentials(const QUrl& baseUrl, const QString& user, const QString& password)
{
m_passwords[baseUrl.toString()] = qMakePair(user, password);
if (!m_wallet && !openWallet(true)) {
if (KMessageBox::warningContinueCancel(nullptr, i18n("Should the password be stored in the configuration file? This is unsafe.")
, i18n("Social Desktop Configuration"))
== KMessageBox::Cancel) {
return false;
}
// use kconfig
KConfigGroup group(m_config, baseUrl.toString());
group.writeEntry("user", user);
group.writeEntry("password", KStringHandler::obscure(password));
qCDebug(ATTICA_PLUGIN_LOG) << "Saved credentials in KConfig";
return true;
}
// Remove the entry when user name is empty
if (user.isEmpty()) {
m_wallet->removeEntry(baseUrl.toString());
return true;
}
const QMap entries = {
{ QStringLiteral("user"), user },
{ QStringLiteral("password"), password }
};
qCDebug(ATTICA_PLUGIN_LOG) << "Saved credentials in KWallet";
return !m_wallet->writeMap(baseUrl.toString(), entries);
}
bool KdePlatformDependent::hasCredentials(const QUrl& baseUrl) const
{
if (m_passwords.contains(baseUrl.toString())) {
return true;
}
QString networkWallet = KWallet::Wallet::NetworkWallet();
if (!KWallet::Wallet::folderDoesNotExist(networkWallet, QStringLiteral("Attica")) &&
!KWallet::Wallet::keyDoesNotExist(networkWallet, QStringLiteral("Attica"), baseUrl.toString())) {
qCDebug(ATTICA_PLUGIN_LOG) << "Found credentials in KWallet";
return true;
}
KConfigGroup group(m_config, baseUrl.toString());
const QString user = group.readEntry("user", QString());
qCDebug(ATTICA_PLUGIN_LOG) << "Credentials found:" << !user.isEmpty();
return !user.isEmpty();
}
bool KdePlatformDependent::loadCredentials(const QUrl& baseUrl, QString& user, QString& password)
{
QString networkWallet = KWallet::Wallet::NetworkWallet();
if (KWallet::Wallet::folderDoesNotExist(networkWallet, QStringLiteral("Attica")) &&
KWallet::Wallet::keyDoesNotExist(networkWallet, QStringLiteral("Attica"), baseUrl.toString())) {
// use KConfig
KConfigGroup group(m_config, baseUrl.toString());
user = group.readEntry("user", QString());
password = KStringHandler::obscure(group.readEntry("password", QString()));
if (!user.isEmpty()) {
qCDebug(ATTICA_PLUGIN_LOG) << "Successfully loaded credentials from kconfig";
m_passwords[baseUrl.toString()] = qMakePair(user, password);
return true;
}
return false;
}
if (!m_wallet && !openWallet(true)) {
return false;
}
QMap entries;
if (m_wallet->readMap(baseUrl.toString(), entries) != 0) {
return false;
}
user = entries.value(QStringLiteral("user"));
password = entries.value(QStringLiteral("password"));
qCDebug(ATTICA_PLUGIN_LOG) << "Successfully loaded credentials.";
m_passwords[baseUrl.toString()] = qMakePair(user, password);
return true;
}
bool Attica::KdePlatformDependent::askForCredentials(const QUrl& baseUrl, QString& user, QString& password)
{
Q_UNUSED(baseUrl);
Q_UNUSED(user);
Q_UNUSED(password);
return false;
}
QList KdePlatformDependent::getDefaultProviderFiles() const
{
KConfigGroup group(m_config, "General");
QStringList pathStrings = group.readPathEntry("providerFiles", QStringList(QStringLiteral("http://download.kde.org/ocs/providers.xml")));
QList paths;
foreach (const QString& pathString, pathStrings) {
paths.append(QUrl(pathString));
}
qCDebug(ATTICA_PLUGIN_LOG) << "Loaded paths from config:" << paths;
return paths;
}
void KdePlatformDependent::addDefaultProviderFile(const QUrl& url)
{
KConfigGroup group(m_config, "General");
QStringList pathStrings = group.readPathEntry("providerFiles", QStringList(QStringLiteral("http://download.kde.org/ocs/providers.xml")));
QString urlString = url.toString();
if(!pathStrings.contains(urlString)) {
pathStrings.append(urlString);
group.writeEntry("providerFiles", pathStrings);
group.sync();
qCDebug(ATTICA_PLUGIN_LOG) << "wrote providers: " << pathStrings;
}
}
void KdePlatformDependent::removeDefaultProviderFile(const QUrl& url)
{
KConfigGroup group(m_config, "General");
QStringList pathStrings = group.readPathEntry("providerFiles", QStringList(QStringLiteral("http://download.kde.org/ocs/providers.xml")));
pathStrings.removeAll(url.toString());
group.writeEntry("providerFiles", pathStrings);
}
void KdePlatformDependent::enableProvider(const QUrl& baseUrl, bool enabled) const
{
KConfigGroup group(m_config, "General");
QStringList pathStrings = group.readPathEntry("disabledProviders", QStringList());
if (enabled) {
pathStrings.removeAll(baseUrl.toString());
} else {
if (!pathStrings.contains(baseUrl.toString())) {
pathStrings.append(baseUrl.toString());
}
}
group.writeEntry("disabledProviders", pathStrings);
group.sync();
}
bool KdePlatformDependent::isEnabled(const QUrl& baseUrl) const
{
KConfigGroup group(m_config, "General");
return !group.readPathEntry("disabledProviders", QStringList()).contains(baseUrl.toString());
}
QNetworkAccessManager* Attica::KdePlatformDependent::nam()
{
return m_accessManager;
}
// TODO: re-enable, see http://community.kde.org/Frameworks/Porting_Notes
// Q_EXPORT_PLUGIN2(attica_kde, Attica::KdePlatformDependent)
diff --git a/config-X11.h.cmake b/config-X11.h.cmake
index ee5183a63..60286dd63 100644
--- a/config-X11.h.cmake
+++ b/config-X11.h.cmake
@@ -1,44 +1,44 @@
/* Define if you have the XRandR extension */
#cmakedefine HAVE_XRANDR 1
/* Define if you have the XDamage extension */
#cmakedefine HAVE_XDAMAGE 1
/* Define if you have the XKB extension */
#cmakedefine HAVE_XKB 1
/* Define if you have the Xinerama extension */
#cmakedefine HAVE_XINERAMA 1
/* Define if you have the XSHM (MIT SHM) extension */
#cmakedefine HAVE_XSHM 1
/* Define if you have the XComposite extension */
#cmakedefine HAVE_XCOMPOSITE 1
/* Define to 1 if you have Xcursor */
#cmakedefine HAVE_XCURSOR 1
/* Define if you have the xf86misc extension */
#cmakedefine HAVE_XF86MISC 1
/* Define if you have the XFixes extension */
#cmakedefine HAVE_XFIXES 1
/* Define if you have the XTest extension */
#cmakedefine HAVE_XTEST 1
/* Define if your system has XRender support */
#cmakedefine HAVE_XRENDER 1
/* Define if you have OpenGL */
#cmakedefine HAVE_OPENGL 1
/* Define if you have the XSync extension */
#cmakedefine HAVE_XSYNC 1
/* Define if you have XRandR 1.3 */
#cmakedefine HAS_RANDR_1_3 1
/* Define if you have X11 at all */
-#define HAVE_X11 ${X11_FOUND}
\ No newline at end of file
+#define HAVE_X11 ${X11_FOUND}
diff --git a/containments/desktop/package/contents/ui/FolderView.qml b/containments/desktop/package/contents/ui/FolderView.qml
index 85b2e152b..ad362b97a 100644
--- a/containments/desktop/package/contents/ui/FolderView.qml
+++ b/containments/desktop/package/contents/ui/FolderView.qml
@@ -1,1370 +1,1370 @@
/***************************************************************************
* 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 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 Item backButton: null
property var dialog: null
property Item editor: null
function rename()
{
if (gridView.currentIndex != -1) {
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);
updateHistory();
dir.cd(row);
}
function doBack() {
url = history.pop();
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) {
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 occured inside the item we expect it in before going ahead. If it
+ // 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 extraWidth = calcExtraSpacing(iconWidth, scrollArea.viewportWidth);
return iconWidth + 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 geomety.
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 && 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 && currentIndex != -1 && dir.hasSelection()) {
var func = root.isPopup ? doCd : dir.run;
func(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;
}
}
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();
}
}
}
diff --git a/containments/desktop/plugins/folder/autotests/positionertest.cpp b/containments/desktop/plugins/folder/autotests/positionertest.cpp
index db0e1d9ad..f89f6f360 100644
--- a/containments/desktop/plugins/folder/autotests/positionertest.cpp
+++ b/containments/desktop/plugins/folder/autotests/positionertest.cpp
@@ -1,342 +1,340 @@
/***************************************************************************
* Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company *
* *
* Author: Andras Mantia *
* *
* 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 "positionertest.h"
-#include
-
#include
#include
#include
#include "foldermodel.h"
#include "positioner.h"
#include "screenmapper.h"
QTEST_MAIN(PositionerTest)
static const QLatin1String desktop(QLatin1String("Desktop"));
void PositionerTest::initTestCase()
{
m_folderDir = new QTemporaryDir();
QDir dir(m_folderDir->path());
dir.mkdir(desktop);
dir.cd(desktop);
dir.mkdir("firstDir");
QFile f;
for (int i = 1; i < 10; i++) {
f.setFileName(QStringLiteral("%1/file%2.txt").arg(dir.path(), QString::number(i)));
f.open(QFile::WriteOnly);
f.close();
}
}
void PositionerTest::cleanupTestCase()
{
delete m_folderDir;
}
void PositionerTest::init()
{
m_folderModel = new FolderModel(this);
m_folderModel->classBegin();
m_folderModel->setScreen(0);
m_folderModel->setUsedByContainment(true);
m_folderModel->componentComplete();
m_positioner = new Positioner(this);
m_positioner->setEnabled(true);
m_positioner->setFolderModel(m_folderModel);
m_positioner->setPerStripe(3);
m_folderModel->setUrl(m_folderDir->path() + QDir::separator() + desktop );
QSignalSpy s(m_folderModel, &FolderModel::listingCompleted);
s.wait(1000);
}
void PositionerTest::cleanup()
{
delete m_folderModel;
m_folderModel = nullptr;
delete m_positioner;
m_positioner = nullptr;
}
void PositionerTest::tst_positions_data()
{
QTest::addColumn("perStripe");
QTest::newRow("3 per column") << 3;
QTest::newRow("5 per column") << 5;
}
void PositionerTest::tst_positions()
{
QFETCH(int, perStripe);
m_positioner->setPerStripe(perStripe);
checkPositions(perStripe);
}
void PositionerTest::tst_map()
{
//by default the mapping is 1-1
for (int i = 0; i < m_positioner->rowCount(); i++) {
QCOMPARE(m_positioner->map(i), i);
}
}
void PositionerTest::tst_move_data()
{
QTest::addColumn("moves");
QTest::addColumn >("result");
QTest::newRow("First to last") << QVariantList({0, 10})
<< QVector({-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0});
QTest::newRow("First to after last") << QVariantList({0, 11})
<< QVector({-1, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, 0});
QTest::newRow("Switch 2nd with 3rd ") << QVariantList({1, 2, 2, 1})
<< QVector({0, 2, 1, 3, 4, 5, 6, 7, 8, 9});
QTest::newRow("Switch 2nd with 2nd ") << QVariantList({1, 1, 1, 1})
<< QVector({0, 1, 2, 3, 4, 5, 6, 7, 8, 9});
QTest::newRow("2nd to last") << QVariantList({2, 10})
<< QVector({0, 1, -1, 3, 4, 5, 6, 7, 8, 9, 2});
}
void PositionerTest::tst_move()
{
QFETCH(QVariantList, moves);
QFETCH(QVector, result);
m_positioner->move(moves);
for (int i = 0; i < m_positioner->rowCount(); i++) {
QCOMPARE(m_positioner->map(i), result[i]);
}
}
void PositionerTest::tst_nearestitem_data()
{
QTest::addColumn("index");
QTest::addColumn >("result");
QTest::newRow("Around first") << 0 << QVector{-1, -1, 3, -1, 1};
QTest::newRow("Around second") << 1 << QVector{-1, -1, 4, 0, 2};
QTest::newRow("Around 5th") << 4 << QVector{-1, 1, 7, 3, 5};
QTest::newRow("Around last") << 9 << QVector{-1, 6, -1, -1, 7};
QTest::newRow("Around invalid") << 11 << QVector{-1, -1, -1, -1, -1};
}
void PositionerTest::tst_nearestitem()
{
QFETCH(int, index);
QFETCH(QVector, result);
for (int i = Qt::NoArrow; i <= Qt::RightArrow; i++) {
QCOMPARE(m_positioner->nearestItem(index, (Qt::ArrowType)i), result[i]);
}
}
void PositionerTest::tst_isBlank()
{
QCOMPARE(m_positioner->isBlank(0), false);
QCOMPARE(m_positioner->isBlank(11), true);
m_positioner->move({0, 10});
QCOMPARE(m_positioner->isBlank(0), true);
}
void PositionerTest::tst_reset()
{
m_positioner->move({0, 10});
m_positioner->reset();
QSignalSpy s(m_positioner, &Positioner::positionsChanged);
s.wait(500);
QCOMPARE(s.count(), 1);
checkPositions(3);
for (int i = 0; i < m_positioner->rowCount(); i++) {
QCOMPARE(m_positioner->map(i), i);
}
}
void PositionerTest::tst_defaultValues()
{
Positioner positioner;
QVERIFY(!positioner.enabled());
QVERIFY(!positioner.folderModel());
QCOMPARE(positioner.perStripe(), 0);
QVERIFY(positioner.positions().isEmpty());
}
void PositionerTest::tst_changeEnabledStatus()
{
Positioner positioner;
QVERIFY(!positioner.enabled());
QSignalSpy s(&positioner, &Positioner::enabledChanged);
positioner.setEnabled(true);
QCOMPARE(s.count(), 1);
positioner.setEnabled(false);
QCOMPARE(s.count(), 2);
//No change
positioner.setEnabled(false);
QCOMPARE(s.count(), 2);
}
void PositionerTest::tst_changePerStripe()
{
Positioner positioner;
QCOMPARE(positioner.perStripe(), 0);
QSignalSpy s(&positioner, &Positioner::perStripeChanged);
positioner.setPerStripe(1);
QCOMPARE(s.count(), 1);
//No change
positioner.setPerStripe(1);
QCOMPARE(s.count(), 1);
positioner.setPerStripe(4);
QCOMPARE(s.count(), 2);
}
void PositionerTest::tst_proxyMapping()
{
auto *screenMapper = ScreenMapper::instance();
FolderModel secondFolderModel;
secondFolderModel.classBegin();
secondFolderModel.setUrl(m_folderDir->path() + QDir::separator() + desktop );
secondFolderModel.setUsedByContainment(true);
secondFolderModel.setScreen(1);
secondFolderModel.componentComplete();
Positioner secondPositioner;
secondPositioner.setEnabled(true);
secondPositioner.setFolderModel(&secondFolderModel);
secondPositioner.setPerStripe(3);
QSignalSpy s2(&secondFolderModel, &FolderModel::listingCompleted);
QVERIFY(s2.wait(1000));
QHash expectedSource2ProxyScreen0;
QHash expectedProxy2SourceScreen0;
QHash expectedProxy2SourceScreen1;
QHash expectedSource2ProxyScreen1;
for (int i = 0; i < m_folderModel->rowCount(); i++) {
expectedSource2ProxyScreen0[i] = i;
expectedProxy2SourceScreen0[i] = i;
}
// swap items 1 and 2 in the positioner
m_positioner->move({1, 2, 2, 1});
expectedSource2ProxyScreen0[1] = 2;
expectedSource2ProxyScreen0[2] = 1;
expectedProxy2SourceScreen0[1] = 2;
expectedProxy2SourceScreen0[2] = 1;
auto savedSource2ProxyScreen0 = expectedSource2ProxyScreen0;
auto savedProxy2SourceScreen0 = expectedProxy2SourceScreen0;
auto verifyMapping = [](const QHash &actual, const QHash &expected) {
auto ensureUnique = [](const QHash mapping) {
auto values = mapping.values();
qSort(values);
auto uniqueValues = values.toSet().toList();
qSort(uniqueValues);
QVERIFY(uniqueValues == values);
};
ensureUnique(actual);
QCOMPARE(actual, expected);
};
verifyMapping(m_positioner->proxyToSourceMapping(), expectedProxy2SourceScreen0);
verifyMapping(m_positioner->sourceToProxyMapping(), expectedSource2ProxyScreen0);
verifyMapping(secondPositioner.proxyToSourceMapping(), expectedProxy2SourceScreen1);
verifyMapping(secondPositioner.sourceToProxyMapping(), expectedSource2ProxyScreen1);
const auto movedItem = m_folderModel->index(1, 0).data(FolderModel::UrlRole).toUrl();
// move the item 1 from source (now in position 2) to the second screen
screenMapper->addMapping(movedItem, 1);
expectedProxy2SourceScreen1[0] = 0;
expectedSource2ProxyScreen1[0] = 0;
expectedSource2ProxyScreen0.clear();
expectedProxy2SourceScreen0.clear();
for (int i = 0; i < m_folderModel->rowCount(); i++) {
// as item 1 disappeared, the mapping of all items after that are shifted
auto proxyIndex = (i <= 1) ? i : i + 1;
expectedProxy2SourceScreen0[proxyIndex] = i;
expectedSource2ProxyScreen0[i] = proxyIndex;
}
verifyMapping(m_positioner->proxyToSourceMapping(), expectedProxy2SourceScreen0);
verifyMapping(m_positioner->sourceToProxyMapping(), expectedSource2ProxyScreen0);
verifyMapping(secondPositioner.proxyToSourceMapping(), expectedProxy2SourceScreen1);
verifyMapping(secondPositioner.sourceToProxyMapping(), expectedSource2ProxyScreen1);
// move back the same item to the first screen
screenMapper->addMapping(movedItem, 0);
// nothing on the second screen
expectedSource2ProxyScreen1.clear();
expectedProxy2SourceScreen1.clear();
// first screen should look like in the beginning
expectedSource2ProxyScreen0 = savedSource2ProxyScreen0;
expectedProxy2SourceScreen0 = savedProxy2SourceScreen0;
verifyMapping(m_positioner->proxyToSourceMapping(), expectedProxy2SourceScreen0);
verifyMapping(m_positioner->sourceToProxyMapping(), expectedSource2ProxyScreen0);
verifyMapping(secondPositioner.proxyToSourceMapping(), expectedProxy2SourceScreen1);
verifyMapping(secondPositioner.sourceToProxyMapping(), expectedSource2ProxyScreen1);
}
void PositionerTest::checkPositions(int perStripe)
{
QSignalSpy s(m_positioner, &Positioner::positionsChanged);
s.wait(500);
const auto positions = m_positioner->positions();
struct Pos {
int x;
int y;
};
const auto fileCount = m_folderModel->rowCount();
QHash posHash;
QCOMPARE(positions[0].toInt(), 1 + ((fileCount - 1) / perStripe)); // rows
QCOMPARE(positions[1].toInt(), perStripe); // columns
for (int i = 2; i < positions.length() - 2; i+=3) {
posHash[positions[i]] = {positions[i + 1].toInt(), positions[i + 2].toInt()};
}
int row = 0;
int col = 0;
for (int i = 0; i < fileCount; i++) {
const auto index = m_folderModel->index(i, 0);
const auto url = index.data(FolderModel::UrlRole).toString();
const Pos pos = posHash[url];
QCOMPARE(pos.x, row);
QCOMPARE(pos.y, col);
col++;
if (col == perStripe) {
row++;
col = 0;
}
}
}
diff --git a/containments/panel/contents/ui/main.qml b/containments/panel/contents/ui/main.qml
index a3f25766e..bdf1b5b86 100644
--- a/containments/panel/contents/ui/main.qml
+++ b/containments/panel/contents/ui/main.qml
@@ -1,444 +1,444 @@
/*
* Copyright 2013 Marco Martin
*
* 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 2.010-1301, USA.
*/
import QtQuick 2.1
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.kquickcontrolsaddons 2.0
import org.kde.draganddrop 2.0 as DragDrop
import "LayoutManager.js" as LayoutManager
DragDrop.DropArea {
id: root
width: 640
height: 48
//BEGIN properties
Layout.minimumWidth: fixedWidth > 0 ? fixedWidth : (currentLayout.Layout.minimumWidth + (isHorizontal && toolBox ? toolBox.width : 0))
Layout.maximumWidth: fixedWidth > 0 ? fixedWidth : (currentLayout.Layout.maximumWidth + (isHorizontal && toolBox ? toolBox.width : 0))
Layout.preferredWidth: fixedWidth > 0 ? fixedWidth : (currentLayout.Layout.preferredWidth + (isHorizontal && toolBox ? toolBox.width : 0))
Layout.minimumHeight: fixedHeight > 0 ? fixedHeight : (currentLayout.Layout.minimumHeight + (!isHorizontal && toolBox ? toolBox.height : 0))
Layout.maximumHeight: fixedHeight > 0 ? fixedHeight : (currentLayout.Layout.maximumHeight + (!isHorizontal && toolBox ? toolBox.height : 0))
Layout.preferredHeight: fixedHeight > 0 ? fixedHeight : (currentLayout.Layout.preferredHeight + (!isHorizontal && toolBox? toolBox.height : 0))
property Item toolBox
property var layoutManager: LayoutManager
property Item dragOverlay
property bool isHorizontal: plasmoid.formFactor != PlasmaCore.Types.Vertical
property int fixedWidth: 0
property int fixedHeight: 0
//END properties
//BEGIN functions
function addApplet(applet, x, y) {
// don't show applet if it choses to be hidden but still make it
// accessible in the panelcontroller
// Due to the nature of how "visible" propagates in QML, we need to
// explicitly set it on the container (so the Layout ignores it)
// as well as the applet (so it reliably knows about), otherwise it can
// happen that an applet erroneously thinks it's visible, or suddenly
// starts thinking that way on teardown (virtual desktop pager)
// leading to crashes
var visibleBinding = Qt.binding(function() {
return applet.status !== PlasmaCore.Types.HiddenStatus || (!plasmoid.immutable && plasmoid.userConfiguring);
})
var container = appletContainerComponent.createObject(root, {
applet: applet,
visible: visibleBinding
});
applet.parent = container;
applet.anchors.fill = container;
applet.visible = visibleBinding;
// Is there a DND placeholder? Replace it!
if (dndSpacer.parent === currentLayout) {
LayoutManager.insertBefore(dndSpacer, container);
dndSpacer.parent = root;
return;
// If the provided position is valid, use it.
} else if (x >= 0 && y >= 0) {
var index = LayoutManager.insertAtCoordinates(container, x , y);
// Fall through to determining an appropriate insert position.
} else {
var before = lastSpacer;
container.animationsEnabled = false;
// Insert icons to the left of whatever is at the center (usually a Task Manager),
// if it exists.
// FIXME TODO: This is a real-world fix to produce a sensible initial position for
// launcher icons added by launcher menu applets. The basic approach has been used
// since Plasma 1. However, "add launcher to X" is a generic-enough concept and
- // frequent-enough occurence that we'd like to abstract it further in the future
- // and get rid of the uglyness of parties external to the containment adding applets
+ // frequent-enough occurrence that we'd like to abstract it further in the future
+ // and get rid of the ugliness of parties external to the containment adding applets
// of a specific type, and the containment caring about the applet type. In a better
// system the containment would be informed of requested launchers, and determine by
// itself what it wants to do with that information.
if (!startupTimer.running && applet.pluginName == "org.kde.plasma.icon") {
var middle = currentLayout.childAt(root.width / 2, root.height / 2);
if (middle) {
before = middle;
}
// lastSpacer is here, enqueue before it.
}
LayoutManager.insertBefore(before, container);
//event compress the enable of animations
startupTimer.restart();
}
}
function checkLastSpacer() {
var flexibleFound = false;
for (var i = 0; i < currentLayout.children.length; ++i) {
var applet = currentLayout.children[i].applet;
if (!applet) {
continue;
}
if (!applet.visible || !applet.Layout) {
continue;
}
if ((root.isHorizontal && applet.Layout.fillWidth) ||
(!root.isHorizontal && applet.Layout.fillHeight)) {
flexibleFound = true;
break
}
}
lastSpacer.visible= !flexibleFound;
}
//END functions
//BEGIN connections
Component.onCompleted: {
currentLayout.isLayoutHorizontal = isHorizontal
LayoutManager.plasmoid = plasmoid;
LayoutManager.root = root;
LayoutManager.layout = currentLayout;
LayoutManager.lastSpacer = lastSpacer;
LayoutManager.restore();
containmentSizeSyncTimer.restart();
plasmoid.action("configure").visible = Qt.binding(function() {
return !plasmoid.immutable;
});
plasmoid.action("configure").enabled = Qt.binding(function() {
return !plasmoid.immutable;
});
}
onDragEnter: {
if (plasmoid.immutable) {
event.ignore();
return;
}
//during drag operations we disable panel auto resize
if (root.isHorizontal) {
root.fixedWidth = root.width
} else {
root.fixedHeight = root.height
}
LayoutManager.insertAtCoordinates(dndSpacer, event.x, event.y)
}
onDragMove: {
LayoutManager.insertAtCoordinates(dndSpacer, event.x, event.y)
}
onDragLeave: {
dndSpacer.parent = root;
root.fixedWidth = 0;
root.fixedHeight = 0;
}
onDrop: {
plasmoid.processMimeData(event.mimeData, event.x, event.y);
event.accept(event.proposedAction);
root.fixedWidth = 0;
root.fixedHeight = 0;
containmentSizeSyncTimer.restart();
}
Containment.onAppletAdded: {
addApplet(applet, x, y);
checkLastSpacer();
LayoutManager.save();
}
Containment.onAppletRemoved: {
LayoutManager.removeApplet(applet);
checkLastSpacer();
LayoutManager.save();
}
Plasmoid.onUserConfiguringChanged: {
if (plasmoid.immutable) {
if (dragOverlay) {
dragOverlay.destroy();
}
return;
}
if (plasmoid.userConfiguring) {
for (var i = 0; i < plasmoid.applets.length; ++i) {
plasmoid.applets[i].expanded = false;
}
if (!dragOverlay) {
var component = Qt.createComponent("ConfigOverlay.qml");
if (component.status === Component.Ready) {
dragOverlay = component.createObject(root);
} else {
console.log("Could not create ConfigOverlay:", component.errorString());
}
component.destroy();
} else {
dragOverlay.visible = true;
}
} else {
dragOverlay.destroy();
}
}
Plasmoid.onFormFactorChanged: containmentSizeSyncTimer.restart();
Plasmoid.onImmutableChanged: containmentSizeSyncTimer.restart();
onToolBoxChanged: {
containmentSizeSyncTimer.restart();
if (startupTimer.running) {
startupTimer.restart();
}
}
//END connections
//BEGIN components
Component {
id: appletContainerComponent
// This loader conditionally manages the BusyIndicator, it's not
// loading the applet. The applet becomes a regular child item.
Loader {
id: container
visible: false
property bool animationsEnabled: true
//when the applet moves caused by its resize, don't animate.
//this is completely heuristic, but looks way less "jumpy"
property bool movingForResize: false
Layout.fillWidth: applet && applet.Layout.fillWidth
Layout.onFillWidthChanged: {
if (plasmoid.formFactor != PlasmaCore.Types.Vertical) {
checkLastSpacer();
}
}
Layout.fillHeight: applet && applet.Layout.fillHeight
Layout.onFillHeightChanged: {
if (plasmoid.formFactor == PlasmaCore.Types.Vertical) {
checkLastSpacer();
}
}
Layout.minimumWidth: (currentLayout.isLayoutHorizontal ? (applet && applet.Layout.minimumWidth > 0 ? applet.Layout.minimumWidth : root.height) : root.width)
Layout.minimumHeight: (!currentLayout.isLayoutHorizontal ? (applet && applet.Layout.minimumHeight > 0 ? applet.Layout.minimumHeight : root.width) : root.height)
Layout.preferredWidth: (currentLayout.isLayoutHorizontal ? (applet && applet.Layout.preferredWidth > 0 ? applet.Layout.preferredWidth : root.height) : root.width)
Layout.preferredHeight: (!currentLayout.isLayoutHorizontal ? (applet && applet.Layout.preferredHeight > 0 ? applet.Layout.preferredHeight : root.width) : root.height)
Layout.maximumWidth: (currentLayout.isLayoutHorizontal ? (applet && applet.Layout.maximumWidth > 0 ? applet.Layout.maximumWidth : (Layout.fillWidth ? root.width : root.height)) : root.height)
Layout.maximumHeight: (!currentLayout.isLayoutHorizontal ? (applet && applet.Layout.maximumHeight > 0 ? applet.Layout.maximumHeight : (Layout.fillHeight ? root.height : root.width)) : root.width)
property int oldX: x
property int oldY: y
property Item applet
onAppletChanged: {
if (!applet) {
destroy();
}
}
active: applet && applet.busy
sourceComponent: PlasmaComponents.BusyIndicator {}
Layout.onMinimumWidthChanged: movingForResize = true;
Layout.onMinimumHeightChanged: movingForResize = true;
Layout.onMaximumWidthChanged: movingForResize = true;
Layout.onMaximumHeightChanged: movingForResize = true;
onXChanged: {
if (movingForResize) {
movingForResize = false;
return;
}
if (!animationsEnabled) {
startupTimer.restart();
return;
}
translation.x = oldX - x
translation.y = oldY - y
translAnim.running = true
oldX = x
oldY = y
}
onYChanged: {
if (movingForResize) {
movingForResize = false;
return;
}
if (!animationsEnabled) {
startupTimer.restart();
return;
}
translation.x = oldX - x
translation.y = oldY - y
translAnim.running = true
oldX = x
oldY = y
}
transform: Translate {
id: translation
}
NumberAnimation {
id: translAnim
duration: units.longDuration
easing.type: Easing.InOutQuad
target: translation
properties: "x,y"
to: 0
}
}
}
//END components
//BEGIN UI elements
Item {
id: lastSpacer
parent: currentLayout
Layout.fillWidth: true
Layout.fillHeight: true
}
Item {
id: dndSpacer
Layout.preferredWidth: width
Layout.preferredHeight: height
width: (plasmoid.formFactor == PlasmaCore.Types.Vertical) ? currentLayout.width : theme.mSize(theme.defaultFont).width * 10
height: (plasmoid.formFactor == PlasmaCore.Types.Vertical) ? theme.mSize(theme.defaultFont).width * 10 : currentLayout.height
}
// while the user is moving the applet when configuring the panel, the applet is reparented
// here so it can be moved freely; previously it was reparented to "root" but this one does not
// take into account the toolbox (which is left-of) the layout in right-to-left languages
Item {
id: moveAppletLayer
anchors.fill: currentLayout
}
GridLayout {
id: currentLayout
property bool isLayoutHorizontal
rowSpacing: units.smallSpacing
columnSpacing: units.smallSpacing
Layout.preferredWidth: {
var width = 0;
for (var i = 0, length = currentLayout.children.length; i < length; ++i) {
var item = currentLayout.children[i];
if (item.Layout) {
width += Math.max(item.Layout.minimumWidth, item.Layout.preferredWidth);
}
}
return width;
}
Layout.preferredHeight: {
var height = 0;
for (var i = 0, length = currentLayout.children.length; i < length; ++i) {
var item = currentLayout.children[i];
if (item.Layout) {
height += Math.max(item.Layout.minimumHeight, item.Layout.preferredHeight);
}
}
return height;
}
rows: 1
columns: 1
//when horizontal layout top-to-bottom, this way it will obey our limit of one row and actually lay out left to right
flow: isHorizontal ? GridLayout.TopToBottom : GridLayout.LeftToRight
layoutDirection: Qt.application.layoutDirection
}
onWidthChanged: {
containmentSizeSyncTimer.restart()
if (startupTimer.running) {
startupTimer.restart();
}
}
onHeightChanged: {
containmentSizeSyncTimer.restart()
if (startupTimer.running) {
startupTimer.restart();
}
}
Timer {
id: containmentSizeSyncTimer
interval: 150
onTriggered: {
dndSpacer.parent = root;
currentLayout.x = (isHorizontal && toolBox && Qt.application.layoutDirection === Qt.RightToLeft && !plasmoid.immutable) ? toolBox.width : 0;
currentLayout.y = 0
currentLayout.width = root.width - (isHorizontal && toolBox && !plasmoid.immutable ? toolBox.width : 0)
currentLayout.height = root.height - (!isHorizontal && toolBox && !plasmoid.immutable ? toolBox.height : 0)
currentLayout.isLayoutHorizontal = isHorizontal
}
}
//FIXME: I don't see other ways at the moment a way to see when the UI is REALLY ready
Timer {
id: startupTimer
interval: 4000
onTriggered: {
for (var i = 0; i < currentLayout.children.length; ++i) {
var item = currentLayout.children[i];
if (item.hasOwnProperty("animationsEnabled")) {
item.animationsEnabled = true;
}
}
}
}
//END UI elements
}
diff --git a/desktoppackage/contents/configuration/AppletConfiguration.qml b/desktoppackage/contents/configuration/AppletConfiguration.qml
index 5ca93a0f4..9ada1121b 100644
--- a/desktoppackage/contents/configuration/AppletConfiguration.qml
+++ b/desktoppackage/contents/configuration/AppletConfiguration.qml
@@ -1,431 +1,431 @@
/*
* Copyright 2013 Marco Martin
*
* 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 2.010-1301, USA.
*/
import QtQuick 2.0
import QtQuick.Dialogs 1.1
import QtQuick.Controls 1.3 as QtControls
import QtQuick.Layouts 1.0
import QtQuick.Window 2.2
import org.kde.plasma.core 2.1 as PlasmaCore
import org.kde.plasma.configuration 2.0
//TODO: all of this will be done with desktop components
Rectangle {
id: root
Layout.minimumWidth: units.gridUnit * 30
Layout.minimumHeight: units.gridUnit * 20
LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
LayoutMirroring.childrenInherit: true
//BEGIN properties
color: syspal.window
width: units.gridUnit * 40
height: units.gridUnit * 30
property bool isContainment: false
//END properties
//BEGIN model
property ConfigModel globalConfigModel: globalAppletConfigModel
ConfigModel {
id: globalAppletConfigModel
ConfigCategory {
name: i18nd("plasma_shell_org.kde.plasma.desktop", "Keyboard shortcuts")
icon: "preferences-desktop-keyboard"
source: "ConfigurationShortcuts.qml"
}
}
PlasmaCore.SortFilterModel {
id: configDialogFilterModel
sourceModel: configDialog.configModel
filterRole: "visible"
filterCallback: function(source_row, value) { return value; }
}
//END model
//BEGIN functions
function saveConfig() {
if (main.currentItem.saveConfig) {
main.currentItem.saveConfig()
}
for (var key in plasmoid.configuration) {
if (main.currentItem["cfg_"+key] !== undefined) {
plasmoid.configuration[key] = main.currentItem["cfg_"+key]
}
}
}
function settingValueChanged() {
applyButton.enabled = true;
}
//END functions
//BEGIN connections
Component.onCompleted: {
if (!isContainment && configDialog.configModel && configDialog.configModel.count > 0) {
if (configDialog.configModel.get(0).source) {
main.sourceFile = configDialog.configModel.get(0).source
} else if (configDialog.configModel.get(0).kcm) {
main.sourceFile = Qt.resolvedUrl("ConfigurationKcmPage.qml");
main.currentItem.kcm = configDialog.configModel.get(0).kcm;
} else {
main.sourceFile = "";
}
main.title = configDialog.configModel.get(0).name
} else {
main.sourceFile = globalConfigModel.get(0).source
main.title = globalConfigModel.get(0).name
}
// root.width = mainColumn.implicitWidth
// root.height = mainColumn.implicitHeight
}
//END connections
//BEGIN UI components
SystemPalette {id: syspal}
MessageDialog {
id: messageDialog
icon: StandardIcon.Warning
property Item delegate
title: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply Settings")
text: i18nd("plasma_shell_org.kde.plasma.desktop", "The settings of the current module have changed. Do you want to apply the changes or discard them?")
standardButtons: StandardButton.Apply | StandardButton.Discard | StandardButton.Cancel
onApply: {
applyAction.trigger()
delegate.openCategory()
}
onDiscard: {
delegate.openCategory()
}
}
ColumnLayout {
id: mainColumn
anchors {
fill: parent
margins: mainColumn.spacing * units.devicePixelRatio //margins are hardcoded in QStyle we should match that here
}
property int implicitWidth: Math.max(contentRow.implicitWidth, buttonsRow.implicitWidth) + 8
property int implicitHeight: contentRow.implicitHeight + buttonsRow.implicitHeight + 8
RowLayout {
id: contentRow
anchors {
left: parent.left
right: parent.right
}
spacing: 0
Layout.fillHeight: true
Layout.preferredHeight: parent.height - buttonsRow.height
QtControls.ScrollView {
id: categoriesScroll
frameVisible: false
horizontalScrollBarPolicy: Qt.ScrollBarAlwaysOff
Layout.fillHeight: true
visible: (configDialog.configModel ? configDialog.configModel.count : 0) + globalConfigModel.count > 1
Layout.maximumWidth: units.gridUnit * 7
Layout.preferredWidth: categories.implicitWidth
flickableItem.interactive: false
Keys.onUpPressed: {
var buttons = categories.children
var foundPrevious = false
for (var i = buttons.length - 1; i >= 0; --i) {
var button = buttons[i];
if (!button.hasOwnProperty("current")) {
// not a ConfigCategoryDelegate
continue;
}
if (foundPrevious) {
button.openCategory()
return
} else if (button.current) {
foundPrevious = true
}
}
}
Keys.onDownPressed: {
var buttons = categories.children
var foundNext = false
for (var i = 0, length = buttons.length; i < length; ++i) {
var button = buttons[i];
console.log(button)
if (!button.hasOwnProperty("current")) {
continue;
}
if (foundNext) {
button.openCategory()
return
} else if (button.current) {
foundNext = true
}
}
}
ColumnLayout {
id: categories
spacing: 0
width: categoriesScroll.viewport.width
property Item currentItem: children[1]
Repeater {
model: root.isContainment ? globalConfigModel : undefined
delegate: ConfigCategoryDelegate {}
}
Repeater {
model: configDialogFilterModel
delegate: ConfigCategoryDelegate {}
}
Repeater {
model: !root.isContainment ? globalConfigModel : undefined
delegate: ConfigCategoryDelegate {}
}
}
}
Rectangle {
Layout.fillHeight: true
width: Math.round(units.devicePixelRatio)
color: syspal.highlight
visible: categoriesScroll.visible
opacity: categoriesScroll.activeFocus && Window.active ? 1 : 0.3
Behavior on color {
ColorAnimation {
duration: units.longDuration
easing.type: Easing.InOutQuad
}
}
}
Item { // spacer
width: units.largeSpacing
visible: categoriesScroll.visible
}
QtControls.ScrollView {
id: scroll
Layout.fillHeight: true
Layout.fillWidth: true
// we want to focus the controls in the settings page right away, don't focus the ScrollView
activeFocusOnTab: false
// this horrible code below ensures the control with active focus stays visible in the window
// by scrolling the view up or down as needed when tabbing through the window
Window.onActiveFocusItemChanged: {
var flickable = scroll.flickableItem;
var item = Window.activeFocusItem;
if (!item) {
return;
}
- // when an item withing ScrollView has active focus the ScrollView,
+ // when an item within ScrollView has active focus the ScrollView,
// as FocusScope, also has it, so we only scroll in this case
if (!scroll.activeFocus) {
return;
}
var padding = units.gridUnit * 2 // some padding to the top/bottom when we scroll
var yPos = item.mapToItem(scroll.contentItem, 0, 0).y;
if (yPos < flickable.contentY) {
flickable.contentY = Math.max(0, yPos - padding);
// The "Math.min(padding, item.height)" ensures that we only scroll the item into view
// when it's barely visible. The logic was mostly meant for keyboard navigating through
// a list of CheckBoxes, so this check keeps us from trying to scroll an inner ScrollView
// into view when it implicitly gains focus (like plasma-pa config dialog has).
} else if (yPos + Math.min(padding, item.height) > flickable.contentY + flickable.height) {
flickable.contentY = Math.min(flickable.contentHeight - flickable.height,
yPos - flickable.height + item.height + padding);
}
}
Column {
spacing: units.largeSpacing / 2
QtControls.Label {
id: pageTitle
width: scroll.viewport.width
font.pointSize: theme.defaultFont.pointSize*2
font.weight: Font.Light
text: main.title
}
QtControls.StackView {
id: main
property string title: ""
property bool invertAnimations: false
height: Math.max((scroll.viewport.height - pageTitle.height - parent.spacing), (main.currentItem ? (main.currentItem.implicitHeight ? main.currentItem.implicitHeight : main.currentItem.childrenRect.height) : 0))
width: scroll.viewport.width
property string sourceFile
onSourceFileChanged: {
if (!sourceFile) {
return;
}
var props = {}
var plasmoidConfig = plasmoid.configuration
for (var key in plasmoidConfig) {
props["cfg_" + key] = plasmoid.configuration[key]
}
var newItem = push({
item: Qt.resolvedUrl(sourceFile),
replace: true,
properties: props
})
for (var key in plasmoidConfig) {
var changedSignal = newItem["cfg_" + key + "Changed"]
if (changedSignal) {
changedSignal.connect(root.settingValueChanged)
}
}
var configurationChangedSignal = newItem.configurationChanged
if (configurationChangedSignal) {
configurationChangedSignal.connect(root.settingValueChanged)
}
applyButton.enabled = false;
scroll.flickableItem.contentY = 0
/*
* This is not needed on a desktop shell that has ok/apply/cancel buttons, i'll leave it here only for future reference until we have a prototype for the active shell.
* root.pageChanged will start a timer, that in turn will call saveConfig() when triggered
for (var prop in currentItem) {
if (prop.indexOf("cfg_") === 0) {
currentItem[prop+"Changed"].connect(root.pageChanged)
}
}*/
}
delegate: QtControls.StackViewDelegate {
function transitionFinished(properties)
{
properties.exitItem.opacity = 1
}
pushTransition: QtControls.StackViewTransition {
PropertyAnimation {
target: enterItem
property: "opacity"
from: 0
to: 1
duration: units.longDuration
easing.type: Easing.InOutQuad
}
PropertyAnimation {
target: enterItem
property: "x"
from: main.invertAnimations ? -target.width/3: target.width/3
to: 0
duration: units.longDuration
easing.type: Easing.InOutQuad
}
PropertyAnimation {
target: exitItem
property: "opacity"
from: 1
to: 0
duration: units.longDuration
easing.type: Easing.InOutQuad
}
PropertyAnimation {
target: exitItem
property: "x"
from: 0
to: main.invertAnimations ? target.width/3 : -target.width/3
duration: units.longDuration
easing.type: Easing.InOutQuad
}
}
}
}
}
}
}
QtControls.Action {
id: acceptAction
onTriggered: {
applyAction.trigger();
configDialog.close();
}
shortcut: "Return"
}
QtControls.Action {
id: applyAction
onTriggered: {
root.saveConfig();
applyButton.enabled = false;
}
}
QtControls.Action {
id: cancelAction
onTriggered: configDialog.close();
shortcut: "Escape"
}
RowLayout {
id: buttonsRow
Layout.alignment: Qt.AlignVCenter | Qt.AlignRight
QtControls.Button {
iconName: "dialog-ok"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "OK")
onClicked: acceptAction.trigger()
}
QtControls.Button {
id: applyButton
enabled: false
iconName: "dialog-ok-apply"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Apply")
visible: main.currentItem && (!main.currentItem.kcm || main.currentItem.kcm.buttons & 4) // 4 = Apply button
onClicked: applyAction.trigger()
}
QtControls.Button {
iconName: "dialog-cancel"
text: i18nd("plasma_shell_org.kde.plasma.desktop", "Cancel")
onClicked: cancelAction.trigger()
}
}
}
//END UI components
}
diff --git a/imports/activitymanager/sortedactivitiesmodel.h b/imports/activitymanager/sortedactivitiesmodel.h
index b51088592..a262e5647 100644
--- a/imports/activitymanager/sortedactivitiesmodel.h
+++ b/imports/activitymanager/sortedactivitiesmodel.h
@@ -1,94 +1,94 @@
/*
* Copyright (C) 2016 Ivan Cukic
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2,
* or (at your option) any later version, as published by the Free
* Software Foundation
*
* 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 SORTED_ACTIVITY_MODEL
-#define SORTED_ACTIVITY_MODEL
+#ifndef SORTED_ACTIVITIES_MODEL_H
+#define SORTED_ACTIVITIES_MODEL_H
// Qt
#include
#include //For WId
// KDE
#include
#include
#include
#include
#include
class SortedActivitiesModel : public QSortFilterProxyModel {
Q_OBJECT
Q_PROPERTY(bool inhibitUpdates READ inhibitUpdates WRITE setInhibitUpdates NOTIFY inhibitUpdatesChanged)
public:
SortedActivitiesModel(const QVector &states, QObject *parent = nullptr);
~SortedActivitiesModel() override;
QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override;
QHash roleNames() const override;
QString relativeActivity(int relative) const;
protected:
uint lastUsedTime(const QString &activity) const;
bool lessThan(const QModelIndex & source_left, const QModelIndex & source_right) const override;
enum AdditionalRoles {
LastTimeUsed = KActivities::ActivitiesModel::UserRole,
LastTimeUsedString = KActivities::ActivitiesModel::UserRole + 1,
WindowCount = KActivities::ActivitiesModel::UserRole + 2,
HasWindows = KActivities::ActivitiesModel::UserRole + 3
};
public Q_SLOTS:
bool inhibitUpdates() const;
void setInhibitUpdates(bool sortByLastUsedTime);
void onBackgroundsUpdated(const QStringList &changedBackgrounds);
void onCurrentActivityChanged(const QString ¤tActivity);
QString activityIdForRow(int row) const;
QString activityIdForIndex(const QModelIndex &index) const;
int rowForActivityId(const QString &activity) const;
void rowChanged(int row, const QVector &roles);
void onWindowAdded(WId window);
void onWindowRemoved(WId window);
void onWindowChanged(WId window, NET::Properties properties, NET::Properties2 properties2);
Q_SIGNALS:
void inhibitUpdatesChanged(bool inhibitUpdates);
private:
bool m_inhibitUpdates;
QString m_previousActivity;
KActivities::ActivitiesModel *m_activitiesModel = nullptr;
KActivities::Consumer *m_activities = nullptr;
QHash> m_activitiesWindows;
};
-#endif // SORTED_ACTIVITY_MODEL
+#endif // SORTED_ACTIVITIES_MODEL_H
diff --git a/kcms/activities/ExtraActivitiesInterface.h b/kcms/activities/ExtraActivitiesInterface.h
index 92ded41b6..2e3abe896 100644
--- a/kcms/activities/ExtraActivitiesInterface.h
+++ b/kcms/activities/ExtraActivitiesInterface.h
@@ -1,50 +1,50 @@
/*
* Copyright (C) 2015 - 2016 by Ivan Cukic
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef EXTRA_ACTIVITES_INTERFACE_H
-#define EXTRA_ACTIVITES_INTERFACE_H
+#ifndef EXTRA_ACTIVITIES_INTERFACE_H
+#define EXTRA_ACTIVITIES_INTERFACE_H
#include
#include
#include
#include
class ExtraActivitiesInterface : public QObject {
Q_OBJECT
public:
explicit ExtraActivitiesInterface(QObject *parent = nullptr);
~ExtraActivitiesInterface();
public Q_SLOTS:
void setIsPrivate(const QString &activity, bool isPrivate,
QJSValue callback);
void getIsPrivate(const QString &activity, QJSValue callback);
void setShortcut(const QString &activity, const QKeySequence &keySequence);
QKeySequence shortcut(const QString &activity);
private:
D_PTR;
};
-#endif // EXTRA_ACTIVITES_INTERFACE_H
+#endif // EXTRA_ACTIVITIES_INTERFACE_H
diff --git a/kcms/activities/utils/optional_view.h b/kcms/activities/utils/optional_view.h
index 72c613881..004ebb2a9 100644
--- a/kcms/activities/utils/optional_view.h
+++ b/kcms/activities/utils/optional_view.h
@@ -1,79 +1,79 @@
/*
* Copyright (C) 2015 - 2016 by Ivan Cukic
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License or (at your option) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-#ifndef UTILS_OPTIONAL_H
-#define UTILS_OPTIONAL_H
+#ifndef UTILS_OPTIONAL_VIEW_H
+#define UTILS_OPTIONAL_VIEW_H
namespace kamd {
namespace utils {
struct none_t {};
inline const none_t none() { return none_t(); }
// A simple implementation of the optional class
// until we can rely on std::optional.
// It is not going to come close in the supported
// features to the std one.
// (we need it in the core library, so we don't
// want to use boost.optional)
template
class optional_view {
public:
explicit optional_view(const T &value)
: m_value(&value)
{
}
optional_view(const none_t &)
: m_value(nullptr)
{
}
bool is_initialized() const
{
return m_value != nullptr;
}
const T &get() const
{
return *m_value;
}
const T *operator->() const
{
return m_value;
}
private:
const T *const m_value;
};
template
optional_view make_optional_view(const T &value)
{
return optional_view(value);
}
} // namespace utils
} // namespace kamd
-#endif // UTILS_OPTIONAL_H
+#endif // UTILS_OPTIONAL_VIEW_H
diff --git a/kcms/baloo/folderselectionwidget.cpp b/kcms/baloo/folderselectionwidget.cpp
index 76caa8b04..e8a4c5ebd 100644
--- a/kcms/baloo/folderselectionwidget.cpp
+++ b/kcms/baloo/folderselectionwidget.cpp
@@ -1,355 +1,355 @@
/*
* This file is part of the KDE Baloo Project
* Copyright (C) 2014 Vishesh Handa
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "folderselectionwidget.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
FolderSelectionWidget::FolderSelectionWidget(QWidget* parent, Qt::WindowFlags f)
: QWidget(parent, f)
{
m_listWidget = new QListWidget(this);
m_listWidget->setAlternatingRowColors(true);
m_messageWidget = new KMessageWidget(this);
m_messageWidget->hide();
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(m_messageWidget);
layout->addWidget(m_listWidget);
QHBoxLayout* hLayout = new QHBoxLayout;
QSpacerItem* spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
hLayout->addItem(spacer);
m_addButton = new QPushButton(this);
m_addButton->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
connect(m_addButton, &QPushButton::clicked, this, &FolderSelectionWidget::slotAddButtonClicked);
m_removeButton = new QPushButton(this);
m_removeButton->setIcon(QIcon::fromTheme(QStringLiteral("list-remove")));
m_removeButton->setEnabled(false);
connect(m_removeButton, &QPushButton::clicked, this, &FolderSelectionWidget::slotRemoveButtonClicked);
connect(m_listWidget, &QListWidget::currentItemChanged, this, &FolderSelectionWidget::slotCurrentItemChanged);
hLayout->addWidget(m_addButton);
hLayout->addWidget(m_removeButton);
layout->addItem(hLayout);
}
namespace {
QStringList addTrailingSlashes(const QStringList& input) {
QStringList output;
Q_FOREACH (QString str, input) {
if (!str.endsWith(QDir::separator()))
str.append(QDir::separator());
output << str;
}
return output;
}
QString makeHomePretty(const QString& url) {
if (url.startsWith(QDir::homePath()))
return QString(url).replace(0, QDir::homePath().length(), QStringLiteral("~"));
return url;
}
}
void FolderSelectionWidget::setDirectoryList(QStringList includeDirs, QStringList exclude)
{
m_listWidget->clear();
m_mountPoints.clear();
QList devices
= Solid::Device::listFromType(Solid::DeviceInterface::StorageAccess);
Q_FOREACH (const Solid::Device& dev, devices) {
const Solid::StorageAccess* sa = dev.as();
if (!sa->isAccessible())
continue;
QString mountPath = sa->filePath();
if (!shouldShowMountPoint(mountPath))
continue;
m_mountPoints << mountPath;
}
m_mountPoints << QDir::homePath();
m_mountPoints = addTrailingSlashes(m_mountPoints);
includeDirs = addTrailingSlashes(includeDirs);
exclude = addTrailingSlashes(exclude);
QStringList excludeList = exclude;
Q_FOREACH (const QString& mountPath, m_mountPoints) {
if (includeDirs.contains(mountPath))
continue;
if (exclude.contains(mountPath))
continue;
if (!excludeList.contains(mountPath)) {
excludeList << mountPath;
}
}
Q_FOREACH (QString url, excludeList) {
QListWidgetItem* item = new QListWidgetItem(m_listWidget);
QString display = folderDisplayName(url);
item->setData(Qt::DisplayRole, display);
item->setData(Qt::WhatsThisRole, url);
item->setData(UrlRole, url);
item->setData(Qt::DecorationRole, QIcon::fromTheme(iconName(url)));
item->setToolTip(makeHomePretty(url));
m_listWidget->addItem(item);
}
if (m_listWidget->count() == 0) {
m_removeButton->setEnabled(false);
}
}
QStringList FolderSelectionWidget::includeFolders() const
{
QStringList folders;
Q_FOREACH (const QString& mountPath, m_mountPoints) {
bool inExclude = false;
for (int i=0; icount(); ++i) {
QListWidgetItem* item = m_listWidget->item(i);
QString url = item->data(UrlRole).toString();
if (mountPath == url) {
inExclude = true;
break;
}
}
if (!inExclude && !folders.contains(mountPath)) {
folders << mountPath;
}
}
return folders;
}
QStringList FolderSelectionWidget::excludeFolders() const
{
QStringList folders;
for (int i=0; icount(); ++i) {
QListWidgetItem* item = m_listWidget->item(i);
QString url = item->data(UrlRole).toString();
if (!folders.contains(url)) {
folders << url;
}
}
return folders;
}
QString FolderSelectionWidget::fetchMountPoint(const QString& url) const
{
QString mountPoint;
Q_FOREACH (const QString& mount, m_mountPoints) {
if (url.startsWith(mount) && mount.size() > mountPoint.size())
mountPoint = mount;
}
return mountPoint;
}
void FolderSelectionWidget::slotAddButtonClicked()
{
QString url = QFileDialog::getExistingDirectory(this, i18n("Select the folder which should be excluded"));
if (url.isEmpty()) {
return;
}
if (!url.endsWith(QDir::separator()))
url.append(QDir::separator());
// We don't care about the root dir
if (url == QLatin1String("/")) {
showMessage(i18n("Not allowed to exclude root folder, please disable File Search if you do not want it"));
return;
}
// Remove any existing folder with that name
// Remove any folder which is a sub-folder
QVector deleteList;
for (int i=0; icount(); ++i) {
QListWidgetItem* item = m_listWidget->item(i);
QString existingUrl = item->data(UrlRole).toString();
if (existingUrl == url) {
QString name = QUrl::fromLocalFile(url).fileName();
showMessage(i18n("Folder %1 is already excluded", name));
deleteList << item;
continue;
}
QString existingMountPoint = fetchMountPoint(existingUrl);
QString mountPoint = fetchMountPoint(url);
if (existingMountPoint == mountPoint) {
// existingUrl is not required since it comes under url
if (existingUrl.startsWith(url)) {
deleteList << item;
}
else if (url.startsWith(existingUrl)) {
// No point adding ourselves since our parents exists
// we just move the parent to the bottom
url = existingUrl;
deleteList << item;
QString name = QUrl::fromLocalFile(url).adjusted(QUrl::StripTrailingSlash).fileName();
showMessage(i18n("Folder's parent %1 is already excluded", name));
}
}
}
qDeleteAll(deleteList);
QListWidgetItem* item = new QListWidgetItem(m_listWidget);
QString displayName = folderDisplayName(url);
item->setData(Qt::DisplayRole, displayName);
item->setData(Qt::WhatsThisRole, url);
item->setData(UrlRole, url);
item->setData(Qt::DecorationRole, QIcon::fromTheme(iconName(url)));
item->setToolTip(makeHomePretty(url));
m_listWidget->addItem(item);
m_listWidget->setCurrentItem(item);
Q_EMIT changed();
}
void FolderSelectionWidget::slotRemoveButtonClicked()
{
QListWidgetItem* item = m_listWidget->currentItem();
delete item;
Q_EMIT changed();
}
void FolderSelectionWidget::slotCurrentItemChanged(QListWidgetItem* current, QListWidgetItem*)
{
m_removeButton->setEnabled(current != 0);
}
void FolderSelectionWidget::showMessage(const QString& message)
{
m_messageWidget->setText(message);
m_messageWidget->setMessageType(KMessageWidget::Warning);
m_messageWidget->animatedShow();
QTimer::singleShot(3000, m_messageWidget, &KMessageWidget::animatedHide);
}
QString FolderSelectionWidget::folderDisplayName(const QString& url) const
{
QString name = url;
// Check Home Dir
QString homePath = QDir::homePath() + QLatin1Char('/');
if (url == homePath) {
return QDir(homePath).dirName();
}
if (url.startsWith(homePath)) {
name = url.mid(homePath.size());
}
else {
// Check Mount allMountPointsExcluded
Q_FOREACH (QString mountPoint, m_mountPoints) {
if (url.startsWith(mountPoint)) {
name = QLatin1Char('[') + QDir(mountPoint).dirName() + QStringLiteral("]/") + url.mid(mountPoint.length());
break;
}
}
}
if (name.endsWith(QLatin1Char('/'))) {
name = name.mid(0, name.size() - 1);
}
return name;
}
bool FolderSelectionWidget::shouldShowMountPoint(const QString& mountPoint)
{
if (mountPoint == QLatin1String("/"))
return false;
if (mountPoint.startsWith(QLatin1String("/boot")))
return false;
if (mountPoint.startsWith(QLatin1String("/tmp")))
return false;
// The user's home directory is forcibly added so we can ignore /home
- // if /home actually contains the home direcory
+ // if /home actually contains the home directory
if (mountPoint.startsWith(QLatin1String("/home")) && QDir::homePath().startsWith(QLatin1String("/home")))
return false;
return true;
}
QString FolderSelectionWidget::iconName(QString path) const
{
// Ensure paths end with /
if (!path.endsWith(QDir::separator()))
path.append(QDir::separator());
QString homePath = QDir::homePath();
if (!homePath.endsWith(QDir::separator()))
homePath.append(QDir::separator());
if (path == homePath)
return QStringLiteral("user-home");
if (m_mountPoints.contains(path))
return QStringLiteral("drive-harddisk");
return QStringLiteral("folder");
}
diff --git a/kcms/colors/previewwidget.h b/kcms/colors/previewwidget.h
index 0657c68a6..473a9ed7e 100644
--- a/kcms/colors/previewwidget.h
+++ b/kcms/colors/previewwidget.h
@@ -1,49 +1,49 @@
/* Preview widget for KDE Display color scheme setup module
* Copyright (C) 2007 Matthew Woehlke
*
* 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
-#ifndef __SCHEMEPREVIEW_H__
-#define __SCHEMEPREVIEW_H__
+#ifndef __PREVIEWWIDGET_H__
+#define __PREVIEWWIDGET_H__
#include
#include
#include
#include "ui_preview.h"
/**
* The Desktop/Colors tab in kcontrol.
*/
class PreviewWidget : public QFrame, Ui::preview
{
Q_OBJECT
public:
PreviewWidget(QWidget *parent);
~PreviewWidget() override;
void setPalette(const KSharedConfigPtr &config,
QPalette::ColorGroup state = QPalette::Active);
protected:
void setPaletteRecursive(QWidget*, const QPalette&);
bool eventFilter(QObject *, QEvent *) override;
};
#endif
diff --git a/kcms/colors/setpreviewwidget.h b/kcms/colors/setpreviewwidget.h
index 557760bd5..5e44b830f 100644
--- a/kcms/colors/setpreviewwidget.h
+++ b/kcms/colors/setpreviewwidget.h
@@ -1,49 +1,49 @@
/* Colorset Preview widget for KDE Display color scheme setup module
* Copyright (C) 2008 Matthew Woehlke
*
* 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
-#ifndef __SCHEMEPREVIEWSET_H__
-#define __SCHEMEPREVIEWSET_H__
+#ifndef __SETPREVIEWWIDGET_H__
+#define __SETPREVIEWWIDGET_H__
#include
#include
#include
#include
#include "ui_setpreview.h"
/**
* The Desktop/Colors tab in kcontrol.
*/
class SetPreviewWidget : public QFrame, Ui::setpreview
{
Q_OBJECT
public:
explicit SetPreviewWidget(QWidget *parent);
~SetPreviewWidget() override;
void setPalette(const KSharedConfigPtr &config, KColorScheme::ColorSet);
protected:
void setPaletteRecursive(QWidget*, const QPalette&);
bool eventFilter(QObject *, QEvent *) override;
};
#endif
diff --git a/kcms/cursortheme/kcmcursortheme.h b/kcms/cursortheme/kcmcursortheme.h
index 3035297a9..f36975ed0 100644
--- a/kcms/cursortheme/kcmcursortheme.h
+++ b/kcms/cursortheme/kcmcursortheme.h
@@ -1,140 +1,140 @@
/*
* Copyright © 2003-2007 Fredrik Höglund
*
* 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 KCMCURSORTHEME_H
#define KCMCURSORTHEME_H
#include
#include
class QStandardItemModel;
class QTemporaryFile;
class CursorThemeModel;
class SortProxyModel;
class CursorTheme;
class CursorThemeConfig : public KQuickAddons::ConfigModule
{
Q_OBJECT
Q_PROPERTY(bool canInstall READ canInstall WRITE setCanInstall NOTIFY canInstallChanged)
Q_PROPERTY(bool canResize READ canResize WRITE setCanResize NOTIFY canResizeChanged)
Q_PROPERTY(bool canConfigure READ canConfigure WRITE setCanConfigure NOTIFY canConfigureChanged)
Q_PROPERTY(QAbstractItemModel *cursorsModel READ cursorsModel CONSTANT)
Q_PROPERTY(QAbstractItemModel *sizesModel READ sizesModel CONSTANT)
Q_PROPERTY(int selectedThemeRow READ selectedThemeRow WRITE setSelectedThemeRow NOTIFY selectedThemeRowChanged)
Q_PROPERTY(int preferredSize READ preferredSize WRITE setPreferredSize NOTIFY preferredSizeChanged)
public:
CursorThemeConfig(QObject *parent, const QVariantList &);
~CursorThemeConfig();
public:
void load() override;
void save() override;
void defaults() override;
//for QML properties
bool canInstall() const;
void setCanInstall(bool can);
bool canResize() const;
void setCanResize(bool can);
bool canConfigure() const;
void setCanConfigure(bool can);
int selectedThemeRow() const;
void setSelectedThemeRow(int row);
/** @returns 0 if in the UI "automatic size" is selected, otherwise
returns the custom size. */
int preferredSize() const;
void setPreferredSize(int size);
QAbstractItemModel *cursorsModel();
QAbstractItemModel *sizesModel();
Q_SIGNALS:
void canInstallChanged();
void canResizeChanged();
void canConfigureChanged();
void selectedThemeRowChanged();
void preferredSizeChanged();
void showSuccessMessage(const QString &message);
void showInfoMessage(const QString &message);
void showErrorMessage(const QString &message);
public Q_SLOTS:
void getNewClicked();
void installThemeFromFile(const QUrl &url);
void removeTheme(int row);
private Q_SLOTS:
void selectionChanged();
/** Updates the size combo box. It loads the size list of the selected cursor
theme with the corresponding icons and chooses an appropriate entry. It
enables the combo box and the label if the theme provides more than one
size, otherwise it disables it. If the size setting is looked in kiosk
mode, it stays always disabled. */
void updateSizeComboBox();
private:
QModelIndex selectedIndex() const;
void installThemeFile(const QString &path);
/** Applies a given theme, using XFixes, XCursor and KGlobalSettings.
@param theme The cursor theme to be applied. It is save to pass 0 here
(will result in \e false as return value).
@param size The size hint that is used to select the cursor size.
@returns If the changes could be applied. Will return \e false if \e theme is
0 or if the XFixes and XCursor libraries aren't available in the required
version, otherwise returns \e true. */
bool applyTheme(const CursorTheme *theme, const int size);
bool iconsIsWritable() const;
CursorThemeModel *m_model;
SortProxyModel *m_proxyModel;
QStandardItemModel *m_sizesModel;
int m_appliedSize;
// This index refers to the CursorThemeModel, not the proxy or the view
QPersistentModelIndex m_appliedIndex;
-/** Holds the last size that was choosen by the user. Example: The user chooses
+/** Holds the last size that was chosen by the user. Example: The user chooses
theme1 which provides the sizes 24 and 36. He chooses 36. preferredSize gets
set to 36. Now, he switchs to theme2 which provides the sizes 30 and 40.
preferredSize still is 36, so the UI will default to 40, which is next to 36.
Now, he chooses theme3 which provides the sizes 34 and 44. preferredSize is
still 36, so the UI defaults to 34. Now the user changes manually to 44. This
will also change preferredSize. */
int m_preferredSize;
int m_originalPreferredSize;
int m_selectedThemeRow;
int m_originalSelectedThemeRow;
bool m_canInstall;
bool m_canResize;
bool m_canConfigure;
QScopedPointer m_tempInstallFile;
};
#endif
diff --git a/kcms/emoticons/emoticonslist.cpp b/kcms/emoticons/emoticonslist.cpp
index 1863fb12b..8b15a0da3 100644
--- a/kcms/emoticons/emoticonslist.cpp
+++ b/kcms/emoticons/emoticonslist.cpp
@@ -1,490 +1,489 @@
/***************************************************************************
* Copyright (C) 2007 by Carlo Segato *
* Copyright (C) 2008 Montel Laurent *
* *
* 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 "emoticonslist.h"
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
EditDialog::EditDialog(QWidget *parent, const QString &name)
: KDialog(parent)
{
setCaption(name);
setupDlg();
}
EditDialog::EditDialog(QWidget *parent, const QString &name, QListWidgetItem *itm, const QString &file)
: KDialog(parent)
{
setCaption(name);
emoticon = file;
setupDlg();
leText->setText(itm->text());
btnIcon->setIcon(itm->icon());
}
void EditDialog::setupDlg()
{
wdg = new QWidget(this);
QVBoxLayout *vl = new QVBoxLayout;
QHBoxLayout *hb = new QHBoxLayout;
leText = new KLineEdit(this);
btnIcon = new QPushButton(this);
btnIcon->setFixedSize(QSize(64, 64));
btnIcon->setIconSize(QSize(64, 64));
QLabel *lab = new QLabel(i18n("Insert the string for the emoticon. If you want multiple strings, separate them by spaces."), wdg);
lab->setWordWrap(true);
vl->addWidget(lab);
hb->addWidget(btnIcon);
hb->addWidget(leText);
vl->addLayout(hb);
wdg->setLayout(vl);
setMainWidget(wdg);
connect(btnIcon, &QPushButton::clicked, this, &EditDialog::btnIconClicked);
connect(leText, &KLineEdit::textChanged, this, &EditDialog::updateOkButton);
updateOkButton();
leText->setFocus();
}
void EditDialog::btnIconClicked()
{
const QUrl url = KFileDialog::getImageOpenUrl();
if (!url.isLocalFile())
return;
emoticon = url.toLocalFile();
if (emoticon.isEmpty())
return;
btnIcon->setIcon(QPixmap(emoticon));
updateOkButton();
}
void EditDialog::updateOkButton()
{
enableButtonOk(!leText->text().isEmpty() && !emoticon.isEmpty());
}
K_PLUGIN_FACTORY(EmoticonsFactory, registerPlugin();)
EmoticonList::EmoticonList(QWidget *parent, const QVariantList &args)
: KCModule(parent, args)
{
KAboutData *about = new KAboutData(QStringLiteral("kcm_emoticons"), i18n("Emoticons"), QStringLiteral("1.0"), QString(), KAboutLicense::GPL);
setAboutData(about);
// setButtons(Apply | Help);
setupUi(this);
btAdd->setIcon(QIcon::fromTheme(QStringLiteral("list-add")));
btEdit->setIcon(QIcon::fromTheme(QStringLiteral("edit-rename")));
btRemoveEmoticon->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
btNew->setIcon(QIcon::fromTheme(QStringLiteral("document-new")));
btGetNew->setIcon(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")));
btInstall->setIcon(QIcon::fromTheme(QStringLiteral("document-import")));
btRemoveTheme->setIcon(QIcon::fromTheme(QStringLiteral("edit-delete")));
connect(themeList, &QListWidget::itemSelectionChanged, this, &EmoticonList::selectTheme);
connect(themeList, &QListWidget::itemSelectionChanged, this, &EmoticonList::updateButton);
connect(btRemoveTheme, &QPushButton::clicked, this, &EmoticonList::btRemoveThemeClicked);
connect(btInstall, &QPushButton::clicked, this, &EmoticonList::installEmoticonTheme);
connect(btNew, &QPushButton::clicked, this, &EmoticonList::newTheme);
connect(btGetNew, &QPushButton::clicked, this, &EmoticonList::getNewStuff);
connect(cbStrict, &QCheckBox::clicked, this, &EmoticonList::somethingChanged);
connect(btAdd, &QPushButton::clicked, this, &EmoticonList::addEmoticon);
connect(btEdit, &QPushButton::clicked, this, &EmoticonList::editEmoticon);
connect(btRemoveEmoticon, &QPushButton::clicked, this, &EmoticonList::btRemoveEmoticonClicked);
connect(emoList, &QListWidget::itemSelectionChanged, this, &EmoticonList::updateButton);
connect(emoList, &QListWidget::itemDoubleClicked, this, &EmoticonList::editEmoticon);
}
EmoticonList::~EmoticonList()
{
}
bool caseInsensitiveLessThan(const QString &s1, const QString &s2)
{
return (QString::localeAwareCompare(s1, s2) < 0);
}
void EmoticonList::load()
{
delFiles.clear();
themeList->clear();
emoMap.clear();
emoList->clear();
QStringList themeList = kEmoticons.themeList();
qSort(themeList.begin(), themeList.end(), caseInsensitiveLessThan);
for (int i = 0; i < themeList.count(); i++) {
loadTheme(themeList.at(i));
}
if (kEmoticons.parseMode() & KEmoticonsTheme::StrictParse) {
cbStrict->setChecked(true);
} else {
cbStrict->setChecked(false);
}
updateButton();
emit changed(false);
}
void EmoticonList::save()
{
for (int i = 0; i < delFiles.size(); i++) {
KIO::NetAccess::del(QUrl(delFiles.at(i)), this);
}
foreach (KEmoticonsTheme t, emoMap) {
t.save();
}
if (themeList->currentItem()) {
kEmoticons.setTheme(themeList->currentItem()->text());
}
if (cbStrict->isChecked()) {
kEmoticons.setParseMode((kEmoticons.parseMode() |= KEmoticonsTheme::StrictParse) &= ~KEmoticonsTheme::RelaxedParse);
} else {
kEmoticons.setParseMode((kEmoticons.parseMode() |= KEmoticonsTheme::RelaxedParse) &= ~KEmoticonsTheme::StrictParse);
}
}
void EmoticonList::somethingChanged()
{
emit changed(true);
}
void EmoticonList::updateButton()
{
const bool can = canEditTheme();
btRemoveEmoticon->setEnabled(themeList->currentItem() && emoList->selectedItems().size() && can);
btRemoveTheme->setEnabled(themeList->currentItem() && themeList->currentItem()->text() != QLatin1String("Glass") && themeList->count() > 1 && can);
btEdit->setEnabled(themeList->currentItem() && emoList->selectedItems().size() && can);
btAdd->setEnabled(themeList->currentItem() && can);
}
void EmoticonList::selectTheme()
{
qDebug() << "current_item: " << themeList->currentItem();
updateButton();
if (!themeList->currentItem()) {
emoList->clear();
return;
}
if (!themeList->currentItem()) {
themeList->currentItem()->setSelected(true);
return;
}
emoList->clear();
KEmoticonsTheme em = emoMap.value(themeList->currentItem()->text());
QHash::const_iterator it = em.emoticonsMap().constBegin();
for (; it != em.emoticonsMap().constEnd(); ++it) {
QString text;
if (it.value().size()) {
text = it.value().at(0);
for (int i = 1; i < it.value().size(); i++) {
text += QLatin1Char(' ') + it.value().at(i);
}
}
new QListWidgetItem(QIcon(it.key()), text, emoList);
}
emit changed();
}
void EmoticonList::btRemoveThemeClicked()
{
if (!themeList->currentItem()) {
return;
}
const QString name = themeList->currentItem()->text();
delFiles.append(KStandardDirs::locate("emoticons", name + QDir::separator()));
delete themeList->currentItem();
emoMap.remove(name);
emit changed();
}
void EmoticonList::installEmoticonTheme()
{
const QUrl themeURL = KUrlRequesterDialog::getUrl(QUrl(), this, i18n("Drag or Type Emoticon Theme URL"));
if (themeURL.isEmpty())
return;
if (!themeURL.isLocalFile()) {
KMessageBox::queuedMessageBox(this, KMessageBox::Error, i18n("Emoticon themes must be installed from local files."),
i18n("Could Not Install Emoticon Theme"));
return;
}
const QStringList installed = kEmoticons.installTheme(themeURL.toLocalFile());
for (int i = 0; i < installed.size(); i++)
loadTheme(installed.at(i));
}
void EmoticonList::btRemoveEmoticonClicked()
{
if (!emoList->currentItem()) {
return;
}
QListWidgetItem *itm = emoList->currentItem();
KEmoticonsTheme theme = emoMap.value(themeList->currentItem()->text());
const QString fPath = theme.emoticonsMap().key(itm->text().split(QLatin1Char(' ')));
if (theme.removeEmoticon(itm->text())) {
int ret = KMessageBox::questionYesNo(this, i18n("Do you want to remove %1 too?", fPath), i18n("Delete emoticon"));
if (ret == KMessageBox::Yes) {
delFiles.append(fPath);
}
delete itm;
themeList->currentItem()->setIcon(QIcon(previewEmoticon(theme)));
emit changed();
}
}
void EmoticonList::addEmoticon()
{
if (!themeList->currentItem())
return;
EditDialog *dlg = new EditDialog(this, i18n("Add Emoticon"));
if (dlg->exec() == QDialog::Rejected) {
delete dlg;
return;
}
KEmoticonsTheme theme = emoMap.value(themeList->currentItem()->text());
if (theme.addEmoticon(dlg->getEmoticon(), dlg->getText(), KEmoticonsProvider::Copy)) {
new QListWidgetItem(QPixmap(dlg->getEmoticon()), dlg->getText(), emoList);
themeList->currentItem()->setIcon(QIcon(previewEmoticon(theme)));
emit changed();
}
delete dlg;
}
void EmoticonList::editEmoticon()
{
if (!themeList->currentItem() || !emoList->currentItem())
return;
KEmoticonsTheme theme = emoMap.value(themeList->currentItem()->text());
const QString path = theme.emoticonsMap().key(emoList->currentItem()->text().split(' '));
const QString f = QFileInfo(path).fileName();
EditDialog *dlg = new EditDialog(this, i18n("Edit Emoticon"), emoList->currentItem(), path);
if (dlg->exec() == QDialog::Rejected) {
delete dlg;
return;
}
bool copy;
QString emo = dlg->getEmoticon();
if (path != dlg->getEmoticon()) {
copy = true;
} else {
copy = false;
KStandardDirs *dir = KGlobal::dirs();
emo = dir->findResource("emoticons", themeList->currentItem()->text() + QDir::separator() + f);
if (emo.isNull())
emo = dir->findResource("emoticons", themeList->currentItem()->text() + QDir::separator() + f + QStringLiteral(".mng"));
if (emo.isNull())
emo = dir->findResource("emoticons", themeList->currentItem()->text() + QDir::separator() + f + QStringLiteral(".png"));
if (emo.isNull())
emo = dir->findResource("emoticons", themeList->currentItem()->text() + QDir::separator() + f + QStringLiteral(".gif"));
if (emo.isNull())
emo = dir->findResource("emoticons", themeList->currentItem()->text() + QDir::separator() + f + QStringLiteral(".jpg"));
if (emo.isNull())
emo = dir->findResource("emoticons", themeList->currentItem()->text() + QDir::separator() + f + QStringLiteral(".jpeg"));
if (emo.isNull()) {
delete dlg;
return;
}
}
if (theme.removeEmoticon(emoList->currentItem()->text())) {
delete emoList->currentItem();
}
if (theme.addEmoticon(emo, dlg->getText(), copy ? KEmoticonsProvider::Copy : KEmoticonsProvider::DoNotCopy)) {
new QListWidgetItem(QPixmap(emo), dlg->getText(), emoList);
}
emit changed();
delete dlg;
}
void EmoticonList::newTheme()
{
const QString name = KInputDialog::getText(i18n("New Emoticon Theme"), i18n("Enter the name of the new emoticon theme:"));
if (name.isEmpty())
return;
const QString path = KGlobal::dirs()->saveLocation("emoticons", name, false);
if (KIO::NetAccess::exists(QUrl(path), KIO::NetAccess::SourceSide, this)) {
KMessageBox::error(this, i18n("%1 theme already exists", name));
} else {
const QString constraint(QStringLiteral("(exist Library)"));
KService::List srv = KServiceTypeTrader::self()->query(QStringLiteral("KEmoticons"), constraint);
QStringList ls;
ls.reserve(srv.size());
int current = 0;
for (int i = 0; i < srv.size(); ++i) {
ls << srv.at(i)->name();
if (srv.at(i)->property(QStringLiteral("X-KDE-Priority")).toInt() > srv.at(current)->property(QStringLiteral("X-KDE-Priority")).toInt()) {
current = i;
}
}
bool ok;
const QString type = KInputDialog::getItem(i18n("New Emoticon Theme"), i18n("Choose the type of emoticon theme to create"),
ls, current, false, &ok, this);
if (ok && !type.isEmpty()) {
int index = ls.indexOf(type);
kEmoticons.newTheme(name, srv.at(index));
loadTheme(name);
}
}
}
void EmoticonList::loadTheme(const QString &name)
{
if (name.isEmpty())
return;
if (emoMap.contains(name)) {
emoMap.remove(name);
QListls = themeList->findItems(name, Qt::MatchExactly);
if (ls.size()) {
delete ls.at(0);
}
}
KEmoticonsTheme emo = kEmoticons.theme(name);
if (!emo.isNull()) {
emoMap[name] = emo;
QIcon previewIcon = QIcon(previewEmoticon(emo));
QListWidgetItem *itm = new QListWidgetItem(previewIcon, name, themeList);
if (name == kEmoticons.currentThemeName()) {
themeList->setCurrentItem(itm);
}
}
}
void EmoticonList::getNewStuff()
{
KNS3::DownloadDialog dialog(QStringLiteral("emoticons.knsrc"), this);
dialog.exec();
if (!dialog.changedEntries().isEmpty()) {
KNS3::Entry::List entries = dialog.changedEntries();
for (int i = 0; i < entries.size(); i ++) {
if (entries.at(i).status() == KNS3::Entry::Installed
&& !entries.at(i).installedFiles().isEmpty()) {
QString name = entries.at(i).installedFiles().at(0).section(QLatin1Char('/'), -2, -2);
loadTheme(name);
} else if (entries.at(i).status() == KNS3::Entry::Deleted) {
QString name = entries.at(i).uninstalledFiles().at(0).section(QLatin1Char('/'), -2, -2);
QList ls = themeList->findItems(name, Qt::MatchExactly);
if (ls.size()) {
delete ls.at(0);
emoMap.remove(name);
}
}
}
}
}
QString EmoticonList::previewEmoticon(const KEmoticonsTheme &theme)
{
QString path = theme.tokenize(QStringLiteral(":)"))[0].picPath;
if (path.isEmpty()) {
path = theme.emoticonsMap().keys().value(0);
}
return path;
}
void EmoticonList::initDefaults()
{
QListls = themeList->findItems(QStringLiteral("Glass"), Qt::MatchExactly);
if (ls.isEmpty())
return;
themeList->setCurrentItem( ls.first() );
cbStrict->setChecked(false);
}
void EmoticonList::defaults()
{
initDefaults();
selectTheme();
emit changed(true);
}
bool EmoticonList::canEditTheme()
{
if (!themeList->currentItem()) {
return false;
}
KEmoticonsTheme theme = emoMap.value(themeList->currentItem()->text());
QFileInfo inf(theme.themePath() + QLatin1Char('/') + theme.fileName());
return inf.isWritable();
}
#include "emoticonslist.moc"
// kate: space-indent on; indent-width 4; replace-tabs on;
diff --git a/kcms/formats/writeexports.h b/kcms/formats/writeexports.h
index be001e254..3faceccd9 100644
--- a/kcms/formats/writeexports.h
+++ b/kcms/formats/writeexports.h
@@ -1,103 +1,108 @@
/*
* Copyright 2014 Sebastian Kügler
*
* 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
*/
+#ifndef WRITEEXPORTS_H
+#define WRITEEXPORTS_H
+
#include
#include
#include
#include
const static QString configFile = QStringLiteral("plasma-localerc");
const static QString exportFile = QStringLiteral("plasma-locale-settings.sh");
const static QString lcLang = QStringLiteral("LANG");
const static QString lcNumeric = QStringLiteral("LC_NUMERIC");
const static QString lcTime = QStringLiteral("LC_TIME");
const static QString lcMonetary = QStringLiteral("LC_MONETARY");
const static QString lcMeasurement = QStringLiteral("LC_MEASUREMENT");
const static QString lcCollate = QStringLiteral("LC_COLLATE");
const static QString lcCtype = QStringLiteral("LC_CTYPE");
const static QString lcLanguage = QStringLiteral("LANGUAGE");
void writeExports()
{
const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + QLatin1Char('/') + exportFile;
QString script(QStringLiteral("# Generated script, do not edit\n"));
script.append(QLatin1String("# Exports language-format specific env vars from startkde.\n"));
script.append(QLatin1String("# This script has been generated from kcmshell5 formats.\n"));
script.append(QLatin1String("# It will automatically be overwritten from there.\n"));
KConfigGroup formatsConfig = KConfigGroup(KSharedConfig::openConfig(configFile), "Formats");
KConfigGroup languageConfig = KConfigGroup(KSharedConfig::openConfig(configFile), "Translations");
const QString _export = QStringLiteral("export ");
// Formats, uses LC_* and LANG variables
const QString lang = formatsConfig.readEntry(lcLang, QString());
if (!lang.isEmpty()) {
script.append(_export + lcLang + QLatin1Char('=') + lang + QLatin1Char('\n'));
}
const QString numeric = formatsConfig.readEntry(lcNumeric, QString());
if (!numeric.isEmpty()) {
script.append(_export + lcNumeric + QLatin1Char('=') + numeric + QLatin1Char('\n'));
}
const QString time = formatsConfig.readEntry(lcTime, QString());
if (!time.isEmpty()) {
script.append(_export + lcTime + QLatin1Char('=') + time + QLatin1Char('\n'));
}
const QString monetary = formatsConfig.readEntry(lcMonetary, QString());
if (!monetary.isEmpty()) {
script.append(_export + lcMonetary + QLatin1Char('=') + monetary + QLatin1Char('\n'));
}
const QString measurement = formatsConfig.readEntry(lcMeasurement, QString());
if (!measurement.isEmpty()) {
script.append(_export + lcMeasurement + QLatin1Char('=') + measurement + QLatin1Char('\n'));
}
const QString collate = formatsConfig.readEntry(lcCollate, QString());
if (!collate.isEmpty()) {
script.append(_export + lcCollate + QLatin1Char('=') + collate + QLatin1Char('\n'));
}
const QString ctype = formatsConfig.readEntry(lcCtype, QString());
if (!ctype.isEmpty()) {
script.append(_export + lcCtype + QLatin1Char('=') + ctype + QLatin1Char('\n'));
}
// Translations, uses LANGUAGE variable
const QString language = languageConfig.readEntry(lcLanguage, QString());
if (!language.isEmpty()) {
script.append(_export + lcLanguage + QLatin1Char('=') + language + QLatin1Char('\n'));
}
QFile file(configPath);
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
qDebug() << "Wrote script: " << configPath << "\n" << script;
out << script;
file.close();
}
+
+#endif
diff --git a/kcms/keys/globalshortcuts.h b/kcms/keys/globalshortcuts.h
index 3f6c53644..3880a0890 100644
--- a/kcms/keys/globalshortcuts.h
+++ b/kcms/keys/globalshortcuts.h
@@ -1,44 +1,44 @@
/*
* Copyright 2007 Andreas Pakulat
* Copyright 2008 Michael Jansen
*
* 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 SHORTCUTS_MODULE_H
-#define SHORTCUTS_MODULE_H
+#ifndef GLOBAL_SHORTCUTS_H
+#define GLOBAL_SHORTCUTS_H
#include
#include
class KGlobalShortcutsEditor;
class GlobalShortcutsModule : public KCModule
{
Q_OBJECT
public:
GlobalShortcutsModule(QWidget *parent, const QVariantList &args);
~GlobalShortcutsModule();
void save() override;
void load() override;
void defaults() override;
private:
KGlobalShortcutsEditor *editor;
};
#endif
diff --git a/kcms/kfontinst/kcmfontinst/GroupList.cpp b/kcms/kfontinst/kcmfontinst/GroupList.cpp
index 0871445ee..4e34ac54e 100644
--- a/kcms/kfontinst/kcmfontinst/GroupList.cpp
+++ b/kcms/kfontinst/kcmfontinst/GroupList.cpp
@@ -1,1026 +1,1026 @@
/*
* KFontInst - KDE Font Installer
*
* Copyright 2003-2007 Craig Drummond
*
* ----
*
* 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "GroupList.h"
#include "FontList.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "FcEngine.h"
#include "Misc.h"
#include "KfiConstants.h"
namespace KFI
{
#define GROUPS_DOC "groups"
#define GROUP_TAG "group"
#define NAME_ATTR "name"
#define FAMILY_TAG "family"
enum EGroupColumns
{
COL_GROUP_NAME,
NUM_GROUP_COLS
};
CGroupListItem::CGroupListItem(const QString &name)
: itsName(name),
itsType(CUSTOM),
itsHighlighted(false),
itsStatus(CFamilyItem::ENABLED)
{
itsData.validated=false;
}
CGroupListItem::CGroupListItem(EType type, CGroupList *p)
: itsType(type),
itsHighlighted(false),
itsStatus(CFamilyItem::ENABLED)
{
switch(itsType)
{
case ALL:
itsName=i18n("All Fonts");
break;
case PERSONAL:
itsName=i18n("Personal Fonts");
break;
case SYSTEM:
itsName=i18n("System Fonts");
break;
default:
itsName=i18n("Unclassified");
}
itsData.parent=p;
}
bool CGroupListItem::hasFont(const CFontItem *fnt) const
{
switch(itsType)
{
case CUSTOM:
return itsFamilies.contains(fnt->family());
case PERSONAL:
return !fnt->isSystem();
case SYSTEM:
return fnt->isSystem();
case ALL:
return true;
case UNCLASSIFIED:
{
QList::ConstIterator it(itsData.parent->itsGroups.begin()),
end(itsData.parent->itsGroups.end());
for(; it!=end; ++it)
if((*it)->isCustom() && (*it)->families().contains(fnt->family()))
return false;
return true;
}
default:
return false;
}
return false;
}
void CGroupListItem::updateStatus(QSet &enabled, QSet &disabled, QSet &partial)
{
QSet families(itsFamilies);
if(0!=families.intersect(partial).count())
itsStatus=CFamilyItem::PARTIAL;
else
{
families=itsFamilies;
bool haveEnabled(0!=families.intersect(enabled).count());
families=itsFamilies;
bool haveDisabled(0!=families.intersect(disabled).count());
if(haveEnabled && haveDisabled)
itsStatus=CFamilyItem::PARTIAL;
else if(haveEnabled && !haveDisabled)
itsStatus=CFamilyItem::ENABLED;
else
itsStatus=CFamilyItem::DISABLED;
}
}
bool CGroupListItem::load(QDomElement &elem)
{
if(elem.hasAttribute(NAME_ATTR))
{
itsName=elem.attribute(NAME_ATTR);
addFamilies(elem);
return true;
}
return false;
}
bool CGroupListItem::addFamilies(QDomElement &elem)
{
int b4(itsFamilies.count());
for(QDomNode n=elem.firstChild(); !n.isNull(); n=n.nextSibling())
{
QDomElement ent=n.toElement();
if(FAMILY_TAG==ent.tagName())
itsFamilies.insert(ent.text());
}
return b4!=itsFamilies.count();
}
void CGroupListItem::save(QTextStream &str)
{
str << " <" GROUP_TAG " " NAME_ATTR "=\"" << Misc::encodeText(itsName, str) << "\">" << endl;
if(itsFamilies.count())
{
QSet::ConstIterator it(itsFamilies.begin()),
end(itsFamilies.end());
for(; it!=end; ++it)
str << " <" FAMILY_TAG ">" << Misc::encodeText(*it, str) << "" FAMILY_TAG ">" << endl;
}
str << " " GROUP_TAG ">" << endl;
}
CGroupList::CGroupList(QWidget *parent)
: QAbstractItemModel(parent),
itsTimeStamp(0),
itsModified(false),
itsParent(parent),
itsSortOrder(Qt::AscendingOrder)
{
itsSpecialGroups[CGroupListItem::ALL]=new CGroupListItem(CGroupListItem::ALL, this);
itsGroups.append(itsSpecialGroups[CGroupListItem::ALL]);
if(Misc::root())
itsSpecialGroups[CGroupListItem::PERSONAL]=
itsSpecialGroups[CGroupListItem::SYSTEM]=NULL;
else
{
itsSpecialGroups[CGroupListItem::PERSONAL]=new CGroupListItem(CGroupListItem::PERSONAL, this);
itsGroups.append(itsSpecialGroups[CGroupListItem::PERSONAL]);
itsSpecialGroups[CGroupListItem::SYSTEM]=new CGroupListItem(CGroupListItem::SYSTEM, this);
itsGroups.append(itsSpecialGroups[CGroupListItem::SYSTEM]);
}
itsSpecialGroups[CGroupListItem::UNCLASSIFIED]=
new CGroupListItem(CGroupListItem::UNCLASSIFIED, this);
// Locate groups.xml file - normall will be ~/.config/fontgroups.xml
QString path(QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + '/');
if(!Misc::dExists(path))
Misc::createDir(path);
itsFileName=path+'/'+KFI_GROUPS_FILE;
rescan();
}
CGroupList::~CGroupList()
{
save();
qDeleteAll(itsGroups);
itsGroups.clear();
}
int CGroupList::columnCount(const QModelIndex &) const
{
return NUM_GROUP_COLS;
}
void CGroupList::update(const QModelIndex &unHighlight, const QModelIndex &highlight)
{
if(unHighlight.isValid())
{
CGroupListItem *grp=static_cast(unHighlight.internalPointer());
if(grp)
grp->setHighlighted(false);
emit dataChanged(unHighlight, unHighlight);
}
if(highlight.isValid())
{
CGroupListItem *grp=static_cast(highlight.internalPointer());
if(grp)
grp->setHighlighted(true);
emit dataChanged(highlight, highlight);
}
}
void CGroupList::updateStatus(QSet &enabled, QSet &disabled,
QSet &partial)
{
QList::Iterator it(itsGroups.begin()),
end(itsGroups.end());
for(; it!=end; ++it)
if((*it)->isCustom())
(*it)->updateStatus(enabled, disabled, partial);
emit layoutChanged();
}
inline QColor midColour(const QColor &a, const QColor &b)
{
return QColor((a.red()+b.red())>>1, (a.green()+b.green())>>1, (a.blue()+b.blue())>>1);
}
QVariant CGroupList::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
CGroupListItem *grp=static_cast(index.internalPointer());
if(grp)
switch(index.column())
{
case COL_GROUP_NAME:
switch(role)
{
case Qt::FontRole:
if(CGroupListItem::SYSTEM==grp->type())
{
QFont font;
font.setItalic(true);
return font;
}
break;
case Qt::SizeHintRole:
{
const int s = KIconLoader::global()->currentSize(KIconLoader::Small);
return QSize(s, s + 4);
}
case Qt::EditRole:
case Qt::DisplayRole:
return grp->name();
case Qt::DecorationRole:
if(grp->highlighted())
switch(grp->type())
{
case CGroupListItem::ALL: // Removing from a group
return SmallIcon("list-remove");
case CGroupListItem::PERSONAL: // Copying/moving
case CGroupListItem::SYSTEM: // Copying/moving
return SmallIcon(Qt::LeftToRight==QApplication::layoutDirection()
? "go-next" : "go-previous");
case CGroupListItem::CUSTOM: // Adding to a group
return SmallIcon("list-add");
default:
break;
}
else
switch(grp->type())
{
case CGroupListItem::ALL:
return SmallIcon("preferences-desktop-font");
case CGroupListItem::PERSONAL:
return SmallIcon("user-identity");
case CGroupListItem::SYSTEM:
return SmallIcon("computer");
case CGroupListItem::UNCLASSIFIED:
return SmallIcon("fontstatus");
case CGroupListItem::CUSTOM:
if(0==grp->families().count())
return SmallIcon("image-missing");
switch(grp->status())
{
case CFamilyItem::PARTIAL:
return SmallIcon("dialog-ok", 0, KIconLoader::DisabledState);
case CFamilyItem::ENABLED:
return SmallIcon("dialog-ok");
case CFamilyItem::DISABLED:
return SmallIcon("dialog-cancel");
}
break;
}
default:
break;
}
break;
}
return QVariant();
}
bool CGroupList::setData(const QModelIndex &index, const QVariant &value, int role)
{
if(Qt::EditRole==role && index.isValid())
{
QString name(value.toString().trimmed());
if(!name.isEmpty())
{
CGroupListItem *grp=static_cast(index.internalPointer());
if(grp && grp->isCustom() && grp->name()!=name && !exists(name, false))
{
grp->setName(name);
itsModified=true;
save();
sort(0, itsSortOrder);
return true;
}
}
}
return false;
}
Qt::ItemFlags CGroupList::flags(const QModelIndex &index) const
{
if (!index.isValid())
return Qt::ItemIsEnabled;
CGroupListItem *grp=static_cast(index.internalPointer());
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled |
(grp && grp->type()==CGroupListItem::CUSTOM ? Qt::ItemIsEditable : Qt::NoItemFlags);
}
QVariant CGroupList::headerData(int section, Qt::Orientation orientation, int role) const
{
if (Qt::Horizontal==orientation && COL_GROUP_NAME==section)
switch(role)
{
case Qt::DisplayRole:
return i18n("Group");
case Qt::TextAlignmentRole:
return QVariant(Qt::AlignLeft | Qt::AlignVCenter);
case Qt::WhatsThisRole:
return whatsThis();
default:
break;
}
return QVariant();
}
QModelIndex CGroupList::index(int row, int column, const QModelIndex &parent) const
{
if(!parent.isValid())
{
CGroupListItem *grp=itsGroups.value(row);
if(grp)
return createIndex(row, column, grp);
}
return QModelIndex();
}
QModelIndex CGroupList::parent(const QModelIndex &) const
{
return QModelIndex();
}
int CGroupList::rowCount(const QModelIndex &) const
{
return itsGroups.count();
}
void CGroupList::rescan()
{
save();
load();
sort(0, itsSortOrder);
}
void CGroupList::load()
{
time_t ts=Misc::getTimeStamp(itsFileName);
if(!ts || ts!=itsTimeStamp)
{
clear();
itsTimeStamp=ts;
if(load(itsFileName))
itsModified=false;
}
}
bool CGroupList::load(const QString &file)
{
QFile f(file);
bool rv(false);
if(f.open(QIODevice::ReadOnly))
{
QDomDocument doc;
if(doc.setContent(&f))
for(QDomNode n=doc.documentElement().firstChild(); !n.isNull(); n=n.nextSibling())
{
QDomElement e=n.toElement();
if(GROUP_TAG==e.tagName() && e.hasAttribute(NAME_ATTR))
{
QString name(e.attribute(NAME_ATTR));
CGroupListItem *item=find(name);
if(!item)
{
item=new CGroupListItem(name);
if(!itsGroups.contains(itsSpecialGroups[CGroupListItem::UNCLASSIFIED]))
itsGroups.append(itsSpecialGroups[CGroupListItem::UNCLASSIFIED]);
itsGroups.append(item);
rv=true;
}
if(item->addFamilies(e))
rv=true;
}
}
}
return rv;
}
bool CGroupList::save()
{
if(itsModified && save(itsFileName, NULL))
{
itsTimeStamp=Misc::getTimeStamp(itsFileName);
return true;
}
return false;
}
bool CGroupList::save(const QString &fileName, CGroupListItem *grp)
{
QSaveFile file(fileName);
if (file.open(QIODevice::WriteOnly))
{
QTextStream str(&file);
str << "<" GROUPS_DOC ">" << endl;
if(grp)
grp->save(str);
else
{
QList::Iterator it(itsGroups.begin()),
end(itsGroups.end());
for(; it!=end; ++it)
if((*it)->isCustom())
(*it)->save(str);
}
str << "" GROUPS_DOC ">" << endl;
itsModified=false;
return file.commit();
}
return false;
}
void CGroupList::merge(const QString &file)
{
if(load(file))
{
itsModified=true;
sort(0, itsSortOrder);
}
}
void CGroupList::clear()
{
beginResetModel();
itsGroups.removeFirst(); // Remove all
if(itsSpecialGroups[CGroupListItem::SYSTEM])
{
itsGroups.removeFirst(); // Remove personal
itsGroups.removeFirst(); // Remove system
}
if(itsGroups.contains(itsSpecialGroups[CGroupListItem::UNCLASSIFIED]))
itsGroups.removeFirst(); // Remove unclassif...
qDeleteAll(itsGroups);
itsGroups.clear();
itsGroups.append(itsSpecialGroups[CGroupListItem::ALL]);
if(itsSpecialGroups[CGroupListItem::SYSTEM])
{
itsGroups.append(itsSpecialGroups[CGroupListItem::PERSONAL]);
itsGroups.append(itsSpecialGroups[CGroupListItem::SYSTEM]);
}
- // Dont add 'Unclassif' until we have some user groups
+ // Don't add 'Unclassif' until we have some user groups
endResetModel();
}
QModelIndex CGroupList::index(CGroupListItem::EType t)
{
return createIndex(t, 0, itsSpecialGroups[t]);
}
void CGroupList::createGroup(const QString &name)
{
if(!exists(name))
{
if(!itsGroups.contains(itsSpecialGroups[CGroupListItem::UNCLASSIFIED]))
itsGroups.append(itsSpecialGroups[CGroupListItem::UNCLASSIFIED]);
itsGroups.append(new CGroupListItem(name));
itsModified=true;
save();
sort(0, itsSortOrder);
}
}
bool CGroupList::removeGroup(const QModelIndex &idx)
{
if(idx.isValid())
{
CGroupListItem *grp=static_cast(idx.internalPointer());
if(grp && grp->isCustom() &&
KMessageBox::Yes==KMessageBox::warningYesNo(itsParent,
i18n("Do you really want to remove \'%1\'?
"
"This will only remove the group, and not "
"the actual fonts.
", grp->name()),
i18n("Remove Group"), KGuiItem(i18n("Remove"), "list-remove",
i18n("Remove group"))))
{
itsModified=true;
itsGroups.removeAll(grp);
int stdGroups=1 +// All
(itsSpecialGroups[CGroupListItem::SYSTEM] ? 2 : 0)+ // Personal, System
1; // Unclassified
if(stdGroups==itsGroups.count() && itsGroups.contains(itsSpecialGroups[CGroupListItem::UNCLASSIFIED]))
itsGroups.removeAll(itsSpecialGroups[CGroupListItem::UNCLASSIFIED]);
delete grp;
save();
sort(0, itsSortOrder);
return true;
}
}
return false;
}
void CGroupList::removeFromGroup(const QModelIndex &group, const QSet &families)
{
if(group.isValid())
{
CGroupListItem *grp=static_cast(group.internalPointer());
if(grp && grp->isCustom())
{
QSet::ConstIterator it(families.begin()),
end(families.end());
bool update(false);
for(; it!=end; ++it)
if(removeFromGroup(grp, *it))
update=true;
if(update)
emit refresh();
}
}
}
QString CGroupList::whatsThis() const
{
return i18n("Font Groups
This list displays the font groups available on your system. "
"There are 2 main types of font groups:"
"
- Standard are special groups used by the font manager.
"
"- Custom are groups created by you. To add a font family to one of "
"these groups simply drag it from the list of fonts, and drop "
"onto the desired group. To remove a family from the group, drag "
"the font onto the \"All Fonts\" group.
"
"
",
Misc::root()
? i18n("All Fonts contains all the fonts installed on your system."
"Unclassified contains all fonts that have not yet been placed "
"within a \"Custom\" group.")
: i18n("All Fonts contains all the fonts installed on your system - "
"both \"System\" and \"Personal\"."
"System contains all fonts that are installed system-wide (i.e. "
"available to all users)."
"Personal contains your personal fonts."
"Unclassified contains all fonts that have not yet been placed "
"within a \"Custom\" group."));
}
void CGroupList::addToGroup(const QModelIndex &group, const QSet &families)
{
if(group.isValid())
{
CGroupListItem *grp=static_cast(group.internalPointer());
if(grp && grp->isCustom())
{
QSet::ConstIterator it(families.begin()),
end(families.end());
bool update(false);
for(; it!=end; ++it)
if(!grp->hasFamily(*it))
{
grp->addFamily(*it);
update=true;
itsModified=true;
}
if(update)
emit refresh();
}
}
}
void CGroupList::removeFamily(const QString &family)
{
QList::ConstIterator it(itsGroups.begin()),
end(itsGroups.end());
for(; it!=end; ++it)
removeFromGroup(*it, family);
}
bool CGroupList::removeFromGroup(CGroupListItem *grp, const QString &family)
{
if(grp && grp->isCustom() && grp->hasFamily(family))
{
grp->removeFamily(family);
itsModified=true;
return true;
}
return false;
}
static bool groupNameLessThan(const CGroupListItem *f1, const CGroupListItem *f2)
{
return f1 && f2 && (f1->type()type() ||
(f1->type()==f2->type() && QString::localeAwareCompare(f1->name(), f2->name())<0));
}
static bool groupNameGreaterThan(const CGroupListItem *f1, const CGroupListItem *f2)
{
return f1 && f2 && (f1->type()type() ||
(f1->type()==f2->type() && QString::localeAwareCompare(f1->name(), f2->name())>0));
}
void CGroupList::sort(int, Qt::SortOrder order)
{
itsSortOrder=order;
qSort(itsGroups.begin(), itsGroups.end(),
Qt::AscendingOrder==order ? groupNameLessThan : groupNameGreaterThan);
emit layoutChanged();
}
Qt::DropActions CGroupList::supportedDropActions() const
{
return Qt::CopyAction | Qt::MoveAction;
}
QStringList CGroupList::mimeTypes() const
{
QStringList types;
types << KFI_FONT_DRAG_MIME;
return types;
}
CGroupListItem * CGroupList::find(const QString &name)
{
QList::ConstIterator it(itsGroups.begin()),
end(itsGroups.end());
for(; it!=end; ++it)
if((*it)->name()==name)
return (*it);
return NULL;
}
bool CGroupList::exists(const QString &name, bool showDialog)
{
if(NULL!=find(name))
{
if(showDialog)
KMessageBox::error(itsParent, i18n("A group named \'%1\' already "
"exists.", name));
return true;
}
return false;
}
class CGroupListViewDelegate : public QStyledItemDelegate
{
public:
CGroupListViewDelegate(QObject *p) : QStyledItemDelegate(p) { }
virtual ~CGroupListViewDelegate() { }
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &idx) const override
{
CGroupListItem *grp=static_cast(idx.internalPointer());
QStyleOptionViewItem opt(option);
if(grp && grp->isUnclassified())
opt.rect.adjust(0, 0, 0, -1);
QStyledItemDelegate::paint(painter, opt, idx);
if(grp && grp->isUnclassified())
{
opt.rect.adjust(2, 0, -2, 1);
painter->setPen(QApplication::palette().color(QPalette::Text));
painter->drawLine(opt.rect.bottomLeft(), opt.rect.bottomRight());
}
}
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &idx) const override
{
QSize sz(QStyledItemDelegate::sizeHint(option, idx));
CGroupListItem *grp=static_cast(idx.internalPointer());
if(grp && grp->isUnclassified())
sz.setHeight(sz.height()+1);
return sz;
}
static bool isCloseEvent(QKeyEvent *event)
{
return Qt::Key_Tab==event->key() || Qt::Key_Backtab==event->key() ||
Qt::Key_Enter==event->key() || Qt::Key_Return==event->key();
}
bool eventFilter(QObject *editor, QEvent *event) override
{
if(editor && event && QEvent::KeyPress==event->type() && isCloseEvent(static_cast(event)) &&
qobject_cast(editor))
{
QString text=static_cast(editor)->text().trimmed();
if(!text.isEmpty() &&
!static_cast(static_cast(parent())->model())->exists(text, false))
{
emit commitData(static_cast(editor));
emit closeEditor(static_cast(editor));
return true;
}
}
return false;
}
};
CGroupListView::CGroupListView(QWidget *parent, CGroupList *model)
: QTreeView(parent)
{
setModel(model);
setItemDelegate(new CGroupListViewDelegate(this));
sortByColumn(COL_GROUP_NAME, Qt::AscendingOrder);
setSelectionMode(QAbstractItemView::SingleSelection);
setSortingEnabled(true);
setAllColumnsShowFocus(true);
setAlternatingRowColors(true);
setAcceptDrops(true);
setDragDropMode(QAbstractItemView::DropOnly);
setDropIndicatorShown(true);
setDragEnabled(false);
header()->setSortIndicatorShown(true);
setRootIsDecorated(false);
itsMenu=new QMenu(this);
itsDeleteAct=itsMenu->addAction(QIcon::fromTheme("list-remove"), i18n("Remove"),
this, SIGNAL(del()));
itsMenu->addSeparator();
itsEnableAct=itsMenu->addAction(QIcon::fromTheme("enablefont"), i18n("Enable"),
this, SIGNAL(enable()));
itsDisableAct=itsMenu->addAction(QIcon::fromTheme("disablefont"), i18n("Disable"),
this, SIGNAL(disable()));
itsMenu->addSeparator();
itsRenameAct=itsMenu->addAction(QIcon::fromTheme("edit-rename"), i18n("Rename..."),
this, SLOT(rename()));
if(!Misc::app(KFI_PRINTER).isEmpty())
{
itsMenu->addSeparator();
itsPrintAct=itsMenu->addAction(QIcon::fromTheme("document-print"), i18n("Print..."),
this, SIGNAL(print()));
}
else
itsPrintAct=0L;
itsMenu->addSeparator();
itsExportAct=itsMenu->addAction(QIcon::fromTheme("document-export"), i18n("Export..."),
this, SIGNAL(zip()));
setWhatsThis(model->whatsThis());
header()->setWhatsThis(whatsThis());
connect(this, SIGNAL(addFamilies(QModelIndex,QSet)),
model, SLOT(addToGroup(QModelIndex,QSet)));
connect(this, SIGNAL(removeFamilies(QModelIndex,QSet)),
model, SLOT(removeFromGroup(QModelIndex,QSet)));
}
CGroupListItem::EType CGroupListView::getType()
{
QModelIndexList selectedItems(selectedIndexes());
if(selectedItems.count() && selectedItems.last().isValid())
{
CGroupListItem *grp=static_cast(selectedItems.last().internalPointer());
return grp->type();
}
return CGroupListItem::ALL;
}
void CGroupListView::controlMenu(bool del, bool en, bool dis, bool p, bool exp)
{
itsDeleteAct->setEnabled(del);
itsRenameAct->setEnabled(del);
itsEnableAct->setEnabled(en);
itsDisableAct->setEnabled(dis);
if(itsPrintAct)
itsPrintAct->setEnabled(p);
itsExportAct->setEnabled(exp);
}
void CGroupListView::selectionChanged(const QItemSelection &selected,
const QItemSelection &deselected)
{
QModelIndexList deselectedItems(deselected.indexes());
QAbstractItemView::selectionChanged(selected, deselected);
QModelIndexList selectedItems(selectedIndexes());
if(0==selectedItems.count() && 1==deselectedItems.count())
selectionModel()->select(deselectedItems.last(), QItemSelectionModel::Select);
else
emit itemSelected(selectedItems.count()
? selectedItems.last()
: QModelIndex());
}
void CGroupListView::rename()
{
QModelIndex index(currentIndex());
if(index.isValid())
edit(index);
}
void CGroupListView::emitMoveFonts()
{
emit moveFonts();
}
void CGroupListView::contextMenuEvent(QContextMenuEvent *ev)
{
if(indexAt(ev->pos()).isValid())
itsMenu->popup(ev->globalPos());
}
void CGroupListView::dragEnterEvent(QDragEnterEvent *event)
{
if(event->mimeData()->hasFormat(KFI_FONT_DRAG_MIME))
event->acceptProposedAction();
}
void CGroupListView::dragMoveEvent(QDragMoveEvent *event)
{
if(event->mimeData()->hasFormat(KFI_FONT_DRAG_MIME))
{
QModelIndex index(indexAt(event->pos()));
if(index.isValid())
{
if(COL_GROUP_NAME!=index.column())
index=((CGroupList *)model())->createIdx(index.row(), COL_GROUP_NAME, index.internalPointer());
CGroupListItem *dest=static_cast(index.internalPointer());
CGroupListItem::EType type=getType();
if(dest)
if(!selectedIndexes().contains(index))
{
bool ok(true);
if(dest->isCustom())
emit info(i18n("Add to \"%1\".", dest->name()));
else if(CGroupListItem::CUSTOM==type && dest->isAll())
emit info(i18n("Remove from current group."));
else if(!Misc::root() && dest->isPersonal() && CGroupListItem::SYSTEM==type)
emit info(i18n("Move to personal folder."));
else if(!Misc::root() && dest->isSystem() && CGroupListItem::PERSONAL==type)
emit info(i18n("Move to system folder."));
else
ok=false;
if(ok)
{
drawHighlighter(index);
event->acceptProposedAction();
return;
}
}
}
event->ignore();
drawHighlighter(QModelIndex());
emit info(QString());
}
}
void CGroupListView::dragLeaveEvent(QDragLeaveEvent *)
{
drawHighlighter(QModelIndex());
emit info(QString());
}
void CGroupListView::dropEvent(QDropEvent *event)
{
emit info(QString());
drawHighlighter(QModelIndex());
if(event->mimeData()->hasFormat(KFI_FONT_DRAG_MIME))
{
event->acceptProposedAction();
QSet families;
QByteArray encodedData(event->mimeData()->data(KFI_FONT_DRAG_MIME));
QDataStream ds(&encodedData, QIODevice::ReadOnly);
QModelIndex from(selectedIndexes().last()),
to(indexAt(event->pos()));
ds >> families;
// Are we mvoeing/copying, removing a font from the current group?
if(to.isValid() && from.isValid())
{
if( ((static_cast(from.internalPointer()))->isSystem() &&
(static_cast(to.internalPointer()))->isPersonal()) ||
((static_cast(from.internalPointer()))->isPersonal() &&
(static_cast(to.internalPointer()))->isSystem()))
QTimer::singleShot(0, this, SLOT(emitMoveFonts()));
else if((static_cast(from.internalPointer()))->isCustom() &&
!(static_cast(to.internalPointer()))->isCustom())
emit removeFamilies(from, families);
else
emit addFamilies(to, families);
}
if(isUnclassified())
emit unclassifiedChanged();
}
}
void CGroupListView::drawHighlighter(const QModelIndex &idx)
{
if(itsCurrentDropItem!=idx)
{
((CGroupList *)model())->update(itsCurrentDropItem, idx);
itsCurrentDropItem=idx;
}
}
bool CGroupListView::viewportEvent(QEvent *event)
{
executeDelayedItemsLayout();
return QTreeView::viewportEvent(event);
}
}
diff --git a/kcms/kfontinst/viewpart/CharTip.cpp b/kcms/kfontinst/viewpart/CharTip.cpp
index 68fdf18b2..90de98d37 100644
--- a/kcms/kfontinst/viewpart/CharTip.cpp
+++ b/kcms/kfontinst/viewpart/CharTip.cpp
@@ -1,297 +1,296 @@
/*
* KFontInst - KDE Font Installer
*
* Copyright 2003-2007 Craig Drummond
*
* ----
*
* 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; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
/*
* Inspired by konq_filetip.cc
*
* Copyright (C) 1998, 1999 Torben Weis
* Copyright (C) 2000, 2001, 2002 David Faure
* Copyright (C) 2004 Martin Koller
*/
#include "CharTip.h"
#include "FontPreview.h"
#include "UnicodeCategories.h"
#include
#include
#include
#include
#include
#include
#include
#include
-#include
#include
namespace KFI
{
EUnicodeCategory getCategory(quint32 ucs2)
{
for(int i=0; UNICODE_INVALID!=constUnicodeCategoryList[i].category; ++i)
if(constUnicodeCategoryList[i].start<=ucs2 &&
constUnicodeCategoryList[i].end>=ucs2)
return constUnicodeCategoryList[i].category;
return UNICODE_UNASSIGNED;
}
static QString toStr(EUnicodeCategory cat)
{
switch (cat)
{
case UNICODE_CONTROL:
return i18n("Other, Control");
case UNICODE_FORMAT:
return i18n("Other, Format");
case UNICODE_UNASSIGNED:
return i18n("Other, Not Assigned");
case UNICODE_PRIVATE_USE:
return i18n("Other, Private Use");
case UNICODE_SURROGATE:
return i18n("Other, Surrogate");
case UNICODE_LOWERCASE_LETTER:
return i18n("Letter, Lowercase");
case UNICODE_MODIFIER_LETTER:
return i18n("Letter, Modifier");
case UNICODE_OTHER_LETTER:
return i18n("Letter, Other");
case UNICODE_TITLECASE_LETTER:
return i18n("Letter, Titlecase");
case UNICODE_UPPERCASE_LETTER:
return i18n("Letter, Uppercase");
case UNICODE_COMBINING_MARK:
return i18n("Mark, Spacing Combining");
case UNICODE_ENCLOSING_MARK:
return i18n("Mark, Enclosing");
case UNICODE_NON_SPACING_MARK:
return i18n("Mark, Non-Spacing");
case UNICODE_DECIMAL_NUMBER:
return i18n("Number, Decimal Digit");
case UNICODE_LETTER_NUMBER:
return i18n("Number, Letter");
case UNICODE_OTHER_NUMBER:
return i18n("Number, Other");
case UNICODE_CONNECT_PUNCTUATION:
return i18n("Punctuation, Connector");
case UNICODE_DASH_PUNCTUATION:
return i18n("Punctuation, Dash");
case UNICODE_CLOSE_PUNCTUATION:
return i18n("Punctuation, Close");
case UNICODE_FINAL_PUNCTUATION:
return i18n("Punctuation, Final Quote");
case UNICODE_INITIAL_PUNCTUATION:
return i18n("Punctuation, Initial Quote");
case UNICODE_OTHER_PUNCTUATION:
return i18n("Punctuation, Other");
case UNICODE_OPEN_PUNCTUATION:
return i18n("Punctuation, Open");
case UNICODE_CURRENCY_SYMBOL:
return i18n("Symbol, Currency");
case UNICODE_MODIFIER_SYMBOL:
return i18n("Symbol, Modifier");
case UNICODE_MATH_SYMBOL:
return i18n("Symbol, Math");
case UNICODE_OTHER_SYMBOL:
return i18n("Symbol, Other");
case UNICODE_LINE_SEPARATOR:
return i18n("Separator, Line");
case UNICODE_PARAGRAPH_SEPARATOR:
return i18n("Separator, Paragraph");
case UNICODE_SPACE_SEPARATOR:
return i18n("Separator, Space");
default:
return "";
}
}
CCharTip::CCharTip(CFontPreview *parent)
: QFrame(0, Qt::ToolTip | Qt::WindowStaysOnTopHint | Qt::X11BypassWindowManagerHint),
itsParent(parent)
{
itsPixmapLabel = new QLabel(this);
itsLabel = new QLabel(this);
itsTimer = new QTimer(this);
QBoxLayout* layout = new QBoxLayout(QBoxLayout::LeftToRight, this);
layout->setMargin(8);
layout->setSpacing(0);
layout->addWidget(itsPixmapLabel);
layout->addWidget(itsLabel);
setPalette(QToolTip::palette());
setFrameShape(QFrame::Box);
setFrameShadow(QFrame::Plain);
hide();
}
CCharTip::~CCharTip()
{
}
void CCharTip::setItem(const CFcEngine::TChar &ch)
{
hideTip();
itsItem=ch;
itsTimer->disconnect(this);
connect(itsTimer, SIGNAL(timeout()), this, SLOT(showTip()));
itsTimer->setSingleShot(true);
itsTimer->start(300);
}
void CCharTip::showTip()
{
if(!itsParent->underMouse())
return;
static const int constPixSize=96;
EUnicodeCategory cat(getCategory(itsItem.ucs4));
QString details("");
details+=""+i18n("Category")+" | "+
toStr(cat)+" |
";
details+=""+i18n("UCS-4")+" | "+
QString().sprintf("U+%4.4X", itsItem.ucs4)+" |
";
QString str(QString::fromUcs4(&(itsItem.ucs4), 1));
details+=""+i18n("UTF-16")+" | ";
const ushort *utf16(str.utf16());
for(int i=0; utf16[i]; ++i)
{
if(i)
details+=' ';
details+=QString().sprintf("0x%4.4X", utf16[i]);
}
details+=" |
";
details+=""+i18n("UTF-8")+" | ";
QByteArray utf8(str.toUtf8());
for(int i=0; i |
";
// Note: the " below is just to stop Qt converting the xml entry into
// a character!
if ((0x0001 <= itsItem.ucs4 && itsItem.ucs4 <= 0xD7FF) ||
(0xE000 <= itsItem.ucs4 && itsItem.ucs4 <= 0xFFFD) ||
(0x10000 <= itsItem.ucs4 && itsItem.ucs4 <= 0x10FFFF))
details+=""+i18n("XML Decimal Entity")+" | "+
QString().sprintf("%d;", itsItem.ucs4)+" |
";
details+="
";
itsLabel->setText(details);
QList range;
range.append(CFcEngine::TRange(itsItem.ucs4, 0));
QColor bgnd(Qt::white);
bgnd.setAlpha(0);
QImage img=itsParent->engine()->draw(itsParent->itsFontName, itsParent->itsStyleInfo,
itsParent->itsCurrentFace-1, palette().text().color(), bgnd,
constPixSize, constPixSize, false, range, NULL);
if(!img.isNull())
itsPixmapLabel->setPixmap(QPixmap::fromImage(img));
else
itsPixmapLabel->setPixmap(QPixmap());
itsTimer->disconnect(this);
connect(itsTimer, SIGNAL(timeout()), this, SLOT(hideTip()));
itsTimer->setSingleShot(true);
itsTimer->start(15000);
qApp->installEventFilter(this);
reposition();
show();
}
void CCharTip::hideTip()
{
itsTimer->stop();
qApp->removeEventFilter(this);
hide();
}
void CCharTip::reposition()
{
QRect rect(itsItem);
rect.moveTopRight(itsParent->mapToGlobal(rect.topRight()));
QPoint pos(rect.center());
QRect desk(QApplication::desktop()->screenGeometry(rect.center()));
if ((rect.center().x() + width()) > desk.right())
{
if (pos.x() - width() < 0)
pos.setX(0);
else
pos.setX( pos.x() - width() );
}
// should the tooltip be shown above or below the ivi ?
if (rect.bottom() + height() > desk.bottom())
pos.setY( rect.top() - height() );
else
pos.setY(rect.bottom() + 1);
move(pos);
update();
}
void CCharTip::resizeEvent(QResizeEvent *event)
{
QFrame::resizeEvent(event);
reposition();
}
bool CCharTip::eventFilter(QObject *, QEvent *e)
{
switch (e->type())
{
case QEvent::Leave:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::FocusIn:
case QEvent::FocusOut:
case QEvent::Wheel:
hideTip();
default: break;
}
return false;
}
}
diff --git a/kcms/migrationlib/kdelibs4config.h b/kcms/migrationlib/kdelibs4config.h
index 074f53298..f28e31ff3 100644
--- a/kcms/migrationlib/kdelibs4config.h
+++ b/kcms/migrationlib/kdelibs4config.h
@@ -1,41 +1,45 @@
/*
*
* Copyright (C) 2014 David Edmundson
*
* 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 KDELIBS4CONFIG_H
+#define KDELIBS4CONFIG_H
+
#include
#include
#include
class Kdelibs4SharedConfig
{
public:
static void syncConfigGroup(const QLatin1String &sourceGroup, const QString &fileName)
{
Kdelibs4Migration migration;
QString configDirPath = migration.saveLocation("config");
KSharedConfigPtr kde4Config = KSharedConfig::openConfig(configDirPath + '/' + fileName);
KSharedConfigPtr simpleConfig = KSharedConfig::openConfig(fileName, KConfig::SimpleConfig);
KConfigGroup simpleConfigGroup(simpleConfig, sourceGroup);
KConfigGroup kde4ConfigGroup = kde4Config->group(sourceGroup);
simpleConfigGroup.copyTo(&kde4ConfigGroup);
kde4ConfigGroup.sync();
}
};
+#endif
diff --git a/kcms/mouse/kcm/libinput/libinput_config.cpp b/kcms/mouse/kcm/libinput/libinput_config.cpp
index bb63f6e32..e235e447b 100644
--- a/kcms/mouse/kcm/libinput/libinput_config.cpp
+++ b/kcms/mouse/kcm/libinput/libinput_config.cpp
@@ -1,230 +1,230 @@
/*
* Copyright 2018 Roman Gilg
*
* 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 "libinput_config.h"
#include "../configcontainer.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "inputbackend.h"
static QVariant getDeviceList(InputBackend *backend)
{
return QVariant::fromValue(backend->getDevices().toList());
}
LibinputConfig::LibinputConfig(ConfigContainer *parent, InputBackend *backend)
: ConfigPlugin(parent)
{
m_backend = backend;
KAboutData* data = new KAboutData(QStringLiteral("kcmmouse"),
i18n("Pointer device KCM"),
QStringLiteral("1.0"),
i18n("System Settings module for managing mice and trackballs."),
KAboutLicense::GPL_V2,
i18n("Copyright 2018 Roman Gilg"),
QString());
data->addAuthor(i18n("Roman Gilg"),
i18n("Developer"),
QStringLiteral("subdiff@gmail.com"));
m_parent->setAboutData(data);
m_initError = !m_backend->errorString().isNull();
m_view = new QQuickWidget(this);
m_errorMessage = new KMessageWidget(this);
m_errorMessage->setCloseButtonVisible(false);
m_errorMessage->setWordWrap(true);
m_errorMessage->setVisible(false);
QVBoxLayout *layout = new QVBoxLayout(parent);
layout->addWidget(m_errorMessage);
layout->addWidget(m_view);
parent->setLayout(layout);
m_view->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_view->setClearColor(Qt::transparent);
m_view->setAttribute(Qt::WA_AlwaysStackOnTop);
m_view->rootContext()->setContextProperty("backend", m_backend);
m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend));
KDeclarative::KDeclarative kdeclarative;
kdeclarative.setDeclarativeEngine(m_view->engine());
kdeclarative.setupBindings();
if (m_backend->mode() == InputBackendMode::XLibinput) {
m_view->setSource(QUrl("qrc:/libinput/main_deviceless.qml"));
} else {
m_view->setSource(QUrl("qrc:/libinput/main.qml"));
}
if (m_initError) {
m_errorMessage->setMessageType(KMessageWidget::Error);
m_errorMessage->setText(m_backend->errorString());
QMetaObject::invokeMethod(m_errorMessage, "animatedShow",
Qt::QueuedConnection);
} else {
connect(m_backend, SIGNAL(deviceAdded(bool)), this, SLOT(onDeviceAdded(bool)));
connect(m_backend, SIGNAL(deviceRemoved(int)), this, SLOT(onDeviceRemoved(int)));
connect(m_view->rootObject(), SIGNAL(changeSignal()), this, SLOT(onChange()));
}
m_view->show();
}
QSize LibinputConfig::sizeHint() const
{
return QQmlProperty::read(m_view->rootObject(), "sizeHint").toSize();
}
QSize LibinputConfig::minimumSizeHint() const
{
return QQmlProperty::read(m_view->rootObject(), "minimumSizeHint").toSize();
}
void LibinputConfig::load()
{
// in case of critical init error in backend, don't try
if (m_initError) {
return;
}
if (!m_backend->getConfig()) {
m_errorMessage->setMessageType(KMessageWidget::Error);
- m_errorMessage->setText(i18n("Error while loading values. See logs for more informations. Please restart this configuration module."));
+ m_errorMessage->setText(i18n("Error while loading values. See logs for more information. Please restart this configuration module."));
m_errorMessage->animatedShow();
} else {
if (!m_backend->deviceCount()) {
m_errorMessage->setMessageType(KMessageWidget::Information);
m_errorMessage->setText(i18n("No pointer device found. Connect now."));
m_errorMessage->animatedShow();
}
}
QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend");
}
void LibinputConfig::save()
{
if (!m_backend->applyConfig()) {
m_errorMessage->setMessageType(KMessageWidget::Error);
- m_errorMessage->setText(i18n("Not able to save all changes. See logs for more informations. Please restart this configuration module and try again."));
+ m_errorMessage->setText(i18n("Not able to save all changes. See logs for more information. Please restart this configuration module and try again."));
m_errorMessage->animatedShow();
} else {
hideErrorMessage();
}
// load newly written values
load();
// in case of error, config still in changed state
emit m_parent->changed(m_backend->isChangedConfig());
}
void LibinputConfig::defaults()
{
// in case of critical init error in backend, don't try
if (m_initError) {
return;
}
if (!m_backend->getDefaultConfig()) {
m_errorMessage->setMessageType(KMessageWidget::Error);
m_errorMessage->setText(i18n("Error while loading default values. Failed to set some options to their default values."));
m_errorMessage->animatedShow();
}
QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend");
emit m_parent->changed(m_backend->isChangedConfig());
}
void LibinputConfig::onChange()
{
if (!m_backend->deviceCount()) {
return;
}
hideErrorMessage();
emit m_parent->changed(m_backend->isChangedConfig());
}
void LibinputConfig::onDeviceAdded(bool success)
{
QQuickItem *rootObj = m_view->rootObject();
if (!success) {
m_errorMessage->setMessageType(KMessageWidget::Error);
m_errorMessage->setText(i18n("Error while adding newly connected device. Please reconnect it and restart this configuration module."));
}
int activeIndex;
if (m_backend->deviceCount() == 1) {
// if no pointer device was connected previously, show the new device and hide the no-device-message
activeIndex = 0;
hideErrorMessage();
} else {
activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt();
}
m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend));
QMetaObject::invokeMethod(rootObj, "resetModel", Q_ARG(QVariant, activeIndex));
QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend");
}
void LibinputConfig::onDeviceRemoved(int index)
{
QQuickItem *rootObj = m_view->rootObject();
int activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt();
if (activeIndex == index) {
m_errorMessage->setMessageType(KMessageWidget::Information);
if (m_backend->deviceCount()) {
m_errorMessage->setText(i18n("Pointer device disconnected. Closed its setting dialog."));
} else {
m_errorMessage->setText(i18n("Pointer device disconnected. No other devices found."));
}
m_errorMessage->animatedShow();
activeIndex = 0;
} else {
if (index < activeIndex) {
activeIndex--;
}
}
m_view->rootContext()->setContextProperty("deviceModel", getDeviceList(m_backend));
QMetaObject::invokeMethod(m_view->rootObject(), "resetModel", Q_ARG(QVariant, activeIndex));
QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend");
emit m_parent->changed(m_backend->isChangedConfig());
}
void LibinputConfig::hideErrorMessage()
{
if (m_errorMessage->isVisible()) {
m_errorMessage->animatedHide();
}
}
diff --git a/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.cpp b/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.cpp
index cd20d8851..a9703249a 100644
--- a/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.cpp
+++ b/kcms/touchpad/src/kcm/libinput/touchpadconfiglibinput.cpp
@@ -1,221 +1,221 @@
/*
* Copyright 2017 Roman Gilg
*
* 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 "touchpadconfiglibinput.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../touchpadconfigcontainer.h"
#include "touchpadbackend.h"
#include "version.h"
TouchpadConfigLibinput::TouchpadConfigLibinput(TouchpadConfigContainer *parent, const QVariantList &args)
: TouchpadConfigPlugin(parent)
{
KAboutData* data = new KAboutData(QStringLiteral("kcm_touchpad"),
i18n("Touchpad KCM"),
TOUCHPAD_KCM_VERSION,
i18n("System Settings module for managing your touchpad"),
KAboutLicense::GPL_V2,
i18n("Copyright © 2016 Roman Gilg"),
QString());
data->addAuthor(i18n("Roman Gilg"),
i18n("Developer"),
QStringLiteral("subdiff@gmail.com"));
m_parent->setAboutData(data);
m_backend = TouchpadBackend::implementation();
m_initError = !m_backend->errorString().isNull();
m_view = new QQuickWidget(this);
m_errorMessage = new KMessageWidget(this);
m_errorMessage->setCloseButtonVisible(false);
m_errorMessage->setWordWrap(true);
m_errorMessage->setVisible(false);
QVBoxLayout *layout = new QVBoxLayout(parent);
layout->addWidget(m_errorMessage);
layout->addWidget(m_view);
parent->setLayout(layout);
m_view->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_view->setClearColor(Qt::transparent);
m_view->setAttribute(Qt::WA_AlwaysStackOnTop);
m_view->rootContext()->setContextProperty("backend", m_backend);
m_view->rootContext()->setContextProperty("deviceModel", QVariant::fromValue(m_backend->getDevices().toList()));
KDeclarative::KDeclarative kdeclarative;
kdeclarative.setDeclarativeEngine(m_view->engine());
kdeclarative.setupBindings();
m_view->setSource(QUrl("qrc:/libinput/main.qml"));
if (m_initError) {
m_errorMessage->setMessageType(KMessageWidget::Error);
m_errorMessage->setText(m_backend->errorString());
QMetaObject::invokeMethod(m_errorMessage, "animatedShow",
Qt::QueuedConnection);
} else {
connect(m_backend, SIGNAL(touchpadAdded(bool)), this, SLOT(onTouchpadAdded(bool)));
connect(m_backend, SIGNAL(touchpadRemoved(int)), this, SLOT(onTouchpadRemoved(int)));
connect(m_view->rootObject(), SIGNAL(changeSignal()), this, SLOT(onChange()));
}
m_view->show();
}
QSize TouchpadConfigLibinput::sizeHint() const
{
return QQmlProperty::read(m_view->rootObject(), "sizeHint").toSize();
}
QSize TouchpadConfigLibinput::minimumSizeHint() const
{
return QQmlProperty::read(m_view->rootObject(), "minimumSizeHint").toSize();
}
void TouchpadConfigLibinput::load()
{
// in case of critical init error in backend, don't try
if (m_initError) {
return;
}
if (!m_backend->getConfig()) {
m_errorMessage->setMessageType(KMessageWidget::Error);
- m_errorMessage->setText(i18n("Error while loading values. See logs for more informations. Please restart this configuration module."));
+ m_errorMessage->setText(i18n("Error while loading values. See logs for more information. Please restart this configuration module."));
m_errorMessage->animatedShow();
} else {
if (!m_backend->touchpadCount()) {
m_errorMessage->setMessageType(KMessageWidget::Information);
m_errorMessage->setText(i18n("No touchpad found. Connect touchpad now."));
m_errorMessage->animatedShow();
}
}
QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend");
}
void TouchpadConfigLibinput::save()
{
if (!m_backend->applyConfig()) {
m_errorMessage->setMessageType(KMessageWidget::Error);
- m_errorMessage->setText(i18n("Not able to save all changes. See logs for more informations. Please restart this configuration module and try again."));
+ m_errorMessage->setText(i18n("Not able to save all changes. See logs for more information. Please restart this configuration module and try again."));
m_errorMessage->animatedShow();
} else {
hideErrorMessage();
}
// load newly written values
load();
// in case of error, config still in changed state
emit m_parent->changed(m_backend->isChangedConfig());
}
void TouchpadConfigLibinput::defaults()
{
// in case of critical init error in backend, don't try
if (m_initError) {
return;
}
if (!m_backend->getDefaultConfig()) {
m_errorMessage->setMessageType(KMessageWidget::Error);
m_errorMessage->setText(i18n("Error while loading default values. Failed to set some options to their default values."));
m_errorMessage->animatedShow();
}
QMetaObject::invokeMethod(m_view->rootObject(), "syncValuesFromBackend");
emit m_parent->changed(m_backend->isChangedConfig());
}
void TouchpadConfigLibinput::onChange()
{
if (!m_backend->touchpadCount()) {
return;
}
hideErrorMessage();
emit m_parent->changed(m_backend->isChangedConfig());
}
void TouchpadConfigLibinput::onTouchpadAdded(bool success)
{
QQuickItem *rootObj = m_view->rootObject();
if (!success) {
m_errorMessage->setMessageType(KMessageWidget::Error);
m_errorMessage->setText(i18n("Error while adding newly connected device. Please reconnect it and restart this configuration module."));
}
int activeIndex;
if (m_backend->touchpadCount() == 1) {
// if no touchpad was connected previously, show the new device and hide the no-device-message
activeIndex = 0;
hideErrorMessage();
} else {
activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt();
}
m_view->rootContext()->setContextProperty("deviceModel", QVariant::fromValue(m_backend->getDevices()));
QMetaObject::invokeMethod(rootObj, "resetModel", Q_ARG(QVariant, activeIndex));
QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend");
}
void TouchpadConfigLibinput::onTouchpadRemoved(int index)
{
QQuickItem *rootObj = m_view->rootObject();
int activeIndex = QQmlProperty::read(rootObj, "deviceIndex").toInt();
if (activeIndex == index) {
m_errorMessage->setMessageType(KMessageWidget::Information);
if (m_backend->touchpadCount()) {
m_errorMessage->setText(i18n("Touchpad disconnected. Closed its setting dialog."));
} else {
m_errorMessage->setText(i18n("Touchpad disconnected. No other touchpads found."));
}
m_errorMessage->animatedShow();
activeIndex = 0;
} else {
if (index < activeIndex) {
activeIndex--;
}
}
m_view->rootContext()->setContextProperty("deviceModel", QVariant::fromValue(m_backend->getDevices()));
QMetaObject::invokeMethod(m_view->rootObject(), "resetModel", Q_ARG(QVariant, activeIndex));
QMetaObject::invokeMethod(rootObj, "syncValuesFromBackend");
emit m_parent->changed(m_backend->isChangedConfig());
}
void TouchpadConfigLibinput::hideErrorMessage()
{
if (m_errorMessage->isVisible()) {
m_errorMessage->animatedHide();
}
}
diff --git a/solid-device-automounter/kcm/LayoutSettings.kcfg b/solid-device-automounter/kcm/LayoutSettings.kcfg
index bcfd2460b..634913ca0 100644
--- a/solid-device-automounter/kcm/LayoutSettings.kcfg
+++ b/solid-device-automounter/kcm/LayoutSettings.kcfg
@@ -1,16 +1,16 @@
true
false
-
\ No newline at end of file
+
diff --git a/toolboxes/desktoptoolbox/contents/ui/ToolBoxButton.qml b/toolboxes/desktoptoolbox/contents/ui/ToolBoxButton.qml
index c3713f375..04f382ab0 100644
--- a/toolboxes/desktoptoolbox/contents/ui/ToolBoxButton.qml
+++ b/toolboxes/desktoptoolbox/contents/ui/ToolBoxButton.qml
@@ -1,312 +1,312 @@
/***************************************************************************
* Copyright 2012,2015 by Sebastian Kügler *
* Copyright 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 org.kde.plasma.core 2.0 as PlasmaCore
import org.kde.plasma.components 2.0 as PlasmaComponents
import org.kde.kquickcontrolsaddons 2.0 as KQuickControlsAddons
import org.kde.plasma.plasmoid 2.0
Item {
id: toolBoxButton
property string text: main.Plasmoid.activityName
property bool isCorner: !buttonMouse.dragging &&
((state == "topleft") || (state == "topright") ||
(state == "bottomright") || (state == "bottomleft"))
property bool isHorizontal: (state != "left" && state != "right")
rotation: switch(state) {
case "left":
return -90;
case "right":
return 90;
default:
return 0;
}
transform: Translate {
x: state == "left" ? Math.round(-width/2 + height/2) : state == "right" ? + Math.round(width/2 - height/2) : 0
Behavior on x {
NumberAnimation {
duration: units.shortDuration * 3;
easing.type: Easing.InOutExpo;
}
}
}
transformOrigin: Item.Center
Behavior on rotation {
NumberAnimation {
duration: units.shortDuration * 3;
easing.type: Easing.InOutExpo;
}
enabled: visible
}
Behavior on x {
NumberAnimation {
duration: units.shortDuration * 3;
easing.type: Easing.InOutExpo;
}
enabled: visible
}
Behavior on y {
NumberAnimation {
duration: units.shortDuration * 3;
easing.type: Easing.InOutExpo;
}
enabled: visible
}
clip: backgroundFrameWidthAnimation.running
width: backgroundFrame.width + backgroundFrame.width % 2
height: backgroundFrame.height + backgroundFrame.height % 2
//x and y default to 0, so top left would be correct
- //If the position is anything else it will updated via onXChanged during intialisation
+ //If the position is anything else it will updated via onXChanged during initialization
state: "topleft"
onXChanged: stateTimer.restart()
onYChanged: stateTimer.restart()
Timer {
id: stateTimer
interval: 100
onTriggered: updateState()
}
function updateState() {
var container = main;
//print(" w: " + container.width +"x"+container.height+" : "+x+"/"+y+" tbw: " + toolBoxButton.width);
var x = toolBoxButton.x - main.x;
var y = toolBoxButton.y - main.y;
var cornerSnap = iconWidth
if (x < cornerSnap && y < cornerSnap) {
toolBoxButton.state = "topleft";
} else if (container.width - x - buttonLayout.width < cornerSnap && y < cornerSnap) {
toolBoxButton.state = "topright";
} else if (container.width - x - buttonLayout.width < cornerSnap && container.height - y - buttonLayout.width < cornerSnap) {
toolBoxButton.state = "bottomright";
} else if (x < cornerSnap && container.height - y - buttonLayout.width < cornerSnap) {
toolBoxButton.state = "bottomleft";
//top diagonal half
} else if (x > (y * (container.width/container.height))) {
//Top edge
if (container.width - x > y ) {
toolBoxButton.state = "top";
//right edge
} else {
//toolBoxButton.transformOrigin = Item.BottomRight
toolBoxButton.state = "right";
}
//bottom diagonal half
} else {
//Left edge
if (container.height - y > x ) {
//toolBoxButton.transformOrigin = Item.TopLeft
toolBoxButton.state = "left";
//Bottom edge
} else {
toolBoxButton.state = "bottom";
}
}
if (!buttonMouse.pressed) {
main.placeToolBox(toolBoxButton.state);
}
stateTimer.running = false;
}
PlasmaCore.FrameSvgItem {
id: backgroundFrame
anchors {
left: parent.left
top: parent.top
}
imagePath: "widgets/translucentbackground"
opacity: buttonMouse.containsMouse || (toolBoxLoader.item && toolBoxLoader.item.visible) ? 1.0 : 0.4
width: Math.round((isCorner ? buttonLayout.height : buttonLayout.width) + margins.horizontal)
height: Math.round(buttonLayout.height + margins.vertical)
Behavior on width {
NumberAnimation {
id: backgroundFrameWidthAnimation
duration: units.longDuration;
easing.type: Easing.InOutQuad;
}
}
Behavior on opacity {
NumberAnimation {
duration: units.longDuration;
}
}
}
Row {
id: buttonLayout
anchors.centerIn: parent
height: Math.max(toolBoxIcon.height, fontMetrics.height)
spacing: units.smallSpacing
Behavior on x {
NumberAnimation {
duration: units.longDuration;
easing.type: Easing.InOutQuad;
}
}
PlasmaCore.SvgItem {
id: toolBoxIcon
svg: PlasmaCore.Svg {
id: iconSvg
imagePath: "widgets/configuration-icons"
onRepaintNeeded: toolBoxIcon.elementId = iconSvg.hasElement("menu") ? "menu" : "configure"
}
elementId: iconSvg.hasElement("menu") ? "menu" : "configure"
anchors.verticalCenter: parent.verticalCenter
width: iconSize
height: iconSize
opacity: buttonMouse.containsMouse || (toolBoxLoader.item && toolBoxLoader.item.visible) ? 1 : 0.5
rotation: isHorizontal ? 0 : -90;
transformOrigin: Item.Center
Behavior on opacity {
NumberAnimation {
duration: units.longDuration;
easing.type: Easing.InOutExpo;
}
}
}
PlasmaComponents.Label {
id: activityName
anchors.verticalCenter: parent.verticalCenter
opacity: isCorner ? 0 : 1
text: toolBoxButton.text
visible: opacity
Behavior on opacity {
//only have this animation when going from hidden -> shown
enabled: activityName.opacity == 0
SequentialAnimation {
//pause to allow the toolbox frame to resize
//otherwise we see the text overflow the box
//whilst that animates
PauseAnimation {
duration: units.longDuration
}
NumberAnimation {
duration: units.shortDuration
easing.type: Easing.InOutExpo
}
}
}
}
FontMetrics {
id: fontMetrics
font: activityName.font
}
}
MouseArea {
id: buttonMouse
property QtObject container: main
property int pressedX
property int pressedY
property bool dragging: false
anchors {
fill: parent
margins: -10
}
drag {
target: main.Plasmoid.immutable ? undefined : toolBoxButton
minimumX: 0
maximumX: container.width - toolBoxIcon.width
minimumY: 0
maximumY: container.height - toolBoxIcon.height
}
hoverEnabled: true
onPressed: {
pressedX = toolBoxButton.x
pressedY = toolBoxButton.y
}
onPositionChanged: {
if (pressed && (Math.abs(toolBoxButton.x - pressedX) > iconSize ||
Math.abs(toolBoxButton.y - pressedY) > iconSize)) {
dragging = true;
}
}
onClicked: {
// the dialog auto-closes on losing focus
main.open = !main.dialogWasVisible
plasmoid.focus = true;
}
onReleased: {
main.Plasmoid.configuration.ToolBoxButtonState = toolBoxButton.state;
main.Plasmoid.configuration.ToolBoxButtonX = toolBoxButton.x;
main.Plasmoid.configuration.ToolBoxButtonY = toolBoxButton.y;
//print("Saved coordinates for ToolBox in config: " + toolBoxButton.x + ", " +toolBoxButton.x);
if (dragging) {
main.placeToolBox();
}
dragging = false;
stateTimer.stop();
updateState();
}
onCanceled: dragging = false;
}
states: [
State {
name: "topleft"
},
State {
name: "top"
},
State {
name: "topright"
},
State {
name: "right"
},
State {
name: "bottomright"
},
State {
name: "bottom"
},
State {
name: "bottomleft"
},
State {
name: "topleft"
},
State {
name: "left"
}
]
}