1 /*
2  * SPDX-FileCopyrightText: 2017~2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: GPL-2.0-or-later
5  *
6  */
7 #include "listoptionwidget.h"
8 #include "varianthelper.h"
9 #include <QAbstractListModel>
10 #include <QDebug>
11 #include <QtGlobal>
12 
13 namespace fcitx {
14 namespace kcm {
15 
16 class ListOptionWidgetModel : public QAbstractListModel {
17 public:
ListOptionWidgetModel(ListOptionWidget * parent)18     ListOptionWidgetModel(ListOptionWidget *parent)
19         : QAbstractListModel(parent), parent_(parent) {}
20 
data(const QModelIndex & index,int role=Qt::DisplayRole) const21     QVariant data(const QModelIndex &index,
22                   int role = Qt::DisplayRole) const override {
23         if (!index.isValid() || index.row() >= values_.size()) {
24             return QVariant();
25         }
26         const auto &value = values_.at(index.row());
27 
28         switch (role) {
29         case Qt::DisplayRole:
30             return parent_->prettify(parent_->subOption(), value);
31         case Qt::UserRole:
32             return value;
33         }
34         return QVariant();
35     }
36 
rowCount(const QModelIndex & parent=QModelIndex ()) const37     int rowCount(const QModelIndex &parent = QModelIndex()) const override {
38         if (parent.isValid()) {
39             return 0;
40         }
41 
42         return values_.size();
43     }
44 
readValueFrom(const QVariantMap & map,const QString & path)45     void readValueFrom(const QVariantMap &map, const QString &path) {
46         beginResetModel();
47         int i = 0;
48         values_.clear();
49         while (true) {
50             auto value = readVariant(map, QString("%1%2%3")
51                                               .arg(path)
52                                               .arg(path.isEmpty() ? "" : "/")
53                                               .arg(i));
54             if (value.isNull()) {
55                 break;
56             }
57             values_ << value;
58             i++;
59         }
60         endResetModel();
61     }
62 
writeValueTo(QVariantMap & map,const QString & path)63     void writeValueTo(QVariantMap &map, const QString &path) {
64         int i = 0;
65         for (auto &value : values_) {
66             writeVariant(map, QString("%1/%2").arg(path).arg(i), value);
67             i++;
68         }
69         if (!i) {
70             map[path] = QVariantMap();
71         }
72     }
73 
addItem(QVariant value)74     void addItem(QVariant value) {
75         beginInsertRows(QModelIndex(), values_.size(), values_.size());
76         values_.append(value);
77         endInsertRows();
78     }
79 
editItem(const QModelIndex & index,QVariant value)80     void editItem(const QModelIndex &index, QVariant value) {
81         if (!index.isValid() || index.row() >= values_.size()) {
82             return;
83         }
84 
85         values_[index.row()] = value;
86         Q_EMIT dataChanged(index, index);
87     }
88 
removeItem(const QModelIndex & index)89     void removeItem(const QModelIndex &index) {
90         if (!index.isValid() || index.row() >= values_.size()) {
91             return;
92         }
93         beginRemoveRows(index.parent(), index.row(), index.row());
94         values_.removeAt(index.row());
95         endRemoveRows();
96     }
97 
moveUpItem(const QModelIndex & index)98     void moveUpItem(const QModelIndex &index) {
99         if (!index.isValid() || index.row() >= values_.size() ||
100             index.row() == 0) {
101             return;
102         }
103         Q_EMIT layoutAboutToBeChanged();
104         if (!beginMoveRows(index.parent(), index.row(), index.row(),
105                            index.parent(), index.row() - 1)) {
106             return;
107         }
108 #if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
109         values_.swap(index.row() - 1, index.row());
110 #else
111         values_.swapItemsAt(index.row() - 1, index.row());
112 #endif
113         endMoveRows();
114     }
115 
moveDownItem(const QModelIndex & index)116     void moveDownItem(const QModelIndex &index) {
117         if (!index.isValid() || index.row() >= values_.size() ||
118             index.row() + 1 == values_.size()) {
119             return;
120         }
121         if (!beginMoveRows(index.parent(), index.row(), index.row(),
122                            index.parent(), index.row() + 2)) {
123             return;
124         }
125 #if (QT_VERSION < QT_VERSION_CHECK(5, 13, 0))
126         values_.swap(index.row(), index.row() + 1);
127 #else
128         values_.swapItemsAt(index.row(), index.row() + 1);
129 #endif
130         endMoveRows();
131     }
132 
133 private:
134     QList<QVariant> values_;
135     ListOptionWidget *parent_;
136 };
137 
ListOptionWidget(const FcitxQtConfigOption & option,const QString & path,QWidget * parent)138 ListOptionWidget::ListOptionWidget(const FcitxQtConfigOption &option,
139                                    const QString &path, QWidget *parent)
140     : OptionWidget(path, parent), model_(new ListOptionWidgetModel(this)),
141       subOption_(option) {
142     setupUi(this);
143     listView->setModel(model_);
144 
145     subOption_.setType(option.type().mid(5)); // Remove List|
146     auto props = option.properties();
147     if (props.contains("ListConstrain")) {
148         auto itemConstrain = props.value("ListConstrain").toMap();
149         props.remove("ListConstrain");
150         for (auto iter = itemConstrain.begin(), end = itemConstrain.end();
151              iter != end; ++iter) {
152             props[iter.key()] = iter.value();
153         }
154     }
155     subOption_.setProperties(props);
156     subOption_.setDefaultValue(QDBusVariant());
157 
158     connect(listView->selectionModel(), &QItemSelectionModel::currentRowChanged,
159             this, [this]() { updateButton(); });
160 
161     connect(model_, &QAbstractListModel::rowsMoved, this,
162             [this]() { updateButton(); });
163     connect(addButton, &QAbstractButton::clicked, this, [this]() {
164         QVariant result;
165         auto ok = OptionWidget::execOptionDialog(this, subOption_, result);
166         if (ok) {
167             model_->addItem(result);
168         }
169     });
170     connect(editButton, &QAbstractButton::clicked, this, [this]() {
171         QVariant result = model_->data(listView->currentIndex(), Qt::UserRole);
172         auto ok = OptionWidget::execOptionDialog(this, subOption_, result);
173         if (ok) {
174             model_->editItem(listView->currentIndex(), result);
175         }
176     });
177     connect(removeButton, &QAbstractButton::clicked, this,
178             [this]() { model_->removeItem(listView->currentIndex()); });
179     connect(moveUpButton, &QAbstractButton::clicked, this,
180             [this]() { model_->moveUpItem(listView->currentIndex()); });
181     connect(moveDownButton, &QAbstractButton::clicked, this,
182             [this]() { model_->moveDownItem(listView->currentIndex()); });
183 
184     auto variant = option.defaultValue().variant();
185     if (variant.canConvert<QDBusArgument>()) {
186         auto argument = qvariant_cast<QDBusArgument>(variant);
187         argument >> defaultValue_;
188     }
189 
190     updateButton();
191 }
192 
updateButton()193 void ListOptionWidget::updateButton() {
194     editButton->setEnabled(listView->currentIndex().isValid());
195     removeButton->setEnabled(listView->currentIndex().isValid());
196     moveUpButton->setEnabled(listView->currentIndex().isValid() &&
197                              listView->currentIndex().row() != 0);
198     moveDownButton->setEnabled(listView->currentIndex().isValid() &&
199                                listView->currentIndex().row() !=
200                                    model_->rowCount() - 1);
201 }
202 
readValueFrom(const QVariantMap & map)203 void ListOptionWidget::readValueFrom(const QVariantMap &map) {
204     model_->readValueFrom(map, path());
205 }
206 
writeValueTo(QVariantMap & map)207 void ListOptionWidget::writeValueTo(QVariantMap &map) {
208     model_->writeValueTo(map, path());
209 }
210 
restoreToDefault()211 void ListOptionWidget::restoreToDefault() {
212     model_->readValueFrom(defaultValue_, "");
213 }
214 
215 } // namespace kcm
216 } // namespace fcitx
217