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