1 /* 2 * Copyright (c) 2005-2010, Haiku, Inc. 3 * Distributed under the terms of the MIT license. 4 * 5 * Author: 6 * DarkWyrm <darkwyrm@gmail.com> 7 */ 8 #include "ResView.h" 9 10 #include <Application.h> 11 #include <File.h> 12 #include <Menu.h> 13 #include <MenuItem.h> 14 #include <Path.h> 15 #include <ScrollView.h> 16 #include <TranslatorRoster.h> 17 #include <TypeConstants.h> 18 19 #include <stdio.h> 20 #include <stdlib.h> 21 22 #include "App.h" 23 #include "ColumnTypes.h" 24 #include "ResourceData.h" 25 #include "ResFields.h" 26 #include "ResListView.h" 27 #include "ResWindow.h" 28 #include "PreviewColumn.h" 29 #include "Editor.h" 30 31 static int32 sUntitled = 1; 32 33 ResourceRoster gResRoster; 34 35 enum { 36 M_NEW_FILE = 'nwfl', 37 M_OPEN_FILE, 38 M_SAVE_FILE, 39 M_QUIT, 40 M_SELECT_FILE, 41 M_DELETE_RESOURCE, 42 M_EDIT_RESOURCE 43 }; 44 45 ResView::ResView(const BRect &frame, const char *name, const int32 &resize, 46 const int32 &flags, const entry_ref *ref) 47 : BView(frame, name, resize, flags), 48 fRef(NULL), 49 fSaveStatus(FILE_INIT), 50 fOpenPanel(NULL), 51 fSavePanel(NULL) 52 { 53 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 54 if (ref) { 55 fRef = new entry_ref; 56 *fRef = *ref; 57 fFileName = fRef->name; 58 } else { 59 fFileName = "Untitled "; 60 fFileName << sUntitled; 61 sUntitled++; 62 } 63 64 BRect r(Bounds()); 65 r.bottom = 16; 66 fBar = new BMenuBar(r, "bar"); 67 AddChild(fBar); 68 69 BuildMenus(fBar); 70 71 r = Bounds(); 72 r.top = fBar->Frame().bottom + 4; 73 fListView = new ResListView(r, "gridview", B_FOLLOW_ALL, B_WILL_DRAW, B_FANCY_BORDER); 74 AddChild(fListView); 75 76 float width = be_plain_font->StringWidth("00000") + 20; 77 fListView->AddColumn(new BStringColumn("ID", width, width, 100, B_TRUNCATE_END), 0); 78 79 fListView->AddColumn(new BStringColumn("Type", width, width, 100, B_TRUNCATE_END), 1); 80 fListView->AddColumn(new BStringColumn("Name", 150, 50, 300, B_TRUNCATE_END), 2); 81 fListView->AddColumn(new PreviewColumn("Data", 150, 50, 300), 3); 82 83 // Editing is disabled for now 84 fListView->SetInvocationMessage(new BMessage(M_EDIT_RESOURCE)); 85 86 width = be_plain_font->StringWidth("1000 bytes") + 20; 87 fListView->AddColumn(new BSizeColumn("Size", width, 10, 100), 4); 88 89 fOpenPanel = new BFilePanel(B_OPEN_PANEL); 90 if (ref) 91 OpenFile(*ref); 92 93 fSavePanel = new BFilePanel(B_SAVE_PANEL); 94 } 95 96 97 ResView::~ResView(void) 98 { 99 EmptyDataList(); 100 delete fRef; 101 delete fOpenPanel; 102 delete fSavePanel; 103 } 104 105 106 void 107 ResView::AttachedToWindow(void) 108 { 109 for (int32 i = 0; i < fBar->CountItems(); i++) 110 fBar->SubmenuAt(i)->SetTargetForItems(this); 111 fListView->SetTarget(this); 112 113 BMessenger messenger(this); 114 fOpenPanel->SetTarget(messenger); 115 fSavePanel->SetTarget(messenger); 116 117 Window()->Lock(); 118 BString title("ResEdit: "); 119 title << fFileName; 120 Window()->SetTitle(title.String()); 121 Window()->Unlock(); 122 } 123 124 125 void 126 ResView::MessageReceived(BMessage *msg) 127 { 128 switch (msg->what) { 129 case M_NEW_FILE: { 130 BRect r(100, 100, 400, 400); 131 if (Window()) 132 r = Window()->Frame().OffsetByCopy(10, 10); 133 ResWindow *win = new ResWindow(r); 134 win->Show(); 135 break; 136 } 137 case M_OPEN_FILE: { 138 be_app->PostMessage(M_SHOW_OPEN_PANEL); 139 break; 140 } 141 case B_CANCEL: { 142 if (fSaveStatus == FILE_QUIT_AFTER_SAVE) 143 SetSaveStatus(FILE_DIRTY); 144 break; 145 } 146 case B_SAVE_REQUESTED: { 147 entry_ref saveDir; 148 BString name; 149 if (msg->FindRef("directory",&saveDir) == B_OK && 150 msg->FindString("name",&name) == B_OK) { 151 SetTo(saveDir,name); 152 SaveFile(); 153 } 154 break; 155 } 156 case M_SAVE_FILE: { 157 if (!fRef) 158 fSavePanel->Show(); 159 else 160 SaveFile(); 161 break; 162 } 163 case M_SHOW_SAVE_PANEL: { 164 fSavePanel->Show(); 165 break; 166 } 167 case M_QUIT: { 168 be_app->PostMessage(B_QUIT_REQUESTED); 169 break; 170 } 171 case B_REFS_RECEIVED: { 172 int32 i = 0; 173 entry_ref ref; 174 while (msg->FindRef("refs", i++, &ref) == B_OK) 175 AddResource(ref); 176 break; 177 } 178 case M_SELECT_FILE: { 179 fOpenPanel->Show(); 180 break; 181 } 182 case M_DELETE_RESOURCE: { 183 DeleteSelectedResources(); 184 break; 185 } 186 case M_EDIT_RESOURCE: { 187 BRow *row = fListView->CurrentSelection(); 188 TypeCodeField *field = (TypeCodeField*)row->GetField(1); 189 gResRoster.SpawnEditor(field->GetResourceData(), this); 190 break; 191 } 192 case M_UPDATE_RESOURCE: { 193 ResourceData *item; 194 if (msg->FindPointer("item", (void **)&item) != B_OK) 195 break; 196 197 for (int32 i = 0; i < fListView->CountRows(); i++) { 198 BRow *row = fListView->RowAt(i); 199 TypeCodeField *field = (TypeCodeField*)row->GetField(1); 200 if (!field || field->GetResourceData() != item) 201 continue; 202 203 UpdateRow(row); 204 break; 205 } 206 break; 207 } 208 default: 209 BView::MessageReceived(msg); 210 } 211 } 212 213 214 status_t 215 ResView::SetTo(const entry_ref &dir, const BString &name) 216 { 217 entry_ref fileRef; 218 219 BPath path(&dir); 220 path.Append(name.String()); 221 BFile file(path.Path(), B_CREATE_FILE | B_READ_WRITE); 222 if (file.InitCheck() != B_OK) 223 return B_ERROR; 224 225 if (!fRef) 226 fRef = new entry_ref(); 227 228 BEntry entry(path.Path()); 229 entry.GetRef(fRef); 230 fFileName = name; 231 return B_OK; 232 } 233 234 235 void 236 ResView::OpenFile(const entry_ref &ref) 237 { 238 // Add all the 133t resources and attributes of the file 239 BFile file(&ref, B_READ_ONLY); 240 BResources resources; 241 if (resources.SetTo(&file) != B_OK) 242 return; 243 file.Unset(); 244 245 resources.PreloadResourceType(); 246 247 int32 index = 0; 248 ResDataRow *row; 249 ResourceData *resData = new ResourceData(); 250 while (resData->SetFromResource(index, resources)) { 251 row = new ResDataRow(resData); 252 fListView->AddRow(row); 253 fDataList.AddItem(resData); 254 resData = new ResourceData(); 255 index++; 256 } 257 delete resData; 258 259 BNode node; 260 if (node.SetTo(&ref) == B_OK) { 261 char attrName[B_ATTR_NAME_LENGTH]; 262 node.RewindAttrs(); 263 resData = new ResourceData(); 264 while (node.GetNextAttrName(attrName) == B_OK) { 265 if (resData->SetFromAttribute(attrName, node)) { 266 row = new ResDataRow(resData); 267 fListView->AddRow(row); 268 fDataList.AddItem(resData); 269 resData = new ResourceData(); 270 } 271 } 272 delete resData; 273 } 274 } 275 276 277 void 278 ResView::SaveFile(void) 279 { 280 if (fSaveStatus == FILE_CLEAN || !fRef) 281 return; 282 283 BFile file(fRef,B_READ_WRITE); 284 BResources res(&file,true); 285 file.Unset(); 286 287 for (int32 i = 0; i < fListView->CountRows(); i++) { 288 ResDataRow *row = (ResDataRow*)fListView->RowAt(i); 289 ResourceData *data = row->GetData(); 290 res.AddResource(data->GetType(), data->GetID(), data->GetData(), 291 data->GetLength(), data->GetName()); 292 } 293 294 res.Sync(); 295 296 if (fSaveStatus == FILE_QUIT_AFTER_SAVE && Window()) 297 Window()->PostMessage(B_QUIT_REQUESTED); 298 SetSaveStatus(FILE_CLEAN); 299 } 300 301 302 void 303 ResView::SaveAndQuit(void) 304 { 305 SetSaveStatus(FILE_QUIT_AFTER_SAVE); 306 if (!fRef) { 307 fSavePanel->Show(); 308 return; 309 } 310 311 SaveFile(); 312 } 313 314 315 void 316 ResView::BuildMenus(BMenuBar *menuBar) 317 { 318 BMenu *menu = new BMenu("File"); 319 menu->AddItem(new BMenuItem("New" B_UTF8_ELLIPSIS, new BMessage(M_NEW_FILE), 'N')); 320 menu->AddSeparatorItem(); 321 menu->AddItem(new BMenuItem("Open" B_UTF8_ELLIPSIS, new BMessage(M_OPEN_FILE), 'O')); 322 menu->AddSeparatorItem(); 323 menu->AddItem(new BMenuItem("Save", new BMessage(M_SAVE_FILE), 'S')); 324 menu->AddItem(new BMenuItem("Save As" B_UTF8_ELLIPSIS, new BMessage(M_SHOW_SAVE_PANEL), 'S', 325 B_COMMAND_KEY | B_SHIFT_KEY)); 326 menuBar->AddItem(menu); 327 328 menu = new BMenu("Resource"); 329 menu->AddItem(new BMenuItem("Add" B_UTF8_ELLIPSIS, new BMessage(M_SELECT_FILE), 'F')); 330 menu->AddItem(new BMenuItem("Delete", new BMessage(M_DELETE_RESOURCE), 'D')); 331 menuBar->AddItem(menu); 332 } 333 334 335 void 336 ResView::EmptyDataList(void) 337 { 338 for (int32 i = 0; i < fDataList.CountItems(); i++) { 339 ResourceData *data = (ResourceData*) fDataList.ItemAt(i); 340 delete data; 341 } 342 fDataList.MakeEmpty(); 343 } 344 345 346 void 347 ResView::UpdateRow(BRow *row) 348 { 349 TypeCodeField *typeField = (TypeCodeField*) row->GetField(1); 350 ResourceData *resData = typeField->GetResourceData(); 351 BStringField *strField = (BStringField *)row->GetField(0); 352 353 if (strcmp("(attr)", strField->String()) != 0) 354 strField->SetString(resData->GetIDString()); 355 356 strField = (BStringField *)row->GetField(2); 357 if (strField) 358 strField->SetString(resData->GetName()); 359 360 PreviewField *preField = (PreviewField*)row->GetField(3); 361 if (preField) 362 preField->SetData(resData->GetData(), resData->GetLength()); 363 364 BSizeField *sizeField = (BSizeField*)row->GetField(4); 365 if (sizeField) 366 sizeField->SetSize(resData->GetLength()); 367 } 368 369 370 void 371 ResView::AddResource(const entry_ref &ref) 372 { 373 BFile file(&ref, B_READ_ONLY); 374 if (file.InitCheck() != B_OK) 375 return; 376 377 BString mime; 378 file.ReadAttrString("BEOS:TYPE", &mime); 379 380 if (mime == "application/x-be-resource") { 381 BMessage msg(B_REFS_RECEIVED); 382 msg.AddRef("refs", &ref); 383 be_app->PostMessage(&msg); 384 return; 385 } 386 387 type_code fileType = 0; 388 389 BTranslatorRoster *roster = BTranslatorRoster::Default(); 390 translator_info info; 391 if (roster->Identify(&file, NULL, &info, 0, mime.String()) == B_OK) 392 fileType = info.type; 393 else 394 fileType = B_RAW_TYPE; 395 396 int32 lastID = -1; 397 for (int32 i = 0; i < fDataList.CountItems(); i++) { 398 ResourceData *resData = (ResourceData*)fDataList.ItemAt(i); 399 if (resData->GetType() == fileType && resData->GetID() > lastID) 400 lastID = resData->GetID(); 401 } 402 403 off_t fileSize; 404 file.GetSize(&fileSize); 405 406 if (fileSize < 1) 407 return; 408 409 char *fileData = (char *)malloc(fileSize); 410 file.Read(fileData, fileSize); 411 412 ResourceData *resData = new ResourceData(fileType, lastID + 1, ref.name, 413 fileData, fileSize); 414 fDataList.AddItem(resData); 415 416 ResDataRow *row = new ResDataRow(resData); 417 fListView->AddRow(row); 418 419 SetSaveStatus(FILE_DIRTY); 420 } 421 422 423 void 424 ResView::DeleteSelectedResources(void) 425 { 426 ResDataRow *selection = (ResDataRow*)fListView->CurrentSelection(); 427 if (!selection) 428 return; 429 430 SetSaveStatus(FILE_DIRTY); 431 432 while (selection) { 433 ResourceData *data = selection->GetData(); 434 fListView->RemoveRow(selection); 435 fDataList.RemoveItem(data); 436 delete data; 437 selection = (ResDataRow*)fListView->CurrentSelection(); 438 } 439 } 440 441 442 void 443 ResView::SetSaveStatus(uint8 value) 444 { 445 if (value == fSaveStatus) 446 return; 447 448 fSaveStatus = value; 449 450 BString title("ResEdit: "); 451 title << fFileName; 452 if (fSaveStatus == FILE_DIRTY) 453 title << "*"; 454 455 if (Window()) { 456 Window()->Lock(); 457 Window()->SetTitle(title.String()); 458 Window()->Unlock(); 459 } 460 } 461 462 463 ResDataRow::ResDataRow(ResourceData *data) 464 : fResData(data) 465 { 466 if (data) { 467 SetField(new BStringField(fResData->GetIDString()), 0); 468 SetField(new TypeCodeField(fResData->GetType(), fResData), 1); 469 SetField(new BStringField(fResData->GetName()), 2); 470 BField *field = gResRoster.MakeFieldForType(fResData->GetType(), 471 fResData->GetData(), 472 fResData->GetLength()); 473 if (field) 474 SetField(field, 3); 475 SetField(new BSizeField(fResData->GetLength()), 4); 476 } 477 } 478 479 480 ResourceData * 481 ResDataRow::GetData(void) const 482 { 483 return fResData; 484 } 485