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