1 /****************************************************************************
2 **
3 ** Copyright (C) 2021 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 #include "eventlist.h"
26 #include "eventlistpluginview.h"
27 #include "eventlistview.h"
28 #include "nodelistview.h"
29 
30 #include "bindingproperty.h"
31 #include "metainfo.h"
32 #include "projectexplorer/project.h"
33 #include "projectexplorer/session.h"
34 #include "qmldesignerplugin.h"
35 #include "signalhandlerproperty.h"
36 #include "utils/fileutils.h"
37 #include "utils/qtcassert.h"
38 #include "variantproperty.h"
39 
40 #include <QDirIterator>
41 #include <QStandardItemModel>
42 
43 namespace QmlDesigner {
44 
projectFilePath()45 Utils::FilePath projectFilePath()
46 {
47     if (auto *doc = QmlDesignerPlugin::instance()->documentManager().currentDesignDocument()) {
48         if (auto *proj = ProjectExplorer::SessionManager::projectForFile(doc->fileName()))
49             return proj->projectDirectory();
50     }
51     return Utils::FilePath();
52 }
53 
findFile(const Utils::FilePath & path,const QString & fileName)54 static Utils::FilePath findFile(const Utils::FilePath &path, const QString &fileName)
55 {
56     QDirIterator it(path.toString(), QDirIterator::Subdirectories);
57 
58     while (it.hasNext()) {
59         QFileInfo file(it.next());
60         if (file.isDir())
61             continue;
62 
63         if (file.fileName() == fileName)
64             return Utils::FilePath::fromFileInfo(file);
65     }
66     return {};
67 }
68 
69 
70 NodeListView *EventList::st_nodeView = nullptr;
71 
setNodeProperties(AbstractView * view)72 void EventList::setNodeProperties(AbstractView *view)
73 {
74     st_nodeView = new NodeListView(view);
75 }
76 
selectNode(int internalId)77 void EventList::selectNode(int internalId)
78 {
79     if (st_nodeView)
80         st_nodeView->selectNode(internalId);
81 }
82 
currentNode()83 int EventList::currentNode()
84 {
85     if (st_nodeView)
86         return st_nodeView->currentNode();
87 
88     return -1;
89 }
90 
hasEventListModel()91 bool EventList::hasEventListModel()
92 {
93     Utils::FilePath projectPath = projectFilePath();
94     if (projectPath.isEmpty())
95         return false;
96 
97     Utils::FilePath path = findFile(projectPath, "EventListModel.qml");
98     return path.exists();
99 }
100 
addEventIdToCurrent(const QString & eventId)101 void EventList::addEventIdToCurrent(const QString &eventId)
102 {
103     int iid = currentNode();
104     if (st_nodeView && iid >= 0)
105         st_nodeView->addEventId(iid, eventId);
106 }
107 
removeEventIdFromCurrent(const QString & eventId)108 void EventList::removeEventIdFromCurrent(const QString &eventId)
109 {
110     int iid = currentNode();
111     if (st_nodeView && iid >= 0)
112         st_nodeView->removeEventIds(iid, {eventId});
113 }
114 
setNodeId(int internalId,const QString & id)115 QString EventList::setNodeId(int internalId, const QString &id)
116 {
117     if (st_nodeView)
118         return st_nodeView->setNodeId(internalId, id);
119 
120     return QString();
121 }
122 
nodeModel()123 QStandardItemModel *EventList::nodeModel()
124 {
125     if (st_nodeView)
126         return st_nodeView->itemModel();
127 
128     return nullptr;
129 }
130 
nodeListView()131 NodeListView *EventList::nodeListView()
132 {
133     return st_nodeView;
134 }
135 
modelNode(const QString & id)136 ModelNode EventList::modelNode(const QString &id)
137 {
138     if (st_nodeView)
139         return st_nodeView->modelNodeForId(id);
140 
141     return ModelNode();
142 }
143 
setSignalSource(SignalHandlerProperty & prop,const QString & source)144 void EventList::setSignalSource(SignalHandlerProperty &prop, const QString &source)
145 {
146     if (st_nodeView) {
147         QmlDesigner::Import import =
148             QmlDesigner::Import::createLibraryImport("QtQuick.Studio.EventSystem", "1.0");
149 
150         if (!st_nodeView->model()->hasImport(import, true, true)) {
151             try {
152                 st_nodeView->model()->changeImports({import}, {});
153             } catch (const QmlDesigner::Exception &) {
154                 QTC_ASSERT(false, return );
155             }
156         }
157 
158         if (source == "{}") {
159             if (ModelNode node = prop.parentModelNode(); node.isValid()) {
160                 st_nodeView->executeInTransaction("EventList::removeProperty",
161                     [&]() { node.removeProperty(prop.name()); });
162             }
163         }
164         else {
165             st_nodeView->executeInTransaction("EventList::setSource",
166                 [&]() { prop.setSource(source); });
167         }
168     }
169 }
170 
EventList()171 EventList::EventList()
172     : m_model(nullptr)
173     , m_eventView(nullptr)
174     , m_path()
175 
176 {}
177 
model() const178 Model *EventList::model() const
179 {
180     return m_model;
181 }
182 
view() const183 EventListView *EventList::view() const
184 {
185     return m_eventView;
186 }
187 
read() const188 QString EventList::read() const
189 {
190     if (!m_path.exists())
191         return QString();
192 
193     Utils::FileReader reader;
194     QTC_ASSERT(reader.fetch(m_path), return QString());
195 
196     return QString::fromUtf8(reader.data());
197 }
198 
199 
initialize(EventListPluginView * parent)200 void EventList::initialize(EventListPluginView *parent)
201 {
202     Utils::FilePath projectPath = projectFilePath();
203     QTC_ASSERT(!projectPath.isEmpty(), return );
204     m_path = findFile(projectPath, "EventListModel.qml");
205 
206     if (!m_model) {
207         QByteArray unqualifiedTypeName = "ListModel";
208         auto metaInfo = parent->model()->metaInfo(unqualifiedTypeName);
209 
210         QByteArray fullTypeName = metaInfo.typeName();
211         int minorVersion = metaInfo.minorVersion();
212         int majorVersion = metaInfo.majorVersion();
213 
214         m_model = Model::create(fullTypeName, majorVersion, minorVersion);
215         m_model->setParent(parent);
216     }
217 
218     if (!m_eventView) {
219         m_eventView = new EventListView(m_model);
220         m_model->attachView(m_eventView);
221     }
222 }
223 
write(const QString & text)224 void EventList::write(const QString &text)
225 {
226     if (!m_path.exists())
227         return;
228 
229     Utils::FileSaver writer(m_path);
230     writer.write(text.toUtf8());
231     writer.finalize();
232 }
233 
234 } // namespace QmlDesigner.
235