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