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