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