File: shellutils.cpp

package info (click to toggle)
kdevelop 4%3A24.12.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 71,888 kB
  • sloc: cpp: 290,869; python: 3,626; javascript: 3,518; sh: 1,316; ansic: 703; xml: 401; php: 95; lisp: 66; makefile: 31; sed: 12
file content (190 lines) | stat: -rw-r--r-- 7,413 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
/*
    SPDX-FileCopyrightText: 2012 Ivan Shapovalov <intelfx100@gmail.com>

    SPDX-License-Identifier: LGPL-2.0-or-later
*/

#include "shellutils.h"

#include <interfaces/icore.h>
#include <interfaces/iuicontroller.h>

#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KParts/MainWindow>
#include <KSharedConfig>

#include <QEvent>
#include <QFile>
#include <QGuiApplication>
#include <QList>
#include <QTextStream>
#include <QUrl>
#include <QWidget>

namespace {

class WidgetGeometrySaver : public QObject
{
    Q_OBJECT
public:
    explicit WidgetGeometrySaver(QWidget& widget, const QString& configGroupName, const QString& configSubgroupName)
        : QObject(&widget)
        , m_configGroupName{configGroupName}
        , m_configSubgroupName{configSubgroupName}
    {
        widget.installEventFilter(this);
    }

    bool restoreWidgetGeometry() const
    {
        return widget().restoreGeometry(configGroup().readEntry(entryName(), QByteArray{}));
    }

    bool eventFilter(QObject* watched, QEvent* event) override
    {
        // Saving geometry on a nonspontaneous hide event is the only choice that is both reliable and safe:
        // * the close event is received only when a dialog is dismissed via
        //   its standard window close button and not when it is accepted or rejected;
        // * the QDialog::finished() signal is unavailable for non-dialog widgets (less general) and is not emitted
        //   if the dialog is explicitly hidden or destroyed while visible rather than finished normally;
        // * Qt documentation does not directly promise to emit the QObject::destroyed() signal from ~QWidget()
        //   as opposed to the too-late ~QObject(). Also Qt developers do not guarantee that the state
        //   of the widget being destroyed can be safely accessed in a slot connected to this signal.
        //   Some parts of the widget, e.g. its layout, are destroyed before the destroyed() signal is emitted.
        // A downside of handling the hide event is that when a widget is hidden, then shown again, its geometry
        // is saved multiple times redundantly. Fortunately, a dialog is unlikely to be shown again
        // after being hidden. If the redundant saving becomes a bottleneck for some widget, the visibility
        // of which changes often, a custom saving of geometry can be implemented in that widget's destructor.
        // See also the discussion of when to save geometry at https://codereview.qt-project.org/c/qt/qtbase/+/498797
        if (event->type() == QEvent::Hide && !event->spontaneous()) {
            const auto& widget = this->widget();
            Q_ASSERT(watched == &widget);
            configGroup().writeEntry(entryName(), widget.saveGeometry());
        }
        return false; // do not filter any events out
    }

private:
    static QString entryName()
    {
        return QStringLiteral("windowGeometry");
    }

    QWidget& widget() const
    {
        Q_ASSERT(parent());
        Q_ASSERT(parent()->isWidgetType());
        return *static_cast<QWidget*>(parent());
    }

    KConfigGroup configGroup() const
    {
        KConfigGroup group(KSharedConfig::openConfig(), m_configGroupName);
        if (m_configSubgroupName.isEmpty()) {
            return group;
        }
        KConfigGroup subgroup(&group, m_configSubgroupName);
        return subgroup;
    }

    const QString m_configGroupName;
    const QString m_configSubgroupName;
};

} // unnamed namespace

namespace KDevelop {
bool askUser(const QString& mainText,
             const QString& ttyPrompt,
             const QString& mboxTitle,
             const QString& mboxAdditionalText,
             const QString& confirmText,
             const QString& rejectText,
             bool ttyDefaultToYes)
{
    if (!qobject_cast<QGuiApplication*>(qApp)) {
        // no ui-mode e.g. for duchainify and other tools
        QTextStream out(stdout);
        out << mainText << Qt::endl;
        QTextStream in(stdin);
        QString input;
        while (true) {
            if (ttyDefaultToYes) {
                out << ttyPrompt << QLatin1String(": [Y/n] ") << Qt::flush;
            } else {
                out << ttyPrompt << QLatin1String(": [y/N] ") << Qt::flush;
            }
            input = in.readLine().trimmed();
            if (input.isEmpty()) {
                return ttyDefaultToYes;
            } else if (input.toLower() == QLatin1String("y")) {
                return true;
            } else if (input.toLower() == QLatin1String("n")) {
                return false;
            }
        }
    } else {
        auto okButton = KStandardGuiItem::ok();
        okButton.setText(confirmText);
        auto rejectButton = KStandardGuiItem::cancel();
        rejectButton.setText(rejectText);
        int userAnswer = KMessageBox::questionTwoActions(ICore::self()->uiController()->activeMainWindow(),
                                                         mainText + QLatin1String("\n\n") + mboxAdditionalText,
                                                         mboxTitle, okButton, rejectButton);
        return userAnswer == KMessageBox::PrimaryAction;
    }
}

bool ensureWritable(const QList<QUrl>& urls)
{
    QStringList notWritable;
    for (const QUrl& url : urls) {
        if (url.isLocalFile()) {
            QFile file(url.toLocalFile());
            if (file.exists() && !(file.permissions() & QFileDevice::WriteOwner) &&
                !(file.permissions() & QFileDevice::WriteGroup)) {
                notWritable << url.toLocalFile();
            }
        }
    }

    if (!notWritable.isEmpty()) {
        int answer = KMessageBox::questionTwoActionsCancel(
            ICore::self()->uiController()->activeMainWindow(),
            i18n("You don't have write permissions for the following files; add write permissions for owner before "
                 "saving?")
                + QLatin1String("\n\n") + notWritable.join(QLatin1Char('\n')),
            i18nc("@title:window", "Some Files are Write-Protected"),
            KGuiItem(i18nc("@action:button", "Set Write Permissions"), QStringLiteral("dialog-ok")),
            KGuiItem(i18nc("@action:button", "Ignore"), QStringLiteral("dialog-cancel")), KStandardGuiItem::cancel());
        if (answer == KMessageBox::PrimaryAction) {
            bool success = true;
            for (const QString& filename : std::as_const(notWritable)) {
                QFile file(filename);
                QFileDevice::Permissions permissions = file.permissions();
                permissions |= QFileDevice::WriteOwner;
                success &= file.setPermissions(permissions);
            }

            if (!success) {
                KMessageBox::error(ICore::self()->uiController()->activeMainWindow(),
                                   i18n("Failed adding write permissions for some files."),
                                   i18nc("@title:window", "Failed Setting Permissions"));
                return false;
            }
        }
        return answer != KMessageBox::Cancel;
    }
    return true;
}

bool restoreAndAutoSaveGeometry(QWidget& widget, const QString& configGroupName, const QString& configSubgroupName)
{
    const auto* const saver = new WidgetGeometrySaver(widget, configGroupName, configSubgroupName);
    return saver->restoreWidgetGeometry();
}
}

#include "shellutils.moc"