xref: /haiku/src/preferences/backgrounds/BackgroundImage.cpp (revision a4f6a81235ca2522c01f532de13cad9b729d4029)
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 <Bitmap.h>
39 #include <Node.h>
40 #include <TranslationKit.h>
41 #include <View.h>
42 #include <Window.h>
43 #include <Message.h>
44 #include <Entry.h>
45 #include <Path.h>
46 #include <Screen.h>
47 #include <String.h>
48 
49 #include <fs_attr.h>
50 #include <stdlib.h>
51 
52 #include "BackgroundImage.h"
53 #include "BackgroundsView.h"
54 
55 #include <new>
56 
57 
58 const char *kBackgroundImageInfo 			= "be:bgndimginfo";
59 const char *kBackgroundImageInfoOffset 		= "be:bgndimginfooffset";
60 const char *kBackgroundImageInfoEraseText	= "be:bgndimginfoerasetext";
61 const char *kBackgroundImageInfoMode 		= "be:bgndimginfomode";
62 const char *kBackgroundImageInfoWorkspaces 	= "be:bgndimginfoworkspaces";
63 const char *kBackgroundImageInfoPath 		= "be:bgndimginfopath";
64 const char *kBackgroundImageInfoSet 		= "be:bgndimginfoset";
65 const char *kBackgroundImageInfoCacheMode	= "be:bgndimginfocachemode";
66 const char *kBackgroundImageSetPeriod		= "be:bgndimgsetperiod";
67 const char *kBackgroundImageRandomChange	= "be:bgndimgrandomchange";
68 const char *kBackgroundImageCacheMode		= "be:bgndimgcachemode";
69 
70 
71 BackgroundImage *
72 BackgroundImage::GetBackgroundImage(const BNode *node, bool isDesktop,
73 	BackgroundsView* view)
74 {
75 	BackgroundImage *result = new BackgroundImage(node, isDesktop, view);
76 	attr_info info;
77 	if (node->GetAttrInfo(kBackgroundImageInfo, &info) != B_OK)
78 		return result;
79 
80 	BMessage container;
81 	char *buffer = new char [info.size];
82 
83 	status_t error = node->ReadAttr(kBackgroundImageInfo, info.type, 0, buffer,
84 		(size_t)info.size);
85 	if (error == info.size)
86 		error = container.Unflatten(buffer);
87 
88 	delete [] buffer;
89 
90 	if (error != B_OK)
91 		return NULL;
92 
93 	PRINT_OBJECT(container);
94 
95 	uint32 imageSetPeriod = 0;
96 	uint32 globalCacheMode = 0;
97 	bool randomChange = false;
98 	uint32 maxImageSet = 0;
99 
100 	if (isDesktop) {
101 		container.FindInt32(kBackgroundImageSetPeriod, (int32 *)&imageSetPeriod);
102 		container.FindInt32(kBackgroundImageCacheMode, (int32 *)&globalCacheMode);
103 		container.FindBool(kBackgroundImageRandomChange, &randomChange);
104 	}
105 
106 	for (int32 index = 0; ; index++) {
107 		const char *path;
108 		uint32 workspaces = B_ALL_WORKSPACES;
109 		Mode mode = kTiled;
110 		bool eraseTextWidgetBackground = true;
111 		BPoint offset;
112 		uint32 imageSet = 0;
113 		uint32 cacheMode = 0;
114 		if (container.FindString(kBackgroundImageInfoPath, index, &path) != B_OK)
115 			break;
116 
117 		BPath bpath(path);
118 		int32 imageIndex = view->AddImage(bpath);
119 		if (imageIndex < 0)
120 			imageIndex = -imageIndex - 1;
121 
122 		container.FindInt32(kBackgroundImageInfoWorkspaces, index,
123 			(int32 *)&workspaces);
124 		container.FindInt32(kBackgroundImageInfoMode, index, (int32 *)&mode);
125 		container.FindBool(kBackgroundImageInfoEraseText, index,
126 			&eraseTextWidgetBackground);
127 		container.FindPoint(kBackgroundImageInfoOffset, index, &offset);
128 
129 		if (isDesktop) {
130 			container.FindInt32(kBackgroundImageInfoSet, index,
131 				(int32 *)&imageSet);
132 			container.FindInt32(kBackgroundImageInfoCacheMode, index,
133 				(int32 *)&cacheMode);
134 		}
135 
136 		BackgroundImage::BackgroundImageInfo *imageInfo = new
137 			BackgroundImage::BackgroundImageInfo(workspaces, imageIndex,
138 				mode, offset, eraseTextWidgetBackground, imageSet, cacheMode);
139 
140 		//imageInfo->UnloadBitmap(globalCacheMode);
141 
142 		if (imageSet > maxImageSet)
143 			maxImageSet = imageSet;
144 
145 		result->Add(imageInfo);
146 	}
147 
148 	if (result) {
149 		result->fImageSetCount = maxImageSet + 1;
150 		result->fRandomChange = randomChange;
151 		result->fImageSetPeriod = imageSetPeriod;
152 		result->fCacheMode = globalCacheMode;
153 		if (result->fImageSetCount > 1)
154 			result->fShowingImageSet = random()%result->fImageSetCount;
155 	}
156 
157 	return result;
158 }
159 
160 
161 BackgroundImage::BackgroundImageInfo::BackgroundImageInfo(uint32 workspaces,
162 	int32 imageIndex, Mode mode, BPoint offset, bool eraseTextWidget,
163 	uint32 imageSet, uint32 cacheMode)
164 	:
165 	fWorkspace(workspaces),
166 	fImageIndex(imageIndex),
167 	fMode(mode),
168 	fOffset(offset),
169 	fEraseTextWidgetBackground(eraseTextWidget),
170 	fImageSet(imageSet),
171 	fCacheMode(cacheMode)
172 {
173 }
174 
175 
176 BackgroundImage::BackgroundImageInfo::~BackgroundImageInfo()
177 {
178 }
179 
180 
181 //	#pragma mark -
182 
183 
184 BackgroundImage::BackgroundImage(const BNode *node, bool desktop, BackgroundsView* view)
185 	:
186 	fIsDesktop(desktop),
187 	fDefinedByNode(*node),
188 	fView(NULL),
189 	fBackgroundsView(view),
190 	fShowingBitmap(NULL),
191 	fBitmapForWorkspaceList(1, true),
192 	fImageSetPeriod(0),
193 	fShowingImageSet(0),
194 	fImageSetCount(0),
195 	fCacheMode(0),
196 	fRandomChange(false)
197 {
198 }
199 
200 
201 BackgroundImage::~BackgroundImage()
202 {
203 }
204 
205 
206 void
207 BackgroundImage::Add(BackgroundImageInfo *info)
208 {
209 	fBitmapForWorkspaceList.AddItem(info);
210 }
211 
212 
213 void
214 BackgroundImage::RemoveAll()
215 {
216 	for (int32 index = 0; index < fBitmapForWorkspaceList.CountItems();) {
217 		BackgroundImageInfo *info = fBitmapForWorkspaceList.ItemAt(index);
218 		if (info->fImageSet != fShowingImageSet)
219 			index++;
220 		else
221 			fBitmapForWorkspaceList.RemoveItemAt(index);
222 	}
223 }
224 
225 
226 void
227 BackgroundImage::Show(BView *view, int32 workspace)
228 {
229 	fView = view;
230 
231 	BackgroundImageInfo *info = ImageInfoForWorkspace(workspace);
232 	if (info) {
233 		/*BPoseView *poseView = dynamic_cast<BPoseView *>(fView);
234 		if (poseView)
235 			poseView->SetEraseWidgetTextBackground(info->fEraseTextWidgetBackground);*/
236 		Show(info, fView);
237 	}
238 }
239 
240 
241 void
242 BackgroundImage::Show(BackgroundImageInfo *info, BView *view)
243 {
244 	BBitmap *bitmap = fBackgroundsView->GetImage(info->fImageIndex)->GetBitmap();
245 
246 	if (!bitmap)
247 		return;
248 
249 	BRect viewBounds(view->Bounds());
250 
251 	display_mode mode;
252 	BScreen().GetMode(&mode);
253 	float x_ratio = viewBounds.Width() / mode.virtual_width;
254 	float y_ratio = viewBounds.Height() / mode.virtual_height;
255 
256 	BRect bitmapBounds(bitmap->Bounds());
257 	BRect destinationBitmapBounds(bitmapBounds);
258 	destinationBitmapBounds.right *= x_ratio;
259 	destinationBitmapBounds.bottom *= y_ratio;
260 	BPoint offset(info->fOffset);
261 	offset.x *= x_ratio;
262 	offset.y *= y_ratio;
263 
264 	uint32 tile = 0;
265 	uint32 followFlags = B_FOLLOW_TOP | B_FOLLOW_LEFT;
266 
267 	// figure out the display mode and the destination bounds for the bitmap
268 	switch (info->fMode) {
269 		case kCentered:
270 			if (fIsDesktop) {
271 				destinationBitmapBounds.OffsetBy(
272 					(viewBounds.Width() - destinationBitmapBounds.Width()) / 2,
273 					(viewBounds.Height() - destinationBitmapBounds.Height()) / 2);
274 				break;
275 			}
276 			// else fall thru
277 		case kScaledToFit:
278 			if (fIsDesktop) {
279 				destinationBitmapBounds = viewBounds;
280 				followFlags = B_FOLLOW_ALL;
281 				break;
282 			}
283 			// else fall thru
284 		case kAtOffset:
285 			{
286 				destinationBitmapBounds.OffsetTo(offset);
287 				break;
288 			}
289 		case kTiled:
290 			// Original Backgrounds Preferences center the tiled paper but the Tracker don't do that
291 			//if (fIsDesktop) {
292 				destinationBitmapBounds.OffsetBy(
293 					(viewBounds.Width() - destinationBitmapBounds.Width()) / 2,
294 					(viewBounds.Height() - destinationBitmapBounds.Height()) / 2);
295 			//}
296 			tile = B_TILE_BITMAP;
297 			break;
298 	}
299 
300 	// switch to the bitmap and force a redraw
301 	view->SetViewBitmap(bitmap, bitmapBounds, destinationBitmapBounds,
302 		followFlags, tile);
303 	view->Invalidate();
304 
305 	/*if(fShowingBitmap != info) {
306 		if(fShowingBitmap)
307 			fShowingBitmap->UnloadBitmap(fCacheMode);
308 		fShowingBitmap = info;
309 	}*/
310 }
311 
312 
313 void
314 BackgroundImage::Remove()
315 {
316 	if (fShowingBitmap) {
317 		fView->ClearViewBitmap();
318 		fView->Invalidate();
319 		/*BPoseView *poseView = dynamic_cast<BPoseView *>(fView);
320 		// make sure text widgets draw the default way, erasing their background
321 		if (poseView)
322 			poseView->SetEraseWidgetTextBackground(true);*/
323 	}
324 	fShowingBitmap = NULL;
325 }
326 
327 
328 BackgroundImage::BackgroundImageInfo *
329 BackgroundImage::ImageInfoForWorkspace(int32 workspace) const
330 {
331 	uint32 workspaceMask = 1;
332 
333 	for (; workspace; workspace--)
334 		workspaceMask *= 2;
335 
336 	int32 count = fBitmapForWorkspaceList.CountItems();
337 
338 	// do a simple lookup for the most likely candidate bitmap -
339 	// pick the imageInfo that is only defined for this workspace over one
340 	// that supports multiple workspaces
341 	BackgroundImageInfo *result = NULL;
342 	for (int32 index = 0; index < count; index++) {
343 		BackgroundImageInfo *info = fBitmapForWorkspaceList.ItemAt(index);
344 		if (info->fImageSet != fShowingImageSet)
345 			continue;
346 
347 		if (fIsDesktop) {
348 			if (info->fWorkspace == workspaceMask)
349 				return info;
350 			if (info->fWorkspace & workspaceMask)
351 				result = info;
352 		} else
353 			return info;
354 	}
355 
356 	return result;
357 }
358 
359 
360 void
361 BackgroundImage::WorkspaceActivated(BView *view, int32 workspace, bool state)
362 {
363 	if (!fIsDesktop) {
364 		// we only care for desktop bitmaps
365 		return;
366 	}
367 
368 	if (!state) {
369 		// we only care comming into a new workspace, not leaving one
370 		return;
371 	}
372 
373 	BackgroundImageInfo *info = ImageInfoForWorkspace(workspace);
374 	if (info != fShowingBitmap) {
375 		if (info)
376 			Show(info, view);
377 		else {
378 			/*if (BPoseView *poseView = dynamic_cast<BPoseView *>(view))
379 				poseView->SetEraseWidgetTextBackground(true);*/
380 			view->ClearViewBitmap();
381 			view->Invalidate();
382 		}
383 		fShowingBitmap = info;
384 	}
385 }
386 
387 
388 void
389 BackgroundImage::ScreenChanged(BRect, color_space)
390 {
391 	if (!fIsDesktop || !fShowingBitmap)
392 		return;
393 
394 	/*if (fShowingBitmap->fMode == kCentered) {
395 		BRect viewBounds(fView->Bounds());
396 		BRect bitmapBounds(fShowingBitmap->fBitmap->Bounds());
397 		BRect destinationBitmapBounds(bitmapBounds);
398 		destinationBitmapBounds.OffsetBy(
399 			(viewBounds.Width() - bitmapBounds.Width()) / 2,
400 			(viewBounds.Height() - bitmapBounds.Height()) / 2);
401 
402 		fView->SetViewBitmap(fShowingBitmap->fBitmap, bitmapBounds, destinationBitmapBounds,
403 			B_FOLLOW_NONE, 0);
404 		fView->Invalidate();
405 	}*/
406 }
407 
408 
409 status_t
410 BackgroundImage::SetBackgroundImage(BNode *node)
411 {
412 	status_t err;
413 	BMessage container;
414 	int32 count = fBitmapForWorkspaceList.CountItems();
415 
416 	for (int32 index = 0; index < count; index++) {
417 		BackgroundImageInfo *info = fBitmapForWorkspaceList.ItemAt(index);
418 		if (fBackgroundsView->GetImage(info->fImageIndex) == NULL)
419 			continue;
420 
421 		container.AddBool(kBackgroundImageInfoEraseText,
422 			info->fEraseTextWidgetBackground);
423 		container.AddString(kBackgroundImageInfoPath,
424 			fBackgroundsView->GetImage(info->fImageIndex)->GetPath().Path());
425 		container.AddInt32(kBackgroundImageInfoWorkspaces, info->fWorkspace);
426 		container.AddPoint(kBackgroundImageInfoOffset, info->fOffset);
427 		container.AddInt32(kBackgroundImageInfoMode, info->fMode);
428 
429 		if (fIsDesktop)
430 			container.AddInt32(kBackgroundImageInfoSet, info->fImageSet);
431 	}
432 
433 	PRINT_OBJECT(container);
434 
435 	size_t flattenedSize = container.FlattenedSize();
436 	char* buffer = new (std::nothrow) char[flattenedSize];
437 	if (buffer == NULL)
438 		return B_NO_MEMORY;
439 
440 	if ((err = container.Flatten(buffer, flattenedSize)) != B_OK)
441 		return err;
442 
443 	ssize_t size = node->WriteAttr(kBackgroundImageInfo, B_MESSAGE_TYPE,
444 		0, buffer, flattenedSize);
445 
446 	delete[] buffer;
447 
448 	if (size < B_OK)
449 		return size;
450 	if ((size_t)size != flattenedSize)
451 		return B_ERROR;
452 
453 	return B_OK;
454 }
455 
456 
457 /*BackgroundImage *
458 BackgroundImage::Refresh(BackgroundImage *oldBackgroundImage,
459 	const BNode *fromNode, bool desktop, BPoseView *poseView)
460 {
461 	if (oldBackgroundImage) {
462 		oldBackgroundImage->Remove();
463 		delete oldBackgroundImage;
464 	}
465 
466 	BackgroundImage *result = GetBackgroundImage(fromNode, desktop);
467 	if (result && poseView->ViewMode() != kListMode)
468 		result->Show(poseView, current_workspace());
469 	return result;
470 }
471 
472 
473 void
474 BackgroundImage::ChangeImageSet(BPoseView *poseView)
475 {
476 	if(fRandomChange) {
477 		if(fImageSetCount > 1) {
478 			uint32 oldShowingImageSet = fShowingImageSet;
479 			while(oldShowingImageSet == fShowingImageSet)
480 				fShowingImageSet = random()%fImageSetCount;
481 		} else
482 			fShowingImageSet = 0;
483 	} else {
484 		fShowingImageSet++;
485 		if(fShowingImageSet > fImageSetCount - 1)
486 			fShowingImageSet = 0;
487 	}
488 
489 	this->Show(poseView, current_workspace());
490 }*/
491 
492 
493 //	#pragma mark -
494 
495 
496 Image::Image(BPath path)
497 	:
498 	fBitmap(NULL),
499 	fPath(path)
500 {
501 	name = path.Leaf();
502 }
503 
504 
505 Image::~Image()
506 {
507 	delete fBitmap;
508 }
509 
510 
511 BBitmap*
512 Image::GetBitmap()
513 {
514 	if (!fBitmap)
515 		fBitmap = BTranslationUtils::GetBitmap(fPath.Path());
516 
517 	return fBitmap;
518 }
519