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