xref: /haiku/src/apps/people/PeopleApp.cpp (revision 372a66634410cf0450e426716c14ad42d40c0da4)
1 /*
2  * Copyright 2005-2010, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Robert Polic
7  *		Axel Dörfler, axeld@pinc-software.de
8  *		Stephan Aßmus <superstippi@gmx.de>
9  *
10  * Copyright 1999, Be Incorporated.   All Rights Reserved.
11  * This file may be used under the terms of the Be Sample Code License.
12  */
13 
14 
15 #include "PeopleApp.h"
16 
17 #include <Alert.h>
18 #include <AutoDeleter.h>
19 #include <Bitmap.h>
20 #include <Catalog.h>
21 #include <Directory.h>
22 #include <FindDirectory.h>
23 #include <fs_index.h>
24 #include <Locale.h>
25 #include <Path.h>
26 #include <Roster.h>
27 #include <Screen.h>
28 #include <Volume.h>
29 #include <VolumeRoster.h>
30 
31 #include "PersonWindow.h"
32 #include "PersonIcons.h"
33 
34 #include <string.h>
35 
36 
37 #undef B_TRANSLATION_CONTEXT
38 #define B_TRANSLATION_CONTEXT "People"
39 
40 
41 struct DefaultAttribute {
42 	const char*	attribute;
43 	int32		width;
44 	const char*	name;
45 };
46 
47 // TODO: Add flags in attribute info message to find these.
48 static const char* kNameAttribute = "META:name";
49 static const char* kCategoryAttribute = "META:group";
50 
51 struct DefaultAttribute sDefaultAttributes[] = {
52 	{ kNameAttribute, 120, B_TRANSLATE("Contact name") },
53 	{ "META:nickname", 120, B_TRANSLATE("Nickname") },
54 	{ "META:company", 120, B_TRANSLATE("Company") },
55 	{ "META:address", 120, B_TRANSLATE("Address") },
56 	{ "META:city", 90, B_TRANSLATE("City") },
57 	{ "META:state", 50, B_TRANSLATE("State") },
58 	{ "META:zip", 50, B_TRANSLATE("Zip") },
59 	{ "META:country", 120, B_TRANSLATE("Country") },
60 	{ "META:hphone", 90, B_TRANSLATE("Home phone") },
61 	{ "META:wphone", 90, B_TRANSLATE("Work phone") },
62 	{ "META:fax", 90, B_TRANSLATE("Fax") },
63 	{ "META:email", 120, B_TRANSLATE("E-mail") },
64 	{ "META:url", 120, B_TRANSLATE("URL") },
65 	{ kCategoryAttribute, 120, B_TRANSLATE("Group") },
66 	{ NULL, 0, NULL }
67 };
68 
69 
70 TPeopleApp::TPeopleApp()
71 	:
72 	BApplication(APP_SIG),
73 	fWindowCount(0),
74 	fAttributes(20, true)
75 {
76 	B_TRANSLATE_MARK_SYSTEM_NAME_VOID("People");
77 
78 	fPosition.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH,
79 		TITLE_BAR_HEIGHT + WIND_HEIGHT);
80 	BPoint pos = fPosition.LeftTop();
81 
82 	BPath path;
83 	find_directory(B_USER_SETTINGS_DIRECTORY, &path, true);
84 
85 	BDirectory dir(path.Path());
86 	BEntry entry;
87 	if (dir.FindEntry("People_data", &entry) == B_OK) {
88 		fPrefs = new BFile(&entry, B_READ_WRITE);
89 		if (fPrefs->InitCheck() == B_NO_ERROR) {
90 			fPrefs->Read(&pos, sizeof(BPoint));
91 			if (BScreen(B_MAIN_SCREEN_ID).Frame().Contains(pos))
92 				fPosition.OffsetTo(pos);
93 		}
94 	} else {
95 		fPrefs = new BFile();
96 		if (dir.CreateFile("People_data", fPrefs) != B_OK) {
97 			delete fPrefs;
98 			fPrefs = NULL;
99 		}
100 	}
101 
102 	// Read attributes from person mime type. If it does not exist,
103 	// or if it contains no attribute definitions, install a "clean"
104 	// person mime type from the hard-coded default attributes.
105 
106 	bool valid = false;
107 	BMimeType mime(B_PERSON_MIMETYPE);
108 	if (mime.IsInstalled()) {
109 		BMessage info;
110 		if (mime.GetAttrInfo(&info) == B_NO_ERROR) {
111 			int32 index = 0;
112 			while (true) {
113 				int32 type;
114 				if (info.FindInt32("attr:type", index, &type) != B_OK)
115 					break;
116 				bool editable;
117 				if (info.FindBool("attr:editable", index, &editable) != B_OK)
118 					break;
119 
120 				// TODO: Support other types besides string attributes.
121 				if (type != B_STRING_TYPE || !editable)
122 					break;
123 
124 				Attribute* attribute = new Attribute();
125 				ObjectDeleter<Attribute> deleter(attribute);
126 				if (info.FindString("attr:public_name", index,
127 						&attribute->name) != B_OK) {
128 					break;
129 				}
130 				if (info.FindString("attr:name", index,
131 						&attribute->attribute) != B_OK) {
132 					break;
133 				}
134 
135 				if (!fAttributes.AddItem(attribute))
136 					break;
137 
138 				deleter.Detach();
139 				index++;
140 			}
141 		}
142 		if (fAttributes.CountItems() == 0) {
143 			valid = false;
144 			mime.Delete();
145 		} else
146 			valid = true;
147 	}
148 	if (!valid) {
149 		mime.Install();
150 		mime.SetShortDescription(B_TRANSLATE_CONTEXT("Person",
151 			"Short mimetype description"));
152 		mime.SetLongDescription(B_TRANSLATE_CONTEXT(
153 			"Contact information for a person.",
154 			"Long mimetype description"));
155 		mime.SetIcon(kPersonIcon, sizeof(kPersonIcon));
156 		mime.SetPreferredApp(APP_SIG);
157 
158 		// add default person fields to meta-mime type
159 		BMessage fields;
160 		for (int32 i = 0; sDefaultAttributes[i].attribute; i++) {
161 			fields.AddString("attr:public_name", sDefaultAttributes[i].name);
162 			fields.AddString("attr:name", sDefaultAttributes[i].attribute);
163 			fields.AddInt32("attr:type", B_STRING_TYPE);
164 			fields.AddBool("attr:viewable", true);
165 			fields.AddBool("attr:editable", true);
166 			fields.AddInt32("attr:width", sDefaultAttributes[i].width);
167 			fields.AddInt32("attr:alignment", B_ALIGN_LEFT);
168 			fields.AddBool("attr:extra", false);
169 
170 			// Add the default attribute to the attribute list, too.
171 			Attribute* attribute = new Attribute();
172 			attribute->name = sDefaultAttributes[i].name;
173 			attribute->attribute = sDefaultAttributes[i].attribute;
174 			if (!fAttributes.AddItem(attribute))
175 				delete attribute;
176 		}
177 
178 		mime.SetAttrInfo(&fields);
179 	}
180 
181 	// create indices on all volumes for the found attributes.
182 
183 	int32 count = fAttributes.CountItems();
184 	BVolumeRoster volumeRoster;
185 	BVolume volume;
186 	while (volumeRoster.GetNextVolume(&volume) == B_OK) {
187 		for (int32 i = 0; i < count; i++) {
188 			Attribute* attribute = fAttributes.ItemAt(i);
189 			fs_create_index(volume.Device(), attribute->attribute,
190 				B_STRING_TYPE, 0);
191 		}
192 	}
193 
194 }
195 
196 
197 TPeopleApp::~TPeopleApp()
198 {
199 	delete fPrefs;
200 }
201 
202 
203 void
204 TPeopleApp::ArgvReceived(int32 argc, char** argv)
205 {
206 	BMessage message(B_REFS_RECEIVED);
207 
208 	for (int32 i = 1; i < argc; i++) {
209 		BEntry entry(argv[i]);
210 		entry_ref ref;
211 		if (entry.Exists() && entry.GetRef(&ref) == B_OK)
212 			message.AddRef("refs", &ref);
213 	}
214 
215 	RefsReceived(&message);
216 }
217 
218 
219 void
220 TPeopleApp::MessageReceived(BMessage* message)
221 {
222 	switch (message->what) {
223 		case M_NEW:
224 		case B_SILENT_RELAUNCH:
225 			_NewWindow();
226 			break;
227 
228 		case M_WINDOW_QUITS:
229 			_SavePreferences(message);
230 			fWindowCount--;
231 			if (fWindowCount < 1)
232 				PostMessage(B_QUIT_REQUESTED);
233 			break;
234 
235 		case M_CONFIGURE_ATTRIBUTES:
236 		{
237 			const char* arguments[] = { "-type", B_PERSON_MIMETYPE, 0 };
238 			status_t ret = be_roster->Launch(
239 				"application/x-vnd.Haiku-FileTypes",
240 				sizeof(arguments) / sizeof(const char*) - 1,
241 				const_cast<char**>(arguments));
242 			if (ret != B_OK && ret != B_ALREADY_RUNNING) {
243 				BString errorMsg(B_TRANSLATE("Launching the FileTypes "
244 					"preflet to configure Person attributes has failed."
245 					"\n\nError: "));
246 				errorMsg << strerror(ret);
247 				BAlert* alert = new BAlert(B_TRANSLATE("Error"),
248 					errorMsg.String(), B_TRANSLATE("OK"), NULL, NULL,
249 					B_WIDTH_AS_USUAL, B_STOP_ALERT);
250 				alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
251 				alert->Go(NULL);
252 			}
253 			break;
254 		}
255 
256 		default:
257 			BApplication::MessageReceived(message);
258 	}
259 }
260 
261 
262 void
263 TPeopleApp::RefsReceived(BMessage* message)
264 {
265 	int32 index = 0;
266 	while (message->HasRef("refs", index)) {
267 		entry_ref ref;
268 		message->FindRef("refs", index++, &ref);
269 
270 		PersonWindow* window = _FindWindow(ref);
271 		if (window != NULL)
272 			window->Activate(true);
273 		else {
274 			BFile file(&ref, B_READ_ONLY);
275 			if (file.InitCheck() == B_OK)
276 				_NewWindow(&ref);
277 		}
278 	}
279 }
280 
281 
282 void
283 TPeopleApp::ReadyToRun()
284 {
285 	if (fWindowCount < 1)
286 		_NewWindow();
287 }
288 
289 
290 // #pragma mark -
291 
292 
293 PersonWindow*
294 TPeopleApp::_NewWindow(entry_ref* ref)
295 {
296 	PersonWindow* window = new PersonWindow(fPosition,
297 		B_TRANSLATE("New person"), kNameAttribute,
298 		kCategoryAttribute, ref);
299 
300 	_AddAttributes(window);
301 
302 	window->Show();
303 
304 	fWindowCount++;
305 
306 	// Offset the position for the next window which will be opened and
307 	// reset it if it would open outside the screen bounds.
308 	fPosition.OffsetBy(20, 20);
309 	BScreen screen(window);
310 	if (fPosition.bottom > screen.Frame().bottom)
311 		fPosition.OffsetTo(fPosition.left, TITLE_BAR_HEIGHT);
312 	if (fPosition.right > screen.Frame().right)
313 		fPosition.OffsetTo(6, fPosition.top);
314 
315 	return window;
316 }
317 
318 
319 void
320 TPeopleApp::_AddAttributes(PersonWindow* window) const
321 {
322 	int32 count = fAttributes.CountItems();
323 	for (int32 i = 0; i < count; i++) {
324 		Attribute* attribute = fAttributes.ItemAt(i);
325 		const char* label = attribute->name;
326 		if (attribute->attribute == kNameAttribute)
327 			label = B_TRANSLATE("Name");
328 
329 		window->AddAttribute(label, attribute->attribute);
330 	}
331 }
332 
333 
334 PersonWindow*
335 TPeopleApp::_FindWindow(const entry_ref& ref) const
336 {
337 	for (int32 i = 0; BWindow* window = WindowAt(i); i++) {
338 		PersonWindow* personWindow = dynamic_cast<PersonWindow*>(window);
339 		if (personWindow == NULL)
340 			continue;
341 		if (personWindow->RefersPersonFile(ref))
342 			return personWindow;
343 	}
344 	return NULL;
345 }
346 
347 
348 void
349 TPeopleApp::_SavePreferences(BMessage* message) const
350 {
351 	BRect frame;
352 	if (message->FindRect("frame", &frame) != B_OK)
353 		return;
354 
355 	BPoint leftTop = frame.LeftTop();
356 
357 	if (fPrefs != NULL) {
358 		fPrefs->Seek(0, 0);
359 		fPrefs->Write(&leftTop, sizeof(BPoint));
360 	}
361 }
362 
363