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
ResView(const BRect & frame,const char * name,const int32 & resize,const int32 & flags,const entry_ref * ref)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
~ResView(void)97 ResView::~ResView(void)
98 {
99 EmptyDataList();
100 delete fRef;
101 delete fOpenPanel;
102 delete fSavePanel;
103 }
104
105
106 void
AttachedToWindow(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
MessageReceived(BMessage * msg)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
SetTo(const entry_ref & dir,const BString & name)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
OpenFile(const entry_ref & ref)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
SaveFile(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
SaveAndQuit(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
BuildMenus(BMenuBar * menuBar)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
EmptyDataList(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
UpdateRow(BRow * row)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
AddResource(const entry_ref & ref)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
DeleteSelectedResources(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
SetSaveStatus(uint8 value)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
ResDataRow(ResourceData * data)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 *
GetData(void) const481 ResDataRow::GetData(void) const
482 {
483 return fResData;
484 }
485