1/*
2 * Copyright (c) 2001-2011, Haiku, Inc.
3 * Distributed under the terms of the MIT license.
4 *
5 * Author:
6 *		DarkWyrm <bpmagic@columbus.rr.com>
7 *		Clemens Zeidler <haiku@clemens-zeidler.de>
8 *		Joseph Groover <looncraz@satx.rr.com>
9 */
10
11#include "DecorManager.h"
12
13#include <Directory.h>
14#include <Entry.h>
15#include <File.h>
16#include <FindDirectory.h>
17#include <Message.h>
18#include <Path.h>
19#include <Rect.h>
20
21#include <syslog.h>
22
23#include "AppServer.h"
24#include "Desktop.h"
25#include "DesktopSettings.h"
26#include "ServerConfig.h"
27#include "SATDecorator.h"
28#include "Window.h"
29
30typedef float get_version(void);
31typedef DecorAddOn* create_decor_addon(image_id id, const char* name);
32
33// Globals
34DecorManager gDecorManager;
35
36
37DecorAddOn::DecorAddOn(image_id id, const char* name)
38	:
39	fImageID(id),
40 	fName(name)
41{
42}
43
44
45DecorAddOn::~DecorAddOn()
46{
47}
48
49
50status_t
51DecorAddOn::InitCheck() const
52{
53	return B_OK;
54}
55
56
57Decorator*
58DecorAddOn::AllocateDecorator(Desktop* desktop, DrawingEngine* engine,
59	BRect rect, const char* title, window_look look, uint32 flags)
60{
61	if (!desktop->LockSingleWindow())
62		return NULL;
63
64	DesktopSettings settings(desktop);
65	Decorator* decorator;
66	decorator = _AllocateDecorator(settings, rect);
67	desktop->UnlockSingleWindow();
68	if (!decorator)
69		return NULL;
70
71	if (decorator->AddTab(settings, title, look, flags) == false) {
72		delete decorator;
73		return NULL;
74	}
75
76	decorator->SetDrawingEngine(engine);
77	return decorator;
78}
79
80
81WindowBehaviour*
82DecorAddOn::AllocateWindowBehaviour(Window* window)
83{
84	return new (std::nothrow)SATWindowBehaviour(window,
85		window->Desktop()->GetStackAndTile());
86}
87
88
89const DesktopListenerList&
90DecorAddOn::GetDesktopListeners()
91{
92	return fDesktopListeners;
93}
94
95
96Decorator*
97DecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect)
98{
99	return new (std::nothrow)SATDecorator(settings, rect);
100}
101
102
103//	#pragma mark -
104
105
106DecorManager::DecorManager()
107	:
108	fDefaultDecor(-1, "Default"),
109	fCurrentDecor(&fDefaultDecor),
110	fPreviewDecor(NULL),
111	fPreviewWindow(NULL),
112	fCurrentDecorPath("Default")
113{
114	_LoadSettingsFromDisk();
115}
116
117
118DecorManager::~DecorManager()
119{
120}
121
122
123Decorator*
124DecorManager::AllocateDecorator(Window* window)
125{
126	// Create a new instance of the current decorator.
127	// Ownership is that of the caller
128
129	if (!fCurrentDecor) {
130		// We should *never* be here. If we do, it's a bug.
131		debugger("DecorManager::AllocateDecorator has a NULL decorator");
132		return NULL;
133	}
134
135	// Are we previewing a specific decorator?
136	if (window == fPreviewWindow) {
137		if (fPreviewDecor != NULL) {
138			return fPreviewDecor->AllocateDecorator(window->Desktop(),
139				window->GetDrawingEngine(), window->Frame(), window->Title(),
140				window->Look(), window->Flags());
141		} else {
142			fPreviewWindow = NULL;
143		}
144	}
145
146	return fCurrentDecor->AllocateDecorator(window->Desktop(),
147		window->GetDrawingEngine(), window->Frame(), window->Title(),
148		window->Look(), window->Flags());
149}
150
151
152WindowBehaviour*
153DecorManager::AllocateWindowBehaviour(Window* window)
154{
155	if (!fCurrentDecor) {
156		// We should *never* be here. If we do, it's a bug.
157		debugger("DecorManager::AllocateDecorator has a NULL decorator");
158		return NULL;
159	}
160
161	return fCurrentDecor->AllocateWindowBehaviour(window);
162}
163
164
165void
166DecorManager::CleanupForWindow(Window* window)
167{
168	// Given window is being deleted, do any cleanup needed
169	if (fPreviewWindow == window && window != NULL){
170		fPreviewWindow = NULL;
171
172		if (fPreviewDecor != NULL)
173			unload_add_on(fPreviewDecor->ImageID());
174
175		fPreviewDecor = NULL;
176	}
177}
178
179
180status_t
181DecorManager::PreviewDecorator(BString path, Window* window)
182{
183	if (fPreviewWindow != NULL && fPreviewWindow != window){
184		// Reset other window to current decorator - only one can preview
185		Window* oldPreviewWindow = fPreviewWindow;
186		fPreviewWindow = NULL;
187		oldPreviewWindow->ReloadDecor();
188	}
189
190	if (window == NULL)
191		return B_BAD_VALUE;
192
193	// We have to jump some hoops because the window must be able to
194	// delete its decorator before we unload the add-on
195	status_t error = B_OK;
196	DecorAddOn* decorPtr = _LoadDecor(path, error);
197	if (decorPtr == NULL)
198		return error == B_OK ? B_ERROR : error;
199
200	BRegion border;
201	window->GetBorderRegion(&border);
202
203	DecorAddOn* oldDecor = fPreviewDecor;
204	fPreviewDecor = decorPtr;
205	fPreviewWindow = window;
206	// After this call, the window has deleted its decorator.
207	fPreviewWindow->ReloadDecor();
208
209	BRegion newBorder;
210	window->GetBorderRegion(&newBorder);
211
212	border.Include(&newBorder);
213	window->Desktop()->RebuildAndRedrawAfterWindowChange(window, border);
214
215	if (oldDecor != NULL)
216		unload_add_on(oldDecor->ImageID());
217
218	return B_OK;
219}
220
221
222const DesktopListenerList&
223DecorManager::GetDesktopListeners()
224{
225	return fCurrentDecor->GetDesktopListeners();
226}
227
228
229BString
230DecorManager::GetCurrentDecorator() const
231{
232	return fCurrentDecorPath.String();
233}
234
235
236status_t
237DecorManager::SetDecorator(BString path, Desktop* desktop)
238{
239	status_t error = B_OK;
240	DecorAddOn* newDecor = _LoadDecor(path, error);
241	if (newDecor == NULL)
242		return error == B_OK ? B_ERROR : error;
243
244	DecorAddOn* oldDecor = fCurrentDecor;
245
246	BString oldPath = fCurrentDecorPath;
247	image_id oldImage = fCurrentDecor->ImageID();
248
249	fCurrentDecor = newDecor;
250	fCurrentDecorPath = path.String();
251
252	if (desktop->ReloadDecor(oldDecor)) {
253		// now safe to unload all old decorator data
254		// saves us from deleting oldDecor...
255		unload_add_on(oldImage);
256		_SaveSettingsToDisk();
257		return B_OK;
258	}
259
260	// TODO: unloading the newDecor and its image
261	// problem is we don't know how many windows failed... or why they failed...
262	syslog(LOG_WARNING,
263		"app_server:DecorManager:SetDecorator:\"%s\" *partly* failed\n",
264		fCurrentDecorPath.String());
265
266	fCurrentDecor = oldDecor;
267	fCurrentDecorPath = oldPath;
268	return B_ERROR;
269}
270
271
272DecorAddOn*
273DecorManager::_LoadDecor(BString _path, status_t& error )
274{
275	if (_path == "Default") {
276		error = B_OK;
277		return &fDefaultDecor;
278	}
279
280	BEntry entry(_path.String(), true);
281	if (!entry.Exists()) {
282		error = B_ENTRY_NOT_FOUND;
283		return NULL;
284	}
285
286	BPath path(&entry);
287	image_id image = load_add_on(path.Path());
288	if (image < 0) {
289		error = B_BAD_IMAGE_ID;
290		return NULL;
291	}
292
293	create_decor_addon*	createFunc;
294	if (get_image_symbol(image, "instantiate_decor_addon", B_SYMBOL_TYPE_TEXT,
295			(void**)&createFunc) != B_OK) {
296		unload_add_on(image);
297		error = B_MISSING_SYMBOL;
298		return NULL;
299	}
300
301	char name[B_FILE_NAME_LENGTH];
302	entry.GetName(name);
303	DecorAddOn* newDecor = createFunc(image, name);
304	if (newDecor == NULL || newDecor->InitCheck() != B_OK) {
305		unload_add_on(image);
306		error = B_ERROR;
307		return NULL;
308	}
309
310	return newDecor;
311}
312
313
314static const char* kSettingsDir = "system/app_server";
315static const char* kSettingsFile = "decorator_settings";
316
317
318bool
319DecorManager::_LoadSettingsFromDisk()
320{
321	// get the user settings directory
322	BPath path;
323	status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
324	if (error != B_OK)
325		return false;
326
327	path.Append(kSettingsDir);
328	path.Append(kSettingsFile);
329	BFile file(path.Path(), B_READ_ONLY);
330	if (file.InitCheck() != B_OK)
331		return false;
332
333	BMessage settings;
334	if (settings.Unflatten(&file) == B_OK) {
335		BString itemPath;
336		if (settings.FindString("decorator", &itemPath) == B_OK) {
337			status_t error = B_OK;
338			DecorAddOn* decor = _LoadDecor(itemPath, error);
339			if (decor != NULL) {
340				fCurrentDecor = decor;
341				return true;
342			} else {
343				//TODO: do something with the reported error
344			}
345		}
346	}
347
348	return false;
349}
350
351
352bool
353DecorManager::_SaveSettingsToDisk()
354{
355	// get the user settings directory
356	BPath path;
357	status_t error = find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
358	if (error != B_OK)
359		return false;
360
361	path.Append(kSettingsDir);
362	if (create_directory(path.Path(), 777) != B_OK)
363		return false;
364
365	path.Append(kSettingsFile);
366	BFile file(path.Path(), B_READ_WRITE | B_CREATE_FILE | B_ERASE_FILE);
367	if (file.InitCheck() != B_OK)
368		return false;
369
370	BMessage settings;
371	if (settings.AddString("decorator", fCurrentDecorPath.String()) != B_OK)
372		return false;
373	if (settings.Flatten(&file) != B_OK)
374		return false;
375
376	return true;
377}
378
379