xref: /haiku/src/kits/interface/DecorInfo.cpp (revision 5c6260dc232fcb2d4d5d1103c1623dba9663b753)
1 /*
2  * Public domain source code.
3  *
4  * Author:
5  *		Joseph "looncraz" Groover <looncraz@satx.rr.com>
6  */
7 
8 
9 #include <DecorInfo.h>
10 
11 #include <new>
12 #include <stdio.h>
13 
14 #include <Autolock.h>
15 #include <Directory.h>
16 #include <FindDirectory.h>
17 #include <Path.h>
18 #include <Resources.h>
19 
20 #include <DecoratorPrivate.h>
21 
22 
23 namespace BPrivate {
24 
25 
26 DecorInfo::DecorInfo()
27 	:
28 	fVersion(0),
29 	fModificationTime(0),
30 	fInitStatus(B_NO_INIT)
31 {
32 }
33 
34 
35 DecorInfo::DecorInfo(const BString& path)
36 	:
37 	fPath(path),
38 	fVersion(0),
39 	fModificationTime(0),
40 	fInitStatus(B_NO_INIT)
41 {
42 	BEntry entry(path.String(), true);
43 	entry.GetRef(&fRef);
44 
45 	_Init();
46 }
47 
48 
49 DecorInfo::DecorInfo(const entry_ref& ref)
50 	:
51 	fRef(ref),
52 	fVersion(0),
53 	fModificationTime(0),
54 	fInitStatus(B_NO_INIT)
55 {
56 	BPath path(&ref);
57 	fPath = path.Path();
58 
59 	_Init();
60 }
61 
62 
63 DecorInfo::~DecorInfo()
64 {
65 }
66 
67 
68 status_t
69 DecorInfo::SetTo(const entry_ref& ref)
70 {
71 	Unset();
72 
73 	BPath path(&ref);
74 	fPath = path.Path();
75 	fRef = ref;
76 	_Init();
77 
78 	return InitCheck();
79 }
80 
81 
82 status_t
83 DecorInfo::SetTo(BString path)
84 {
85 	BEntry entry(path.String(), true);
86 	entry_ref ref;
87 	entry.GetRef(&ref);
88 	return SetTo(ref);
89 }
90 
91 
92 status_t
93 DecorInfo::InitCheck()	const
94 {
95 	return fInitStatus;
96 }
97 
98 
99 void
100 DecorInfo::Unset()
101 {
102 	fRef = entry_ref();
103 	fPath = "";
104 	fName = "";
105 	fAuthors = "";
106 	fShortDescription = "";
107 	fLicenseURL = "";
108 	fLicenseName = "";
109 	fSupportURL = "";
110 	fVersion = 0;
111 	fModificationTime = 0;
112 	fInitStatus = B_NO_INIT;
113 }
114 
115 
116 bool
117 DecorInfo::IsDefault() const
118 {
119 	return fInitStatus == B_OK && fPath == "Default";
120 }
121 
122 
123 BString
124 DecorInfo::Path() const
125 {
126 	return fPath;
127 }
128 
129 
130 const entry_ref*
131 DecorInfo::Ref() const
132 {
133 	if (InitCheck() != B_OK || IsDefault())
134 		return NULL;
135 	return &fRef;
136 }
137 
138 
139 BString
140 DecorInfo::Name() const
141 {
142 	return fName;
143 }
144 
145 
146 BString
147 DecorInfo::ShortcutName() const
148 {
149 	if (Ref())
150 		return fRef.name;
151 	return Name();
152 }
153 
154 
155 BString
156 DecorInfo::Authors() const
157 {
158 	return fAuthors;
159 }
160 
161 
162 BString
163 DecorInfo::ShortDescription() const
164 {
165 	return fShortDescription;
166 }
167 
168 
169 BString
170 DecorInfo::LongDescription() const
171 {
172 	return fLongDescription;
173 }
174 
175 
176 BString
177 DecorInfo::LicenseURL() const
178 {
179 	return fLicenseURL;
180 }
181 
182 
183 BString
184 DecorInfo::LicenseName() const
185 {
186 	return fLicenseName;
187 }
188 
189 
190 BString
191 DecorInfo::SupportURL() const
192 {
193 	return fSupportURL;
194 }
195 
196 
197 float
198 DecorInfo::Version() const
199 {
200 	return fVersion;
201 }
202 
203 
204 time_t
205 DecorInfo::ModificationTime() const
206 {
207 	return fModificationTime;
208 }
209 
210 
211 bool
212 DecorInfo::CheckForChanges(bool& deleted)
213 {
214 	if (InitCheck() != B_OK)
215 		return false;
216 
217 	BEntry entry(&fRef);
218 
219 	if (entry.InitCheck() != B_OK)
220 		return false;
221 
222 	if (!entry.Exists()) {
223 		deleted = true;
224 		return true;
225 	}
226 
227 	time_t modtime = 0;
228 	if (entry.GetModificationTime(&modtime) != B_OK) {
229 		fprintf(stderr, "DecorInfo::CheckForChanges()\tERROR: "
230 			"BEntry:GetModificationTime() failed\n");
231 		return false;
232 	}
233 
234 	if (fModificationTime != modtime) {
235 		_Init(true);
236 		return true;
237 	}
238 
239 	return false;
240 }
241 
242 
243 void
244 DecorInfo::_Init(bool isUpdate)
245 {
246 	if (!isUpdate && InitCheck() != B_NO_INIT) {
247 		// TODO: remove after validation
248 		fprintf(stderr, "DecorInfo::_Init()\tImproper init state\n");
249 		return;
250 	}
251 
252 	BEntry entry;
253 
254 	if (fPath == "Default") {
255 		if (isUpdate){
256 			// should never happen
257 			fprintf(stderr, "DecorInfo::_Init(true)\tBUG BUG updating default"
258 				"decorator!?!?!\n");
259 			return;
260 		}
261 
262 		fName = "Default";
263 		fAuthors = "DarkWyrm, Stephan Aßmus, Clemens Zeidler, Ingo Weinhold";
264 		fShortDescription = "Default Haiku window decorator.";
265 		fLongDescription = fShortDescription;
266 		fLicenseURL = "http://";
267 		fLicenseName = "MIT";
268 		fSupportURL = "http://www.haiku-os.org/";
269 		fVersion = 0.5;
270 		fInitStatus = B_OK;
271 
272 		// The following is to get the modification time of the app_server
273 		// and, thusly, the Default decorator...
274 		// If you can make it more simple, please do!
275 		BPath path;
276 		find_directory(B_BEOS_SERVERS_DIRECTORY, &path);
277 		path.Append("app_server");
278 		entry.SetTo(path.Path(), true);
279 		if (!entry.Exists()) {
280 			fprintf(stderr, "Server MIA the world has become its slave! "
281 				"Call the CIA!\n");
282 			return;
283 		}
284 
285 		entry.GetModificationTime(&fModificationTime);
286 		return;
287 	}
288 
289 	// Is a file system object...
290 
291 	entry.SetTo(&fRef, true);	// follow link
292 	if (entry.InitCheck() != B_OK) {
293 		fInitStatus = entry.InitCheck();
294 		return;
295 	}
296 
297 	if (!entry.Exists()) {
298 		if (isUpdate) {
299 			fprintf(stderr, "DecorInfo::_Init()\tERROR: decorator deleted"
300 					" after CheckForChanges() found it!\n");
301 			fprintf(stderr, "DecorInfo::_Init()\tERROR: DecorInfo will "
302 					"Unset\n");
303 			Unset();
304 		}
305 		return;
306 	}
307 
308 	// update fRef to match file system object
309 	entry.GetRef(&fRef);
310 	entry.GetModificationTime(&fModificationTime);
311 
312 	BResources resources(&fRef);
313 	if (resources.InitCheck() != B_OK) {
314 		fprintf(stderr, "DecorInfo::_Init()\t BResource InitCheck() failure\n");
315 		return;
316 	}
317 
318 	size_t infoSize = 0;
319 	const void* infoData = resources.LoadResource(B_MESSAGE_TYPE,
320 		"be:decor:info", &infoSize);
321 	BMessage infoMessage;
322 
323 	if (infoData == NULL || infoSize == 0
324 		|| infoMessage.Unflatten((const char*)infoData) != B_OK) {
325 		fprintf(stderr, "DecorInfo::_init()\tNo extended information found for"
326 			" \"%s\"\n", fRef.name);
327 	} else {
328 		infoMessage.FindString("name", &fName);
329 		infoMessage.FindString("authors", &fAuthors);
330 		infoMessage.FindString("short_descr", &fShortDescription);
331 		infoMessage.FindString("long_descr", &fLongDescription);
332 		infoMessage.FindString("lic_url", &fLicenseURL);
333 		infoMessage.FindString("lic_name", &fLicenseName);
334 		infoMessage.FindString("support_url", &fSupportURL);
335 		infoMessage.FindFloat ("version", &fVersion);
336 	}
337 
338 	fInitStatus = B_OK;
339 	fName = fRef.name;
340 }
341 
342 
343 // #pragma mark - DecorInfoUtility
344 
345 
346 DecorInfoUtility::DecorInfoUtility(bool scanNow)
347 	:
348 	fHasScanned(false)
349 {
350 	// get default decorator from app_server
351 	DecorInfo* info = new(std::nothrow) DecorInfo("Default");
352 	if (info == NULL || info->InitCheck() != B_OK)	{
353 		delete info;
354 		fprintf(stderr, "DecorInfoUtility::constructor\tdefault decorator's "
355 			"DecorInfo failed InitCheck()\n");
356 		return;
357 	}
358 
359 	fList.AddItem(info);
360 
361 	if (scanNow)
362 		ScanDecorators();
363 }
364 
365 
366 DecorInfoUtility::~DecorInfoUtility()
367 {
368 	BAutolock _(fLock);
369 	for	(int i = fList.CountItems() - 1; i >= 0; --i)
370 		delete fList.ItemAt(i);
371 }
372 
373 
374 status_t
375 DecorInfoUtility::ScanDecorators()
376 {
377 	BPath decorPath;
378 	status_t ret = find_directory(B_USER_ADDONS_DIRECTORY, &decorPath);
379 	if (ret != B_OK)
380 		return ret;
381 	ret = decorPath.Append("decorators");
382 	if (ret != B_OK)
383 		return ret;
384 
385 	BDirectory dir(decorPath.Path());
386 	ret = dir.InitCheck();
387 	if (ret != B_OK) {
388 		fprintf(stderr, "DecorInfoUtility::ScanDecorators:\tERROR: "
389 			"DECORATORS_DIR not found!\n");
390 		return ret;
391 	}
392 
393 	BAutolock _(fLock);
394 	// First, run through our list and DecorInfos CheckForChanges()
395 
396 	if (fHasScanned) {
397 		for (int i = fList.CountItems() - 1; i > 0; --i) {
398 			DecorInfo* decorInfo = fList.ItemAt(i);
399 
400 			bool deleted = false;
401 			decorInfo->CheckForChanges(deleted);
402 
403 			if (deleted) {
404 				fList.RemoveItem(decorInfo);
405 				delete decorInfo;
406 			}
407 		}
408 	}
409 
410 	BPath path;
411 	entry_ref ref;
412 	// Now, look at file system, skip the entries for which we already have
413 	// a DecorInfo in the list.
414 	while (dir.GetNextRef(&ref) == B_OK) {
415 		path.SetTo(decorPath.Path());
416 		path.Append(ref.name);
417 		if (_FindDecor(path.Path()) != NULL)
418 			continue;
419 
420 		DecorInfo* decorInfo = new(std::nothrow) DecorInfo(ref);
421 		if (decorInfo == NULL || decorInfo->InitCheck() != B_OK) {
422 			fprintf(stderr, "DecorInfoUtility::ScanDecorators()\tInitCheck() "
423 				"failure on decorator, skipping\n");
424 			delete decorInfo;
425 			continue;
426 		}
427 
428 		fList.AddItem(decorInfo);
429 	}
430 
431 	fHasScanned = true;
432 
433 	return B_OK;
434 }
435 
436 
437 int32
438 DecorInfoUtility::CountDecorators()
439 {
440 	BAutolock _(fLock);
441 	if (!fHasScanned)
442 		ScanDecorators();
443 
444 	return fList.CountItems();
445 }
446 
447 
448 DecorInfo*
449 DecorInfoUtility::DecoratorAt(int32 index)
450 {
451 	BAutolock _(fLock);
452 	return fList.ItemAt(index);
453 }
454 
455 
456 DecorInfo*
457 DecorInfoUtility::FindDecorator(const BString& string)
458 {
459 	if (string.Length() == 0)
460 		return CurrentDecorator();
461 
462 	if (string.ICompare("default") == 0)
463 		return DefaultDecorator();
464 
465 	BAutolock _(fLock);
466 	if (!fHasScanned)
467 		ScanDecorators();
468 
469 	// search by path
470 	DecorInfo* decor = _FindDecor(string);
471 	if (decor != NULL)
472 		return decor;
473 
474 	// search by name or short cut name
475 	for (int i = 1; i < fList.CountItems(); ++i) {
476 		decor = fList.ItemAt(i);
477 		if (string.ICompare(decor->ShortcutName()) == 0
478 			|| string.ICompare(decor->Name()) == 0) {
479 			return decor;
480 		}
481 	}
482 
483 	return NULL;
484 }
485 
486 
487 DecorInfo*
488 DecorInfoUtility::CurrentDecorator()
489 {
490 	BAutolock _(fLock);
491 	if (!fHasScanned)
492 		ScanDecorators();
493 
494 	BString name;
495 	get_decorator(name);
496 	return FindDecorator(name);
497 }
498 
499 
500 DecorInfo*
501 DecorInfoUtility::DefaultDecorator()
502 {
503 	BAutolock _(fLock);
504 	return fList.ItemAt(0);
505 }
506 
507 
508 bool
509 DecorInfoUtility::IsCurrentDecorator(DecorInfo* decor)
510 {
511 	BAutolock _(fLock);
512 	if (decor == NULL)
513 		 return false;
514 	return decor->Path() == CurrentDecorator()->Path();
515 }
516 
517 
518 status_t
519 DecorInfoUtility::SetDecorator(DecorInfo* decor)
520 {
521 	if (decor == NULL)
522 		return B_BAD_VALUE;
523 
524 	BAutolock _(fLock);
525 	if (decor->IsDefault())
526 		return set_decorator("Default");
527 
528 	return set_decorator(decor->Path());
529 }
530 
531 
532 status_t
533 DecorInfoUtility::SetDecorator(int32 index)
534 {
535 	BAutolock _(fLock);
536 	if (!fHasScanned)
537 		return B_ERROR;
538 
539 	DecorInfo* decor = DecoratorAt(index);
540 	if (decor == NULL)
541 		return B_BAD_INDEX;
542 
543 	return SetDecorator(decor);
544 }
545 
546 
547 status_t
548 DecorInfoUtility::Preview(DecorInfo* decor, BWindow* window)
549 {
550 	if (decor == NULL)
551 		return B_BAD_VALUE;
552 
553 	return preview_decorator(decor->Path(), window);
554 }
555 
556 
557 // #pargma mark - private
558 
559 
560 DecorInfo*
561 DecorInfoUtility::_FindDecor(const BString& pathString)
562 {
563 	// find decor by path and path alone!
564 	if (!fLock.IsLocked()) {
565 		fprintf(stderr, "DecorInfoUtility::_find_decor()\tfailure to lock! - "
566 			"BUG BUG BUG\n");
567 		return NULL;
568 	}
569 
570 	if (pathString == "Default")
571 		return fList.ItemAt(0);
572 
573 	for (int i = 1; i < fList.CountItems(); ++i) {
574 		DecorInfo* decor = fList.ItemAt(i);
575 		// Find the DecoratorInfo either by its true current location or by
576 		// what we still think the location is (before we had a chance to
577 		// update). NOTE: This will only catch the case when the user moved the
578 		// folder in which the add-on file lives. It will not work when the user
579 		// moves the add-on file itself or renames it.
580 		BPath path(decor->Ref());
581 		if (path.Path() == pathString || decor->Path() == pathString)
582 			return decor;
583 	}
584 
585 	return NULL;
586 }
587 
588 
589 }	// namespace BPrivate
590