1/*
2 * Copyright 2006, 2011, Stephan A��mus <superstippi@gmx.de>.
3 * All rights reserved. Distributed under the terms of the MIT License.
4 */
5
6
7#include "Exporter.h"
8
9#include <fs_attr.h>
10#include <new>
11#include <stdio.h>
12
13#include <Alert.h>
14#include <Catalog.h>
15#include <File.h>
16#include <Locale.h>
17#include <Node.h>
18#include <NodeInfo.h>
19#include <Path.h>
20#include <Roster.h>
21#include <String.h>
22
23#include "CommandStack.h"
24#include "Document.h"
25#include "Icon.h"
26
27
28#undef B_TRANSLATION_CONTEXT
29#define B_TRANSLATION_CONTEXT "Icon-O-Matic-Exporter"
30
31
32using std::nothrow;
33
34
35Exporter::Exporter()
36	: fDocument(NULL),
37	  fClonedIcon(NULL),
38	  fRef(),
39	  fExportThread(-1),
40	  fSelfDestroy(false)
41{
42}
43
44
45Exporter::~Exporter()
46{
47	WaitForExportThread();
48
49	delete fClonedIcon;
50}
51
52
53status_t
54Exporter::Export(Document* document, const entry_ref& ref)
55{
56	if (!document || ref.name == NULL)
57		return B_BAD_VALUE;
58
59	fDocument = document;
60	fClonedIcon = fDocument->Icon()->Clone();
61	if (!fClonedIcon)
62		return B_NO_MEMORY;
63
64	fRef = ref;
65
66	fExportThread = spawn_thread(_ExportThreadEntry, "export",
67								 B_NORMAL_PRIORITY, this);
68	if (fExportThread < 0)
69		return (status_t)fExportThread;
70
71	resume_thread(fExportThread);
72
73	return B_OK;
74}
75
76
77void
78Exporter::SetSelfDestroy(bool selfDestroy)
79{
80	fSelfDestroy = selfDestroy;
81}
82
83
84void
85Exporter::WaitForExportThread()
86{
87	if (fExportThread >= 0 && find_thread(NULL) != fExportThread) {
88		status_t ret;
89		wait_for_thread(fExportThread, &ret);
90		fExportThread = -1;
91	}
92}
93
94
95// #pragma mark -
96
97
98int32
99Exporter::_ExportThreadEntry(void* cookie)
100{
101	Exporter* exporter = (Exporter*)cookie;
102	return exporter->_ExportThread();
103}
104
105
106int32
107Exporter::_ExportThread()
108{
109	status_t ret = _Export(fClonedIcon, &fRef);
110	if (ret < B_OK) {
111		// inform user of failure at this point
112		BString helper(B_TRANSLATE("Saving your document failed!"));
113		helper << "\n\n" << ErrorCodeToString(ret);
114		BAlert* alert = new BAlert(B_TRANSLATE_CONTEXT("Bad news", "Title of error alert"),
115			helper.String(),
116			B_TRANSLATE_COMMENT("Bleep!", "Exporter - Continue in error dialog"),
117			NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
118		// launch alert asynchronously
119		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
120		alert->Go(NULL);
121	} else {
122		// success
123
124		// add to recent document list
125		be_roster->AddToRecentDocuments(&fRef);
126		// mark command stack state as saved,
127		fDocument->CommandStack()->Save();
128			// NOTE: CommandStack is thread safe
129
130		if (fDocument->WriteLock()) {
131			// set ref and name of document
132//			fDocument->SetRef(fRef);
133			fDocument->SetName(fRef.name);
134
135			fDocument->WriteUnlock();
136		}
137	}
138
139	if (fSelfDestroy)
140		delete this;
141
142	return ret;
143}
144
145
146status_t
147Exporter::_Export(const Icon* icon, const entry_ref* docRef)
148{
149	// TODO: reenable the commented out code, but make it work
150	// the opposite direction, ie *copy* the file contents
151
152	BEntry entry(docRef, true);
153	if (entry.IsDirectory())
154		return B_BAD_VALUE;
155
156	const entry_ref* ref = docRef;
157//	entry_ref tempRef;
158//
159//	if (entry.Exists()) {
160//		// if the file exists create a temporary file in the same folder
161//		// and hope that it doesn't already exist...
162//		BPath tempPath(docRef);
163//		if (tempPath.GetParent(&tempPath) >= B_OK) {
164//			BString helper(docRef->name);
165//			helper << system_time();
166//			if (tempPath.Append(helper.String()) >= B_OK
167//				&& entry.SetTo(tempPath.Path()) >= B_OK
168//				&& entry.GetRef(&tempRef) >= B_OK) {
169//				// have the output ref point to the temporary
170//				// file instead
171//				ref = &tempRef;
172//			}
173//		}
174//	}
175
176	status_t ret = B_BAD_VALUE;
177
178	// do the actual save operation into a file
179	BFile outFile(ref, B_CREATE_FILE | B_READ_WRITE | B_ERASE_FILE);
180	ret = outFile.InitCheck();
181	if (ret == B_OK) {
182		try {
183			// export using the virtual Export() version
184			ret = Export(icon, &outFile);
185		} catch (...) {
186			printf("Exporter::_Export() - "
187				   "unkown exception occured!\n");
188			ret = B_ERROR;
189		}
190		if (ret < B_OK) {
191			printf("Exporter::_Export() - "
192				   "failed to export icon: %s\n", strerror(ret));
193		}
194	} else {
195		printf("Exporter::_Export() - "
196			   "failed to create output file: %s\n", strerror(ret));
197	}
198	outFile.Unset();
199
200//	if (ret < B_OK && ref != docRef) {
201//		// in case of failure, remove temporary file
202//		entry.Remove();
203//	}
204//
205//	if (ret >= B_OK && ref != docRef) {
206//		// move temp file overwriting actual document file
207//		BEntry docEntry(docRef, true);
208//		// copy attributes of previous document file
209//		BNode sourceNode(&docEntry);
210//		BNode destNode(&entry);
211//		if (sourceNode.InitCheck() >= B_OK && destNode.InitCheck() >= B_OK) {
212//			// lock the nodes
213//			if (sourceNode.Lock() >= B_OK) {
214//				if (destNode.Lock() >= B_OK) {
215//					// iterate over the attributes
216//					char attrName[B_ATTR_NAME_LENGTH];
217//					while (sourceNode.GetNextAttrName(attrName) >= B_OK) {
218////						// skip the icon, since we probably wrote that
219////						// before
220////						if (strcmp(attrName, "BEOS:ICON") == 0)
221////							continue;
222//						attr_info info;
223//						if (sourceNode.GetAttrInfo(attrName, &info) >= B_OK) {
224//							char *buffer = new (nothrow) char[info.size];
225//							if (buffer && sourceNode.ReadAttr(attrName, info.type, 0,
226//															  buffer, info.size) == info.size) {
227//								destNode.WriteAttr(attrName, info.type, 0,
228//												   buffer, info.size);
229//							}
230//							delete[] buffer;
231//						}
232//					}
233//					destNode.Unlock();
234//				}
235//				sourceNode.Unlock();
236//			}
237//		}
238//		// clobber the orginal file with the new temporary one
239//		ret = entry.Rename(docRef->name, true);
240//	}
241
242	if (ret >= B_OK && MIMEType()) {
243		// set file type
244		BNode node(docRef);
245		if (node.InitCheck() == B_OK) {
246			BNodeInfo nodeInfo(&node);
247			if (nodeInfo.InitCheck() == B_OK)
248				nodeInfo.SetType(MIMEType());
249		}
250	}
251	return ret;
252}
253