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