#include <QDebug>
#include <QDir>
#include <QMessageBox>
#include <QPixmap>
#include <QLabel>
#include <QTextStream>
#include "file.h"
#include "process.h"
#if defined(Q_OS_WIN32)
#include "mkdtemp.h"
#include <windows.h>
#endif
using namespace File;
QString maskPath(QString p)
{
// Change " " to "\ " to enable blanks in filenames
p=p.replace(QChar('&'),"\\&");
return p.replace(QChar(' '),"\\ ");
}
QString convertToRel (const QString &src, const QString &dst)
{
QString s=src;
QString d=dst;
int i;
if (s==d)
{
// Special case, we just need the name of the file,
// not the complete path
i=d.lastIndexOf ("/");
d=d.right (d.length()-i-1);
} else
{
// remove identical left parts
while (s.section("/",0,0) == d.section("/",0,0) )
{
i=s.indexOf ("/");
s=s.right (s.length()-i-1);
d=d.right (d.length()-i-1);
}
// Now take care of paths where we have to go back first
int srcsep=s.count("/");
while (srcsep > 0 )
{
d="../"+d;
srcsep--;
}
}
return d;
}
QString basename(const QString &path)
{
return path.section ('/', -1);
}
extern QString vymName;
bool reallyWriteDirectory(const QString &dir)
{
QStringList eList = QDir(dir).entryList();
if (eList.first() ==".") eList.pop_front(); // remove "."
if (eList.first() =="..") eList.pop_front(); // remove "."
if (!eList.isEmpty())
{
QMessageBox mb( vymName,
QObject::tr("The directory %1 is not empty.\nDo you risk to overwrite its contents?","write directory").arg(dir),
QMessageBox::Warning,
QMessageBox::Yes ,
QMessageBox::Cancel | QMessageBox::Default,
QMessageBox::NoButton );
mb.setButtonText( QMessageBox::Yes, QObject::tr("Overwrite") );
mb.setButtonText( QMessageBox::No, QObject::tr("Cancel"));
switch( mb.exec() )
{
case QMessageBox::Yes:
// save
return true;
case QMessageBox::Cancel:
// do nothing
return false;
}
}
return true;
}
QString makeTmpDir (bool &ok, QString prefix)
{
bool b;
QString path=makeUniqueDir (b,QDir::tempPath()+"/"+prefix+"-XXXXXX");
ok=b;
return path;
}
bool isInTmpDir(QString fn)
{
QString temp=QDir::tempPath();
int l=temp.length();
return fn.left(l)==temp;
}
QString makeUniqueDir (bool &ok,QString s)
{
// Create unique directory e.g. for s="/tmp/vym-XXXXXX"
// Convert Separators
s=QDir::convertSeparators(s);
// Convert QString to string
ok=true;
char *p;
int bytes=s.length();
p=(char*) malloc (bytes+1);
int i;
for (i=0;i<bytes;i++)
p[i]=s.at(i).unicode();
p[bytes]=0;
QString r=mkdtemp (p);
if (r.isEmpty()) ok=false;
free (p);
return r;
}
void removeDir(QDir d)
{
// This check should_ not be necessary, but proved to be useful ;-)
if (!isInTmpDir(d.path()))
{
qWarning ()<<"file.cpp::removeDir should remove "+d.path()+" - aborted.";
return;
}
// Traverse directories
d.setFilter( QDir::Dirs| QDir::Hidden | QDir::NoSymLinks );
QFileInfoList list = d.entryInfoList();
QFileInfo fi;
for (int i = 0; i < list.size(); ++i)
{
fi=list.at(i);
if (fi.fileName() != "." && fi.fileName() != ".." )
{
if ( !d.cd(fi.fileName()) )
qWarning ()<<"removeDir() cannot find the directory "+fi.fileName();
else
{
// Recursively remove subdirs
removeDir (d);
d.cdUp();
}
}
}
// Traverse files
d.setFilter( QDir::Files| QDir::Hidden | QDir::NoSymLinks );
list = d.entryInfoList();
for (int i = 0; i < list.size(); ++i)
{
fi=list.at(i);
QFile (fi.filePath()).remove();
}
if (!d.rmdir(d.path()))
qWarning ()<<"removeDir("+d.path()+") failed!";
}
bool copyDir (QDir src, QDir dst, const bool &override)
{
QStringList dirs = src.entryList(QDir::AllDirs | QDir::Hidden | QDir::NoDotAndDotDot);
QStringList files = src.entryList(QDir::Files );
// Check if dst is a subdir of src, which would cause endless recursion...
if (dst.absolutePath().contains(src.absolutePath())) return false;
// Traverse directories
QList<QString>::iterator d,f;
for (d = dirs.begin(); d != dirs.end(); ++d)
{
if (!QFileInfo(src.path() + "/" + (*d)).isDir()) continue;
QDir cdir(dst.path() + "/" + (*d));
cdir.mkpath(cdir.path());
if (!copyDir (QDir(src.path() + "/" + (*d)), QDir(dst.path() + "/" + (*d)), override))
return false;
}
// Traverse files
for (f = files.begin(); f != files.end(); ++f)
{
QFile cfile(src.path() + "/" + (*f));
QFile destFile(dst.path()+ "/" + src.relativeFilePath(cfile.fileName()));
if (destFile.exists() && override)
destFile.remove();
if (!cfile.copy(dst.path() + "/" + src.relativeFilePath(cfile.fileName())))
return false;
}
return true;
}
void makeSubDirs (const QString &s)
{
QDir d(s);
d.mkdir(s);
d.mkdir ("images");
d.mkdir ("flags");
}
ErrorCode zipDir (const QDir &zipDir, const QString &zipName)
{
ErrorCode err = Success;
QString newName;
// Move existing file away
QFile file(zipName);
if (file.exists() )
{
newName = zipName + ".tmp";
int n=0;
while (!file.rename (newName) && n<5)
{
newName = newName + QString().setNum(n);
n++;
}
if (n>=5)
{
QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
QObject::tr("Couldn't move existing file out of the way before saving."));
return Aborted;
}
}
// zip the temporary directory
QStringList args;
Process *zipProc=new Process ();
zipProc->setWorkingDirectory (zipDir.path());
args <<"-r";
args <<zipName;
args <<".";
zipProc->start ("zip",args);
if (!zipProc->waitForStarted() )
{
// zip could not be started
QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
QObject::tr("Couldn't start zip to compress data."));
err=Aborted;
} else
{
// zip could be started
zipProc->waitForFinished();
if (zipProc->exitStatus()!=QProcess::NormalExit )
{
QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
QObject::tr("zip didn't exit normally")+
"\n" + zipProc->getErrout());
err=Aborted;
} else
{
if (zipProc->exitCode()>0)
{
QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
QString("zip exit code: %1").arg(zipProc->exitCode() )+
"\n" + zipProc->getErrout() );
err=Aborted;
}
}
}
// Try to restore previous file, if zipping failed
if (err == Aborted && !newName.isEmpty() && !file.rename (zipName) )
QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
QObject::tr("Couldn't rename %1 back to %2").arg(newName).arg(zipName) );
else
{
// Remove temporary file
if (!newName.isEmpty() && !file.remove() )
QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
QObject::tr("Saved %1, but couldn't remove %2").arg(zipName).arg(newName));
}
return err;
}
File::ErrorCode unzipDir (const QDir &zipDir, const QString &zipName)
{
ErrorCode err=Success;
// Try to unzip file
#if !defined(Q_OS_WIN32)
QStringList args;
Process *zipProc=new Process ();
zipProc->setWorkingDirectory (zipDir.path());
args << "-o"; // overwrite existing files!
args << zipName ;
args << "-d";
args << zipDir.path();
zipProc->start ("unzip",args);
if (!zipProc->waitForStarted() )
{
QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
QObject::tr("Couldn't start unzip to decompress data."));
err=Aborted;
} else
{
zipProc->waitForFinished();
if (zipProc->exitStatus()!=QProcess::NormalExit )
{
QMessageBox::critical( 0,QObject::tr( "Critical Error" ),
QObject::tr("unzip didn't exit normally") +
zipProc->getErrout() );
err=Aborted;
} else
{
if (zipProc->exitCode()>0)
{
if (zipProc->exitCode()==9)
// no zipped file, but maybe .xml or old version? Try again.
err=NoZip;
else
{
QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
QString("unzip exit code: %1").arg(zipProc->exitCode() ) +
zipProc->getErrout() );
err=Aborted;
}
}
}
}
#else
// Do this process creation using Win32 API.
//! Create process.
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
// Initialize members of the PROCESS_INFORMATION structure.
::ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
// Set up members of the STARTUPINFO structure.
::ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
// Create command line.
QString argv("unzip -o ");
argv.append(QDir::convertSeparators(zipName));
argv.append(" -d ");
argv.append(QDir::convertSeparators(zipDir.path()));
// Create the child process.
if( !::CreateProcess(NULL,
(LPWSTR)argv.unicode(), // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
NULL, // use parent's current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo) ) // receives PROCESS_INFORMATION
{
err = Aborted;
}
else
{
// Wait for it to finish.
::WaitForSingleObject( piProcInfo.hProcess, 10000 );
}
#endif
return err;
}
bool loadStringFromDisk (const QString &fname, QString &s)
{
s="";
QFile file(fname);
if (!file.open(QFile::ReadOnly | QFile::Text)) {
qWarning()<<QString("loadStringFromDisk: Cannot read file %1\n%2")
.arg(fname)
.arg(file.errorString());
return false;
}
QTextStream in(&file);
s=in.readAll();
return true;
}
bool saveStringToDisk (const QString &fname, const QString &s)
{
QFile file(fname);
if (!file.open(QFile::WriteOnly | QFile::Text)) {
qWarning()<<QString("saveStringToDisk: Cannot write file %1:\n%2.")
.arg(fname)
.arg(file.errorString());
return false;
}
QTextStream out(&file);
out.setCodec("UTF-8");
out << s;
return true;
}
FileType getMapType (const QString &fn)
{
int i=fn.lastIndexOf(".");
if (i>=0)
{
QString postfix=fn.mid(i+1);
if (postfix=="vym" || postfix=="vyp" || postfix=="xml") return VymMap;
if (postfix=="mm") return FreemindMap;
}
return UnknownMap;
}
ImageIO::ImageIO ()
{
// Create list with supported image types
// foreach (QByteArray format, QImageWriter::supportedImageFormats())
// imageTypes.append( tr("%1...").arg(QString(format).toUpper()));
imageFilters.append ("Images (*.png *.jpg *.jpeg *.bmp *.bmp *.ppm *.xpm *.xbm)");
imageTypes.append ("PNG");
imageFilters.append ("Portable Network Graphics (*.png)");
imageTypes.append ("PNG");
imageFilters.append ("Joint Photographic Experts Group (*.jpg)");
imageTypes.append ("JPG");
imageFilters.append ("Joint Photographic Experts Group (*.jpeg)");
imageTypes.append ("JPG");
imageFilters.append ("Windows Bitmap (*.bmp)");
imageTypes.append ("BMP");
imageFilters.append ("Portable Pixmap (*.ppm)");
imageTypes.append ("PPM");
imageFilters.append ("X11 Bitmap (*.xpm)");
imageTypes.append ("XPM");
imageFilters.append ("X11 Bitmap (*.xbm)");
imageTypes.append ("XBM");
}
QStringList ImageIO::getFilters()
{
return imageFilters;
}
QString ImageIO::getType(QString filter)
{
for (int i=0;i<imageFilters.count()+1;i++)
if (imageFilters.at(i)==filter) return imageTypes.at(i);
return QString();
}
QString ImageIO::guessType(QString fn)
{
int i=fn.lastIndexOf(".");
if (i>=0)
{
QString postfix=fn.mid(i+1);
for (int i=1;i<imageFilters.count();i++)
if (imageFilters.at(i).contains(postfix)) return imageTypes.at(i);
}
return QString();
}