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_TRANSLATION_CONTEXT 41 #define B_TRANSLATION_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_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 .Add(menuBar) 112 .AddGroup(B_VERTICAL, 0) 113 .Add(fView) 114 .SetInsets(B_USE_WINDOW_SPACING) 115 .End(); 116 117 fRevert->SetTarget(fView); 118 selectAllItem->SetTarget(fView); 119 } 120 121 122 PersonWindow::~PersonWindow() 123 { 124 _SetToRef(NULL); 125 } 126 127 128 void 129 PersonWindow::MenusBeginning() 130 { 131 bool enabled = !fView->IsSaved(); 132 fSave->SetEnabled(enabled); 133 fRevert->SetEnabled(enabled); 134 135 bool isRedo = false; 136 bool undoEnabled = false; 137 bool cutAndCopyEnabled = false; 138 139 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus()); 140 if (textView != NULL) { 141 undo_state state = textView->UndoState(&isRedo); 142 undoEnabled = state != B_UNDO_UNAVAILABLE; 143 144 cutAndCopyEnabled = fView->IsTextSelected(); 145 } 146 147 if (isRedo) 148 fUndo->SetLabel(B_TRANSLATE("Redo")); 149 else 150 fUndo->SetLabel(B_TRANSLATE("Undo")); 151 fUndo->SetEnabled(undoEnabled); 152 fCut->SetEnabled(cutAndCopyEnabled); 153 fCopy->SetEnabled(cutAndCopyEnabled); 154 155 be_clipboard->Lock(); 156 fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE)); 157 be_clipboard->Unlock(); 158 159 fView->BuildGroupMenu(); 160 } 161 162 163 void 164 PersonWindow::MessageReceived(BMessage* msg) 165 { 166 char str[256]; 167 BDirectory directory; 168 BEntry entry; 169 BFile file; 170 BNodeInfo *node; 171 172 switch (msg->what) { 173 case M_SAVE: 174 if (!fRef) { 175 SaveAs(); 176 break; 177 } 178 // supposed to fall through 179 case M_REVERT: 180 case M_SELECT: 181 fView->MessageReceived(msg); 182 break; 183 184 case M_SAVE_AS: 185 SaveAs(); 186 break; 187 188 case B_UNDO: // fall through 189 case B_CUT: 190 case B_COPY: 191 case B_PASTE: 192 { 193 BView* view = CurrentFocus(); 194 if (view != NULL) 195 view->MessageReceived(msg); 196 break; 197 } 198 199 case B_SAVE_REQUESTED: 200 { 201 entry_ref dir; 202 if (msg->FindRef("directory", &dir) == B_OK) { 203 const char* name = NULL; 204 msg->FindString("name", &name); 205 directory.SetTo(&dir); 206 if (directory.InitCheck() == B_NO_ERROR) { 207 directory.CreateFile(name, &file); 208 if (file.InitCheck() == B_NO_ERROR) { 209 node = new BNodeInfo(&file); 210 node->SetType("application/x-person"); 211 delete node; 212 213 directory.FindEntry(name, &entry); 214 entry.GetRef(&dir); 215 _SetToRef(new entry_ref(dir)); 216 SetTitle(fRef->name); 217 fView->CreateFile(fRef); 218 } 219 else { 220 sprintf(str, B_TRANSLATE("Could not create %s."), name); 221 BAlert* alert = new BAlert("", str, B_TRANSLATE("Sorry")); 222 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); 223 alert->Go(); 224 } 225 } 226 } 227 break; 228 } 229 230 case B_NODE_MONITOR: 231 { 232 int32 opcode; 233 if (msg->FindInt32("opcode", &opcode) == B_OK) { 234 switch (opcode) { 235 case B_ENTRY_REMOVED: 236 // We lost our file. Close the window. 237 PostMessage(B_QUIT_REQUESTED); 238 break; 239 240 case B_ENTRY_MOVED: 241 { 242 // We may have renamed our entry. Obtain relevant data 243 // from message. 244 BString name; 245 msg->FindString("name", &name); 246 247 int64 directory; 248 msg->FindInt64("to directory", &directory); 249 250 int32 device; 251 msg->FindInt32("device", &device); 252 253 // Update our ref. 254 delete fRef; 255 fRef = new entry_ref(device, directory, name.String()); 256 257 // And our window title. 258 SetTitle(name); 259 260 // If moved to Trash, close window. 261 BVolume volume(device); 262 BPath trash; 263 find_directory(B_TRASH_DIRECTORY, &trash, false, 264 &volume); 265 BPath folder(fRef); 266 folder.GetParent(&folder); 267 if (folder == trash) 268 PostMessage(B_QUIT_REQUESTED); 269 270 break; 271 } 272 273 case B_ATTR_CHANGED: 274 { 275 // An attribute was updated. 276 BString attr; 277 if (msg->FindString("attr", &attr) == B_OK) 278 fView->SetAttribute(attr.String(), true); 279 break; 280 } 281 case B_STAT_CHANGED: 282 fView->UpdatePicture(fRef); 283 break; 284 } 285 } 286 break; 287 } 288 289 default: 290 BWindow::MessageReceived(msg); 291 } 292 } 293 294 295 bool 296 PersonWindow::QuitRequested() 297 { 298 status_t result; 299 300 if (!fView->IsSaved()) { 301 BAlert* alert = new BAlert("", 302 B_TRANSLATE("Save changes before closing?"), B_TRANSLATE("Cancel"), 303 B_TRANSLATE("Don't save"), B_TRANSLATE("Save"), 304 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT); 305 alert->SetShortcut(0, B_ESCAPE); 306 alert->SetShortcut(1, 'd'); 307 alert->SetShortcut(2, 's'); 308 result = alert->Go(); 309 310 if (result == 2) { 311 if (fRef) 312 fView->Save(); 313 else { 314 SaveAs(); 315 return false; 316 } 317 } else if (result == 0) 318 return false; 319 } 320 321 delete fPanel; 322 323 BMessage message(M_WINDOW_QUITS); 324 message.AddRect("frame", Frame()); 325 if (be_app->Lock()) { 326 be_app->PostMessage(&message); 327 be_app->Unlock(); 328 } 329 330 return true; 331 } 332 333 334 void 335 PersonWindow::Show() 336 { 337 fView->MakeFocus(); 338 BWindow::Show(); 339 } 340 341 342 void 343 PersonWindow::AddAttribute(const char* label, const char* attribute) 344 { 345 fView->AddAttribute(label, attribute); 346 } 347 348 349 void 350 PersonWindow::SaveAs() 351 { 352 char name[B_FILE_NAME_LENGTH]; 353 _GetDefaultFileName(name); 354 355 if (fPanel == NULL) { 356 BMessenger target(this); 357 fPanel = new BFilePanel(B_SAVE_PANEL, &target); 358 359 BPath path; 360 find_directory(B_USER_DIRECTORY, &path, true); 361 362 BDirectory dir; 363 dir.SetTo(path.Path()); 364 365 BEntry entry; 366 if (dir.FindEntry("people", &entry) == B_OK 367 || (dir.CreateDirectory("people", &dir) == B_OK 368 && dir.GetEntry(&entry) == B_OK)) { 369 fPanel->SetPanelDirectory(&entry); 370 } 371 } 372 373 if (fPanel->Window()->Lock()) { 374 fPanel->SetSaveText(name); 375 if (fPanel->Window()->IsHidden()) 376 fPanel->Window()->Show(); 377 else 378 fPanel->Window()->Activate(); 379 fPanel->Window()->Unlock(); 380 } 381 } 382 383 384 bool 385 PersonWindow::RefersPersonFile(const entry_ref& ref) const 386 { 387 if (fRef == NULL) 388 return false; 389 return *fRef == ref; 390 } 391 392 393 void 394 PersonWindow::_GetDefaultFileName(char* name) 395 { 396 strncpy(name, fView->AttributeValue(fNameAttribute), B_FILE_NAME_LENGTH); 397 while (*name) { 398 if (*name == '/') 399 *name = '-'; 400 name++; 401 } 402 } 403 404 405 void 406 PersonWindow::_SetToRef(entry_ref* ref) 407 { 408 if (fRef != NULL) { 409 _WatchChanges(false); 410 delete fRef; 411 } 412 413 fRef = ref; 414 415 _WatchChanges(true); 416 } 417 418 419 void 420 PersonWindow::_WatchChanges(bool enable) 421 { 422 if (fRef == NULL) 423 return; 424 425 node_ref nodeRef; 426 427 BNode node(fRef); 428 node.GetNodeRef(&nodeRef); 429 430 uint32 flags; 431 BString action; 432 433 if (enable) { 434 // Start watching. 435 flags = B_WATCH_ALL; 436 action = "starting"; 437 } else { 438 // Stop watching. 439 flags = B_STOP_WATCHING; 440 action = "stoping"; 441 } 442 443 if (watch_node(&nodeRef, flags, this) != B_OK) { 444 printf("Error %s node monitor.\n", action.String()); 445 } 446 } 447