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