xref: /haiku/src/preferences/backgrounds/BackgroundsView.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
1 /*
2  * Copyright 2002-2015 Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Axel Dörfler, axeld@pinc-software.de
7  *		Jerome Duval, jerome.duval@free.fr
8  *		Jonas Sundström, jonas@kirilla.se
9  *		John Scipione, jscipione@gmail.com
10  */
11 
12 
13 #include "BackgroundsView.h"
14 
15 #include <algorithm>
16 
17 #include <stdio.h>
18 #include <stdlib.h>
19 
20 #include <Bitmap.h>
21 #include <Catalog.h>
22 #include <ControlLook.h>
23 #include <Cursor.h>
24 #include <Debug.h>
25 #include <File.h>
26 #include <FindDirectory.h>
27 #include <LayoutBuilder.h>
28 #include <Locale.h>
29 #include <MenuField.h>
30 #include <Messenger.h>
31 #include <MimeType.h>
32 #include <Point.h>
33 #include <PopUpMenu.h>
34 
35 #include <be_apps/Tracker/Background.h>
36 
37 #include "ImageFilePanel.h"
38 
39 
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "Main View"
42 
43 
44 static const uint32 kMsgApplySettings = 'aply';
45 static const uint32 kMsgRevertSettings = 'rvrt';
46 
47 static const uint32 kMsgUpdateColor = 'upcl';
48 static const uint32 kMsgAllWorkspaces = 'alwk';
49 static const uint32 kMsgCurrentWorkspace = 'crwk';
50 static const uint32 kMsgDefaultFolder = 'dffl';
51 static const uint32 kMsgOtherFolder = 'otfl';
52 static const uint32 kMsgNoImage = 'noim';
53 static const uint32 kMsgOtherImage = 'otim';
54 static const uint32 kMsgImageSelected = 'imsl';
55 static const uint32 kMsgFolderSelected = 'flsl';
56 
57 static const uint32 kMsgCenterPlacement = 'cnpl';
58 static const uint32 kMsgManualPlacement = 'mnpl';
59 static const uint32 kMsgScalePlacement = 'scpl';
60 static const uint32 kMsgTilePlacement = 'tlpl';
61 static const uint32 kMsgIconLabelOutline = 'ilol';
62 
63 static const uint32 kMsgImagePlacement = 'xypl';
64 static const uint32 kMsgUpdatePreviewPlacement = 'pvpl';
65 
66 
67 BackgroundsView::BackgroundsView()
68 	:
69 	BBox("BackgroundsView"),
70 	fCurrent(NULL),
71 	fCurrentInfo(NULL),
72 	fLastImageIndex(-1),
73 	fPathList(1, true),
74 	fImageList(1, true),
75 	fFoundPositionSetting(false)
76 {
77 	SetBorder(B_NO_BORDER);
78 
79 	BBox* previewBox = new BBox("preview");
80 	previewBox->SetLabel(B_TRANSLATE("Preview"));
81 
82 	fPreview = new Preview();
83 
84 	fTopLeft = new FramePart(FRAME_TOP_LEFT);
85 	fTop = new FramePart(FRAME_TOP);
86 	fTopRight = new FramePart(FRAME_TOP_RIGHT);
87 	fLeft = new FramePart(FRAME_LEFT_SIDE);
88 	fRight = new FramePart(FRAME_RIGHT_SIDE);
89 	fBottomLeft = new FramePart(FRAME_BOTTOM_LEFT);
90 	fBottom = new FramePart(FRAME_BOTTOM);
91 	fBottomRight = new FramePart(FRAME_BOTTOM_RIGHT);
92 
93 	fXPlacementText = new BTextControl(B_TRANSLATE("X:"), NULL,
94 		new BMessage(kMsgImagePlacement));
95 	fYPlacementText = new BTextControl(B_TRANSLATE("Y:"), NULL,
96 		new BMessage(kMsgImagePlacement));
97 
98 	fXPlacementText->TextView()->SetMaxBytes(5);
99 	fYPlacementText->TextView()->SetMaxBytes(5);
100 
101 	for (int32 i = 0; i < 256; i++) {
102 		if ((i < '0' || i > '9') && i != '-') {
103 			fXPlacementText->TextView()->DisallowChar(i);
104 			fYPlacementText->TextView()->DisallowChar(i);
105 		}
106 	}
107 
108 	previewBox->AddChild(BLayoutBuilder::Group<>()
109 		.AddGlue()
110 		.AddGroup(B_VERTICAL, 0)
111 			.AddGroup(B_HORIZONTAL, 0)
112 				.AddGlue()
113 				.AddGrid(0, 0, 1)
114 					.Add(fTopLeft, 0, 0)
115 					.Add(fTop, 1, 0)
116 					.Add(fTopRight, 2, 0)
117 					.Add(fLeft, 0, 1)
118 					.Add(fPreview, 1, 1)
119 					.Add(fRight, 2, 1)
120 					.Add(fBottomLeft, 0, 2)
121 					.Add(fBottom, 1, 2)
122 					.Add(fBottomRight, 2, 2)
123 					.End()
124 				.AddGlue()
125 				.End()
126 			.AddStrut(be_control_look->DefaultItemSpacing() * 2)
127 			.AddGroup(B_HORIZONTAL)
128 				.Add(fXPlacementText)
129 				.Add(fYPlacementText)
130 				.End()
131 			.AddGlue()
132 			.SetInsets(B_USE_DEFAULT_SPACING)
133 			.End()
134 		.AddGlue()
135 		.View());
136 
137 	BBox* rightbox = new BBox("rightbox");
138 
139 	fWorkspaceMenu = new BPopUpMenu(B_TRANSLATE("pick one"));
140 	fWorkspaceMenu->AddItem(new BMenuItem(B_TRANSLATE("All workspaces"),
141 		new BMessage(kMsgAllWorkspaces)));
142 	BMenuItem* menuItem;
143 	fWorkspaceMenu->AddItem(menuItem = new BMenuItem(
144 		B_TRANSLATE("Current workspace"),
145 		new BMessage(kMsgCurrentWorkspace)));
146 	menuItem->SetMarked(true);
147 	fLastWorkspaceIndex =
148 		fWorkspaceMenu->IndexOf(fWorkspaceMenu->FindMarked());
149 	fWorkspaceMenu->AddSeparatorItem();
150 	fWorkspaceMenu->AddItem(new BMenuItem(B_TRANSLATE("Default folder"),
151 		new BMessage(kMsgDefaultFolder)));
152 	fWorkspaceMenu->AddItem(new BMenuItem(
153 		B_TRANSLATE("Other folder" B_UTF8_ELLIPSIS),
154 		new BMessage(kMsgOtherFolder)));
155 
156 	BMenuField* workspaceMenuField = new BMenuField("workspaceMenuField",
157 		NULL, fWorkspaceMenu);
158 	workspaceMenuField->ResizeToPreferred();
159 	rightbox->SetLabel(workspaceMenuField);
160 
161 	fImageMenu = new BPopUpMenu(B_TRANSLATE("pick one"));
162 	fImageMenu->AddItem(new BGImageMenuItem(B_TRANSLATE("None"), -1,
163 		new BMessage(kMsgNoImage)));
164 	fImageMenu->AddSeparatorItem();
165 	fImageMenu->AddItem(new BMenuItem(B_TRANSLATE("Other" B_UTF8_ELLIPSIS),
166 		new BMessage(kMsgOtherImage)));
167 
168 	BMenuField* imageMenuField = new BMenuField("image",
169 		B_TRANSLATE("Image:"), fImageMenu);
170 	imageMenuField->SetAlignment(B_ALIGN_RIGHT);
171 
172 	fPlacementMenu = new BPopUpMenu(B_TRANSLATE("pick one"));
173 	fPlacementMenu->AddItem(new BMenuItem(B_TRANSLATE("Manual"),
174 		new BMessage(kMsgManualPlacement)));
175 	fPlacementMenu->AddItem(new BMenuItem(B_TRANSLATE("Center"),
176 		new BMessage(kMsgCenterPlacement)));
177 	fPlacementMenu->AddItem(new BMenuItem(B_TRANSLATE("Scale to fit"),
178 		new BMessage(kMsgScalePlacement)));
179 	fPlacementMenu->AddItem(new BMenuItem(B_TRANSLATE("Tile"),
180 		new BMessage(kMsgTilePlacement)));
181 
182 	BMenuField* placementMenuField = new BMenuField("placement",
183 		B_TRANSLATE("Placement:"), fPlacementMenu);
184 	placementMenuField->SetAlignment(B_ALIGN_RIGHT);
185 
186 	fIconLabelOutline = new BCheckBox(B_TRANSLATE("Icon label outline"),
187 		new BMessage(kMsgIconLabelOutline));
188 	fIconLabelOutline->SetValue(B_CONTROL_OFF);
189 
190 	fPicker = new BColorControl(BPoint(0, 0), B_CELLS_32x8, 8.0, "Picker",
191 		new BMessage(kMsgUpdateColor));
192 
193 	rightbox->AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
194 		.AddGroup(B_HORIZONTAL, 0.0f)
195 			.AddGrid(B_USE_DEFAULT_SPACING, B_USE_SMALL_SPACING)
196 				.Add(imageMenuField->CreateLabelLayoutItem(), 0, 0)
197 				.AddGroup(B_HORIZONTAL, 0.0f, 1, 0)
198 					.Add(imageMenuField->CreateMenuBarLayoutItem(), 0.0f)
199 					.AddGlue()
200 					.End()
201 				.Add(placementMenuField->CreateLabelLayoutItem(), 0, 1)
202 				.AddGroup(B_HORIZONTAL, 0.0f, 1, 1)
203 					.Add(placementMenuField->CreateMenuBarLayoutItem(), 0.0f)
204 					.AddGlue()
205 					.End()
206 				.Add(fIconLabelOutline, 1, 2)
207 				.End()
208 			.AddGlue()
209 			.End()
210 		.AddGlue()
211 		.Add(fPicker)
212 		.SetInsets(B_USE_DEFAULT_SPACING)
213 		.View());
214 
215 	fRevert = new BButton(B_TRANSLATE("Revert"),
216 		new BMessage(kMsgRevertSettings));
217 	fApply = new BButton(B_TRANSLATE("Apply"),
218 		new BMessage(kMsgApplySettings));
219 
220 	fRevert->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT,
221 		B_ALIGN_NO_VERTICAL));
222 	fApply->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT,
223 		B_ALIGN_NO_VERTICAL));
224 
225 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
226 		.SetInsets(B_USE_WINDOW_SPACING)
227 		.AddGroup(B_HORIZONTAL, 0)
228 			.AddGroup(B_VERTICAL, 0)
229 				.AddStrut(floorf(rightbox->TopBorderOffset()
230 					- previewBox->TopBorderOffset()) - 1)
231 				.Add(previewBox)
232 				.End()
233 			.AddStrut(B_USE_DEFAULT_SPACING)
234 			.Add(rightbox)
235 			.End()
236 		.AddGroup(B_HORIZONTAL, 0)
237 			.Add(fRevert)
238 			.Add(fApply)
239 			.SetInsets(0, B_USE_DEFAULT_SPACING, 0,	0)
240 			.End();
241 
242 	fApply->MakeDefault(true);
243 }
244 
245 
246 BackgroundsView::~BackgroundsView()
247 {
248 	// The order matter. The last panel saves the state
249 	delete fFolderPanel;
250 	delete fPanel;
251 }
252 
253 
254 void
255 BackgroundsView::AllAttached()
256 {
257 	fPlacementMenu->SetTargetForItems(this);
258 	fImageMenu->SetTargetForItems(this);
259 	fWorkspaceMenu->SetTargetForItems(this);
260 	fXPlacementText->SetTarget(this);
261 	fYPlacementText->SetTarget(this);
262 	fIconLabelOutline->SetTarget(this);
263 	fPicker->SetTarget(this);
264 	fApply->SetTarget(this);
265 	fRevert->SetTarget(this);
266 
267 	BPath path;
268 	entry_ref ref;
269 	if (find_directory(B_SYSTEM_DATA_DIRECTORY, &path) == B_OK) {
270 		path.Append("artwork");
271 		get_ref_for_path(path.Path(), &ref);
272 	}
273 
274 	BMessenger messenger(this);
275 	fPanel = new ImageFilePanel(B_OPEN_PANEL, &messenger, &ref,
276 		B_FILE_NODE, false, NULL, new CustomRefFilter(true));
277 	fPanel->SetButtonLabel(B_DEFAULT_BUTTON, B_TRANSLATE("Select"));
278 
279 	fFolderPanel = new BFilePanel(B_OPEN_PANEL, &messenger, NULL,
280 		B_DIRECTORY_NODE, false, NULL, new CustomRefFilter(false));
281 	fFolderPanel->SetButtonLabel(B_DEFAULT_BUTTON, B_TRANSLATE("Select"));
282 
283 	_LoadSettings();
284 	_LoadDesktopFolder();
285 
286 	BPoint point;
287 	if (fSettings.FindPoint("pos", &point) == B_OK) {
288 		fFoundPositionSetting = true;
289 		Window()->MoveTo(point);
290 	}
291 
292 	fApply->SetEnabled(false);
293 	fRevert->SetEnabled(false);
294 }
295 
296 
297 void
298 BackgroundsView::MessageReceived(BMessage* message)
299 {
300 	// Color drop
301 	if (message->WasDropped()) {
302 		rgb_color *clr;
303 		ssize_t out_size;
304 		if (message->FindData("RGBColor", B_RGB_COLOR_TYPE,
305 			(const void **)&clr, &out_size) == B_OK) {
306 			fPicker->SetValue(*clr);
307 			_UpdatePreview();
308 			_UpdateButtons();
309 			return;
310 		}
311 	}
312 
313 	switch (message->what) {
314 		case B_SIMPLE_DATA:
315 		case B_REFS_RECEIVED:
316 			RefsReceived(message);
317 			break;
318 
319 		case kMsgUpdatePreviewPlacement:
320 		{
321 			BString xstring, ystring;
322 			xstring << (int)fPreview->fPoint.x;
323 			ystring << (int)fPreview->fPoint.y;
324 			fXPlacementText->SetText(xstring.String());
325 			fYPlacementText->SetText(ystring.String());
326 			_UpdatePreview();
327 			_UpdateButtons();
328 			break;
329 		}
330 
331 		case kMsgManualPlacement:
332 		case kMsgTilePlacement:
333 		case kMsgScalePlacement:
334 		case kMsgCenterPlacement:
335 			_UpdatePreview();
336 			_UpdateButtons();
337 			break;
338 
339 		case kMsgIconLabelOutline:
340 			_UpdateButtons();
341 			break;
342 
343 		case kMsgUpdateColor:
344 		case kMsgImagePlacement:
345 			_UpdatePreview();
346 			_UpdateButtons();
347 			break;
348 
349 		case kMsgCurrentWorkspace:
350 		case kMsgAllWorkspaces:
351 			fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("None"));
352 			fLastWorkspaceIndex = fWorkspaceMenu->IndexOf(
353 				fWorkspaceMenu->FindMarked());
354 			if (fCurrent && fCurrent->IsDesktop()) {
355 				_UpdateButtons();
356 			} else {
357 				_SetDesktop(true);
358 				_LoadDesktopFolder();
359 			}
360 			break;
361 
362 		case kMsgDefaultFolder:
363 			fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("None"));
364 			fLastWorkspaceIndex = fWorkspaceMenu->IndexOf(
365 				fWorkspaceMenu->FindMarked());
366 			_SetDesktop(false);
367 			_LoadDefaultFolder();
368 			break;
369 
370 		case kMsgOtherFolder:
371 			fFolderPanel->Show();
372 			break;
373 
374 		case kMsgOtherImage:
375 			fPanel->Show();
376 			break;
377 
378 		case B_CANCEL:
379 		{
380 			PRINT(("cancel received\n"));
381 			void* pointer;
382 			message->FindPointer("source", &pointer);
383 			if (pointer == fPanel) {
384 				if (fLastImageIndex >= 0)
385 					_FindImageItem(fLastImageIndex)->SetMarked(true);
386 				else
387 					fImageMenu->ItemAt(0)->SetMarked(true);
388 			} else if (pointer == fFolderPanel) {
389 				if (fLastWorkspaceIndex >= 0)
390 					fWorkspaceMenu->ItemAt(fLastWorkspaceIndex)
391 						->SetMarked(true);
392 			}
393 			break;
394 		}
395 
396 		case kMsgImageSelected:
397 		case kMsgNoImage:
398 			fLastImageIndex = ((BGImageMenuItem*)fImageMenu->FindMarked())
399 				->ImageIndex();
400 			_UpdatePreview();
401 			_UpdateButtons();
402 			break;
403 
404 		case kMsgFolderSelected:
405 			fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("Default"));
406 			fLastWorkspaceIndex = fWorkspaceMenu->IndexOf(
407 				fWorkspaceMenu->FindMarked());
408 			_SetDesktop(false);
409 
410 			_LoadRecentFolder(*fPathList.ItemAt(fWorkspaceMenu->IndexOf(
411 				fWorkspaceMenu->FindMarked()) - 6));
412 			break;
413 
414 		case kMsgApplySettings:
415 		{
416 			_Save();
417 
418 			//_NotifyServer();
419 			thread_id notify_thread;
420 			notify_thread = spawn_thread(BackgroundsView::_NotifyThread,
421 				"notifyServer", B_NORMAL_PRIORITY, this);
422 			resume_thread(notify_thread);
423 			_UpdateButtons();
424 			break;
425 		}
426 		case kMsgRevertSettings:
427 			_UpdateWithCurrent();
428 			break;
429 
430 		default:
431 			BView::MessageReceived(message);
432 			break;
433 	}
434 }
435 
436 
437 void
438 BackgroundsView::_LoadDesktopFolder()
439 {
440 	BPath path;
441 	if (find_directory(B_DESKTOP_DIRECTORY, &path) == B_OK) {
442 		status_t err;
443 		err = get_ref_for_path(path.Path(), &fCurrentRef);
444 		if (err != B_OK)
445 			printf("error in LoadDesktopSettings\n");
446 		_LoadFolder(true);
447 	}
448 }
449 
450 
451 void
452 BackgroundsView::_LoadDefaultFolder()
453 {
454 	BPath path;
455 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
456 		BString pathString = path.Path();
457 		pathString << "/Tracker/DefaultFolderTemplate";
458 		status_t err;
459 		err = get_ref_for_path(pathString.String(), &fCurrentRef);
460 		if (err != B_OK)
461 			printf("error in LoadDefaultFolderSettings\n");
462 		_LoadFolder(false);
463 	}
464 }
465 
466 
467 void
468 BackgroundsView::_LoadRecentFolder(BPath path)
469 {
470 	status_t err;
471 	err = get_ref_for_path(path.Path(), &fCurrentRef);
472 	if (err != B_OK)
473 		printf("error in LoadRecentFolder\n");
474 	_LoadFolder(false);
475 }
476 
477 
478 void
479 BackgroundsView::_LoadFolder(bool isDesktop)
480 {
481 	if (fCurrent) {
482 		delete fCurrent;
483 		fCurrent = NULL;
484 	}
485 
486 	BNode node(&fCurrentRef);
487 	if (node.InitCheck() == B_OK)
488 		fCurrent = BackgroundImage::GetBackgroundImage(&node, isDesktop, this);
489 
490 	_UpdateWithCurrent();
491 }
492 
493 
494 void
495 BackgroundsView::_UpdateWithCurrent(void)
496 {
497 	if (fCurrent == NULL)
498 		return;
499 
500 	fPlacementMenu->FindItem(kMsgScalePlacement)
501 		->SetEnabled(fCurrent->IsDesktop());
502 	fPlacementMenu->FindItem(kMsgCenterPlacement)
503 		->SetEnabled(fCurrent->IsDesktop());
504 
505 	if (fWorkspaceMenu->IndexOf(fWorkspaceMenu->FindMarked()) > 5)
506 		fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("Default"));
507 	else
508 		fImageMenu->FindItem(kMsgNoImage)->SetLabel(B_TRANSLATE("None"));
509 
510 	for (int32 i = fImageMenu->CountItems() - 5; i >= 0; i--) {
511 		fImageMenu->RemoveItem(2);
512 	}
513 
514 	for (int32 i = fImageList.CountItems() - 1; i >= 0; i--) {
515 		BMessage* message = new BMessage(kMsgImageSelected);
516 		_AddItem(new BGImageMenuItem(GetImage(i)->GetName(), i, message));
517 	}
518 
519 	fImageMenu->SetTargetForItems(this);
520 
521 	fCurrentInfo = fCurrent->ImageInfoForWorkspace(current_workspace());
522 
523 	if (!fCurrentInfo) {
524 		fImageMenu->FindItem(kMsgNoImage)->SetMarked(true);
525 		fPlacementMenu->FindItem(kMsgManualPlacement)->SetMarked(true);
526 		fIconLabelOutline->SetValue(B_CONTROL_ON);
527 	} else {
528 		fIconLabelOutline->SetValue(fCurrentInfo->fTextWidgetLabelOutline
529 			? B_CONTROL_ON : B_CONTROL_OFF);
530 
531 		fLastImageIndex = fCurrentInfo->fImageIndex;
532 		_FindImageItem(fLastImageIndex)->SetMarked(true);
533 
534 		if (fLastImageIndex > -1) {
535 
536 			BString xtext, ytext;
537 			int32 cmd = 0;
538 			switch (fCurrentInfo->fMode) {
539 				case BackgroundImage::kCentered:
540 					cmd = kMsgCenterPlacement;
541 					break;
542 				case BackgroundImage::kScaledToFit:
543 					cmd = kMsgScalePlacement;
544 					break;
545 				case BackgroundImage::kAtOffset:
546 					cmd = kMsgManualPlacement;
547 					xtext << (int)fCurrentInfo->fOffset.x;
548 					ytext << (int)fCurrentInfo->fOffset.y;
549 					break;
550 				case BackgroundImage::kTiled:
551 					cmd = kMsgTilePlacement;
552 					break;
553 			}
554 
555 			if (cmd != 0)
556 				fPlacementMenu->FindItem(cmd)->SetMarked(true);
557 
558 			fXPlacementText->SetText(xtext.String());
559 			fYPlacementText->SetText(ytext.String());
560 		} else {
561 			fPlacementMenu->FindItem(kMsgManualPlacement)->SetMarked(true);
562 		}
563 	}
564 
565 	rgb_color color = {255, 255, 255, 255};
566 	if (fCurrent->IsDesktop()) {
567 		color = BScreen().DesktopColor();
568 		fPicker->SetEnabled(true);
569 	} else
570 		fPicker->SetEnabled(false);
571 
572 	fPicker->SetValue(color);
573 
574 	_UpdatePreview();
575 	_UpdateButtons();
576 }
577 
578 
579 void
580 BackgroundsView::_Save()
581 {
582 	bool textWidgetLabelOutline
583 		= fIconLabelOutline->Value() == B_CONTROL_ON;
584 
585 	BackgroundImage::Mode mode = _FindPlacementMode();
586 	BPoint offset(atoi(fXPlacementText->Text()), atoi(fYPlacementText->Text()));
587 
588 	if (!fCurrent->IsDesktop()) {
589 		if (fCurrentInfo == NULL) {
590 			fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
591 				B_ALL_WORKSPACES, fLastImageIndex, mode, offset,
592 				textWidgetLabelOutline, 0, 0);
593 			fCurrent->Add(fCurrentInfo);
594 		} else {
595 			fCurrentInfo->fTextWidgetLabelOutline = textWidgetLabelOutline;
596 			fCurrentInfo->fMode = mode;
597 			if (fCurrentInfo->fMode == BackgroundImage::kAtOffset)
598 				fCurrentInfo->fOffset = offset;
599 			fCurrentInfo->fImageIndex = fLastImageIndex;
600 		}
601 	} else {
602 		uint32 workspaceMask = 1;
603 		int32 workspace = current_workspace();
604 		for (; workspace; workspace--)
605 			workspaceMask *= 2;
606 
607 		if (fCurrentInfo != NULL) {
608 			if (fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()) {
609 				if (fCurrentInfo->fWorkspace & workspaceMask
610 					&& fCurrentInfo->fWorkspace != workspaceMask) {
611 					fCurrentInfo->fWorkspace = fCurrentInfo->fWorkspace
612 						^ workspaceMask;
613 					fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
614 						workspaceMask, fLastImageIndex, mode, offset,
615 						textWidgetLabelOutline, fCurrentInfo->fImageSet,
616 						fCurrentInfo->fCacheMode);
617 					fCurrent->Add(fCurrentInfo);
618 				} else if (fCurrentInfo->fWorkspace == workspaceMask) {
619 					fCurrentInfo->fTextWidgetLabelOutline
620 						= textWidgetLabelOutline;
621 					fCurrentInfo->fMode = mode;
622 					if (fCurrentInfo->fMode == BackgroundImage::kAtOffset)
623 						fCurrentInfo->fOffset = offset;
624 
625 					fCurrentInfo->fImageIndex = fLastImageIndex;
626 				}
627 			} else {
628 				fCurrent->RemoveAll();
629 
630 				fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
631 					B_ALL_WORKSPACES, fLastImageIndex, mode, offset,
632 					textWidgetLabelOutline, fCurrent->GetShowingImageSet(),
633 					fCurrentInfo->fCacheMode);
634 				fCurrent->Add(fCurrentInfo);
635 			}
636 		} else {
637 			if (fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()) {
638 				fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
639 					workspaceMask, fLastImageIndex, mode, offset,
640 					textWidgetLabelOutline, fCurrent->GetShowingImageSet(), 0);
641 			} else {
642 				fCurrent->RemoveAll();
643 				fCurrentInfo = new BackgroundImage::BackgroundImageInfo(
644 					B_ALL_WORKSPACES, fLastImageIndex, mode, offset,
645 					textWidgetLabelOutline, fCurrent->GetShowingImageSet(), 0);
646 			}
647 			fCurrent->Add(fCurrentInfo);
648 		}
649 
650 		if (!fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked()) {
651 			for (int32 i = 0; i < count_workspaces(); i++) {
652 				BScreen().SetDesktopColor(fPicker->ValueAsColor(), i, true);
653 			}
654 		} else
655 			BScreen().SetDesktopColor(fPicker->ValueAsColor(), true);
656 	}
657 
658 	BNode node(&fCurrentRef);
659 
660 	status_t status = fCurrent->SetBackgroundImage(&node);
661 	if (status != B_OK) {
662 		// TODO: this should be a BAlert!
663 		printf("setting background image failed: %s\n", strerror(status));
664 	}
665 }
666 
667 
668 void
669 BackgroundsView::_NotifyServer()
670 {
671 	BMessenger tracker("application/x-vnd.Be-TRAK");
672 
673 	if (fCurrent->IsDesktop()) {
674 		tracker.SendMessage(new BMessage(B_RESTORE_BACKGROUND_IMAGE));
675 	} else {
676 		int32 i = -1;
677 		BMessage reply;
678 		int32 err;
679 		BEntry currentEntry(&fCurrentRef);
680 		BPath currentPath(&currentEntry);
681 		bool isCustomFolder
682 			= !fWorkspaceMenu->FindItem(kMsgDefaultFolder)->IsMarked();
683 
684 		do {
685 			BMessage msg(B_GET_PROPERTY);
686 			i++;
687 
688 			// look at the "Poses" in every Tracker window
689 			msg.AddSpecifier("Poses");
690 			msg.AddSpecifier("Window", i);
691 
692 			reply.MakeEmpty();
693 			tracker.SendMessage(&msg, &reply);
694 
695 			// break out of the loop when we're at the end of
696 			// the windows
697 			if (reply.what == B_MESSAGE_NOT_UNDERSTOOD
698 				&& reply.FindInt32("error", &err) == B_OK
699 				&& err == B_BAD_INDEX)
700 				break;
701 
702 			// don't stop for windows that don't understand
703 			// a request for "Poses"; they're not displaying
704 			// folders
705 			if (reply.what == B_MESSAGE_NOT_UNDERSTOOD
706 				&& reply.FindInt32("error", &err) == B_OK
707 				&& err != B_BAD_SCRIPT_SYNTAX)
708 				continue;
709 
710 			BMessenger trackerWindow;
711 			if (reply.FindMessenger("result", &trackerWindow) != B_OK)
712 				continue;
713 
714 			if (isCustomFolder) {
715 				// found a window with poses, ask for its path
716 				msg.MakeEmpty();
717 				msg.what = B_GET_PROPERTY;
718 				msg.AddSpecifier("Path");
719 				msg.AddSpecifier("Poses");
720 				msg.AddSpecifier("Window", i);
721 
722 				reply.MakeEmpty();
723 				tracker.SendMessage(&msg, &reply);
724 
725 				// go on with the next if this din't have a path
726 				if (reply.what == B_MESSAGE_NOT_UNDERSTOOD)
727 					continue;
728 
729 				entry_ref ref;
730 				if (reply.FindRef("result", &ref) == B_OK) {
731 					BEntry entry(&ref);
732 					BPath path(&entry);
733 
734 					// these are not the paths you're looking for
735 					if (currentPath != path)
736 						continue;
737 				}
738 			}
739 
740 			trackerWindow.SendMessage(B_RESTORE_BACKGROUND_IMAGE);
741 		} while (true);
742 	}
743 }
744 
745 
746 int32
747 BackgroundsView::_NotifyThread(void* data)
748 {
749 	BackgroundsView* view = (BackgroundsView*)data;
750 
751 	view->_NotifyServer();
752 	return B_OK;
753 }
754 
755 
756 void
757 BackgroundsView::SaveSettings(void)
758 {
759 	BPath path;
760 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) {
761 		path.Append(SETTINGS_FILE);
762 		BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
763 
764 		BPoint point = Window()->Frame().LeftTop();
765 		if (fSettings.ReplacePoint("pos", point) != B_OK)
766 			fSettings.AddPoint("pos", point);
767 
768 		entry_ref ref;
769 		BEntry entry;
770 
771 		fPanel->GetPanelDirectory(&ref);
772 		entry.SetTo(&ref);
773 		entry.GetPath(&path);
774 		if (fSettings.ReplaceString("paneldir", path.Path()) != B_OK)
775 			fSettings.AddString("paneldir", path.Path());
776 
777 		fFolderPanel->GetPanelDirectory(&ref);
778 		entry.SetTo(&ref);
779 		entry.GetPath(&path);
780 		if (fSettings.ReplaceString("folderpaneldir", path.Path()) != B_OK)
781 			fSettings.AddString("folderpaneldir", path.Path());
782 
783 		fSettings.RemoveName("recentfolder");
784 		for (int32 i = 0; i < fPathList.CountItems(); i++) {
785 			fSettings.AddString("recentfolder", fPathList.ItemAt(i)->Path());
786 		}
787 
788 		fSettings.Flatten(&file);
789 	}
790 }
791 
792 
793 void
794 BackgroundsView::_LoadSettings()
795 {
796 	fSettings.MakeEmpty();
797 
798 	BPath path;
799 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
800 		return;
801 
802 	path.Append(SETTINGS_FILE);
803 	BFile file(path.Path(), B_READ_ONLY);
804 	if (file.InitCheck() != B_OK)
805 		return;
806 
807 	if (fSettings.Unflatten(&file) != B_OK) {
808 		printf("Error unflattening settings file %s\n", path.Path());
809 		return;
810 	}
811 
812 	PRINT_OBJECT(fSettings);
813 
814 	BString string;
815 	if (fSettings.FindString("paneldir", &string) == B_OK)
816 		fPanel->SetPanelDirectory(string.String());
817 
818 	if (fSettings.FindString("folderpaneldir", &string) == B_OK)
819 		fFolderPanel->SetPanelDirectory(string.String());
820 
821 	int32 index = 0;
822 	while (fSettings.FindString("recentfolder", index, &string) == B_OK) {
823 		if (index == 0)
824 			fWorkspaceMenu->AddSeparatorItem();
825 
826 		path.SetTo(string.String());
827 		int32 i = _AddPath(path);
828 		BString s;
829 		s << B_TRANSLATE("Folder: ") << path.Leaf();
830 		BMenuItem* item = new BMenuItem(s.String(),
831 			new BMessage(kMsgFolderSelected));
832 		fWorkspaceMenu->AddItem(item, -i - 1 + 6);
833 		index++;
834 	}
835 	fWorkspaceMenu->SetTargetForItems(this);
836 
837 	PRINT(("Settings Loaded\n"));
838 }
839 
840 
841 void
842 BackgroundsView::WorkspaceActivated(uint32 oldWorkspaces, bool active)
843 {
844 	_UpdateWithCurrent();
845 }
846 
847 
848 void
849 BackgroundsView::_UpdatePreview()
850 {
851 	bool imageEnabled = !(fImageMenu->FindItem(kMsgNoImage)->IsMarked());
852 	if (fPlacementMenu->IsEnabled() ^ imageEnabled)
853 		fPlacementMenu->SetEnabled(imageEnabled);
854 
855 	bool textEnabled
856 		= (fPlacementMenu->FindItem(kMsgManualPlacement)->IsMarked())
857 		&& imageEnabled;
858 	if (fXPlacementText->IsEnabled() ^ textEnabled)
859 		fXPlacementText->SetEnabled(textEnabled);
860 	if (fYPlacementText->IsEnabled() ^ textEnabled)
861 		fYPlacementText->SetEnabled(textEnabled);
862 
863 	if (textEnabled && (strcmp(fXPlacementText->Text(), "") == 0)) {
864 		fXPlacementText->SetText("0");
865 		fYPlacementText->SetText("0");
866 	}
867 	if (!textEnabled) {
868 		fXPlacementText->SetText(NULL);
869 		fYPlacementText->SetText(NULL);
870 	}
871 
872 	fXPlacementText->TextView()->MakeSelectable(textEnabled);
873 	fYPlacementText->TextView()->MakeSelectable(textEnabled);
874 	fXPlacementText->TextView()->MakeEditable(textEnabled);
875 	fYPlacementText->TextView()->MakeEditable(textEnabled);
876 
877 	fPreview->ClearViewBitmap();
878 
879 	int32 index = ((BGImageMenuItem*)fImageMenu->FindMarked())->ImageIndex();
880 	if (index >= 0) {
881 		BBitmap* bitmap = GetImage(index)->GetBitmap();
882 		if (bitmap) {
883 			BackgroundImage::BackgroundImageInfo* info
884 				= new BackgroundImage::BackgroundImageInfo(0, index,
885 					_FindPlacementMode(), BPoint(atoi(fXPlacementText->Text()),
886 						atoi(fYPlacementText->Text())),
887 					fIconLabelOutline->Value() == B_CONTROL_ON, 0, 0);
888 			if (info->fMode == BackgroundImage::kAtOffset) {
889 				fPreview->SetEnabled(true);
890 				fPreview->fPoint.x = atoi(fXPlacementText->Text());
891 				fPreview->fPoint.y = atoi(fYPlacementText->Text());
892 			} else
893 				fPreview->SetEnabled(false);
894 
895 			fPreview->fImageBounds = BRect(bitmap->Bounds());
896 			fCurrent->Show(info, fPreview);
897 
898 			delete info;
899 		}
900 	} else
901 		fPreview->SetEnabled(false);
902 
903 	fPreview->SetViewColor(fPicker->ValueAsColor());
904 	fPreview->Invalidate();
905 }
906 
907 
908 BackgroundImage::Mode
909 BackgroundsView::_FindPlacementMode()
910 {
911 	BackgroundImage::Mode mode = BackgroundImage::kAtOffset;
912 
913 	if (fPlacementMenu->FindItem(kMsgCenterPlacement)->IsMarked())
914 		mode = BackgroundImage::kCentered;
915 	if (fPlacementMenu->FindItem(kMsgScalePlacement)->IsMarked())
916 		mode = BackgroundImage::kScaledToFit;
917 	if (fPlacementMenu->FindItem(kMsgManualPlacement)->IsMarked())
918 		mode = BackgroundImage::kAtOffset;
919 	if (fPlacementMenu->FindItem(kMsgTilePlacement)->IsMarked())
920 		mode = BackgroundImage::kTiled;
921 
922 	return mode;
923 }
924 
925 
926 void
927 BackgroundsView::_UpdateButtons()
928 {
929 	bool hasChanged = false;
930 	if (fPicker->IsEnabled()
931 		&& fPicker->ValueAsColor() != BScreen().DesktopColor()) {
932 		hasChanged = true;
933 	} else if (fCurrentInfo) {
934 		if ((fIconLabelOutline->Value() == B_CONTROL_ON)
935 			^ fCurrentInfo->fTextWidgetLabelOutline) {
936 			hasChanged = true;
937 		} else if (_FindPlacementMode() != fCurrentInfo->fMode) {
938 			hasChanged = true;
939 		} else if (fCurrentInfo->fImageIndex
940 			!= ((BGImageMenuItem*)fImageMenu->FindMarked())->ImageIndex()) {
941 			hasChanged = true;
942 		} else if (fCurrent->IsDesktop()
943 			&& ((fCurrentInfo->fWorkspace != B_ALL_WORKSPACES)
944 				^ (fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->IsMarked())))
945 		{
946 			hasChanged = true;
947 		} else if (fCurrentInfo->fImageIndex > -1
948 			&& fCurrentInfo->fMode == BackgroundImage::kAtOffset) {
949 			BString oldString, newString;
950 			oldString << (int)fCurrentInfo->fOffset.x;
951 			if (oldString != BString(fXPlacementText->Text())) {
952 				hasChanged = true;
953 			}
954 			oldString = "";
955 			oldString << (int)fCurrentInfo->fOffset.y;
956 			if (oldString != BString(fYPlacementText->Text())) {
957 				hasChanged = true;
958 			}
959 		}
960 	} else if (fImageMenu->IndexOf(fImageMenu->FindMarked()) > 0) {
961 		hasChanged = true;
962 	} else if (fIconLabelOutline->Value() == B_CONTROL_OFF) {
963 		hasChanged = true;
964 	}
965 
966 	fApply->SetEnabled(hasChanged);
967 	fRevert->SetEnabled(hasChanged);
968 }
969 
970 
971 void
972 BackgroundsView::RefsReceived(BMessage* message)
973 {
974 	if (!message->HasRef("refs") && message->HasRef("dir_ref")) {
975 		entry_ref dirRef;
976 		if (message->FindRef("dir_ref", &dirRef) == B_OK)
977 			message->AddRef("refs", &dirRef);
978 	}
979 
980 	entry_ref ref;
981 	int32 i = 0;
982 	BMimeType imageType("image");
983 	BPath desktopPath;
984 	find_directory(B_DESKTOP_DIRECTORY, &desktopPath);
985 
986 	while (message->FindRef("refs", i++, &ref) == B_OK) {
987 		BPath path;
988 		BEntry entry(&ref, true);
989 		path.SetTo(&entry);
990 		BNode node(&entry);
991 
992 		if (node.IsFile()) {
993 			BMimeType refType;
994 			BMimeType::GuessMimeType(&ref, &refType);
995 			if (!imageType.Contains(&refType))
996 				continue;
997 
998 			BGImageMenuItem* item;
999 			int32 index = AddImage(path);
1000 			if (index >= 0) {
1001 				item = _FindImageItem(index);
1002 				fLastImageIndex = index;
1003 			} else {
1004 				const char* name = GetImage(-index - 1)->GetName();
1005 				item = new BGImageMenuItem(name, -index - 1,
1006 					new BMessage(kMsgImageSelected));
1007 				_AddItem(item);
1008 				item->SetTarget(this);
1009 				fLastImageIndex = -index - 1;
1010 			}
1011 
1012 			// An optional placement may have been sent
1013 			int32 placement = 0;
1014 			if (message->FindInt32("placement", &placement) == B_OK) {
1015 				BMenuItem* item = fPlacementMenu->FindItem(placement);
1016 				if (item)
1017 					item->SetMarked(true);
1018 			}
1019 			item->SetMarked(true);
1020 			BMessenger(this).SendMessage(kMsgImageSelected);
1021 		} else if (node.IsDirectory()) {
1022 			if (desktopPath == path) {
1023 				fWorkspaceMenu->FindItem(kMsgCurrentWorkspace)->SetMarked(true);
1024 				BMessenger(this).SendMessage(kMsgCurrentWorkspace);
1025 				break;
1026 			}
1027 			BMenuItem* item;
1028 			int32 index = _AddPath(path);
1029 			if (index >= 0) {
1030 				item = fWorkspaceMenu->ItemAt(index + 6);
1031 				fLastWorkspaceIndex = index + 6;
1032 			} else {
1033 				if (fWorkspaceMenu->CountItems() <= 5)
1034 					fWorkspaceMenu->AddSeparatorItem();
1035 				BString s;
1036 				s << B_TRANSLATE("Folder: ") << path.Leaf();
1037 				item = new BMenuItem(s.String(),
1038 					new BMessage(kMsgFolderSelected));
1039 				fWorkspaceMenu->AddItem(item, -index - 1 + 6);
1040 				item->SetTarget(this);
1041 				fLastWorkspaceIndex = -index - 1 + 6;
1042 			}
1043 
1044 			item->SetMarked(true);
1045 			BMessenger(this).SendMessage(kMsgFolderSelected);
1046 		}
1047 	}
1048 }
1049 
1050 
1051 int32
1052 BackgroundsView::_AddPath(BPath path)
1053 {
1054 	int32 count = fPathList.CountItems();
1055 	int32 index = 0;
1056 	for (; index < count; index++) {
1057 		BPath* p = fPathList.ItemAt(index);
1058 		int c = BString(p->Path()).ICompare(path.Path());
1059 		if (c == 0)
1060 			return index;
1061 
1062 		if (c > 0)
1063 			break;
1064 	}
1065 	fPathList.AddItem(new BPath(path), index);
1066 	return -index - 1;
1067 }
1068 
1069 
1070 int32
1071 BackgroundsView::AddImage(BPath path)
1072 {
1073 	int32 count = fImageList.CountItems();
1074 	int32 index = 0;
1075 	for (; index < count; index++) {
1076 		Image* image = fImageList.ItemAt(index);
1077 		if (image->GetPath() == path)
1078 			return index;
1079 	}
1080 
1081 	fImageList.AddItem(new Image(path));
1082 	return -index - 1;
1083 }
1084 
1085 
1086 Image*
1087 BackgroundsView::GetImage(int32 imageIndex)
1088 {
1089 	return fImageList.ItemAt(imageIndex);
1090 }
1091 
1092 
1093 BGImageMenuItem*
1094 BackgroundsView::_FindImageItem(const int32 imageIndex)
1095 {
1096 	if (imageIndex < 0)
1097 		return (BGImageMenuItem*)fImageMenu->ItemAt(0);
1098 
1099 	int32 count = fImageMenu->CountItems() - 2;
1100 	int32 index = 2;
1101 	for (; index < count; index++) {
1102 		BGImageMenuItem* image = (BGImageMenuItem*)fImageMenu->ItemAt(index);
1103 		if (image->ImageIndex() == imageIndex)
1104 			return image;
1105 	}
1106 	return NULL;
1107 }
1108 
1109 
1110 bool
1111 BackgroundsView::_AddItem(BGImageMenuItem* item)
1112 {
1113 	int32 count = fImageMenu->CountItems() - 2;
1114 	int32 index = 2;
1115 	if (count < index) {
1116 		fImageMenu->AddItem(new BSeparatorItem(), 1);
1117 		count = fImageMenu->CountItems() - 2;
1118 	}
1119 
1120 	for (; index < count; index++) {
1121 		BGImageMenuItem* image = (BGImageMenuItem*)fImageMenu->ItemAt(index);
1122 		int c = (BString(image->Label()).ICompare(BString(item->Label())));
1123 		if (c > 0)
1124 			break;
1125 	}
1126 	return fImageMenu->AddItem(item, index);
1127 }
1128 
1129 
1130 void
1131 BackgroundsView::_SetDesktop(bool isDesktop)
1132 {
1133 	fTopLeft->SetDesktop(isDesktop);
1134 	fTop->SetDesktop(isDesktop);
1135 	fTopRight->SetDesktop(isDesktop);
1136 	fLeft->SetDesktop(isDesktop);
1137 	fRight->SetDesktop(isDesktop);
1138 	fBottomLeft->SetDesktop(isDesktop);
1139 	fBottom->SetDesktop(isDesktop);
1140 	fBottomRight->SetDesktop(isDesktop);
1141 
1142 	Invalidate();
1143 }
1144 
1145 
1146 bool
1147 BackgroundsView::FoundPositionSetting()
1148 {
1149 	return fFoundPositionSetting;
1150 }
1151 
1152 
1153 //	#pragma mark -
1154 
1155 
1156 Preview::Preview()
1157 	:
1158 	BControl("PreView", NULL, NULL, B_WILL_DRAW | B_SUBPIXEL_PRECISE)
1159 {
1160 	float aspectRatio = BScreen().Frame().Width() / BScreen().Frame().Height();
1161 	float previewWidth = 120.0f * std::max(1.0f, be_plain_font->Size() / 12.0f);
1162 	float previewHeight = ceil(previewWidth / aspectRatio);
1163 
1164 	ResizeTo(previewWidth, previewHeight);
1165 	SetExplicitMinSize(BSize(previewWidth, previewHeight));
1166 	SetExplicitMaxSize(BSize(previewWidth, previewHeight));
1167 }
1168 
1169 
1170 void
1171 Preview::AttachedToWindow()
1172 {
1173 	rgb_color color = ViewColor();
1174 	BControl::AttachedToWindow();
1175 	SetViewColor(color);
1176 }
1177 
1178 
1179 void
1180 Preview::MouseDown(BPoint point)
1181 {
1182 	if (IsEnabled() && Bounds().Contains(point)) {
1183 		uint32 buttons;
1184 		GetMouse(&point, &buttons);
1185 		if (buttons & B_PRIMARY_MOUSE_BUTTON) {
1186 			fOldPoint = point;
1187 			SetTracking(true);
1188 			BScreen().GetMode(&fMode);
1189 			fXRatio = Bounds().Width() / fMode.virtual_width;
1190 			fYRatio = Bounds().Height() / fMode.virtual_height;
1191 			SetMouseEventMask(B_POINTER_EVENTS,
1192 				B_LOCK_WINDOW_FOCUS | B_NO_POINTER_HISTORY);
1193 
1194 			BCursor grabbingCursor(B_CURSOR_ID_GRABBING);
1195 			SetViewCursor(&grabbingCursor);
1196 		}
1197 	}
1198 }
1199 
1200 
1201 void
1202 Preview::MouseUp(BPoint point)
1203 {
1204 	if (IsTracking()) {
1205 		SetTracking(false);
1206 		BCursor grabCursor(B_CURSOR_ID_GRAB);
1207 		SetViewCursor(&grabCursor);
1208 	}
1209 }
1210 
1211 
1212 void
1213 Preview::MouseMoved(BPoint point, uint32 transit, const BMessage* message)
1214 {
1215 	if (!IsTracking()) {
1216 		BCursor cursor(IsEnabled()
1217 			? B_CURSOR_ID_GRAB : B_CURSOR_ID_SYSTEM_DEFAULT);
1218 		SetViewCursor(&cursor);
1219 	} else {
1220 		float x, y;
1221 		x = fPoint.x + (point.x - fOldPoint.x) / fXRatio;
1222 		y = fPoint.y + (point.y - fOldPoint.y) / fYRatio;
1223 		bool min, max, mustSend = false;
1224 		min = (x > -fImageBounds.Width());
1225 		max = (x < fMode.virtual_width);
1226 		if (min && max) {
1227 			fOldPoint.x = point.x;
1228 			fPoint.x = x;
1229 			mustSend = true;
1230 		} else {
1231 			if (!min && fPoint.x > -fImageBounds.Width()) {
1232 				fPoint.x = -fImageBounds.Width();
1233 				fOldPoint.x = point.x - (x - fPoint.x) * fXRatio;
1234 				mustSend = true;
1235 			}
1236 			if (!max && fPoint.x < fMode.virtual_width) {
1237 				fPoint.x = fMode.virtual_width;
1238 				fOldPoint.x = point.x - (x - fPoint.x) * fXRatio;
1239 				mustSend = true;
1240 			}
1241 		}
1242 
1243 		min = (y > -fImageBounds.Height());
1244 		max = (y < fMode.virtual_height);
1245 		if (min && max) {
1246 			fOldPoint.y = point.y;
1247 			fPoint.y = y;
1248 			mustSend = true;
1249 		} else {
1250 			if (!min && fPoint.y > -fImageBounds.Height()) {
1251 				fPoint.y = -fImageBounds.Height();
1252 				fOldPoint.y = point.y - (y - fPoint.y) * fYRatio;
1253 				mustSend = true;
1254 			}
1255 			if (!max && fPoint.y < fMode.virtual_height) {
1256 				fPoint.y = fMode.virtual_height;
1257 				fOldPoint.y = point.y - (y - fPoint.y) * fYRatio;
1258 				mustSend = true;
1259 			}
1260 		}
1261 
1262 		if (mustSend) {
1263 			BMessenger messenger(Parent());
1264 			messenger.SendMessage(kMsgUpdatePreviewPlacement);
1265 		}
1266 	}
1267 
1268 	BControl::MouseMoved(point, transit, message);
1269 }
1270 
1271 
1272 //	#pragma mark -
1273 
1274 
1275 BGImageMenuItem::BGImageMenuItem(const char* label, int32 imageIndex,
1276 	BMessage* message, char shortcut, uint32 modifiers)
1277 	: BMenuItem(label, message, shortcut, modifiers),
1278 	fImageIndex(imageIndex)
1279 {
1280 }
1281 
1282 
1283 //	#pragma mark -
1284 
1285 
1286 FramePart::FramePart(int32 part)
1287 	:
1288 	BView(NULL, B_WILL_DRAW | B_FRAME_EVENTS),
1289 	fFramePart(part),
1290 	fIsDesktop(true)
1291 {
1292 	_SetSizeAndAlignment();
1293 }
1294 
1295 
1296 void
1297 FramePart::Draw(BRect rect)
1298 {
1299 	rgb_color color = HighColor();
1300 	SetDrawingMode(B_OP_COPY);
1301 	SetHighColor(Parent()->ViewColor());
1302 
1303 	if (fIsDesktop) {
1304 		switch (fFramePart) {
1305 			case FRAME_TOP_LEFT:
1306 				FillRect(rect);
1307 				SetHighColor(160, 160, 160);
1308 				FillRoundRect(BRect(0, 0, 8, 8), 3, 3);
1309 				SetHighColor(96, 96, 96);
1310 				StrokeRoundRect(BRect(0, 0, 8, 8), 3, 3);
1311 				break;
1312 
1313 			case FRAME_TOP:
1314 				SetHighColor(160, 160, 160);
1315 				FillRect(BRect(0, 1, rect.right, 3));
1316 				SetHighColor(96, 96, 96);
1317 				StrokeLine(BPoint(0, 0), BPoint(rect.right, 0));
1318 				SetHighColor(0, 0, 0);
1319 				StrokeLine(BPoint(0, 4), BPoint(rect.right, 4));
1320 				break;
1321 
1322 			case FRAME_TOP_RIGHT:
1323 				FillRect(rect);
1324 				SetHighColor(160, 160, 160);
1325 				FillRoundRect(BRect(-4, 0, 4, 8), 3, 3);
1326 				SetHighColor(96, 96, 96);
1327 				StrokeRoundRect(BRect(-4, 0, 4, 8), 3, 3);
1328 				break;
1329 
1330 			case FRAME_LEFT_SIDE:
1331 				SetHighColor(160, 160, 160);
1332 				FillRect(BRect(1, 0, 3, rect.bottom));
1333 				SetHighColor(96, 96, 96);
1334 				StrokeLine(BPoint(0, 0), BPoint(0, rect.bottom));
1335 				SetHighColor(0, 0, 0);
1336 				StrokeLine(BPoint(4, 0), BPoint(4, rect.bottom));
1337 				break;
1338 
1339 			case FRAME_RIGHT_SIDE:
1340 				SetHighColor(160, 160, 160);
1341 				FillRect(BRect(1, 0, 3, rect.bottom));
1342 				SetHighColor(0, 0, 0);
1343 				StrokeLine(BPoint(0, 0), BPoint(0, rect.bottom));
1344 				SetHighColor(96, 96, 96);
1345 				StrokeLine(BPoint(4, 0), BPoint(4, rect.bottom));
1346 				break;
1347 
1348 			case FRAME_BOTTOM_LEFT:
1349 				FillRect(rect);
1350 				SetHighColor(160, 160, 160);
1351 				FillRoundRect(BRect(0, -4, 8, 4), 3, 3);
1352 				SetHighColor(96, 96, 96);
1353 				StrokeRoundRect(BRect(0, -4, 8, 4), 3, 3);
1354 				break;
1355 
1356 			case FRAME_BOTTOM:
1357 				SetHighColor(160, 160, 160);
1358 				FillRect(BRect(0, 1, rect.right, 3));
1359 				SetHighColor(0, 0, 0);
1360 				StrokeLine(BPoint(0, 0), BPoint(rect.right, 0));
1361 				SetHighColor(96, 96, 96);
1362 				StrokeLine(BPoint(0, 4), BPoint(rect.right, 4));
1363 				SetHighColor(228, 0, 0);
1364 				StrokeLine(BPoint(5, 2), BPoint(7, 2));
1365 				break;
1366 
1367 			case FRAME_BOTTOM_RIGHT:
1368 				FillRect(rect);
1369 				SetHighColor(160, 160, 160);
1370 				FillRoundRect(BRect(-4, -4, 4, 4), 3, 3);
1371 				SetHighColor(96, 96, 96);
1372 				StrokeRoundRect(BRect(-4, -4, 4, 4), 3, 3);
1373 				break;
1374 
1375 			default:
1376 				break;
1377 		}
1378 	} else {
1379 		switch (fFramePart) {
1380 			case FRAME_TOP_LEFT:
1381 				SetHighColor(152, 152, 152);
1382 				StrokeLine(BPoint(0, 0), BPoint(0, 12));
1383 				StrokeLine(BPoint(0, 0), BPoint(4, 0));
1384 				StrokeLine(BPoint(3, 12), BPoint(3, 12));
1385 				SetHighColor(255, 203, 0);
1386 				FillRect(BRect(1, 1, 3, 9));
1387 				SetHighColor(240, 240, 240);
1388 				StrokeLine(BPoint(1, 12), BPoint(1, 10));
1389 				StrokeLine(BPoint(2, 10), BPoint(3, 10));
1390 				SetHighColor(200, 200, 200);
1391 				StrokeLine(BPoint(2, 12), BPoint(2, 11));
1392 				StrokeLine(BPoint(3, 11), BPoint(3, 11));
1393 				break;
1394 
1395 			case FRAME_TOP:
1396 				FillRect(BRect(54, 0, rect.right, 8));
1397 				SetHighColor(255, 203, 0);
1398 				FillRect(BRect(0, 1, 52, 9));
1399 				SetHighColor(152, 152, 152);
1400 				StrokeLine(BPoint(0, 0), BPoint(53, 0));
1401 				StrokeLine(BPoint(53, 1), BPoint(53, 9));
1402 				StrokeLine(BPoint(54, 9), BPoint(rect.right, 9));
1403 				SetHighColor(240, 240, 240);
1404 				StrokeLine(BPoint(0, 10), BPoint(rect.right, 10));
1405 				SetHighColor(200, 200, 200);
1406 				StrokeLine(BPoint(0, 11), BPoint(rect.right, 11));
1407 				SetHighColor(152, 152, 152);
1408 				StrokeLine(BPoint(0, 12), BPoint(rect.right, 12));
1409 				break;
1410 
1411 			case FRAME_TOP_RIGHT:
1412 				FillRect(BRect(0, 0, 3, 8));
1413 				SetHighColor(152, 152, 152);
1414 				StrokeLine(BPoint(0, 12), BPoint(0, 12));
1415 				StrokeLine(BPoint(0, 9), BPoint(3, 9));
1416 				StrokeLine(BPoint(3, 12), BPoint(3, 9));
1417 				SetHighColor(240, 240, 240);
1418 				StrokeLine(BPoint(0, 10), BPoint(2, 10));
1419 				StrokeLine(BPoint(1, 12), BPoint(1, 12));
1420 				SetHighColor(200, 200, 200);
1421 				StrokeLine(BPoint(2, 12), BPoint(2, 12));
1422 				StrokeLine(BPoint(0, 11), BPoint(2, 11));
1423 				break;
1424 
1425 			case FRAME_LEFT_SIDE:
1426 			case FRAME_RIGHT_SIDE:
1427 				SetHighColor(152, 152, 152);
1428 				StrokeLine(BPoint(0, 0), BPoint(0, rect.bottom));
1429 				SetHighColor(240, 240, 240);
1430 				StrokeLine(BPoint(1, 0), BPoint(1, rect.bottom));
1431 				SetHighColor(200, 200, 200);
1432 				StrokeLine(BPoint(2, 0), BPoint(2, rect.bottom));
1433 				SetHighColor(152, 152, 152);
1434 				StrokeLine(BPoint(3, 0), BPoint(3, rect.bottom));
1435 				break;
1436 
1437 			case FRAME_BOTTOM_LEFT:
1438 				SetHighColor(152, 152, 152);
1439 				StrokeLine(BPoint(0, 0), BPoint(0, 3));
1440 				StrokeLine(BPoint(0, 3), BPoint(3, 3));
1441 				StrokeLine(BPoint(3, 0), BPoint(3, 0));
1442 				SetHighColor(240, 240, 240);
1443 				StrokeLine(BPoint(1, 0), BPoint(1, 2));
1444 				StrokeLine(BPoint(3, 1), BPoint(3, 1));
1445 				SetHighColor(200, 200, 200);
1446 				StrokeLine(BPoint(2, 0), BPoint(2, 2));
1447 				StrokeLine(BPoint(3, 2), BPoint(3, 2));
1448 				break;
1449 
1450 			case FRAME_BOTTOM:
1451 				SetHighColor(152, 152, 152);
1452 				StrokeLine(BPoint(0, 0), BPoint(rect.right, 0));
1453 				SetHighColor(240, 240, 240);
1454 				StrokeLine(BPoint(0, 1), BPoint(rect.right, 1));
1455 				SetHighColor(200, 200, 200);
1456 				StrokeLine(BPoint(0, 2), BPoint(rect.right, 2));
1457 				SetHighColor(152, 152, 152);
1458 				StrokeLine(BPoint(0, 3), BPoint(rect.right, 3));
1459 				break;
1460 
1461 			case FRAME_BOTTOM_RIGHT:
1462 				SetHighColor(152, 152, 152);
1463 				StrokeLine(BPoint(0, 0), BPoint(0, 0));
1464 				SetHighColor(240, 240, 240);
1465 				StrokeLine(BPoint(1, 0), BPoint(1, 1));
1466 				StrokeLine(BPoint(0, 1), BPoint(0, 1));
1467 				SetHighColor(200, 200, 200);
1468 				StrokeLine(BPoint(2, 0), BPoint(2, 2));
1469 				StrokeLine(BPoint(0, 2), BPoint(1, 2));
1470 				SetHighColor(152, 152, 152);
1471 				StrokeLine(BPoint(3, 0), BPoint(3, 3));
1472 				StrokeLine(BPoint(0, 3), BPoint(2, 3));
1473 				break;
1474 
1475 			default:
1476 				break;
1477 		}
1478 	}
1479 
1480 	SetHighColor(color);
1481 }
1482 
1483 
1484 void
1485 FramePart::SetDesktop(bool isDesktop)
1486 {
1487 	fIsDesktop = isDesktop;
1488 
1489 	_SetSizeAndAlignment();
1490 	Invalidate();
1491 }
1492 
1493 
1494 void
1495 FramePart::_SetSizeAndAlignment()
1496 {
1497 	if (fIsDesktop) {
1498 		switch (fFramePart) {
1499 			case FRAME_TOP_LEFT:
1500 				SetExplicitMinSize(BSize(4, 4));
1501 				SetExplicitMaxSize(BSize(4, 4));
1502 				break;
1503 
1504 			case FRAME_TOP:
1505 				SetExplicitMinSize(BSize(1, 4));
1506 				SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 4));
1507 				break;
1508 
1509 			case FRAME_TOP_RIGHT:
1510 				SetExplicitMinSize(BSize(4, 4));
1511 				SetExplicitMaxSize(BSize(4, 4));
1512 				break;
1513 
1514 			case FRAME_LEFT_SIDE:
1515 				SetExplicitMinSize(BSize(4, 1));
1516 				SetExplicitMaxSize(BSize(4, B_SIZE_UNLIMITED));
1517 				break;
1518 
1519 			case FRAME_RIGHT_SIDE:
1520 				SetExplicitMinSize(BSize(4, 1));
1521 				SetExplicitMaxSize(BSize(4, B_SIZE_UNLIMITED));
1522 				break;
1523 
1524 			case FRAME_BOTTOM_LEFT:
1525 				SetExplicitMinSize(BSize(4, 4));
1526 				SetExplicitMaxSize(BSize(4, 4));
1527 				break;
1528 
1529 			case FRAME_BOTTOM:
1530 				SetExplicitMinSize(BSize(1, 4));
1531 				SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 4));
1532 				break;
1533 
1534 			case FRAME_BOTTOM_RIGHT:
1535 				SetExplicitMaxSize(BSize(4, 4));
1536 				SetExplicitMinSize(BSize(4, 4));
1537 				break;
1538 
1539 			default:
1540 				break;
1541 		}
1542 	} else {
1543 		switch (fFramePart) {
1544 			case FRAME_TOP_LEFT:
1545 				SetExplicitMinSize(BSize(3, 12));
1546 				SetExplicitMaxSize(BSize(3, 12));
1547 				break;
1548 
1549 			case FRAME_TOP:
1550 				SetExplicitMinSize(BSize(1, 12));
1551 				SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 12));
1552 				break;
1553 
1554 			case FRAME_TOP_RIGHT:
1555 				SetExplicitMinSize(BSize(3, 12));
1556 				SetExplicitMaxSize(BSize(3, 12));
1557 				break;
1558 
1559 			case FRAME_LEFT_SIDE:
1560 				SetExplicitMinSize(BSize(3, 1));
1561 				SetExplicitMaxSize(BSize(3, B_SIZE_UNLIMITED));
1562 				break;
1563 
1564 			case FRAME_RIGHT_SIDE:
1565 				SetExplicitMinSize(BSize(3, 1));
1566 				SetExplicitMaxSize(BSize(3, B_SIZE_UNLIMITED));
1567 				break;
1568 
1569 			case FRAME_BOTTOM_LEFT:
1570 				SetExplicitMinSize(BSize(3, 3));
1571 				SetExplicitMaxSize(BSize(3, 3));
1572 				break;
1573 
1574 			case FRAME_BOTTOM:
1575 				SetExplicitMinSize(BSize(1, 3));
1576 				SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, 3));
1577 				break;
1578 
1579 			case FRAME_BOTTOM_RIGHT:
1580 				SetExplicitMaxSize(BSize(3, 3));
1581 				SetExplicitMinSize(BSize(3, 3));
1582 				break;
1583 
1584 			default:
1585 				break;
1586 		}
1587 	}
1588 
1589 	switch (fFramePart) {
1590 		case FRAME_TOP_LEFT:
1591 			SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_BOTTOM));
1592 			break;
1593 
1594 		case FRAME_TOP:
1595 			SetExplicitAlignment(BAlignment(B_ALIGN_CENTER, B_ALIGN_BOTTOM));
1596 			break;
1597 
1598 		case FRAME_TOP_RIGHT:
1599 			SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_BOTTOM));
1600 			break;
1601 
1602 		case FRAME_LEFT_SIDE:
1603 			SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_MIDDLE));
1604 			break;
1605 
1606 		case FRAME_RIGHT_SIDE:
1607 			SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_MIDDLE));
1608 			break;
1609 
1610 		case FRAME_BOTTOM_LEFT:
1611 			SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP));
1612 			break;
1613 
1614 		case FRAME_BOTTOM:
1615 			SetExplicitAlignment(BAlignment(B_ALIGN_CENTER, B_ALIGN_TOP));
1616 			break;
1617 
1618 		case FRAME_BOTTOM_RIGHT:
1619 			SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP));
1620 			break;
1621 
1622 		default:
1623 			break;
1624 	}
1625 }
1626 
1627