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 * Stephan Aßmus <superstippi@gmx.de> 8 * 9 * Copyright 1999, Be Incorporated. All Rights Reserved. 10 * This file may be used under the terms of the Be Sample Code License. 11 */ 12 13 #include "PersonWindow.h" 14 15 #include <stdio.h> 16 #include <string.h> 17 18 #include <Alert.h> 19 #include <Catalog.h> 20 #include <Clipboard.h> 21 #include <ControlLook.h> 22 #include <FilePanel.h> 23 #include <FindDirectory.h> 24 #include <Font.h> 25 #include <LayoutBuilder.h> 26 #include <Locale.h> 27 #include <MenuBar.h> 28 #include <MenuItem.h> 29 #include <NodeInfo.h> 30 #include <NodeMonitor.h> 31 #include <Path.h> 32 #include <String.h> 33 #include <TextView.h> 34 #include <Volume.h> 35 36 #include "PeopleApp.h" 37 #include "PersonView.h" 38 39 40 #undef B_TRANSLATE_CONTEXT 41 #define B_TRANSLATE_CONTEXT "People" 42 43 44 PersonWindow::PersonWindow(BRect frame, const char* title, 45 const char* nameAttribute, const char* categoryAttribute, 46 const entry_ref* ref) 47 : 48 BWindow(frame, title, B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE 49 | B_AUTO_UPDATE_SIZE_LIMITS), 50 fRef(NULL), 51 fPanel(NULL), 52 fNameAttribute(nameAttribute) 53 { 54 BMenu* menu; 55 BMenuItem* item; 56 57 BMenuBar* menuBar = new BMenuBar(""); 58 menu = new BMenu(B_TRANSLATE("File")); 59 menu->AddItem(item = new BMenuItem( 60 B_TRANSLATE("New person" B_UTF8_ELLIPSIS), 61 new BMessage(M_NEW), 'N')); 62 item->SetTarget(be_app); 63 menu->AddItem(new BMenuItem(B_TRANSLATE("Close"), 64 new BMessage(B_QUIT_REQUESTED), 'W')); 65 menu->AddSeparatorItem(); 66 menu->AddItem(fSave = new BMenuItem(B_TRANSLATE("Save"), 67 new BMessage(M_SAVE), 'S')); 68 fSave->SetEnabled(FALSE); 69 menu->AddItem(new BMenuItem( 70 B_TRANSLATE("Save as"B_UTF8_ELLIPSIS), 71 new BMessage(M_SAVE_AS))); 72 menu->AddItem(fRevert = new BMenuItem(B_TRANSLATE("Revert"), 73 new BMessage(M_REVERT), 'R')); 74 fRevert->SetEnabled(FALSE); 75 menu->AddSeparatorItem(); 76 item = new BMenuItem(B_TRANSLATE("Quit"), 77 new BMessage(B_QUIT_REQUESTED), 'Q'); 78 item->SetTarget(be_app); 79 menu->AddItem(item); 80 menuBar->AddItem(menu); 81 82 menu = new BMenu(B_TRANSLATE("Edit")); 83 menu->AddItem(fUndo = new BMenuItem(B_TRANSLATE("Undo"), 84 new BMessage(B_UNDO), 'Z')); 85 fUndo->SetEnabled(false); 86 menu->AddSeparatorItem(); 87 menu->AddItem(fCut = new BMenuItem(B_TRANSLATE("Cut"), 88 new BMessage(B_CUT), 'X')); 89 menu->AddItem(fCopy = new BMenuItem(B_TRANSLATE("Copy"), 90 new BMessage(B_COPY), 'C')); 91 menu->AddItem(fPaste = new BMenuItem(B_TRANSLATE("Paste"), 92 new BMessage(B_PASTE), 'V')); 93 BMenuItem* selectAllItem = new BMenuItem(B_TRANSLATE("Select all"), 94 new BMessage(M_SELECT), 'A'); 95 menu->AddItem(selectAllItem); 96 menu->AddSeparatorItem(); 97 menu->AddItem(item = new BMenuItem(B_TRANSLATE("Configure attributes"), 98 new BMessage(M_CONFIGURE_ATTRIBUTES), 'F')); 99 item->SetTarget(be_app); 100 menuBar->AddItem(menu); 101 102 if (ref != NULL) { 103 SetTitle(ref->name); 104 _SetToRef(new entry_ref(*ref)); 105 } else 106 _SetToRef(NULL); 107 108 fView = new PersonView("PeopleView", categoryAttribute, fRef); 109 110 BLayoutBuilder::Group<>(this, B_VERTICAL, 0) 111 .SetInsets(0, 0, 0, 0) 112 .Add(menuBar) 113 .Add(fView); 114 115 fRevert->SetTarget(fView); 116 selectAllItem->SetTarget(fView); 117 } 118 119 120 PersonWindow::~PersonWindow() 121 { 122 _SetToRef(NULL); 123 } 124 125 126 void 127 PersonWindow::MenusBeginning() 128 { 129 bool enabled = !fView->IsSaved(); 130 fSave->SetEnabled(enabled); 131 fRevert->SetEnabled(enabled); 132 133 bool isRedo = false; 134 bool undoEnabled = false; 135 bool cutAndCopyEnabled = false; 136 137 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus()); 138 if (textView != NULL) { 139 undo_state state = textView->UndoState(&isRedo); 140 undoEnabled = state != B_UNDO_UNAVAILABLE; 141 142 cutAndCopyEnabled = fView->IsTextSelected(); 143 } 144 145 if (isRedo) 146 fUndo->SetLabel(B_TRANSLATE("Redo")); 147 else 148 fUndo->SetLabel(B_TRANSLATE("Undo")); 149 fUndo->SetEnabled(undoEnabled); 150 fCut->SetEnabled(cutAndCopyEnabled); 151 fCopy->SetEnabled(cutAndCopyEnabled); 152 153 be_clipboard->Lock(); 154 fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE)); 155 be_clipboard->Unlock(); 156 157 fView->BuildGroupMenu(); 158 } 159 160 161 void 162 PersonWindow::MessageReceived(BMessage* msg) 163 { 164 char str[256]; 165 BDirectory directory; 166 BEntry entry; 167 BFile file; 168 BNodeInfo *node; 169 170 switch (msg->what) { 171 case M_SAVE: 172 if (!fRef) { 173 SaveAs(); 174 break; 175 } 176 // supposed to fall through 177 case M_REVERT: 178 case M_SELECT: 179 fView->MessageReceived(msg); 180 break; 181 182 case M_SAVE_AS: 183 SaveAs(); 184 break; 185 186 case B_SAVE_REQUESTED: 187 { 188 entry_ref dir; 189 if (msg->FindRef("directory", &dir) == B_OK) { 190 const char* name = NULL; 191 msg->FindString("name", &name); 192 directory.SetTo(&dir); 193 if (directory.InitCheck() == B_NO_ERROR) { 194 directory.CreateFile(name, &file); 195 if (file.InitCheck() == B_NO_ERROR) { 196 node = new BNodeInfo(&file); 197 node->SetType("application/x-person"); 198 delete node; 199 200 directory.FindEntry(name, &entry); 201 entry.GetRef(&dir); 202 _SetToRef(new entry_ref(dir)); 203 SetTitle(fRef->name); 204 fView->CreateFile(fRef); 205 } 206 else { 207 sprintf(str, B_TRANSLATE("Could not create %s."), name); 208 (new BAlert("", str, B_TRANSLATE("Sorry")))->Go(); 209 } 210 } 211 } 212 break; 213 } 214 215 case B_NODE_MONITOR: 216 { 217 int32 opcode; 218 if (msg->FindInt32("opcode", &opcode) == B_OK) { 219 switch (opcode) { 220 case B_ENTRY_REMOVED: 221 // We lost our file. Close the window. 222 PostMessage(B_QUIT_REQUESTED); 223 break; 224 225 case B_ENTRY_MOVED: 226 { 227 // We may have renamed our entry. Obtain relevant data 228 // from message. 229 BString name; 230 msg->FindString("name", &name); 231 232 int64 directory; 233 msg->FindInt64("to directory", &directory); 234 235 int32 device; 236 msg->FindInt32("device", &device); 237 238 // Update our ref. 239 delete fRef; 240 fRef = new entry_ref(device, directory, name.String()); 241 242 // And our window title. 243 SetTitle(name); 244 245 // If moved to Trash, close window. 246 BVolume volume(device); 247 BPath trash; 248 find_directory(B_TRASH_DIRECTORY, &trash, false, 249 &volume); 250 BPath folder(fRef); 251 folder.GetParent(&folder); 252 if (folder == trash) 253 PostMessage(B_QUIT_REQUESTED); 254 255 break; 256 } 257 258 case B_ATTR_CHANGED: 259 { 260 // An attribute was updated. 261 BString attr; 262 if (msg->FindString("attr", &attr) == B_OK) 263 fView->SetAttribute(attr.String(), true); 264 break; 265 } 266 case B_STAT_CHANGED: 267 fView->UpdatePicture(fRef); 268 break; 269 } 270 } 271 break; 272 } 273 274 default: 275 BWindow::MessageReceived(msg); 276 } 277 } 278 279 280 bool 281 PersonWindow::QuitRequested() 282 { 283 status_t result; 284 285 if (!fView->IsSaved()) { 286 result = (new BAlert("", B_TRANSLATE("Save changes before quitting?"), 287 B_TRANSLATE("Cancel"), B_TRANSLATE("Quit"), 288 B_TRANSLATE("Save")))->Go(); 289 if (result == 2) { 290 if (fRef) 291 fView->Save(); 292 else { 293 SaveAs(); 294 return false; 295 } 296 } else if (result == 0) 297 return false; 298 } 299 300 delete fPanel; 301 302 BMessage message(M_WINDOW_QUITS); 303 message.AddRect("frame", Frame()); 304 if (be_app->Lock()) { 305 be_app->PostMessage(&message); 306 be_app->Unlock(); 307 } 308 309 return true; 310 } 311 312 313 void 314 PersonWindow::Show() 315 { 316 fView->MakeFocus(); 317 BWindow::Show(); 318 } 319 320 321 void 322 PersonWindow::AddAttribute(const char* label, const char* attribute) 323 { 324 fView->AddAttribute(label, attribute); 325 } 326 327 328 void 329 PersonWindow::SaveAs() 330 { 331 char name[B_FILE_NAME_LENGTH]; 332 _GetDefaultFileName(name); 333 334 if (fPanel == NULL) { 335 BMessenger target(this); 336 fPanel = new BFilePanel(B_SAVE_PANEL, &target); 337 338 BPath path; 339 find_directory(B_USER_DIRECTORY, &path, true); 340 341 BDirectory dir; 342 dir.SetTo(path.Path()); 343 344 BEntry entry; 345 if (dir.FindEntry("people", &entry) == B_OK 346 || (dir.CreateDirectory("people", &dir) == B_OK 347 && dir.GetEntry(&entry) == B_OK)) { 348 fPanel->SetPanelDirectory(&entry); 349 } 350 } 351 352 if (fPanel->Window()->Lock()) { 353 fPanel->SetSaveText(name); 354 if (fPanel->Window()->IsHidden()) 355 fPanel->Window()->Show(); 356 else 357 fPanel->Window()->Activate(); 358 fPanel->Window()->Unlock(); 359 } 360 } 361 362 363 bool 364 PersonWindow::RefersPersonFile(const entry_ref& ref) const 365 { 366 if (fRef == NULL) 367 return false; 368 return *fRef == ref; 369 } 370 371 372 void 373 PersonWindow::_GetDefaultFileName(char* name) 374 { 375 strncpy(name, fView->AttributeValue(fNameAttribute), B_FILE_NAME_LENGTH); 376 while (*name) { 377 if (*name == '/') 378 *name = '-'; 379 name++; 380 } 381 } 382 383 384 void 385 PersonWindow::_SetToRef(entry_ref* ref) 386 { 387 if (fRef != NULL) { 388 _WatchChanges(false); 389 delete fRef; 390 } 391 392 fRef = ref; 393 394 _WatchChanges(true); 395 } 396 397 398 void 399 PersonWindow::_WatchChanges(bool enable) 400 { 401 if (fRef == NULL) 402 return; 403 404 node_ref nodeRef; 405 406 BNode node(fRef); 407 node.GetNodeRef(&nodeRef); 408 409 uint32 flags; 410 BString action; 411 412 if (enable) { 413 // Start watching. 414 flags = B_WATCH_ALL; 415 action = "starting"; 416 } else { 417 // Stop watching. 418 flags = B_STOP_WATCHING; 419 action = "stoping"; 420 } 421 422 if (watch_node(&nodeRef, flags, this) != B_OK) { 423 printf("Error %s node monitor.\n", action.String()); 424 } 425 } 426