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 #include "Signature.h" 36 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 41 #include <Clipboard.h> 42 #include <InterfaceKit.h> 43 #include <StorageKit.h> 44 45 #include "MailApp.h" 46 #include "MailPopUpMenu.h" 47 #include "MailSupport.h" 48 #include "MailWindow.h" 49 #include "Messages.h" 50 51 #include <MDRLanguage.h> 52 53 54 extern BRect signature_window; 55 extern const char *kUndoStrings[]; 56 extern const char *kRedoStrings[]; 57 58 const char kNameText[] = MDR_DIALECT_CHOICE ("Title:", "署名の名称:"); 59 const char kSigText[] = MDR_DIALECT_CHOICE ("Signature:", "署名:"); 60 61 62 //==================================================================== 63 64 TSignatureWindow::TSignatureWindow(BRect rect) 65 : BWindow (rect, MDR_DIALECT_CHOICE ("Signatures","署名の編集"), B_TITLED_WINDOW, 0), 66 fFile(NULL) 67 { 68 BMenu *menu; 69 BMenuBar *menu_bar; 70 BMenuItem *item; 71 72 BRect r = Bounds(); 73 /*** Set up the menus ****/ 74 menu_bar = new BMenuBar(r, "MenuBar"); 75 menu = new BMenu(MDR_DIALECT_CHOICE ("Signature","S) 署名")); 76 menu->AddItem(fNew = new BMenuItem(MDR_DIALECT_CHOICE ("New","N) 新規"), new BMessage(M_NEW), 'N')); 77 fSignature = new TMenu(MDR_DIALECT_CHOICE ("Open","O) 開く"), INDEX_SIGNATURE, M_SIGNATURE); 78 menu->AddItem(new BMenuItem(fSignature)); 79 menu->AddSeparatorItem(); 80 menu->AddItem(fSave = new BMenuItem(MDR_DIALECT_CHOICE ("Save","S) 保存"), new BMessage(M_SAVE), 'S')); 81 menu->AddItem(fDelete = new BMenuItem(MDR_DIALECT_CHOICE ("Delete","T) 削除"), new BMessage(M_DELETE), 'T')); 82 menu_bar->AddItem(menu); 83 84 menu = new BMenu(MDR_DIALECT_CHOICE ("Edit","E) 編集")); 85 menu->AddItem(fUndo = new BMenuItem(MDR_DIALECT_CHOICE ("Undo","Z) やり直し"), new BMessage(B_UNDO), 'Z')); 86 fUndo->SetTarget(NULL, this); 87 menu->AddSeparatorItem(); 88 menu->AddItem(fCut = new BMenuItem(MDR_DIALECT_CHOICE ("Cut","X) 切り取り"), new BMessage(B_CUT), 'X')); 89 fCut->SetTarget(NULL, this); 90 menu->AddItem(fCopy = new BMenuItem(MDR_DIALECT_CHOICE ("Copy","C) コピー"), new BMessage(B_COPY), 'C')); 91 fCopy->SetTarget(NULL, this); 92 menu->AddItem(fPaste = new BMenuItem(MDR_DIALECT_CHOICE ("Paste","V) 貼り付け"), new BMessage(B_PASTE), 'V')); 93 fPaste->SetTarget(NULL, this); 94 menu->AddSeparatorItem(); 95 menu->AddItem(item = new BMenuItem(MDR_DIALECT_CHOICE ("Select All","A) 全文選択"), new BMessage(M_SELECT), 'A')); 96 item->SetTarget(NULL, this); 97 menu_bar->AddItem(menu); 98 99 AddChild(menu_bar); 100 /**** Done with the menu set up *****/ 101 102 /**** Add on the panel, giving it the width and at least one vertical pixel *****/ 103 fSigView = new TSignatureView(BRect(0, menu_bar->Frame().bottom+1, 104 rect.Width(), menu_bar->Frame().bottom+2)); 105 AddChild(fSigView); 106 107 /* resize the window to the correct height */ 108 fSigView->SetResizingMode(B_FOLLOW_NONE); 109 ResizeTo(rect.Width()-2, fSigView->Frame().bottom-2); 110 fSigView->SetResizingMode(B_FOLLOW_ALL); 111 112 SetSizeLimits(kSigWidth, RIGHT_BOUNDARY, r.top + 100, RIGHT_BOUNDARY); 113 } 114 115 116 TSignatureWindow::~TSignatureWindow() 117 { 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 msg.AddRect("window frame", Frame()); 249 250 be_app->PostMessage(&msg); 251 return true; 252 } 253 return false; 254 } 255 256 257 void 258 TSignatureWindow::FrameResized(float width, float height) 259 { 260 fSigView->FrameResized(width, height); 261 } 262 263 264 void 265 TSignatureWindow::Show() 266 { 267 BTextView *text_view; 268 269 Lock(); 270 text_view = (BTextView *)fSigView->fName->TextView(); 271 fSigView->fName->MakeFocus(true); 272 text_view->Select(0, text_view->TextLength()); 273 Unlock(); 274 275 BWindow::Show(); 276 } 277 278 279 bool 280 TSignatureWindow::Clear() 281 { 282 int32 result; 283 284 if (IsDirty()) { 285 beep(); 286 BAlert *alert = new BAlert("", 287 MDR_DIALECT_CHOICE ("Save changes to this signature?","変更した署名を保存しますか?"), 288 MDR_DIALECT_CHOICE ("Don't Save","保存しない"), 289 MDR_DIALECT_CHOICE ("Cancel","中止"), 290 MDR_DIALECT_CHOICE ("Save","保存する"), 291 B_WIDTH_AS_USUAL, B_WARNING_ALERT); 292 alert->SetShortcut(0,'d'); 293 alert->SetShortcut(1,B_ESCAPE); 294 result = alert->Go(); 295 if (result == 1) 296 return false; 297 if (result == 2) 298 Save(); 299 } 300 301 delete fFile; 302 fFile = NULL; 303 fSigView->fTextView->fDirty = false; 304 return true; 305 } 306 307 308 bool 309 TSignatureWindow::IsDirty() 310 { 311 char name[B_FILE_NAME_LENGTH]; 312 313 if (fFile) { 314 fFile->ReadAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, name, sizeof(name)); 315 if ((strcmp(name, fSigView->fName->Text())) || (fSigView->fTextView->fDirty)) 316 return true; 317 } 318 else { 319 if ((strlen(fSigView->fName->Text())) || 320 (fSigView->fTextView->TextLength())) 321 return true; 322 } 323 return false; 324 } 325 326 327 void 328 TSignatureWindow::Save() 329 { 330 char name[B_FILE_NAME_LENGTH]; 331 int32 index = 0; 332 status_t result; 333 BDirectory dir; 334 BEntry entry; 335 BNodeInfo *node; 336 BPath path; 337 338 if (!fFile) { 339 find_directory(B_USER_SETTINGS_DIRECTORY, &path, true); 340 dir.SetTo(path.Path()); 341 342 if (dir.FindEntry("Mail", &entry) == B_NO_ERROR) 343 dir.SetTo(&entry); 344 else 345 dir.CreateDirectory("Mail", &dir); 346 347 if (dir.InitCheck() != B_NO_ERROR) 348 goto err_exit; 349 350 if (dir.FindEntry("signatures", &entry) == B_NO_ERROR) 351 dir.SetTo(&entry); 352 else 353 dir.CreateDirectory("signatures", &dir); 354 355 if (dir.InitCheck() != B_NO_ERROR) 356 goto err_exit; 357 358 fFile = new BFile(); 359 while(true) { 360 sprintf(name, "signature_%ld", index++); 361 if ((result = dir.CreateFile(name, fFile, true)) == B_NO_ERROR) 362 break; 363 if (result != EEXIST) 364 goto err_exit; 365 } 366 dir.FindEntry(name, &fEntry); 367 node = new BNodeInfo(fFile); 368 node->SetType("text/plain"); 369 delete node; 370 } 371 372 fSigView->fTextView->fDirty = false; 373 fFile->Seek(0, 0); 374 fFile->Write(fSigView->fTextView->Text(), 375 fSigView->fTextView->TextLength()); 376 fFile->SetSize(fFile->Position()); 377 fFile->WriteAttr(INDEX_SIGNATURE, B_STRING_TYPE, 0, fSigView->fName->Text(), 378 strlen(fSigView->fName->Text()) + 1); 379 return; 380 381 err_exit: 382 beep(); 383 (new BAlert("", MDR_DIALECT_CHOICE ( 384 "An error occurred trying to save this signature.", 385 "署名を保存しようとした時にエラーが発生しました。"), 386 MDR_DIALECT_CHOICE ("Sorry","了解")))->Go(); 387 } 388 389 390 //==================================================================== 391 // #pragma mark - 392 393 394 TSignatureView::TSignatureView(BRect rect) 395 : BBox(rect, "SigView", B_FOLLOW_ALL, B_WILL_DRAW) 396 { 397 } 398 399 400 void 401 TSignatureView::AttachedToWindow() 402 { 403 BRect rect = Bounds(); 404 float name_text_length = StringWidth(kNameText); 405 float sig_text_length = StringWidth(kSigText); 406 float divide_length; 407 408 if (name_text_length > sig_text_length) 409 divide_length = name_text_length; 410 else 411 divide_length = sig_text_length; 412 413 rect.InsetBy(8,0); 414 rect.top+= 8; 415 416 fName = new TNameControl(rect, kNameText, new BMessage(NAME_FIELD)); 417 AddChild(fName); 418 419 fName->SetDivider(divide_length + 10); 420 fName->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); 421 422 rect.OffsetBy(0,fName->Bounds().Height()+5); 423 rect.bottom = rect.top + kSigHeight; 424 rect.left = fName->TextView()->Frame().left; 425 426 BRect text = rect; 427 text.OffsetTo(10,0); 428 fTextView = new TSigTextView(rect, text); 429 BScrollView *scroller = new BScrollView("SigScroller", fTextView, B_FOLLOW_ALL, 0, false, true); 430 AddChild(scroller); 431 scroller->ResizeBy(-1 * scroller->ScrollBar(B_VERTICAL)->Frame().Width() - 9, 0); 432 scroller->MoveBy(7,0); 433 434 /* back up a bit to make room for the label */ 435 436 rect = scroller->Frame(); 437 BStringView *stringView = new BStringView(rect, "SigLabel", kSigText); 438 AddChild(stringView); 439 440 float tWidth, tHeight; 441 stringView->GetPreferredSize(&tWidth, &tHeight); 442 443 /* the 5 is for the spacer in the TextView */ 444 445 rect.OffsetBy(-1 *(tWidth) - 5, 0); 446 rect.right = rect.left + tWidth; 447 rect.bottom = rect.top + tHeight; 448 449 stringView->MoveTo(rect.LeftTop()); 450 stringView->ResizeTo(rect.Width(), rect.Height()); 451 452 /* Resize the View to the correct height */ 453 scroller->SetResizingMode(B_FOLLOW_NONE); 454 ResizeTo(Frame().Width(), scroller->Frame().bottom + 8); 455 scroller->SetResizingMode(B_FOLLOW_ALL); 456 } 457 458 459 //==================================================================== 460 // #pragma mark - 461 462 463 TNameControl::TNameControl(BRect rect, const char *label, BMessage *msg) 464 :BTextControl(rect, "", label, "", msg, B_FOLLOW_LEFT_RIGHT) 465 { 466 strcpy(fLabel, label); 467 } 468 469 470 void 471 TNameControl::AttachedToWindow() 472 { 473 BTextControl::AttachedToWindow(); 474 475 SetDivider(StringWidth(fLabel) + 6); 476 TextView()->SetMaxBytes(B_FILE_NAME_LENGTH - 1); 477 } 478 479 480 void 481 TNameControl::MessageReceived(BMessage *msg) 482 { 483 switch (msg->what) { 484 case M_SELECT: 485 TextView()->Select(0, TextView()->TextLength()); 486 break; 487 488 default: 489 BTextControl::MessageReceived(msg); 490 } 491 } 492 493 494 //==================================================================== 495 // #pragma mark - 496 497 498 TSigTextView::TSigTextView(BRect frame, BRect text) 499 :BTextView(frame, "SignatureView", text, B_FOLLOW_ALL, B_NAVIGABLE | B_WILL_DRAW) 500 { 501 fDirty = false; 502 SetDoesUndo(true); 503 } 504 505 506 void 507 TSigTextView::FrameResized(float /*width*/, float /*height*/) 508 { 509 BRect r(Bounds()); 510 r.InsetBy(3, 3); 511 SetTextRect(r); 512 } 513 514 515 void 516 TSigTextView::DeleteText(int32 offset, int32 len) 517 { 518 fDirty = true; 519 BTextView::DeleteText(offset, len); 520 } 521 522 523 void 524 TSigTextView::InsertText(const char *text, int32 len, int32 offset, 525 const text_run_array *runs) 526 { 527 fDirty = true; 528 BTextView::InsertText(text, len, offset, runs); 529 } 530 531 532 void 533 TSigTextView::KeyDown(const char *key, int32 count) 534 { 535 bool up = false; 536 int32 height; 537 BRect r; 538 539 switch (key[0]) { 540 case B_HOME: 541 Select(0, 0); 542 ScrollToSelection(); 543 break; 544 545 case B_END: 546 Select(TextLength(), TextLength()); 547 ScrollToSelection(); 548 break; 549 550 case B_PAGE_UP: 551 up = true; 552 case B_PAGE_DOWN: 553 r = Bounds(); 554 height = (int32)((up ? r.top - r.bottom : r.bottom - r.top) - 25); 555 if ((up) && (!r.top)) 556 break; 557 ScrollBy(0, height); 558 break; 559 560 default: 561 BTextView::KeyDown(key, count); 562 } 563 } 564 565 566 void 567 TSigTextView::MessageReceived(BMessage *msg) 568 { 569 char type[B_FILE_NAME_LENGTH]; 570 char *text; 571 int32 end; 572 int32 start; 573 BFile file; 574 BNodeInfo *node; 575 entry_ref ref; 576 off_t size; 577 578 switch (msg->what) { 579 case B_SIMPLE_DATA: 580 if (msg->HasRef("refs")) { 581 msg->FindRef("refs", &ref); 582 file.SetTo(&ref, O_RDONLY); 583 if (file.InitCheck() == B_NO_ERROR) { 584 node = new BNodeInfo(&file); 585 node->GetType(type); 586 delete node; 587 file.GetSize(&size); 588 if ((!strncasecmp(type, "text/", 5)) && (size)) { 589 text = (char *)malloc(size); 590 file.Read(text, size); 591 Delete(); 592 GetSelection(&start, &end); 593 Insert(text, size); 594 Select(start, start + size); 595 free(text); 596 } 597 } 598 } 599 else 600 BTextView::MessageReceived(msg); 601 break; 602 603 case M_SELECT: 604 if (IsSelectable()) 605 Select(0, TextLength()); 606 break; 607 608 default: 609 BTextView::MessageReceived(msg); 610 } 611 } 612 613