1 /* 2 Open Tracker License 3 4 Terms and Conditions 5 6 Copyright (c) 1991-2001, Be Incorporated. All rights reserved. 7 8 Permission is hereby granted, free of charge, to any person obtaining a copy of 9 this software and associated documentation files (the "Software"), to deal in 10 the Software without restriction, including without limitation the rights to 11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 12 of the Software, and to permit persons to whom the Software is furnished to do 13 so, subject to the following conditions: 14 15 The above copyright notice and this permission notice applies to all licensees 16 and shall be included in all copies or substantial portions of the Software. 17 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY, 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN 22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION 23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 25 Except as contained in this notice, the name of Be Incorporated shall not be 26 used in advertising or otherwise to promote the sale, use or other dealings in 27 this Software without prior written authorization from Be Incorporated. 28 29 BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks 30 of Be Incorporated in the United States and other countries. Other brand product 31 names are registered trademarks or trademarks of their respective holders. 32 All rights reserved. 33 */ 34 35 //-------------------------------------------------------------------- 36 // 37 // Signature.cpp 38 // 39 //-------------------------------------------------------------------- 40 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <Clipboard.h> 45 #include <InterfaceKit.h> 46 #include <StorageKit.h> 47 48 #include "Mail.h" 49 #include "Signature.h" 50 51 #include <MDRLanguage.h> 52 53 extern BRect signature_window; 54 extern const char *kUndoStrings[]; 55 extern const char *kRedoStrings[]; 56 57 const char kNameText[] = MDR_DIALECT_CHOICE ("Title:", "署名の名称:"); 58 const char kSigText[] = MDR_DIALECT_CHOICE ("Signature:", "署名:"); 59 60 61 //==================================================================== 62 63 TSignatureWindow::TSignatureWindow(BRect rect) 64 : BWindow (rect, MDR_DIALECT_CHOICE ("Signatures","署名の編集"), B_TITLED_WINDOW, 0), 65 fFile(NULL) 66 { 67 BMenu *menu; 68 BMenuBar *menu_bar; 69 BMenuItem *item; 70 71 BRect r = Bounds(); 72 /*** Set up the menus ****/ 73 menu_bar = new BMenuBar(r, "MenuBar"); 74 menu = new BMenu(MDR_DIALECT_CHOICE ("Signature","S) 署名")); 75 menu->AddItem(fNew = new BMenuItem(MDR_DIALECT_CHOICE ("New","N) 新規"), new BMessage(M_NEW), 'N')); 76 fSignature = new TMenu(MDR_DIALECT_CHOICE ("Open","O) 開く"), INDEX_SIGNATURE, M_SIGNATURE); 77 menu->AddItem(new BMenuItem(fSignature)); 78 menu->AddSeparatorItem(); 79 menu->AddItem(fSave = new BMenuItem(MDR_DIALECT_CHOICE ("Save","S) 保存"), new BMessage(M_SAVE), 'S')); 80 menu->AddItem(fDelete = new BMenuItem(MDR_DIALECT_CHOICE ("Delete","T) 削除"), new BMessage(M_DELETE), 'T')); 81 menu_bar->AddItem(menu); 82 83 menu = new BMenu(MDR_DIALECT_CHOICE ("Edit","E) 編集")); 84 menu->AddItem(fUndo = new BMenuItem(MDR_DIALECT_CHOICE ("Undo","Z) やり直し"), new BMessage(B_UNDO), 'Z')); 85 fUndo->SetTarget(NULL, this); 86 menu->AddSeparatorItem(); 87 menu->AddItem(fCut = new BMenuItem(MDR_DIALECT_CHOICE ("Cut","X) 切り取り"), new BMessage(B_CUT), 'X')); 88 fCut->SetTarget(NULL, this); 89 menu->AddItem(fCopy = new BMenuItem(MDR_DIALECT_CHOICE ("Copy","C) コピー"), new BMessage(B_COPY), 'C')); 90 fCopy->SetTarget(NULL, this); 91 menu->AddItem(fPaste = new BMenuItem(MDR_DIALECT_CHOICE ("Paste","V) 貼り付け"), new BMessage(B_PASTE), 'V')); 92 fPaste->SetTarget(NULL, this); 93 menu->AddSeparatorItem(); 94 menu->AddItem(item = new BMenuItem(MDR_DIALECT_CHOICE ("Select All","A) 全文選択"), new BMessage(M_SELECT), 'A')); 95 item->SetTarget(NULL, this); 96 menu_bar->AddItem(menu); 97 98 AddChild(menu_bar); 99 /**** Done with the menu set up *****/ 100 101 /**** Add on the panel, giving it the width and at least one vertical pixel *****/ 102 fSigView = new TSignatureView(BRect(0, menu_bar->Frame().bottom+1, 103 rect.Width(), menu_bar->Frame().bottom+2)); 104 AddChild(fSigView); 105 106 /* resize the window to the correct height */ 107 fSigView->SetResizingMode(B_FOLLOW_NONE); 108 ResizeTo(rect.Width()-2, fSigView->Frame().bottom-2); 109 fSigView->SetResizingMode(B_FOLLOW_ALL); 110 111 SetSizeLimits(kSigWidth, RIGHT_BOUNDARY, r.top + 100, RIGHT_BOUNDARY); 112 } 113 114 115 TSignatureWindow::~TSignatureWindow() 116 { 117 signature_window = Frame(); 118 } 119 120 121 void 122 TSignatureWindow::MenusBeginning() 123 { 124 int32 finish = 0; 125 int32 start = 0; 126 BTextView *text_view; 127 128 fDelete->SetEnabled(fFile); 129 fSave->SetEnabled(IsDirty()); 130 fUndo->SetEnabled(false); // ***TODO*** 131 132 text_view = (BTextView *)fSigView->fName->ChildAt(0); 133 if (text_view->IsFocus()) 134 text_view->GetSelection(&start, &finish); 135 else 136 fSigView->fTextView->GetSelection(&start, &finish); 137 138 fCut->SetEnabled(start != finish); 139 fCopy->SetEnabled(start != finish); 140 141 fNew->SetEnabled(text_view->TextLength() | fSigView->fTextView->TextLength()); 142 be_clipboard->Lock(); 143 fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE)); 144 be_clipboard->Unlock(); 145 146 // Undo stuff 147 bool isRedo = false; 148 undo_state undoState = B_UNDO_UNAVAILABLE; 149 150 BTextView *focusTextView = dynamic_cast<BTextView *>(CurrentFocus()); 151 if (focusTextView != NULL) 152 undoState = focusTextView->UndoState(&isRedo); 153 154 fUndo->SetLabel((isRedo) ? kRedoStrings[undoState] : kUndoStrings[undoState]); 155 fUndo->SetEnabled(undoState != B_UNDO_UNAVAILABLE); 156 } 157 158 159 void 160 TSignatureWindow::MessageReceived(BMessage* msg) 161 { 162 char *sig; 163 char name[B_FILE_NAME_LENGTH]; 164 BFont *font; 165 BTextView *text_view; 166 entry_ref ref; 167 off_t size; 168 169 switch(msg->what) { 170 case CHANGE_FONT: 171 msg->FindPointer("font", (void **)&font); 172 fSigView->fTextView->SetFontAndColor(font); 173 fSigView->fTextView->Invalidate(fSigView->fTextView->Bounds()); 174 break; 175 176 case M_NEW: 177 if (Clear()) { 178 fSigView->fName->SetText(""); 179 // fSigView->fTextView->SetText(NULL, (int32)0); 180 fSigView->fTextView->SetText(""); 181 fSigView->fName->MakeFocus(true); 182 } 183 break; 184 185 case M_SAVE: 186 Save(); 187 break; 188 189 case M_DELETE: 190 if (!(new BAlert("",MDR_DIALECT_CHOICE ( 191 "Really delete this signature? This cannot be undone.", 192 "この署名を削除しますか?"), 193 MDR_DIALECT_CHOICE ("Cancel","取消l"), 194 MDR_DIALECT_CHOICE ("Delete","削除"), NULL, B_WIDTH_AS_USUAL, 195 B_WARNING_ALERT))->Go()) 196 break; 197 198 if (fFile) { 199 delete fFile; 200 fFile = NULL; 201 fEntry.Remove(); 202 fSigView->fName->SetText(""); 203 fSigView->fTextView->SetText(NULL, (int32)0); 204 fSigView->fName->MakeFocus(true); 205 } 206 break; 207 208 case M_SIGNATURE: 209 if (Clear()) { 210 msg->FindRef("ref", &ref); 211 fEntry.SetTo(&ref); 212 fFile = new BFile(&ref, O_RDWR); 213 if (fFile->InitCheck() == B_NO_ERROR) { 214 fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name)); 215 fSigView->fName->SetText(name); 216 fFile->GetSize(&size); 217 sig = (char *)malloc(size); 218 size = fFile->Read(sig, size); 219 fSigView->fTextView->SetText(sig, size); 220 fSigView->fName->MakeFocus(true); 221 text_view = (BTextView *)fSigView->fName->ChildAt(0); 222 text_view->Select(0, text_view->TextLength()); 223 fSigView->fTextView->fDirty = false; 224 } 225 else { 226 fFile = NULL; 227 beep(); 228 (new BAlert("", MDR_DIALECT_CHOICE ( 229 "Couldn't open this signature. Sorry.", 230 "署名を開く時にエラーが発生しました。"), 231 MDR_DIALECT_CHOICE ("OK","了解")))->Go(); 232 } 233 } 234 break; 235 236 default: 237 BWindow::MessageReceived(msg); 238 } 239 } 240 241 242 bool 243 TSignatureWindow::QuitRequested() 244 { 245 if (Clear()) { 246 BMessage msg(WINDOW_CLOSED); 247 msg.AddInt32("kind", SIG_WINDOW); 248 249 be_app->PostMessage(&msg); 250 return true; 251 } 252 return false; 253 } 254 255 256 void 257 TSignatureWindow::FrameResized(float width, float height) 258 { 259 fSigView->FrameResized(width, height); 260 } 261 262 263 void 264 TSignatureWindow::Show() 265 { 266 BTextView *text_view; 267 268 Lock(); 269 text_view = (BTextView *)fSigView->fName->TextView(); 270 fSigView->fName->MakeFocus(true); 271 text_view->Select(0, text_view->TextLength()); 272 Unlock(); 273 274 BWindow::Show(); 275 } 276 277 278 bool 279 TSignatureWindow::Clear() 280 { 281 int32 result; 282 283 if (IsDirty()) { 284 beep(); 285 BAlert *alert = new BAlert("", 286 MDR_DIALECT_CHOICE ("Save changes to this signature?","変更した署名を保存しますか?"), 287 MDR_DIALECT_CHOICE ("Don't Save","保存しない"), 288 MDR_DIALECT_CHOICE ("Cancel","中止"), 289 MDR_DIALECT_CHOICE ("Save","保存する"), 290 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 291 alert->SetShortcut(0,'d'); 292 alert->SetShortcut(1,B_ESCAPE); 293 result = alert->Go(); 294 if (result == 1) 295 return false; 296 if (result == 2) 297 Save(); 298 } 299 300 delete fFile; 301 fFile = NULL; 302 fSigView->fTextView->fDirty = false; 303 return true; 304 } 305 306 307 bool 308 TSignatureWindow::IsDirty() 309 { 310 char name[B_FILE_NAME_LENGTH]; 311 312 if (fFile) { 313 fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name)); 314 if ((strcmp(name, fSigView->fName->Text())) || (fSigView->fTextView->fDirty)) 315 return true; 316 } 317 else { 318 if ((strlen(fSigView->fName->Text())) || 319 (fSigView->fTextView->TextLength())) 320 return true; 321 } 322 return false; 323 } 324 325 326 void 327 TSignatureWindow::Save() 328 { 329 char name[B_FILE_NAME_LENGTH]; 330 int32 index = 0; 331 status_t result; 332 BDirectory dir; 333 BEntry entry; 334 BNodeInfo *node; 335 BPath path; 336 337 if (!fFile) { 338 find_directory(B_USER_SETTINGS_DIRECTORY, &path, true); 339 dir.SetTo(path.Path()); 340 341 if (dir.FindEntry("bemail", &entry) == B_NO_ERROR) 342 dir.SetTo(&entry); 343 else 344 dir.CreateDirectory("bemail", &dir); 345 346 if (dir.InitCheck() != B_NO_ERROR) 347 goto err_exit; 348 349 if (dir.FindEntry("signatures", &entry) == B_NO_ERROR) 350 dir.SetTo(&entry); 351 else 352 dir.CreateDirectory("signatures", &dir); 353 354 if (dir.InitCheck() != B_NO_ERROR) 355 goto err_exit; 356 357 fFile = new BFile(); 358 while(true) { 359 sprintf(name, "signature_%ld", index++); 360 if ((result = dir.CreateFile(name, fFile, true)) == B_NO_ERROR) 361 break; 362 if (result != EEXIST) 363 goto err_exit; 364 } 365 dir.FindEntry(name, &fEntry); 366 node = new BNodeInfo(fFile); 367 node->SetType("text/plain"); 368 delete node; 369 } 370 371 fSigView->fTextView->fDirty = false; 372 fFile->Seek(0, 0); 373 fFile->Write(fSigView->fTextView->Text(), 374 fSigView->fTextView->TextLength()); 375 fFile->SetSize(fFile->Position()); 376 fFile->WriteAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, fSigView->fName->Text(), 377 strlen(fSigView->fName->Text()) + 1); 378 return; 379 380 err_exit: 381 beep(); 382 (new BAlert("", MDR_DIALECT_CHOICE ( 383 "An error occurred trying to save this signature.", 384 "署名を保存しようとした時にエラーが発生しました。"), 385 MDR_DIALECT_CHOICE ("Sorry","了解")))->Go(); 386 } 387 388 389 //==================================================================== 390 // #pragma mark - 391 392 393 TSignatureView::TSignatureView(BRect rect) 394 : BBox(rect, "SigView", B_FOLLOW_ALL, B_WILL_DRAW) 395 { 396 } 397 398 399 void 400 TSignatureView::AttachedToWindow() 401 { 402 BRect rect = Bounds(); 403 float name_text_length = StringWidth(kNameText); 404 float sig_text_length = StringWidth(kSigText); 405 float divide_length; 406 407 if (name_text_length > sig_text_length) 408 divide_length = name_text_length; 409 else 410 divide_length = sig_text_length; 411 412 rect.InsetBy(8,0); 413 rect.top+= 8; 414 415 fName = new TNameControl(rect, kNameText, new BMessage(NAME_FIELD)); 416 AddChild(fName); 417 418 fName->SetDivider(divide_length + 10); 419 fName->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 420 421 rect.OffsetBy(0,fName->Bounds().Height()+5); 422 rect.bottom = rect.top + kSigHeight; 423 rect.left = fName->TextView()->Frame().left; 424 425 BRect text = rect; 426 text.OffsetTo(10,0); 427 fTextView = new TSigTextView(rect, text); 428 BScrollView *scroller = new BScrollView("SigScroller", fTextView, B_FOLLOW_ALL, 0, false, true); 429 AddChild(scroller); 430 scroller->ResizeBy(-1 * scroller->ScrollBar(B_VERTICAL)->Frame().Width() - 9, 0); 431 scroller->MoveBy(7,0); 432 433 /* back up a bit to make room for the label */ 434 435 rect = scroller->Frame(); 436 BStringView *stringView = new BStringView(rect, "SigLabel", kSigText); 437 AddChild(stringView); 438 439 float tWidth, tHeight; 440 stringView->GetPreferredSize(&tWidth, &tHeight); 441 442 /* the 5 is for the spacer in the TextView */ 443 444 rect.OffsetBy(-1 *(tWidth) - 5, 0); 445 rect.right = rect.left + tWidth; 446 rect.bottom = rect.top + tHeight; 447 448 stringView->MoveTo(rect.LeftTop()); 449 stringView->ResizeTo(rect.Width(), rect.Height()); 450 451 /* Resize the View to the correct height */ 452 scroller->SetResizingMode(B_FOLLOW_NONE); 453 ResizeTo(Frame().Width(), scroller->Frame().bottom + 8); 454 scroller->SetResizingMode(B_FOLLOW_ALL); 455 } 456 457 458 //==================================================================== 459 // #pragma mark - 460 461 462 TNameControl::TNameControl(BRect rect, const char *label, BMessage *msg) 463 :BTextControl(rect, "", label, "", msg, B_FOLLOW_LEFT_RIGHT) 464 { 465 strcpy(fLabel, label); 466 } 467 468 469 void 470 TNameControl::AttachedToWindow() 471 { 472 BTextControl::AttachedToWindow(); 473 474 SetDivider(StringWidth(fLabel) + 6); 475 TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1); 476 } 477 478 479 void 480 TNameControl::MessageReceived(BMessage *msg) 481 { 482 switch (msg->what) { 483 case M_SELECT: 484 TextView()->Select(0, TextView()->TextLength()); 485 break; 486 487 default: 488 BTextControl::MessageReceived(msg); 489 } 490 } 491 492 493 //==================================================================== 494 // #pragma mark - 495 496 497 TSigTextView::TSigTextView(BRect frame, BRect text) 498 :BTextView(frame, "SignatureView", text, B_FOLLOW_ALL, B_NAVIGABLE | B_WILL_DRAW) 499 { 500 fDirty = false; 501 SetDoesUndo(true); 502 } 503 504 505 void 506 TSigTextView::FrameResized(float /*width*/, float /*height*/) 507 { 508 BRect r(Bounds()); 509 r.InsetBy(3, 3); 510 SetTextRect(r); 511 } 512 513 514 void 515 TSigTextView::DeleteText(int32 offset, int32 len) 516 { 517 fDirty = true; 518 BTextView::DeleteText(offset, len); 519 } 520 521 522 void 523 TSigTextView::InsertText(const char *text, int32 len, int32 offset, 524 const text_run_array *runs) 525 { 526 fDirty = true; 527 BTextView::InsertText(text, len, offset, runs); 528 } 529 530 531 void 532 TSigTextView::KeyDown(const char *key, int32 count) 533 { 534 bool up = false; 535 int32 height; 536 BRect r; 537 538 switch (key[0]) { 539 case B_HOME: 540 Select(0, 0); 541 ScrollToSelection(); 542 break; 543 544 case B_END: 545 Select(TextLength(), TextLength()); 546 ScrollToSelection(); 547 break; 548 549 case B_PAGE_UP: 550 up = true; 551 case B_PAGE_DOWN: 552 r = Bounds(); 553 height = (int32)((up ? r.top - r.bottom : r.bottom - r.top) - 25); 554 if ((up) && (!r.top)) 555 break; 556 ScrollBy(0, height); 557 break; 558 559 default: 560 BTextView::KeyDown(key, count); 561 } 562 } 563 564 565 void 566 TSigTextView::MessageReceived(BMessage *msg) 567 { 568 char type[B_FILE_NAME_LENGTH]; 569 char *text; 570 int32 end; 571 int32 start; 572 BFile file; 573 BNodeInfo *node; 574 entry_ref ref; 575 off_t size; 576 577 switch (msg->what) { 578 case B_SIMPLE_DATA: 579 if (msg->HasRef("refs")) { 580 msg->FindRef("refs", &ref); 581 file.SetTo(&ref, O_RDONLY); 582 if (file.InitCheck() == B_NO_ERROR) { 583 node = new BNodeInfo(&file); 584 node->GetType(type); 585 delete node; 586 file.GetSize(&size); 587 if ((!strncasecmp(type, "text/", 5)) && (size)) { 588 text = (char *)malloc(size); 589 file.Read(text, size); 590 Delete(); 591 GetSelection(&start, &end); 592 Insert(text, size); 593 Select(start, start + size); 594 free(text); 595 } 596 } 597 } 598 else 599 BTextView::MessageReceived(msg); 600 break; 601 602 case M_SELECT: 603 if (IsSelectable()) 604 Select(0, TextLength()); 605 break; 606 607 default: 608 BTextView::MessageReceived(msg); 609 } 610 } 611 612