xref: /haiku/src/tools/translation/inspector/ImageView.cpp (revision 4f00613311d0bd6b70fa82ce19931c41f071ea4e)
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