Lomiri
Loading...
Searching...
No Matches
indicatorsmanager.cpp
1/*
2 * Copyright (C) 2013-2016 Canonical Ltd.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16
17#include "indicatorsmanager.h"
18
19#include <QSettings>
20#include <QDebug>
21
22#include <paths.h>
23
24
25class IndicatorsManager::IndicatorData
26{
27public:
28 IndicatorData(const QString& name, const QFileInfo& fileInfo)
29 : m_name(name)
30 , m_fileInfo(fileInfo)
31 , m_verified (true)
32 {
33 }
34
35 QString m_name;
36 QFileInfo m_fileInfo;
37
38 bool m_verified;
39 Indicator::Ptr m_indicator;
40};
41
42IndicatorsManager::IndicatorsManager(QObject* parent)
43 : QObject(parent)
44 , m_loaded(false)
45 , m_profile(QStringLiteral("phone"))
46{
47}
48
49IndicatorsManager::~IndicatorsManager()
50{
51 unload();
52}
53
54void IndicatorsManager::load()
55{
56 unload();
57
58 m_fsWatcher.reset(new QFileSystemWatcher(this));
59
60 for (const auto &xdgPath : shellDataDirs()) {
61 // For legacy reasons we keep the old unity indicator path
62 const auto unityPath = QDir::cleanPath(xdgPath + "/unity/indicators");
63 if (QFile::exists(unityPath)) {
64 // watch folder for changes.
65 m_fsWatcher->addPath(unityPath);
66 loadDir(unityPath);
67 }
68
69 const auto ayatanaPath = QDir::cleanPath(xdgPath + "/ayatana/indicators");
70 if (QFile::exists(ayatanaPath)) {
71 // watch folder for changes.
72 m_fsWatcher->addPath(ayatanaPath);
73 loadDir(ayatanaPath);
74 }
75 }
76
77 QObject::connect(m_fsWatcher.data(), &QFileSystemWatcher::directoryChanged, this, &IndicatorsManager::onDirectoryChanged);
78 QObject::connect(m_fsWatcher.data(), &QFileSystemWatcher::fileChanged, this, &IndicatorsManager::onFileChanged);
79 setLoaded(true);
80}
81
82void IndicatorsManager::onDirectoryChanged(const QString& directory)
83{
84 loadDir(QDir(directory));
85}
86
87void IndicatorsManager::onFileChanged(const QString& file)
88{
89 QFileInfo file_info(file);
90 if (!file_info.exists())
91 {
92 unloadFile(file_info);
93 return;
94 }
95 else
96 {
97 loadFile(QFileInfo(file));
98 }
99}
100
101void IndicatorsManager::loadDir(const QDir& dir)
102{
103 startVerify(dir.canonicalPath());
104
105 const QFileInfoList indicator_files = dir.entryInfoList(QStringList(), QDir::Files|QDir::NoDotAndDotDot);
106 Q_FOREACH(const QFileInfo& indicator_file, indicator_files)
107 {
108 loadFile(indicator_file);
109 }
110
111 endVerify(dir.canonicalPath());
112}
113
114void IndicatorsManager::loadFile(const QFileInfo& file_info)
115{
116 QSettings indicator_settings(file_info.absoluteFilePath(), QSettings::IniFormat, this);
117 const QString name = indicator_settings.value(QStringLiteral("Indicator Service/Name")).toString();
118
119 auto iter = m_indicatorsData.constFind(name);
120 if (iter != m_indicatorsData.constEnd())
121 {
122 const QString newFileInfoDir = QDir::cleanPath(file_info.canonicalPath());
123 IndicatorData* currentData = (*iter);
124 currentData->m_verified = true;
125
126 int file_info_location = -1;
127 int current_data_location = -1;
128
129 const QString currentDataDir = QDir::cleanPath(currentData->m_fileInfo.canonicalPath());
130
131 // if we've already got this indicator, we need to make sure we're not overwriting data which is
132 // from a lower priority standard path
133 QStringList xdgLocations = shellDataDirs();
134 for (int i = 0; i < xdgLocations.size(); i++)
135 {
136 const QString xdgLocation = QDir::cleanPath(xdgLocations[i]);
137
138 if (newFileInfoDir.startsWith(xdgLocation))
139 {
140 file_info_location = i;
141 }
142 if (currentDataDir.startsWith(xdgLocation))
143 {
144 current_data_location = i;
145 }
146
147 if (file_info_location != -1 && current_data_location != -1)
148 {
149 break;
150 }
151 }
152
153 // file location is higher (or of equal) priority. overwrite.
154 if (file_info_location <= current_data_location &&
155 file_info != currentData->m_fileInfo)
156 {
157 currentData->m_fileInfo = file_info;
158 Q_EMIT indicatorLoaded(name);
159 }
160 }
161 else
162 {
163 IndicatorData* data = new IndicatorData(name, file_info);
164 data->m_verified = true;
165 m_indicatorsData[name]= data;
166 Q_EMIT indicatorLoaded(name);
167 }
168}
169
170void IndicatorsManager::unload()
171{
172 QHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
173 while(iter.hasNext())
174 {
175 iter.next();
176 Q_EMIT indicatorAboutToBeUnloaded(iter.key());
177 }
178
179 qDeleteAll(m_indicatorsData);
180 m_indicatorsData.clear();
181
182 setLoaded(false);
183}
184
185void IndicatorsManager::unloadFile(const QFileInfo& file)
186{
187 QMutableHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
188 while(iter.hasNext())
189 {
190 iter.next();
191 IndicatorData* data = iter.value();
192 if (data->m_fileInfo.absoluteFilePath() == file.absoluteFilePath())
193 {
194 if (!data->m_verified)
195 {
196 const QString name = data->m_name;
197 Q_EMIT indicatorAboutToBeUnloaded(name);
198
199 delete data;
200 iter.remove();
201 }
202 }
203 }
204
205 setLoaded(m_indicatorsData.size() > 0);
206}
207
208void IndicatorsManager::setLoaded(bool loaded)
209{
210 if (loaded != m_loaded)
211 {
212 m_loaded = loaded;
213 Q_EMIT loadedChanged(m_loaded);
214 }
215}
216
217QString IndicatorsManager::profile() const
218{
219 return m_profile;
220}
221
222void IndicatorsManager::setProfile(const QString& profile)
223{
224 if (m_profile != profile) {
225 m_profile = profile;
226 Q_EMIT profileChanged(m_profile);
227 }
228}
229
230void IndicatorsManager::startVerify(const QString& path)
231{
232 QHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
233 while(iter.hasNext())
234 {
235 iter.next();
236 IndicatorData* data = iter.value();
237 if (data->m_fileInfo.canonicalPath() == path)
238 {
239 data->m_verified = false;
240 }
241 }
242}
243
244void IndicatorsManager::endVerify(const QString& path)
245{
246 QMutableHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
247 while(iter.hasNext())
248 {
249 iter.next();
250 IndicatorData* data = iter.value();
251 if (data->m_fileInfo.canonicalPath() == path)
252 {
253 if (!data->m_verified)
254 {
255 const QString name = data->m_name;
256 Q_EMIT indicatorAboutToBeUnloaded(name);
257
258 delete data;
259 iter.remove();
260 }
261 }
262 }
263}
264
265Indicator::Ptr IndicatorsManager::indicator(const QString& indicator_name)
266{
267 if (!m_indicatorsData.contains(indicator_name))
268 {
269 qWarning() << Q_FUNC_INFO << "Invalid indicator name: " << indicator_name;
270 return Indicator::Ptr();
271 }
272
273 IndicatorData *data = m_indicatorsData.value(indicator_name);
274 if (data->m_indicator)
275 {
276 return data->m_indicator;
277 }
278
279 Indicator::Ptr new_indicator(new Indicator(this));
280 data->m_indicator = new_indicator;
281 QSettings settings(data->m_fileInfo.absoluteFilePath(), QSettings::IniFormat, this);
282 new_indicator->init(data->m_fileInfo.fileName(), settings);
283
284 // convergence:
285 // 1) enable session indicator
286 // 2) enable keyboard indicator
287 //
288 // The rest of the indicators respect their default profile (which is "phone", even on desktop PCs)
289 if ((new_indicator->identifier() == QStringLiteral("ayatana-indicator-session"))
290 || new_indicator->identifier() == QStringLiteral("ayatana-indicator-keyboard")) {
291 new_indicator->setProfile(QString(m_profile).replace(QStringLiteral("phone"), QStringLiteral("desktop")));
292 } else {
293 new_indicator->setProfile(m_profile);
294 }
295
296 QObject::connect(this, &IndicatorsManager::profileChanged, new_indicator.data(), &Indicator::setProfile);
297 return new_indicator;
298}
299
300QVector<Indicator::Ptr> IndicatorsManager::indicators()
301{
302 QVector<Indicator::Ptr> list;
303 Q_FOREACH(IndicatorData* data, m_indicatorsData)
304 {
305 Indicator::Ptr ret = indicator(data->m_name);
306 if (ret)
307 list.append(ret);
308 }
309 return list;
310}
311
312bool IndicatorsManager::isLoaded() const
313{
314 return m_loaded;
315}