xref: /haiku/src/apps/showimage/ShowImageView.cpp (revision 51978af14a173e7fae0563b562be5603bc652aeb)
1 /*****************************************************************************/
2 // ShowImageView
3 // Written by Fernando Francisco de Oliveira, Michael Wilber
4 //
5 // ShowImageView.cpp
6 //
7 //
8 // Copyright (c) 2003 OpenBeOS Project
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining a
11 // copy of this software and associated documentation files (the "Software"),
12 // to deal in the Software without restriction, including without limitation
13 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 // and/or sell copies of the Software, and to permit persons to whom the
15 // Software is furnished to do so, subject to the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be included
18 // in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 // DEALINGS IN THE SOFTWARE.
27 /*****************************************************************************/
28 
29 #include <stdio.h>
30 #include <Message.h>
31 #include <ScrollBar.h>
32 #include <StopWatch.h>
33 #include <Alert.h>
34 #include <MenuBar.h>
35 #include <MenuItem.h>
36 #include <File.h>
37 #include <Bitmap.h>
38 #include <TranslatorRoster.h>
39 #include <BitmapStream.h>
40 #include <Rect.h>
41 #include <SupportDefs.h>
42 
43 #include "ShowImageConstants.h"
44 #include "ShowImageView.h"
45 
46 #ifndef min
47 #define min(a,b) ((a)>(b)?(b):(a))
48 #endif
49 #ifndef max
50 #define max(a,b) ((a)>(b)?(a):(b))
51 #endif
52 
53 #define BORDER_WIDTH 16
54 #define BORDER_HEIGHT 16
55 #define PEN_SIZE 1.0f
56 const rgb_color kborderColor = { 0, 0, 0, 255 };
57 
58 ShowImageView::ShowImageView(BRect rect, const char *name, uint32 resizingMode,
59 	uint32 flags)
60 	: BView(rect, name, resizingMode, flags)
61 {
62 	fpbitmap = NULL;
63 	fdocumentIndex = 1;
64 	fdocumentCount = 1;
65 	fbhasSelection = false;
66 
67 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
68 	SetHighColor(kborderColor);
69 	SetPenSize(PEN_SIZE);
70 }
71 
72 ShowImageView::~ShowImageView()
73 {
74 	delete fpbitmap;
75 	fpbitmap = NULL;
76 }
77 
78 void
79 ShowImageView::SetImage(const entry_ref *pref)
80 {
81 	ClearViewBitmap();
82 	delete fpbitmap;
83 	fpbitmap = NULL;
84 
85 	entry_ref ref;
86 	if (!pref)
87 		ref = fcurrentRef;
88 	else
89 		ref = *pref;
90 
91 	BTranslatorRoster *proster = BTranslatorRoster::Default();
92 	if (!proster)
93 		return;
94 	BFile file(&ref, B_READ_ONLY);
95 	translator_info info;
96 	memset(&info, 0, sizeof(translator_info));
97 	BMessage ioExtension;
98 	if (ref != fcurrentRef)
99 		// if new image, reset to first document
100 		fdocumentIndex = 1;
101 	if (ioExtension.AddInt32("/documentIndex", fdocumentIndex) != B_OK)
102 		return;
103 	if (proster->Identify(&file, &ioExtension, &info, 0, NULL,
104 		B_TRANSLATOR_BITMAP) != B_OK)
105 		return;
106 
107 	// Translate image data and create a new ShowImage window
108 	BBitmapStream outstream;
109 	if (proster->Translate(&file, &info, &ioExtension, &outstream,
110 		B_TRANSLATOR_BITMAP) != B_OK)
111 		return;
112 	if (outstream.DetachBitmap(&fpbitmap) != B_OK)
113 		return;
114 	fcurrentRef = ref;
115 
116 	// get the number of documents (pages) if it has been supplied
117 	int32 documentCount = 0;
118 	if (ioExtension.FindInt32("/documentCount", &documentCount) == B_OK &&
119 		documentCount > 0)
120 		fdocumentCount = documentCount;
121 	else
122 		fdocumentCount = 1;
123 
124 	// send message to parent about new image
125 	BMessage msg(MSG_UPDATE_STATUS);
126 	msg.AddString("status", info.name);
127 	BMessenger msgr(Window());
128 	msgr.SendMessage(&msg);
129 
130 	SetViewBitmap(fpbitmap, fpbitmap->Bounds(),
131 		BRect(BORDER_WIDTH, BORDER_HEIGHT,
132 			fpbitmap->Bounds().Width() + BORDER_WIDTH,
133 			fpbitmap->Bounds().Height() + BORDER_HEIGHT),
134 		B_FOLLOW_TOP | B_FOLLOW_LEFT, 0
135 	);
136 
137 	FixupScrollBars();
138 	Invalidate();
139 }
140 
141 BBitmap *
142 ShowImageView::GetBitmap()
143 {
144 	return fpbitmap;
145 }
146 
147 void
148 ShowImageView::AttachedToWindow()
149 {
150 	FixupScrollBars();
151 }
152 
153 void
154 ShowImageView::Draw(BRect updateRect)
155 {
156 	if (fpbitmap) {
157 		// Draw black rectangle around image
158 		StrokeRect(
159 			BRect(BORDER_WIDTH - PEN_SIZE,
160 				BORDER_HEIGHT - PEN_SIZE,
161 				fpbitmap->Bounds().Width() + BORDER_WIDTH + PEN_SIZE,
162 				fpbitmap->Bounds().Height() + BORDER_HEIGHT + PEN_SIZE));
163 
164 		if (fbhasSelection)
165 			DrawSelectionBox(fselectionRect);
166 	}
167 }
168 
169 void
170 ShowImageView::DrawSelectionBox(BRect &rect)
171 {
172 	// TODO: Make my own pattern to mimic the
173 	// marching ants in Be's ShowImage?
174 
175 	StrokeRect(rect, B_MIXED_COLORS);
176 	Sync();
177 }
178 
179 void
180 ShowImageView::ClearSelectionBox(BRect &rect)
181 {
182 	BRect bitmapRect = rect;
183 	if (!bitmapRect.IsValid())
184 		printf("Invalid Rect\n");
185 
186 	bitmapRect.OffsetBy(-(BORDER_WIDTH), -(BORDER_HEIGHT));
187 	DrawBitmapAsync(fpbitmap, bitmapRect, rect);
188 		// don't draw the bitmap immediately, more drawing
189 		// almost always comes after this function is used
190 }
191 
192 void
193 ShowImageView::FrameResized(float /* width */, float /* height */)
194 {
195 	FixupScrollBars();
196 }
197 
198 void
199 ShowImageView::ConstrainToImage(BPoint &point)
200 {
201 	point.ConstrainTo(
202 		BRect(
203 			BORDER_WIDTH, BORDER_HEIGHT,
204 			fpbitmap->Bounds().Width() + BORDER_WIDTH,
205 			fpbitmap->Bounds().Height() + BORDER_HEIGHT
206 		)
207 	);
208 }
209 
210 void
211 ShowImageView::MouseDown(BPoint point)
212 {
213 	// TODO: Need to handle the case where user clicks
214 	// inside an existing selection and starts a drag operation
215 
216 	if (fbhasSelection)
217 		ClearSelectionBox(fselectionRect);
218 
219 	fbhasSelection = false;
220 
221 	if (!fpbitmap) {
222 		// Can't select anything if there is no image
223 		Invalidate();
224 		return;
225 	}
226 
227 	BPoint firstPoint, secondPoint;
228 	firstPoint = point;
229 	ConstrainToImage(firstPoint);
230 	secondPoint = firstPoint;
231 
232 	BRect curSel, lastSel;
233 
234 	BMessage *pmsg = Window()->CurrentMessage();
235 	uint32 buttons = static_cast<uint32>(pmsg->FindInt32("buttons"));
236 	bool bfirst = true;
237 	// While any combination of mouse buttons is down,
238 	// allow the user to size their selection. When all
239 	// mouse buttons are up, the loop ends, and the selection
240 	// has been made.
241 	while (buttons) {
242 		ConstrainToImage(secondPoint);
243 
244 		// make a BRect that passes IsValid()
245 		curSel.left = min(firstPoint.x, secondPoint.x);
246 		curSel.top = min(firstPoint.y, secondPoint.y);
247 		curSel.right = max(firstPoint.x, secondPoint.x);
248 		curSel.bottom = max(firstPoint.y, secondPoint.y);
249 
250 		if (!bfirst && curSel != lastSel)
251 			ClearSelectionBox(lastSel);
252 		if (curSel != lastSel)
253 			DrawSelectionBox(curSel);
254 
255 		snooze(25 * 1000);
256 		lastSel = curSel;
257 		GetMouse(&secondPoint, &buttons);
258 		bfirst = false;
259 	}
260 
261 	if (curSel.IntegerWidth() < 2 && curSel.IntegerHeight() < 2) {
262 		// The user must select at least 2 pixels
263 		fbhasSelection = false;
264 		Invalidate();
265 	} else {
266 		fselectionRect = curSel;
267 		fbhasSelection = true;
268 	}
269 }
270 
271 void
272 ShowImageView::MouseMoved(BPoint point, uint32 state, const BMessage *pmsg)
273 {
274 }
275 
276 void
277 ShowImageView::MouseUp(BPoint point)
278 {
279 }
280 
281 void
282 ShowImageView::MessageReceived(BMessage *pmsg)
283 {
284 	switch (pmsg->what) {
285 		default:
286 			BView::MessageReceived(pmsg);
287 			break;
288 	}
289 }
290 
291 void
292 ShowImageView::FixupScrollBars()
293 {
294 	BRect rctview = Bounds(), rctbitmap(0, 0, 0, 0);
295 	if (fpbitmap)
296 		rctbitmap = fpbitmap->Bounds();
297 
298 	float prop, range;
299 	BScrollBar *psb = ScrollBar(B_HORIZONTAL);
300 	if (psb) {
301 		range = rctbitmap.Width() + (BORDER_WIDTH * 2) - rctview.Width();
302 		if (range < 0) range = 0;
303 		prop = rctview.Width() / (rctbitmap.Width() + (BORDER_WIDTH * 2));
304 		if (prop > 1.0f) prop = 1.0f;
305 		psb->SetRange(0, range);
306 		psb->SetProportion(prop);
307 		psb->SetSteps(10, 100);
308 	}
309 
310 	psb = ScrollBar(B_VERTICAL);
311 	if (psb) {
312 		range = rctbitmap.Height() + (BORDER_HEIGHT * 2) - rctview.Height();
313 		if (range < 0) range = 0;
314 		prop = rctview.Height() / (rctbitmap.Height() + (BORDER_HEIGHT * 2));
315 		if (prop > 1.0f) prop = 1.0f;
316 		psb->SetRange(0, range);
317 		psb->SetProportion(prop);
318 		psb->SetSteps(10, 100);
319 	}
320 }
321 
322 int32
323 ShowImageView::CurrentPage()
324 {
325 	return fdocumentIndex;
326 }
327 
328 int32
329 ShowImageView::PageCount()
330 {
331 	return fdocumentCount;
332 }
333 
334 void
335 ShowImageView::FirstPage()
336 {
337 	if (fdocumentIndex != 1) {
338 		fdocumentIndex = 1;
339 		SetImage(NULL);
340 	}
341 }
342 
343 void
344 ShowImageView::LastPage()
345 {
346 	if (fdocumentIndex != fdocumentCount) {
347 		fdocumentIndex = fdocumentCount;
348 		SetImage(NULL);
349 	}
350 }
351 
352 void
353 ShowImageView::NextPage()
354 {
355 	if (fdocumentIndex < fdocumentCount) {
356 		fdocumentIndex++;
357 		SetImage(NULL);
358 	}
359 }
360 
361 void
362 ShowImageView::PrevPage()
363 {
364 	if (fdocumentIndex > 1) {
365 		fdocumentIndex--;
366 		SetImage(NULL);
367 	}
368 }
369