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