xref: /haiku/src/tools/translation/inspector/ImageView.cpp (revision e5ef530596d14e246ef8b44da8c6757624930065)
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 
67 	SetViewColor(192, 192, 192);
68 	SetHighColor(0, 0, 0);
69 	SetPenSize(PEN_SIZE);
70 }
71 
72 ImageView::~ImageView()
73 {
74 	delete fpbitmap;
75 	fpbitmap = NULL;
76 }
77 
78 void
79 ImageView::AttachedToWindow()
80 {
81 	AdjustScrollBars();
82 }
83 
84 void
85 ImageView::Draw(BRect rect)
86 {
87 	if (HasImage()) {
88 		// Draw black rectangle around image
89 		StrokeRect(
90 			BRect(BORDER_WIDTH - PEN_SIZE,
91 				BORDER_HEIGHT - PEN_SIZE,
92 				fpbitmap->Bounds().Width() + BORDER_WIDTH + PEN_SIZE,
93 				fpbitmap->Bounds().Height() + BORDER_HEIGHT + PEN_SIZE));
94 
95 		DrawBitmap(fpbitmap, BPoint(BORDER_WIDTH, BORDER_HEIGHT));
96 	}
97 }
98 
99 void
100 ImageView::ReDraw()
101 {
102 	Draw(Bounds());
103 }
104 
105 void
106 ImageView::FrameResized(float width, float height)
107 {
108 	AdjustScrollBars();
109 
110 	if (!HasImage())
111 		Invalidate();
112 }
113 
114 void
115 ImageView::MouseDown(BPoint point)
116 {
117 	if (!HasImage())
118 		return;
119 
120 	// Only accept left button clicks
121 	BMessage *pmsg = Window()->CurrentMessage();
122 	int32 button = pmsg->FindInt32("buttons");
123 	if (button != B_PRIMARY_MOUSE_BUTTON)
124 		return;
125 
126 	// Tell BeOS to setup a Drag/Drop operation
127 	//
128 	// (When the image is dropped, BeOS sends
129 	// the following message to ImageWindow,
130 	// which causes it to call ImageView::SetImage())
131 	BMessage msg(B_SIMPLE_DATA);
132 	msg.AddInt32("be:actions", B_COPY_TARGET);
133 	msg.AddString("be:filetypes", "application/octet-stream");
134 	msg.AddString("be:types", "application/octet-stream");
135 	msg.AddString("be:clip_name", "Bitmap");
136 
137 	DragMessage(&msg, Bounds());
138 }
139 
140 void
141 ImageView::MouseMoved(BPoint point, uint32 state, const BMessage *pmsg)
142 {
143 }
144 
145 void
146 ImageView::MouseUp(BPoint point)
147 {
148 }
149 
150 void
151 ImageView::MessageReceived(BMessage *pmsg)
152 {
153 	switch (pmsg->what) {
154 		case B_COPY_TARGET:
155 			SaveImageAtDropLocation(pmsg);
156 			break;
157 
158 		default:
159 			BView::MessageReceived(pmsg);
160 			break;
161 	}
162 }
163 
164 void
165 ImageView::SaveImageAtDropLocation(BMessage *pmsg)
166 {
167 	// Find the location and name of the drop and
168 	// write the image file there
169 
170 	BBitmapStream stream(fpbitmap);
171 
172 	StatusCheck chk;
173 		// throw an exception if this is assigned
174 		// anything other than B_OK
175 	try {
176 		entry_ref dirref;
177 		chk = pmsg->FindRef("directory", &dirref);
178 		const char *filename;
179 		chk = pmsg->FindString("name", &filename);
180 
181 		BDirectory dir(&dirref);
182 		BFile file(&dir, filename, B_WRITE_ONLY | B_CREATE_FILE);
183 		chk = file.InitCheck();
184 
185 		BTranslatorRoster *proster = BTranslatorRoster::Default();
186 		chk = proster->Translate(&stream, NULL, NULL, &file, B_TGA_FORMAT);
187 
188 	} catch (StatusNotOKException) {
189 		BAlert *palert = new BAlert(NULL,
190 			"Sorry, unable to write the image file.", "OK");
191 		palert->Go();
192 	}
193 
194 	stream.DetachBitmap(&fpbitmap);
195 }
196 
197 void
198 ImageView::AdjustScrollBars()
199 {
200 	BRect rctview = Bounds(), rctbitmap(0, 0, 0, 0);
201 	if (HasImage())
202 		rctbitmap = fpbitmap->Bounds();
203 
204 	float prop, range;
205 	BScrollBar *psb = ScrollBar(B_HORIZONTAL);
206 	if (psb) {
207 		range = rctbitmap.Width() + (BORDER_WIDTH * 2) - rctview.Width();
208 		if (range < 0) range = 0;
209 		prop = rctview.Width() / (rctbitmap.Width() + (BORDER_WIDTH * 2));
210 		if (prop > 1.0f) prop = 1.0f;
211 		psb->SetRange(0, range);
212 		psb->SetProportion(prop);
213 		psb->SetSteps(10, 100);
214 	}
215 
216 	psb = ScrollBar(B_VERTICAL);
217 	if (psb) {
218 		range = rctbitmap.Height() + (BORDER_HEIGHT * 2) - rctview.Height();
219 		if (range < 0) range = 0;
220 		prop = rctview.Height() / (rctbitmap.Height() + (BORDER_HEIGHT * 2));
221 		if (prop > 1.0f) prop = 1.0f;
222 		psb->SetRange(0, range);
223 		psb->SetProportion(prop);
224 		psb->SetSteps(10, 100);
225 	}
226 }
227 
228 struct ColorSpaceName {
229 	color_space id;
230 	const char *name;
231 };
232 #define COLORSPACENAME(id) {id, #id}
233 
234 // convert colorspace numerical value to
235 // a string value
236 const char *
237 get_color_space_name(color_space colors)
238 {
239 	// print out colorspace if it matches an item in the list
240 	const ColorSpaceName kcolorspaces[] = {
241 		COLORSPACENAME(B_NO_COLOR_SPACE),
242 		COLORSPACENAME(B_RGB32),
243 		COLORSPACENAME(B_RGBA32),
244 		COLORSPACENAME(B_RGB24),
245 		COLORSPACENAME(B_RGB16),
246 		COLORSPACENAME(B_RGB15),
247 		COLORSPACENAME(B_RGBA15),
248 		COLORSPACENAME(B_CMAP8),
249 		COLORSPACENAME(B_GRAY8),
250 		COLORSPACENAME(B_GRAY1),
251 		COLORSPACENAME(B_RGB32_BIG),
252 		COLORSPACENAME(B_RGBA32_BIG),
253 		COLORSPACENAME(B_RGB24_BIG),
254 		COLORSPACENAME(B_RGB16_BIG),
255 		COLORSPACENAME(B_RGB15_BIG),
256 		COLORSPACENAME(B_RGBA15_BIG),
257 		COLORSPACENAME(B_YCbCr422),
258 		COLORSPACENAME(B_YCbCr411),
259 		COLORSPACENAME(B_YCbCr444),
260 		COLORSPACENAME(B_YCbCr420),
261 		COLORSPACENAME(B_YUV422),
262 		COLORSPACENAME(B_YUV411),
263 		COLORSPACENAME(B_YUV444),
264 		COLORSPACENAME(B_YUV420),
265 		COLORSPACENAME(B_YUV9),
266 		COLORSPACENAME(B_YUV12),
267 		COLORSPACENAME(B_UVL24),
268 		COLORSPACENAME(B_UVL32),
269 		COLORSPACENAME(B_UVLA32),
270 		COLORSPACENAME(B_LAB24),
271 		COLORSPACENAME(B_LAB32),
272 		COLORSPACENAME(B_LABA32),
273 		COLORSPACENAME(B_HSI24),
274 		COLORSPACENAME(B_HSI32),
275 		COLORSPACENAME(B_HSIA32),
276 		COLORSPACENAME(B_HSV24),
277 		COLORSPACENAME(B_HSV32),
278 		COLORSPACENAME(B_HSVA32),
279 		COLORSPACENAME(B_HLS24),
280 		COLORSPACENAME(B_HLS32),
281 		COLORSPACENAME(B_HLSA32),
282 		COLORSPACENAME(B_CMY24),
283 		COLORSPACENAME(B_CMY32),
284 		COLORSPACENAME(B_CMYA32),
285 		COLORSPACENAME(B_CMYK32)
286 	};
287 	const int32 kncolorspaces =  sizeof(kcolorspaces) /
288 		sizeof(ColorSpaceName);
289 	for (int32 i = 0; i < kncolorspaces; i++) {
290 		if (colors == kcolorspaces[i].id)
291 			return kcolorspaces[i].name;
292 	}
293 
294 	return "Unknown";
295 }
296 
297 // return a string of the passed number formated
298 // as a hexadecimal number in lowercase with a leading "0x"
299 const char *
300 hex_format(uint32 num)
301 {
302 	static char str[11] = { 0 };
303 	sprintf(str, "0x%.8lx", num);
304 
305 	return str;
306 }
307 
308 // convert passed number to a string of 4 characters
309 // and return that string
310 const char *
311 char_format(uint32 num)
312 {
313 	static char str[5] = { 0 };
314 	uint32 bnum = B_HOST_TO_BENDIAN_INT32(num);
315 	memcpy(str, &bnum, 4);
316 
317 	return str;
318 }
319 
320 // Send information about the currently open image to the
321 // BApplication object so it can send it to the InfoWindow
322 void
323 ImageView::UpdateInfoWindow(const BPath &path, const translator_info &tinfo,
324 	const char *tranname, const char *traninfo, int32 tranversion)
325 {
326 	BMessage msg(M_INFO_WINDOW_TEXT);
327 	BString bstr;
328 
329 	// Bitmap Info
330 	bstr << "Image: " << path.Path() << "\n";
331 	color_space cs = fpbitmap->ColorSpace();
332 	bstr << "Color Space: " << get_color_space_name(cs) << " (" <<
333 		hex_format(static_cast<uint32>(cs)) << ")\n";
334 	bstr << "Dimensions: " << fpbitmap->Bounds().IntegerWidth() + 1 << " x " <<
335 		fpbitmap->Bounds().IntegerHeight() + 1 << "\n";
336 	bstr << "Bytes per Row: " << fpbitmap->BytesPerRow() << "\n";
337 	bstr << "Total Bytes: " << fpbitmap->BitsLength() << "\n";
338 
339 	// Identify Info
340 	bstr << "\nIdentify Info:\n";
341 	bstr << "ID String: " << tinfo.name << "\n";
342 	bstr << "MIME Type: " << tinfo.MIME << "\n";
343 	bstr << "Type: '" << char_format(tinfo.type) << "' (" <<
344 		hex_format(tinfo.type) <<")\n";
345 	bstr << "Translator ID: " << tinfo.translator << "\n";
346 	bstr << "Group: '" << char_format(tinfo.group) << "' (" <<
347 		hex_format(tinfo.group) << ")\n";
348 	bstr << "Quality: " << tinfo.quality << "\n";
349 	bstr << "Capability: " << tinfo.capability << "\n";
350 
351 	// Translator Info
352 	bstr << "\nTranslator Used:\n";
353 	bstr << "Name: " << tranname << "\n";
354 	bstr << "Info: " << traninfo << "\n";
355 	bstr << "Version: " << tranversion << "\n";
356 
357 	msg.AddString("text", bstr);
358 	be_app->PostMessage(&msg);
359 }
360 
361 BTranslatorRoster *
362 ImageView::SelectTranslatorRoster(BTranslatorRoster &roster)
363 {
364 	bool bNoneSelected = true;
365 
366 	InspectorApp *papp;
367 	papp = static_cast<InspectorApp *>(be_app);
368 	if (papp) {
369 		BList *plist = papp->GetTranslatorsList();
370 		if (plist) {
371 			for (int32 i = 0; i < plist->CountItems(); i++) {
372 				BTranslatorItem *pitem =
373 					static_cast<BTranslatorItem *>(plist->ItemAt(i));
374 				if (pitem->IsSelected()) {
375 					bNoneSelected = false;
376 					roster.AddTranslators(pitem->Path());
377 				}
378 			}
379 		}
380 	}
381 
382 	if (bNoneSelected)
383 		return BTranslatorRoster::Default();
384 	else
385 		return &roster;
386 }
387 
388 void
389 ImageView::SetImage(BMessage *pmsg)
390 {
391 	// Replace current image with the image
392 	// specified in the given BMessage
393 
394 	StatusCheck chk;
395 
396 	try {
397 		entry_ref ref;
398 		chk = pmsg->FindRef("refs", &ref);
399 
400 		BFile file(&ref, B_READ_ONLY);
401 		chk = file.InitCheck();
402 
403 		BTranslatorRoster roster, *proster;
404 		proster = SelectTranslatorRoster(roster);
405 		if (!proster)
406 			// throw exception
407 			chk = B_ERROR;
408 
409 		// determine what type the image is
410 		translator_info tinfo;
411 		chk = proster->Identify(&file, NULL, &tinfo, 0, NULL,
412 			B_TRANSLATOR_BITMAP);
413 
414 		// get the name and info about the translator
415 		const char *tranname = NULL, *traninfo = NULL;
416 		int32 tranversion = 0;
417 		chk = proster->GetTranslatorInfo(tinfo.translator, &tranname,
418 			&traninfo, &tranversion);
419 
420 		// perform the actual translation
421 		BBitmapStream outstream;
422 		chk = proster->Translate(&file, &tinfo, NULL, &outstream,
423 			B_TRANSLATOR_BITMAP);
424 		BBitmap *pbitmap = NULL;
425 		chk = outstream.DetachBitmap(&pbitmap);
426 		fpbitmap = pbitmap;
427 		pbitmap = NULL;
428 
429 		// Set the name of the Window to reflect the file name
430 		BWindow *pwin = Window();
431 		BEntry entry(&ref);
432 		BPath path;
433 		if (entry.InitCheck() == B_OK) {
434 			if (path.SetTo(&entry) == B_OK)
435 				pwin->SetTitle(path.Leaf());
436 			else
437 				pwin->SetTitle(IMAGEWINDOW_TITLE);
438 		} else
439 			pwin->SetTitle(IMAGEWINDOW_TITLE);
440 
441 		UpdateInfoWindow(path, tinfo, tranname, traninfo, tranversion);
442 
443 		// Resize parent window and set size limits to
444 		// reflect the size of the new bitmap
445 		float width, height;
446 		BMenuBar *pbar = pwin->KeyMenuBar();
447 		width = fpbitmap->Bounds().Width() + B_V_SCROLL_BAR_WIDTH + (BORDER_WIDTH * 2);
448 		height = fpbitmap->Bounds().Height() +
449 			pbar->Bounds().Height() + B_H_SCROLL_BAR_HEIGHT + (BORDER_HEIGHT * 2) + 1;
450 
451 		BScreen *pscreen = new BScreen(pwin);
452 		BRect rctscreen = pscreen->Frame();
453 		if (width > rctscreen.Width())
454 			width = rctscreen.Width();
455 		if (height > rctscreen.Height())
456 			height = rctscreen.Height();
457 		pwin->SetSizeLimits(B_V_SCROLL_BAR_WIDTH * 4, width,
458 			pbar->Bounds().Height() + (B_H_SCROLL_BAR_HEIGHT * 4) + 1, height);
459 		pwin->SetZoomLimits(width, height);
460 
461 		AdjustScrollBars();
462 
463 		//pwin->Zoom();
464 			// Perform all of the hard work of resizing the
465 			// window while taking into account the size of
466 			// the screen, the tab and borders of the window
467 			//
468 			// HACK: Need to fix case where window un-zooms
469 			// when the window is already the correct size
470 			// for the current image
471 
472 		// repaint view
473 		Invalidate();
474 
475 	} catch (StatusNotOKException) {
476 		BAlert *palert = new BAlert(NULL,
477 			"Sorry, unable to load the image.", "OK");
478 		palert->Go();
479 	}
480 }
481 
482