xref: /haiku/src/preferences/backgrounds/BackgroundImage.cpp (revision cbed190f71b8aff814bf95539c39a1bcfb953ed8)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 //  Classes used for setting up and managing background images
36 //
37 
38 #include "BackgroundImage.h"
39 
40 #include <new>
41 #include <stdlib.h>
42 
43 #include <Bitmap.h>
44 #include <Debug.h>
45 #include <fs_attr.h>
46 #include <Node.h>
47 #include <TranslationKit.h>
48 #include <View.h>
49 #include <Window.h>
50 #include <Message.h>
51 #include <Entry.h>
52 #include <Path.h>
53 #include <Screen.h>
54 #include <String.h>
55 
56 #include "BackgroundsView.h"
57 
58 
59 const char* kBackgroundImageInfo 			= "be:bgndimginfo";
60 const char* kBackgroundImageInfoOffset 		= "be:bgndimginfooffset";
61 // const char* kBackgroundImageInfoTextOutline	= "be:bgndimginfotextoutline";
62 const char* kBackgroundImageInfoTextOutline	= "be:bgndimginfoerasetext";
63 // NOTE: the attribute keeps the old name for backwards compatibility,
64 // just in case some users spend time configuring a few windows with
65 // this feature on or off...
66 const char* kBackgroundImageInfoMode 		= "be:bgndimginfomode";
67 const char* kBackgroundImageInfoWorkspaces 	= "be:bgndimginfoworkspaces";
68 const char* kBackgroundImageInfoPath 		= "be:bgndimginfopath";
69 const char* kBackgroundImageInfoSet 		= "be:bgndimginfoset";
70 const char* kBackgroundImageInfoCacheMode	= "be:bgndimginfocachemode";
71 const char* kBackgroundImageSetPeriod		= "be:bgndimgsetperiod";
72 const char* kBackgroundImageRandomChange	= "be:bgndimgrandomchange";
73 const char* kBackgroundImageCacheMode		= "be:bgndimgcachemode";
74 
75 
76 BackgroundImage*
77 BackgroundImage::GetBackgroundImage(const BNode* node, bool isDesktop,
78 	BackgroundsView* view)
79 {
80 	BackgroundImage* result = new BackgroundImage(node, isDesktop, view);
81 	attr_info info;
82 	if (node->GetAttrInfo(kBackgroundImageInfo, &info) != B_OK)
83 		return result;
84 
85 	BMessage container;
86 	char* buffer = new char [info.size];
87 
88 	status_t error = node->ReadAttr(kBackgroundImageInfo, info.type, 0, buffer,
89 		(size_t)info.size);
90 	if (error == info.size)
91 		error = container.Unflatten(buffer);
92 
93 	delete [] buffer;
94 
95 	if (error != B_OK)
96 		return result;
97 
98 	PRINT_OBJECT(container);
99 
100 	uint32 imageSetPeriod = 0;
101 	uint32 globalCacheMode = 0;
102 	bool randomChange = false;
103 	uint32 maxImageSet = 0;
104 
105 	if (isDesktop) {
106 		container.FindInt32(kBackgroundImageSetPeriod, (int32*)&imageSetPeriod);
107 		container.FindInt32(kBackgroundImageCacheMode,
108 			(int32*)&globalCacheMode);
109 		container.FindBool(kBackgroundImageRandomChange, &randomChange);
110 	}
111 
112 	for (int32 index = 0; ; index++) {
113 		const char* path;
114 		uint32 workspaces = B_ALL_WORKSPACES;
115 		Mode mode = kTiled;
116 		bool textWidgetLabelOutline = false;
117 		BPoint offset;
118 		uint32 imageSet = 0;
119 		uint32 cacheMode = 0;
120 		int32 imageIndex = -1;
121 
122 		if (container.FindString(kBackgroundImageInfoPath, index, &path)
123 			== B_OK) {
124 			if (strcmp(path, "")) {
125 				BPath bpath(path);
126 				imageIndex = view->AddImage(bpath);
127 				if (imageIndex < 0) {
128 					imageIndex = -imageIndex - 1;
129 				}
130 			}
131 		} else
132 			break;
133 
134 		container.FindInt32(kBackgroundImageInfoWorkspaces, index,
135 			(int32*)&workspaces);
136 		container.FindInt32(kBackgroundImageInfoMode, index, (int32*)&mode);
137 		container.FindBool(kBackgroundImageInfoTextOutline, index,
138 			&textWidgetLabelOutline);
139 		container.FindPoint(kBackgroundImageInfoOffset, index, &offset);
140 
141 		if (isDesktop) {
142 			container.FindInt32(kBackgroundImageInfoSet, index,
143 				(int32*)&imageSet);
144 			container.FindInt32(kBackgroundImageInfoCacheMode, index,
145 				(int32*)&cacheMode);
146 		}
147 
148 		BackgroundImage::BackgroundImageInfo* imageInfo = new
149 			BackgroundImage::BackgroundImageInfo(workspaces, imageIndex,
150 				mode, offset, textWidgetLabelOutline, imageSet, cacheMode);
151 
152 		// imageInfo->UnloadBitmap(globalCacheMode);
153 
154 		if (imageSet > maxImageSet)
155 			maxImageSet = imageSet;
156 
157 		result->Add(imageInfo);
158 	}
159 
160 	if (result) {
161 		result->fImageSetCount = maxImageSet + 1;
162 		result->fRandomChange = randomChange;
163 		result->fImageSetPeriod = imageSetPeriod;
164 		result->fCacheMode = globalCacheMode;
165 		if (result->fImageSetCount > 1)
166 			result->fShowingImageSet = random() % result->fImageSetCount;
167 	}
168 
169 	return result;
170 }
171 
172 
173 BackgroundImage::BackgroundImageInfo::BackgroundImageInfo(uint32 workspaces,
174 	int32 imageIndex, Mode mode, BPoint offset, bool textWidgetLabelOutline,
175 	uint32 imageSet, uint32 cacheMode)
176 	:
177 	fWorkspace(workspaces),
178 	fImageIndex(imageIndex),
179 	fMode(mode),
180 	fOffset(offset),
181 	fTextWidgetLabelOutline(textWidgetLabelOutline),
182 	fImageSet(imageSet),
183 	fCacheMode(cacheMode)
184 {
185 }
186 
187 
188 BackgroundImage::BackgroundImageInfo::~BackgroundImageInfo()
189 {
190 }
191 
192 
193 //	#pragma mark -
194 
195 
196 BackgroundImage::BackgroundImage(const BNode* node, bool desktop,
197 	BackgroundsView* view)
198 	:
199 	fIsDesktop(desktop),
200 	fDefinedByNode(*node),
201 	fView(NULL),
202 	fBackgroundsView(view),
203 	fShowingBitmap(NULL),
204 	fBitmapForWorkspaceList(1, true),
205 	fImageSetPeriod(0),
206 	fShowingImageSet(0),
207 	fImageSetCount(0),
208 	fCacheMode(0),
209 	fRandomChange(false)
210 {
211 }
212 
213 
214 BackgroundImage::~BackgroundImage()
215 {
216 }
217 
218 
219 void
220 BackgroundImage::Add(BackgroundImageInfo* info)
221 {
222 	fBitmapForWorkspaceList.AddItem(info);
223 }
224 
225 
226 void
227 BackgroundImage::Remove(BackgroundImageInfo* info)
228 {
229 	fBitmapForWorkspaceList.RemoveItem(info);
230 }
231 
232 
233 void
234 BackgroundImage::RemoveAll()
235 {
236 	for (int32 index = 0; index < fBitmapForWorkspaceList.CountItems();) {
237 		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
238 		if (info->fImageSet != fShowingImageSet)
239 			index++;
240 		else
241 			fBitmapForWorkspaceList.RemoveItemAt(index);
242 	}
243 }
244 
245 
246 void
247 BackgroundImage::Show(BView* view, int32 workspace)
248 {
249 	fView = view;
250 
251 	BackgroundImageInfo* info = ImageInfoForWorkspace(workspace);
252 	if (info) {
253 		/*BPoseView* poseView = dynamic_cast<BPoseView*>(fView);
254 		if (poseView)
255 			poseView
256 				->SetEraseWidgetTextBackground(info->fTextWidgetLabelOutline);*/
257 		Show(info, fView);
258 	}
259 }
260 
261 
262 void
263 BackgroundImage::Show(BackgroundImageInfo* info, BView* view)
264 {
265 	BBitmap* bitmap
266 		= fBackgroundsView->GetImage(info->fImageIndex)->GetBitmap();
267 
268 	if (!bitmap)
269 		return;
270 
271 	BRect viewBounds(view->Bounds());
272 
273 	display_mode mode;
274 	BScreen().GetMode(&mode);
275 	float x_ratio = viewBounds.Width() / mode.virtual_width;
276 	float y_ratio = viewBounds.Height() / mode.virtual_height;
277 
278 	BRect bitmapBounds(bitmap->Bounds());
279 	BRect destinationBitmapBounds(bitmapBounds);
280 	destinationBitmapBounds.right *= x_ratio;
281 	destinationBitmapBounds.bottom *= y_ratio;
282 	BPoint offset(info->fOffset);
283 	offset.x *= x_ratio;
284 	offset.y *= y_ratio;
285 
286 	uint32 tile = 0;
287 	uint32 followFlags = B_FOLLOW_TOP | B_FOLLOW_LEFT;
288 
289 	// figure out the display mode and the destination bounds for the bitmap
290 	switch (info->fMode) {
291 		case kCentered:
292 			if (fIsDesktop) {
293 				destinationBitmapBounds.OffsetBy(
294 					(viewBounds.Width() - destinationBitmapBounds.Width()) / 2,
295 					(viewBounds.Height() - destinationBitmapBounds.Height())
296 					/ 2);
297 				break;
298 			}
299 			// else fall thru
300 		case kScaledToFit:
301 			if (fIsDesktop) {
302 				if (BRectRatio(destinationBitmapBounds)
303 					>= BRectRatio(viewBounds)) {
304 					float overlap = BRectHorizontalOverlap(viewBounds,
305 						destinationBitmapBounds);
306 					destinationBitmapBounds.Set(-overlap, 0,
307 						viewBounds.Width() + overlap, viewBounds.Height());
308 				} else {
309 					float overlap = BRectVerticalOverlap(viewBounds,
310 						destinationBitmapBounds);
311 					destinationBitmapBounds.Set(0, -overlap,
312 						viewBounds.Width(), viewBounds.Height() + overlap);
313 				}
314 				followFlags = B_FOLLOW_ALL;
315 				break;
316 			}
317 			// else fall thru
318 		case kAtOffset:
319 		{
320 			destinationBitmapBounds.OffsetTo(offset);
321 			break;
322 		}
323 		case kTiled:
324 			// Original Backgrounds Preferences center the tiled paper
325 			// but Tracker doesn't do that
326 			//if (fIsDesktop) {
327 			destinationBitmapBounds.OffsetBy(
328 				(viewBounds.Width() - destinationBitmapBounds.Width()) / 2,
329 				(viewBounds.Height() - destinationBitmapBounds.Height()) / 2);
330 			//}
331 			tile = B_TILE_BITMAP;
332 			break;
333 	}
334 
335 	// switch to the bitmap and force a redraw
336 	view->SetViewBitmap(bitmap, bitmapBounds, destinationBitmapBounds,
337 		followFlags, tile);
338 	view->Invalidate();
339 
340 	/*if (fShowingBitmap != info) {
341 		if (fShowingBitmap)
342 			fShowingBitmap->UnloadBitmap(fCacheMode);
343 		fShowingBitmap = info;
344 	}*/
345 }
346 
347 
348 float
349 BackgroundImage::BRectRatio(BRect rect)
350 {
351 	return rect.Width() / rect.Height();
352 }
353 
354 
355 float
356 BackgroundImage::BRectHorizontalOverlap(BRect hostRect, BRect resizedRect)
357 {
358 	return ((hostRect.Height() / resizedRect.Height() * resizedRect.Width())
359 		- hostRect.Width()) / 2;
360 }
361 
362 
363 float
364 BackgroundImage::BRectVerticalOverlap(BRect hostRect, BRect resizedRect)
365 {
366 	return ((hostRect.Width() / resizedRect.Width() * resizedRect.Height())
367 		- hostRect.Height()) / 2;
368 }
369 
370 
371 void
372 BackgroundImage::Remove()
373 {
374 	if (fShowingBitmap) {
375 		fView->ClearViewBitmap();
376 		fView->Invalidate();
377 		/*BPoseView* poseView = dynamic_cast<BPoseView*>(fView);
378 		// make sure text widgets draw the default way, erasing their background
379 		if (poseView)
380 			poseView->SetEraseWidgetTextBackground(true);*/
381 	}
382 	fShowingBitmap = NULL;
383 }
384 
385 
386 BackgroundImage::BackgroundImageInfo*
387 BackgroundImage::ImageInfoForWorkspace(int32 workspace) const
388 {
389 	uint32 workspaceMask = 1;
390 
391 	for (; workspace; workspace--)
392 		workspaceMask *= 2;
393 
394 	int32 count = fBitmapForWorkspaceList.CountItems();
395 
396 	// do a simple lookup for the most likely candidate bitmap -
397 	// pick the imageInfo that is only defined for this workspace over one
398 	// that supports multiple workspaces
399 	BackgroundImageInfo* result = NULL;
400 	for (int32 index = 0; index < count; index++) {
401 		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
402 		if (info->fImageSet != fShowingImageSet)
403 			continue;
404 
405 		if (fIsDesktop) {
406 			if (info->fWorkspace == workspaceMask)
407 				return info;
408 
409 			if (info->fWorkspace & workspaceMask)
410 				result = info;
411 		} else
412 			return info;
413 	}
414 	return result;
415 }
416 
417 
418 void
419 BackgroundImage::WorkspaceActivated(BView* view, int32 workspace, bool state)
420 {
421 	if (!fIsDesktop) {
422 		// we only care for desktop bitmaps
423 		return;
424 	}
425 
426 	if (!state) {
427 		// we only care comming into a new workspace, not leaving one
428 		return;
429 	}
430 
431 	BackgroundImageInfo* info = ImageInfoForWorkspace(workspace);
432 	if (info != fShowingBitmap) {
433 		if (info)
434 			Show(info, view);
435 		else {
436 			/*if (BPoseView* poseView = dynamic_cast<BPoseView*>(view))
437 				poseView->SetEraseWidgetTextBackground(true);*/
438 			view->ClearViewBitmap();
439 			view->Invalidate();
440 		}
441 		fShowingBitmap = info;
442 	}
443 }
444 
445 
446 void
447 BackgroundImage::ScreenChanged(BRect, color_space)
448 {
449 	if (!fIsDesktop || !fShowingBitmap)
450 		return;
451 
452 	/*if (fShowingBitmap->fMode == kCentered) {
453 		BRect viewBounds(fView->Bounds());
454 		BRect bitmapBounds(fShowingBitmap->fBitmap->Bounds());
455 		BRect destinationBitmapBounds(bitmapBounds);
456 		destinationBitmapBounds.OffsetBy(
457 			(viewBounds.Width() - bitmapBounds.Width()) / 2,
458 			(viewBounds.Height() - bitmapBounds.Height()) / 2);
459 
460 		fView->SetViewBitmap(fShowingBitmap->fBitmap, bitmapBounds,
461 			destinationBitmapBounds, B_FOLLOW_NONE, 0);
462 		fView->Invalidate();
463 	}*/
464 }
465 
466 
467 status_t
468 BackgroundImage::SetBackgroundImage(BNode* node)
469 {
470 	status_t err;
471 	BMessage container;
472 	int32 count = fBitmapForWorkspaceList.CountItems();
473 
474 	for (int32 index = 0; index < count; index++) {
475 		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
476 
477 		container.AddBool(kBackgroundImageInfoTextOutline,
478 			info->fTextWidgetLabelOutline);
479 		if (fBackgroundsView->GetImage(info->fImageIndex) != NULL) {
480 			container.AddString(kBackgroundImageInfoPath,
481 				fBackgroundsView
482 					->GetImage(info->fImageIndex)->GetPath().Path());
483 		} else
484 			container.AddString(kBackgroundImageInfoPath, "");
485 
486 		container.AddInt32(kBackgroundImageInfoWorkspaces, info->fWorkspace);
487 		container.AddPoint(kBackgroundImageInfoOffset, info->fOffset);
488 		container.AddInt32(kBackgroundImageInfoMode, info->fMode);
489 
490 		if (fIsDesktop)
491 			container.AddInt32(kBackgroundImageInfoSet, info->fImageSet);
492 	}
493 
494 	PRINT_OBJECT(container);
495 
496 	size_t flattenedSize = container.FlattenedSize();
497 	char* buffer = new(std::nothrow) char[flattenedSize];
498 	if (buffer == NULL)
499 		return B_NO_MEMORY;
500 
501 	if ((err = container.Flatten(buffer, flattenedSize)) != B_OK) {
502 		delete[] buffer;
503 		return err;
504 	}
505 
506 	ssize_t size = node->WriteAttr(kBackgroundImageInfo, B_MESSAGE_TYPE,
507 		0, buffer, flattenedSize);
508 
509 	delete[] buffer;
510 
511 	if (size < B_OK)
512 		return size;
513 	if ((size_t)size != flattenedSize)
514 		return B_ERROR;
515 
516 	return B_OK;
517 }
518 
519 
520 /*BackgroundImage*
521 BackgroundImage::Refresh(BackgroundImage* oldBackgroundImage,
522 	const BNode* fromNode, bool desktop, BPoseView* poseView)
523 {
524 	if (oldBackgroundImage) {
525 		oldBackgroundImage->Remove();
526 		delete oldBackgroundImage;
527 	}
528 
529 	BackgroundImage* result = GetBackgroundImage(fromNode, desktop);
530 	if (result && poseView->ViewMode() != kListMode)
531 		result->Show(poseView, current_workspace());
532 	return result;
533 }
534 
535 
536 void
537 BackgroundImage::ChangeImageSet(BPoseView* poseView)
538 {
539 	if (fRandomChange) {
540 		if (fImageSetCount > 1) {
541 			uint32 oldShowingImageSet = fShowingImageSet;
542 			while (oldShowingImageSet == fShowingImageSet)
543 				fShowingImageSet = random()%fImageSetCount;
544 		} else
545 			fShowingImageSet = 0;
546 	} else {
547 		fShowingImageSet++;
548 		if (fShowingImageSet > fImageSetCount - 1)
549 			fShowingImageSet = 0;
550 	}
551 
552 	this->Show(poseView, current_workspace());
553 }*/
554 
555 
556 //	#pragma mark -
557 
558 
559 Image::Image(BPath path)
560 	:
561 	fBitmap(NULL),
562 	fPath(path)
563 {
564 	name = path.Leaf();
565 }
566 
567 
568 Image::~Image()
569 {
570 	delete fBitmap;
571 }
572 
573 
574 BBitmap*
575 Image::GetBitmap()
576 {
577 	if (!fBitmap)
578 		fBitmap = BTranslationUtils::GetBitmap(fPath.Path());
579 
580 	return fBitmap;
581 }
582 
583