haiku/src/apps/resedit/ResView.cpp

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;
}