xref: /haiku/src/preferences/backgrounds/BackgroundImage.cpp (revision 02ab820d0e26d680b9d158024b1cde1bc5ded6c0)
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*
GetBackgroundImage(const BNode * node,bool isDesktop,BackgroundsView * view)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 
BackgroundImageInfo(uint32 workspaces,int32 imageIndex,Mode mode,BPoint offset,bool textWidgetLabelOutline,uint32 imageSet,uint32 cacheMode)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 
~BackgroundImageInfo()188 BackgroundImage::BackgroundImageInfo::~BackgroundImageInfo()
189 {
190 }
191 
192 
193 //	#pragma mark -
194 
195 
BackgroundImage(const BNode * node,bool desktop,BackgroundsView * view)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 
~BackgroundImage()214 BackgroundImage::~BackgroundImage()
215 {
216 }
217 
218 
219 void
Add(BackgroundImageInfo * info)220 BackgroundImage::Add(BackgroundImageInfo* info)
221 {
222 	fBitmapForWorkspaceList.AddItem(info);
223 }
224 
225 
226 void
Remove(BackgroundImageInfo * info)227 BackgroundImage::Remove(BackgroundImageInfo* info)
228 {
229 	fBitmapForWorkspaceList.RemoveItem(info);
230 }
231 
232 
233 void
RemoveAll()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
Show(BView * view,int32 workspace)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
Show(BackgroundImageInfo * info,BView * view)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 options = 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 				options |= B_FILTER_BITMAP_BILINEAR;
316 				break;
317 			}
318 			// else fall thru
319 		case kAtOffset:
320 		{
321 			destinationBitmapBounds.OffsetTo(offset);
322 			break;
323 		}
324 		case kTiled:
325 			// Original Backgrounds Preferences center the tiled paper
326 			// but Tracker doesn't do that
327 			//if (fIsDesktop) {
328 			destinationBitmapBounds.OffsetBy(
329 				(viewBounds.Width() - destinationBitmapBounds.Width()) / 2,
330 				(viewBounds.Height() - destinationBitmapBounds.Height()) / 2);
331 			//}
332 			options |= B_TILE_BITMAP;
333 			break;
334 	}
335 
336 	// switch to the bitmap and force a redraw
337 	view->SetViewBitmap(bitmap, bitmapBounds, destinationBitmapBounds,
338 		followFlags, options);
339 	view->Invalidate();
340 
341 	/*if (fShowingBitmap != info) {
342 		if (fShowingBitmap)
343 			fShowingBitmap->UnloadBitmap(fCacheMode);
344 		fShowingBitmap = info;
345 	}*/
346 }
347 
348 
349 float
BRectRatio(BRect rect)350 BackgroundImage::BRectRatio(BRect rect)
351 {
352 	return rect.Width() / rect.Height();
353 }
354 
355 
356 float
BRectHorizontalOverlap(BRect hostRect,BRect resizedRect)357 BackgroundImage::BRectHorizontalOverlap(BRect hostRect, BRect resizedRect)
358 {
359 	return ((hostRect.Height() / resizedRect.Height() * resizedRect.Width())
360 		- hostRect.Width()) / 2;
361 }
362 
363 
364 float
BRectVerticalOverlap(BRect hostRect,BRect resizedRect)365 BackgroundImage::BRectVerticalOverlap(BRect hostRect, BRect resizedRect)
366 {
367 	return ((hostRect.Width() / resizedRect.Width() * resizedRect.Height())
368 		- hostRect.Height()) / 2;
369 }
370 
371 
372 void
Remove()373 BackgroundImage::Remove()
374 {
375 	if (fShowingBitmap) {
376 		fView->ClearViewBitmap();
377 		fView->Invalidate();
378 		/*BPoseView* poseView = dynamic_cast<BPoseView*>(fView);
379 		// make sure text widgets draw the default way, erasing their background
380 		if (poseView)
381 			poseView->SetEraseWidgetTextBackground(true);*/
382 	}
383 	fShowingBitmap = NULL;
384 }
385 
386 
387 BackgroundImage::BackgroundImageInfo*
ImageInfoForWorkspace(int32 workspace) const388 BackgroundImage::ImageInfoForWorkspace(int32 workspace) const
389 {
390 	uint32 workspaceMask = 1;
391 
392 	for (; workspace; workspace--)
393 		workspaceMask *= 2;
394 
395 	int32 count = fBitmapForWorkspaceList.CountItems();
396 
397 	// do a simple lookup for the most likely candidate bitmap -
398 	// pick the imageInfo that is only defined for this workspace over one
399 	// that supports multiple workspaces
400 	BackgroundImageInfo* result = NULL;
401 	for (int32 index = 0; index < count; index++) {
402 		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
403 		if (info->fImageSet != fShowingImageSet)
404 			continue;
405 
406 		if (fIsDesktop) {
407 			if (info->fWorkspace == workspaceMask)
408 				return info;
409 
410 			if (info->fWorkspace & workspaceMask)
411 				result = info;
412 		} else
413 			return info;
414 	}
415 	return result;
416 }
417 
418 
419 void
WorkspaceActivated(BView * view,int32 workspace,bool state)420 BackgroundImage::WorkspaceActivated(BView* view, int32 workspace, bool state)
421 {
422 	if (!fIsDesktop) {
423 		// we only care for desktop bitmaps
424 		return;
425 	}
426 
427 	if (!state) {
428 		// we only care comming into a new workspace, not leaving one
429 		return;
430 	}
431 
432 	BackgroundImageInfo* info = ImageInfoForWorkspace(workspace);
433 	if (info != fShowingBitmap) {
434 		if (info)
435 			Show(info, view);
436 		else {
437 			/*if (BPoseView* poseView = dynamic_cast<BPoseView*>(view))
438 				poseView->SetEraseWidgetTextBackground(true);*/
439 			view->ClearViewBitmap();
440 			view->Invalidate();
441 		}
442 		fShowingBitmap = info;
443 	}
444 }
445 
446 
447 void
ScreenChanged(BRect,color_space)448 BackgroundImage::ScreenChanged(BRect, color_space)
449 {
450 	if (!fIsDesktop || !fShowingBitmap)
451 		return;
452 
453 	/*if (fShowingBitmap->fMode == kCentered) {
454 		BRect viewBounds(fView->Bounds());
455 		BRect bitmapBounds(fShowingBitmap->fBitmap->Bounds());
456 		BRect destinationBitmapBounds(bitmapBounds);
457 		destinationBitmapBounds.OffsetBy(
458 			(viewBounds.Width() - bitmapBounds.Width()) / 2,
459 			(viewBounds.Height() - bitmapBounds.Height()) / 2);
460 
461 		fView->SetViewBitmap(fShowingBitmap->fBitmap, bitmapBounds,
462 			destinationBitmapBounds, B_FOLLOW_NONE, 0);
463 		fView->Invalidate();
464 	}*/
465 }
466 
467 
468 status_t
SetBackgroundImage(BNode * node)469 BackgroundImage::SetBackgroundImage(BNode* node)
470 {
471 	status_t err;
472 	BMessage container;
473 	int32 count = fBitmapForWorkspaceList.CountItems();
474 
475 	for (int32 index = 0; index < count; index++) {
476 		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
477 
478 		container.AddBool(kBackgroundImageInfoTextOutline,
479 			info->fTextWidgetLabelOutline);
480 		if (fBackgroundsView->GetImage(info->fImageIndex) != NULL) {
481 			container.AddString(kBackgroundImageInfoPath,
482 				fBackgroundsView
483 					->GetImage(info->fImageIndex)->GetPath().Path());
484 		} else
485 			container.AddString(kBackgroundImageInfoPath, "");
486 
487 		container.AddInt32(kBackgroundImageInfoWorkspaces, info->fWorkspace);
488 		container.AddPoint(kBackgroundImageInfoOffset, info->fOffset);
489 		container.AddInt32(kBackgroundImageInfoMode, info->fMode);
490 
491 		if (fIsDesktop)
492 			container.AddInt32(kBackgroundImageInfoSet, info->fImageSet);
493 	}
494 
495 	PRINT_OBJECT(container);
496 
497 	ssize_t flattenedSize = container.FlattenedSize();
498 	if (flattenedSize < B_OK)
499 		return flattenedSize;
500 
501 	char* buffer = new(std::nothrow) char[flattenedSize];
502 	if (buffer == NULL)
503 		return B_NO_MEMORY;
504 
505 	if ((err = container.Flatten(buffer, flattenedSize)) != B_OK) {
506 		delete[] buffer;
507 		return err;
508 	}
509 
510 	ssize_t size = node->WriteAttr(kBackgroundImageInfo, B_MESSAGE_TYPE,
511 		0, buffer, flattenedSize);
512 
513 	delete[] buffer;
514 
515 	if (size < B_OK)
516 		return size;
517 	if (size != flattenedSize)
518 		return B_ERROR;
519 
520 	return B_OK;
521 }
522 
523 
524 /*BackgroundImage*
525 BackgroundImage::Refresh(BackgroundImage* oldBackgroundImage,
526 	const BNode* fromNode, bool desktop, BPoseView* poseView)
527 {
528 	if (oldBackgroundImage) {
529 		oldBackgroundImage->Remove();
530 		delete oldBackgroundImage;
531 	}
532 
533 	BackgroundImage* result = GetBackgroundImage(fromNode, desktop);
534 	if (result && poseView->ViewMode() != kListMode)
535 		result->Show(poseView, current_workspace());
536 	return result;
537 }
538 
539 
540 void
541 BackgroundImage::ChangeImageSet(BPoseView* poseView)
542 {
543 	if (fRandomChange) {
544 		if (fImageSetCount > 1) {
545 			uint32 oldShowingImageSet = fShowingImageSet;
546 			while (oldShowingImageSet == fShowingImageSet)
547 				fShowingImageSet = random()%fImageSetCount;
548 		} else
549 			fShowingImageSet = 0;
550 	} else {
551 		fShowingImageSet++;
552 		if (fShowingImageSet > fImageSetCount - 1)
553 			fShowingImageSet = 0;
554 	}
555 
556 	this->Show(poseView, current_workspace());
557 }*/
558 
559 
560 //	#pragma mark -
561 
562 
Image(BPath path)563 Image::Image(BPath path)
564 	:
565 	fBitmap(NULL),
566 	fPath(path)
567 {
568 	const int32 kMaxNameChars = 40;
569 	fName = path.Leaf();
570 	int extra = fName.CountChars() - kMaxNameChars;
571 	if (extra > 0) {
572 		BString extension;
573 		int offset = fName.FindLast('.');
574 		if (offset > 0)
575 			fName.CopyInto(extension, ++offset, -1);
576 		fName.TruncateChars(kMaxNameChars) << B_UTF8_ELLIPSIS << extension;
577 	}
578 }
579 
580 
~Image()581 Image::~Image()
582 {
583 	delete fBitmap;
584 }
585 
586 
587 BBitmap*
GetBitmap()588 Image::GetBitmap()
589 {
590 	if (!fBitmap)
591 		fBitmap = BTranslationUtils::GetBitmap(fPath.Path());
592 
593 	return fBitmap;
594 }
595 
596