vfs: check userland buffers before reading them.
[haiku.git] / src / apps / resedit / ResView.cpp
blobc1152c86bf42daa263392aa94d48b3314435ee29
1 /*
2 * Copyright (c) 2005-2010, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
5 * Author:
6 * DarkWyrm <darkwyrm@gmail.com>
7 */
8 #include "ResView.h"
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>
19 #include <stdio.h>
20 #include <stdlib.h>
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"
31 static int32 sUntitled = 1;
33 ResourceRoster gResRoster;
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
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)
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++;
64 BRect r(Bounds());
65 r.bottom = 16;
66 fBar = new BMenuBar(r, "bar");
67 AddChild(fBar);
69 BuildMenus(fBar);
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);
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));
80 float width = be_plain_font->StringWidth("00000") + 20;
81 fListView->AddColumn(new BStringColumn("ID", width, width, 100, B_TRUNCATE_END), 0);
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);
87 // Editing is disabled for now
88 fListView->SetInvocationMessage(new BMessage(M_EDIT_RESOURCE));
90 width = be_plain_font->StringWidth("1000 bytes") + 20;
91 fListView->AddColumn(new BSizeColumn("Size", width, 10, 100), 4);
93 fOpenPanel = new BFilePanel(B_OPEN_PANEL);
94 if (ref)
95 OpenFile(*ref);
97 fSavePanel = new BFilePanel(B_SAVE_PANEL);
101 ResView::~ResView(void)
103 EmptyDataList();
104 delete fRef;
105 delete fOpenPanel;
106 delete fSavePanel;
110 void
111 ResView::AttachedToWindow(void)
113 for (int32 i = 0; i < fBar->CountItems(); i++)
114 fBar->SubmenuAt(i)->SetTargetForItems(this);
115 fListView->SetTarget(this);
117 BMessenger messenger(this);
118 fOpenPanel->SetTarget(messenger);
119 fSavePanel->SetTarget(messenger);
121 Window()->Lock();
122 BString title("ResEdit: ");
123 title << fFileName;
124 Window()->SetTitle(title.String());
125 Window()->Unlock();
129 void
130 ResView::MessageReceived(BMessage *msg)
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;
141 case M_OPEN_FILE: {
142 be_app->PostMessage(M_SHOW_OPEN_PANEL);
143 break;
145 case B_CANCEL: {
146 if (fSaveStatus == FILE_QUIT_AFTER_SAVE)
147 SetSaveStatus(FILE_DIRTY);
148 break;
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();
158 break;
160 case M_SAVE_FILE: {
161 if (!fRef)
162 fSavePanel->Show();
163 else
164 SaveFile();
165 break;
167 case M_SHOW_SAVE_PANEL: {
168 fSavePanel->Show();
169 break;
171 case M_QUIT: {
172 be_app->PostMessage(B_QUIT_REQUESTED);
173 break;
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;
182 case M_SELECT_FILE: {
183 fOpenPanel->Show();
184 break;
186 case M_DELETE_RESOURCE: {
187 DeleteSelectedResources();
188 break;
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;
196 case M_UPDATE_RESOURCE: {
197 ResourceData *item;
198 if (msg->FindPointer("item", (void **)&item) != B_OK)
199 break;
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;
207 UpdateRow(row);
208 break;
210 break;
212 default:
213 BView::MessageReceived(msg);
218 status_t
219 ResView::SetTo(const entry_ref &dir, const BString &name)
221 entry_ref fileRef;
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;
229 if (!fRef)
230 fRef = new entry_ref();
232 BEntry entry(path.Path());
233 entry.GetRef(fRef);
234 fFileName = name;
235 return B_OK;
239 void
240 ResView::OpenFile(const entry_ref &ref)
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();
249 resources.PreloadResourceType();
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++;
261 delete resData;
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();
276 delete resData;
281 void
282 ResView::SaveFile(void)
284 if (fSaveStatus == FILE_CLEAN || !fRef)
285 return;
287 BFile file(fRef,B_READ_WRITE);
288 BResources res(&file,true);
289 file.Unset();
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());
298 res.Sync();
300 if (fSaveStatus == FILE_QUIT_AFTER_SAVE && Window())
301 Window()->PostMessage(B_QUIT_REQUESTED);
302 SetSaveStatus(FILE_CLEAN);
306 void
307 ResView::SaveAndQuit(void)
309 SetSaveStatus(FILE_QUIT_AFTER_SAVE);
310 if (!fRef) {
311 fSavePanel->Show();
312 return;
315 SaveFile();
319 void
320 ResView::BuildMenus(BMenuBar *menuBar)
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);
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);
339 void
340 ResView::EmptyDataList(void)
342 for (int32 i = 0; i < fDataList.CountItems(); i++) {
343 ResourceData *data = (ResourceData*) fDataList.ItemAt(i);
344 delete data;
346 fDataList.MakeEmpty();
350 void
351 ResView::UpdateRow(BRow *row)
353 TypeCodeField *typeField = (TypeCodeField*) row->GetField(1);
354 ResourceData *resData = typeField->GetResourceData();
355 BStringField *strField = (BStringField *)row->GetField(0);
357 if (strcmp("(attr)", strField->String()) != 0)
358 strField->SetString(resData->GetIDString());
360 strField = (BStringField *)row->GetField(2);
361 if (strField)
362 strField->SetString(resData->GetName());
364 PreviewField *preField = (PreviewField*)row->GetField(3);
365 if (preField)
366 preField->SetData(resData->GetData(), resData->GetLength());
368 BSizeField *sizeField = (BSizeField*)row->GetField(4);
369 if (sizeField)
370 sizeField->SetSize(resData->GetLength());
374 void
375 ResView::AddResource(const entry_ref &ref)
377 BFile file(&ref, B_READ_ONLY);
378 if (file.InitCheck() != B_OK)
379 return;
381 BString mime;
382 file.ReadAttrString("BEOS:TYPE", &mime);
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;
391 type_code fileType = 0;
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;
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();
407 off_t fileSize;
408 file.GetSize(&fileSize);
410 if (fileSize < 1)
411 return;
413 char *fileData = (char *)malloc(fileSize);
414 file.Read(fileData, fileSize);
416 ResourceData *resData = new ResourceData(fileType, lastID + 1, ref.name,
417 fileData, fileSize);
418 fDataList.AddItem(resData);
420 ResDataRow *row = new ResDataRow(resData);
421 fListView->AddRow(row);
423 SetSaveStatus(FILE_DIRTY);
427 void
428 ResView::DeleteSelectedResources(void)
430 ResDataRow *selection = (ResDataRow*)fListView->CurrentSelection();
431 if (!selection)
432 return;
434 SetSaveStatus(FILE_DIRTY);
436 while (selection) {
437 ResourceData *data = selection->GetData();
438 fListView->RemoveRow(selection);
439 fDataList.RemoveItem(data);
440 delete data;
441 selection = (ResDataRow*)fListView->CurrentSelection();
446 void
447 ResView::SetSaveStatus(uint8 value)
449 if (value == fSaveStatus)
450 return;
452 fSaveStatus = value;
454 BString title("ResEdit: ");
455 title << fFileName;
456 if (fSaveStatus == FILE_DIRTY)
457 title << "*";
459 if (Window()) {
460 Window()->Lock();
461 Window()->SetTitle(title.String());
462 Window()->Unlock();
467 ResDataRow::ResDataRow(ResourceData *data)
468 : fResData(data)
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);
484 ResourceData *
485 ResDataRow::GetData(void) const
487 return fResData;