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_TRANSLATE_CONTEXT 38 #define B_TRANSLATE_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("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_WITH_CONTEXT("Person", 151 "Short mimetype description")); 152 mime.SetLongDescription(B_TRANSLATE_WITH_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->Go(NULL); 251 } 252 break; 253 } 254 255 default: 256 BApplication::MessageReceived(message); 257 } 258 } 259 260 261 void 262 TPeopleApp::RefsReceived(BMessage* message) 263 { 264 int32 index = 0; 265 while (message->HasRef("refs", index)) { 266 entry_ref ref; 267 message->FindRef("refs", index++, &ref); 268 269 PersonWindow* window = _FindWindow(ref); 270 if (window != NULL) 271 window->Activate(true); 272 else { 273 BFile file(&ref, B_READ_ONLY); 274 if (file.InitCheck() == B_OK) 275 _NewWindow(&ref); 276 } 277 } 278 } 279 280 281 void 282 TPeopleApp::ReadyToRun() 283 { 284 if (fWindowCount < 1) 285 _NewWindow(); 286 } 287 288 289 // #pragma mark - 290 291 292 PersonWindow* 293 TPeopleApp::_NewWindow(entry_ref* ref) 294 { 295 PersonWindow* window = new PersonWindow(fPosition, 296 B_TRANSLATE("New person"), kNameAttribute, 297 kCategoryAttribute, ref); 298 299 _AddAttributes(window); 300 301 window->Show(); 302 303 fWindowCount++; 304 305 // Offset the position for the next window which will be opened and 306 // reset it if it would open outside the screen bounds. 307 fPosition.OffsetBy(20, 20); 308 BScreen screen(window); 309 if (fPosition.bottom > screen.Frame().bottom) 310 fPosition.OffsetTo(fPosition.left, TITLE_BAR_HEIGHT); 311 if (fPosition.right > screen.Frame().right) 312 fPosition.OffsetTo(6, fPosition.top); 313 314 return window; 315 } 316 317 318 void 319 TPeopleApp::_AddAttributes(PersonWindow* window) const 320 { 321 int32 count = fAttributes.CountItems(); 322 for (int32 i = 0; i < count; i++) { 323 Attribute* attribute = fAttributes.ItemAt(i); 324 const char* label = attribute->name; 325 if (attribute->attribute == kNameAttribute) 326 label = B_TRANSLATE("Name"); 327 328 window->AddAttribute(label, attribute->attribute); 329 } 330 } 331 332 333 PersonWindow* 334 TPeopleApp::_FindWindow(const entry_ref& ref) const 335 { 336 for (int32 i = 0; BWindow* window = WindowAt(i); i++) { 337 PersonWindow* personWindow = dynamic_cast<PersonWindow*>(window); 338 if (personWindow == NULL) 339 continue; 340 if (personWindow->RefersPersonFile(ref)) 341 return personWindow; 342 } 343 return NULL; 344 } 345 346 347 void 348 TPeopleApp::_SavePreferences(BMessage* message) const 349 { 350 BRect frame; 351 if (message->FindRect("frame", &frame) != B_OK) 352 return; 353 354 BPoint leftTop = frame.LeftTop(); 355 356 if (fPrefs != NULL) { 357 fPrefs->Seek(0, 0); 358 fPrefs->Write(&leftTop, sizeof(BPoint)); 359 } 360 } 361 362