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