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