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