xref: /haiku/src/preferences/backgrounds/BackgroundsView.cpp (revision acc71e7c7c666824557eacb3bd42a9aa802c1196)
1 /*
2  * Copyright 2002-2009, Haiku, Inc. All Rights Reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jerome Duval (jerome.duval@free.fr)
7  *		Axel Dörfler, axeld@pinc-software.de
8  */
9 
10 
11 #include "BackgroundsView.h"
12 #include "ImageFilePanel.h"
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 
17 #include <Debug.h>
18 #include <OS.h>
19 #include <MenuField.h>
20 #include <StorageKit.h>
21 #include <Window.h>
22 #include <Messenger.h>
23 #include <Bitmap.h>
24 #include <Point.h>
25 #include <TranslationKit.h>
26 #include <PopUpMenu.h>
27 
28 #include <be_apps/Tracker/Background.h>
29 
30 
31 static const uint32 kMsgApplySettings = 'aply';
32 static const uint32 kMsgRevertSettings = 'rvrt';
33 
34 static const uint32 kMsgUpdateColor = 'upcl';
35 static const uint32 kMsgAllWorkspaces = 'alwk';
36 static const uint32 kMsgCurrentWorkspace = 'crwk';
37 static const uint32 kMsgDefaultFolder = 'dffl';
38 static const uint32 kMsgOtherFolder = 'otfl';
39 static const uint32 kMsgNoImage = 'noim';
40 static const uint32 kMsgOtherImage = 'otim';
41 static const uint32 kMsgImageSelected = 'imsl';
42 static const uint32 kMsgFolderSelected = 'flsl';
43 
44 static const uint32 kMsgCenterPlacement = 'cnpl';
45 static const uint32 kMsgManualPlacement = 'mnpl';
46 static const uint32 kMsgScalePlacement = 'scpl';
47 static const uint32 kMsgTilePlacement = 'tlpl';
48 static const uint32 kMsgIconLabelOutline = 'ilol';
49 
50 static const uint32 kMsgImagePlacement = 'xypl';
51 static const uint32 kMsgUpdatePreviewPlacement = 'pvpl';
52 
53 
54 const uint8 kHandCursorData[68] = {
55 	16, 1, 2, 2,
56 
57 	0, 0,		// 0000000000000000
58 	7, 128,		// 0000011110000000
59 	61, 112,	// 0011110101110000
60 	37, 40,		// 0010010100101000
61 	36, 168,	// 0010010010101000
62 	18, 148,	// 0001001010010100
63 	18, 84,		// 0001001001010100
64 	9, 42,		// 0000100100101010
65 	8, 1,		// 0000100000000001
66 	60, 1,		// 0011110000000001
67 	76, 1,		// 0100110000000001
68 	66, 1,		// 0100001000000001
69 	48, 1,		// 0011000000000001
70 	12, 1,		// 0000110000000001
71 	2, 0,		// 0000001000000000
72 	1, 0,		// 0000000100000000
73 
74 	0, 0,		// 0000000000000000
75 	7, 128,		// 0000011110000000
76 	63, 240,	// 0011111111110000
77 	63, 248,	// 0011111111111000
78 	63, 248,	// 0011111111111000
79 	31, 252,	// 0001111111111100
80 	31, 252,	// 0001111111111100
81 	15, 254,	// 0000111111111110
82 	15, 255,	// 0000111111111111
83 	63, 255,	// 0011111111111111
84 	127, 255,	// 0111111111111111
85 	127, 255,	// 0111111111111111
86 	63, 255,	// 0011111111111111
87 	15, 255,	// 0000111111111111
88 	3, 254,		// 0000001111111110
89 	1, 248		// 0000000111111000
90 };
91 
92 
93 BackgroundsView::BackgroundsView(BRect frame, const char *name, int32 resize,
94 	int32 flags)
95 	: BBox(frame, name, resize, flags | B_WILL_DRAW | B_FRAME_EVENTS, B_NO_BORDER),
96 	fCurrent(NULL),
97 	fCurrentInfo(NULL),
98 	fLastImageIndex(-1),
99 	fPathList(1, true),
100 	fImageList(1, true)
101 {
102 	// we need the "Current Workspace" first to get its height
103 
104 	BMenuItem *menuItem;
105 	fWorkspaceMenu = new BPopUpMenu("pick one");
106 	fWorkspaceMenu->AddItem(menuItem = new BMenuItem("All Workspaces",
107 		new BMessage(kMsgAllWorkspaces)));
108 	fWorkspaceMenu->AddItem(menuItem = new BMenuItem("Current Workspace",
109 		new BMessage(kMsgCurrentWorkspace)));
110 	menuItem->SetMarked(true);
111 	fLastWorkspaceIndex = fWorkspaceMenu->IndexOf(fWorkspaceMenu->FindMarked());
112 	fWorkspaceMenu->AddSeparatorItem();
113 	fWorkspaceMenu->AddItem(menuItem = new BMenuItem("Default folder",
114 		new BMessage(kMsgDefaultFolder)));
115 	fWorkspaceMenu->AddItem(menuItem = new BMenuItem("Other folder" B_UTF8_ELLIPSIS,
116 		new BMessage(kMsgOtherFolder)));
117 
118 	BMenuField *workspaceMenuField = new BMenuField(BRect(0, 0, 130, 18),
119 		"workspaceMenuField", NULL, fWorkspaceMenu, true);
120 	workspaceMenuField->ResizeToPreferred();
121 
122 	/* the preview box */
123 
124 	BFont font(be_plain_font);
125 	font_height fontHeight;
126 	font.GetHeight(&fontHeight);
127 
128 	float preview_width = 120.0f;
129 
130 	// get aspect ratio
131 	float aspect_ratio = BScreen().Frame().Width() /
132 		BScreen().Frame().Height();
133 
134 	float preview_height = ceil(preview_width / aspect_ratio);
135 
136 	fPreview = new PreviewBox(BRect(10, 8.0 + workspaceMenuField->Bounds().Height() / 2.0f
137 		- ceilf(fontHeight.ascent + fontHeight.descent) / 2.0f, 160, 180), "preview",
138 		19 + 90 - preview_height);
139 	fPreview->SetFont(&font);
140 	fPreview->SetLabel("Preview");
141 	AddChild(fPreview);
142 
143 	BRect rect(10, fPreview->Bounds().bottom - 30, 70, fPreview->Bounds().bottom - 10);
144 	fXPlacementText = new BTextControl(rect, "xPlacementText", "X:", NULL,
145 		new BMessage(kMsgImagePlacement), B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
146 	fXPlacementText->SetDivider(fXPlacementText->StringWidth(fXPlacementText->Label()) + 4.0f);
147 	fXPlacementText->TextView()->SetMaxBytes(5);
148 	float width, height;
149 	fXPlacementText->GetPreferredSize(&width, &height);
150 	float delta = fXPlacementText->Bounds().Height() - height;
151 	fXPlacementText->MoveBy(0, delta);
152 	fXPlacementText->ResizeTo(fXPlacementText->Bounds().Width(), height);
153 	fPreview->AddChild(fXPlacementText);
154 
155 	rect.OffsetBy(70, delta);
156 	fYPlacementText = new BTextControl(rect, "yPlacementText", "Y:", NULL,
157 		new BMessage(kMsgImagePlacement), B_FOLLOW_LEFT | B_FOLLOW_BOTTOM);
158 	fYPlacementText->SetDivider(fYPlacementText->StringWidth(fYPlacementText->Label()) + 4.0f);
159 	fYPlacementText->TextView()->SetMaxBytes(5);
160 	fXPlacementText->ResizeTo(fYPlacementText->Bounds().Width(), height);
161 	fPreview->AddChild(fYPlacementText);
162 
163 	for (int32 i = 0; i < 256; i++) {
164 		if ((i < '0' || i > '9') && i != '-') {
165 			fXPlacementText->TextView()->DisallowChar(i);
166 			fYPlacementText->TextView()->DisallowChar(i);
167 		}
168 	}
169 
170 	fPreView = new PreView(BRect((150 - preview_width) / 2, 25 + 90 - preview_height,
171 		150 - (150 - preview_width) / 2, 25 + 90),
172 		"preView",
173 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW | B_SUBPIXEL_PRECISE);
174 	fPreview->AddChild(fPreView);
175 
176 	/* the right box */
177 
178 	BBox *rightbox = new BBox(BRect(fPreview->Frame().right + 10,
179 		7, Frame().right - 10, fPreview->Frame().bottom),
180 		"rightbox");
181 	rightbox->SetLabel(workspaceMenuField);
182 	AddChild(rightbox);
183 
184 	float offset = be_plain_font->StringWidth("Placement:") + 5;
185 	rect.Set(10, 0, rightbox->Bounds().right - 10, 30);
186 #ifdef __HAIKU__
187 	rect.top = 8 + rightbox->InnerFrame().top;
188 #else
189 	rect.top = 5 + workspaceMenuField->Bounds().Height();
190 	rect.bottom = rect.top + workspaceMenuField->Bounds().Height();
191 #endif
192 
193 	fImageMenu = new BPopUpMenu("pick one");
194 	fImageMenu->AddItem(new BGImageMenuItem("None", -1, new BMessage(kMsgNoImage)));
195 	fImageMenu->AddSeparatorItem();
196 	fImageMenu->AddItem(new BMenuItem("Other" B_UTF8_ELLIPSIS, new BMessage(kMsgOtherImage)));
197 
198 	BMenuField *imageMenuField = new BMenuField(rect, "imageMenuField",
199 		"Image:", fImageMenu);
200 	imageMenuField->SetDivider(offset);
201 	imageMenuField->SetAlignment(B_ALIGN_RIGHT);
202 	imageMenuField->ResizeToPreferred();
203 	rightbox->AddChild(imageMenuField);
204 
205 	fPlacementMenu = new BPopUpMenu("pick one");
206 	fPlacementMenu->AddItem(new BMenuItem("Manual",
207 		new BMessage(kMsgManualPlacement)));
208 	fPlacementMenu->AddItem(new BMenuItem("Center",
209 		new BMessage(kMsgCenterPlacement)));
210 	fPlacementMenu->AddItem(new BMenuItem("Scale to fit",
211 		new BMessage(kMsgScalePlacement)));
212 	fPlacementMenu->AddItem(new BMenuItem("Tile",
213 		new BMessage(kMsgTilePlacement)));
214 
215 	rect.OffsetBy(0, imageMenuField->Bounds().Height() + 5);
216 	BMenuField *placementMenuField = new BMenuField(rect, "placementMenuField",
217 		"Placement:", fPlacementMenu);
218 	placementMenuField->SetDivider(offset);
219 	placementMenuField->SetAlignment(B_ALIGN_RIGHT);
220 	placementMenuField->ResizeToPreferred();
221 	rightbox->AddChild(placementMenuField);
222 
223 	rect.OffsetBy(offset, placementMenuField->Bounds().Height() + 5);
224 	fIconLabelOutline = new BCheckBox(rect, "iconLabelOutline",
225 		"Icon label outline", new BMessage(kMsgIconLabelOutline));
226 	fIconLabelOutline->SetValue(B_CONTROL_OFF);
227 	fIconLabelOutline->ResizeToPreferred();
228 	rightbox->AddChild(fIconLabelOutline);
229 
230 	rect.top += fIconLabelOutline->Bounds().Height() + 15;
231 	fPicker = new BColorControl(BPoint(10, rect.top), B_CELLS_32x8, 7.0, "Picker",
232 		new BMessage(kMsgUpdateColor));
233 	rightbox->AddChild(fPicker);
234 
235 	float xDelta = max_c(fIconLabelOutline->Frame().right, fPicker->Frame().right)
236 		+ 10.0f - rightbox->Bounds().Width();
237 	delta = fPicker->Frame().bottom + 10.0f - rightbox->Bounds().Height();
238 
239 	rightbox->ResizeBy(xDelta, delta);
240 	fPreview->ResizeBy(0, delta);
241 
242 	// we're not yet attached to a view, so we need to move them manually
243 	fXPlacementText->MoveBy(0, delta);
244 	fYPlacementText->MoveBy(0, delta);
245 
246 	rect = fPreview->Frame();
247 	rect.top = rect.bottom + 10.0;
248 	fRevert = new BButton(rect, "revert", "Revert", new BMessage(kMsgRevertSettings));
249 	AddChild(fRevert);
250 	fRevert->ResizeToPreferred();
251 
252 	rect.left = rightbox->Frame().right;
253 	fApply = new BButton(rect, "apply", "Apply", new BMessage(kMsgApplySettings));
254 	AddChild(fApply);
255 	fApply->ResizeToPreferred();
256 	fApply->MoveBy(-fApply->Bounds().Width(), 0);
257 	fApply->MakeDefault(true);
258 }
259 
260 
261 BackgroundsView::~BackgroundsView()
262 {
263 	delete fPanel;
264 	delete fFolderPanel;
265 }
266 
267 
268 void
269 BackgroundsView::GetPreferredSize(float* _width, float* _height)
270 {
271 	if (_width)
272 		*_width = fApply->Frame().right + 10;
273 	if (_height)
274 		*_height = fApply->Frame().bottom + 10;
275 }
276 
277 
278 void
279 BackgroundsView::AllAttached()
280 {
281 	fPlacementMenu->SetTargetForItems(this);
282 	fImageMenu->SetTargetForItems(this);
283 	fWorkspaceMenu->SetTargetForItems(this);
284 	fXPlacementText->SetTarget(this);
285 	fYPlacementText->SetTarget(this);
286 	fIconLabelOutline->SetTarget(this);
287 	fPicker->SetTarget(this);
288 	fApply->SetTarget(this);
289 	fRevert->SetTarget(this);
290 
291 	BPath path;
292 	entry_ref ref;
293 	if (find_directory(B_SYSTEM_DATA_DIRECTORY, &path) == B_OK) {
294 		path.Append("artwork");
295 		get_ref_for_path(path.Path(), &ref);
296 	}
297 
298 	BMessenger messenger(this);
299 	fPanel = new ImageFilePanel(B_OPEN_PANEL, &messenger, &ref,
300 		B_FILE_NODE, false, NULL, new CustomRefFilter(true));
301 	fPanel->SetButtonLabel(B_DEFAULT_BUTTON, "Select");
302 
303 	fFolderPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL,
304 		B_DIRECTORY_NODE, false, NULL, new CustomRefFilter(false));
305 	fFolderPanel->SetButtonLabel(B_DEFAULT_BUTTON, "Select");
306 
307 	LoadSettings();
308 	LoadDesktopFolder();
309 
310 	float width, height;
311 	GetPreferredSize(&width, &height);
312 
313 	BPoint point;
314 	if (fSettings.FindPoint("pos", &point) != B_OK) {
315 		BRect frame(BScreen().Frame());
316 		point.Set((frame.right - width) / 2, (frame.bottom - height) / 2);
317 	}
318 
319 	ResizeTo(width, height);
320 	Window()->MoveTo(point);
321 	Window()->ResizeTo(width, height);
322 
323 	fApply->SetEnabled(false);
324 	fRevert->SetEnabled(false);
325 }
326 
327 
328 void
329 BackgroundsView::MessageReceived(BMessage *msg)
330 {
331 	switch (msg->what) {
332 		case B_SIMPLE_DATA:
333 		case B_REFS_RECEIVED:
334 			RefsReceived(msg);
335 			break;
336 
337 		case kMsgUpdatePreviewPlacement:
338 		{
339 			BString xstring, ystring;
340 			xstring << (int)fPreView->fPoint.x;
341 			ystring << (int)fPreView->fPoint.y;
342 			fXPlacementText->SetText(xstring.String());
343 			fYPlacementText->SetText(ystring.String());
344 			UpdatePreview();
345 			UpdateButtons();
346 			break;
347 		}
348 
349 		case kMsgManualPlacement:
350 			UpdatePreview();
351 			UpdateButtons();
352 			break;
353 
354 		case kMsgTilePlacement:
355 		case kMsgScalePlacement:
356 		case kMsgCenterPlacement:
357 			UpdatePreview();
358 			UpdateButtons();
359 			break;
360 
361 		case kMsgIconLabelOutline:
362 			UpdateButtons();
363 			break;
364 
365 		case kMsgUpdateColor:
366 		case kMsgImagePlacement:
367 			UpdatePreview();
368 			UpdateButtons();
369 			break;
370 
371 		case kMsgCurrentWorkspace:
372 		case kMsgAllWorkspaces:
373 			fImageMenu->FindItem(kMsgNoImage)->SetLabel("None");
374 			fLastWorkspaceIndex = fWorkspaceMenu->IndexOf(
375 				fWorkspaceMenu->FindMarked());
376 			if (fCurrent && fCurrent->IsDesktop()) {
377 				UpdateButtons();
378 			} else {
379 				fPreview->SetDesktop(true);
380 				LoadDesktopFolder();
381 			}
382 			break;
383 
384 		case kMsgDefaultFolder:
385 			fImageMenu->FindItem(kMsgNoImage)->SetLabel("None");
386 			fLastWorkspaceIndex = fWorkspaceMenu->IndexOf(
387 				fWorkspaceMenu->FindMarked());
388 			fPreview->SetDesktop(false);
389 			LoadDefaultFolder();
390 			break;
391 
392 		case kMsgOtherFolder:
393 			fFolderPanel->Show();
394 			break;
395 
396 		case kMsgOtherImage:
397 			fPanel->Show();
398 			break;
399 
400 		case B_CANCEL:
401 		{
402 			PRINT(("cancel received\n"));
403 			void* pointer;
404 			msg->FindPointer("source", &pointer);
405 			if (pointer == fPanel) {
406 				if (fLastImageIndex >= 0)
407 					FindImageItem(fLastImageIndex)->SetMarked(true);
408 				else
409 					fImageMenu->ItemAt(0)->SetMarked(true);
410 			} else if (pointer == fFolderPanel) {
411 				if (fLastWorkspaceIndex >= 0)
412 					fWorkspaceMenu->ItemAt(fLastWorkspaceIndex)->SetMarked(true);
413 			}
414 			break;
415 		}
416 
417 		case kMsgImageSelected:
418 		case kMsgNoImage:
419 			fLastImageIndex = ((BGImageMenuItem*)fImageMenu->FindMarked())->ImageIndex();
420 			UpdatePreview();
421 			UpdateButtons();
422 			break;
423 
424 		case kMsgFolderSelected:
425 			fImageMenu->FindItem(kMsgNoImage)->SetLabel("Default");
426 			fLastWorkspaceIndex = fWorkspaceMenu->IndexOf(
427 				fWorkspaceMenu->FindMarked());
428 			fPreview->SetDesktop(false);
429 			LoadRecentFolder(*fPathList.ItemAt(fWorkspaceMenu->IndexOf(
430 				fWorkspaceMenu->FindMarked()) - 6));
431 			break;
432 
433 		case kMsgApplySettings:
434 		{
435 			Save();
436 
437 			//NotifyServer();
438 			thread_id notify_thread;
439 			notify_thread = spawn_thread(BackgroundsView::NotifyThread, "notifyServer",
440 				B_NORMAL_PRIORITY, this);
441 			resume_thread(notify_thread);
442 			UpdateButtons();
443 			break;
444 		}
445 		case kMsgRevertSettings:
446 			UpdateWithCurrent();
447 			break;
448 
449 		default:
450 			BView::MessageReceived(msg);
451 			break;
452 	}
453 }
454 
455 
456 void
457 BackgroundsView::LoadDesktopFolder()
458 {
459 	BPath path;
460 	if (find_directory(B_DESKTOP_DIRECTORY, &path) == B_OK) {
461 		status_t err;
462 		err = get_ref_for_path(path.Path(), &fCurrentRef);
463 		if (err != B_OK)
464 			printf("error in LoadDesktopSettings\n");
465 		LoadFolder(true);
466 	}
467 }
468 
469 
470 void
471 BackgroundsView::LoadDefaultFolder()
472 {
473 	BPath path;
474 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
475 		BString pathString = path.Path();
476 		pathString << "/Tracker/DefaultFolderTemplate";
477 		status_t err;
478 		err = get_ref_for_path(pathString.String(), &fCurrentRef);
479 		if (err != B_OK)
480 			printf("error in LoadDefaultFolderSettings\n");
481 		LoadFolder(false);
482 	}
483 }
484 
485 
486 void
487 BackgroundsView::LoadRecentFolder(BPath path)
488 {
489 	status_t err;
490 	err = get_ref_for_path(path.Path(), &fCurrentRef);
491 	if (err != B_OK)
492 		printf("error in LoadRecentFolder\n");
493 	LoadFolder(false);
494 }
495 
496 
497 void
498 BackgroundsView::LoadFolder(bool isDesktop)
499 {
500 	if (fCurrent) {
501 		delete fCurrent;
502 		fCurrent = NULL;
503 	}
504 
505 	BNode node(&fCurrentRef);
506 	if (node.InitCheck() == B_OK)
507 		fCurrent = BackgroundImage::GetBackgroundImage(&node, isDesktop, this);
508 
509 	UpdateWithCurrent();
510 }
511 
512 
513 void
514 BackgroundsView::UpdateWithCurrent(void)
515 {
516 	if (fCurrent == NULL)
517 		return;
518 
519 	fPlacementMenu->FindItem(kMsgScalePlacement)->SetEnabled(fCurrent->IsDesktop());
520 	fPlacementMenu->FindItem(kMsgCenterPlacement)->SetEnabled(fCurrent->IsDesktop());
521 
522 	if (fWorkspaceMenu->IndexOf(fWorkspaceMenu->FindMarked()) > 5)
523 		fImageMenu->FindItem(kMsgNoImage)->SetLabel("Default");
524 	else
525 		fImageMenu->FindItem(kMsgNoImage)->SetLabel("None");
526 
527 	for (int32 i = fImageMenu->CountItems() - 5; i >= 0; i--) {
528 		fImageMenu->RemoveItem(2);
529 	}
530 
531 	for (int32 i = fImageList.CountItems() - 1; i >= 0; i--) {
532 		BMessage *message = new BMessage(kMsgImageSelected);
533 		AddItem(new BGImageMenuItem(GetImage(i)->GetName(), i, message));
534 	}
535 
536 	fImageMenu->SetTargetForItems(this);
537 
538 	fCurrentInfo = fCurrent->ImageInfoForWorkspace(current_workspace());
539 
540 	if (!fCurrentInfo) {
541 		fImageMenu->FindItem(kMsgNoImage)->SetMarked(true);
542 		fPlacementMenu->FindItem(kMsgManualPlacement)->SetMarked(true);
543 		fIconLabelOutline->SetValue(B_CONTROL_ON);
544 	} else {
545 		fIconLabelOutline->SetValue(fCurrentInfo->fTextWidgetLabelOutline
546 			? B_CONTROL_ON : B_CONTROL_OFF);
547 
548 		fLastImageIndex = fCurrentInfo->fImageIndex;
549 		FindImageItem(fLastImageIndex)->SetMarked(true);
550 
551 		if (fLastImageIndex > -1) {
552 
553 			BString xtext, ytext;
554 			int32 cmd = 0;
555 			switch (fCurrentInfo->fMode) {
556 				case BackgroundImage::kCentered:
557 					cmd = kMsgCenterPlacement;
558 					break;
559 				case BackgroundImage::kScaledToFit:
560 					cmd = kMsgScalePlacement;
561 					break;
562 				case BackgroundImage::kAtOffset:
563 					cmd = kMsgManualPlacement;
564 					xtext << (int)fCurrentInfo->fOffset.x;
565 					ytext << (int)fCurrentInfo->fOffset.y;
566 					break;
567 				case BackgroundImage::kTiled:
568 					cmd = kMsgTilePlacement;
569 					break;
570 			}
571 
572 			if (cmd != 0)
573 				fPlacementMenu->FindItem(cmd)->SetMarked(true);
574 
575 			fXPlacementText->SetText(xtext.String());
576 			fYPlacementText->SetText(ytext.String());
577 		} else {
578 			fPlacementMenu->FindItem(kMsgManualPlacement)->SetMarked(true);
579 		}
580 	}
581 
582 	rgb_color color = {255, 255, 255, 255};
583 	if (fCurrent->IsDesktop()) {
584 		color = BScreen().DesktopColor();
585 		fPicker->SetEnabled(true);
586 	} else
587 		fPicker->SetEnabled(false);
588 
589 	fPicker->SetValue(color);
590 
591 	UpdatePreview();
592 	UpdateButtons();
593 }
594 
595 
596 void
597 BackgroundsView::Save()
598 {
599 	bool textWidgetLabelOutline =
600 		fIconLabelOutline->Value() == B_CONTROL_ON;
601 
602 	BackgroundImage::Mode mode = FindPlacementMode();
603 	BPoint offset(atoi(fXPlacementText->Text()), atoi(fYPlacementText->Text()));
604 
605 	if (!fCurrent->IsDesktop()) {
606 		if (fCurrentInfo == NULL) {
607 			fCurrentInfo = new BackgroundImage::BackgroundImageInfo(B_ALL_WORKSPACES,
608 				fLastImageIndex, mode, offset, textWidgetLabelOutline, 0, 0);
609 			fCurrent->Add(fCurrentInfo);
610 		} else {
611 			fCurrentInfo->fTextWidgetLabelOutline = textWidgetLabelOutline;
612 			fCurrentInfo->fMode = mode;
613 			if (fCurrentInfo->fMode == BackgroundImage::kAtOffset)
614 				fCurrentInfo->fOffset = offset;
615 			fCurrentInfo->fImageIndex = fLastImageIndex;
616 		}
617 	} else {
618 		uint32 workspaceMask = 1;
619 		int32 workspace = current_workspace();
620 		for (; workspace; workspace--)
621 			workspaceMask *= 2;
622 
623 		if (fCurrentInfo != NULL) {
624 			if (fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()) {
625 				if (fCurrentInfo->fWorkspace & workspaceMask
626 					&& fCurrentInfo->fWorkspace != workspaceMask) {
627 					fCurrentInfo->fWorkspace = fCurrentInfo->fWorkspace
628 						^ workspaceMask;
629 					fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
630 						workspaceMask, fLastImageIndex, mode, offset,
631 						textWidgetLabelOutline, fCurrentInfo->fImageSet,
632 						fCurrentInfo->fCacheMode);
633 					fCurrent->Add(fCurrentInfo);
634 				} else if (fCurrentInfo->fWorkspace == workspaceMask) {
635 					fCurrentInfo->fTextWidgetLabelOutline =
636 						textWidgetLabelOutline;
637 					fCurrentInfo->fMode = mode;
638 					if (fCurrentInfo->fMode == BackgroundImage::kAtOffset)
639 						fCurrentInfo->fOffset = offset;
640 
641 					fCurrentInfo->fImageIndex = fLastImageIndex;
642 				}
643 			} else {
644 				fCurrent->RemoveAll();
645 
646 				fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
647 					B_ALL_WORKSPACES, fLastImageIndex, mode, offset,
648 					textWidgetLabelOutline, fCurrent->GetShowingImageSet(),
649 					fCurrentInfo->fCacheMode);
650 				fCurrent->Add(fCurrentInfo);
651 			}
652 		} else {
653 			if (fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()) {
654 				fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
655 					workspaceMask, fLastImageIndex, mode, offset,
656 					textWidgetLabelOutline, fCurrent->GetShowingImageSet(), 0);
657 			} else {
658 				fCurrent->RemoveAll();
659 				fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
660 					B_ALL_WORKSPACES, fLastImageIndex, mode, offset,
661 					textWidgetLabelOutline, fCurrent->GetShowingImageSet(), 0);
662 			}
663 			fCurrent->Add(fCurrentInfo);
664 		}
665 
666 		if (!fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()) {
667 			for (int32 i = 0; i < count_workspaces(); i++) {
668 				BScreen().SetDesktopColor(fPicker->ValueAsColor(), i, true);
669 			}
670 		} else
671 			BScreen().SetDesktopColor(fPicker->ValueAsColor(), true);
672 	}
673 
674 	BNode node(&fCurrentRef);
675 
676 	status_t status = fCurrent->SetBackgroundImage(&node);
677 	if (status != B_OK) {
678 		// TODO: this should be a BAlert!
679 		printf("setting background image failed: %s\n", strerror(status));
680 	}
681 }
682 
683 
684 void
685 BackgroundsView::NotifyServer()
686 {
687 	BMessenger tracker("application/x-vnd.Be-TRAK");
688 
689 	if (fCurrent->IsDesktop()) {
690 		tracker.SendMessage(new BMessage(B_RESTORE_BACKGROUND_IMAGE));
691 	} else {
692 		int32 i = -1;
693 		BMessage reply;
694 		int32 err;
695 		BEntry currentEntry(&fCurrentRef);
696 		BPath currentPath(&currentEntry);
697 		bool isCustomFolder = !fWorkspaceMenu->FindItem(kMsgDefaultFolder)->IsMarked();
698 
699 		do {
700 			BMessage msg(B_GET_PROPERTY);
701 			i++;
702 
703 			// look at the "Poses" in every Tracker window
704 			msg.AddSpecifier("Poses");
705 			msg.AddSpecifier("Window", i);
706 
707 			reply.MakeEmpty();
708 			tracker.SendMessage(&msg, &reply);
709 
710 			// break out of the loop when we're at the end of
711 			// the windows
712 			if (reply.what == B_MESSAGE_NOT_UNDERSTOOD
713 				&& reply.FindInt32("error", &err) == B_OK
714 				&& err == B_BAD_INDEX)
715 				break;
716 
717 			// don't stop for windows that don't understand
718 			// a request for "Poses"; they're not displaying
719 			// folders
720 			if (reply.what == B_MESSAGE_NOT_UNDERSTOOD
721 				&& reply.FindInt32("error", &err) == B_OK
722 				&& err != B_BAD_SCRIPT_SYNTAX)
723 				continue;
724 
725 			BMessenger trackerWindow;
726 			if (reply.FindMessenger("result", &trackerWindow) != B_OK)
727 				continue;
728 
729 			if (isCustomFolder) {
730 				// found a window with poses, ask for its path
731 				msg.MakeEmpty();
732 				msg.what = B_GET_PROPERTY;
733 				msg.AddSpecifier("Path");
734 				msg.AddSpecifier("Poses");
735 				msg.AddSpecifier("Window", i);
736 
737 				reply.MakeEmpty();
738 				tracker.SendMessage(&msg, &reply);
739 
740 				// go on with the next if this din't have a path
741 				if (reply.what == B_MESSAGE_NOT_UNDERSTOOD)
742 					continue;
743 
744 				entry_ref ref;
745 				if (reply.FindRef("result", &ref) == B_OK) {
746 					BEntry entry(&ref);
747 					BPath path(&entry);
748 
749 					// these are not the paths you're looking for
750 					if (currentPath != path)
751 						continue;
752 				}
753 			}
754 
755 			trackerWindow.SendMessage(B_RESTORE_BACKGROUND_IMAGE);
756 		} while (true);
757 	}
758 }
759 
760 
761 int32
762 BackgroundsView::NotifyThread(void *data)
763 {
764 	BackgroundsView *view = (BackgroundsView*)data;
765 
766 	view->NotifyServer();
767 	return B_OK;
768 }
769 
770 
771 void
772 BackgroundsView::SaveSettings(void)
773 {
774 	BPath path;
775 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
776 		path.Append(SETTINGS_FILE);
777 		BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
778 
779 		BPoint point = Window()->Frame().LeftTop();
780 		if (fSettings.ReplacePoint("pos", point) != B_OK)
781 			fSettings.AddPoint("pos", point);
782 
783 		entry_ref ref;
784 		BEntry entry;
785 
786 		fPanel->GetPanelDirectory(&ref);
787 		entry.SetTo(&ref);
788 		entry.GetPath(&path);
789 		if (fSettings.ReplaceString("paneldir", path.Path()) != B_OK)
790 			fSettings.AddString("paneldir", path.Path());
791 
792 		fFolderPanel->GetPanelDirectory(&ref);
793 		entry.SetTo(&ref);
794 		entry.GetPath(&path);
795 		if (fSettings.ReplaceString("folderpaneldir", path.Path()) != B_OK)
796 			fSettings.AddString("folderpaneldir", path.Path());
797 
798 		fSettings.RemoveName("recentfolder");
799 		for (int32 i = 0; i < fPathList.CountItems(); i++) {
800 			fSettings.AddString("recentfolder", fPathList.ItemAt(i)->Path());
801 		}
802 
803 		fSettings.Flatten(&file);
804 	}
805 }
806 
807 
808 void
809 BackgroundsView::LoadSettings()
810 {
811 	fSettings.MakeEmpty();
812 
813 	BPath path;
814 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
815 		return;
816 
817 	path.Append(SETTINGS_FILE);
818 	BFile file(path.Path(), B_READ_ONLY);
819 	if (file.InitCheck() != B_OK)
820 		return;
821 
822 	if (fSettings.Unflatten(&file) != B_OK) {
823 		printf("Error unflattening settings file %s\n", path.Path());
824 		return;
825 	}
826 
827 	PRINT_OBJECT(fSettings);
828 
829 	BString string;
830 	if (fSettings.FindString("paneldir", &string) == B_OK)
831 		fPanel->SetPanelDirectory(string.String());
832 
833 	if (fSettings.FindString("folderpaneldir", &string) == B_OK)
834 		fFolderPanel->SetPanelDirectory(string.String());
835 
836 	int32 index = 0;
837 	while (fSettings.FindString("recentfolder", index, &string) == B_OK) {
838 		if (index == 0)
839 			fWorkspaceMenu->AddSeparatorItem();
840 
841 		path.SetTo(string.String());
842 		int32 i = AddPath(path);
843 		BString s;
844 		s << "Folder: " << path.Leaf();
845 		BMenuItem *item = new BMenuItem(s.String(),
846 			new BMessage(kMsgFolderSelected));
847 		fWorkspaceMenu->AddItem(item, -i - 1 + 6);
848 		index++;
849 	}
850 	fWorkspaceMenu->SetTargetForItems(this);
851 
852 	PRINT(("Settings Loaded\n"));
853 }
854 
855 
856 void
857 BackgroundsView::WorkspaceActivated(uint32 oldWorkspaces, bool active)
858 {
859 	UpdateWithCurrent();
860 }
861 
862 
863 void
864 BackgroundsView::UpdatePreview()
865 {
866 	bool imageEnabled = !(fImageMenu->FindItem(kMsgNoImage)->IsMarked());
867 	if (fPlacementMenu->IsEnabled() ^ imageEnabled)
868 		fPlacementMenu->SetEnabled(imageEnabled);
869 
870 	bool textEnabled = (fPlacementMenu->FindItem(kMsgManualPlacement)->IsMarked())
871 		&& imageEnabled;
872 	if (fXPlacementText->IsEnabled() ^ textEnabled)
873 		fXPlacementText->SetEnabled(textEnabled);
874 	if (fYPlacementText->IsEnabled() ^ textEnabled)
875 		fYPlacementText->SetEnabled(textEnabled);
876 
877 	if (textEnabled && (strcmp(fXPlacementText->Text(), "") == 0)) {
878 		fXPlacementText->SetText("0");
879 		fYPlacementText->SetText("0");
880 	}
881 	if (!textEnabled) {
882 		fXPlacementText->SetText(NULL);
883 		fYPlacementText->SetText(NULL);
884 	}
885 
886 	fXPlacementText->TextView()->MakeSelectable(textEnabled);
887 	fYPlacementText->TextView()->MakeSelectable(textEnabled);
888 	fXPlacementText->TextView()->MakeEditable(textEnabled);
889 	fYPlacementText->TextView()->MakeEditable(textEnabled);
890 
891 	fPreView->ClearViewBitmap();
892 
893 	int32 index = ((BGImageMenuItem*)fImageMenu->FindMarked())->ImageIndex();
894 	if (index >= 0) {
895 		BBitmap *bitmap = GetImage(index)->GetBitmap();
896 		if (bitmap) {
897 			BackgroundImage::BackgroundImageInfo *info =
898 				new BackgroundImage::BackgroundImageInfo(0, index,
899 					FindPlacementMode(), BPoint(atoi(fXPlacementText->Text()),
900 						atoi(fYPlacementText->Text())),
901 					fIconLabelOutline->Value() == B_CONTROL_ON, 0, 0);
902 			if (info->fMode == BackgroundImage::kAtOffset) {
903 				fPreView->SetEnabled(true);
904 				fPreView->fPoint.x = atoi(fXPlacementText->Text());
905 				fPreView->fPoint.y = atoi(fYPlacementText->Text());
906 			} else
907 				fPreView->SetEnabled(false);
908 
909 			fPreView->fImageBounds = BRect(bitmap->Bounds());
910 			fCurrent->Show(info, fPreView);
911 		}
912 	} else
913 		fPreView->SetEnabled(false);
914 
915 	fPreView->SetViewColor(fPicker->ValueAsColor());
916 	fPreView->Invalidate();
917 }
918 
919 
920 BackgroundImage::Mode
921 BackgroundsView::FindPlacementMode()
922 {
923 	BackgroundImage::Mode mode = BackgroundImage::kAtOffset;
924 
925 	if (fPlacementMenu->FindItem(kMsgCenterPlacement)->IsMarked())
926 		mode = BackgroundImage::kCentered;
927 	if (fPlacementMenu->FindItem(kMsgScalePlacement)->IsMarked())
928 		mode = BackgroundImage::kScaledToFit;
929 	if (fPlacementMenu->FindItem(kMsgManualPlacement)->IsMarked())
930 		mode = BackgroundImage::kAtOffset;
931 	if (fPlacementMenu->FindItem(kMsgTilePlacement)->IsMarked())
932 		mode = BackgroundImage::kTiled;
933 
934 	return mode;
935 }
936 
937 
938 #ifndef __HAIKU__
939 inline bool operator!=(const rgb_color& x, const rgb_color& y)
940 {
941 	return (x.red != y.red || x.blue != y.blue || x.green != y.green);
942 }
943 #endif
944 
945 
946 void
947 BackgroundsView::UpdateButtons()
948 {
949 	bool hasChanged = false;
950 	if (fPicker->IsEnabled()
951 		&& fPicker->ValueAsColor() != BScreen().DesktopColor()) {
952 		hasChanged = true;
953 	} else if (fCurrentInfo) {
954 		if ((fIconLabelOutline->Value() == B_CONTROL_ON) ^
955 			fCurrentInfo->fTextWidgetLabelOutline) {
956 			hasChanged = true;
957 		} else if (FindPlacementMode() != fCurrentInfo->fMode) {
958 			hasChanged = true;
959 		} else if (fCurrentInfo->fImageIndex !=
960 			((BGImageMenuItem*)fImageMenu->FindMarked())->ImageIndex()) {
961 			hasChanged = true;
962 		} else if (fCurrent->IsDesktop()
963 			&& ((fCurrentInfo->fWorkspace != B_ALL_WORKSPACES)
964 				^ (fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()))) {
965 			hasChanged = true;
966 		} else if (fCurrentInfo->fImageIndex > -1 &&
967 			fCurrentInfo->fMode == BackgroundImage::kAtOffset) {
968 			BString oldString, newString;
969 			oldString << (int)fCurrentInfo->fOffset.x;
970 			if (oldString != BString(fXPlacementText->Text())) {
971 				hasChanged = true;
972 			}
973 			oldString = "";
974 			oldString << (int)fCurrentInfo->fOffset.y;
975 			if (oldString != BString(fYPlacementText->Text())) {
976 				hasChanged = true;
977 			}
978 		}
979 	} else if (fImageMenu->IndexOf(fImageMenu->FindMarked()) > 0) {
980 		hasChanged = true;
981 	} else if (fIconLabelOutline->Value() == B_CONTROL_OFF) {
982 		hasChanged = true;
983 	}
984 
985 	fApply->SetEnabled(hasChanged);
986 	fRevert->SetEnabled(hasChanged);
987 }
988 
989 
990 void
991 BackgroundsView::RefsReceived(BMessage *msg)
992 {
993 	entry_ref ref;
994 	int32 i = 0;
995 	BMimeType imageType("image");
996 	BPath desktopPath;
997 	find_directory(B_DESKTOP_DIRECTORY, &desktopPath);
998 
999 	while (msg->FindRef("refs", i++, &ref) == B_OK) {
1000 		BPath path;
1001 		BEntry entry(&ref, true);
1002 		path.SetTo(&entry);
1003 		BNode node(&entry);
1004 
1005 		if (node.IsFile()) {
1006 			BMimeType refType;
1007 			BMimeType::GuessMimeType(&ref, &refType);
1008 			if (!imageType.Contains(&refType))
1009 				continue;
1010 
1011 			BGImageMenuItem *item;
1012 			int32 index = AddImage(path);
1013 			if (index >= 0) {
1014 				item = FindImageItem(index);
1015 				fLastImageIndex = index;
1016 			} else {
1017 				const char* name = GetImage(-index - 1)->GetName();
1018 				item = new BGImageMenuItem(name, -index - 1,
1019 					new BMessage(kMsgImageSelected));
1020 				AddItem(item);
1021 				item->SetTarget(this);
1022 				fLastImageIndex = -index - 1;
1023 			}
1024 
1025 			// An optional placement may have been sent
1026 			int32 placement = 0;
1027 			if (msg->FindInt32("placement", &placement) == B_OK) {
1028 				BMenuItem* item = fPlacementMenu->FindItem(placement);
1029 				if (item)
1030 					item->SetMarked(true);
1031 			}
1032 			item->SetMarked(true);
1033 			BMessenger(this).SendMessage(kMsgImageSelected);
1034 		} else if (node.IsDirectory()) {
1035 			if (desktopPath == path) {
1036 				fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->SetMarked(true);
1037 				BMessenger(this).SendMessage(kMsgCurrentWorkspace);
1038 				break;
1039 			}
1040 			BMenuItem *item;
1041 			int32 index = AddPath(path);
1042 			if (index >= 0) {
1043 				item = fWorkspaceMenu->ItemAt(index + 6);
1044 				fLastWorkspaceIndex = index + 6;
1045 			} else {
1046 				if (fWorkspaceMenu->CountItems() <= 5)
1047 					fWorkspaceMenu->AddSeparatorItem();
1048 				BString s;
1049 				s << "Folder: " << path.Leaf();
1050 				item = new BMenuItem(s.String(), new BMessage(kMsgFolderSelected));
1051 				fWorkspaceMenu->AddItem(item, -index - 1 + 6);
1052 				item->SetTarget(this);
1053 				fLastWorkspaceIndex = -index - 1 + 6;
1054 			}
1055 
1056 			item->SetMarked(true);
1057 			BMessenger(this).SendMessage(kMsgFolderSelected);
1058 		}
1059 	}
1060 }
1061 
1062 
1063 int32
1064 BackgroundsView::AddPath(BPath path)
1065 {
1066 	int32 count = fPathList.CountItems();
1067 	int32 index = 0;
1068 	for (; index < count; index++) {
1069 		BPath *p = fPathList.ItemAt(index);
1070 		int c = BString(p->Path()).ICompare(path.Path());
1071 		if (c == 0)
1072 			return index;
1073 
1074 		if (c > 0)
1075 			break;
1076 	}
1077 	fPathList.AddItem(new BPath(path), index);
1078 	return -index - 1;
1079 }
1080 
1081 
1082 int32
1083 BackgroundsView::AddImage(BPath path)
1084 {
1085 	int32 count = fImageList.CountItems();
1086 	int32 index = 0;
1087 	for (; index < count; index++) {
1088 		Image *image = fImageList.ItemAt(index);
1089 		if (image->GetPath() == path)
1090 			return index;
1091 	}
1092 
1093 	fImageList.AddItem(new Image(path));
1094 	return -index - 1;
1095 }
1096 
1097 
1098 void
1099 BackgroundsView::ProcessRefs(entry_ref dir, BMessage* refs)
1100 {
1101 	fWorkspaceMenu->FindItem(kMsgDefaultFolder)->SetMarked(true);
1102 	BMessenger messenger(this);
1103 	messenger.SendMessage(kMsgDefaultFolder);
1104 
1105 	if (refs->CountNames(B_REF_TYPE) > 0) {
1106 		messenger.SendMessage(refs);
1107 	} else {
1108 		BMessage message(B_REFS_RECEIVED);
1109 		message.AddRef("refs", &dir);
1110 		messenger.SendMessage(&message);
1111 	}
1112 }
1113 
1114 
1115 Image*
1116 BackgroundsView::GetImage(int32 imageIndex)
1117 {
1118 	return fImageList.ItemAt(imageIndex);
1119 }
1120 
1121 
1122 BGImageMenuItem*
1123 BackgroundsView::FindImageItem(const int32 imageIndex)
1124 {
1125 	if (imageIndex < 0)
1126 		return (BGImageMenuItem *)fImageMenu->ItemAt(0);
1127 
1128 	int32 count = fImageMenu->CountItems() - 2;
1129 	int32 index = 2;
1130 	for (; index < count; index++) {
1131 		BGImageMenuItem *image = (BGImageMenuItem *)fImageMenu->ItemAt(index);
1132 		if (image->ImageIndex() == imageIndex)
1133 			return image;
1134 	}
1135 	return NULL;
1136 }
1137 
1138 
1139 bool
1140 BackgroundsView::AddItem(BGImageMenuItem *item)
1141 {
1142 	int32 count = fImageMenu->CountItems() - 2;
1143 	int32 index = 2;
1144 	if (count < index) {
1145 		fImageMenu->AddItem(new BSeparatorItem(), 1);
1146 		count = fImageMenu->CountItems() - 2;
1147 	}
1148 
1149 	for (; index < count; index++) {
1150 		BGImageMenuItem* image = (BGImageMenuItem *)fImageMenu->ItemAt(index);
1151 		int c = (BString(image->Label()).ICompare(BString(item->Label())));
1152 		if (c > 0)
1153 			break;
1154 	}
1155 	return fImageMenu->AddItem(item, index);
1156 }
1157 
1158 
1159 //	#pragma mark -
1160 
1161 
1162 PreView::PreView(BRect frame, const char *name, int32 resize, int32 flags)
1163 	: BControl(frame, name, NULL, NULL, resize, flags),
1164 	fMoveHandCursor(kHandCursorData)
1165 {
1166 }
1167 
1168 
1169 void
1170 PreView::AttachedToWindow()
1171 {
1172 	rgb_color color = ViewColor();
1173 	BControl::AttachedToWindow();
1174 	SetViewColor(color);
1175 }
1176 
1177 
1178 void
1179 PreView::MouseDown(BPoint point)
1180 {
1181 	if (IsEnabled() && Bounds().Contains(point)) {
1182 		uint32 buttons;
1183 		GetMouse(&point, &buttons);
1184 		if (buttons & B_PRIMARY_MOUSE_BUTTON) {
1185 			fOldPoint = point;
1186 			SetTracking(true);
1187 			BScreen().GetMode(&mode);
1188 			x_ratio = Bounds().Width() / mode.virtual_width;
1189 			y_ratio = Bounds().Height() / mode.virtual_height;
1190 			SetMouseEventMask(B_POINTER_EVENTS,
1191 				B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
1192 		}
1193 	}
1194 }
1195 
1196 
1197 void
1198 PreView::MouseUp(BPoint point)
1199 {
1200 	if (IsTracking())
1201 		SetTracking(false);
1202 }
1203 
1204 
1205 void
1206 PreView::MouseMoved(BPoint point, uint32 transit, const BMessage *message)
1207 {
1208 	if (IsEnabled())
1209 		SetViewCursor(&fMoveHandCursor);
1210 	else
1211 		SetViewCursor(B_CURSOR_SYSTEM_DEFAULT);
1212 
1213 	if (IsTracking()) {
1214 		float x, y;
1215 		x = fPoint.x + (point.x - fOldPoint.x) / x_ratio;
1216 		y = fPoint.y + (point.y - fOldPoint.y) / y_ratio;
1217 		bool min, max, mustSend = false;
1218 		min = (x > -fImageBounds.Width());
1219 		max = (x < mode.virtual_width);
1220 		if (min && max) {
1221 			fOldPoint.x = point.x;
1222 			fPoint.x = x;
1223 			mustSend = true;
1224 		} else {
1225 			if (!min && fPoint.x > -fImageBounds.Width()) {
1226 				fPoint.x = -fImageBounds.Width();
1227 				fOldPoint.x = point.x - (x - fPoint.x) * x_ratio;
1228 				mustSend = true;
1229 			}
1230 			if (!max && fPoint.x < mode.virtual_width) {
1231 				fPoint.x = mode.virtual_width;
1232 				fOldPoint.x = point.x - (x - fPoint.x) * x_ratio;
1233 				mustSend = true;
1234 			}
1235 		}
1236 
1237 		min = (y > -fImageBounds.Height());
1238 		max = (y < mode.virtual_height);
1239 		if (min && max) {
1240 			fOldPoint.y = point.y;
1241 			fPoint.y = y;
1242 			mustSend = true;
1243 		} else {
1244 			if (!min && fPoint.y > -fImageBounds.Height()) {
1245 				fPoint.y = -fImageBounds.Height();
1246 				fOldPoint.y = point.y - (y - fPoint.y) * y_ratio;
1247 				mustSend = true;
1248 			}
1249 			if (!max && fPoint.y < mode.virtual_height) {
1250 				fPoint.y = mode.virtual_height;
1251 				fOldPoint.y = point.y - (y - fPoint.y) * y_ratio;
1252 				mustSend = true;
1253 			}
1254 		}
1255 
1256 		if (mustSend) {
1257 			BMessenger messenger(Parent());
1258 			messenger.SendMessage(kMsgUpdatePreviewPlacement);
1259 		}
1260 	}
1261 	BControl::MouseMoved(point, transit, message);
1262 }
1263 
1264 
1265 //	#pragma mark -
1266 
1267 
1268 PreviewBox::PreviewBox(BRect frame, const char *name, float _top)
1269 	:
1270 	BBox(frame, name),
1271 	fDrawingTop(_top)
1272 {
1273 	fIsDesktop = true;
1274 }
1275 
1276 
1277 void
1278 PreviewBox::Draw(BRect rect)
1279 {
1280 	rgb_color color = HighColor();
1281 
1282 	SetHighColor(LowColor());
1283 	FillRect(BRect(9, 19, 141, 127));
1284 
1285 	if (fIsDesktop) {
1286 		BPoint points[] = {
1287 			BPoint(11, fDrawingTop), BPoint(139, fDrawingTop),
1288 			BPoint(141, fDrawingTop + 2),
1289 			BPoint(141, 119), BPoint(139, 121), BPoint(118, 121),
1290 			BPoint(118, 126), BPoint(117, 127), BPoint(33, 127),
1291 			BPoint(32, 126), BPoint(32, 121), BPoint(11, 121),
1292 			BPoint(9, 119), BPoint(9, fDrawingTop + 2),
1293 			BPoint(11, fDrawingTop)
1294 		};
1295 		SetHighColor(184, 184, 184);
1296 		FillPolygon(points, 15);
1297 		SetHighColor(96, 96, 96);
1298 		StrokePolygon(points, 15);
1299 		FillRect(BRect(107, 121, 111, 123));
1300 		SetHighColor(0, 0, 0);
1301 		StrokeRect(BRect(14, fDrawingTop + 5, 136, 116));
1302 		SetHighColor(0, 255, 0);
1303 		FillRect(BRect(101, 122, 103, 123));
1304 	} else {
1305 		SetHighColor(152, 152, 152);
1306 		StrokeLine(BPoint(11, 13), BPoint(67, 13));
1307 		StrokeLine(BPoint(67, 21));
1308 		StrokeLine(BPoint(139, 21));
1309 		StrokeLine(BPoint(139, 119));
1310 		StrokeLine(BPoint(11, 119));
1311 		StrokeLine(BPoint(11, 13));
1312 		StrokeRect(BRect(14, 24, 136, 116));
1313 		SetHighColor(255, 203, 0);
1314 		FillRect(BRect(12, 14, 66, 21));
1315 		SetHighColor(240, 240, 240);
1316 		StrokeRect(BRect(12, 22, 137, 117));
1317 		StrokeLine(BPoint(138, 22), BPoint(138, 22));
1318 		StrokeLine(BPoint(12, 118), BPoint(12, 118));
1319 		SetHighColor(200, 200, 200);
1320 		StrokeRect(BRect(13, 23, 138, 118));
1321 	}
1322 
1323 	SetHighColor(color);
1324 	BBox::Draw(rect);
1325 }
1326 
1327 
1328 void
1329 PreviewBox::SetDesktop(bool isDesktop)
1330 {
1331 	fIsDesktop = isDesktop;
1332 	Invalidate();
1333 }
1334 
1335 
1336 //	#pragma mark -
1337 
1338 
1339 BGImageMenuItem::BGImageMenuItem(const char *label, int32 imageIndex,
1340 	BMessage *message, char shortcut, uint32 modifiers)
1341 	: BMenuItem(label, message, shortcut, modifiers),
1342 	fImageIndex(imageIndex)
1343 {
1344 }
1345