xref: /haiku/src/preferences/backgrounds/BackgroundImage.cpp (revision 23f179da55b1bd1ba84fbf3d3c56947e2c8d0aca)
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 				destinationBitmapBounds = viewBounds;
303 				followFlags = B_FOLLOW_ALL;
304 				break;
305 			}
306 			// else fall thru
307 		case kAtOffset:
308 		{
309 			destinationBitmapBounds.OffsetTo(offset);
310 			break;
311 		}
312 		case kTiled:
313 			// Original Backgrounds Preferences center the tiled paper
314 			// but Tracker doesn't do that
315 			//if (fIsDesktop) {
316 			destinationBitmapBounds.OffsetBy(
317 				(viewBounds.Width() - destinationBitmapBounds.Width()) / 2,
318 				(viewBounds.Height() - destinationBitmapBounds.Height()) / 2);
319 			//}
320 			tile = B_TILE_BITMAP;
321 			break;
322 	}
323 
324 	// switch to the bitmap and force a redraw
325 	view->SetViewBitmap(bitmap, bitmapBounds, destinationBitmapBounds,
326 		followFlags, tile);
327 	view->Invalidate();
328 
329 	/*if (fShowingBitmap != info) {
330 		if (fShowingBitmap)
331 			fShowingBitmap->UnloadBitmap(fCacheMode);
332 		fShowingBitmap = info;
333 	}*/
334 }
335 
336 
337 void
338 BackgroundImage::Remove()
339 {
340 	if (fShowingBitmap) {
341 		fView->ClearViewBitmap();
342 		fView->Invalidate();
343 		/*BPoseView* poseView = dynamic_cast<BPoseView*>(fView);
344 		// make sure text widgets draw the default way, erasing their background
345 		if (poseView)
346 			poseView->SetEraseWidgetTextBackground(true);*/
347 	}
348 	fShowingBitmap = NULL;
349 }
350 
351 
352 BackgroundImage::BackgroundImageInfo*
353 BackgroundImage::ImageInfoForWorkspace(int32 workspace) const
354 {
355 	uint32 workspaceMask = 1;
356 
357 	for (; workspace; workspace--)
358 		workspaceMask *= 2;
359 
360 	int32 count = fBitmapForWorkspaceList.CountItems();
361 
362 	// do a simple lookup for the most likely candidate bitmap -
363 	// pick the imageInfo that is only defined for this workspace over one
364 	// that supports multiple workspaces
365 	BackgroundImageInfo* result = NULL;
366 	for (int32 index = 0; index < count; index++) {
367 		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
368 		if (info->fImageSet != fShowingImageSet)
369 			continue;
370 
371 		if (fIsDesktop) {
372 			if (info->fWorkspace == workspaceMask)
373 				return info;
374 
375 			if (info->fWorkspace & workspaceMask)
376 				result = info;
377 		} else
378 			return info;
379 	}
380 	return result;
381 }
382 
383 
384 void
385 BackgroundImage::WorkspaceActivated(BView* view, int32 workspace, bool state)
386 {
387 	if (!fIsDesktop) {
388 		// we only care for desktop bitmaps
389 		return;
390 	}
391 
392 	if (!state) {
393 		// we only care comming into a new workspace, not leaving one
394 		return;
395 	}
396 
397 	BackgroundImageInfo* info = ImageInfoForWorkspace(workspace);
398 	if (info != fShowingBitmap) {
399 		if (info)
400 			Show(info, view);
401 		else {
402 			/*if (BPoseView* poseView = dynamic_cast<BPoseView*>(view))
403 				poseView->SetEraseWidgetTextBackground(true);*/
404 			view->ClearViewBitmap();
405 			view->Invalidate();
406 		}
407 		fShowingBitmap = info;
408 	}
409 }
410 
411 
412 void
413 BackgroundImage::ScreenChanged(BRect, color_space)
414 {
415 	if (!fIsDesktop || !fShowingBitmap)
416 		return;
417 
418 	/*if (fShowingBitmap->fMode == kCentered) {
419 		BRect viewBounds(fView->Bounds());
420 		BRect bitmapBounds(fShowingBitmap->fBitmap->Bounds());
421 		BRect destinationBitmapBounds(bitmapBounds);
422 		destinationBitmapBounds.OffsetBy(
423 			(viewBounds.Width() - bitmapBounds.Width()) / 2,
424 			(viewBounds.Height() - bitmapBounds.Height()) / 2);
425 
426 		fView->SetViewBitmap(fShowingBitmap->fBitmap, bitmapBounds,
427 			destinationBitmapBounds, B_FOLLOW_NONE, 0);
428 		fView->Invalidate();
429 	}*/
430 }
431 
432 
433 status_t
434 BackgroundImage::SetBackgroundImage(BNode* node)
435 {
436 	status_t err;
437 	BMessage container;
438 	int32 count = fBitmapForWorkspaceList.CountItems();
439 
440 	for (int32 index = 0; index < count; index++) {
441 		BackgroundImageInfo* info = fBitmapForWorkspaceList.ItemAt(index);
442 
443 		container.AddBool(kBackgroundImageInfoTextOutline,
444 			info->fTextWidgetLabelOutline);
445 		if (fBackgroundsView->GetImage(info->fImageIndex) != NULL) {
446 			container.AddString(kBackgroundImageInfoPath,
447 				fBackgroundsView
448 					->GetImage(info->fImageIndex)->GetPath().Path());
449 		} else
450 			container.AddString(kBackgroundImageInfoPath, "");
451 
452 		container.AddInt32(kBackgroundImageInfoWorkspaces, info->fWorkspace);
453 		container.AddPoint(kBackgroundImageInfoOffset, info->fOffset);
454 		container.AddInt32(kBackgroundImageInfoMode, info->fMode);
455 
456 		if (fIsDesktop)
457 			container.AddInt32(kBackgroundImageInfoSet, info->fImageSet);
458 	}
459 
460 	PRINT_OBJECT(container);
461 
462 	size_t flattenedSize = container.FlattenedSize();
463 	char* buffer = new(std::nothrow) char[flattenedSize];
464 	if (buffer == NULL)
465 		return B_NO_MEMORY;
466 
467 	if ((err = container.Flatten(buffer, flattenedSize)) != B_OK)
468 		return err;
469 
470 	ssize_t size = node->WriteAttr(kBackgroundImageInfo, B_MESSAGE_TYPE,
471 		0, buffer, flattenedSize);
472 
473 	delete[] buffer;
474 
475 	if (size < B_OK)
476 		return size;
477 	if ((size_t)size != flattenedSize)
478 		return B_ERROR;
479 
480 	return B_OK;
481 }
482 
483 
484 /*BackgroundImage*
485 BackgroundImage::Refresh(BackgroundImage* oldBackgroundImage,
486 	const BNode* fromNode, bool desktop, BPoseView* poseView)
487 {
488 	if (oldBackgroundImage) {
489 		oldBackgroundImage->Remove();
490 		delete oldBackgroundImage;
491 	}
492 
493 	BackgroundImage* result = GetBackgroundImage(fromNode, desktop);
494 	if (result && poseView->ViewMode() != kListMode)
495 		result->Show(poseView, current_workspace());
496 	return result;
497 }
498 
499 
500 void
501 BackgroundImage::ChangeImageSet(BPoseView* poseView)
502 {
503 	if (fRandomChange) {
504 		if (fImageSetCount > 1) {
505 			uint32 oldShowingImageSet = fShowingImageSet;
506 			while (oldShowingImageSet == fShowingImageSet)
507 				fShowingImageSet = random()%fImageSetCount;
508 		} else
509 			fShowingImageSet = 0;
510 	} else {
511 		fShowingImageSet++;
512 		if (fShowingImageSet > fImageSetCount - 1)
513 			fShowingImageSet = 0;
514 	}
515 
516 	this->Show(poseView, current_workspace());
517 }*/
518 
519 
520 //	#pragma mark -
521 
522 
523 Image::Image(BPath path)
524 	:
525 	fBitmap(NULL),
526 	fPath(path)
527 {
528 	name = path.Leaf();
529 }
530 
531 
532 Image::~Image()
533 {
534 	delete fBitmap;
535 }
536 
537 
538 BBitmap*
539 Image::GetBitmap()
540 {
541 	if (!fBitmap)
542 		fBitmap = BTranslationUtils::GetBitmap(fPath.Path());
543 
544 	return fBitmap;
545 }
546 
547