]> git.sven.stormbind.net Git - sven/vym.git/blob - file.cpp
Import Upstream version 2.6.11
[sven/vym.git] / file.cpp
1 #include <QDebug>
2 #include <QDir>
3 #include <QMessageBox>
4 #include <QPixmap>
5 #include <QLabel>
6 #include <QTextStream>
7 #include <iostream>
8 #include <cstdlib>
9
10 #include "file.h"
11 #include "vymprocess.h"
12
13 #if defined(Q_OS_WIN32)
14     #include "mkdtemp.h"
15     #include <windows.h>
16 #endif
17
18 #if defined(Q_OS_MACX)
19     #include "unistd.h"
20 #endif
21
22 using namespace File;
23
24 extern QString zipToolPath;
25
26 QString convertToRel (const QString &src, const QString &dst)
27 {
28     // Creates a relative path pointing from src to dst
29
30     QString s=src;
31     QString d=dst;
32     int i;
33
34     if (s==d)
35     {
36         // Special case, we just need the name of the file,
37         // not the complete path
38         i=d.lastIndexOf ("/");
39         d=d.right (d.length()-i-1);
40     } else
41     {
42         // remove identical left parts
43         while (s.section("/",0,0) == d.section("/",0,0) )
44         {
45             i=s.indexOf ("/");
46             s=s.right (s.length()-i-1);
47             d=d.right (d.length()-i-1);
48         }
49
50         // Now take care of paths where we have to go back first
51         int srcsep=s.count("/");
52         while (srcsep > 0 )
53         {
54             d="../"+d;
55             srcsep--;
56         }
57     }
58     return d;
59 }
60
61 QString convertToAbs (const QString &src, const QString &dst)
62 {
63     // Creates a relative path pointing from src to dst
64     QDir dd(src);
65     return dd.absoluteFilePath(dst);
66 }
67
68
69 QString basename(const QString &path)
70 {
71     return path.section ('/', -1);
72 }
73
74 QString dirname(const QString &path)
75 {
76     return path.section('/', 0, -2);
77 }
78
79 extern QString vymName;
80 bool reallyWriteDirectory(const QString &dir)
81 {
82     QStringList eList = QDir(dir).entryList();
83     if (eList.first() ==".")  eList.pop_front();    // remove "."
84     if (eList.first() =="..") eList.pop_front();    // remove "."
85     if (!eList.isEmpty())
86     {
87         QMessageBox mb( vymName,
88                         QObject::tr("The directory %1 is not empty.\nDo you risk to overwrite its contents?","write directory").arg(dir),
89                         QMessageBox::Warning,
90                         QMessageBox::Yes ,
91                         QMessageBox::Cancel | QMessageBox::Default,
92                         QMessageBox::NoButton );
93
94         mb.setButtonText( QMessageBox::Yes, QObject::tr("Overwrite") );
95         mb.setButtonText( QMessageBox::No, QObject::tr("Cancel"));
96         switch( mb.exec() )
97         {
98         case QMessageBox::Yes:
99             // save
100             return true;
101         case QMessageBox::Cancel:
102             // do nothing
103             return false;
104         }
105     }
106     return true;
107 }
108
109 QString makeTmpDir (bool &ok, QString prefix)   //FIXME-3 use QTemporaryDir
110 {
111     bool b;
112     QString path=makeUniqueDir (b,QDir::tempPath()+"/"+prefix+"-XXXXXX");
113     ok=b;
114     return path;
115 }
116
117 bool isInTmpDir(QString fn)
118 {
119     QString temp=QDir::tempPath();
120     int l=temp.length();
121     return fn.left(l)==temp;
122 }
123
124 QString makeUniqueDir (bool &ok,QString s)
125 {
126     ok=true;
127
128     QString r;
129
130 #if defined(Q_OS_WIN32)
131     r=mkdtemp (s);
132 #else
133     // On Linux and friends use cstdlib
134     
135     // Convert QString to string 
136     ok=true;
137     char *p;
138     int bytes=s.length();
139     p=(char*) malloc (bytes+1);
140     int i;
141     for (i=0;i<bytes;i++)
142         p[i]=s.at(i).unicode();
143     p[bytes]=0; 
144
145     r=mkdtemp (p);
146     free (p);
147 #endif
148
149     if (r.isEmpty()) ok=false;
150     return r;
151 }
152
153 void removeDir(QDir d)
154 {
155     // This check should_ not be necessary, but proved to be useful ;-)
156     if (!isInTmpDir(d.path()))
157     {
158         qWarning ()<<"file.cpp::removeDir should remove " + d.path() + " - aborted.";
159         return;
160     }
161
162     // Traverse directories
163     d.setFilter( QDir::Dirs| QDir::Hidden | QDir::NoSymLinks );
164     QFileInfoList list = d.entryInfoList();
165     QFileInfo fi;
166
167     for (int i = 0; i < list.size(); ++i)
168     {
169         fi=list.at(i);
170         if (fi.fileName() != "." && fi.fileName() != ".." )
171         {
172             if ( !d.cd(fi.fileName()) )
173                 qWarning ()<<"removeDir() cannot find the directory "+fi.fileName();
174             else
175             {
176                 // Recursively remove subdirs
177                 removeDir (d);
178                 d.cdUp();
179             }
180         }
181     }
182
183     // Traverse files
184     d.setFilter( QDir::Files| QDir::Hidden | QDir::NoSymLinks );
185     list = d.entryInfoList();
186
187     for (int i = 0; i < list.size(); ++i)
188     {
189         fi=list.at(i);
190         QFile (fi.filePath()).remove();
191     }
192
193     QString dirName = d.dirName();
194     d.cdUp();
195     if (!d.rmdir(dirName))
196         qWarning ()<< "removeDir(" + dirName + ") failed!";
197 }       
198
199 bool copyDir (QDir src, QDir dst, const bool &override)   
200 {
201     QStringList dirs  = src.entryList(QDir::AllDirs | QDir::Hidden | QDir::NoDotAndDotDot);
202     QStringList files = src.entryList(QDir::Files );
203
204     // Check if dst is a subdir of src, which would cause endless recursion...
205     if (dst.absolutePath().contains(src.absolutePath())) return false;
206     
207     // Traverse directories
208     QList<QString>::iterator d,f;
209     for (d = dirs.begin(); d != dirs.end(); ++d) 
210     {
211         if (!QFileInfo(src.path() + "/" + (*d)).isDir()) continue;
212
213         QDir cdir(dst.path() + "/" + (*d));
214         cdir.mkpath(cdir.path());
215
216         if (!copyDir (QDir(src.path() + "/" + (*d)), QDir(dst.path() + "/" + (*d)), override)) 
217             return false;
218     }
219
220     // Traverse files
221     for (f = files.begin(); f != files.end(); ++f) 
222     {
223         QFile cfile(src.path() + "/" + (*f));
224         QFile destFile(dst.path()+ "/" + src.relativeFilePath(cfile.fileName()));
225         if (destFile.exists() && override) 
226             destFile.remove();
227
228         if (!cfile.copy(dst.path() + "/" + src.relativeFilePath(cfile.fileName()))) 
229             return false;
230     }
231     return true;
232 }
233
234 void makeSubDirs (const QString &s)
235 {
236     QDir d(s);
237     d.mkdir(s);
238     d.mkdir ("images"); 
239     d.mkdir ("flags");  
240 }
241
242 ErrorCode zipDir ( QDir zipInputDir, QString zipName)
243 {
244     zipName = QDir::toNativeSeparators(zipName);
245     ErrorCode err = Success;
246
247     QString symLinkTarget;
248
249     QString newName;
250     // Move existing file away
251     QFile file(zipName);
252     if (file.exists() )
253     {
254         symLinkTarget = file.symLinkTarget();
255         newName = zipName + ".tmp";
256         int n = 0;
257         while (!file.rename (newName) && n < 5)
258         {
259             newName = newName + QString().setNum(n);
260             n++;
261         }
262         if (n >= 5)
263         {
264             QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
265                                    QObject::tr("Couldn't move existing file out of the way before saving."));
266             return Aborted;
267         }
268     }
269
270     // zip the temporary directory
271     VymProcess *zipProc=new VymProcess ();
272     QStringList args;
273
274 #if defined(Q_OS_WIN32)
275     zipProc->setWorkingDirectory (QDir::toNativeSeparators(zipInputDir.path() + "\\") );    
276     args << "a" << zipName << "-tzip" << "-scsUTF-8"  << "*";
277     zipProc->start(zipToolPath, args);
278  
279     if (!zipProc->waitForStarted())
280     {
281         QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
282             QObject::tr("Couldn't start tool to decompress data."));
283         err=Aborted;
284
285     }
286     while(zipProc->state()!=QProcess::NotRunning){
287         zipProc->waitForReadyRead();
288         result = zipProc->readAll();
289     }
290     
291     if (!zipProc->waitForStarted() )
292     {
293         // zip could not be started
294         QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
295                                QObject::tr("Couldn't start zip to compress data."));
296         err=Aborted;
297     } else
298     {
299         // zip could be started
300         zipProc->waitForFinished();
301         if (zipProc->exitStatus()!=QProcess::NormalExit )
302         {
303             QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
304                                    QObject::tr("zip didn't exit normally")+
305                                    "\n" + zipProc->getErrout());
306             err=Aborted;
307         } else
308         {
309             if (zipProc->exitCode()>0)
310             {
311                 QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
312                                        QString("zip exit code:  %1").arg(zipProc->exitCode() )+
313                                        "\n" + zipProc->getErrout() );
314                 err=Aborted;
315             }
316         }
317     }
318     // qDebug() <<"Output: " << zipProc->getStdout()<<flush;   
319 #else
320     zipProc->setWorkingDirectory (QDir::toNativeSeparators(zipInputDir.path() ));  
321     args <<"-r";
322     args <<zipName;
323     args <<".";
324
325     zipProc->start ("zip",args);
326     if (!zipProc->waitForStarted() )
327     {
328         // zip could not be started
329         QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
330                                QObject::tr("Couldn't start zip to compress data."));
331         err=Aborted;
332     } else
333     {
334         // zip could be started
335         zipProc->waitForFinished();
336         if (zipProc->exitStatus()!=QProcess::NormalExit )
337         {
338             QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
339                                    QObject::tr("zip didn't exit normally")+
340                                    "\n" + zipProc->getErrout());
341             err=Aborted;
342         } else
343         {
344             if (zipProc->exitCode()>0)
345             {
346                 QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
347                                        QString("zip exit code:  %1").arg(zipProc->exitCode() )+
348                                        "\n" + zipProc->getErrout() );
349                 err=Aborted;
350             }
351         }
352     }
353 #endif
354     // Try to restore previous file, if zipping failed
355     if (err == Aborted && !newName.isEmpty() && !file.rename (zipName) )
356         QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
357            QObject::tr("Couldn't rename %1 back to %2").arg(newName).arg(zipName) );
358     else
359     {
360         // Take care of symbolic link
361         if (!symLinkTarget.isEmpty() )
362         {
363             if (!QFile(symLinkTarget).remove() )
364             {
365                 QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
366                    QObject::tr("Couldn't remove target of old symbolic link %1").arg(symLinkTarget));
367                 err = Aborted;
368                 return err;
369             }
370
371             if (!QFile(zipName).rename(symLinkTarget) )
372             {
373                 QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
374                    QObject::tr("Couldn't rename output to target of old symbolic link %1").arg(symLinkTarget));
375                 err = Aborted;
376                 return err;
377             }
378             if (!QFile(symLinkTarget).link(zipName) )
379             {
380                 QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
381                    QObject::tr("Couldn't link from %1 to target of old symbolic link %2").arg(zipName).arg(symLinkTarget));
382                 err = Aborted;
383                 return err;
384             }
385         }
386
387     // Remove temporary file
388         if (!newName.isEmpty()  && !file.remove() )
389             QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
390            QObject::tr("Saved %1, but couldn't remove %2").arg(zipName).arg(newName));
391     }
392
393     return err; 
394 }
395
396 File::ErrorCode unzipDir ( QDir zipOutputDir, QString zipName)
397 {
398     ErrorCode err=Success;
399
400     VymProcess *zipProc = new VymProcess ();
401     QStringList args;
402
403 #if defined(Q_OS_WIN32)
404     zipProc->setWorkingDirectory (QDir::toNativeSeparators(zipOutputDir.path() + "\\") );
405     args << "-o" + zipOutputDir.path() << "x" << zipName.toUtf8() << "-scsUTF-8";
406     zipProc->start(zipToolPath, args);
407
408     if (!zipProc->waitForStarted())
409     {
410         QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
411                                QObject::tr("Couldn't start tool to decompress data."));
412         err=Aborted;
413     }
414
415     while(zipProc->state()!=QProcess::NotRunning){
416         zipProc->waitForReadyRead();
417         result = zipProc->readAll();
418         //qDebug() << result << flush;
419     }
420     //qDebug() << zipProc->getStdout()<<flush;
421 #else
422     zipProc->setWorkingDirectory (QDir::toNativeSeparators(zipOutputDir.path()));
423     args << "-o";   // overwrite existing files!
424     args << zipName ;
425     args << "-d";
426     args << zipOutputDir.path();
427
428     zipProc->start ("unzip",args);
429 #endif
430     if (!zipProc->waitForStarted() )
431     {
432         QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
433                                QObject::tr("Couldn't start %1 to decompress data.").arg(zipToolPath));
434         err=Aborted;
435
436
437     } else
438     {
439         zipProc->waitForFinished();
440         if (zipProc->exitStatus()!=QProcess::NormalExit )
441         {
442             QMessageBox::critical( 0,QObject::tr( "Critical Error" ),
443                                    QObject::tr("%1 didn't exit normally").arg(zipToolPath) +
444                                    zipProc->getErrout() );
445             err=Aborted;
446         } else
447         {
448             if (zipProc->exitCode()>0)
449             {
450                 if (zipProc->exitCode()==9)
451                     // no zipped file, but maybe .xml or old version? Try again.
452                     err=NoZip;
453                 else
454                 {
455                     QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
456                                            QString("%1 exit code:  %2").arg(zipToolPath).arg(zipProc->exitCode() ) +
457                                            zipProc->getErrout() );
458                     err=Aborted;
459                 }
460             }
461         }
462     }
463     return err;
464 }
465
466 bool loadStringFromDisk (const QString &fname, QString &s)
467 {
468     s="";
469     QFile file(fname);
470     if (!file.open(QFile::ReadOnly | QFile::Text)) {
471         qWarning()<<QString("loadStringFromDisk: Cannot read file %1\n%2")
472                     .arg(fname)
473                     .arg(file.errorString());
474         return false;
475     }
476
477     QTextStream in(&file);
478     s=in.readAll();
479     return true;
480 }
481
482 bool saveStringToDisk (const QString &fname, const QString &s)
483 {
484     QFile file(fname);
485     // Write as binary (default), QFile::Text would convert linebreaks
486     if (!file.open(QFile::WriteOnly  )) {
487         qWarning()<<QString("saveStringToDisk: Cannot write file %1:\n%2.")
488                     .arg(fname)
489                     .arg(file.errorString());
490         return false;
491     }
492
493     QTextStream out(&file);
494     out.setCodec("UTF-8");
495     out << s;
496
497     return true;
498 }
499
500 FileType getMapType (const QString &fn)
501 {
502     int i=fn.lastIndexOf(".");
503     if (i>=0)
504     {
505         QString postfix=fn.mid(i+1);
506         if (postfix=="vym" || postfix=="vyp" || postfix=="xml") return VymMap;
507         if (postfix=="mm") return FreemindMap;
508     }
509     return UnknownMap;
510 }
511
512 ImageIO::ImageIO ()
513 {
514     // Create list with supported image types
515     // foreach (QByteArray format, QImageWriter::supportedImageFormats())
516     // imageTypes.append( tr("%1...").arg(QString(format).toUpper()));
517     imageFilters.append ("Images (*.png *.jpg *.jpeg *.bmp *.bmp *.ppm *.xpm *.xbm)");
518     imageTypes.append ("PNG");
519     imageFilters.append ("Portable Network Graphics (*.png)");
520     imageTypes.append ("PNG");
521     imageFilters.append ("Joint Photographic Experts Group (*.jpg)");
522     imageTypes.append ("JPG");
523     imageFilters.append ("Joint Photographic Experts Group (*.jpeg)");
524     imageTypes.append ("JPG");
525     imageFilters.append ("Windows Bitmap (*.bmp)");
526     imageTypes.append ("BMP");
527     imageFilters.append ("Portable Pixmap (*.ppm)");
528     imageTypes.append ("PPM");
529     imageFilters.append ("X11 Bitmap (*.xpm)");
530     imageTypes.append ("XPM");
531     imageFilters.append ("X11 Bitmap (*.xbm)");
532     imageTypes.append ("XBM");
533 }
534
535 QStringList ImageIO::getFilters()
536 {
537     return imageFilters;
538 }
539
540 QString ImageIO::getType(QString filter)
541 {
542     for (int i=0;i<imageFilters.count()+1;i++)
543         if (imageFilters.at(i)==filter) return imageTypes.at(i);
544     return QString();
545 }
546
547 QString ImageIO::guessType(QString fn)
548 {
549     int i=fn.lastIndexOf(".");
550     if (i>=0)
551     {
552         QString postfix=fn.mid(i+1);
553         for (int i=1;i<imageFilters.count();i++)
554             if (imageFilters.at(i).contains(postfix)) return imageTypes.at(i);
555     }
556     return QString();
557 }
558