datapaths.cpp
1 //------------------------------------------------------------------------------
2 // datapaths.cpp
3 //------------------------------------------------------------------------------
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301, USA.
19 //
20 //------------------------------------------------------------------------------
21 // Copyright (C) 2010 "Zalewa" <zalewapl@gmail.com>
22 //------------------------------------------------------------------------------
23 #include "datapaths.h"
24 
25 #include <QCoreApplication>
26 #include "strings.h"
27 #include <QDesktopServices>
28 #include <cassert>
29 #include <cstdlib>
30 
31 DClass<DataPaths>
32 {
33  public:
34  bool bIsPortableModeOn;
35  QString programsDirectoryName;
36  QString programsSupportDirectoryName;
37  QString demosDirectoryName;
38  QString workingDirectory;
39 };
40 
41 DPointered(DataPaths)
42 
43 DataPaths *DataPaths::staticDefaultInstance = NULL;
44 
45 
46 #ifdef Q_OS_MAC
47 const QString DataPaths::PROGRAMS_APPDATA_DIR_NAME = "Library/Preferences/Doomseeker";
48 const QString DataPaths::PROGRAMS_APPDATASUPPORT_DIR_NAME = "Library/Application Support/Doomseeker";
49 #else
50 const QString DataPaths::PROGRAMS_APPDATA_DIR_NAME = ".doomseeker";
51 const QString DataPaths::PROGRAMS_APPDATASUPPORT_DIR_NAME = "";
52 #endif
53 const QString DataPaths::DEMOS_DIR_NAME = "demos";
54 const QString DataPaths::CHATLOGS_DIR_NAME = "chatlogs";
55 const QString DataPaths::TRANSLATIONS_DIR_NAME = "translations";
56 const QString DataPaths::UPDATE_PACKAGES_DIR_NAME = "updates";
57 const QString DataPaths::UPDATE_PACKAGE_FILENAME_PREFIX = "doomseeker-update-pkg-";
58 
59 DataPaths::DataPaths(bool bPortableModeOn)
60 {
61  d->bIsPortableModeOn = bPortableModeOn;
62 
63  d->programsDirectoryName = PROGRAMS_APPDATA_DIR_NAME;
64  d->programsSupportDirectoryName = PROGRAMS_APPDATASUPPORT_DIR_NAME;
65  d->demosDirectoryName = PROGRAMS_APPDATA_DIR_NAME + QDir::separator() + DEMOS_DIR_NAME;
66  d->workingDirectory = "./";
67 }
68 
69 DataPaths::~DataPaths()
70 {
71 }
72 
73 QStringList DataPaths::canWrite() const
74 {
75  QStringList failedList;
76 
77  QString dataDirectory = programsDataDirectoryPath();
78  if (!validateDir(dataDirectory))
79  {
80  failedList.append(dataDirectory);
81  }
82 
83  return failedList;
84 }
85 
87 {
88  // This variable should only be changed to false and only if something
89  // fails.
90  bool bAllSuccessful = true;
91 
92  QDir appDataDir(systemAppDataDirectory());
93  if (!tryCreateDirectory(appDataDir, programDirName()))
94  {
95  bAllSuccessful = false;
96  }
97  if (!programsDataSupportDirectoryPath().isEmpty()
99  {
100  bAllSuccessful = false;
101  }
102 
103  QDir programDirectory(programsDataDirectoryPath());
104  if (!tryCreateDirectory(programDirectory, "demos"))
105  {
106  bAllSuccessful = false;
107  }
108 
109  return bAllSuccessful;
110 }
111 
113 {
114  return staticDefaultInstance;
115 }
116 
117 QString DataPaths::demosDirectoryPath() const
118 {
119  QString demosDir = systemAppDataDirectory(d->demosDirectoryName);
120  return demosDir;
121 }
122 
123 QStringList DataPaths::directoriesExist() const
124 {
125  QStringList failedList;
126  QList<QDir> checkList;
127 
128  checkList << programsDataDirectoryPath();
129  if (!d->programsSupportDirectoryName.isEmpty())
130  checkList << programsDataSupportDirectoryPath();
131 
132  foreach(const QDir &dataDirectory, checkList)
133  {
134  if (!dataDirectory.exists())
135  {
136  failedList.append(dataDirectory.absolutePath());
137  }
138  }
139 
140  return failedList;
141 }
142 
143 const QString& DataPaths::programDirName() const
144 {
145  return d->programsDirectoryName;
146 }
147 
148 void DataPaths::initDefault(bool bPortableModeOn)
149 {
150  assert(staticDefaultInstance == NULL && "DataPaths can have only one default.");
151  if (staticDefaultInstance == NULL)
152  {
153  staticDefaultInstance = new DataPaths(bPortableModeOn);
154  }
155 }
156 
157 bool DataPaths::isPortableModeOn() const
158 {
159  return d->bIsPortableModeOn;
160 }
161 
162 void DataPaths::setPortableModeOn(bool b)
163 {
164  d->bIsPortableModeOn = b;
165 }
166 
167 void DataPaths::setProgramDirName(const QString& name)
168 {
169  d->programsDirectoryName = name;
170 }
171 
172 QString DataPaths::localDataLocationPath(const QString& subpath) const
173 {
174  // TODO This won't work correctly on Mac because we didn't use
175  // the QDesktopServices from the beginning. Using this class
176  // would save a lot of trouble and simplify code in this file a lot.
177  // Unfortunatelly right now using it would cause compatibility errors for
178  // Linux users who already have Doomseeker installed as the locations
179  // returned by QDesktopServices are different in certain cases. However,
180  // with some work some compromise could be achieved.
181  QString rootPath;
182  if (!isPortableModeOn())
183  {
184  rootPath = QDesktopServices::storageLocation(QDesktopServices::DataLocation);
185  rootPath = Strings::combinePaths(rootPath, ".doomseeker");
186  }
187  else
188  {
189  rootPath = systemAppDataDirectory(".static");
190  }
191  return Strings::combinePaths(rootPath, subpath);
192 }
193 
194 QString DataPaths::programFilesDirectory(MachineType machineType)
195 {
196  #ifdef Q_OS_WIN32
197  QString envVarName = "";
198 
199  switch (machineType)
200  {
201  case x86:
202  envVarName = "ProgramFiles(x86)";
203  break;
204 
205  case x64:
206  envVarName = "ProgramW6432";
207  break;
208 
209  case Preferred:
210  envVarName = "ProgramFiles";
211  break;
212 
213  default:
214  return QString();
215  }
216 
217  QString path = getenv(envVarName.toAscii().constData());
218  if (path.isEmpty() && machineType != Preferred)
219  {
220  // Empty outcome may happen on 32-bit systems where variables
221  // like "ProgramFiles(x86)" may not exist.
222  //
223  // If "ProgramFiles" variable is empty then something is seriously
224  // wrong with the system.
225  path = programFilesDirectory(Preferred);
226  }
227 
228  return path;
229 
230  #else
231  return QString();
232  #endif
233 }
234 
236 {
237  QString appDataDir = systemAppDataDirectory(programDirName());
238  return appDataDir;
239 }
240 
242 {
243  if (isPortableModeOn() || d->programsSupportDirectoryName.isEmpty())
244  return programsDataDirectoryPath();
245 
246  QString appSupportDataDir = systemAppDataDirectory(d->programsSupportDirectoryName);
247  return appSupportDataDir;
248 }
249 
250 void DataPaths::setWorkingDirectory(const QString &workingDirectory)
251 {
252  d->workingDirectory = workingDirectory;
253 }
254 
255 QStringList DataPaths::staticDataSearchDirs(const QString& subdir)
256 {
257  QStringList paths;
258  paths.append(QDir::currentPath()); // current working dir
259  paths.append(QCoreApplication::applicationDirPath()); // where exe is located
260  paths.append("/usr/share/doomseeker"); // standard linux path
261  paths.append("/usr/local/share/doomseeker"); // standard linux path 2
262  QString subdirFiltered = subdir.trimmed();
263  if (!subdirFiltered.isEmpty())
264  {
265  for (int i = 0; i < paths.size(); ++i)
266  {
267  paths[i] = Strings::combinePaths(paths[i], subdirFiltered);
268  }
269  }
270  return paths;
271 }
272 
273 QString DataPaths::systemAppDataDirectory(QString append) const
274 {
275  Strings::triml(append, "/\\");
276 
277  if (isPortableModeOn())
278  {
279  QString path = d->workingDirectory + "/" + append;
280  return QDir(path).absolutePath();
281  }
282 
283  // For non-portable model this continues here:
284  QString dir;
285 
286  #ifdef Q_OS_WIN32
287  // Let's open new block to prevent variable "bleeding".
288  {
289  QString envVar = getenv("APPDATA");
290  if (validateDir(envVar))
291  {
292  dir = envVar;
293  }
294  }
295  #endif
296 
297  if (dir.isEmpty())
298  {
299  dir = QDir::homePath();
300  if (!validateDir(dir))
301  {
302  return QString();
303  }
304  }
305 
306  Strings::trimr(dir, "/\\");
307 
308  dir += QDir::separator() + append;
309 
310  return QDir(dir).absolutePath();
311 }
312 
313 bool DataPaths::tryCreateDirectory(const QDir& rootDir, const QString& dirToCreate) const
314 {
315  if (!rootDir.exists(dirToCreate))
316  {
317  return rootDir.mkdir(dirToCreate);
318  }
319 
320  return true;
321 }
322 
324 {
326 }
327 
328 bool DataPaths::validateDir(const QString& path)
329 {
330  QFileInfo fileInfo(path);
331 
332  bool bCondition1 = !path.isEmpty();
333  bool bCondition2 = fileInfo.exists();
334  bool bCondition3 = fileInfo.isDir();
335 
336  return bCondition1 && bCondition2 && bCondition3;
337 }
338 
339 const QString &DataPaths::workingDirectory() const
340 {
341  return d->workingDirectory;
342 }
343 
static QString combinePaths(QString pathFront, QString pathEnd)
Definition: strings.cpp:145
bool tryCreateDirectory(const QDir &rootDir, const QString &dirToCreate) const
If directory already exists true is returned.
Definition: datapaths.cpp:313
const QString & programDirName() const
Defaults to PROGRAMS_APPDATA_DIR_NAME.
Definition: datapaths.cpp:143
QStringList directoriesExist() const
Checks if all necessary directories exist.
Definition: datapaths.cpp:123
static bool validateDir(const QString &path)
Definition: datapaths.cpp:328
QString programsDataDirectoryPath() const
Path to directory where this concrete application should store it's data.
Definition: datapaths.cpp:235
Represents directories used by Doomseeker to store data.
Definition: datapaths.h:43
static QString programFilesDirectory(MachineType machineType)
Definition: datapaths.cpp:194
QString localDataLocationPath(const QString &subpath=QString()) const
Path to the directory where large data should be saved.
Definition: datapaths.cpp:172
const QString & workingDirectory() const
Program working directory.
Definition: datapaths.cpp:339
QStringList canWrite() const
Checks if all directories can be written to.
Definition: datapaths.cpp:73
QString systemAppDataDirectory(QString append=QString()) const
Gets path to the root directory for data storage.
Definition: datapaths.cpp:273
QString programsDataSupportDirectoryPath() const
Allows switching from Preferences to Application Support on OS X.
Definition: datapaths.cpp:241
bool validateAppDataDirectory()
Checks if the root directory for Doomseeker data storage is accessible.
Definition: datapaths.cpp:323
static QStringList staticDataSearchDirs(const QString &subdir=QString())
Paths to directories where program should search for its static data.
Definition: datapaths.cpp:255
static DataPaths * defaultInstance()
Retrieves default instance that is used throughout the program.
Definition: datapaths.cpp:112
bool createDirectories()
Creates necessary directories for application run.
Definition: datapaths.cpp:86