xref: /haiku/src/servers/app/decorator/DecorManager.cpp (revision 83b1a68c52ba3e0e8796282759f694b7fdddf06d)
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);
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 
81 WindowBehaviour*
82 DecorAddOn::AllocateWindowBehaviour(Window* window)
83 {
84 	return new (std::nothrow)SATWindowBehaviour(window,
85 		window->Desktop()->GetStackAndTile());
86 }
87 
88 
89 const DesktopListenerList&
90 DecorAddOn::GetDesktopListeners()
91 {
92 	return fDesktopListeners;
93 }
94 
95 
96 Decorator*
97 DecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect)
98 {
99 	return new (std::nothrow)SATDecorator(settings, rect);
100 }
101 
102 
103 //	#pragma mark -
104 
105 
106 DecorManager::DecorManager()
107 	:
108 	fDefaultDecor(-1, "Default"),
109 	fCurrentDecor(&fDefaultDecor),
110 	fPreviewDecor(NULL),
111 	fPreviewWindow(NULL),
112 	fCurrentDecorPath("Default")
113 {
114 	_LoadSettingsFromDisk();
115 }
116 
117 
118 DecorManager::~DecorManager()
119 {
120 }
121 
122 
123 Decorator*
124 DecorManager::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 
152 WindowBehaviour*
153 DecorManager::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 
165 void
166 DecorManager::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 
180 status_t
181 DecorManager::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 
222 const DesktopListenerList&
223 DecorManager::GetDesktopListeners()
224 {
225 	return fCurrentDecor->GetDesktopListeners();
226 }
227 
228 
229 BString
230 DecorManager::GetCurrentDecorator() const
231 {
232 	return fCurrentDecorPath.String();
233 }
234 
235 
236 status_t
237 DecorManager::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 
272 DecorAddOn*
273 DecorManager::_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 
314 static const char* kSettingsDir = "system/app_server";
315 static const char* kSettingsFile = "decorator_settings";
316 
317 
318 bool
319 DecorManager::_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 
352 bool
353 DecorManager::_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