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