Utilities.h revision 6a960972b5349596f2f8f11b65ae71407b7d7bf0
1/*
2Open Tracker License
3
4Terms and Conditions
5
6Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7
8Permission is hereby granted, free of charge, to any person obtaining a copy of
9this software and associated documentation files (the "Software"), to deal in
10the Software without restriction, including without limitation the rights to
11use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12of the Software, and to permit persons to whom the Software is furnished to do
13so, subject to the following conditions:
14
15The above copyright notice and this permission notice applies to all licensees
16and shall be included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
25Except as contained in this notice, the name of Be Incorporated shall not be
26used in advertising or otherwise to promote the sale, use or other dealings in
27this Software without prior written authorization from Be Incorporated.
28
29Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30of Be Incorporated in the United States and other countries. Other brand product
31names are registered trademarks or trademarks of their respective holders.
32All rights reserved.
33*/
34
35#ifndef	_UTILITIES_H
36#define _UTILITIES_H
37
38#include <stdarg.h>
39#include <stdlib.h>
40
41#include <ByteOrder.h>
42#include <Bitmap.h>
43#include <DataIO.h>
44#include <Directory.h>
45#include <Entry.h>
46#include <Font.h>
47#include <GraphicsDefs.h>
48#include <Looper.h>
49#include <MenuItem.h>
50#include <MessageFilter.h>
51#include <Mime.h>
52#include <ObjectList.h>
53#include <Point.h>
54#include <Path.h>
55#include <String.h>
56#include <StringView.h>
57
58
59class BMessage;
60class BVolume;
61class BBitmap;
62class BTextView;
63class BView;
64
65namespace BPrivate {
66
67class Benaphore;
68class BPose;
69class BPoseView;
70
71// global variables
72extern const rgb_color kBlack;
73extern const rgb_color kWhite;
74
75const int64 kHalfKBSize = 512;
76const int64 kKBSize = 1024;
77const int64 kMBSize = 1048576;
78const int64 kGBSize = 1073741824;
79const int64 kTBSize = kGBSize * kKBSize;
80
81const int32 kMiniIconSeparator = 3;
82
83#ifdef __HAIKU__
84const color_space kDefaultIconDepth = B_RGBA32;
85#else
86const color_space kDefaultIconDepth = B_CMAP8;
87#endif
88
89// misc typedefs, constants and structs
90
91// Periodically updated poses (ones with a volume space bar) register
92// themselfs in this global list. This way they can be iterated over instead
93// of sending around update messages.
94
95class PeriodicUpdatePoses {
96	public:
97		PeriodicUpdatePoses();
98		~PeriodicUpdatePoses();
99
100		typedef bool (*PeriodicUpdateCallback)(BPose *pose, void *cookie);
101
102		void AddPose(BPose *pose, BPoseView *poseView,
103			PeriodicUpdateCallback callback, void *cookie);
104		bool RemovePose(BPose *pose, void **cookie);
105
106		void DoPeriodicUpdate(bool forceRedraw);
107
108	private:
109		struct periodic_pose {
110			BPose					*pose;
111			BPoseView				*pose_view;
112			PeriodicUpdateCallback	callback;
113			void					*cookie;
114		};
115
116		Benaphore *fLock;
117		BObjectList<periodic_pose> fPoseList;
118};
119
120extern PeriodicUpdatePoses gPeriodicUpdatePoses;
121
122
123// PoseInfo is the structure that gets saved as attributes for every node on
124// disk, defining the node's position and visibility
125class PoseInfo {
126	public:
127		static void EndianSwap(void *castToThis);
128		void PrintToStream();
129
130		bool fInvisible;
131		ino_t fInitedDirectory;
132			// for a location to be valid, fInitedDirectory has to contain the inode
133			// of the items parent directory
134			// This makes it impossible to for instance zip up files and extract
135			// them in the same location. This should probably be reworked -- Tracker
136			// could say strip the file location attributes when dropping files into
137			// a closed folder
138		BPoint fLocation;
139};
140
141
142// extends PoseInfo adding workspace support; used for desktop
143// poses only
144class ExtendedPoseInfo {
145	public:
146		size_t Size() const;
147		static size_t Size(int32);
148		size_t SizeWithHeadroom() const;
149		static size_t SizeWithHeadroom(size_t);
150		bool HasLocationForFrame(BRect) const;
151		BPoint LocationForFrame(BRect) const;
152		bool SetLocationForFrame(BPoint, BRect);
153
154		static void EndianSwap(void *castToThis);
155		void PrintToStream();
156
157		uint32 fWorkspaces;
158		bool fInvisible;
159		bool fShowFromBootOnly;
160		bool fReservedBool1;
161		bool fReservedBool2;
162		int32 fReservedInt1;
163		int32 fReservedInt2;
164		int32 fReservedInt3;
165		int32 fReservedInt4;
166		int32 fReservedInt5;
167
168		int32 fNumFrames;
169		struct FrameLocation {
170			BPoint fLocation;
171			BRect fFrame;
172			uint32 fWorkspaces;
173		};
174
175		FrameLocation fLocations[0];
176};
177
178// misc functions
179void DisallowMetaKeys(BTextView *);
180void DisallowFilenameKeys(BTextView *);
181
182
183// #pragma mark - Natural sorting
184
185
186struct natural_chunk {
187	enum chunk_type {
188		NUMBER,
189		ASCII,
190		END
191	};
192	chunk_type	type;
193	char		buffer[B_FILE_NAME_LENGTH];
194	int32		length;
195};
196
197
198inline bool
199IsNaturalDigit(const char c)
200{
201	return (c >= '0' && c <= '9') || c == ' ';
202}
203
204
205inline void
206FetchNaturalChunk(natural_chunk& chunk, const char* source)
207{
208	if (chunk.type == natural_chunk::ASCII) {
209		// string chunk
210		int32 pos = 0;
211		while (!IsNaturalDigit(source[pos]) && source[pos] != '\0') {
212			pos++;
213		}
214		strlcpy(chunk.buffer, source, pos + 1);
215		chunk.length = pos;
216	} else {
217		// number chunk
218		int32 pos = 0;
219		while (IsNaturalDigit(source[pos]) && source[pos] != '\0') {
220			pos++;
221		}
222		strlcpy(&chunk.buffer[sizeof(chunk.buffer) - 1 - pos], source, pos + 1);
223		chunk.length = pos;
224
225		// replace leading zeros with spaces
226		for (pos = sizeof(chunk.buffer) - 1 - pos;
227				pos < (int32)sizeof(chunk.buffer) - 1; pos++) {
228			if (chunk.buffer[pos] != ' ' && chunk.buffer[pos] != '0')
229				break;
230
231			chunk.buffer[pos] = ' ';
232		}
233	}
234}
235
236
237//! Makes sure both number strings have the same size
238inline void
239NormalizeNumberChunks(natural_chunk& a, natural_chunk& b)
240{
241	if (a.length > b.length) {
242		memset(&b.buffer[sizeof(b.buffer) - 1 - a.length], ' ',
243			a.length - b.length);
244		b.length = a.length;
245	} else if (b.length > a.length) {
246		memset(&a.buffer[sizeof(a.buffer) - 1 - b.length], ' ',
247			b.length - a.length);
248		a.length = b.length;
249	}
250}
251
252
253//! Compares two strings naturally, as opposed to lexicographically
254inline int
255NaturalCompare(const char* stringA, const char* stringB)
256{
257	natural_chunk a;
258	natural_chunk b;
259
260	uint32 indexA = 0;
261	uint32 indexB = 0;
262
263	while (true) {
264		// Determine type of next chunks in each string based on first char
265		if (stringA[indexA] == '\0')
266			a.type = natural_chunk::END;
267		else if (IsNaturalDigit(stringA[indexA]))
268			a.type = natural_chunk::NUMBER;
269		else
270			a.type = natural_chunk::ASCII;
271
272		if (stringB[indexB] == '\0')
273			b.type = natural_chunk::END;
274		else if (IsNaturalDigit(stringB[indexB]))
275			b.type = natural_chunk::NUMBER;
276		else
277			b.type = natural_chunk::ASCII;
278
279		// Check if we reached the end of either string
280		if (a.type == natural_chunk::END)
281			return b.type == natural_chunk::END ? 0 : -1;
282		if (b.type == natural_chunk::END)
283			return 1;
284
285		if (a.type != b.type) {
286			// Different chunk types, just compare the remaining strings
287			return strcasecmp(&stringA[indexA], &stringB[indexB]);
288		}
289
290		// Fetch the next chunks
291		FetchNaturalChunk(a, &stringA[indexA]);
292		FetchNaturalChunk(b, &stringB[indexB]);
293
294		indexA += a.length;
295		indexB += b.length;
296
297		// Compare the two chunks based on their type
298		if (a.type == natural_chunk::ASCII) {
299			// String chunks
300			int result = strcasecmp(a.buffer, b.buffer);
301			if (result != 0)
302				return result;
303		} else {
304			// Number chunks - they are compared as strings to allow an
305			// arbitrary number of digits.
306			NormalizeNumberChunks(a, b);
307
308			int result = strcmp(a.buffer - 1 + sizeof(a.buffer) - a.length,
309				b.buffer - 1 + sizeof(b.buffer) - b.length);
310			if (result != 0)
311				return result;
312		}
313
314		// The chunks were equal, proceed with the next chunk
315	}
316
317	return 0;
318}
319
320
321bool ValidateStream(BMallocIO *, uint32, int32 version);
322
323
324uint32 HashString(const char *string, uint32 seed);
325uint32 AttrHashString(const char *string, uint32 type);
326
327
328class OffscreenBitmap {
329	// a utility class for setting up offscreen bitmaps
330	public:
331		OffscreenBitmap(BRect bounds);
332		OffscreenBitmap();
333		~OffscreenBitmap();
334
335		BView *BeginUsing(BRect bounds);
336		void DoneUsing();
337		BBitmap *Bitmap() const;
338			// blit this to your view when you are done rendering
339		BView *View() const;
340			// use this to render your image
341
342	private:
343		void NewBitmap(BRect frame);
344		BBitmap *fBitmap;
345};
346
347
348// bitmap functions
349extern void FadeRGBA32Horizontal(uint32 *bits, int32 width, int32 height, int32 from, int32 to);
350extern void FadeRGBA32Vertical(uint32 *bits, int32 width, int32 height, int32 from, int32 to);
351
352
353class FlickerFreeStringView : public BStringView {
354	// Adds support for offscreen bitmap drawing for string views that update often
355	// this would be better implemented as an option of BStringView
356	public:
357		FlickerFreeStringView(BRect bounds, const char *name,
358			const char *text, uint32 resizeFlags = B_FOLLOW_LEFT | B_FOLLOW_TOP,
359			uint32 flags = B_WILL_DRAW);
360		FlickerFreeStringView(BRect bounds, const char *name,
361			const char *text, BBitmap *existingOffscreen,
362			uint32 resizeFlags = B_FOLLOW_LEFT | B_FOLLOW_TOP,
363			uint32 flags = B_WILL_DRAW);
364		virtual ~FlickerFreeStringView();
365		virtual void Draw(BRect);
366		virtual void AttachedToWindow();
367		virtual void SetViewColor(rgb_color);
368		virtual void SetLowColor(rgb_color);
369
370	private:
371		OffscreenBitmap *fBitmap;
372		rgb_color fViewColor;
373		rgb_color fLowColor;
374		BBitmap *fOrigBitmap;
375
376		typedef BStringView _inherited;
377};
378
379
380class DraggableIcon : public BView {
381	// used to determine a save location for a file
382	public:
383		DraggableIcon(BRect, const char *, const char *mimeType, icon_size,
384			const BMessage *, BMessenger,
385			uint32 resizeFlags = B_FOLLOW_LEFT | B_FOLLOW_TOP,
386			uint32 flags = B_WILL_DRAW);
387		virtual ~DraggableIcon();
388
389		static BRect PreferredRect(BPoint offset, icon_size);
390		void SetTarget(BMessenger);
391
392	protected:
393		virtual void AttachedToWindow();
394		virtual void MouseDown(BPoint);
395		virtual void Draw(BRect);
396
397		virtual bool DragStarted(BMessage *dragMessage);
398
399	protected:
400		BBitmap *fBitmap;
401		BMessage fMessage;
402		BMessenger fTarget;
403};
404
405
406class PositionPassingMenuItem : public BMenuItem {
407	public:
408		PositionPassingMenuItem(const char *title, BMessage *, char shortcut = 0,
409			uint32 modifiers = 0);
410
411		PositionPassingMenuItem(BMenu *, BMessage *);
412
413	protected:
414		virtual status_t Invoke(BMessage * = 0);
415			// appends the invoke location for NewFolder, etc. to use
416
417	private:
418		typedef BMenuItem _inherited;
419};
420
421
422class Benaphore {
423	// aka benaphore
424	public:
425		Benaphore(const char *name = "Light Lock")
426		:	fSemaphore(create_sem(0, name)),
427			fCount(1)
428		{
429		}
430
431		~Benaphore()
432		{
433			delete_sem(fSemaphore);
434		}
435
436		bool Lock()
437		{
438			if (atomic_add(&fCount, -1) <= 0)
439				return acquire_sem(fSemaphore) == B_OK;
440
441			return true;
442		}
443
444		void Unlock()
445		{
446			if (atomic_add(&fCount, 1) < 0)
447				release_sem(fSemaphore);
448		}
449
450		bool IsLocked() const
451		{
452			return fCount <= 0;
453		}
454
455	private:
456		sem_id fSemaphore;
457		int32 fCount;
458};
459
460
461class SeparatorLine : public BView {
462	public:
463		SeparatorLine(BPoint , float , bool vertical, const char *name = "");
464		virtual	void Draw(BRect bounds);
465};
466
467
468class TitledSeparatorItem : public BMenuItem {
469	public:
470		TitledSeparatorItem(const char *);
471		virtual ~TitledSeparatorItem();
472
473		virtual void SetEnabled(bool state);
474
475	protected:
476		virtual	void GetContentSize(float *width, float *height);
477		virtual	void Draw();
478
479	private:
480		typedef BMenuItem _inherited;
481};
482
483
484class LooperAutoLocker {
485	public:
486		LooperAutoLocker(BHandler *handler)
487		:	fHandler(handler),
488			fHasLock(handler->LockLooper())
489		{
490		}
491
492		~LooperAutoLocker()
493		{
494			if (fHasLock)
495				fHandler->UnlockLooper();
496		}
497
498		bool operator!() const
499		{
500			return !fHasLock;
501		}
502
503		bool IsLocked() const
504		{
505			return fHasLock;
506		}
507
508	private:
509		BHandler *fHandler;
510		bool fHasLock;
511};
512
513
514class MessengerAutoLocker {
515	// move this into AutoLock.h
516	public:
517		MessengerAutoLocker(BMessenger *messenger)
518			:	fMessenger(messenger),
519				fHasLock(messenger->LockTarget())
520		{ }
521
522		~MessengerAutoLocker()
523		{
524			Unlock();
525		}
526
527		bool operator!() const
528		{
529			return !fHasLock;
530		}
531
532		bool IsLocked() const
533		{
534			return fHasLock;
535		}
536
537		void Unlock()
538		{
539			if (fHasLock) {
540				BLooper *looper;
541				fMessenger->Target(&looper);
542				if (looper)
543					looper->Unlock();
544				fHasLock = false;
545			}
546		}
547
548	private:
549		BMessenger *fMessenger;
550		bool fHasLock;
551};
552
553
554class ShortcutFilter : public BMessageFilter {
555	public:
556		ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
557			uint32 shortcutWhat, BHandler *target);
558
559	protected:
560		filter_result Filter(BMessage *, BHandler **);
561
562	private:
563		uint32 fShortcutKey;
564		uint32 fShortcutModifier;
565		uint32 fShortcutWhat;
566		BHandler *fTarget;
567};
568
569// iterates over all the refs in a message
570entry_ref *EachEntryRef(BMessage *, entry_ref *(*)(entry_ref *, void *),
571	void *passThru = 0);
572const entry_ref *EachEntryRef(const BMessage *,
573	const entry_ref *(*)(const entry_ref *, void *), void *passThru = 0);
574
575entry_ref *EachEntryRef(BMessage *, entry_ref *(*)(entry_ref *, void *),
576	void *passThru, int32 maxCount);
577const entry_ref *EachEntryRef(const BMessage *,
578	const entry_ref *(*)(const entry_ref *, void *), void *passThru, int32 maxCount);
579
580
581bool ContainsEntryRef(const BMessage *, const entry_ref *);
582int32 CountRefs(const BMessage *);
583
584BMenuItem *EachMenuItem(BMenu *menu, bool recursive, BMenuItem *(*func)(BMenuItem *));
585const BMenuItem *EachMenuItem(const BMenu *menu, bool recursive,
586	BMenuItem *(*func)(const BMenuItem *));
587
588int64 StringToScalar(const char *text);
589	// string to num, understands kB, MB, etc.
590
591// misc calls
592void EmbedUniqueVolumeInfo(BMessage *, const BVolume *);
593status_t MatchArchivedVolume(BVolume *, const BMessage *, int32 index = 0);
594void TruncateLeaf(BString *string);
595
596void StringFromStream(BString *, BMallocIO *, bool endianSwap = false);
597void StringToStream(const BString *, BMallocIO *);
598int32 ArchiveSize(const BString *);
599
600extern void EnableNamedMenuItem(BMenu *menu, const char *itemName, bool on);
601extern void MarkNamedMenuItem(BMenu *menu, const char *itemName, bool on);
602extern void EnableNamedMenuItem(BMenu *menu, uint32 commandName, bool on);
603extern void MarkNamedMenuItem(BMenu *menu, uint32 commandName, bool on);
604extern void DeleteSubmenu(BMenuItem *submenuItem);
605
606extern bool BootedInSafeMode();
607
608// Now is in kits
609#if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI && !defined(__HAIKU__)
610
611// Should be in kits
612bool operator==(const rgb_color &, const rgb_color &);
613bool operator!=(const rgb_color &, const rgb_color &);
614
615#endif
616
617inline rgb_color
618Color(int32 r, int32 g, int32 b, int32 alpha = 255)
619{
620	rgb_color result;
621	result.red = (uchar)r;
622	result.green = (uchar)g;
623	result.blue = (uchar)b;
624	result.alpha = (uchar)alpha;
625
626	return result;
627}
628
629void PrintToStream(rgb_color color);
630
631template <class InitCheckable>
632void
633ThrowOnInitCheckError(InitCheckable *item)
634{
635	if (!item)
636		throw B_ERROR;
637	status_t error = item->InitCheck();
638	if (error != B_OK)
639		throw error;
640}
641
642#if DEBUG
643#define ThrowOnError(error) _ThrowOnError(error, __FILE__, __LINE__)
644#define ThrowIfNotSize(error) _ThrowIfNotSize(error, __FILE__, __LINE__)
645#define ThrowOnErrorWithMessage(error, debugStr) _ThrowOnError(error, debugStr, __FILE__, __LINE__)
646#else
647#define ThrowOnError(x) _ThrowOnError(x, 0, 0)
648#define ThrowIfNotSize(x) _ThrowIfNotSize(x, 0, 0)
649#define ThrowOnErrorWithMessage(error, debugStr) _ThrowOnError(error, debugStr, __FILE__, __LINE__)
650#endif
651
652void _ThrowOnError(status_t, const char *, int32);
653void _ThrowIfNotSize(ssize_t, const char *, int32);
654void _ThrowOnError(status_t, const char *debugStr, const char *, int32);
655
656// stub calls that work around BAppFile info inefficiency
657status_t GetAppSignatureFromAttr(BFile *, char *);
658status_t GetAppIconFromAttr(BFile *, BBitmap *, icon_size);
659status_t GetFileIconFromAttr(BNode *, BBitmap *, icon_size);
660
661
662// debugging
663void HexDump(const void *buffer, int32 length);
664
665#if xDEBUG
666
667inline void
668PrintRefToStream(const entry_ref *ref, const char *trailer = "\n")
669{
670	if (!ref) {
671		PRINT(("NULL entry_ref%s", trailer));
672		return;
673	}
674	BPath path;
675	BEntry entry(ref);
676	entry.GetPath(&path);
677	PRINT(("%s%s", path.Path(), trailer));
678}
679
680inline void
681PrintEntryToStream(const BEntry *entry, const char *trailer = "\n")
682{
683	if (!entry) {
684		PRINT(("NULL entry%s", trailer));
685		return;
686	}
687	BPath path;
688	entry->GetPath(&path);
689	PRINT(("%s%s", path.Path(), trailer));
690}
691
692inline void
693PrintDirToStream(const BDirectory *dir, const char *trailer = "\n")
694{
695	if (!dir) {
696		PRINT(("NULL entry_ref%s", trailer));
697		return;
698	}
699	BPath path;
700	BEntry entry;
701	dir->GetEntry(&entry);
702	entry.GetPath(&path);
703	PRINT(("%s%s", path.Path(), trailer));
704}
705
706#else
707
708inline void PrintRefToStream(const entry_ref *, const char * = 0) {}
709inline void PrintEntryToStream(const BEntry *, const char * = 0) {}
710inline void PrintDirToStream(const BDirectory *, const char * = 0) {}
711
712#endif
713
714#ifdef xDEBUG
715
716	extern FILE *logFile;
717
718	inline void PrintToLogFile(const char *fmt, ...)
719	{
720    	va_list ap;
721        va_start(ap, fmt);
722		vfprintf(logFile, fmt, ap);
723		va_end(ap);
724	}
725
726	#define WRITELOG(_ARGS_)													\
727		if (logFile == 0) 														\
728			logFile = fopen("/var/log/tracker.log", "a+"); 						\
729		if (logFile != 0) {														\
730			thread_info info;													\
731			get_thread_info(find_thread(NULL), &info);							\
732			PrintToLogFile("[t %Ld] \"%s\" (%s:%i) ", system_time(),			\
733				info.name, __FILE__, __LINE__);									\
734			PrintToLogFile _ARGS_;												\
735			PrintToLogFile("\n");												\
736			fflush(logFile);													\
737		}
738
739#else
740
741	#define WRITELOG(_ARGS_)
742
743#endif
744
745// fancy casting macros
746
747template <typename NewType, typename OldType>
748inline NewType assert_cast(OldType castedPointer) {
749	ASSERT(dynamic_cast<NewType>(castedPointer) != NULL);
750	return static_cast<NewType>(castedPointer);
751}
752
753// B_SWAP_INT32 have broken signedness, simple cover calls to fix that
754// should fix up in ByteOrder.h
755
756inline int32 SwapInt32(int32 value) { return (int32)B_SWAP_INT32((uint32)value); }
757inline uint32 SwapUInt32(uint32 value) { return B_SWAP_INT32(value); }
758inline int64 SwapInt64(int64 value) { return (int64)B_SWAP_INT64((uint64)value); }
759inline uint64 SwapUInt64(uint64 value) { return B_SWAP_INT64(value); }
760
761
762extern const float kExactMatchScore;
763float ComputeTypeAheadScore(const char *text, const char *match,
764	bool wordMode = false);
765
766} // namespace BPrivate
767
768#endif	// _UTILITIES_H
769