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 fPosition.Set(6, TITLE_BAR_HEIGHT, 6 + WIND_WIDTH, 77 TITLE_BAR_HEIGHT + WIND_HEIGHT); 78 BPoint pos = fPosition.LeftTop(); 79 80 BPath path; 81 find_directory(B_USER_SETTINGS_DIRECTORY, &path, true); 82 83 BDirectory dir(path.Path()); 84 BEntry entry; 85 if (dir.FindEntry("People_data", &entry) == B_OK) { 86 fPrefs = new BFile(&entry, B_READ_WRITE); 87 if (fPrefs->InitCheck() == B_NO_ERROR) { 88 fPrefs->Read(&pos, sizeof(BPoint)); 89 if (BScreen(B_MAIN_SCREEN_ID).Frame().Contains(pos)) 90 fPosition.OffsetTo(pos); 91 } 92 } else { 93 fPrefs = new BFile(); 94 if (dir.CreateFile("People_data", fPrefs) != B_OK) { 95 delete fPrefs; 96 fPrefs = NULL; 97 } 98 } 99 100 // Read attributes from person mime type. If it does not exist, 101 // or if it contains no attribute definitions, install a "clean" 102 // person mime type from the hard-coded default attributes. 103 104 bool valid = false; 105 BMimeType mime(B_PERSON_MIMETYPE); 106 if (mime.IsInstalled()) { 107 BMessage info; 108 if (mime.GetAttrInfo(&info) == B_NO_ERROR) { 109 int32 index = 0; 110 while (true) { 111 int32 type; 112 if (info.FindInt32("attr:type", index, &type) != B_OK) 113 break; 114 bool editable; 115 if (info.FindBool("attr:editable", index, &editable) != B_OK) 116 break; 117 118 // TODO: Support other types besides string attributes. 119 if (type != B_STRING_TYPE || !editable) 120 break; 121 122 Attribute* attribute = new Attribute(); 123 ObjectDeleter<Attribute> deleter(attribute); 124 if (info.FindString("attr:public_name", index, 125 &attribute->name) != B_OK) { 126 break; 127 } 128 if (info.FindString("attr:name", index, 129 &attribute->attribute) != B_OK) { 130 break; 131 } 132 133 if (!fAttributes.AddItem(attribute)) 134 break; 135 136 deleter.Detach(); 137 index++; 138 } 139 } 140 if (fAttributes.CountItems() == 0) { 141 valid = false; 142 mime.Delete(); 143 } else 144 valid = true; 145 } 146 if (!valid) { 147 mime.Install(); 148 mime.SetShortDescription(B_TRANSLATE_WITH_CONTEXT("Person", 149 "Short mimetype description")); 150 mime.SetLongDescription(B_TRANSLATE_WITH_CONTEXT( 151 "Contact information for a person.", 152 "Long mimetype description")); 153 mime.SetIcon(kPersonIcon, sizeof(kPersonIcon)); 154 mime.SetPreferredApp(APP_SIG); 155 156 // add default person fields to meta-mime type 157 BMessage fields; 158 for (int32 i = 0; sDefaultAttributes[i].attribute; i++) { 159 fields.AddString("attr:public_name", sDefaultAttributes[i].name); 160 fields.AddString("attr:name", sDefaultAttributes[i].attribute); 161 fields.AddInt32("attr:type", B_STRING_TYPE); 162 fields.AddBool("attr:viewable", true); 163 fields.AddBool("attr:editable", true); 164 fields.AddInt32("attr:width", sDefaultAttributes[i].width); 165 fields.AddInt32("attr:alignment", B_ALIGN_LEFT); 166 fields.AddBool("attr:extra", false); 167 168 // Add the default attribute to the attribute list, too. 169 Attribute* attribute = new Attribute(); 170 attribute->name = sDefaultAttributes[i].name; 171 attribute->attribute = sDefaultAttributes[i].attribute; 172 if (!fAttributes.AddItem(attribute)) 173 delete attribute; 174 } 175 176 mime.SetAttrInfo(&fields); 177 } 178 179 // create indices on all volumes for the found attributes. 180 181 int32 count = fAttributes.CountItems(); 182 BVolumeRoster volumeRoster; 183 BVolume volume; 184 while (volumeRoster.GetNextVolume(&volume) == B_OK) { 185 for (int32 i = 0; i < count; i++) { 186 Attribute* attribute = fAttributes.ItemAt(i); 187 fs_create_index(volume.Device(), attribute->attribute, 188 B_STRING_TYPE, 0); 189 } 190 } 191 192 } 193 194 195 TPeopleApp::~TPeopleApp() 196 { 197 delete fPrefs; 198 } 199 200 201 void 202 TPeopleApp::ArgvReceived(int32 argc, char** argv) 203 { 204 BMessage message(B_REFS_RECEIVED); 205 206 for (int32 i = 1; i < argc; i++) { 207 BEntry entry(argv[i]); 208 entry_ref ref; 209 if (entry.Exists() && entry.GetRef(&ref) == B_OK) 210 message.AddRef("refs", &ref); 211 } 212 213 RefsReceived(&message); 214 } 215 216 217 void 218 TPeopleApp::MessageReceived(BMessage* message) 219 { 220 switch (message->what) { 221 case M_NEW: 222 case B_SILENT_RELAUNCH: 223 _NewWindow(); 224 break; 225 226 case M_WINDOW_QUITS: 227 _SavePreferences(message); 228 fWindowCount--; 229 if (fWindowCount < 1) 230 PostMessage(B_QUIT_REQUESTED); 231 break; 232 233 case M_CONFIGURE_ATTRIBUTES: 234 { 235 const char* arguments[] = { "-type", B_PERSON_MIMETYPE, 0 }; 236 status_t ret = be_roster->Launch( 237 "application/x-vnd.Haiku-FileTypes", 238 sizeof(arguments) / sizeof(const char*) - 1, 239 const_cast<char**>(arguments)); 240 if (ret != B_OK && ret != B_ALREADY_RUNNING) { 241 BString errorMsg(B_TRANSLATE("Launching the FileTypes " 242 "preflet to configure Person attributes has failed." 243 "\n\nError: ")); 244 errorMsg << strerror(ret); 245 BAlert* alert = new BAlert(B_TRANSLATE("Error"), 246 errorMsg.String(), B_TRANSLATE("OK"), NULL, NULL, 247 B_WIDTH_AS_USUAL, B_STOP_ALERT); 248 alert->Go(NULL); 249 } 250 break; 251 } 252 253 default: 254 BApplication::MessageReceived(message); 255 } 256 } 257 258 259 void 260 TPeopleApp::RefsReceived(BMessage* message) 261 { 262 int32 index = 0; 263 while (message->HasRef("refs", index)) { 264 entry_ref ref; 265 message->FindRef("refs", index++, &ref); 266 267 PersonWindow* window = _FindWindow(ref); 268 if (window != NULL) 269 window->Activate(true); 270 else { 271 BFile file(&ref, B_READ_ONLY); 272 if (file.InitCheck() == B_OK) 273 _NewWindow(&ref); 274 } 275 } 276 } 277 278 279 void 280 TPeopleApp::ReadyToRun() 281 { 282 if (fWindowCount < 1) 283 _NewWindow(); 284 } 285 286 287 // #pragma mark - 288 289 290 PersonWindow* 291 TPeopleApp::_NewWindow(entry_ref* ref) 292 { 293 PersonWindow* window = new PersonWindow(fPosition, 294 B_TRANSLATE("New person"), kNameAttribute, 295 kCategoryAttribute, ref); 296 297 _AddAttributes(window); 298 299 window->Show(); 300 301 fWindowCount++; 302 303 // Offset the position for the next window which will be opened and 304 // reset it if it would open outside the screen bounds. 305 fPosition.OffsetBy(20, 20); 306 BScreen screen(window); 307 if (fPosition.bottom > screen.Frame().bottom) 308 fPosition.OffsetTo(fPosition.left, TITLE_BAR_HEIGHT); 309 if (fPosition.right > screen.Frame().right) 310 fPosition.OffsetTo(6, fPosition.top); 311 312 return window; 313 } 314 315 316 void 317 TPeopleApp::_AddAttributes(PersonWindow* window) const 318 { 319 int32 count = fAttributes.CountItems(); 320 for (int32 i = 0; i < count; i++) { 321 Attribute* attribute = fAttributes.ItemAt(i); 322 const char* label = attribute->name; 323 if (attribute->attribute == kNameAttribute) 324 label = B_TRANSLATE("Name"); 325 326 window->AddAttribute(label, attribute->attribute); 327 } 328 } 329 330 331 PersonWindow* 332 TPeopleApp::_FindWindow(const entry_ref& ref) const 333 { 334 for (int32 i = 0; BWindow* window = WindowAt(i); i++) { 335 PersonWindow* personWindow = dynamic_cast<PersonWindow*>(window); 336 if (personWindow == NULL) 337 continue; 338 if (personWindow->RefersPersonFile(ref)) 339 return personWindow; 340 } 341 return NULL; 342 } 343 344 345 void 346 TPeopleApp::_SavePreferences(BMessage* message) const 347 { 348 BRect frame; 349 if (message->FindRect("frame", &frame) != B_OK) 350 return; 351 352 BPoint leftTop = frame.LeftTop(); 353 354 if (fPrefs != NULL) { 355 fPrefs->Seek(0, 0); 356 fPrefs->Write(&leftTop, sizeof(BPoint)); 357 } 358 } 359 360