Unity 8
indicatorsmanager.cpp
1 /*
2  * Copyright (C) 2013 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  * Author: Nick Dedekind <nick.dedekind@canonical.com>
17  */
18 
19 #include "indicatorsmanager.h"
20 
21 #include <QSettings>
22 #include <QDebug>
23 
24 #include <paths.h>
25 
26 
27 class IndicatorsManager::IndicatorData
28 {
29 public:
30  IndicatorData(const QString& name, const QFileInfo& fileInfo)
31  : m_name(name)
32  , m_fileInfo(fileInfo)
33  , m_verified (true)
34  {
35  }
36 
37  QString m_name;
38  QFileInfo m_fileInfo;
39 
40  bool m_verified;
41  Indicator::Ptr m_indicator;
42 };
43 
44 IndicatorsManager::IndicatorsManager(QObject* parent)
45  : QObject(parent)
46  , m_loaded(false)
47  , m_profile(QStringLiteral("phone"))
48 {
49 }
50 
51 IndicatorsManager::~IndicatorsManager()
52 {
53  unload();
54 
55 }
56 
57 void IndicatorsManager::load()
58 {
59  unload();
60 
61  QStringList xdgLocations = shellDataDirs();//QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
62 
63  m_fsWatcher.reset(new QFileSystemWatcher(this));
64 
65  Q_FOREACH(const QString& xdgLocation, xdgLocations)
66  {
67  QString indicator_path = QDir::cleanPath(xdgLocation + "/unity/indicators");
68  QDir indicator_dir(indicator_path);
69  if (indicator_dir.exists())
70  {
71  // watch folder for changes.
72  m_fsWatcher->addPath(indicator_path);
73 
74  loadDir(indicator_dir);
75  }
76  }
77 
78  QObject::connect(m_fsWatcher.data(), &QFileSystemWatcher::directoryChanged, this, &IndicatorsManager::onDirectoryChanged);
79  QObject::connect(m_fsWatcher.data(), &QFileSystemWatcher::fileChanged, this, &IndicatorsManager::onFileChanged);
80  setLoaded(true);
81 }
82 
83 void IndicatorsManager::onDirectoryChanged(const QString& directory)
84 {
85  loadDir(QDir(directory));
86 }
87 
88 void IndicatorsManager::onFileChanged(const QString& file)
89 {
90  QFileInfo file_info(file);
91  if (!file_info.exists())
92  {
93  unloadFile(file_info);
94  return;
95  }
96  else
97  {
98  loadFile(QFileInfo(file));
99  }
100 }
101 
102 void IndicatorsManager::loadDir(const QDir& dir)
103 {
104  startVerify(dir.canonicalPath());
105 
106  QFileInfoList indicator_files = dir.entryInfoList(QStringList(), QDir::Files|QDir::NoDotAndDotDot);
107  Q_FOREACH(const QFileInfo& indicator_file, indicator_files)
108  {
109  loadFile(indicator_file);
110  }
111 
112  endVerify(dir.canonicalPath());
113 }
114 
115 void IndicatorsManager::loadFile(const QFileInfo& file_info)
116 {
117  QSettings indicator_settings(file_info.absoluteFilePath(), QSettings::IniFormat, this);
118  QString name = indicator_settings.value(QStringLiteral("Indicator Service/Name")).toString();
119 
120  auto iter = m_indicatorsData.find(name);
121  if (iter != m_indicatorsData.end())
122  {
123  QString newFileInfoDir = QDir::cleanPath(file_info.canonicalPath());
124  IndicatorData* currentData = (*iter);
125  currentData->m_verified = true;
126 
127  int file_info_location = -1;
128  int current_data_location = -1;
129 
130  QString currentDataDir = QDir::cleanPath(currentData->m_fileInfo.canonicalPath());
131 
132  // if we've already got this indicator, we need to make sure we're not overwriting data which is
133  // from a lower priority standard path
134  QStringList xdgLocations = shellDataDirs();
135  for (int i = 0; i < xdgLocations.size(); i++)
136  {
137  QString indicatorDir = QDir::cleanPath(xdgLocations[i] + "/unity/indicators");
138 
139  if (newFileInfoDir == indicatorDir)
140  {
141  file_info_location = i;
142  }
143  if (currentDataDir == indicatorDir)
144  {
145  current_data_location = i;
146  }
147 
148  if (file_info_location != -1 && current_data_location != -1)
149  {
150  break;
151  }
152  }
153 
154  // file location is higher (or of equal) priority. overwrite.
155  if (file_info_location <= current_data_location &&
156  file_info != currentData->m_fileInfo)
157  {
158  currentData->m_fileInfo = file_info;
159  Q_EMIT indicatorLoaded(name);
160  }
161  }
162  else
163  {
164  IndicatorData* data = new IndicatorData(name, file_info);
165  data->m_verified = true;
166  m_indicatorsData[name]= data;
167  Q_EMIT indicatorLoaded(name);
168  }
169 }
170 
171 void IndicatorsManager::unload()
172 {
173  QHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
174  while(iter.hasNext())
175  {
176  iter.next();
177  Q_EMIT indicatorAboutToBeUnloaded(iter.key());
178  }
179 
180  qDeleteAll(m_indicatorsData);
181  m_indicatorsData.clear();
182 
183  setLoaded(false);
184 }
185 
186 void IndicatorsManager::unloadFile(const QFileInfo& file)
187 {
188  QMutableHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
189  while(iter.hasNext())
190  {
191  iter.next();
192  IndicatorData* data = iter.value();
193  if (data->m_fileInfo.absoluteFilePath() == file.absoluteFilePath())
194  {
195  if (!data->m_verified)
196  {
197  QString name = data->m_name;
198  Q_EMIT indicatorAboutToBeUnloaded(name);
199 
200  delete data;
201  iter.remove();
202  }
203  }
204  }
205 
206  setLoaded(m_indicatorsData.size() > 0);
207 }
208 
209 void IndicatorsManager::setLoaded(bool loaded)
210 {
211  if (loaded != m_loaded)
212  {
213  m_loaded = loaded;
214  Q_EMIT loadedChanged(m_loaded);
215  }
216 }
217 
218 QString IndicatorsManager::profile() const
219 {
220  return m_profile;
221 }
222 
223 void IndicatorsManager::setProfile(const QString& profile)
224 {
225  if (m_profile != profile) {
226  m_profile = profile;
227  Q_EMIT profileChanged(m_profile);
228  }
229 }
230 
231 void IndicatorsManager::startVerify(const QString& path)
232 {
233  QHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
234  while(iter.hasNext())
235  {
236  iter.next();
237  IndicatorData* data = iter.value();
238  if (data->m_fileInfo.canonicalPath() == path)
239  {
240  data->m_verified = false;
241  }
242  }
243 }
244 
245 void IndicatorsManager::endVerify(const QString& path)
246 {
247  QMutableHashIterator<QString, IndicatorData*> iter(m_indicatorsData);
248  while(iter.hasNext())
249  {
250  iter.next();
251  IndicatorData* data = iter.value();
252  if (data->m_fileInfo.canonicalPath() == path)
253  {
254  if (!data->m_verified)
255  {
256  QString name = data->m_name;
257  Q_EMIT indicatorAboutToBeUnloaded(name);
258 
259  delete data;
260  iter.remove();
261  }
262  }
263  }
264 }
265 
266 Indicator::Ptr IndicatorsManager::indicator(const QString& indicator_name)
267 {
268  if (!m_indicatorsData.contains(indicator_name))
269  {
270  qWarning() << Q_FUNC_INFO << "Invalid indicator name: " << indicator_name;
271  return Indicator::Ptr();
272  }
273 
274  IndicatorData *data = m_indicatorsData[indicator_name];
275  if (data->m_indicator)
276  {
277  return data->m_indicator;
278  }
279 
280  Indicator::Ptr new_indicator(new Indicator(this));
281  data->m_indicator = new_indicator;
282  QSettings settings(data->m_fileInfo.absoluteFilePath(), QSettings::IniFormat, this);
283  new_indicator->init(data->m_fileInfo.fileName(), settings);
284  new_indicator->setProfile(m_profile);
285  QObject::connect(this, &IndicatorsManager::profileChanged, new_indicator.data(), &Indicator::setProfile);
286  return new_indicator;
287 }
288 
289 QVector<Indicator::Ptr> IndicatorsManager::indicators()
290 {
291  QVector<Indicator::Ptr> list;
292  Q_FOREACH(IndicatorData* data, m_indicatorsData)
293  {
294  Indicator::Ptr ret = indicator(data->m_name);
295  if (ret)
296  list.append(ret);
297  }
298  return list;
299 }
300 
301 bool IndicatorsManager::isLoaded() const
302 {
303  return m_loaded;
304 }