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 #include "Signature.h" 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 40 #include <Clipboard.h> 41 #include <InterfaceKit.h> 42 #include <StorageKit.h> 43 44 #include "MailApp.h" 45 #include "MailPopUpMenu.h" 46 #include "MailSupport.h" 47 #include "MailWindow.h" 48 #include "Messages.h" 49 50 #include <MDRLanguage.h> 51 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 } 118 119 120 void 121 TSignatureWindow::MenusBeginning() 122 { 123 int32 finish = 0; 124 int32 start = 0; 125 BTextView *text_view; 126 127 fDelete->SetEnabled(fFile); 128 fSave->SetEnabled(IsDirty()); 129 fUndo->SetEnabled(false); // ***TODO*** 130 131 text_view = (BTextView *)fSigView->fName->ChildAt(0); 132 if (text_view->IsFocus()) 133 text_view->GetSelection(&start, &finish); 134 else 135 fSigView->fTextView->GetSelection(&start, &finish); 136 137 fCut->SetEnabled(start != finish); 138 fCopy->SetEnabled(start != finish); 139 140 fNew->SetEnabled(text_view->TextLength() | fSigView->fTextView->TextLength()); 141 be_clipboard->Lock(); 142 fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE)); 143 be_clipboard->Unlock(); 144 145 // Undo stuff 146 bool isRedo = false; 147 undo_state undoState = B_UNDO_UNAVAILABLE; 148 149 BTextView *focusTextView = dynamic_cast<BTextView *>(CurrentFocus()); 150 if (focusTextView != NULL) 151 undoState = focusTextView->UndoState(&isRedo); 152 153 fUndo->SetLabel((isRedo) ? kRedoStrings[undoState] : kUndoStrings[undoState]); 154 fUndo->SetEnabled(undoState != B_UNDO_UNAVAILABLE); 155 } 156 157 158 void 159 TSignatureWindow::MessageReceived(BMessage* msg) 160 { 161 char *sig; 162 char name[B_FILE_NAME_LENGTH]; 163 BFont *font; 164 BTextView *text_view; 165 entry_ref ref; 166 off_t size; 167 168 switch(msg->what) { 169 case CHANGE_FONT: 170 msg->FindPointer("font", (void **)&font); 171 fSigView->fTextView->SetFontAndColor(font); 172 fSigView->fTextView->Invalidate(fSigView->fTextView->Bounds()); 173 break; 174 175 case M_NEW: 176 if (Clear()) { 177 fSigView->fName->SetText(""); 178 // fSigView->fTextView->SetText(NULL, (int32)0); 179 fSigView->fTextView->SetText(""); 180 fSigView->fName->MakeFocus(true); 181 } 182 break; 183 184 case M_SAVE: 185 Save(); 186 break; 187 188 case M_DELETE: 189 if (!(new BAlert("",MDR_DIALECT_CHOICE ( 190 "Really delete this signature? This cannot be undone.", 191 "この署名を削除しますか?"), 192 MDR_DIALECT_CHOICE ("Cancel","取消l"), 193 MDR_DIALECT_CHOICE ("Delete","削除"), NULL, B_WIDTH_AS_USUAL, 194 B_WARNING_ALERT))->Go()) 195 break; 196 197 if (fFile) { 198 delete fFile; 199 fFile = NULL; 200 fEntry.Remove(); 201 fSigView->fName->SetText(""); 202 fSigView->fTextView->SetText(NULL, (int32)0); 203 fSigView->fName->MakeFocus(true); 204 } 205 break; 206 207 case M_SIGNATURE: 208 if (Clear()) { 209 msg->FindRef("ref", &ref); 210 fEntry.SetTo(&ref); 211 fFile = new BFile(&ref, O_RDWR); 212 if (fFile->InitCheck() == B_NO_ERROR) { 213 fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name)); 214 fSigView->fName->SetText(name); 215 fFile->GetSize(&size); 216 sig = (char *)malloc(size); 217 size = fFile->Read(sig, size); 218 fSigView->fTextView->SetText(sig, size); 219 fSigView->fName->MakeFocus(true); 220 text_view = (BTextView *)fSigView->fName->ChildAt(0); 221 text_view->Select(0, text_view->TextLength()); 222 fSigView->fTextView->fDirty = false; 223 } 224 else { 225 fFile = NULL; 226 beep(); 227 (new BAlert("", MDR_DIALECT_CHOICE ( 228 "Couldn't open this signature. Sorry.", 229 "署名を開く時にエラーが発生しました。"), 230 MDR_DIALECT_CHOICE ("OK","了解")))->Go(); 231 } 232 } 233 break; 234 235 default: 236 BWindow::MessageReceived(msg); 237 } 238 } 239 240 241 bool 242 TSignatureWindow::QuitRequested() 243 { 244 if (Clear()) { 245 BMessage msg(WINDOW_CLOSED); 246 msg.AddInt32("kind", SIG_WINDOW); 247 msg.AddRect("window frame", Frame()); 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