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