1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All 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
59const char* kBackgroundImageInfo 			= "be:bgndimginfo";
60const char* kBackgroundImageInfoOffset 		= "be:bgndimginfooffset";
61// const char* kBackgroundImageInfoTextOutline	= "be:bgndimginfotextoutline";
62const 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...
66const char* kBackgroundImageInfoMode 		= "be:bgndimginfomode";
67const char* kBackgroundImageInfoWorkspaces 	= "be:bgndimginfoworkspaces";
68const char* kBackgroundImageInfoPath 		= "be:bgndimginfopath";
69const char* kBackgroundImageInfoSet 		= "be:bgndimginfoset";
70const char* kBackgroundImageInfoCacheMode	= "be:bgndimginfocachemode";
71const char* kBackgroundImageSetPeriod		= "be:bgndimgsetperiod";
72const char* kBackgroundImageRandomChange	= "be:bgndimgrandomchange";
73const char* kBackgroundImageCacheMode		= "be:bgndimgcachemode";
74
75
76BackgroundImage*
77BackgroundImage::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
173BackgroundImage::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
188BackgroundImage::BackgroundImageInfo::~BackgroundImageInfo()
189{
190}
191
192
193//	#pragma mark -
194
195
196BackgroundImage::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
214BackgroundImage::~BackgroundImage()
215{
216}
217
218
219void
220BackgroundImage::Add(BackgroundImageInfo* info)
221{
222	fBitmapForWorkspaceList.AddItem(info);
223}
224
225
226void
227BackgroundImage::Remove(BackgroundImageInfo* info)
228{
229	fBitmapForWorkspaceList.RemoveItem(info);
230}
231
232
233void
234BackgroundImage::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
246void
247BackgroundImage::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
262void
263BackgroundImage::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
349float
350BackgroundImage::BRectRatio(BRect rect)
351{
352	return rect.Width() / rect.Height();
353}
354
355
356float
357BackgroundImage::BRectHorizontalOverlap(BRect hostRect, BRect resizedRect)
358{
359	return ((hostRect.Height() / resizedRect.Height() * resizedRect.Width())
360		- hostRect.Width()) / 2;
361}
362
363
364float
365BackgroundImage::BRectVerticalOverlap(BRect hostRect, BRect resizedRect)
366{
367	return ((hostRect.Width() / resizedRect.Width() * resizedRect.Height())
368		- hostRect.Height()) / 2;
369}
370
371
372void
373BackgroundImage::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
387BackgroundImage::BackgroundImageInfo*
388BackgroundImage::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
419void
420BackgroundImage::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
447void
448BackgroundImage::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
468status_t
469BackgroundImage::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*
525BackgroundImage::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
540void
541BackgroundImage::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
563Image::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
581Image::~Image()
582{
583	delete fBitmap;
584}
585
586
587BBitmap*
588Image::GetBitmap()
589{
590	if (!fBitmap)
591		fBitmap = BTranslationUtils::GetBitmap(fPath.Path());
592
593	return fBitmap;
594}
595
596