/*****************************************************************************/ // ImageView // Written by Michael Wilber, OBOS Translation Kit Team // // ImageView.cpp // // BView class for showing images. Images can be dropped on this view // from the tracker and images viewed in this view can be dragged // to the tracker to be saved. // // // Copyright (c) 2003 OpenBeOS Project // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. /*****************************************************************************/ #include "ImageView.h" #include "Constants.h" #include "StatusCheck.h" #include "InspectorApp.h" #include "TranslatorItem.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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()) { // Draw black rectangle around image 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; // Only accept left button clicks BMessage *pmsg = Window()->CurrentMessage(); int32 button = pmsg->FindInt32("buttons"); if (button != B_PRIMARY_MOUSE_BUTTON) return; // Tell BeOS to setup a Drag/Drop operation // // (When the image is dropped, BeOS sends // the following message to ImageWindow, // which causes it to call ImageView::SetImage()) 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) { // Find the location and name of the drop and // write the image file there BBitmapStream stream(fpbitmap); StatusCheck chk; // throw an exception if this is assigned // anything other than B_OK 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} // convert colorspace numerical value to // a string value const char * get_color_space_name(color_space colors) { // print out colorspace if it matches an item in the list 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"); } // return a string of the passed number formated // as a hexadecimal number in lowercase with a leading "0x" const char * hex_format(uint32 num) { static char str[11] = { 0 }; sprintf(str, "0x%.8lx", num); return str; } // convert passed number to a string of 4 characters // and return that string 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(); } // Send information about the currently open image to the // BApplication object so it can send it to the InfoWindow 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(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; // Translator Info 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()); } // Translator Input / Output Formats 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(be_app); if (papp) { BList *plist = papp->GetTranslatorsList(); if (plist) { for (int32 i = 0; i < plist->CountItems(); i++) { BTranslatorItem *pitem = static_cast(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) { // Replace current image with the image // specified in the given BMessage entry_ref ref; if (!pmsg) ref = fcurrentRef; else if (pmsg->FindRef("refs", &ref) != B_OK) // If refs not found, just ignore the message return; StatusCheck chk; try { BFile file(&ref, B_READ_ONLY); chk = file.InitCheck(); BTranslatorRoster roster, *proster; proster = SelectTranslatorRoster(roster); if (!proster) // throw exception chk = B_ERROR; // determine what type the image is translator_info tinfo; BMessage ioExtension; if (ref != fcurrentRef) // if new image, reset to first document fdocumentIndex = 1; chk = ioExtension.AddInt32("/documentIndex", fdocumentIndex); chk = proster->Identify(&file, &ioExtension, &tinfo, 0, NULL, B_TRANSLATOR_BITMAP); // perform the actual translation 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; // need to keep the ref around if user wants to switch pages int32 documentCount = 0; if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK && documentCount > 0) fdocumentCount = documentCount; else fdocumentCount = 1; // Set the name of the Window to reflect the file name 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); // Resize parent window and set size limits to // reflect the size of the new bitmap 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(); //pwin->Zoom(); // Perform all of the hard work of resizing the // window while taking into account the size of // the screen, the tab and borders of the window // // HACK: Need to fix case where window un-zooms // when the window is already the correct size // for the current image // repaint view 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); } }