489 lines
11 KiB
C++
489 lines
11 KiB
C++
/*
|
|
* Copyright (c) 2005-2010, Haiku, Inc.
|
|
* Distributed under the terms of the MIT license.
|
|
*
|
|
* Author:
|
|
* DarkWyrm <darkwyrm@gmail.com>
|
|
*/
|
|
#include "ResView.h"
|
|
|
|
#include <Application.h>
|
|
#include <File.h>
|
|
#include <Menu.h>
|
|
#include <MenuItem.h>
|
|
#include <Path.h>
|
|
#include <ScrollView.h>
|
|
#include <TranslatorRoster.h>
|
|
#include <TypeConstants.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "App.h"
|
|
#include "ColumnTypes.h"
|
|
#include "ResourceData.h"
|
|
#include "ResFields.h"
|
|
#include "ResListView.h"
|
|
#include "ResWindow.h"
|
|
#include "PreviewColumn.h"
|
|
#include "Editor.h"
|
|
|
|
static int32 sUntitled = 1;
|
|
|
|
ResourceRoster gResRoster;
|
|
|
|
enum {
|
|
M_NEW_FILE = 'nwfl',
|
|
M_OPEN_FILE,
|
|
M_SAVE_FILE,
|
|
M_QUIT,
|
|
M_SELECT_FILE,
|
|
M_DELETE_RESOURCE,
|
|
M_EDIT_RESOURCE
|
|
};
|
|
|
|
ResView::ResView(const BRect &frame, const char *name, const int32 &resize,
|
|
const int32 &flags, const entry_ref *ref)
|
|
: BView(frame, name, resize, flags),
|
|
fRef(NULL),
|
|
fSaveStatus(FILE_INIT),
|
|
fOpenPanel(NULL),
|
|
fSavePanel(NULL)
|
|
{
|
|
SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
|
|
if (ref) {
|
|
fRef = new entry_ref;
|
|
*fRef = *ref;
|
|
fFileName = fRef->name;
|
|
} else {
|
|
fFileName = "Untitled ";
|
|
fFileName << sUntitled;
|
|
sUntitled++;
|
|
}
|
|
|
|
BRect r(Bounds());
|
|
r.bottom = 16;
|
|
fBar = new BMenuBar(r, "bar");
|
|
AddChild(fBar);
|
|
|
|
BuildMenus(fBar);
|
|
|
|
r = Bounds();
|
|
r.top = fBar->Frame().bottom + 4;
|
|
fListView = new ResListView(r, "gridview", B_FOLLOW_ALL, B_WILL_DRAW, B_FANCY_BORDER);
|
|
AddChild(fListView);
|
|
|
|
rgb_color white = { 255, 255, 255, 255 };
|
|
fListView->SetColor(B_COLOR_BACKGROUND, white);
|
|
fListView->SetColor(B_COLOR_SELECTION, ui_color(B_MENU_BACKGROUND_COLOR));
|
|
|
|
float width = be_plain_font->StringWidth("00000") + 20;
|
|
fListView->AddColumn(new BStringColumn("ID", width, width, 100, B_TRUNCATE_END), 0);
|
|
|
|
fListView->AddColumn(new BStringColumn("Type", width, width, 100, B_TRUNCATE_END), 1);
|
|
fListView->AddColumn(new BStringColumn("Name", 150, 50, 300, B_TRUNCATE_END), 2);
|
|
fListView->AddColumn(new PreviewColumn("Data", 150, 50, 300), 3);
|
|
|
|
// Editing is disabled for now
|
|
fListView->SetInvocationMessage(new BMessage(M_EDIT_RESOURCE));
|
|
|
|
width = be_plain_font->StringWidth("1000 bytes") + 20;
|
|
fListView->AddColumn(new BSizeColumn("Size", width, 10, 100), 4);
|
|
|
|
fOpenPanel = new BFilePanel(B_OPEN_PANEL);
|
|
if (ref)
|
|
OpenFile(*ref);
|
|
|
|
fSavePanel = new BFilePanel(B_SAVE_PANEL);
|
|
}
|
|
|
|
|
|
ResView::~ResView(void)
|
|
{
|
|
EmptyDataList();
|
|
delete fRef;
|
|
delete fOpenPanel;
|
|
delete fSavePanel;
|
|
}
|
|
|
|
|
|
void
|
|
ResView::AttachedToWindow(void)
|
|
{
|
|
for (int32 i = 0; i < fBar->CountItems(); i++)
|
|
fBar->SubmenuAt(i)->SetTargetForItems(this);
|
|
fListView->SetTarget(this);
|
|
|
|
BMessenger messenger(this);
|
|
fOpenPanel->SetTarget(messenger);
|
|
fSavePanel->SetTarget(messenger);
|
|
|
|
Window()->Lock();
|
|
BString title("ResEdit: ");
|
|
title << fFileName;
|
|
Window()->SetTitle(title.String());
|
|
Window()->Unlock();
|
|
}
|
|
|
|
|
|
void
|
|
ResView::MessageReceived(BMessage *msg)
|
|
{
|
|
switch (msg->what) {
|
|
case M_NEW_FILE: {
|
|
BRect r(100, 100, 400, 400);
|
|
if (Window())
|
|
r = Window()->Frame().OffsetByCopy(10, 10);
|
|
ResWindow *win = new ResWindow(r);
|
|
win->Show();
|
|
break;
|
|
}
|
|
case M_OPEN_FILE: {
|
|
be_app->PostMessage(M_SHOW_OPEN_PANEL);
|
|
break;
|
|
}
|
|
case B_CANCEL: {
|
|
if (fSaveStatus == FILE_QUIT_AFTER_SAVE)
|
|
SetSaveStatus(FILE_DIRTY);
|
|
break;
|
|
}
|
|
case B_SAVE_REQUESTED: {
|
|
entry_ref saveDir;
|
|
BString name;
|
|
if (msg->FindRef("directory",&saveDir) == B_OK &&
|
|
msg->FindString("name",&name) == B_OK) {
|
|
SetTo(saveDir,name);
|
|
SaveFile();
|
|
}
|
|
break;
|
|
}
|
|
case M_SAVE_FILE: {
|
|
if (!fRef)
|
|
fSavePanel->Show();
|
|
else
|
|
SaveFile();
|
|
break;
|
|
}
|
|
case M_SHOW_SAVE_PANEL: {
|
|
fSavePanel->Show();
|
|
break;
|
|
}
|
|
case M_QUIT: {
|
|
be_app->PostMessage(B_QUIT_REQUESTED);
|
|
break;
|
|
}
|
|
case B_REFS_RECEIVED: {
|
|
int32 i = 0;
|
|
entry_ref ref;
|
|
while (msg->FindRef("refs", i++, &ref) == B_OK)
|
|
AddResource(ref);
|
|
break;
|
|
}
|
|
case M_SELECT_FILE: {
|
|
fOpenPanel->Show();
|
|
break;
|
|
}
|
|
case M_DELETE_RESOURCE: {
|
|
DeleteSelectedResources();
|
|
break;
|
|
}
|
|
case M_EDIT_RESOURCE: {
|
|
BRow *row = fListView->CurrentSelection();
|
|
TypeCodeField *field = (TypeCodeField*)row->GetField(1);
|
|
gResRoster.SpawnEditor(field->GetResourceData(), this);
|
|
break;
|
|
}
|
|
case M_UPDATE_RESOURCE: {
|
|
ResourceData *item;
|
|
if (msg->FindPointer("item", (void **)&item) != B_OK)
|
|
break;
|
|
|
|
for (int32 i = 0; i < fListView->CountRows(); i++) {
|
|
BRow *row = fListView->RowAt(i);
|
|
TypeCodeField *field = (TypeCodeField*)row->GetField(1);
|
|
if (!field || field->GetResourceData() != item)
|
|
continue;
|
|
|
|
UpdateRow(row);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
BView::MessageReceived(msg);
|
|
}
|
|
}
|
|
|
|
|
|
status_t
|
|
ResView::SetTo(const entry_ref &dir, const BString &name)
|
|
{
|
|
entry_ref fileRef;
|
|
|
|
BPath path(&dir);
|
|
path.Append(name.String());
|
|
BFile file(path.Path(), B_CREATE_FILE | B_READ_WRITE);
|
|
if (file.InitCheck() != B_OK)
|
|
return B_ERROR;
|
|
|
|
if (!fRef)
|
|
fRef = new entry_ref();
|
|
|
|
BEntry entry(path.Path());
|
|
entry.GetRef(fRef);
|
|
fFileName = name;
|
|
return B_OK;
|
|
}
|
|
|
|
|
|
void
|
|
ResView::OpenFile(const entry_ref &ref)
|
|
{
|
|
// Add all the 133t resources and attributes of the file
|
|
BFile file(&ref, B_READ_ONLY);
|
|
BResources resources;
|
|
if (resources.SetTo(&file) != B_OK)
|
|
return;
|
|
file.Unset();
|
|
|
|
resources.PreloadResourceType();
|
|
|
|
int32 index = 0;
|
|
ResDataRow *row;
|
|
ResourceData *resData = new ResourceData();
|
|
while (resData->SetFromResource(index, resources)) {
|
|
row = new ResDataRow(resData);
|
|
fListView->AddRow(row);
|
|
fDataList.AddItem(resData);
|
|
resData = new ResourceData();
|
|
index++;
|
|
}
|
|
delete resData;
|
|
|
|
BNode node;
|
|
if (node.SetTo(&ref) == B_OK) {
|
|
char attrName[B_ATTR_NAME_LENGTH];
|
|
node.RewindAttrs();
|
|
resData = new ResourceData();
|
|
while (node.GetNextAttrName(attrName) == B_OK) {
|
|
if (resData->SetFromAttribute(attrName, node)) {
|
|
row = new ResDataRow(resData);
|
|
fListView->AddRow(row);
|
|
fDataList.AddItem(resData);
|
|
resData = new ResourceData();
|
|
}
|
|
}
|
|
delete resData;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ResView::SaveFile(void)
|
|
{
|
|
if (fSaveStatus == FILE_CLEAN || !fRef)
|
|
return;
|
|
|
|
BFile file(fRef,B_READ_WRITE);
|
|
BResources res(&file,true);
|
|
file.Unset();
|
|
|
|
for (int32 i = 0; i < fListView->CountRows(); i++) {
|
|
ResDataRow *row = (ResDataRow*)fListView->RowAt(i);
|
|
ResourceData *data = row->GetData();
|
|
res.AddResource(data->GetType(), data->GetID(), data->GetData(),
|
|
data->GetLength(), data->GetName());
|
|
}
|
|
|
|
res.Sync();
|
|
|
|
if (fSaveStatus == FILE_QUIT_AFTER_SAVE && Window())
|
|
Window()->PostMessage(B_QUIT_REQUESTED);
|
|
SetSaveStatus(FILE_CLEAN);
|
|
}
|
|
|
|
|
|
void
|
|
ResView::SaveAndQuit(void)
|
|
{
|
|
SetSaveStatus(FILE_QUIT_AFTER_SAVE);
|
|
if (!fRef) {
|
|
fSavePanel->Show();
|
|
return;
|
|
}
|
|
|
|
SaveFile();
|
|
}
|
|
|
|
|
|
void
|
|
ResView::BuildMenus(BMenuBar *menuBar)
|
|
{
|
|
BMenu *menu = new BMenu("File");
|
|
menu->AddItem(new BMenuItem("New" B_UTF8_ELLIPSIS, new BMessage(M_NEW_FILE), 'N'));
|
|
menu->AddSeparatorItem();
|
|
menu->AddItem(new BMenuItem("Open" B_UTF8_ELLIPSIS, new BMessage(M_OPEN_FILE), 'O'));
|
|
menu->AddSeparatorItem();
|
|
menu->AddItem(new BMenuItem("Save", new BMessage(M_SAVE_FILE), 'S'));
|
|
menu->AddItem(new BMenuItem("Save As" B_UTF8_ELLIPSIS, new BMessage(M_SHOW_SAVE_PANEL), 'S',
|
|
B_COMMAND_KEY | B_SHIFT_KEY));
|
|
menuBar->AddItem(menu);
|
|
|
|
menu = new BMenu("Resource");
|
|
menu->AddItem(new BMenuItem("Add" B_UTF8_ELLIPSIS, new BMessage(M_SELECT_FILE), 'F'));
|
|
menu->AddItem(new BMenuItem("Delete", new BMessage(M_DELETE_RESOURCE), 'D'));
|
|
menuBar->AddItem(menu);
|
|
}
|
|
|
|
|
|
void
|
|
ResView::EmptyDataList(void)
|
|
{
|
|
for (int32 i = 0; i < fDataList.CountItems(); i++) {
|
|
ResourceData *data = (ResourceData*) fDataList.ItemAt(i);
|
|
delete data;
|
|
}
|
|
fDataList.MakeEmpty();
|
|
}
|
|
|
|
|
|
void
|
|
ResView::UpdateRow(BRow *row)
|
|
{
|
|
TypeCodeField *typeField = (TypeCodeField*) row->GetField(1);
|
|
ResourceData *resData = typeField->GetResourceData();
|
|
BStringField *strField = (BStringField *)row->GetField(0);
|
|
|
|
if (strcmp("(attr)", strField->String()) != 0)
|
|
strField->SetString(resData->GetIDString());
|
|
|
|
strField = (BStringField *)row->GetField(2);
|
|
if (strField)
|
|
strField->SetString(resData->GetName());
|
|
|
|
PreviewField *preField = (PreviewField*)row->GetField(3);
|
|
if (preField)
|
|
preField->SetData(resData->GetData(), resData->GetLength());
|
|
|
|
BSizeField *sizeField = (BSizeField*)row->GetField(4);
|
|
if (sizeField)
|
|
sizeField->SetSize(resData->GetLength());
|
|
}
|
|
|
|
|
|
void
|
|
ResView::AddResource(const entry_ref &ref)
|
|
{
|
|
BFile file(&ref, B_READ_ONLY);
|
|
if (file.InitCheck() != B_OK)
|
|
return;
|
|
|
|
BString mime;
|
|
file.ReadAttrString("BEOS:TYPE", &mime);
|
|
|
|
if (mime == "application/x-be-resource") {
|
|
BMessage msg(B_REFS_RECEIVED);
|
|
msg.AddRef("refs", &ref);
|
|
be_app->PostMessage(&msg);
|
|
return;
|
|
}
|
|
|
|
type_code fileType = 0;
|
|
|
|
BTranslatorRoster *roster = BTranslatorRoster::Default();
|
|
translator_info info;
|
|
if (roster->Identify(&file, NULL, &info, 0, mime.String()) == B_OK)
|
|
fileType = info.type;
|
|
else
|
|
fileType = B_RAW_TYPE;
|
|
|
|
int32 lastID = -1;
|
|
for (int32 i = 0; i < fDataList.CountItems(); i++) {
|
|
ResourceData *resData = (ResourceData*)fDataList.ItemAt(i);
|
|
if (resData->GetType() == fileType && resData->GetID() > lastID)
|
|
lastID = resData->GetID();
|
|
}
|
|
|
|
off_t fileSize;
|
|
file.GetSize(&fileSize);
|
|
|
|
if (fileSize < 1)
|
|
return;
|
|
|
|
char *fileData = (char *)malloc(fileSize);
|
|
file.Read(fileData, fileSize);
|
|
|
|
ResourceData *resData = new ResourceData(fileType, lastID + 1, ref.name,
|
|
fileData, fileSize);
|
|
fDataList.AddItem(resData);
|
|
|
|
ResDataRow *row = new ResDataRow(resData);
|
|
fListView->AddRow(row);
|
|
|
|
SetSaveStatus(FILE_DIRTY);
|
|
}
|
|
|
|
|
|
void
|
|
ResView::DeleteSelectedResources(void)
|
|
{
|
|
ResDataRow *selection = (ResDataRow*)fListView->CurrentSelection();
|
|
if (!selection)
|
|
return;
|
|
|
|
SetSaveStatus(FILE_DIRTY);
|
|
|
|
while (selection) {
|
|
ResourceData *data = selection->GetData();
|
|
fListView->RemoveRow(selection);
|
|
fDataList.RemoveItem(data);
|
|
delete data;
|
|
selection = (ResDataRow*)fListView->CurrentSelection();
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
ResView::SetSaveStatus(uint8 value)
|
|
{
|
|
if (value == fSaveStatus)
|
|
return;
|
|
|
|
fSaveStatus = value;
|
|
|
|
BString title("ResEdit: ");
|
|
title << fFileName;
|
|
if (fSaveStatus == FILE_DIRTY)
|
|
title << "*";
|
|
|
|
if (Window()) {
|
|
Window()->Lock();
|
|
Window()->SetTitle(title.String());
|
|
Window()->Unlock();
|
|
}
|
|
}
|
|
|
|
|
|
ResDataRow::ResDataRow(ResourceData *data)
|
|
: fResData(data)
|
|
{
|
|
if (data) {
|
|
SetField(new BStringField(fResData->GetIDString()), 0);
|
|
SetField(new TypeCodeField(fResData->GetType(), fResData), 1);
|
|
SetField(new BStringField(fResData->GetName()), 2);
|
|
BField *field = gResRoster.MakeFieldForType(fResData->GetType(),
|
|
fResData->GetData(),
|
|
fResData->GetLength());
|
|
if (field)
|
|
SetField(field, 3);
|
|
SetField(new BSizeField(fResData->GetLength()), 4);
|
|
}
|
|
}
|
|
|
|
|
|
ResourceData *
|
|
ResDataRow::GetData(void) const
|
|
{
|
|
return fResData;
|
|
}
|