file.cpp
author insilmaril
Mon, 22 Oct 2007 09:50:08 +0000
changeset 608 6cdc2e7b1937
parent 606 84c73902f727
child 624 4ffc47ae27e4
permissions -rw-r--r--
Various patches: Better windows support, branch sorting, Freedesktop support
     1 #include <QDir>
     2 #include <QMessageBox>
     3 #include <QPixmap>
     4 #include <QLabel>
     5 #include <QTextStream>
     6 #include <iostream>
     7 
     8 #include "file.h"
     9 #include "process.h"
    10 
    11 // Avoid full inclusion of io.h by just defining mktemp's prototype here.
    12 #if defined(Q_OS_WIN32)
    13 extern "C" char *_mktemp(char *fmt);
    14 #include <windows.h>
    15 #endif
    16 
    17 QString maskPath(QString p)
    18 {
    19 	// Change " " to "\ " to enable blanks in filenames
    20 	p=p.replace(QChar('&'),"\\&");
    21 	return p.replace(QChar(' '),"\\ ");
    22 }
    23 
    24 QString convertToRel (const QString &src, const QString &dst)
    25 {
    26 	QString s=src;
    27 	QString d=dst;
    28 	int i;
    29 
    30 	if (s==d) 
    31 	{
    32 		// Special case, we just need the name of the file,
    33 		// not the complete path
    34 		i=d.findRev ("/");
    35 		d=d.right (d.length()-i-1);
    36 	} else
    37 	{
    38 		// Find relative path from src to dst
    39 
    40 		// Remove the first "/"
    41 		if (s.section ("/",0,0).isEmpty()) 
    42 		{
    43 			s=s.right (s.length()-1);
    44 			d=d.right (d.length()-1);
    45 		}
    46 		
    47 		// remove identical left parts
    48 		while (s.section("/",0,0) == d.section("/",0,0) ) 
    49 		{
    50 			i=s.find ("/");
    51 			s=s.right (s.length()-i-1);
    52 			d=d.right (d.length()-i-1);
    53 		}
    54 
    55 		// Now take care of paths where we have to go back first
    56 		int srcsep=s.count("/");
    57 		int dstsep=d.count("/");
    58 		if (srcsep <=  dstsep )
    59 		{
    60 			// find path to go up first and then back to dst
    61 			i=1;
    62 			while (i<=srcsep) 
    63 			{
    64 				d="../"+d;
    65 				i++;
    66 			}	
    67 		}
    68 	}	
    69 	return d;
    70 }
    71 
    72 #include <QFileDialog>
    73 extern QString vymName;
    74 extern QDir lastFileDir;
    75 
    76 QString browseDirectory (QWidget *parent,const QString &caption)
    77 {
    78 	QFileDialog fd(parent,caption);
    79 	fd.setMode (QFileDialog::DirectoryOnly);
    80 	fd.setCaption(vymName+ " - "+caption);
    81 	fd.setDir (lastFileDir);
    82 	fd.show();
    83 	
    84 	if ( fd.exec() == QDialog::Accepted )
    85 		return fd.selectedFile();
    86 	else
    87 		return "";
    88 }
    89 
    90 
    91 
    92 bool reallyWriteDirectory(const QString &dir)
    93 {
    94 	QStringList eList = QDir(dir).entryList();
    95 	if (eList.first() ==".")  eList.pop_front();	// remove "."
    96 	if (eList.first() =="..") eList.pop_front();	// remove "."
    97 	if (!eList.isEmpty())
    98 	{
    99 		QMessageBox mb( vymName,
   100 			QObject::tr("The directory %1 is not empty.\nDo you risk to overwrite its contents?","write directory").arg(dir),
   101 		QMessageBox::Warning,
   102 		QMessageBox::Yes ,
   103 		QMessageBox::Cancel | QMessageBox::Default,
   104 		QMessageBox::NoButton );
   105 
   106 		mb.setButtonText( QMessageBox::Yes, QObject::tr("Overwrite") );
   107 		mb.setButtonText( QMessageBox::No, QObject::tr("Cancel"));
   108 		switch( mb.exec() ) 
   109 		{
   110 			case QMessageBox::Yes:
   111 				// save 
   112 				return true;
   113 			case QMessageBox::Cancel:
   114 				// do nothing
   115 				return false;
   116 		}
   117 	}
   118 	return true;
   119 }
   120 
   121 QString makeTmpDir (bool &ok, QString prefix)
   122 {
   123 	bool b;
   124 	QString path=makeUniqueDir (b,QDir::tempPath()+"/"+prefix+"-XXXXXX");
   125 	ok=b;
   126 	return path;
   127 }
   128 
   129 bool isInTmpDir(QString fn)
   130 {
   131 	QString temp=QDir::tempPath();
   132 	int l=temp.length();
   133 	return fn.left(l)==temp;
   134 }
   135 
   136 QString makeUniqueDir (bool &ok,QString s)
   137 {
   138 	// Create unique directory e.g. for s="/tmp/vym-XXXXXX"
   139 
   140 	// Convert Separators
   141 	s=QDir::convertSeparators(s);
   142 
   143 	// Convert QString to string 
   144 	ok=true;
   145 	char *p;
   146 	int bytes=s.length();
   147 	p=(char*) malloc (bytes+1);
   148 	int i;
   149 	for (i=0;i<bytes;i++)
   150 		p[i]=s.at(i).latin1();
   151 	p[bytes]=0;	
   152 
   153 #if defined(Q_OS_WIN32)
   154     // There's no mkdtemp on VCEE.
   155     QString r=_mktemp(p);
   156 #else
   157 	QString r=mkdtemp (p);
   158 #endif
   159 	if (r.isEmpty()) ok=false;
   160 	free (p);
   161 	return r;
   162 }
   163 
   164 void removeDir(QDir d)
   165 {
   166 	// This check should_ not be necessary, but proved to be useful ;-)
   167 	if (!isInTmpDir(d.path()))
   168 	{
   169 		qWarning ("file.cpp::removeDir should remove "+d.path()+" - aborted.");
   170 		return;
   171 	}
   172 
   173 	// Traverse directories
   174 	d.setFilter( QDir::Dirs| QDir::Hidden | QDir::NoSymLinks );
   175 	QFileInfoList list = d.entryInfoList();
   176 	QFileInfo fi;
   177 
   178 	for (int i = 0; i < list.size(); ++i) 
   179 	{
   180 		fi=list.at(i);
   181 		if (fi.fileName() != "." && fi.fileName() != ".." )
   182 		{
   183 			if ( !d.cd(fi.fileName()) ) 
   184 				qWarning ("removeDir() cannot find the directory "+fi.fileName());
   185 			else 
   186 			{
   187 				// Recursively remove subdirs
   188 				removeDir (d);
   189 				d.cdUp();
   190 			}
   191 		}	
   192 	}
   193 
   194 	// Traverse files
   195 	d.setFilter( QDir::Files| QDir::Hidden | QDir::NoSymLinks );
   196 	list = d.entryInfoList();
   197 
   198 	for (int i = 0; i < list.size(); ++i) 
   199 	{
   200 		fi=list.at(i);
   201 		QFile (fi.filePath()).remove(); 
   202 	}	
   203 
   204 	if (!d.rmdir(d.path()))
   205 		qWarning ("removeDir("+d.path()+") failed!");
   206 }		
   207 
   208 void copyDir (QDir src, QDir dst)
   209 {
   210 	system ("cp -r "+src.path()+"/* "+dst.path());
   211 
   212 	/*
   213 	ErrorCode err=success;
   214 
   215 	Process *cpProc=new Process ();
   216 	QStringList args;
   217 	cpProc->setWorkingDirectory (src.path());
   218 	args <<"-r";
   219 	args <<src.path();
   220 	args <<dst.path();
   221 
   222 	cpProc->start ("cp",args);
   223 	if (!cpProc->waitForStarted() )
   224 	{	
   225 		// zip could not be started
   226 		QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
   227 					   QObject::tr("Couldn't start zip to compress data."));
   228 		err=aborted;
   229 	} else
   230 	{
   231 		// zip could be started
   232 		cpProc->waitForFinished();
   233 		if (cpProc->exitStatus()!=QProcess::NormalExit )
   234 		{
   235 			QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
   236 						   QObject::tr("cp didn't exit normally")+
   237 						   "\n" + cpProc->getErrout());
   238 			err=aborted;
   239 		} else
   240 		{
   241 			if (cpProc->exitCode()>0)
   242 			{
   243 				QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
   244 						   QString("cp exit code:  %1").arg(cpProc->exitCode() )+
   245 						   "\n" + cpProc->getErrout() );
   246 				err=aborted;
   247 			}
   248 		}
   249 	}	// cp could be started
   250 	*/
   251 }
   252 
   253 void makeSubDirs (const QString &s)
   254 {
   255 	QDir d(s);
   256 	d.mkdir(s);
   257 	d.mkdir ("images");	
   258 	d.mkdir ("flags");	
   259 }
   260 
   261 ErrorCode zipDir (const QDir &zipDir, const QString &zipName)
   262 {
   263 	ErrorCode err=success;
   264 	
   265 	// zip the temporary directory
   266 	QStringList args;
   267 	Process *zipProc=new Process ();
   268 	zipProc->setWorkingDirectory (zipDir.path());
   269 	args <<"-r";
   270 	args <<zipName;
   271 	args <<".";
   272 
   273 	zipProc->start ("zip",args);
   274 	if (!zipProc->waitForStarted() )
   275 	{	
   276 		// zip could not be started
   277 		QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
   278 					   QObject::tr("Couldn't start zip to compress data."));
   279 		err=aborted;
   280 	} else
   281 	{
   282 		// zip could be started
   283 		zipProc->waitForFinished();
   284 		if (zipProc->exitStatus()!=QProcess::NormalExit )
   285 		{
   286 			QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
   287 						   QObject::tr("zip didn't exit normally")+
   288 						   "\n" + zipProc->getErrout());
   289 			err=aborted;
   290 		} else
   291 		{
   292 			if (zipProc->exitCode()>0)
   293 			{
   294 				QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
   295 						   QString("zip exit code:  %1").arg(zipProc->exitCode() )+
   296 						   "\n" + zipProc->getErrout() );
   297 				err=aborted;
   298 			}
   299 		}
   300 	}	// zip could be started
   301 	return err;	
   302 }
   303 
   304 ErrorCode unzipDir (const QDir &zipDir, const QString &zipName)
   305 {
   306 	ErrorCode err=success;
   307 
   308 	// Try to unzip file
   309 #if !defined(Q_OS_WIN32)
   310 	QStringList args;
   311 	Process *zipProc=new Process ();
   312 	zipProc->setWorkingDirectory (zipDir.path());
   313 	args << "-o";	// overwrite existing files!
   314 	args << zipName ;
   315 	args << "-d";
   316 	args << zipDir.path();
   317 
   318 	zipProc->start ("unzip",args);
   319 	if (!zipProc->waitForStarted() )
   320 	{
   321 		QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
   322 					   QObject::tr("Couldn't start unzip to decompress data."));
   323 		err=aborted;
   324 		
   325 	} else
   326 	{
   327 		zipProc->waitForFinished();
   328 		if (zipProc->exitStatus()!=QProcess::NormalExit )
   329 		{
   330 			QMessageBox::critical( 0,QObject::tr( "Critical Error" ),
   331 						   QObject::tr("unzip didn't exit normally") +
   332 						   zipProc->getErrout() );
   333 			err=aborted;
   334 		} else
   335 		{
   336 			if (zipProc->exitCode()>0)
   337 			{
   338 				if (zipProc->exitCode()==9)
   339 					// no zipped file, but maybe .xml or old version? Try again.
   340 					err=nozip;
   341 				else	
   342 				{
   343 					QMessageBox::critical( 0, QObject::tr( "Critical Error" ),
   344 								   QString("unzip exit code:  %1").arg(zipProc->exitCode() ) +
   345 								   zipProc->getErrout() );
   346 					err=aborted;
   347 				}
   348 			} 
   349 		}
   350 	}
   351 #else
   352     // Do this process creation using Win32 API.
   353     //! Create process.
   354     PROCESS_INFORMATION piProcInfo;
   355     STARTUPINFO siStartInfo;
   356 
   357     // Initialize members of the PROCESS_INFORMATION structure.
   358     ::ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
   359 
   360     // Set up members of the STARTUPINFO structure.
   361     ::ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
   362     siStartInfo.cb = sizeof(STARTUPINFO);
   363 
   364     // Create command line.
   365     QString argv("unzip -o ");
   366     argv.append(QDir::convertSeparators(zipName));
   367     argv.append(" -d ");
   368     argv.append(QDir::convertSeparators(zipDir.path()));
   369 
   370     // Create the child process.
   371     if( !::CreateProcess(NULL, 
   372         (LPWSTR)argv.unicode(), // command line
   373         NULL, // process security attributes
   374         NULL, // primary thread security attributes
   375         TRUE, // handles are inherited
   376         0, // creation flags
   377         NULL, // use parent's environment
   378         NULL, // use parent's current directory
   379         &siStartInfo, // STARTUPINFO pointer
   380         &piProcInfo) ) // receives PROCESS_INFORMATION
   381     {
   382         err = aborted;
   383     }
   384     else
   385     {
   386         // Wait for it to finish.
   387         ::WaitForSingleObject( piProcInfo.hProcess, 10000 );
   388     }
   389 #endif
   390 	return err;	
   391 }
   392 
   393 bool loadStringFromDisk (const QString &fname, QString &s)
   394 {
   395 	s="";
   396 	QFile file ( fname);
   397 	if ( !file.open( QIODevice::ReadOnly ) ) return false;
   398 
   399 	QTextStream ts( &file );
   400 	ts.setEncoding (QTextStream::UnicodeUTF8);
   401 	while ( !ts.atEnd() ) 
   402 		s+=ts.readLine()+"\n"; 
   403 	file.close();
   404 	return true;
   405 }
   406 
   407 bool saveStringToDisk (const QString &fname, const QString &s)
   408 {
   409 	QFile file( fname);
   410 
   411 	file.setName ( fname);
   412 	if ( !file.open( QIODevice::WriteOnly ) ) 
   413 	{
   414 		file.close();
   415 		return false;
   416 	}	
   417 
   418 	// Write it finally, and write in UTF8, no matter what 
   419 	QTextStream ts( &file );
   420 	ts.setEncoding (QTextStream::UnicodeUTF8);
   421 	ts << s;
   422 	file.close();
   423 	return true;
   424 }
   425 
   426 
   427 ImagePreview::ImagePreview (QWidget *par=0): QLabel (par)
   428 {
   429 	fdia=(Q3FileDialog*)par;
   430 }
   431 
   432 void ImagePreview::previewUrl( const Q3Url &u )
   433 {
   434     QString path = u.path();
   435     QPixmap pix( path );
   436     if ( pix.isNull() )
   437 	{
   438 		// Strange: If we have fd->setMode (QFileDialog::ExistingFiles)
   439 		// in the filedialog, then there are 3 calls to previewURL 
   440 		// for each selection. And only the first is the actual selected file
   441 		// while the following 2 point to the directory above the current one.
   442 		// So here's my workaround:
   443 		
   444 		if (fdia && fdia->selectedFiles().count()==0)
   445 			setText( QObject::tr("This is not an image.") );
   446 		if (fdia &&fdia->selectedFiles().count()>1)
   447 			setText( QObject::tr("Sorry, no preview for\nmultiple selected files.") );
   448 	}	
   449     else
   450 	{
   451 		float max_w=300;
   452 		float max_h=300;
   453 		float r;
   454 		if (pix.width()>max_w)
   455 		{
   456 			r=max_w / pix.width();
   457 			pix.resize(qRound(pix.width()*r), qRound(pix.height()*r));
   458 			// TODO not a resize, but a shrink/enlarge is needed here...
   459 		}
   460 		if (pix.height()>max_h)
   461 		{
   462 			r=max_h / pix.height();
   463 			pix.resize(qRound(pix.width()*r), qRound(pix.height()*r));
   464 			// TODO not a resize, but a shrink/enlarge is needed here...
   465 		}
   466         setPixmap( pix );
   467 	}	
   468 }
   469 
   470 ImageIO::ImageIO ()
   471 {
   472 	// Create list with supported image types
   473 	// foreach (QByteArray format, QImageWriter::supportedImageFormats()) 
   474 	// imageTypes.append( tr("%1...").arg(QString(format).toUpper()));
   475 	imageFilters.append ("Images (*.png *.jpg *.jpeg *.bmp *.bmp *.ppm *.xpm *.xbm)");
   476 	imageTypes.append ("PNG");
   477 	imageFilters.append ("Portable Network Graphics (*.png)");
   478 	imageTypes.append ("PNG");
   479 	imageFilters.append ("Joint Photographic Experts Group (*.jpg)");
   480 	imageTypes.append ("JPG");
   481 	imageFilters.append ("Joint Photographic Experts Group (*.jpeg)");
   482 	imageTypes.append ("JPG");
   483 	imageFilters.append ("Windows Bitmap (*.bmp)");
   484 	imageTypes.append ("BMP");
   485 	imageFilters.append ("Portable Pixmap (*.ppm)");
   486 	imageTypes.append ("PPM");
   487 	imageFilters.append ("X11 Bitmap (*.xpm)");
   488 	imageTypes.append ("XPM");
   489 	imageFilters.append ("X11 Bitmap (*.xbm)");
   490 	imageTypes.append ("XBM");
   491 }
   492 
   493 QStringList ImageIO::getFilters()
   494 {
   495 	return imageFilters;
   496 }
   497 
   498 QString ImageIO::getType(QString filter)
   499 {
   500 	for (int i=0;i<imageFilters.count()+1;i++)
   501 		if (imageFilters.at(i)==filter) return imageTypes.at(i);
   502 	return QString();	
   503 }
   504 
   505