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