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