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 <Catalog.h> 41 #include <Message.h> 42 #include <Locale.h> 43 #include <List.h> 44 #include <String.h> 45 #include <TranslationUtils.h> 46 #include <TranslatorRoster.h> 47 #include <BitmapStream.h> 48 #include <Entry.h> 49 #include <Path.h> 50 #include <Directory.h> 51 #include <File.h> 52 #include <MenuBar.h> 53 #include <Screen.h> 54 #include <ScrollBar.h> 55 #include <Alert.h> 56 #include <stdlib.h> 57 #include <stdio.h> 58 #include <string.h> 59 60 #define BORDER_WIDTH 16 61 #define BORDER_HEIGHT 16 62 #define PEN_SIZE 1.0f 63 64 #undef B_TRANSLATION_CONTEXT 65 #define B_TRANSLATION_CONTEXT "ImageView" 66 67 68 ImageView::ImageView(BRect rect, const char *name) 69 : BView(rect, name, B_FOLLOW_ALL, B_WILL_DRAW | B_FRAME_EVENTS) 70 { 71 fpbitmap = NULL; 72 fdocumentIndex = 1; 73 fdocumentCount = 1; 74 75 SetViewColor(192, 192, 192); 76 SetHighColor(0, 0, 0); 77 SetPenSize(PEN_SIZE); 78 } 79 80 81 ImageView::~ImageView() 82 { 83 delete fpbitmap; 84 fpbitmap = NULL; 85 } 86 87 88 void 89 ImageView::AttachedToWindow() 90 { 91 AdjustScrollBars(); 92 } 93 94 95 void 96 ImageView::Draw(BRect rect) 97 { 98 if (HasImage()) { 99 // Draw black rectangle around image 100 StrokeRect( 101 BRect(BORDER_WIDTH - PEN_SIZE, 102 BORDER_HEIGHT - PEN_SIZE, 103 fpbitmap->Bounds().Width() + BORDER_WIDTH + PEN_SIZE, 104 fpbitmap->Bounds().Height() + BORDER_HEIGHT + PEN_SIZE)); 105 106 DrawBitmap(fpbitmap, BPoint(BORDER_WIDTH, BORDER_HEIGHT)); 107 } 108 } 109 110 111 void 112 ImageView::ReDraw() 113 { 114 Draw(Bounds()); 115 } 116 117 118 void 119 ImageView::FrameResized(float width, float height) 120 { 121 AdjustScrollBars(); 122 123 if (!HasImage()) 124 Invalidate(); 125 } 126 127 128 void 129 ImageView::MouseDown(BPoint point) 130 { 131 if (!HasImage()) 132 return; 133 134 // Only accept left button clicks 135 BMessage *pmsg = Window()->CurrentMessage(); 136 int32 button = pmsg->FindInt32("buttons"); 137 if (button != B_PRIMARY_MOUSE_BUTTON) 138 return; 139 140 // Tell BeOS to setup a Drag/Drop operation 141 // 142 // (When the image is dropped, BeOS sends 143 // the following message to ImageWindow, 144 // which causes it to call ImageView::SetImage()) 145 BMessage msg(B_SIMPLE_DATA); 146 msg.AddInt32("be:actions", B_COPY_TARGET); 147 msg.AddString("be:filetypes", "application/octet-stream"); 148 msg.AddString("be:types", "application/octet-stream"); 149 msg.AddString("be:clip_name", "Bitmap"); 150 151 DragMessage(&msg, Bounds()); 152 } 153 154 155 void 156 ImageView::MouseMoved(BPoint point, uint32 state, const BMessage *pmsg) 157 { 158 } 159 160 161 void 162 ImageView::MouseUp(BPoint point) 163 { 164 } 165 166 167 void 168 ImageView::MessageReceived(BMessage *pmsg) 169 { 170 switch (pmsg->what) { 171 case B_COPY_TARGET: 172 SaveImageAtDropLocation(pmsg); 173 break; 174 175 default: 176 BView::MessageReceived(pmsg); 177 break; 178 } 179 } 180 181 182 void 183 ImageView::SaveImageAtDropLocation(BMessage *pmsg) 184 { 185 // Find the location and name of the drop and 186 // write the image file there 187 BBitmapStream stream(fpbitmap); 188 189 StatusCheck chk; 190 // throw an exception if this is assigned 191 // anything other than B_OK 192 193 try { 194 entry_ref dirref; 195 chk = pmsg->FindRef("directory", &dirref); 196 const char *filename; 197 chk = pmsg->FindString("name", &filename); 198 199 BDirectory dir(&dirref); 200 BFile file(&dir, filename, B_WRITE_ONLY | B_CREATE_FILE); 201 chk = file.InitCheck(); 202 203 BTranslatorRoster *proster = BTranslatorRoster::Default(); 204 chk = proster->Translate(&stream, NULL, NULL, &file, B_TGA_FORMAT); 205 206 } catch (StatusNotOKException) { 207 BAlert *palert = new BAlert(NULL, 208 B_TRANSLATE("Sorry, unable to write the image file."), 209 B_TRANSLATE("OK")); 210 palert->Go(); 211 } 212 213 stream.DetachBitmap(&fpbitmap); 214 } 215 216 217 void 218 ImageView::AdjustScrollBars() 219 { 220 BRect rctview = Bounds(), rctbitmap(0, 0, 0, 0); 221 if (HasImage()) 222 rctbitmap = fpbitmap->Bounds(); 223 224 float prop, range; 225 BScrollBar *psb = ScrollBar(B_HORIZONTAL); 226 if (psb) { 227 range = rctbitmap.Width() + (BORDER_WIDTH * 2) - rctview.Width(); 228 if (range < 0) range = 0; 229 prop = rctview.Width() / (rctbitmap.Width() + (BORDER_WIDTH * 2)); 230 if (prop > 1.0f) prop = 1.0f; 231 psb->SetRange(0, range); 232 psb->SetProportion(prop); 233 psb->SetSteps(10, 100); 234 } 235 236 psb = ScrollBar(B_VERTICAL); 237 if (psb) { 238 range = rctbitmap.Height() + (BORDER_HEIGHT * 2) - rctview.Height(); 239 if (range < 0) range = 0; 240 prop = rctview.Height() / (rctbitmap.Height() + (BORDER_HEIGHT * 2)); 241 if (prop > 1.0f) prop = 1.0f; 242 psb->SetRange(0, range); 243 psb->SetProportion(prop); 244 psb->SetSteps(10, 100); 245 } 246 } 247 248 249 struct ColorSpaceName { 250 color_space id; 251 const char *name; 252 }; 253 #define COLORSPACENAME(id) {id, #id} 254 255 256 // convert colorspace numerical value to 257 // a string value 258 const char * 259 get_color_space_name(color_space colors) 260 { 261 // print out colorspace if it matches an item in the list 262 const ColorSpaceName kcolorspaces[] = { 263 COLORSPACENAME(B_NO_COLOR_SPACE), 264 COLORSPACENAME(B_RGB32), 265 COLORSPACENAME(B_RGBA32), 266 COLORSPACENAME(B_RGB24), 267 COLORSPACENAME(B_RGB16), 268 COLORSPACENAME(B_RGB15), 269 COLORSPACENAME(B_RGBA15), 270 COLORSPACENAME(B_CMAP8), 271 COLORSPACENAME(B_GRAY8), 272 COLORSPACENAME(B_GRAY1), 273 COLORSPACENAME(B_RGB32_BIG), 274 COLORSPACENAME(B_RGBA32_BIG), 275 COLORSPACENAME(B_RGB24_BIG), 276 COLORSPACENAME(B_RGB16_BIG), 277 COLORSPACENAME(B_RGB15_BIG), 278 COLORSPACENAME(B_RGBA15_BIG), 279 COLORSPACENAME(B_YCbCr422), 280 COLORSPACENAME(B_YCbCr411), 281 COLORSPACENAME(B_YCbCr444), 282 COLORSPACENAME(B_YCbCr420), 283 COLORSPACENAME(B_YUV422), 284 COLORSPACENAME(B_YUV411), 285 COLORSPACENAME(B_YUV444), 286 COLORSPACENAME(B_YUV420), 287 COLORSPACENAME(B_YUV9), 288 COLORSPACENAME(B_YUV12), 289 COLORSPACENAME(B_UVL24), 290 COLORSPACENAME(B_UVL32), 291 COLORSPACENAME(B_UVLA32), 292 COLORSPACENAME(B_LAB24), 293 COLORSPACENAME(B_LAB32), 294 COLORSPACENAME(B_LABA32), 295 COLORSPACENAME(B_HSI24), 296 COLORSPACENAME(B_HSI32), 297 COLORSPACENAME(B_HSIA32), 298 COLORSPACENAME(B_HSV24), 299 COLORSPACENAME(B_HSV32), 300 COLORSPACENAME(B_HSVA32), 301 COLORSPACENAME(B_HLS24), 302 COLORSPACENAME(B_HLS32), 303 COLORSPACENAME(B_HLSA32), 304 COLORSPACENAME(B_CMY24), 305 COLORSPACENAME(B_CMY32), 306 COLORSPACENAME(B_CMYA32), 307 COLORSPACENAME(B_CMYK32) 308 }; 309 const int32 kncolorspaces = sizeof(kcolorspaces) / 310 sizeof(ColorSpaceName); 311 for (int32 i = 0; i < kncolorspaces; i++) { 312 if (colors == kcolorspaces[i].id) 313 return kcolorspaces[i].name; 314 } 315 316 return B_TRANSLATE("Unknown"); 317 } 318 319 320 // return a string of the passed number formated 321 // as a hexadecimal number in lowercase with a leading "0x" 322 const char * 323 hex_format(uint32 num) 324 { 325 static char str[11] = { 0 }; 326 sprintf(str, "0x%.8lx", num); 327 328 return str; 329 } 330 331 332 // convert passed number to a string of 4 characters 333 // and return that string 334 const char * 335 char_format(uint32 num) 336 { 337 static char str[5] = { 0 }; 338 uint32 bnum = B_HOST_TO_BENDIAN_INT32(num); 339 memcpy(str, &bnum, 4); 340 341 return str; 342 } 343 344 345 void 346 dump_translation_formats(BString &bstr, const translation_format *pfmts, 347 int32 nfmts) 348 { 349 BString *str1 = NULL; 350 for (int i = 0; i < nfmts; i++) { 351 BString string = B_TRANSLATE("\nType: '%1' (%2)\n" 352 "Group: '%3' (%4)\n" 353 "Quality: %5\n" 354 "Capability: %6\n" 355 "MIME Type: %7\n" 356 "Name: %8\n"); 357 string.ReplaceFirst("%1", char_format(pfmts[i].type)); 358 string.ReplaceFirst("%2", hex_format(pfmts[i].type)); 359 string.ReplaceFirst("%3", char_format(pfmts[i].group)); 360 string.ReplaceFirst("%4", hex_format(pfmts[i].group)); 361 char str2[127] = { 0 }; 362 sprintf(str2, "%f", pfmts[i].quality); 363 string.ReplaceFirst("%5", str2 ); 364 str2[0] = '\0'; 365 sprintf(str2, "%f", pfmts[i].capability); 366 string.ReplaceFirst("%6", str2 ); 367 string.ReplaceFirst("%7", pfmts[i].MIME); 368 string.ReplaceFirst("%8", pfmts[i].name); 369 if (i == 0) 370 str1 = new BString(string); 371 else 372 str1->Append(string); 373 } 374 bstr = str1->String(); 375 } 376 377 378 // Send information about the currently open image to the 379 // BApplication object so it can send it to the InfoWindow 380 void 381 ImageView::UpdateInfoWindow(const BPath &path, BMessage &ioExtension, 382 const translator_info &tinfo, BTranslatorRoster *proster) 383 { 384 BMessage msg(M_INFO_WINDOW_TEXT); 385 BString bstr; 386 387 bstr = B_TRANSLATE("Image: %1\n" 388 "Color Space: %2 (%3)\n" 389 "Dimensions: %4 x %5\n" 390 "Bytes per Row: %6\n" 391 "Total Bytes: %7\n" 392 "\nIdentify Info:\n" 393 "ID String: %8\n" 394 "MIME Type: %9\n" 395 "Type: '%10' (%11)\n" 396 "Translator ID: %12\n" 397 "Group: '%13' (%14)\n" 398 "Quality: %15\n" 399 "Capability: %16\n" 400 "\nExtension Info:\n"); 401 bstr.ReplaceFirst("%1", path.Path()); 402 color_space cs = fpbitmap->ColorSpace(); 403 bstr.ReplaceFirst("%2", get_color_space_name(cs)); 404 bstr.ReplaceFirst("%3", hex_format(static_cast<uint32>(cs))); 405 char str2[127] = { 0 }; 406 sprintf(str2, "%ld", fpbitmap->Bounds().IntegerWidth() + 1); 407 bstr.ReplaceFirst("%4", str2); 408 str2[0] = '\0'; 409 sprintf(str2, "%ld", fpbitmap->Bounds().IntegerHeight() + 1); 410 bstr.ReplaceFirst("%5", str2); 411 str2[0] = '\0'; 412 sprintf(str2, "%ld", fpbitmap->BytesPerRow()); 413 bstr.ReplaceFirst("%6", str2); 414 str2[0] = '\0'; 415 sprintf(str2, "%ld", fpbitmap->BitsLength()); 416 bstr.ReplaceFirst("%7", str2); 417 bstr.ReplaceFirst("%8", tinfo.name); 418 bstr.ReplaceFirst("%9", tinfo.MIME); 419 bstr.ReplaceFirst("%10", char_format(tinfo.type)); 420 bstr.ReplaceFirst("%11", hex_format(tinfo.type)); 421 str2[0] = '\0'; 422 sprintf(str2, "%ld", tinfo.translator); 423 bstr.ReplaceFirst("%12", str2); 424 bstr.ReplaceFirst("%13", char_format(tinfo.group)); 425 bstr.ReplaceFirst("%14", hex_format(tinfo.group)); 426 str2[0] = '\0'; 427 sprintf(str2, "%f", tinfo.quality); 428 bstr.ReplaceFirst("%15", str2); 429 str2[0] = '\0'; 430 sprintf(str2, "%f", tinfo.capability); 431 bstr.ReplaceFirst("%16", str2); 432 433 int32 document_count = 0, document_index = 0; 434 // Translator Info 435 const char *tranname = NULL, *traninfo = NULL; 436 int32 tranversion = 0; 437 438 if (ioExtension.FindInt32("/documentCount", &document_count) == B_OK) { 439 BString str = B_TRANSLATE("Number of Documents: %1\n" 440 "\nTranslator Used:\n" 441 "Name: %2\n" 442 "Info: %3\n" 443 "Version: %4\n"); 444 char str2[127] = { 0 }; 445 sprintf(str2, "%ld", document_count); 446 str.ReplaceFirst("%1", str2); 447 str.ReplaceFirst("%2", tranname); 448 str.ReplaceFirst("%3", traninfo); 449 str2[0] = '\0'; 450 sprintf(str2, "%d", (int)tranversion); 451 str.ReplaceFirst("%4", str2); 452 bstr.Append(str.String()); 453 } 454 else 455 if (ioExtension.FindInt32("/documentIndex", &document_index) == B_OK) { 456 BString str = B_TRANSLATE("Selected Document: %1\n" 457 "\nTranslator Used:\n" 458 "Name: %2\n" 459 "Info: %3\n" 460 "Version: %4\n"); 461 char str2[127] = { 0 }; 462 sprintf(str2, "%ld", document_index); 463 str.ReplaceFirst("%1", str2); 464 str.ReplaceFirst("%2", tranname); 465 str.ReplaceFirst("%3", traninfo); 466 str2[0] = '\0'; 467 sprintf(str2, "%d", (int)tranversion); 468 str.ReplaceFirst("%4", str2); 469 bstr.Append(str.String()); 470 } 471 else 472 if (proster->GetTranslatorInfo(tinfo.translator, &tranname, 473 &traninfo, &tranversion) == B_OK) { 474 BString str = B_TRANSLATE("\nTranslator Used:\n" 475 "Name: %1\n" 476 "Info: %2\n" 477 "Version: %3\n"); 478 str.ReplaceFirst("%1", tranname); 479 str.ReplaceFirst("%2", traninfo); 480 char str2[127] = { 0 }; 481 sprintf(str2, "%d", (int)tranversion); 482 str.ReplaceFirst("%3", str2); 483 bstr.Append(str.String()); 484 } 485 486 // Translator Input / Output Formats 487 int32 nins = 0, nouts = 0; 488 const translation_format *pins = NULL, *pouts = NULL; 489 if (proster->GetInputFormats(tinfo.translator, &pins, &nins) == B_OK) { 490 bstr << B_TRANSLATE("\nInput Formats:"); 491 dump_translation_formats(bstr, pins, nins); 492 pins = NULL; 493 } 494 if (proster->GetOutputFormats(tinfo.translator, &pouts, &nouts) == B_OK) { 495 bstr << B_TRANSLATE("\nOutput Formats:"); 496 dump_translation_formats(bstr, pouts, nouts); 497 pouts = NULL; 498 } 499 msg.AddString("text", bstr); 500 be_app->PostMessage(&msg); 501 } 502 503 504 BTranslatorRoster * 505 ImageView::SelectTranslatorRoster(BTranslatorRoster &roster) 506 { 507 bool bNoneSelected = true; 508 509 InspectorApp *papp; 510 papp = static_cast<InspectorApp *>(be_app); 511 if (papp) { 512 BList *plist = papp->GetTranslatorsList(); 513 if (plist) { 514 for (int32 i = 0; i < plist->CountItems(); i++) { 515 BTranslatorItem *pitem = 516 static_cast<BTranslatorItem *>(plist->ItemAt(i)); 517 if (pitem->IsSelected()) { 518 bNoneSelected = false; 519 roster.AddTranslators(pitem->Path()); 520 } 521 } 522 } 523 } 524 525 if (bNoneSelected) 526 return BTranslatorRoster::Default(); 527 else 528 return &roster; 529 } 530 531 532 void 533 ImageView::SetImage(BMessage *pmsg) 534 { 535 // Replace current image with the image 536 // specified in the given BMessage 537 538 entry_ref ref; 539 if (!pmsg) 540 ref = fcurrentRef; 541 else if (pmsg->FindRef("refs", &ref) != B_OK) 542 // If refs not found, just ignore the message 543 return; 544 545 StatusCheck chk; 546 547 try { 548 BFile file(&ref, B_READ_ONLY); 549 chk = file.InitCheck(); 550 551 BTranslatorRoster roster, *proster; 552 proster = SelectTranslatorRoster(roster); 553 if (!proster) 554 // throw exception 555 chk = B_ERROR; 556 // determine what type the image is 557 translator_info tinfo; 558 BMessage ioExtension; 559 if (ref != fcurrentRef) 560 // if new image, reset to first document 561 fdocumentIndex = 1; 562 chk = ioExtension.AddInt32("/documentIndex", fdocumentIndex); 563 chk = proster->Identify(&file, &ioExtension, &tinfo, 0, NULL, 564 B_TRANSLATOR_BITMAP); 565 566 // perform the actual translation 567 BBitmapStream outstream; 568 chk = proster->Translate(&file, &tinfo, &ioExtension, &outstream, 569 B_TRANSLATOR_BITMAP); 570 BBitmap *pbitmap = NULL; 571 chk = outstream.DetachBitmap(&pbitmap); 572 delete fpbitmap; 573 fpbitmap = pbitmap; 574 pbitmap = NULL; 575 fcurrentRef = ref; 576 // need to keep the ref around if user wants to switch pages 577 int32 documentCount = 0; 578 if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK && 579 documentCount > 0) 580 fdocumentCount = documentCount; 581 else 582 fdocumentCount = 1; 583 584 // Set the name of the Window to reflect the file name 585 BWindow *pwin = Window(); 586 BEntry entry(&ref); 587 BPath path; 588 if (entry.InitCheck() == B_OK) { 589 if (path.SetTo(&entry) == B_OK) 590 pwin->SetTitle(path.Leaf()); 591 else 592 pwin->SetTitle(IMAGEWINDOW_TITLE); 593 } else 594 pwin->SetTitle(IMAGEWINDOW_TITLE); 595 UpdateInfoWindow(path, ioExtension, tinfo, proster); 596 597 // Resize parent window and set size limits to 598 // reflect the size of the new bitmap 599 float width, height; 600 BMenuBar *pbar = pwin->KeyMenuBar(); 601 width = fpbitmap->Bounds().Width() + B_V_SCROLL_BAR_WIDTH + (BORDER_WIDTH * 2); 602 height = fpbitmap->Bounds().Height() + 603 pbar->Bounds().Height() + B_H_SCROLL_BAR_HEIGHT + (BORDER_HEIGHT * 2) + 1; 604 BScreen *pscreen = new BScreen(pwin); 605 BRect rctscreen = pscreen->Frame(); 606 if (width > rctscreen.Width()) 607 width = rctscreen.Width(); 608 if (height > rctscreen.Height()) 609 height = rctscreen.Height(); 610 pwin->SetSizeLimits(B_V_SCROLL_BAR_WIDTH * 4, width, 611 pbar->Bounds().Height() + (B_H_SCROLL_BAR_HEIGHT * 4) + 1, height); 612 pwin->SetZoomLimits(width, height); 613 AdjustScrollBars(); 614 615 //pwin->Zoom(); 616 // Perform all of the hard work of resizing the 617 // window while taking into account the size of 618 // the screen, the tab and borders of the window 619 // 620 // HACK: Need to fix case where window un-zooms 621 // when the window is already the correct size 622 // for the current image 623 624 // repaint view 625 Invalidate(); 626 627 } catch (StatusNotOKException) { 628 BAlert *palert = new BAlert(NULL, 629 B_TRANSLATE("Sorry, unable to load the image."), 630 B_TRANSLATE("OK")); 631 palert->Go(); 632 } 633 } 634 635 636 void 637 ImageView::FirstPage() 638 { 639 if (fdocumentIndex != 1) { 640 fdocumentIndex = 1; 641 SetImage(NULL); 642 } 643 } 644 645 646 void 647 ImageView::LastPage() 648 { 649 if (fdocumentIndex != fdocumentCount) { 650 fdocumentIndex = fdocumentCount; 651 SetImage(NULL); 652 } 653 } 654 655 656 void 657 ImageView::NextPage() 658 { 659 if (fdocumentIndex < fdocumentCount) { 660 fdocumentIndex++; 661 SetImage(NULL); 662 } 663 } 664 665 666 void 667 ImageView::PrevPage() 668 { 669 if (fdocumentIndex > 1) { 670 fdocumentIndex--; 671 SetImage(NULL); 672 } 673 } 674 675