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