mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
Implemented Packages, + some minor tweaks to headers
git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@6 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -48,6 +48,14 @@ class PackageError : public Error {
|
|||||||
inline PackageError(const String& str) : Error(str) {}
|
inline PackageError(const String& str) : Error(str) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A file is not found
|
||||||
|
class FileNotFoundError : public PackageError {
|
||||||
|
public:
|
||||||
|
inline FileNotFoundError(const String& file, const String& package)
|
||||||
|
: PackageError(_("File not found: ") + file + _(" in package ") + package)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Parse errors
|
// ----------------------------------------------------------------------------- : Parse errors
|
||||||
|
|
||||||
/// Parse errors
|
/// Parse errors
|
||||||
|
|||||||
@@ -0,0 +1,395 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||||
|
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||||
|
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/io/package.hpp>
|
||||||
|
#include <util/error.hpp>
|
||||||
|
#include <wx/wfstream.h>
|
||||||
|
#include <wx/zipstrm.h>
|
||||||
|
#include <wx/dir.h>
|
||||||
|
#include <boost/scoped_ptr.hpp>
|
||||||
|
|
||||||
|
DECLARE_TYPEOF(Package::FileInfos);
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Package : outside
|
||||||
|
|
||||||
|
Package::Package()
|
||||||
|
: zipStream (nullptr)
|
||||||
|
, fileStream(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Package::~Package() {
|
||||||
|
delete zipStream;
|
||||||
|
delete fileStream;
|
||||||
|
// remove any remaining temporary files
|
||||||
|
FOR_EACH(f, files) {
|
||||||
|
if (f.second.wasWritten()) {
|
||||||
|
wxRemoveFile(f.second.tempName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Package::isOpened() const {
|
||||||
|
return !filename.empty();
|
||||||
|
}
|
||||||
|
bool Package::needSaveAs() const {
|
||||||
|
return !filename.empty();
|
||||||
|
}
|
||||||
|
String Package::name() const {
|
||||||
|
// wxFileName is too slow (profiled)
|
||||||
|
// wxFileName fn(filename);
|
||||||
|
// return fn.GetName();
|
||||||
|
size_t ext = filename.find_last_of(_('.'));
|
||||||
|
size_t slash = filename.find_last_of(_("/\\:"), ext);
|
||||||
|
if (slash == String::npos && ext == String::npos) return filename;
|
||||||
|
else if (slash == String::npos) return filename.substr(0,ext);
|
||||||
|
else if ( ext == String::npos) return filename.substr(slash+1);
|
||||||
|
else return filename.substr(slash+1, ext-slash-1);
|
||||||
|
}
|
||||||
|
const String& Package::absoluteFilename() const {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Package::open(const String& n) {
|
||||||
|
assert(!isOpened()); // not already opened
|
||||||
|
// get absolute path
|
||||||
|
wxFileName fn(n);
|
||||||
|
fn.Normalize();
|
||||||
|
filename = fn.GetFullPath();
|
||||||
|
// get modified time
|
||||||
|
if (!fn.FileExists() || !fn.GetTimes(0, &modified, 0)) {
|
||||||
|
modified = wxDateTime(0.0); // long time ago
|
||||||
|
}
|
||||||
|
// type of package
|
||||||
|
if (wxDirExists(filename)) {
|
||||||
|
openDirectory();
|
||||||
|
} else if (wxFileExists(filename)) {
|
||||||
|
openZipfile();
|
||||||
|
} else {
|
||||||
|
throw PackageError(_("Package not found: '") + filename + _("'"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Package::save(bool removeUnused) {
|
||||||
|
assert(!needSaveAs());
|
||||||
|
saveAs(filename, removeUnused);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Package::saveAs(const String& name, bool removeUnused) {
|
||||||
|
// type of package
|
||||||
|
if (wxDirExists(name)) {
|
||||||
|
saveToDirectory(name, removeUnused);
|
||||||
|
} else {
|
||||||
|
saveToZipfile (name, removeUnused);
|
||||||
|
}
|
||||||
|
filename = name;
|
||||||
|
// cleanup : remove temp files, remove deleted files from the list
|
||||||
|
FileInfos::iterator it = files.begin();
|
||||||
|
while (it != files.end()) {
|
||||||
|
if (it->second.wasWritten()) {
|
||||||
|
// remove corresponding temp file
|
||||||
|
wxRemoveFile(it->second.tempName);
|
||||||
|
}
|
||||||
|
if (!it->second.keep && removeUnused) {
|
||||||
|
// also remove the record of deleted files
|
||||||
|
FileInfos::iterator toRemove = it;
|
||||||
|
++it;
|
||||||
|
files.erase(toRemove);
|
||||||
|
} else {
|
||||||
|
// free zip entry, we will reopen the file
|
||||||
|
it->second.keep = false;
|
||||||
|
it->second.tempName.clear();
|
||||||
|
delete it->second.zipEntry;
|
||||||
|
it->second.zipEntry = 0;
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// reopen only needed for zipfile
|
||||||
|
if (!wxDirExists(name)) {
|
||||||
|
openZipfile();
|
||||||
|
} else {
|
||||||
|
// make sure we have no zip open
|
||||||
|
delete zipStream; zipStream = nullptr;
|
||||||
|
delete fileStream; fileStream = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Package : inside
|
||||||
|
|
||||||
|
/// Class that is a wxZipInputStream over a wxFileInput stream
|
||||||
|
/** Note that wxFileInputStream is also a base class, because it must be constructed first
|
||||||
|
* This class requires a patch in wxWidgets (2.5.4)
|
||||||
|
* change zipstrm.cpp line 1745:;
|
||||||
|
* if ((!m_ffile || AtHeader()));
|
||||||
|
* to:
|
||||||
|
* if ((AtHeader()));
|
||||||
|
* It seems that in 2.6.3 this is no longer necessary (TODO: test)
|
||||||
|
*/
|
||||||
|
class ZipFileInputStream : private wxFileInputStream, public wxZipInputStream {
|
||||||
|
public:
|
||||||
|
ZipFileInputStream(const String& filename, wxZipEntry* entry)
|
||||||
|
: wxFileInputStream(filename)
|
||||||
|
, wxZipInputStream(static_cast<wxFileInputStream&>(*this))
|
||||||
|
{
|
||||||
|
OpenEntry(*entry);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
InputStreamP Package::openIn(const String& file) {
|
||||||
|
// if (!n.empty() && n.getChar(0) == _('/')) {
|
||||||
|
// // absolute path, open file from another package
|
||||||
|
// return packMan.openFileFromPackage(n);
|
||||||
|
// }
|
||||||
|
FileInfos::iterator it = files.find(toStandardName(file));
|
||||||
|
if (it == files.end()) {
|
||||||
|
throw FileNotFoundError(file, filename);
|
||||||
|
}
|
||||||
|
InputStreamP stream;
|
||||||
|
if (it->second.wasWritten()) {
|
||||||
|
// written to this file, open the temp file
|
||||||
|
stream = new_shared1<wxFileInputStream>(it->second.tempName);
|
||||||
|
} else if (wxFileExists(filename+_("/")+file)) {
|
||||||
|
// a file in directory package
|
||||||
|
stream = new_shared1<wxFileInputStream>(filename+_("/")+file);
|
||||||
|
} else if (wxFileExists(filename) && it->second.zipEntry) {
|
||||||
|
// a file in a zip archive
|
||||||
|
stream = static_pointer_cast<wxZipInputStream>(
|
||||||
|
new_shared2<ZipFileInputStream>(filename, it->second.zipEntry));
|
||||||
|
} else {
|
||||||
|
// shouldn't happen, packaged changed by someone else since opening it
|
||||||
|
throw FileNotFoundError(file, filename);
|
||||||
|
}
|
||||||
|
if (!stream || !stream->IsOk()) {
|
||||||
|
throw FileNotFoundError(file, filename);
|
||||||
|
} else {
|
||||||
|
return stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
OutputStreamP Package::openOut(const String& file) {
|
||||||
|
return new_shared1<wxFileOutputStream>(nameOut(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
String Package::nameOut(const String& file) {
|
||||||
|
assert(wxThread::IsMain()); // Writing should only be done from the main thread
|
||||||
|
FileInfos::iterator it = files.find(file);
|
||||||
|
if (it == files.end()) {
|
||||||
|
// new file
|
||||||
|
it = addFile(file);
|
||||||
|
}
|
||||||
|
// return stream
|
||||||
|
if (it->second.wasWritten()) {
|
||||||
|
return it->second.tempName;
|
||||||
|
} else {
|
||||||
|
// create temp file
|
||||||
|
String name = wxFileName::CreateTempFileName(_("mse"));
|
||||||
|
it->second.tempName = name;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String Package::newFileName(const String& prefix, const String& suffix) {
|
||||||
|
assert(wxThread::IsMain()); // Writing should only be done from the main thread
|
||||||
|
String name;
|
||||||
|
UInt infix = 0;
|
||||||
|
while (true) {
|
||||||
|
// create filename
|
||||||
|
name = prefix;
|
||||||
|
name << ++infix;
|
||||||
|
name += suffix;
|
||||||
|
// check if a file with that name exists
|
||||||
|
FileInfos::iterator it = files.find(name);
|
||||||
|
if (it == files.end()) {
|
||||||
|
// name doesn't exist yet
|
||||||
|
addFile(name);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String Package::absoluteName(const String& file) {
|
||||||
|
assert(wxThread::IsMain());
|
||||||
|
FileInfos::iterator it = files.find(toStandardName(file));
|
||||||
|
if (it == files.end()) {
|
||||||
|
throw FileNotFoundError(file, filename);
|
||||||
|
}
|
||||||
|
if (it->second.wasWritten()) {
|
||||||
|
// written to this file, return the temp file
|
||||||
|
return it->second.tempName;
|
||||||
|
} else if (wxFileExists(filename+_("/")+file)) {
|
||||||
|
// dir package
|
||||||
|
return filename+_("/")+file;
|
||||||
|
} else {
|
||||||
|
// assume zip package
|
||||||
|
return filename+_("\1")+file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Package : private
|
||||||
|
|
||||||
|
Package::FileInfo::FileInfo()
|
||||||
|
: keep(false), zipEntry(nullptr)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Package::FileInfo::~FileInfo() {
|
||||||
|
delete zipEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Package::openDirectory() {
|
||||||
|
openSubdir(wxEmptyString);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Package::openSubdir(const String& name) {
|
||||||
|
wxDir d(filename + _("/") + name);
|
||||||
|
if (!d.IsOpened()) return; // ignore errors here
|
||||||
|
// find files
|
||||||
|
String f; // filename
|
||||||
|
for(bool ok = d.GetFirst(&f, wxEmptyString, wxDIR_FILES | wxDIR_HIDDEN) ; ok ; ok = d.GetNext(&f)) {
|
||||||
|
// add file
|
||||||
|
addFile(name + f);
|
||||||
|
// get modified time
|
||||||
|
wxFileName fn(filename + _("/") + name + f);
|
||||||
|
wxDateTime mod;
|
||||||
|
if (fn.GetTimes(0, &mod, 0) && mod > modified) modified = mod;
|
||||||
|
}
|
||||||
|
// find subdirs
|
||||||
|
for(bool ok = d.GetFirst(&f, wxEmptyString, wxDIR_DIRS | wxDIR_HIDDEN) ; ok ; ok = d.GetNext(&f)) {
|
||||||
|
openSubdir(name+f+_("/"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Package::openZipfile() {
|
||||||
|
// close old streams
|
||||||
|
delete fileStream; fileStream = nullptr;
|
||||||
|
delete zipStream; zipStream = nullptr;
|
||||||
|
// open streams
|
||||||
|
fileStream = new wxFileInputStream(filename);
|
||||||
|
if (!fileStream->IsOk()) throw PackageError(_("Package not found: '")+filename+_("'"));
|
||||||
|
zipStream = new wxZipInputStream(*fileStream);
|
||||||
|
if (!zipStream->IsOk()) throw PackageError(_("Package not found: '")+filename+_("'"));
|
||||||
|
// read zip entries
|
||||||
|
while (true) {
|
||||||
|
wxZipEntry* entry = zipStream->GetNextEntry();
|
||||||
|
if (!entry) break;
|
||||||
|
String name = toStandardName(entry->GetName());
|
||||||
|
files[name].zipEntry = entry;
|
||||||
|
}
|
||||||
|
zipStream->CloseEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Package::saveToDirectory(const String& saveAs, bool removeUnused) {
|
||||||
|
// write to a directory
|
||||||
|
FOR_EACH(f, files) {
|
||||||
|
if (!f.second.keep && removeUnused) {
|
||||||
|
// remove files that are not to be kept
|
||||||
|
// ignore failure (new file that is not kept)
|
||||||
|
wxRemoveFile(saveAs+_("/")+f.first);
|
||||||
|
} else if (f.second.wasWritten()) {
|
||||||
|
// move files that were updated
|
||||||
|
wxRemoveFile(saveAs+_("/")+f.first);
|
||||||
|
if (!wxRenameFile(f.second.tempName, saveAs+_("/")+f.first)) {
|
||||||
|
throw PackageError(_("Error while saving, unable to store file"));
|
||||||
|
}
|
||||||
|
} else if (filename != saveAs) {
|
||||||
|
// save as, copy old filess
|
||||||
|
if (!wxCopyFile(filename+_("/")+f.first, saveAs+_("/")+f.first)) {
|
||||||
|
throw PackageError(_("Error while saving, unable to store file"));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// old file, just keep it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Package::saveToZipfile(const String& saveAs, bool removeUnused) {
|
||||||
|
// create a temporary zip file name
|
||||||
|
String tempFile = saveAs + _(".tmp");
|
||||||
|
wxRemoveFile(tempFile);
|
||||||
|
// open zip file
|
||||||
|
try {
|
||||||
|
scoped_ptr<wxFileOutputStream> newFile(new wxFileOutputStream(tempFile));
|
||||||
|
if (!newFile->IsOk()) throw PackageError(_("Error while saving, unable to open output file"));
|
||||||
|
scoped_ptr<wxZipOutputStream> newZip(new wxZipOutputStream(*newFile));
|
||||||
|
if (!newZip->IsOk()) throw PackageError(_("Error while saving, unable to open output file"));
|
||||||
|
// copy everything to a new zip file, unless it's updated or removed
|
||||||
|
if (zipStream) newZip->CopyArchiveMetaData(*zipStream);
|
||||||
|
FOR_EACH(f, files) {
|
||||||
|
if (!f.second.keep && removeUnused) {
|
||||||
|
// to remove a file simply don't copy it
|
||||||
|
} else if (f.second.zipEntry && !f.second.wasWritten()) {
|
||||||
|
// old file, was also in zip, not changed
|
||||||
|
zipStream->CloseEntry();
|
||||||
|
newZip->CopyEntry(f.second.zipEntry, *zipStream);
|
||||||
|
f.second.zipEntry = 0;
|
||||||
|
} else {
|
||||||
|
// changed file, or the old package was not a zipfile
|
||||||
|
newZip->PutNextEntry(f.first);
|
||||||
|
InputStreamP temp = openIn(f.first);
|
||||||
|
newZip->Write(*temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// close the old file
|
||||||
|
delete zipStream; zipStream = nullptr;
|
||||||
|
delete fileStream; fileStream = nullptr;
|
||||||
|
} catch (Error e) {
|
||||||
|
// when things go wrong delete the temp file
|
||||||
|
wxRemoveFile(tempFile);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
// replace the old file with the new file, in effect commiting the changes
|
||||||
|
if (wxFileExists(saveAs)) {
|
||||||
|
// rename old file to .bak
|
||||||
|
wxRemoveFile(saveAs + _(".bak"));
|
||||||
|
wxRenameFile(saveAs, saveAs + _(".bak"));
|
||||||
|
}
|
||||||
|
wxRenameFile(tempFile, saveAs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Package::FileInfos::iterator Package::addFile(const String& name) {
|
||||||
|
return files.insert(make_pair(toStandardName(name), FileInfo())).first;
|
||||||
|
}
|
||||||
|
|
||||||
|
String Package::toStandardName(const String& name) {
|
||||||
|
String ret;
|
||||||
|
FOR_EACH_CONST(c, name) {
|
||||||
|
if (c==_('\\')) ret += _('/');
|
||||||
|
else ret += toLower(c);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Packaged
|
||||||
|
|
||||||
|
// note: reflection must be declared before it is used
|
||||||
|
IMPLEMENT_REFLECTION(Packaged) {
|
||||||
|
// default does nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
Packaged::Packaged() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void Packaged::open(const String& package) {
|
||||||
|
Package::open(package);
|
||||||
|
Reader reader(openIn(typeName()), absoluteFilename() + _("/") + typeName());
|
||||||
|
// try {
|
||||||
|
reader.handle(*this);
|
||||||
|
// } catch (const ParseError& err) {
|
||||||
|
// throw FileParseError(err.what(), filename+_("/")+typeName()); // more detailed message
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
void Packaged::save() {
|
||||||
|
//writeFile(thisT().fileName, thisT());
|
||||||
|
Package::save();
|
||||||
|
}
|
||||||
|
void Packaged::saveAs(const String& package) {
|
||||||
|
//writeFile(thisT().fileName, thisT());
|
||||||
|
Package::saveAs(package);
|
||||||
|
}
|
||||||
@@ -0,0 +1,183 @@
|
|||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
|
||||||
|
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
|
||||||
|
//| License: GNU General Public License 2 or later (see file COPYING) |
|
||||||
|
//+----------------------------------------------------------------------------+
|
||||||
|
|
||||||
|
#ifndef HEADER_UTIL_IO_PACKAGE
|
||||||
|
#define HEADER_UTIL_IO_PACKAGE
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
|
#include <util/reflect.hpp>
|
||||||
|
|
||||||
|
class wxFileInputStream;
|
||||||
|
class wxZipInputStream;
|
||||||
|
class wxZipEntry;
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Package
|
||||||
|
|
||||||
|
/// A package is a container for files. On disk it is either a directory or a zip file.
|
||||||
|
/** Specific types of packages should inherit from Package or from Packaged.
|
||||||
|
*
|
||||||
|
* When modifications are made to a package they are not directly written,
|
||||||
|
* only when save() or saveAs() is called
|
||||||
|
*
|
||||||
|
* To accomplish this modified files are first written to temporary files, when save() is called
|
||||||
|
* the temporary files are moved/copied.
|
||||||
|
*
|
||||||
|
* Zip files are accessed using wxZip(Input|Output)Stream.
|
||||||
|
* The zip input stream appears to only allow one file at a time, since the stream itself maintains
|
||||||
|
* state about what file we are reading.
|
||||||
|
* There are multiple solutions:
|
||||||
|
* 1. (currently used) Open a new ZipInputStream for each file
|
||||||
|
* 2. (may be faster) First read the file into a memory buffer,
|
||||||
|
* return a stream based on that buffer (StringInputStream).
|
||||||
|
*
|
||||||
|
* TODO: maybe support sub packages (a package inside another package)?
|
||||||
|
*/
|
||||||
|
class Package {
|
||||||
|
public:
|
||||||
|
// --------------------------------------------------- : Managing the outside of the package
|
||||||
|
|
||||||
|
/// Creates a new package
|
||||||
|
Package();
|
||||||
|
virtual ~Package();
|
||||||
|
|
||||||
|
/// Is a file opened?
|
||||||
|
bool isOpened() const;
|
||||||
|
/// Must the package be saved with saveAs()?
|
||||||
|
/// This is the case if the package has not been saved/opened before
|
||||||
|
bool needSaveAs() const;
|
||||||
|
/// Determines the short name of this package: the filename without path or extension
|
||||||
|
String name() const;
|
||||||
|
/// Return the absolute filename of this file
|
||||||
|
const String& absoluteFilename() const;
|
||||||
|
|
||||||
|
/// Open a package, should only be called when the package is constructed using the default constructor!
|
||||||
|
/// @pre open not called before [TODO]
|
||||||
|
void open(const String& package);
|
||||||
|
|
||||||
|
/// Saves the package, by default saves as a zip file, unless
|
||||||
|
/// it was already a directory
|
||||||
|
/** If removeUnused=true all files that were in the file and
|
||||||
|
* are not touched with referenceFile will be deleted from the new archive!
|
||||||
|
* This is a form of garbage collection, to get rid of old picture files for example.
|
||||||
|
*/
|
||||||
|
void save(bool removeUnused = true);
|
||||||
|
|
||||||
|
/// Saves the package under a different filename
|
||||||
|
void saveAs(const String& package, bool removeUnused = true);
|
||||||
|
|
||||||
|
|
||||||
|
// --------------------------------------------------- : Managing the inside of the package
|
||||||
|
|
||||||
|
/// Open an input stream for a file in the package.
|
||||||
|
InputStreamP openIn(const String& file);
|
||||||
|
|
||||||
|
/// Open an output stream for a file in the package.
|
||||||
|
/// (changes are only committed with save())
|
||||||
|
OutputStreamP openOut(const String& file);
|
||||||
|
|
||||||
|
/// Get a filename that can be written to to modfify a file in the package
|
||||||
|
/// (changes are only committed with save())
|
||||||
|
String nameOut(const String& file);
|
||||||
|
|
||||||
|
/// Creates a new, unique, filename with the specified prefix and suffix
|
||||||
|
/// for example newFileName("image/",".jpg") -> "image/1.jpg"
|
||||||
|
/// Returns the name of a temporary file that can be written to.
|
||||||
|
String newFileName(const String& prefix, const String& suffix);
|
||||||
|
|
||||||
|
/// Signal that a file is still used by this package.
|
||||||
|
/// Must be called for files not opened using openOut/nameOut
|
||||||
|
/// If they are to be kept in the package.
|
||||||
|
void referenceFile(const String& file);
|
||||||
|
|
||||||
|
/// Get an 'absolute filename' for a file in the package.
|
||||||
|
/// This file can later be opened from anywhere (other process) using openAbsoluteFile()
|
||||||
|
String absoluteName(const String& file);
|
||||||
|
|
||||||
|
/// Open a file given an absolute filename
|
||||||
|
static InputStreamP openAbsoluteFile(const String& name);
|
||||||
|
/*
|
||||||
|
// --------------------------------------------------- : Managing the inside of the package : IO files
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void readFile<T> (String n, T& obj) {
|
||||||
|
In i(openFileIn(n), filename + _("/") + n);
|
||||||
|
try {
|
||||||
|
i(obj);
|
||||||
|
} catch (ParseError e) {
|
||||||
|
throw FileParseError(e.what(), filename+_("/")+n); // more detailed message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void writeFile(const String& file, T obj) {
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
// --------------------------------------------------- : Private stuff
|
||||||
|
private:
|
||||||
|
|
||||||
|
/// Information about a file in the package
|
||||||
|
struct FileInfo {
|
||||||
|
FileInfo();
|
||||||
|
~FileInfo();
|
||||||
|
bool keep; ///< Should this file be kept in the package? (as opposed to deleting it)
|
||||||
|
String tempName; ///< Name of the temporary file where new contents of this file are placed
|
||||||
|
wxZipEntry* zipEntry; ///< Entry in the zip file for this file
|
||||||
|
inline bool wasWritten() ///< Is this file changed, and therefore written to a temporary file?
|
||||||
|
{ return !tempName.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Filename of the package
|
||||||
|
String filename;
|
||||||
|
/// Last modified time
|
||||||
|
DateTime modified;
|
||||||
|
public:
|
||||||
|
/// Information on files in the package
|
||||||
|
/** Note: must be public for DECLARE_TYPEOF to work */
|
||||||
|
typedef map<String, FileInfo> FileInfos;
|
||||||
|
private:
|
||||||
|
/// All files in the package
|
||||||
|
FileInfos files;
|
||||||
|
/// Filestream for reading zip files
|
||||||
|
wxFileInputStream* fileStream;
|
||||||
|
/// Filestream for reading zip files
|
||||||
|
wxZipInputStream* zipStream;
|
||||||
|
|
||||||
|
void openDirectory();
|
||||||
|
void openSubdir(const String&);
|
||||||
|
void openZipfile();
|
||||||
|
void saveToDirectory(const String&, bool);
|
||||||
|
void saveToZipfile(const String&, bool);
|
||||||
|
FileInfos::iterator addFile(const String& file);
|
||||||
|
static String toStandardName(const String& file);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : Packaged
|
||||||
|
|
||||||
|
/// Utility class for data types that are always stored in a package.
|
||||||
|
/** When the package is opened/saved a file describing the data object is read/written
|
||||||
|
*/
|
||||||
|
class Packaged : public Package {
|
||||||
|
public:
|
||||||
|
Packaged();
|
||||||
|
virtual ~Packaged() {}
|
||||||
|
|
||||||
|
/// Open a package, and read the data
|
||||||
|
void open(const String& package);
|
||||||
|
void save();
|
||||||
|
void saveAs(const String& package);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// filename of the data file, and extension of the package file
|
||||||
|
virtual String typeName() = 0;
|
||||||
|
/// Can be overloaded to do validation after loading
|
||||||
|
virtual void validate() {}
|
||||||
|
|
||||||
|
DECLARE_REFLECTION_VIRTUAL();
|
||||||
|
};
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------- : EOF
|
||||||
|
#endif
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# pragma warning (disable: 4100) // unreferenced formal parameter
|
# pragma warning (disable: 4100) // unreferenced formal parameter
|
||||||
|
# pragma warning (disable: 4355) // 'this' : used in base member initializer list
|
||||||
# pragma warning (disable: 4800) // 'int' : forcing value to bool 'true' or 'false' (performance warning)
|
# pragma warning (disable: 4800) // 'int' : forcing value to bool 'true' or 'false' (performance warning)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Includes
|
// ----------------------------------------------------------------------------- : Includes
|
||||||
|
|
||||||
#include <util/for_each.hpp>
|
|
||||||
#include <boost/shared_ptr.hpp>
|
#include <boost/shared_ptr.hpp>
|
||||||
using namespace boost;
|
using namespace boost;
|
||||||
|
|
||||||
@@ -23,8 +22,7 @@ using namespace boost;
|
|||||||
/// Declares the type TypeP as a shared_ptr<Type>
|
/// Declares the type TypeP as a shared_ptr<Type>
|
||||||
#define DECLARE_POINTER_TYPE(Type) \
|
#define DECLARE_POINTER_TYPE(Type) \
|
||||||
class Type; \
|
class Type; \
|
||||||
typedef shared_ptr<Type> Type##P; \
|
typedef shared_ptr<Type> Type##P;
|
||||||
DECLARE_TYPEOF_COLLECTION(Type##P)
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------- : Creating
|
// ----------------------------------------------------------------------------- : Creating
|
||||||
|
|
||||||
|
|||||||
+12
-11
@@ -103,23 +103,23 @@ enum ChildMenuID {
|
|||||||
|
|
||||||
// SymbolSelectEditor toolbar/menu
|
// SymbolSelectEditor toolbar/menu
|
||||||
, ID_PART = 2001
|
, ID_PART = 2001
|
||||||
, ID_PART_MERGE = ID_PART + PART_MERGE
|
, ID_PART_MERGE = ID_PART + 0//PART_MERGE
|
||||||
, ID_PART_SUBTRACT = ID_PART + PART_SUBTRACT
|
, ID_PART_SUBTRACT = ID_PART + 1//PART_SUBTRACT
|
||||||
, ID_PART_INTERSECTION = ID_PART + PART_INTERSECTION
|
, ID_PART_INTERSECTION = ID_PART + 2//PART_INTERSECTION
|
||||||
, ID_PART_DIFFERENCE = ID_PART + PART_DIFFERENCE
|
, ID_PART_DIFFERENCE = ID_PART + 3//PART_DIFFERENCE
|
||||||
, ID_PART_OVERLAP = ID_PART + PART_OVERLAP
|
, ID_PART_OVERLAP = ID_PART + 4//PART_OVERLAP
|
||||||
, ID_PART_BORDER = ID_PART + PART_BORDER
|
, ID_PART_BORDER = ID_PART + 5//PART_BORDER
|
||||||
, ID_PART_MAX
|
, ID_PART_MAX
|
||||||
|
|
||||||
// SymbolPointEditor toolbar/menu
|
// SymbolPointEditor toolbar/menu
|
||||||
, ID_SEGMENT = 2101
|
, ID_SEGMENT = 2101
|
||||||
, ID_SEGMENT_LINE = ID_SEGMENT + SEGMENT_LINE
|
, ID_SEGMENT_LINE = ID_SEGMENT + 0//SEGMENT_LINE
|
||||||
, ID_SEGMENT_CURVE = ID_SEGMENT + SEGMENT_CURVE
|
, ID_SEGMENT_CURVE = ID_SEGMENT + 1//SEGMENT_CURVE
|
||||||
, ID_SEGMENT_MAX
|
, ID_SEGMENT_MAX
|
||||||
, ID_LOCK = 2151
|
, ID_LOCK = 2151
|
||||||
, ID_LOCK_FREE = ID_LOCK + LOCK_FREE
|
, ID_LOCK_FREE = ID_LOCK + 0//LOCK_FREE
|
||||||
, ID_LOCK_DIR = ID_LOCK + LOCK_DIR
|
, ID_LOCK_DIR = ID_LOCK + 1//LOCK_DIR
|
||||||
, ID_LOCK_SIZE = ID_LOCK + LOCK_SIZE
|
, ID_LOCK_SIZE = ID_LOCK + 2//LOCK_SIZE
|
||||||
, ID_LOCK_MAX
|
, ID_LOCK_MAX
|
||||||
|
|
||||||
// SymbolBasicShapeEditor toolbar/menu
|
// SymbolBasicShapeEditor toolbar/menu
|
||||||
@@ -145,6 +145,7 @@ enum ControlID {
|
|||||||
, ID_VIEWER = 6001
|
, ID_VIEWER = 6001
|
||||||
, ID_EDITOR
|
, ID_EDITOR
|
||||||
, ID_CONTROL
|
, ID_CONTROL
|
||||||
|
, ID_TAB_BAR
|
||||||
, ID_CARD_LIST
|
, ID_CARD_LIST
|
||||||
, ID_PART_LIST
|
, ID_PART_LIST
|
||||||
, ID_NOTES
|
, ID_NOTES
|
||||||
|
|||||||
Reference in New Issue
Block a user