#include "ImageView.h"
#include "Constants.h"
#include "StatusCheck.h"
#include "InspectorApp.h"
#include "TranslatorItem.h"
#include <Application.h>
#include <Catalog.h>
#include <Message.h>
#include <Locale.h>
#include <List.h>
#include <String.h>
#include <TranslationUtils.h>
#include <TranslatorRoster.h>
#include <BitmapStream.h>
#include <Entry.h>
#include <Path.h>
#include <Directory.h>
#include <File.h>
#include <MenuBar.h>
#include <Screen.h>
#include <ScrollBar.h>
#include <Alert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define BORDER_WIDTH 16
#define BORDER_HEIGHT 16
#define PEN_SIZE 1.0f
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "ImageView"
ImageView::ImageView(BRect rect, const char *name)
: BView(rect, name, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS)
{
fpbitmap = NULL;
fdocumentIndex = 1;
fdocumentCount = 1;
SetViewColor(192, 192, 192);
SetHighColor(0, 0, 0);
SetPenSize(PEN_SIZE);
}
ImageView::~ImageView()
{
delete fpbitmap;
fpbitmap = NULL;
}
void
ImageView::AttachedToWindow()
{
AdjustScrollBars();
}
void
ImageView::Draw(BRect rect)
{
if (HasImage()) {
StrokeRect(
BRect(BORDER_WIDTH - PEN_SIZE,
BORDER_HEIGHT - PEN_SIZE,
fpbitmap->Bounds().Width() + BORDER_WIDTH + PEN_SIZE,
fpbitmap->Bounds().Height() + BORDER_HEIGHT + PEN_SIZE));
DrawBitmap(fpbitmap, BPoint(BORDER_WIDTH, BORDER_HEIGHT));
}
}
void
ImageView::ReDraw()
{
Draw(Bounds());
}
void
ImageView::FrameResized(float width, float height)
{
AdjustScrollBars();
if (!HasImage())
Invalidate();
}
void
ImageView::MouseDown(BPoint point)
{
if (!HasImage())
return;
BMessage *pmsg = Window()->CurrentMessage();
int32 button = pmsg->FindInt32("buttons");
if (button != B_PRIMARY_MOUSE_BUTTON)
return;
BMessage msg(B_SIMPLE_DATA);
msg.AddInt32("be:actions", B_COPY_TARGET);
msg.AddString("be:filetypes", "application/octet-stream");
msg.AddString("be:types", "application/octet-stream");
msg.AddString("be:clip_name", "Bitmap");
DragMessage(&msg, Bounds());
}
void
ImageView::MouseMoved(BPoint point, uint32 state, const BMessage *pmsg)
{
}
void
ImageView::MouseUp(BPoint point)
{
}
void
ImageView::MessageReceived(BMessage *pmsg)
{
switch (pmsg->what) {
case B_COPY_TARGET:
SaveImageAtDropLocation(pmsg);
break;
default:
BView::MessageReceived(pmsg);
break;
}
}
void
ImageView::SaveImageAtDropLocation(BMessage *pmsg)
{
BBitmapStream stream(fpbitmap);
StatusCheck chk;
try {
entry_ref dirref;
chk = pmsg->FindRef("directory", &dirref);
const char *filename;
chk = pmsg->FindString("name", &filename);
BDirectory dir(&dirref);
BFile file(&dir, filename, B_WRITE_ONLY | B_CREATE_FILE);
chk = file.InitCheck();
BTranslatorRoster *proster = BTranslatorRoster::Default();
chk = proster->Translate(&stream, NULL, NULL, &file, B_TGA_FORMAT);
} catch (StatusNotOKException) {
BAlert *palert = new BAlert(NULL,
B_TRANSLATE("Sorry, unable to write the image file."),
B_TRANSLATE("OK"));
palert->Go();
}
stream.DetachBitmap(&fpbitmap);
}
void
ImageView::AdjustScrollBars()
{
BRect rctview = Bounds(), rctbitmap(0, 0, 0, 0);
if (HasImage())
rctbitmap = fpbitmap->Bounds();
float prop, range;
BScrollBar *psb = ScrollBar(B_HORIZONTAL);
if (psb) {
range = rctbitmap.Width() + (BORDER_WIDTH * 2) - rctview.Width();
if (range < 0) range = 0;
prop = rctview.Width() / (rctbitmap.Width() + (BORDER_WIDTH * 2));
if (prop > 1.0f) prop = 1.0f;
psb->SetRange(0, range);
psb->SetProportion(prop);
psb->SetSteps(10, 100);
}
psb = ScrollBar(B_VERTICAL);
if (psb) {
range = rctbitmap.Height() + (BORDER_HEIGHT * 2) - rctview.Height();
if (range < 0) range = 0;
prop = rctview.Height() / (rctbitmap.Height() + (BORDER_HEIGHT * 2));
if (prop > 1.0f) prop = 1.0f;
psb->SetRange(0, range);
psb->SetProportion(prop);
psb->SetSteps(10, 100);
}
}
struct ColorSpaceName {
color_space id;
const char *name;
};
#define COLORSPACENAME(id) {id, #id}
const char *
get_color_space_name(color_space colors)
{
const ColorSpaceName kcolorspaces[] = {
COLORSPACENAME(B_NO_COLOR_SPACE),
COLORSPACENAME(B_RGB32),
COLORSPACENAME(B_RGBA32),
COLORSPACENAME(B_RGB24),
COLORSPACENAME(B_RGB16),
COLORSPACENAME(B_RGB15),
COLORSPACENAME(B_RGBA15),
COLORSPACENAME(B_CMAP8),
COLORSPACENAME(B_GRAY8),
COLORSPACENAME(B_GRAY1),
COLORSPACENAME(B_RGB32_BIG),
COLORSPACENAME(B_RGBA32_BIG),
COLORSPACENAME(B_RGB24_BIG),
COLORSPACENAME(B_RGB16_BIG),
COLORSPACENAME(B_RGB15_BIG),
COLORSPACENAME(B_RGBA15_BIG),
COLORSPACENAME(B_YCbCr422),
COLORSPACENAME(B_YCbCr411),
COLORSPACENAME(B_YCbCr444),
COLORSPACENAME(B_YCbCr420),
COLORSPACENAME(B_YUV422),
COLORSPACENAME(B_YUV411),
COLORSPACENAME(B_YUV444),
COLORSPACENAME(B_YUV420),
COLORSPACENAME(B_YUV9),
COLORSPACENAME(B_YUV12),
COLORSPACENAME(B_UVL24),
COLORSPACENAME(B_UVL32),
COLORSPACENAME(B_UVLA32),
COLORSPACENAME(B_LAB24),
COLORSPACENAME(B_LAB32),
COLORSPACENAME(B_LABA32),
COLORSPACENAME(B_HSI24),
COLORSPACENAME(B_HSI32),
COLORSPACENAME(B_HSIA32),
COLORSPACENAME(B_HSV24),
COLORSPACENAME(B_HSV32),
COLORSPACENAME(B_HSVA32),
COLORSPACENAME(B_HLS24),
COLORSPACENAME(B_HLS32),
COLORSPACENAME(B_HLSA32),
COLORSPACENAME(B_CMY24),
COLORSPACENAME(B_CMY32),
COLORSPACENAME(B_CMYA32),
COLORSPACENAME(B_CMYK32)
};
const int32 kncolorspaces = sizeof(kcolorspaces) /
sizeof(ColorSpaceName);
for (int32 i = 0; i < kncolorspaces; i++) {
if (colors == kcolorspaces[i].id)
return kcolorspaces[i].name;
}
return B_TRANSLATE("Unknown");
}
const char *
hex_format(uint32 num)
{
static char str[11] = { 0 };
sprintf(str, "0x%.8lx", num);
return str;
}
const char *
char_format(uint32 num)
{
static char str[5] = { 0 };
uint32 bnum = B_HOST_TO_BENDIAN_INT32(num);
memcpy(str, &bnum, 4);
return str;
}
void
dump_translation_formats(BString &bstr, const translation_format *pfmts,
int32 nfmts)
{
BString *str1 = NULL;
for (int i = 0; i < nfmts; i++) {
BString string = B_TRANSLATE("\nType: '%1' (%2)\n"
"Group: '%3' (%4)\n"
"Quality: %5\n"
"Capability: %6\n"
"MIME Type: %7\n"
"Name: %8\n");
string.ReplaceFirst("%1", char_format(pfmts[i].type));
string.ReplaceFirst("%2", hex_format(pfmts[i].type));
string.ReplaceFirst("%3", char_format(pfmts[i].group));
string.ReplaceFirst("%4", hex_format(pfmts[i].group));
char str2[127] = { 0 };
sprintf(str2, "%f", pfmts[i].quality);
string.ReplaceFirst("%5", str2 );
str2[0] = '\0';
sprintf(str2, "%f", pfmts[i].capability);
string.ReplaceFirst("%6", str2 );
string.ReplaceFirst("%7", pfmts[i].MIME);
string.ReplaceFirst("%8", pfmts[i].name);
if (i == 0)
str1 = new BString(string);
else
str1->Append(string);
}
bstr = str1->String();
}
void
ImageView::UpdateInfoWindow(const BPath &path, BMessage &ioExtension,
const translator_info &tinfo, BTranslatorRoster *proster)
{
BMessage msg(M_INFO_WINDOW_TEXT);
BString bstr;
bstr = B_TRANSLATE("Image: %1\n"
"Color Space: %2 (%3)\n"
"Dimensions: %4 x %5\n"
"Bytes per Row: %6\n"
"Total Bytes: %7\n"
"\nIdentify Info:\n"
"ID String: %8\n"
"MIME Type: %9\n"
"Type: '%10' (%11)\n"
"Translator ID: %12\n"
"Group: '%13' (%14)\n"
"Quality: %15\n"
"Capability: %16\n"
"\nExtension Info:\n");
bstr.ReplaceFirst("%1", path.Path());
color_space cs = fpbitmap->ColorSpace();
bstr.ReplaceFirst("%2", get_color_space_name(cs));
bstr.ReplaceFirst("%3", hex_format(static_cast<uint32>(cs)));
char str2[127] = { 0 };
sprintf(str2, "%ld", fpbitmap->Bounds().IntegerWidth() + 1);
bstr.ReplaceFirst("%4", str2);
str2[0] = '\0';
sprintf(str2, "%ld", fpbitmap->Bounds().IntegerHeight() + 1);
bstr.ReplaceFirst("%5", str2);
str2[0] = '\0';
sprintf(str2, "%ld", fpbitmap->BytesPerRow());
bstr.ReplaceFirst("%6", str2);
str2[0] = '\0';
sprintf(str2, "%ld", fpbitmap->BitsLength());
bstr.ReplaceFirst("%7", str2);
bstr.ReplaceFirst("%8", tinfo.name);
bstr.ReplaceFirst("%9", tinfo.MIME);
bstr.ReplaceFirst("%10", char_format(tinfo.type));
bstr.ReplaceFirst("%11", hex_format(tinfo.type));
str2[0] = '\0';
sprintf(str2, "%ld", tinfo.translator);
bstr.ReplaceFirst("%12", str2);
bstr.ReplaceFirst("%13", char_format(tinfo.group));
bstr.ReplaceFirst("%14", hex_format(tinfo.group));
str2[0] = '\0';
sprintf(str2, "%f", tinfo.quality);
bstr.ReplaceFirst("%15", str2);
str2[0] = '\0';
sprintf(str2, "%f", tinfo.capability);
bstr.ReplaceFirst("%16", str2);
int32 document_count = 0, document_index = 0;
const char *tranname = NULL, *traninfo = NULL;
int32 tranversion = 0;
if (ioExtension.FindInt32("/documentCount", &document_count) == B_OK) {
BString str = B_TRANSLATE("Number of Documents: %1\n"
"\nTranslator Used:\n"
"Name: %2\n"
"Info: %3\n"
"Version: %4\n");
char str2[127] = { 0 };
sprintf(str2, "%ld", document_count);
str.ReplaceFirst("%1", str2);
str.ReplaceFirst("%2", tranname);
str.ReplaceFirst("%3", traninfo);
str2[0] = '\0';
sprintf(str2, "%d", (int)tranversion);
str.ReplaceFirst("%4", str2);
bstr.Append(str.String());
}
else
if (ioExtension.FindInt32("/documentIndex", &document_index) == B_OK) {
BString str = B_TRANSLATE("Selected Document: %1\n"
"\nTranslator Used:\n"
"Name: %2\n"
"Info: %3\n"
"Version: %4\n");
char str2[127] = { 0 };
sprintf(str2, "%ld", document_index);
str.ReplaceFirst("%1", str2);
str.ReplaceFirst("%2", tranname);
str.ReplaceFirst("%3", traninfo);
str2[0] = '\0';
sprintf(str2, "%d", (int)tranversion);
str.ReplaceFirst("%4", str2);
bstr.Append(str.String());
}
else
if (proster->GetTranslatorInfo(tinfo.translator, &tranname,
&traninfo, &tranversion) == B_OK) {
BString str = B_TRANSLATE("\nTranslator Used:\n"
"Name: %1\n"
"Info: %2\n"
"Version: %3\n");
str.ReplaceFirst("%1", tranname);
str.ReplaceFirst("%2", traninfo);
char str2[127] = { 0 };
sprintf(str2, "%d", (int)tranversion);
str.ReplaceFirst("%3", str2);
bstr.Append(str.String());
}
int32 nins = 0, nouts = 0;
const translation_format *pins = NULL, *pouts = NULL;
if (proster->GetInputFormats(tinfo.translator, &pins, &nins) == B_OK) {
bstr << B_TRANSLATE("\nInput Formats:");
dump_translation_formats(bstr, pins, nins);
pins = NULL;
}
if (proster->GetOutputFormats(tinfo.translator, &pouts, &nouts) == B_OK) {
bstr << B_TRANSLATE("\nOutput Formats:");
dump_translation_formats(bstr, pouts, nouts);
pouts = NULL;
}
msg.AddString("text", bstr);
be_app->PostMessage(&msg);
}
BTranslatorRoster *
ImageView::SelectTranslatorRoster(BTranslatorRoster &roster)
{
bool bNoneSelected = true;
InspectorApp *papp;
papp = static_cast<InspectorApp *>(be_app);
if (papp) {
BList *plist = papp->GetTranslatorsList();
if (plist) {
for (int32 i = 0; i < plist->CountItems(); i++) {
BTranslatorItem *pitem =
static_cast<BTranslatorItem *>(plist->ItemAt(i));
if (pitem->IsSelected()) {
bNoneSelected = false;
roster.AddTranslators(pitem->Path());
}
}
}
}
if (bNoneSelected)
return BTranslatorRoster::Default();
else
return &roster;
}
void
ImageView::SetImage(BMessage *pmsg)
{
entry_ref ref;
if (!pmsg)
ref = fcurrentRef;
else if (pmsg->FindRef("refs", &ref) != B_OK)
return;
StatusCheck chk;
try {
BFile file(&ref, B_READ_ONLY);
chk = file.InitCheck();
BTranslatorRoster roster, *proster;
proster = SelectTranslatorRoster(roster);
if (!proster)
chk = B_ERROR;
translator_info tinfo;
BMessage ioExtension;
if (ref != fcurrentRef)
fdocumentIndex = 1;
chk = ioExtension.AddInt32("/documentIndex", fdocumentIndex);
chk = proster->Identify(&file, &ioExtension, &tinfo, 0, NULL,
B_TRANSLATOR_BITMAP);
BBitmapStream outstream;
chk = proster->Translate(&file, &tinfo, &ioExtension, &outstream,
B_TRANSLATOR_BITMAP);
BBitmap *pbitmap = NULL;
chk = outstream.DetachBitmap(&pbitmap);
delete fpbitmap;
fpbitmap = pbitmap;
pbitmap = NULL;
fcurrentRef = ref;
int32 documentCount = 0;
if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK &&
documentCount > 0)
fdocumentCount = documentCount;
else
fdocumentCount = 1;
BWindow *pwin = Window();
BEntry entry(&ref);
BPath path;
if (entry.InitCheck() == B_OK) {
if (path.SetTo(&entry) == B_OK)
pwin->SetTitle(path.Leaf());
else
pwin->SetTitle(IMAGEWINDOW_TITLE);
} else
pwin->SetTitle(IMAGEWINDOW_TITLE);
UpdateInfoWindow(path, ioExtension, tinfo, proster);
float width, height;
BMenuBar *pbar = pwin->KeyMenuBar();
width = fpbitmap->Bounds().Width() + B_V_SCROLL_BAR_WIDTH + (BORDER_WIDTH * 2);
height = fpbitmap->Bounds().Height() +
pbar->Bounds().Height() + B_H_SCROLL_BAR_HEIGHT + (BORDER_HEIGHT * 2) + 1;
BScreen *pscreen = new BScreen(pwin);
BRect rctscreen = pscreen->Frame();
if (width > rctscreen.Width())
width = rctscreen.Width();
if (height > rctscreen.Height())
height = rctscreen.Height();
pwin->SetSizeLimits(B_V_SCROLL_BAR_WIDTH * 4, width,
pbar->Bounds().Height() + (B_H_SCROLL_BAR_HEIGHT * 4) + 1, height);
pwin->SetZoomLimits(width, height);
AdjustScrollBars();
Invalidate();
} catch (StatusNotOKException) {
BAlert *palert = new BAlert(NULL,
B_TRANSLATE("Sorry, unable to load the image."),
B_TRANSLATE("OK"));
palert->Go();
}
}
void
ImageView::FirstPage()
{
if (fdocumentIndex != 1) {
fdocumentIndex = 1;
SetImage(NULL);
}
}
void
ImageView::LastPage()
{
if (fdocumentIndex != fdocumentCount) {
fdocumentIndex = fdocumentCount;
SetImage(NULL);
}
}
void
ImageView::NextPage()
{
if (fdocumentIndex < fdocumentCount) {
fdocumentIndex++;
SetImage(NULL);
}
}
void
ImageView::PrevPage()
{
if (fdocumentIndex > 1) {
fdocumentIndex--;
SetImage(NULL);
}
}