xref: /haiku/src/apps/icon-o-matic/import_export/Exporter.cpp (revision 405c9dc3f27cf660facae5253303ea24aaaf382e)
10e1ba39fSStephan Aßmus /*
21f3678c1SStephan Aßmus  * Copyright 2006, 2011, Stephan Aßmus <superstippi@gmx.de>.
31f3678c1SStephan Aßmus  * All rights reserved. Distributed under the terms of the MIT License.
40e1ba39fSStephan Aßmus  */
50e1ba39fSStephan Aßmus 
61f3678c1SStephan Aßmus 
70e1ba39fSStephan Aßmus #include "Exporter.h"
80e1ba39fSStephan Aßmus 
90e1ba39fSStephan Aßmus #include <fs_attr.h>
10fb3e35fcSMichael Lotz #include <new>
11252f4767SRyan Leavengood #include <stdio.h>
120e1ba39fSStephan Aßmus 
130e1ba39fSStephan Aßmus #include <Alert.h>
14518852fcSAdrien Destugues #include <Catalog.h>
150e1ba39fSStephan Aßmus #include <File.h>
16518852fcSAdrien Destugues #include <Locale.h>
170e1ba39fSStephan Aßmus #include <Node.h>
180e1ba39fSStephan Aßmus #include <NodeInfo.h>
190e1ba39fSStephan Aßmus #include <Path.h>
200e1ba39fSStephan Aßmus #include <Roster.h>
210e1ba39fSStephan Aßmus #include <String.h>
220e1ba39fSStephan Aßmus 
230e1ba39fSStephan Aßmus #include "CommandStack.h"
240e1ba39fSStephan Aßmus #include "Document.h"
250e1ba39fSStephan Aßmus #include "Icon.h"
260e1ba39fSStephan Aßmus 
27518852fcSAdrien Destugues 
28546208a5SOliver Tappe #undef B_TRANSLATION_CONTEXT
29546208a5SOliver Tappe #define B_TRANSLATION_CONTEXT "Icon-O-Matic-Exporter"
30518852fcSAdrien Destugues 
31518852fcSAdrien Destugues 
323ec18e87SMarcus Overhagen using std::nothrow;
333ec18e87SMarcus Overhagen 
341f3678c1SStephan Aßmus 
Exporter()350e1ba39fSStephan Aßmus Exporter::Exporter()
360e1ba39fSStephan Aßmus 	: fDocument(NULL),
370e1ba39fSStephan Aßmus 	  fClonedIcon(NULL),
380e1ba39fSStephan Aßmus 	  fRef(),
390e1ba39fSStephan Aßmus 	  fExportThread(-1),
400e1ba39fSStephan Aßmus 	  fSelfDestroy(false)
410e1ba39fSStephan Aßmus {
420e1ba39fSStephan Aßmus }
430e1ba39fSStephan Aßmus 
441f3678c1SStephan Aßmus 
~Exporter()450e1ba39fSStephan Aßmus Exporter::~Exporter()
460e1ba39fSStephan Aßmus {
471f3678c1SStephan Aßmus 	WaitForExportThread();
480e1ba39fSStephan Aßmus 
490e1ba39fSStephan Aßmus 	delete fClonedIcon;
500e1ba39fSStephan Aßmus }
510e1ba39fSStephan Aßmus 
521f3678c1SStephan Aßmus 
530e1ba39fSStephan Aßmus status_t
Export(Document * document,const entry_ref & ref)540e1ba39fSStephan Aßmus Exporter::Export(Document* document, const entry_ref& ref)
550e1ba39fSStephan Aßmus {
560e1ba39fSStephan Aßmus 	if (!document || ref.name == NULL)
570e1ba39fSStephan Aßmus 		return B_BAD_VALUE;
580e1ba39fSStephan Aßmus 
590e1ba39fSStephan Aßmus 	fDocument = document;
600e1ba39fSStephan Aßmus 	fClonedIcon = fDocument->Icon()->Clone();
610e1ba39fSStephan Aßmus 	if (!fClonedIcon)
620e1ba39fSStephan Aßmus 		return B_NO_MEMORY;
630e1ba39fSStephan Aßmus 
640e1ba39fSStephan Aßmus 	fRef = ref;
650e1ba39fSStephan Aßmus 
660e1ba39fSStephan Aßmus 	fExportThread = spawn_thread(_ExportThreadEntry, "export",
670e1ba39fSStephan Aßmus 								 B_NORMAL_PRIORITY, this);
681f3678c1SStephan Aßmus 	if (fExportThread < 0)
691f3678c1SStephan Aßmus 		return (status_t)fExportThread;
701f3678c1SStephan Aßmus 
710e1ba39fSStephan Aßmus 	resume_thread(fExportThread);
720e1ba39fSStephan Aßmus 
731f3678c1SStephan Aßmus 	return B_OK;
740e1ba39fSStephan Aßmus }
750e1ba39fSStephan Aßmus 
761f3678c1SStephan Aßmus 
770e1ba39fSStephan Aßmus void
SetSelfDestroy(bool selfDestroy)780e1ba39fSStephan Aßmus Exporter::SetSelfDestroy(bool selfDestroy)
790e1ba39fSStephan Aßmus {
800e1ba39fSStephan Aßmus 	fSelfDestroy = selfDestroy;
810e1ba39fSStephan Aßmus }
820e1ba39fSStephan Aßmus 
831f3678c1SStephan Aßmus 
841f3678c1SStephan Aßmus void
WaitForExportThread()851f3678c1SStephan Aßmus Exporter::WaitForExportThread()
861f3678c1SStephan Aßmus {
871f3678c1SStephan Aßmus 	if (fExportThread >= 0 && find_thread(NULL) != fExportThread) {
881f3678c1SStephan Aßmus 		status_t ret;
891f3678c1SStephan Aßmus 		wait_for_thread(fExportThread, &ret);
901f3678c1SStephan Aßmus 		fExportThread = -1;
911f3678c1SStephan Aßmus 	}
921f3678c1SStephan Aßmus }
931f3678c1SStephan Aßmus 
941f3678c1SStephan Aßmus 
950e1ba39fSStephan Aßmus // #pragma mark -
960e1ba39fSStephan Aßmus 
971f3678c1SStephan Aßmus 
980e1ba39fSStephan Aßmus int32
_ExportThreadEntry(void * cookie)990e1ba39fSStephan Aßmus Exporter::_ExportThreadEntry(void* cookie)
1000e1ba39fSStephan Aßmus {
1010e1ba39fSStephan Aßmus 	Exporter* exporter = (Exporter*)cookie;
1020e1ba39fSStephan Aßmus 	return exporter->_ExportThread();
1030e1ba39fSStephan Aßmus }
1040e1ba39fSStephan Aßmus 
1051f3678c1SStephan Aßmus 
1060e1ba39fSStephan Aßmus int32
_ExportThread()1070e1ba39fSStephan Aßmus Exporter::_ExportThread()
1080e1ba39fSStephan Aßmus {
1090e1ba39fSStephan Aßmus 	status_t ret = _Export(fClonedIcon, &fRef);
1100e1ba39fSStephan Aßmus 	if (ret < B_OK) {
1110e1ba39fSStephan Aßmus 		// inform user of failure at this point
112518852fcSAdrien Destugues 		BString helper(B_TRANSLATE("Saving your document failed!"));
113*405c9dc3SZardshard 		helper << "\n\n" << ErrorCodeToString(ret);
114a4e4beafSHumdinger 		BAlert* alert = new BAlert(B_TRANSLATE_CONTEXT("Bad news", "Title of error alert"),
115a4e4beafSHumdinger 			helper.String(),
116a4e4beafSHumdinger 			B_TRANSLATE_COMMENT("Bleep!", "Exporter - Continue in error dialog"),
117*405c9dc3SZardshard 			NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
1180e1ba39fSStephan Aßmus 		// launch alert asynchronously
119aed35104SHumdinger 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
1200e1ba39fSStephan Aßmus 		alert->Go(NULL);
1210e1ba39fSStephan Aßmus 	} else {
1220e1ba39fSStephan Aßmus 		// success
1230e1ba39fSStephan Aßmus 
1240e1ba39fSStephan Aßmus 		// add to recent document list
1250e1ba39fSStephan Aßmus 		be_roster->AddToRecentDocuments(&fRef);
1260e1ba39fSStephan Aßmus 		// mark command stack state as saved,
1270e1ba39fSStephan Aßmus 		fDocument->CommandStack()->Save();
1280e1ba39fSStephan Aßmus 			// NOTE: CommandStack is thread safe
1290e1ba39fSStephan Aßmus 
1300e1ba39fSStephan Aßmus 		if (fDocument->WriteLock()) {
1310e1ba39fSStephan Aßmus 			// set ref and name of document
1320e1ba39fSStephan Aßmus //			fDocument->SetRef(fRef);
1330e1ba39fSStephan Aßmus 			fDocument->SetName(fRef.name);
1340e1ba39fSStephan Aßmus 
1350e1ba39fSStephan Aßmus 			fDocument->WriteUnlock();
1360e1ba39fSStephan Aßmus 		}
1370e1ba39fSStephan Aßmus 	}
1380e1ba39fSStephan Aßmus 
1390e1ba39fSStephan Aßmus 	if (fSelfDestroy)
1400e1ba39fSStephan Aßmus 		delete this;
1410e1ba39fSStephan Aßmus 
1420e1ba39fSStephan Aßmus 	return ret;
1430e1ba39fSStephan Aßmus }
1440e1ba39fSStephan Aßmus 
1451f3678c1SStephan Aßmus 
1460e1ba39fSStephan Aßmus status_t
_Export(const Icon * icon,const entry_ref * docRef)1471f3678c1SStephan Aßmus Exporter::_Export(const Icon* icon, const entry_ref* docRef)
1480e1ba39fSStephan Aßmus {
149129302e4SStephan Aßmus 	// TODO: reenable the commented out code, but make it work
150129302e4SStephan Aßmus 	// the opposite direction, ie *copy* the file contents
151129302e4SStephan Aßmus 
1520e1ba39fSStephan Aßmus 	BEntry entry(docRef, true);
1530e1ba39fSStephan Aßmus 	if (entry.IsDirectory())
1540e1ba39fSStephan Aßmus 		return B_BAD_VALUE;
1550e1ba39fSStephan Aßmus 
1560e1ba39fSStephan Aßmus 	const entry_ref* ref = docRef;
157129302e4SStephan Aßmus //	entry_ref tempRef;
158129302e4SStephan Aßmus //
159129302e4SStephan Aßmus //	if (entry.Exists()) {
160129302e4SStephan Aßmus //		// if the file exists create a temporary file in the same folder
161129302e4SStephan Aßmus //		// and hope that it doesn't already exist...
162129302e4SStephan Aßmus //		BPath tempPath(docRef);
163129302e4SStephan Aßmus //		if (tempPath.GetParent(&tempPath) >= B_OK) {
164129302e4SStephan Aßmus //			BString helper(docRef->name);
165129302e4SStephan Aßmus //			helper << system_time();
166129302e4SStephan Aßmus //			if (tempPath.Append(helper.String()) >= B_OK
167129302e4SStephan Aßmus //				&& entry.SetTo(tempPath.Path()) >= B_OK
168129302e4SStephan Aßmus //				&& entry.GetRef(&tempRef) >= B_OK) {
169129302e4SStephan Aßmus //				// have the output ref point to the temporary
170129302e4SStephan Aßmus //				// file instead
171129302e4SStephan Aßmus //				ref = &tempRef;
172129302e4SStephan Aßmus //			}
173129302e4SStephan Aßmus //		}
174129302e4SStephan Aßmus //	}
1750e1ba39fSStephan Aßmus 
1760e1ba39fSStephan Aßmus 	status_t ret = B_BAD_VALUE;
1770e1ba39fSStephan Aßmus 
1780e1ba39fSStephan Aßmus 	// do the actual save operation into a file
1790e1ba39fSStephan Aßmus 	BFile outFile(ref, B_CREATE_FILE | B_READ_WRITE | B_ERASE_FILE);
1800e1ba39fSStephan Aßmus 	ret = outFile.InitCheck();
1810e1ba39fSStephan Aßmus 	if (ret == B_OK) {
1820e1ba39fSStephan Aßmus 		try {
1830e1ba39fSStephan Aßmus 			// export using the virtual Export() version
1840e1ba39fSStephan Aßmus 			ret = Export(icon, &outFile);
1850e1ba39fSStephan Aßmus 		} catch (...) {
1860e1ba39fSStephan Aßmus 			printf("Exporter::_Export() - "
1870e1ba39fSStephan Aßmus 				   "unkown exception occured!\n");
1880e1ba39fSStephan Aßmus 			ret = B_ERROR;
1890e1ba39fSStephan Aßmus 		}
1900e1ba39fSStephan Aßmus 		if (ret < B_OK) {
1910e1ba39fSStephan Aßmus 			printf("Exporter::_Export() - "
1920e1ba39fSStephan Aßmus 				   "failed to export icon: %s\n", strerror(ret));
1930e1ba39fSStephan Aßmus 		}
1940e1ba39fSStephan Aßmus 	} else {
1950e1ba39fSStephan Aßmus 		printf("Exporter::_Export() - "
1960e1ba39fSStephan Aßmus 			   "failed to create output file: %s\n", strerror(ret));
1970e1ba39fSStephan Aßmus 	}
1980e1ba39fSStephan Aßmus 	outFile.Unset();
1990e1ba39fSStephan Aßmus 
200129302e4SStephan Aßmus //	if (ret < B_OK && ref != docRef) {
201129302e4SStephan Aßmus //		// in case of failure, remove temporary file
202129302e4SStephan Aßmus //		entry.Remove();
203129302e4SStephan Aßmus //	}
204129302e4SStephan Aßmus //
205129302e4SStephan Aßmus //	if (ret >= B_OK && ref != docRef) {
206129302e4SStephan Aßmus //		// move temp file overwriting actual document file
207129302e4SStephan Aßmus //		BEntry docEntry(docRef, true);
208129302e4SStephan Aßmus //		// copy attributes of previous document file
209129302e4SStephan Aßmus //		BNode sourceNode(&docEntry);
210129302e4SStephan Aßmus //		BNode destNode(&entry);
211129302e4SStephan Aßmus //		if (sourceNode.InitCheck() >= B_OK && destNode.InitCheck() >= B_OK) {
212129302e4SStephan Aßmus //			// lock the nodes
213129302e4SStephan Aßmus //			if (sourceNode.Lock() >= B_OK) {
214129302e4SStephan Aßmus //				if (destNode.Lock() >= B_OK) {
215129302e4SStephan Aßmus //					// iterate over the attributes
216129302e4SStephan Aßmus //					char attrName[B_ATTR_NAME_LENGTH];
217129302e4SStephan Aßmus //					while (sourceNode.GetNextAttrName(attrName) >= B_OK) {
218129302e4SStephan Aßmus ////						// skip the icon, since we probably wrote that
219129302e4SStephan Aßmus ////						// before
220129302e4SStephan Aßmus ////						if (strcmp(attrName, "BEOS:ICON") == 0)
221129302e4SStephan Aßmus ////							continue;
222129302e4SStephan Aßmus //						attr_info info;
223129302e4SStephan Aßmus //						if (sourceNode.GetAttrInfo(attrName, &info) >= B_OK) {
224129302e4SStephan Aßmus //							char *buffer = new (nothrow) char[info.size];
225129302e4SStephan Aßmus //							if (buffer && sourceNode.ReadAttr(attrName, info.type, 0,
226129302e4SStephan Aßmus //															  buffer, info.size) == info.size) {
227129302e4SStephan Aßmus //								destNode.WriteAttr(attrName, info.type, 0,
228129302e4SStephan Aßmus //												   buffer, info.size);
229129302e4SStephan Aßmus //							}
230129302e4SStephan Aßmus //							delete[] buffer;
231129302e4SStephan Aßmus //						}
232129302e4SStephan Aßmus //					}
233129302e4SStephan Aßmus //					destNode.Unlock();
234129302e4SStephan Aßmus //				}
235129302e4SStephan Aßmus //				sourceNode.Unlock();
236129302e4SStephan Aßmus //			}
237129302e4SStephan Aßmus //		}
238129302e4SStephan Aßmus //		// clobber the orginal file with the new temporary one
239129302e4SStephan Aßmus //		ret = entry.Rename(docRef->name, true);
240129302e4SStephan Aßmus //	}
2410e1ba39fSStephan Aßmus 
2420e1ba39fSStephan Aßmus 	if (ret >= B_OK && MIMEType()) {
2430e1ba39fSStephan Aßmus 		// set file type
2440e1ba39fSStephan Aßmus 		BNode node(docRef);
2450e1ba39fSStephan Aßmus 		if (node.InitCheck() == B_OK) {
2460e1ba39fSStephan Aßmus 			BNodeInfo nodeInfo(&node);
2470e1ba39fSStephan Aßmus 			if (nodeInfo.InitCheck() == B_OK)
2480e1ba39fSStephan Aßmus 				nodeInfo.SetType(MIMEType());
2490e1ba39fSStephan Aßmus 		}
2500e1ba39fSStephan Aßmus 	}
2510e1ba39fSStephan Aßmus 	return ret;
2520e1ba39fSStephan Aßmus }
253