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