1 /*****************************************************************************/ 2 // ImageView 3 // Written by Michael Wilber, OBOS Translation Kit Team 4 // 5 // ImageView.cpp 6 // 7 // BView class for showing images. Images can be dropped on this view 8 // from the tracker and images viewed in this view can be dragged 9 // to the tracker to be saved. 10 // 11 // 12 // Copyright (c) 2003 OpenBeOS Project 13 // 14 // Permission is hereby granted, free of charge, to any person obtaining a 15 // copy of this software and associated documentation files (the "Software"), 16 // to deal in the Software without restriction, including without limitation 17 // the rights to use, copy, modify, merge, publish, distribute, sublicense, 18 // and/or sell copies of the Software, and to permit persons to whom the 19 // Software is furnished to do so, subject to the following conditions: 20 // 21 // The above copyright notice and this permission notice shall be included 22 // in all copies or substantial portions of the Software. 23 // 24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 25 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 27 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 29 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 30 // DEALINGS IN THE SOFTWARE. 31 /*****************************************************************************/ 32 33 34 #include "ImageView.h" 35 #include "Constants.h" 36 #include "StatusCheck.h" 37 #include "InspectorApp.h" 38 #include "TranslatorItem.h" 39 #include <Application.h> 40 #include <Message.h> 41 #include <List.h> 42 #include <String.h> 43 #include <TranslationUtils.h> 44 #include <TranslatorRoster.h> 45 #include <BitmapStream.h> 46 #include <Entry.h> 47 #include <Path.h> 48 #include <Directory.h> 49 #include <File.h> 50 #include <MenuBar.h> 51 #include <Screen.h> 52 #include <ScrollBar.h> 53 #include <Alert.h> 54 #include <stdlib.h> 55 #include <stdio.h> 56 #include <string.h> 57 58 #define BORDER_WIDTH 16 59 #define BORDER_HEIGHT 16 60 #define PEN_SIZE 1.0f 61 62 ImageView::ImageView(BRect rect, const char *name) 63 : BView(rect, name, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS) 64 { 65 fpbitmap = NULL; 66 fdocumentIndex = 1; 67 fdocumentCount = 1; 68 69 SetViewColor(192, 192, 192); 70 SetHighColor(0, 0, 0); 71 SetPenSize(PEN_SIZE); 72 } 73 74 ImageView::~ImageView() 75 { 76 delete fpbitmap; 77 fpbitmap = NULL; 78 } 79 80 void 81 ImageView::AttachedToWindow() 82 { 83 AdjustScrollBars(); 84 } 85 86 void 87 ImageView::Draw(BRect rect) 88 { 89 if (HasImage()) { 90 // Draw black rectangle around image 91 StrokeRect( 92 BRect(BORDER_WIDTH - PEN_SIZE, 93 BORDER_HEIGHT - PEN_SIZE, 94 fpbitmap->Bounds().Width() + BORDER_WIDTH + PEN_SIZE, 95 fpbitmap->Bounds().Height() + BORDER_HEIGHT + PEN_SIZE)); 96 97 DrawBitmap(fpbitmap, BPoint(BORDER_WIDTH, BORDER_HEIGHT)); 98 } 99 } 100 101 void 102 ImageView::ReDraw() 103 { 104 Draw(Bounds()); 105 } 106 107 void 108 ImageView::FrameResized(float width, float height) 109 { 110 AdjustScrollBars(); 111 112 if (!HasImage()) 113 Invalidate(); 114 } 115 116 void 117 ImageView::MouseDown(BPoint point) 118 { 119 if (!HasImage()) 120 return; 121 122 // Only accept left button clicks 123 BMessage *pmsg = Window()->CurrentMessage(); 124 int32 button = pmsg->FindInt32("buttons"); 125 if (button != B_PRIMARY_MOUSE_BUTTON) 126 return; 127 128 // Tell BeOS to setup a Drag/Drop operation 129 // 130 // (When the image is dropped, BeOS sends 131 // the following message to ImageWindow, 132 // which causes it to call ImageView::SetImage()) 133 BMessage msg(B_SIMPLE_DATA); 134 msg.AddInt32("be:actions", B_COPY_TARGET); 135 msg.AddString("be:filetypes", "application/octet-stream"); 136 msg.AddString("be:types", "application/octet-stream"); 137 msg.AddString("be:clip_name", "Bitmap"); 138 139 DragMessage(&msg, Bounds()); 140 } 141 142 void 143 ImageView::MouseMoved(BPoint point, uint32 state, const BMessage *pmsg) 144 { 145 } 146 147 void 148 ImageView::MouseUp(BPoint point) 149 { 150 } 151 152 void 153 ImageView::MessageReceived(BMessage *pmsg) 154 { 155 switch (pmsg->what) { 156 case B_COPY_TARGET: 157 SaveImageAtDropLocation(pmsg); 158 break; 159 160 default: 161 BView::MessageReceived(pmsg); 162 break; 163 } 164 } 165 166 void 167 ImageView::SaveImageAtDropLocation(BMessage *pmsg) 168 { 169 // Find the location and name of the drop and 170 // write the image file there 171 BBitmapStream stream(fpbitmap); 172 173 StatusCheck chk; 174 // throw an exception if this is assigned 175 // anything other than B_OK 176 177 try { 178 entry_ref dirref; 179 chk = pmsg->FindRef("directory", &dirref); 180 const char *filename; 181 chk = pmsg->FindString("name", &filename); 182 183 BDirectory dir(&dirref); 184 BFile file(&dir, filename, B_WRITE_ONLY | B_CREATE_FILE); 185 chk = file.InitCheck(); 186 187 BTranslatorRoster *proster = BTranslatorRoster::Default(); 188 chk = proster->Translate(&stream, NULL, NULL, &file, B_TGA_FORMAT); 189 190 } catch (StatusNotOKException) { 191 BAlert *palert = new BAlert(NULL, 192 "Sorry, unable to write the image file.", "OK"); 193 palert->Go(); 194 } 195 196 stream.DetachBitmap(&fpbitmap); 197 } 198 199 void 200 ImageView::AdjustScrollBars() 201 { 202 BRect rctview = Bounds(), rctbitmap(0, 0, 0, 0); 203 if (HasImage()) 204 rctbitmap = fpbitmap->Bounds(); 205 206 float prop, range; 207 BScrollBar *psb = ScrollBar(B_HORIZONTAL); 208 if (psb) { 209 range = rctbitmap.Width() + (BORDER_WIDTH * 2) - rctview.Width(); 210 if (range < 0) range = 0; 211 prop = rctview.Width() / (rctbitmap.Width() + (BORDER_WIDTH * 2)); 212 if (prop > 1.0f) prop = 1.0f; 213 psb->SetRange(0, range); 214 psb->SetProportion(prop); 215 psb->SetSteps(10, 100); 216 } 217 218 psb = ScrollBar(B_VERTICAL); 219 if (psb) { 220 range = rctbitmap.Height() + (BORDER_HEIGHT * 2) - rctview.Height(); 221 if (range < 0) range = 0; 222 prop = rctview.Height() / (rctbitmap.Height() + (BORDER_HEIGHT * 2)); 223 if (prop > 1.0f) prop = 1.0f; 224 psb->SetRange(0, range); 225 psb->SetProportion(prop); 226 psb->SetSteps(10, 100); 227 } 228 } 229 230 struct ColorSpaceName { 231 color_space id; 232 const char *name; 233 }; 234 #define COLORSPACENAME(id) {id, #id} 235 236 // convert colorspace numerical value to 237 // a string value 238 const char * 239 get_color_space_name(color_space colors) 240 { 241 // print out colorspace if it matches an item in the list 242 const ColorSpaceName kcolorspaces[] = { 243 COLORSPACENAME(B_NO_COLOR_SPACE), 244 COLORSPACENAME(B_RGB32), 245 COLORSPACENAME(B_RGBA32), 246 COLORSPACENAME(B_RGB24), 247 COLORSPACENAME(B_RGB16), 248 COLORSPACENAME(B_RGB15), 249 COLORSPACENAME(B_RGBA15), 250 COLORSPACENAME(B_CMAP8), 251 COLORSPACENAME(B_GRAY8), 252 COLORSPACENAME(B_GRAY1), 253 COLORSPACENAME(B_RGB32_BIG), 254 COLORSPACENAME(B_RGBA32_BIG), 255 COLORSPACENAME(B_RGB24_BIG), 256 COLORSPACENAME(B_RGB16_BIG), 257 COLORSPACENAME(B_RGB15_BIG), 258 COLORSPACENAME(B_RGBA15_BIG), 259 COLORSPACENAME(B_YCbCr422), 260 COLORSPACENAME(B_YCbCr411), 261 COLORSPACENAME(B_YCbCr444), 262 COLORSPACENAME(B_YCbCr420), 263 COLORSPACENAME(B_YUV422), 264 COLORSPACENAME(B_YUV411), 265 COLORSPACENAME(B_YUV444), 266 COLORSPACENAME(B_YUV420), 267 COLORSPACENAME(B_YUV9), 268 COLORSPACENAME(B_YUV12), 269 COLORSPACENAME(B_UVL24), 270 COLORSPACENAME(B_UVL32), 271 COLORSPACENAME(B_UVLA32), 272 COLORSPACENAME(B_LAB24), 273 COLORSPACENAME(B_LAB32), 274 COLORSPACENAME(B_LABA32), 275 COLORSPACENAME(B_HSI24), 276 COLORSPACENAME(B_HSI32), 277 COLORSPACENAME(B_HSIA32), 278 COLORSPACENAME(B_HSV24), 279 COLORSPACENAME(B_HSV32), 280 COLORSPACENAME(B_HSVA32), 281 COLORSPACENAME(B_HLS24), 282 COLORSPACENAME(B_HLS32), 283 COLORSPACENAME(B_HLSA32), 284 COLORSPACENAME(B_CMY24), 285 COLORSPACENAME(B_CMY32), 286 COLORSPACENAME(B_CMYA32), 287 COLORSPACENAME(B_CMYK32) 288 }; 289 const int32 kncolorspaces = sizeof(kcolorspaces) / 290 sizeof(ColorSpaceName); 291 for (int32 i = 0; i < kncolorspaces; i++) { 292 if (colors == kcolorspaces[i].id) 293 return kcolorspaces[i].name; 294 } 295 296 return "Unknown"; 297 } 298 299 // return a string of the passed number formated 300 // as a hexadecimal number in lowercase with a leading "0x" 301 const char * 302 hex_format(uint32 num) 303 { 304 static char str[11] = { 0 }; 305 sprintf(str, "0x%.8lx", num); 306 307 return str; 308 } 309 310 // convert passed number to a string of 4 characters 311 // and return that string 312 const char * 313 char_format(uint32 num) 314 { 315 static char str[5] = { 0 }; 316 uint32 bnum = B_HOST_TO_BENDIAN_INT32(num); 317 memcpy(str, &bnum, 4); 318 319 return str; 320 } 321 322 void 323 dump_translation_formats(BString &bstr, const translation_format *pfmts, 324 int32 nfmts) 325 { 326 for (int i = 0; i < nfmts; i++) { 327 bstr << "\nType: '" << char_format(pfmts[i].type) << "' (" << 328 hex_format(pfmts[i].type) << ")\n"; 329 bstr << "Group: '" << char_format(pfmts[i].group) << "' (" << 330 hex_format(pfmts[i].group) << ")\n"; 331 bstr << "Quality: " << pfmts[i].quality << "\n"; 332 bstr << "Capability: " << pfmts[i].capability << "\n"; 333 bstr << "MIME Type: " << pfmts[i].MIME << "\n"; 334 bstr << "Name: " << pfmts[i].name << "\n"; 335 } 336 } 337 338 // Send information about the currently open image to the 339 // BApplication object so it can send it to the InfoWindow 340 void 341 ImageView::UpdateInfoWindow(const BPath &path, BMessage &ioExtension, 342 const translator_info &tinfo, BTranslatorRoster *proster) 343 { 344 BMessage msg(M_INFO_WINDOW_TEXT); 345 BString bstr; 346 347 // Bitmap Info 348 bstr << "Image: " << path.Path() << "\n"; 349 color_space cs = fpbitmap->ColorSpace(); 350 bstr << "Color Space: " << get_color_space_name(cs) << " (" << 351 hex_format(static_cast<uint32>(cs)) << ")\n"; 352 bstr << "Dimensions: " << fpbitmap->Bounds().IntegerWidth() + 1 << " x " << 353 fpbitmap->Bounds().IntegerHeight() + 1 << "\n"; 354 bstr << "Bytes per Row: " << fpbitmap->BytesPerRow() << "\n"; 355 bstr << "Total Bytes: " << fpbitmap->BitsLength() << "\n"; 356 357 // Identify Info 358 bstr << "\nIdentify Info:\n"; 359 bstr << "ID String: " << tinfo.name << "\n"; 360 bstr << "MIME Type: " << tinfo.MIME << "\n"; 361 bstr << "Type: '" << char_format(tinfo.type) << "' (" << 362 hex_format(tinfo.type) << ")\n"; 363 bstr << "Translator ID: " << tinfo.translator << "\n"; 364 bstr << "Group: '" << char_format(tinfo.group) << "' (" << 365 hex_format(tinfo.group) << ")\n"; 366 bstr << "Quality: " << tinfo.quality << "\n"; 367 bstr << "Capability: " << tinfo.capability << "\n"; 368 369 // Extension Info 370 bstr << "\nExtension Info:\n"; 371 int32 document_count = 0, document_index = 0; 372 if (ioExtension.FindInt32("/documentCount", &document_count) == B_OK) 373 bstr << "Number of Documents: " << document_count << "\n"; 374 if (ioExtension.FindInt32("/documentIndex", &document_index) == B_OK) 375 bstr << "Selected Document: " << document_index << "\n"; 376 377 // Translator Info 378 const char *tranname = NULL, *traninfo = NULL; 379 int32 tranversion = 0; 380 if (proster->GetTranslatorInfo(tinfo.translator, &tranname, &traninfo, 381 &tranversion) == B_OK) { 382 bstr << "\nTranslator Used:\n"; 383 bstr << "Name: " << tranname << "\n"; 384 bstr << "Info: " << traninfo << "\n"; 385 bstr << "Version: " << tranversion << "\n"; 386 } 387 388 // Translator Input / Output Formats 389 int32 nins = 0, nouts = 0; 390 const translation_format *pins = NULL, *pouts = NULL; 391 if (proster->GetInputFormats(tinfo.translator, &pins, &nins) == B_OK) { 392 bstr << "\nInput Formats:"; 393 dump_translation_formats(bstr, pins, nins); 394 pins = NULL; 395 } 396 if (proster->GetOutputFormats(tinfo.translator, &pouts, &nouts) == B_OK) { 397 bstr << "\nOutput Formats:"; 398 dump_translation_formats(bstr, pouts, nouts); 399 pouts = NULL; 400 } 401 402 msg.AddString("text", bstr); 403 be_app->PostMessage(&msg); 404 } 405 406 BTranslatorRoster * 407 ImageView::SelectTranslatorRoster(BTranslatorRoster &roster) 408 { 409 bool bNoneSelected = true; 410 411 InspectorApp *papp; 412 papp = static_cast<InspectorApp *>(be_app); 413 if (papp) { 414 BList *plist = papp->GetTranslatorsList(); 415 if (plist) { 416 for (int32 i = 0; i < plist->CountItems(); i++) { 417 BTranslatorItem *pitem = 418 static_cast<BTranslatorItem *>(plist->ItemAt(i)); 419 if (pitem->IsSelected()) { 420 bNoneSelected = false; 421 roster.AddTranslators(pitem->Path()); 422 } 423 } 424 } 425 } 426 427 if (bNoneSelected) 428 return BTranslatorRoster::Default(); 429 else 430 return &roster; 431 } 432 433 void 434 ImageView::SetImage(BMessage *pmsg) 435 { 436 // Replace current image with the image 437 // specified in the given BMessage 438 439 entry_ref ref; 440 if (!pmsg) 441 ref = fcurrentRef; 442 else if (pmsg->FindRef("refs", &ref) != B_OK) 443 // If refs not found, just ignore the message 444 return; 445 446 StatusCheck chk; 447 448 try { 449 BFile file(&ref, B_READ_ONLY); 450 chk = file.InitCheck(); 451 452 BTranslatorRoster roster, *proster; 453 proster = SelectTranslatorRoster(roster); 454 if (!proster) 455 // throw exception 456 chk = B_ERROR; 457 458 // determine what type the image is 459 translator_info tinfo; 460 BMessage ioExtension; 461 if (ref != fcurrentRef) 462 // if new image, reset to first document 463 fdocumentIndex = 1; 464 chk = ioExtension.AddInt32("/documentIndex", fdocumentIndex); 465 chk = proster->Identify(&file, &ioExtension, &tinfo, 0, NULL, 466 B_TRANSLATOR_BITMAP); 467 468 // perform the actual translation 469 BBitmapStream outstream; 470 chk = proster->Translate(&file, &tinfo, &ioExtension, &outstream, 471 B_TRANSLATOR_BITMAP); 472 BBitmap *pbitmap = NULL; 473 chk = outstream.DetachBitmap(&pbitmap); 474 delete fpbitmap; 475 fpbitmap = pbitmap; 476 pbitmap = NULL; 477 fcurrentRef = ref; 478 // need to keep the ref around if user wants to switch pages 479 int32 documentCount = 0; 480 if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK && 481 documentCount > 0) 482 fdocumentCount = documentCount; 483 else 484 fdocumentCount = 1; 485 486 // Set the name of the Window to reflect the file name 487 BWindow *pwin = Window(); 488 BEntry entry(&ref); 489 BPath path; 490 if (entry.InitCheck() == B_OK) { 491 if (path.SetTo(&entry) == B_OK) 492 pwin->SetTitle(path.Leaf()); 493 else 494 pwin->SetTitle(IMAGEWINDOW_TITLE); 495 } else 496 pwin->SetTitle(IMAGEWINDOW_TITLE); 497 498 UpdateInfoWindow(path, ioExtension, tinfo, proster); 499 500 // Resize parent window and set size limits to 501 // reflect the size of the new bitmap 502 float width, height; 503 BMenuBar *pbar = pwin->KeyMenuBar(); 504 width = fpbitmap->Bounds().Width() + B_V_SCROLL_BAR_WIDTH + (BORDER_WIDTH * 2); 505 height = fpbitmap->Bounds().Height() + 506 pbar->Bounds().Height() + B_H_SCROLL_BAR_HEIGHT + (BORDER_HEIGHT * 2) + 1; 507 508 BScreen *pscreen = new BScreen(pwin); 509 BRect rctscreen = pscreen->Frame(); 510 if (width > rctscreen.Width()) 511 width = rctscreen.Width(); 512 if (height > rctscreen.Height()) 513 height = rctscreen.Height(); 514 pwin->SetSizeLimits(B_V_SCROLL_BAR_WIDTH * 4, width, 515 pbar->Bounds().Height() + (B_H_SCROLL_BAR_HEIGHT * 4) + 1, height); 516 pwin->SetZoomLimits(width, height); 517 518 AdjustScrollBars(); 519 520 //pwin->Zoom(); 521 // Perform all of the hard work of resizing the 522 // window while taking into account the size of 523 // the screen, the tab and borders of the window 524 // 525 // HACK: Need to fix case where window un-zooms 526 // when the window is already the correct size 527 // for the current image 528 529 // repaint view 530 Invalidate(); 531 532 } catch (StatusNotOKException) { 533 BAlert *palert = new BAlert(NULL, 534 "Sorry, unable to load the image.", "OK"); 535 palert->Go(); 536 } 537 } 538 539 void 540 ImageView::FirstPage() 541 { 542 if (fdocumentIndex != 1) { 543 fdocumentIndex = 1; 544 SetImage(NULL); 545 } 546 } 547 548 void 549 ImageView::LastPage() 550 { 551 if (fdocumentIndex != fdocumentCount) { 552 fdocumentIndex = fdocumentCount; 553 SetImage(NULL); 554 } 555 } 556 557 void 558 ImageView::NextPage() 559 { 560 if (fdocumentIndex < fdocumentCount) { 561 fdocumentIndex++; 562 SetImage(NULL); 563 } 564 } 565 566 void 567 ImageView::PrevPage() 568 { 569 if (fdocumentIndex > 1) { 570 fdocumentIndex--; 571 SetImage(NULL); 572 } 573 } 574 575