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 #include "MailWindow.h" 37 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <fcntl.h> 42 #include <unistd.h> 43 #include <sys/stat.h> 44 #include <sys/utsname.h> 45 46 #include <Autolock.h> 47 #include <Clipboard.h> 48 #include <Debug.h> 49 #include <E-mail.h> 50 #include <InterfaceKit.h> 51 #include <Roster.h> 52 #include <Screen.h> 53 #include <StorageKit.h> 54 #include <String.h> 55 #include <TextView.h> 56 #include <UTF8.h> 57 58 #include <fs_index.h> 59 #include <fs_info.h> 60 61 #include <MailMessage.h> 62 #include <MailSettings.h> 63 #include <MailDaemon.h> 64 #include <mail_util.h> 65 #include <MDRLanguage.h> 66 67 #include <CharacterSetRoster.h> 68 69 using namespace BPrivate ; 70 71 #ifdef HAIKU_TARGET_PLATFORM_BEOS 72 #include <netdb.h> 73 #endif 74 75 #include "ButtonBar.h" 76 #include "Content.h" 77 #include "Enclosures.h" 78 #include "FieldMsg.h" 79 #include "FindWindow.h" 80 #include "Header.h" 81 #include "Messages.h" 82 #include "MailApp.h" 83 #include "MailPopUpMenu.h" 84 #include "MailSupport.h" 85 #include "Prefs.h" 86 #include "QueryMenu.h" 87 #include "Signature.h" 88 #include "Status.h" 89 #include "String.h" 90 #include "Utilities.h" 91 #include "Words.h" 92 93 94 const char *kUndoStrings[] = { 95 MDR_DIALECT_CHOICE ("Undo","Z) 取り消し"), 96 MDR_DIALECT_CHOICE ("Undo Typing","Z) 取り消し(入力)"), 97 MDR_DIALECT_CHOICE ("Undo Cut","Z) 取り消し(切り取り)"), 98 MDR_DIALECT_CHOICE ("Undo Paste","Z) 取り消し(貼り付け)"), 99 MDR_DIALECT_CHOICE ("Undo Clear","Z) 取り消し(消去)"), 100 MDR_DIALECT_CHOICE ("Undo Drop","Z) 取り消し(ドロップ)") 101 }; 102 103 const char *kRedoStrings[] = { 104 MDR_DIALECT_CHOICE ("Redo", "Z) やり直し"), 105 MDR_DIALECT_CHOICE ("Redo Typing", "Z) やり直し(入力)"), 106 MDR_DIALECT_CHOICE ("Redo Cut", "Z) やり直し(切り取り)"), 107 MDR_DIALECT_CHOICE ("Redo Paste", "Z) やり直し(貼り付け)"), 108 MDR_DIALECT_CHOICE ("Redo Clear", "Z) やり直し(消去)"), 109 MDR_DIALECT_CHOICE ("Redo Drop", "Z) やり直し(ドロップ)") 110 }; 111 112 113 // Text for both the main menu and the pop-up menu. 114 static const char *kSpamMenuItemTextArray[] = { 115 "Mark as Spam and Move to Trash", // M_TRAIN_SPAM_AND_DELETE 116 "Mark as Spam", // M_TRAIN_SPAM 117 "Unmark this Message", // M_UNTRAIN 118 "Mark as Genuine" // M_TRAIN_GENUINE 119 }; 120 121 122 // static list for tracking of Windows 123 BList TMailWindow::sWindowList; 124 BLocker TMailWindow::sWindowListLock; 125 126 127 // #pragma mark - 128 129 130 TMailWindow::TMailWindow(BRect rect, const char* title, TMailApp* app, 131 const entry_ref* ref, const char* to, const BFont* font, bool resending, 132 BMessenger* messenger) 133 : BWindow(rect, title, B_DOCUMENT_WINDOW, 0), 134 fApp(app), 135 fFieldState(0), 136 fPanel(NULL), 137 fSendButton(NULL), 138 fSaveButton(NULL), 139 fPrintButton(NULL), 140 fSigButton(NULL), 141 fZoom(rect), 142 fEnclosuresView(NULL), 143 fPrevTrackerPositionSaved(false), 144 fNextTrackerPositionSaved(false), 145 fSigAdded(false), 146 fReplying(false), 147 fResending(resending), 148 fSent(false), 149 fDraft(false), 150 fChanged(false), 151 fStartingText(NULL), 152 fOriginatingWindow(NULL) 153 { 154 if (messenger != NULL) 155 fTrackerMessenger = *messenger; 156 157 char str[256]; 158 char status[272]; 159 uint32 message; 160 float height; 161 BMenu *menu; 162 BMenu *subMenu; 163 BMenuBar *menu_bar; 164 BMenuItem *item; 165 BMessage *msg; 166 attr_info info; 167 BFile file(ref, B_READ_ONLY); 168 169 if (ref) { 170 fRef = new entry_ref(*ref); 171 fMail = new BEmailMessage(fRef); 172 fIncoming = true; 173 } else { 174 fRef = NULL; 175 fMail = NULL; 176 fIncoming = false; 177 } 178 179 BRect r(0, 0, RIGHT_BOUNDARY, 15); 180 181 // Create real menu bar 182 fMenuBar = menu_bar = new BMenuBar(r, ""); 183 184 // 185 // File Menu 186 // 187 menu = new BMenu(MDR_DIALECT_CHOICE ("File","F) ファイル")); 188 189 msg = new BMessage(M_NEW); 190 msg->AddInt32("type", M_NEW); 191 menu->AddItem(item = new BMenuItem(MDR_DIALECT_CHOICE ( 192 "New Mail Message", "N) 新規メッセージ作成"), msg, 'N')); 193 item->SetTarget(be_app); 194 195 // Cheap hack - only show the drafts menu when composing messages. Insert 196 // a "true || " in the following IF statement if you want the old BeMail 197 // behaviour. The difference is that without live draft menu updating you 198 // can open around 100 e-mails (the BeOS maximum number of open files) 199 // rather than merely around 20, since each open draft-monitoring query 200 // sucks up one file handle per mounted BFS disk volume. Plus mail file 201 // opening speed is noticably improved! ToDo: change this to populate the 202 // Draft menu with the file names on demand - when the user clicks on it; 203 // don't need a live query since the menu isn't staying up for more than a 204 // few seconds. 205 206 if (!fIncoming) { 207 QueryMenu *queryMenu; 208 queryMenu = new QueryMenu(MDR_DIALECT_CHOICE ("Open Draft", "O) ドラフトを開く"), false); 209 queryMenu->SetTargetForItems(be_app); 210 211 queryMenu->SetPredicate("MAIL:draft==1"); 212 menu->AddItem(queryMenu); 213 } 214 215 if(!fIncoming || resending) { 216 menu->AddItem(fSendLater = new BMenuItem( 217 MDR_DIALECT_CHOICE ("Save as Draft", "S)ドラフトとして保存"), 218 new BMessage(M_SAVE_AS_DRAFT), 'S')); 219 } 220 221 if(!resending && fIncoming) { 222 menu->AddSeparatorItem(); 223 224 subMenu = new BMenu(MDR_DIALECT_CHOICE ("Close and ","C) 閉じる")); 225 if (file.GetAttrInfo(B_MAIL_ATTR_STATUS, &info) == B_NO_ERROR) 226 file.ReadAttr(B_MAIL_ATTR_STATUS, B_STRING_TYPE, 0, str, info.size); 227 else 228 str[0] = 0; 229 230 if (!strcmp(str, "New")) { 231 subMenu->AddItem(item = new BMenuItem( 232 MDR_DIALECT_CHOICE ("Leave as New", "N) 新規<New>のままにする"), 233 new BMessage(M_CLOSE_SAME), 'W', B_SHIFT_KEY)); 234 #if 0 235 subMenu->AddItem(item = new BMenuItem( 236 MDR_DIALECT_CHOICE ("Set to Read", "R) 開封済<Read>に設定"), 237 new BMessage(M_CLOSE_READ), 'W')); 238 #endif 239 message = M_CLOSE_READ; 240 } else { 241 if (strlen(str)) 242 sprintf(status, MDR_DIALECT_CHOICE ("Leave as '%s'","W) 属性を<%s>にする"), str); 243 else 244 sprintf(status, MDR_DIALECT_CHOICE ("Leave same","W) 属性はそのまま")); 245 subMenu->AddItem(item = new BMenuItem(status, 246 new BMessage(M_CLOSE_SAME), 'W')); 247 message = M_CLOSE_SAME; 248 AddShortcut('W', B_COMMAND_KEY | B_SHIFT_KEY, new BMessage(M_CLOSE_SAME)); 249 } 250 251 subMenu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE("Move to Trash", 252 "T) 削除"), new BMessage(M_DELETE), 'T', B_CONTROL_KEY)); 253 AddShortcut('T', B_SHIFT_KEY | B_COMMAND_KEY, new BMessage(M_DELETE_NEXT)); 254 255 subMenu->AddSeparatorItem(); 256 257 subMenu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE("Set to Saved", 258 "S) 属性を<Saved>に設定"), new BMessage(M_CLOSE_SAVED), 'W', B_CONTROL_KEY)); 259 260 if (add_query_menu_items(subMenu, INDEX_STATUS, M_STATUS, 261 MDR_DIALECT_CHOICE("Set to %s", "属性を<%s>に設定")) > 0) 262 subMenu->AddSeparatorItem(); 263 264 subMenu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE("Set to", "X) 他の属性に変更") 265 B_UTF8_ELLIPSIS, new BMessage(M_CLOSE_CUSTOM))); 266 267 #if 0 268 subMenu->AddItem(new BMenuItem(new TMenu( 269 MDR_DIALECT_CHOICE ("Set to", "X) 他の属性に変更")B_UTF8_ELLIPSIS, 270 INDEX_STATUS, M_STATUS, false, false), new BMessage(M_CLOSE_CUSTOM))); 271 #endif 272 menu->AddItem(subMenu); 273 } 274 else 275 { 276 menu->AddSeparatorItem(); 277 menu->AddItem(new BMenuItem( 278 MDR_DIALECT_CHOICE ("Close", "W) 閉じる"), 279 new BMessage(B_CLOSE_REQUESTED), 'W')); 280 } 281 282 menu->AddSeparatorItem(); 283 menu->AddItem(fPrint = new BMenuItem( 284 MDR_DIALECT_CHOICE ("Page Setup", "G) ページ設定") B_UTF8_ELLIPSIS, 285 new BMessage(M_PRINT_SETUP))); 286 menu->AddItem(fPrint = new BMenuItem( 287 MDR_DIALECT_CHOICE ("Print", "P) 印刷") B_UTF8_ELLIPSIS, 288 new BMessage(M_PRINT), 'P')); 289 menu_bar->AddItem(menu); 290 291 menu->AddSeparatorItem(); 292 menu->AddItem(item = new BMenuItem( 293 MDR_DIALECT_CHOICE ("About Mail", "A) Mailについて") B_UTF8_ELLIPSIS, 294 new BMessage(B_ABOUT_REQUESTED))); 295 item->SetTarget(be_app); 296 297 menu->AddSeparatorItem(); 298 menu->AddItem(item = new BMenuItem( 299 MDR_DIALECT_CHOICE ("Quit", "Q) 終了"), 300 new BMessage(B_QUIT_REQUESTED), 'Q')); 301 item->SetTarget(be_app); 302 303 // 304 // Edit Menu 305 // 306 menu = new BMenu(MDR_DIALECT_CHOICE ("Edit","E) 編集")); 307 menu->AddItem(fUndo = new BMenuItem(MDR_DIALECT_CHOICE ("Undo","Z) 元に戻す"), new BMessage(B_UNDO), 'Z', 0)); 308 fUndo->SetTarget(NULL, this); 309 menu->AddItem(fRedo = new BMenuItem(MDR_DIALECT_CHOICE ("Redo","Z) やり直し"), new BMessage(M_REDO), 'Z', B_SHIFT_KEY)); 310 fRedo->SetTarget(NULL, this); 311 menu->AddSeparatorItem(); 312 menu->AddItem(fCut = new BMenuItem(MDR_DIALECT_CHOICE ("Cut","X) 切り取り"), new BMessage(B_CUT), 'X')); 313 fCut->SetTarget(NULL, this); 314 menu->AddItem(fCopy = new BMenuItem(MDR_DIALECT_CHOICE ("Copy","C) コピー"), new BMessage(B_COPY), 'C')); 315 fCopy->SetTarget(NULL, this); 316 menu->AddItem(fPaste = new BMenuItem(MDR_DIALECT_CHOICE ("Paste","V) 貼り付け"), new BMessage(B_PASTE), 'V')); 317 fPaste->SetTarget(NULL, this); 318 menu->AddSeparatorItem(); 319 menu->AddItem(item = new BMenuItem(MDR_DIALECT_CHOICE ("Select All", "A) 全文選択"), new BMessage(M_SELECT), 'A')); 320 menu->AddSeparatorItem(); 321 item->SetTarget(NULL, this); 322 menu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Find", "F) 検索") B_UTF8_ELLIPSIS, new BMessage(M_FIND), 'F')); 323 menu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Find Again", "G) 次を検索"), new BMessage(M_FIND_AGAIN), 'G')); 324 if (!fIncoming) { 325 menu->AddSeparatorItem(); 326 menu->AddItem(fQuote =new BMenuItem( 327 MDR_DIALECT_CHOICE ("Quote","Q) 引用符をつける"), 328 new BMessage(M_QUOTE), B_RIGHT_ARROW)); 329 menu->AddItem(fRemoveQuote = new BMenuItem( 330 MDR_DIALECT_CHOICE ("Remove Quote","R) 引用符を削除"), 331 new BMessage(M_REMOVE_QUOTE), B_LEFT_ARROW)); 332 menu->AddSeparatorItem(); 333 fSpelling = new BMenuItem( 334 MDR_DIALECT_CHOICE ("Check Spelling","H) スペルチェック"), 335 new BMessage( M_CHECK_SPELLING ), ';' ); 336 menu->AddItem(fSpelling); 337 if (fApp->StartWithSpellCheckOn()) 338 PostMessage (M_CHECK_SPELLING); 339 } 340 menu->AddSeparatorItem(); 341 menu->AddItem(item = new BMenuItem( 342 MDR_DIALECT_CHOICE ("Preferences","P) Mailの設定") B_UTF8_ELLIPSIS, 343 new BMessage(M_PREFS),',')); 344 item->SetTarget(be_app); 345 menu_bar->AddItem(menu); 346 347 // View Menu 348 349 if (!resending && fIncoming) { 350 menu = new BMenu("View"); 351 menu->AddItem(fHeader = new BMenuItem(MDR_DIALECT_CHOICE ("Show Header","H) ヘッダーを表示"), new BMessage(M_HEADER), 'H')); 352 if (fApp->ShowHeader()) 353 fHeader->SetMarked(true); 354 menu->AddItem(fRaw = new BMenuItem(MDR_DIALECT_CHOICE ("Show Raw Message"," メッセージを生で表示"), new BMessage(M_RAW))); 355 menu_bar->AddItem(menu); 356 } 357 358 // 359 // Message Menu 360 // 361 menu = new BMenu(MDR_DIALECT_CHOICE ("Message", "M) メッセージ")); 362 363 if (!resending && fIncoming) { 364 BMenuItem *menuItem; 365 menu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Reply","R) 返信"), new BMessage(M_REPLY),'R')); 366 menu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Reply to Sender","S) 送信者に返信"), new BMessage(M_REPLY_TO_SENDER),'R',B_OPTION_KEY)); 367 menu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Reply to All","P) 全員に返信"), new BMessage(M_REPLY_ALL), 'R', B_SHIFT_KEY)); 368 369 menu->AddSeparatorItem(); 370 371 menu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Forward","J) 転送"), new BMessage(M_FORWARD), 'J')); 372 menu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Forward without Attachments","The opposite: F) 添付ファイルを含めて転送"), new BMessage(M_FORWARD_WITHOUT_ATTACHMENTS))); 373 menu->AddItem(menuItem = new BMenuItem(MDR_DIALECT_CHOICE ("Resend"," 再送信"), new BMessage(M_RESEND))); 374 menu->AddItem(menuItem = new BMenuItem(MDR_DIALECT_CHOICE ("Copy to New","D) 新規メッセージへコピー"), new BMessage(M_COPY_TO_NEW), 'D')); 375 376 menu->AddSeparatorItem(); 377 fDeleteNext = new BMenuItem(MDR_DIALECT_CHOICE ("Move to Trash","T) 削除"), new BMessage(M_DELETE_NEXT), 'T'); 378 menu->AddItem(fDeleteNext); 379 menu->AddSeparatorItem(); 380 381 fPrevMsg = new BMenuItem(MDR_DIALECT_CHOICE ("Previous Message","B) 前のメッセージ"), new BMessage(M_PREVMSG), 382 B_UP_ARROW); 383 menu->AddItem(fPrevMsg); 384 fNextMsg = new BMenuItem(MDR_DIALECT_CHOICE ("Next Message","N) 次のメッセージ"), new BMessage(M_NEXTMSG), 385 B_DOWN_ARROW); 386 menu->AddItem(fNextMsg); 387 menu->AddSeparatorItem(); 388 fSaveAddrMenu = subMenu = new BMenu(MDR_DIALECT_CHOICE ("Save Address", " アドレスを保存")); 389 390 // create the list of addresses 391 392 BList addressList; 393 get_address_list(addressList, fMail->To(), extract_address); 394 get_address_list(addressList, fMail->CC(), extract_address); 395 get_address_list(addressList, fMail->From(), extract_address); 396 get_address_list(addressList, fMail->ReplyTo(), extract_address); 397 398 for (int32 i = addressList.CountItems(); i-- > 0;) { 399 char *address = (char *)addressList.RemoveItem(0L); 400 401 // insert the new address in alphabetical order 402 int32 index = 0; 403 while ((item = subMenu->ItemAt(index)) != NULL) { 404 if (!strcmp(address, item->Label())) { 405 // item already in list 406 goto skip; 407 } 408 409 if (strcmp(address, item->Label()) < 0) 410 break; 411 412 index++; 413 } 414 415 msg = new BMessage(M_SAVE); 416 msg->AddString("address", address); 417 subMenu->AddItem(new BMenuItem(address, msg), index); 418 419 skip: 420 free(address); 421 } 422 423 menu->AddItem(subMenu); 424 menu_bar->AddItem(menu); 425 426 // Spam Menu 427 428 if (fApp->ShowSpamGUI()) { 429 menu = new BMenu("Spam Filtering"); 430 menu->AddItem(new BMenuItem("Mark as Spam and Move to Trash", 431 new BMessage(M_TRAIN_SPAM_AND_DELETE), 'K')); 432 menu->AddItem(new BMenuItem("Mark as Spam", 433 new BMessage(M_TRAIN_SPAM), 'K', B_OPTION_KEY)); 434 menu->AddSeparatorItem(); 435 menu->AddItem(new BMenuItem("Unmark this Message", 436 new BMessage(M_UNTRAIN))); 437 menu->AddSeparatorItem(); 438 menu->AddItem(new BMenuItem("Mark as Genuine", 439 new BMessage(M_TRAIN_GENUINE), 'K', B_SHIFT_KEY)); 440 menu_bar->AddItem(menu); 441 } 442 } else { 443 menu->AddItem(fSendNow = new BMenuItem( 444 MDR_DIALECT_CHOICE ("Send Message", "M) メッセージを送信"), 445 new BMessage(M_SEND_NOW), 'M')); 446 447 if(!fIncoming) { 448 menu->AddSeparatorItem(); 449 fSignature = new TMenu( 450 MDR_DIALECT_CHOICE ("Add Signature", "D) 署名を追加"), 451 INDEX_SIGNATURE, M_SIGNATURE); 452 menu->AddItem(new BMenuItem(fSignature)); 453 menu->AddItem(item = new BMenuItem( 454 MDR_DIALECT_CHOICE ("Edit Signatures","S) 署名の編集") B_UTF8_ELLIPSIS, 455 new BMessage(M_EDIT_SIGNATURE))); 456 item->SetTarget(be_app); 457 menu->AddSeparatorItem(); 458 menu->AddItem(fAdd = new BMenuItem(MDR_DIALECT_CHOICE ("Add Enclosure","E) 追加")B_UTF8_ELLIPSIS, new BMessage(M_ADD), 'E')); 459 menu->AddItem(fRemove = new BMenuItem(MDR_DIALECT_CHOICE ("Remove Enclosure","T) 削除"), new BMessage(M_REMOVE), 'T')); 460 } 461 menu_bar->AddItem(menu); 462 } 463 464 AddChild(menu_bar); 465 height = menu_bar->Bounds().bottom + 1; 466 467 // 468 // Button Bar 469 // 470 float bbwidth = 0, bbheight = 0; 471 472 bool showButtonBar = fApp->ShowButtonBar(); 473 474 if (showButtonBar) { 475 BuildButtonBar(); 476 fButtonBar->ShowLabels(showButtonBar); 477 fButtonBar->Arrange(/* True for all buttons same size, false to just fit */ 478 MDR_DIALECT_CHOICE (true, true)); 479 fButtonBar->GetPreferredSize(&bbwidth, &bbheight); 480 fButtonBar->ResizeTo(Bounds().right+3, bbheight+1); 481 fButtonBar->MoveTo(-1, height-1); 482 fButtonBar->Show(); 483 } else 484 fButtonBar = NULL; 485 486 r.top = r.bottom = height + bbheight + 1; 487 fHeaderView = new THeaderView (r, rect, fIncoming, fMail, resending, 488 (resending || !fIncoming) 489 ? fApp->MailCharacterSet() // Use preferences setting for composing mail. 490 : B_MAIL_NULL_CONVERSION, // Default is automatic selection for reading mail. 491 fApp->DefaultChain()); 492 493 r = Frame(); 494 r.OffsetTo(0, 0); 495 r.top = fHeaderView->Frame().bottom - 1; 496 fContentView = new TContentView(r, fIncoming, fMail, 497 const_cast<BFont *>(font), fApp->ShowHeader(), fApp->ColoredQuotes()); 498 // TContentView needs to be properly const, for now cast away constness 499 500 AddChild(fHeaderView); 501 if (fEnclosuresView) 502 AddChild(fEnclosuresView); 503 AddChild(fContentView); 504 505 if (to) { 506 fHeaderView->fTo->SetText(to); 507 } 508 509 AddShortcut('n', B_COMMAND_KEY, new BMessage(M_NEW)); 510 511 // 512 // If auto-signature, add signature to the text here. 513 // 514 515 BString signature = fApp->Signature(); 516 517 if (!fIncoming && strcmp(signature.String(), SIG_NONE) != 0) { 518 if (strcmp(signature.String(), SIG_RANDOM) == 0) 519 PostMessage(M_RANDOM_SIG); 520 else { 521 // 522 // Create a query to find this signature 523 // 524 BVolume volume; 525 BVolumeRoster().GetBootVolume(&volume); 526 527 BQuery query; 528 query.SetVolume(&volume); 529 query.PushAttr(INDEX_SIGNATURE); 530 query.PushString(signature.String()); 531 query.PushOp(B_EQ); 532 query.Fetch(); 533 534 // 535 // If we find the named query, add it to the text. 536 // 537 BEntry entry; 538 if (query.GetNextEntry(&entry) == B_NO_ERROR) { 539 off_t size; 540 BFile file; 541 file.SetTo(&entry, O_RDWR); 542 if (file.InitCheck() == B_NO_ERROR) { 543 file.GetSize(&size); 544 char *str = (char *)malloc(size); 545 size = file.Read(str, size); 546 547 fContentView->fTextView->Insert(str, size); 548 fContentView->fTextView->GoToLine(0); 549 fContentView->fTextView->ScrollToSelection(); 550 551 fStartingText = (char *)malloc(size = strlen(fContentView->fTextView->Text()) + 1); 552 if (fStartingText != NULL) 553 strcpy(fStartingText, fContentView->fTextView->Text()); 554 } 555 } else { 556 char tempString [2048]; 557 query.GetPredicate (tempString, sizeof (tempString)); 558 printf ("Query failed, was looking for: %s\n", tempString); 559 } 560 } 561 } 562 563 if (fRef) 564 SetTitleForMessage(); 565 566 _UpdateSizeLimits(); 567 } 568 569 570 void 571 TMailWindow::BuildButtonBar() 572 { 573 ButtonBar *bbar; 574 575 bbar = new ButtonBar(BRect(0, 0, 100, 100), "ButtonBar", 2, 3, 0, 1, 10, 2); 576 bbar->AddButton(MDR_DIALECT_CHOICE ("New","新規"), 28, new BMessage(M_NEW)); 577 bbar->AddDivider(5); 578 579 if (fResending) { 580 fSendButton = bbar->AddButton(MDR_DIALECT_CHOICE ("Send","送信"), 8, new BMessage(M_SEND_NOW)); 581 bbar->AddDivider(5); 582 } else if (!fIncoming) { 583 fSendButton = bbar->AddButton(MDR_DIALECT_CHOICE ("Send","送信"), 8, new BMessage(M_SEND_NOW)); 584 fSendButton->SetEnabled(false); 585 fSigButton = bbar->AddButton(MDR_DIALECT_CHOICE ("Signature","署名"), 4, new BMessage(M_SIG_MENU)); 586 fSigButton->InvokeOnButton(B_SECONDARY_MOUSE_BUTTON); 587 fSaveButton = bbar->AddButton(MDR_DIALECT_CHOICE ("Save","保存"), 44, new BMessage(M_SAVE_AS_DRAFT)); 588 fSaveButton->SetEnabled(false); 589 fPrintButton = bbar->AddButton(MDR_DIALECT_CHOICE ("Print","印刷"), 16, new BMessage(M_PRINT)); 590 fPrintButton->SetEnabled(false); 591 bbar->AddButton(MDR_DIALECT_CHOICE ("Trash","削除"), 0, new BMessage(M_DELETE)); 592 bbar->AddDivider(5); 593 } else { 594 BmapButton *button = bbar->AddButton(MDR_DIALECT_CHOICE ("Reply","返信"), 12, new BMessage(M_REPLY)); 595 button->InvokeOnButton(B_SECONDARY_MOUSE_BUTTON); 596 button = bbar->AddButton(MDR_DIALECT_CHOICE ("Forward","転送"), 40, new BMessage(M_FORWARD)); 597 button->InvokeOnButton(B_SECONDARY_MOUSE_BUTTON); 598 fPrintButton = bbar->AddButton(MDR_DIALECT_CHOICE ("Print","印刷"), 16, new BMessage(M_PRINT)); 599 bbar->AddButton(MDR_DIALECT_CHOICE ("Trash","削除"), 0, new BMessage(M_DELETE_NEXT)); 600 if (fApp->ShowSpamGUI()) { 601 button = bbar->AddButton("Spam", 48, new BMessage(M_SPAM_BUTTON)); 602 button->InvokeOnButton(B_SECONDARY_MOUSE_BUTTON); 603 } 604 bbar->AddDivider(5); 605 bbar->AddButton(MDR_DIALECT_CHOICE ("Next","次へ"), 24, new BMessage(M_NEXTMSG)); 606 bbar->AddButton(MDR_DIALECT_CHOICE ("Previous","前へ"), 20, new BMessage(M_PREVMSG)); 607 } 608 bbar->AddButton(MDR_DIALECT_CHOICE ("Inbox","受信箱"), 36, new BMessage(M_OPEN_MAIL_BOX)); 609 bbar->AddButton(MDR_DIALECT_CHOICE ("Mail","メール"), 32, new BMessage(M_OPEN_MAIL_FOLDER)); 610 611 bbar->SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 612 bbar->Hide(); 613 AddChild(bbar); 614 fButtonBar = bbar; 615 } 616 617 618 void 619 TMailWindow::UpdateViews() 620 { 621 float bbwidth = 0, bbheight = 0; 622 float nextY = fMenuBar->Frame().bottom+1; 623 624 bool showButtonBar = fApp->ShowButtonBar(); 625 626 // Show/Hide Button Bar 627 if (showButtonBar) { 628 // Create the Button Bar if needed 629 if (!fButtonBar) 630 BuildButtonBar(); 631 fButtonBar->ShowLabels(showButtonBar); 632 fButtonBar->Arrange(/* True for all buttons same size, false to just fit */ 633 MDR_DIALECT_CHOICE (true, true)); 634 fButtonBar->GetPreferredSize( &bbwidth, &bbheight); 635 fButtonBar->ResizeTo(Bounds().right+3, bbheight+1); 636 fButtonBar->MoveTo(-1, nextY-1); 637 nextY += bbheight + 1; 638 if (fButtonBar->IsHidden()) 639 fButtonBar->Show(); 640 else 641 fButtonBar->Invalidate(); 642 } else if (fButtonBar) 643 fButtonBar->Hide(); 644 645 // Arange other views to match 646 fHeaderView->MoveTo(0, nextY); 647 nextY = fHeaderView->Frame().bottom; 648 if (fEnclosuresView) { 649 fEnclosuresView->MoveTo(0, nextY); 650 nextY = fEnclosuresView->Frame().bottom+1; 651 } 652 BRect bounds(Bounds()); 653 fContentView->MoveTo(0, nextY-1); 654 fContentView->ResizeTo(bounds.right-bounds.left, bounds.bottom-nextY+1); 655 656 _UpdateSizeLimits(); 657 } 658 659 660 TMailWindow::~TMailWindow() 661 { 662 fApp->SetLastWindowFrame(Frame()); 663 664 delete fMail; 665 delete fPanel; 666 delete fOriginatingWindow; 667 668 BAutolock locker(sWindowListLock); 669 sWindowList.RemoveItem(this); 670 } 671 672 673 status_t 674 TMailWindow::GetMailNodeRef(node_ref &nodeRef) const 675 { 676 if (fRef == NULL) 677 return B_ERROR; 678 679 BNode node(fRef); 680 return node.GetNodeRef(&nodeRef); 681 } 682 683 684 bool 685 TMailWindow::GetTrackerWindowFile(entry_ref *ref, bool next) const 686 { 687 // Position was already saved 688 if (next && fNextTrackerPositionSaved) 689 { 690 *ref = fNextRef; 691 return true; 692 } 693 if (!next && fPrevTrackerPositionSaved) 694 { 695 *ref = fPrevRef; 696 return true; 697 } 698 699 if (!fTrackerMessenger.IsValid()) 700 return false; 701 702 // 703 // Ask the tracker what the next/prev file in the window is. 704 // Continue asking for the next reference until a valid 705 // email file is found (ignoring other types). 706 // 707 entry_ref nextRef = *ref; 708 bool foundRef = false; 709 while (!foundRef) 710 { 711 BMessage request(B_GET_PROPERTY); 712 BMessage spc; 713 if (next) 714 spc.what = 'snxt'; 715 else 716 spc.what = 'sprv'; 717 718 spc.AddString("property", "Entry"); 719 spc.AddRef("data", &nextRef); 720 721 request.AddSpecifier(&spc); 722 BMessage reply; 723 if (fTrackerMessenger.SendMessage(&request, &reply) != B_OK) 724 return false; 725 726 if (reply.FindRef("result", &nextRef) != B_OK) 727 return false; 728 729 char fileType[256]; 730 BNode node(&nextRef); 731 if (node.InitCheck() != B_OK) 732 return false; 733 734 if (BNodeInfo(&node).GetType(fileType) != B_OK) 735 return false; 736 737 if (strcasecmp(fileType,"text/x-email") == 0) 738 foundRef = true; 739 } 740 741 *ref = nextRef; 742 return foundRef; 743 } 744 745 746 void 747 TMailWindow::SaveTrackerPosition(entry_ref *ref) 748 { 749 // if only one of them is saved, we're not going to do it again 750 if (fNextTrackerPositionSaved || fPrevTrackerPositionSaved) 751 return; 752 753 fNextRef = fPrevRef = *ref; 754 755 fNextTrackerPositionSaved = GetTrackerWindowFile(&fNextRef, true); 756 fPrevTrackerPositionSaved = GetTrackerWindowFile(&fPrevRef, false); 757 } 758 759 760 void 761 TMailWindow::SetOriginatingWindow(BWindow *window) 762 { 763 delete fOriginatingWindow; 764 fOriginatingWindow = new BMessenger(window); 765 } 766 767 768 void 769 TMailWindow::SetTrackerSelectionToCurrent() 770 { 771 BMessage setSelection(B_SET_PROPERTY); 772 setSelection.AddSpecifier("Selection"); 773 setSelection.AddRef("data", fRef); 774 775 fTrackerMessenger.SendMessage(&setSelection); 776 } 777 778 779 void 780 TMailWindow::SetCurrentMessageRead() 781 { 782 BNode node(fRef); 783 if (node.InitCheck() == B_NO_ERROR) 784 { 785 BString status; 786 if (ReadAttrString(&node, B_MAIL_ATTR_STATUS, &status) == B_NO_ERROR 787 && !status.ICompare("New")) 788 { 789 node.RemoveAttr(B_MAIL_ATTR_STATUS); 790 WriteAttrString(&node, B_MAIL_ATTR_STATUS, "Read"); 791 } 792 } 793 } 794 795 796 void 797 TMailWindow::FrameResized(float width, float height) 798 { 799 fContentView->FrameResized(width, height); 800 } 801 802 803 void 804 TMailWindow::MenusBeginning() 805 { 806 bool enable; 807 int32 finish = 0; 808 int32 start = 0; 809 BTextView *textView; 810 811 if (!fIncoming) { 812 enable = strlen(fHeaderView->fTo->Text()) 813 || strlen(fHeaderView->fBcc->Text()); 814 fSendNow->SetEnabled(enable); 815 fSendLater->SetEnabled(enable); 816 817 be_clipboard->Lock(); 818 fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE) 819 && (fEnclosuresView == NULL || !fEnclosuresView->fList->IsFocus())); 820 be_clipboard->Unlock(); 821 822 fQuote->SetEnabled(false); 823 fRemoveQuote->SetEnabled(false); 824 825 fAdd->SetEnabled(true); 826 fRemove->SetEnabled(fEnclosuresView != NULL 827 && fEnclosuresView->fList->CurrentSelection() >= 0); 828 } else { 829 if (fResending) { 830 enable = strlen(fHeaderView->fTo->Text()); 831 fSendNow->SetEnabled(enable); 832 // fSendLater->SetEnabled(enable); 833 834 if (fHeaderView->fTo->HasFocus()) { 835 textView = fHeaderView->fTo->TextView(); 836 textView->GetSelection(&start, &finish); 837 838 fCut->SetEnabled(start != finish); 839 be_clipboard->Lock(); 840 fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE)); 841 be_clipboard->Unlock(); 842 } else { 843 fCut->SetEnabled(false); 844 fPaste->SetEnabled(false); 845 } 846 } else { 847 fCut->SetEnabled(false); 848 fPaste->SetEnabled(false); 849 850 if (!fTrackerMessenger.IsValid()) { 851 fNextMsg->SetEnabled(false); 852 fPrevMsg->SetEnabled(false); 853 } 854 } 855 } 856 857 fPrint->SetEnabled(fContentView->fTextView->TextLength()); 858 859 textView = dynamic_cast<BTextView *>(CurrentFocus()); 860 if (textView != NULL 861 && dynamic_cast<TTextControl *>(textView->Parent()) != NULL) { 862 // one of To:, Subject:, Account:, Cc:, Bcc: 863 textView->GetSelection(&start, &finish); 864 } else if (fContentView->fTextView->IsFocus()) { 865 fContentView->fTextView->GetSelection(&start, &finish); 866 if (!fIncoming) { 867 fQuote->SetEnabled(true); 868 fRemoveQuote->SetEnabled(true); 869 } 870 } 871 872 fCopy->SetEnabled(start != finish); 873 if (!fIncoming) 874 fCut->SetEnabled(start != finish); 875 876 // Undo stuff 877 bool isRedo = false; 878 undo_state undoState = B_UNDO_UNAVAILABLE; 879 880 BTextView *focusTextView = dynamic_cast<BTextView *>(CurrentFocus()); 881 if (focusTextView != NULL) 882 undoState = focusTextView->UndoState(&isRedo); 883 884 // fUndo->SetLabel((isRedo) ? kRedoStrings[undoState] : kUndoStrings[undoState]); 885 fUndo->SetEnabled(undoState != B_UNDO_UNAVAILABLE); 886 } 887 888 889 void 890 TMailWindow::MessageReceived(BMessage *msg) 891 { 892 switch (msg->what) { 893 case FIELD_CHANGED: 894 { 895 int32 prevState = fFieldState, fieldMask = msg->FindInt32("bitmask"); 896 void *source; 897 898 if (msg->FindPointer("source", &source) == B_OK) { 899 int32 length; 900 901 if (fieldMask == FIELD_BODY) 902 length = ((TTextView *)source)->TextLength(); 903 else 904 length = ((BComboBox *)source)->TextView()->TextLength(); 905 906 if (length) 907 fFieldState |= fieldMask; 908 else 909 fFieldState &= ~fieldMask; 910 } 911 912 // Has anything changed? 913 if (prevState != fFieldState || !fChanged) { 914 // Change Buttons to reflect this 915 if (fSaveButton) 916 fSaveButton->SetEnabled(fFieldState); 917 if (fPrintButton) 918 fPrintButton->SetEnabled(fFieldState); 919 if (fSendButton) 920 fSendButton->SetEnabled((fFieldState & FIELD_TO) || (fFieldState & FIELD_BCC)); 921 } 922 fChanged = true; 923 924 // Update title bar if "subject" has changed 925 if (!fIncoming && fieldMask & FIELD_SUBJECT) { 926 // If no subject, set to "Mail" 927 if (!fHeaderView->fSubject->TextView()->TextLength()) 928 SetTitle("Mail"); 929 else 930 SetTitle(fHeaderView->fSubject->Text()); 931 } 932 break; 933 } 934 case LIST_INVOKED: 935 PostMessage(msg, fEnclosuresView); 936 break; 937 938 case CHANGE_FONT: 939 PostMessage(msg, fContentView); 940 break; 941 942 case M_NEW: 943 { 944 BMessage message(M_NEW); 945 message.AddInt32("type", msg->what); 946 be_app->PostMessage(&message); 947 break; 948 } 949 950 case M_SPAM_BUTTON: 951 { 952 /* 953 A popup from a button is good only when the behavior has some consistency and 954 there is some visual indication that a menu will be shown when clicked. A 955 workable implementation would have an extra button attached to the main one 956 which has a downward-pointing arrow. Mozilla Thunderbird's 'Get Mail' button 957 is a good example of this. 958 959 TODO: Replace this code with a split toolbar button 960 */ 961 uint32 buttons; 962 if (msg->FindInt32("buttons", (int32 *)&buttons) == B_OK 963 && buttons == B_SECONDARY_MOUSE_BUTTON) { 964 BPopUpMenu menu("Spam Actions", false, false); 965 for (int i = 0; i < 4; i++) 966 menu.AddItem(new BMenuItem(kSpamMenuItemTextArray[i], new BMessage(M_TRAIN_SPAM_AND_DELETE + i))); 967 968 BPoint where; 969 msg->FindPoint("where", &where); 970 BMenuItem *item; 971 if ((item = menu.Go(where, false, false)) != NULL) 972 PostMessage(item->Message()); 973 break; 974 } else { 975 // Default action for left clicking on the spam button. 976 PostMessage(new BMessage(M_TRAIN_SPAM_AND_DELETE)); 977 } 978 break; 979 } 980 981 case M_TRAIN_SPAM_AND_DELETE: 982 PostMessage(M_DELETE_NEXT); 983 case M_TRAIN_SPAM: 984 TrainMessageAs("Spam"); 985 break; 986 987 case M_UNTRAIN: 988 TrainMessageAs("Uncertain"); 989 break; 990 991 case M_TRAIN_GENUINE: 992 TrainMessageAs("Genuine"); 993 break; 994 995 case M_REPLY: 996 { 997 // TODO: This needs removed in favor of a split toolbar button. See comments for Spam button 998 uint32 buttons; 999 if (msg->FindInt32("buttons", (int32 *)&buttons) == B_OK 1000 && buttons == B_SECONDARY_MOUSE_BUTTON) { 1001 BPopUpMenu menu("Reply To", false, false); 1002 menu.AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Reply","R) 返信"),new BMessage(M_REPLY))); 1003 menu.AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Reply to Sender","S) 送信者に返信"),new BMessage(M_REPLY_TO_SENDER))); 1004 menu.AddItem(new BMenuItem(MDR_DIALECT_CHOICE ("Reply to All","P) 全員に返信"),new BMessage(M_REPLY_ALL))); 1005 1006 BPoint where; 1007 msg->FindPoint("where", &where); 1008 1009 BMenuItem *item; 1010 if ((item = menu.Go(where, false, false)) != NULL) { 1011 item->SetTarget(this); 1012 PostMessage(item->Message()); 1013 } 1014 break; 1015 } 1016 // Fall through 1017 } 1018 case M_FORWARD: 1019 { 1020 // TODO: This needs removed in favor of a split toolbar button. See comments for Spam button 1021 uint32 buttons; 1022 if (msg->FindInt32("buttons", (int32 *)&buttons) == B_OK 1023 && buttons == B_SECONDARY_MOUSE_BUTTON) { 1024 BPopUpMenu menu("Forward", false, false); 1025 menu.AddItem(new BMenuItem(MDR_DIALECT_CHOICE("Forward", "J) 転送"), 1026 new BMessage(M_FORWARD))); 1027 menu.AddItem(new BMenuItem(MDR_DIALECT_CHOICE("Forward without Attachments", 1028 "The opposite: F) 添付ファイルを含む転送"), 1029 new BMessage(M_FORWARD_WITHOUT_ATTACHMENTS))); 1030 1031 BPoint where; 1032 msg->FindPoint("where", &where); 1033 1034 BMenuItem *item; 1035 if ((item = menu.Go(where, false, false)) != NULL) { 1036 item->SetTarget(this); 1037 PostMessage(item->Message()); 1038 } 1039 break; 1040 } 1041 } 1042 1043 // Fall Through 1044 case M_REPLY_ALL: 1045 case M_REPLY_TO_SENDER: 1046 case M_FORWARD_WITHOUT_ATTACHMENTS: 1047 case M_RESEND: 1048 case M_COPY_TO_NEW: 1049 { 1050 BMessage message(M_NEW); 1051 message.AddRef("ref", fRef); 1052 message.AddPointer("window", this); 1053 message.AddInt32("type", msg->what); 1054 be_app->PostMessage(&message); 1055 break; 1056 } 1057 case M_DELETE: 1058 case M_DELETE_PREV: 1059 case M_DELETE_NEXT: 1060 { 1061 if (msg->what == M_DELETE_NEXT && (modifiers() & B_SHIFT_KEY)) 1062 msg->what = M_DELETE_PREV; 1063 1064 bool foundRef = false; 1065 entry_ref nextRef; 1066 if ((msg->what == M_DELETE_PREV || msg->what == M_DELETE_NEXT) && fRef) { 1067 // Find the next message that should be displayed 1068 nextRef = *fRef; 1069 foundRef = GetTrackerWindowFile(&nextRef, msg->what == 1070 M_DELETE_NEXT); 1071 } 1072 if (fIncoming) 1073 SetCurrentMessageRead(); 1074 1075 if (!fTrackerMessenger.IsValid() || !fIncoming) { 1076 // Not associated with a tracker window. Create a new 1077 // messenger and ask the tracker to delete this entry 1078 if (fDraft || fIncoming) { 1079 BMessenger tracker("application/x-vnd.Be-TRAK"); 1080 if (tracker.IsValid()) { 1081 BMessage msg('Ttrs'); 1082 msg.AddRef("refs", fRef); 1083 tracker.SendMessage(&msg); 1084 } else { 1085 (new BAlert("", 1086 MDR_DIALECT_CHOICE ( "Need tracker to move items to trash", 1087 "削除するにはTrackerが必要です。"), 1088 MDR_DIALECT_CHOICE ("sorry","削除できませんでした。")))->Go(); 1089 } 1090 } 1091 } else { 1092 // This is associated with a tracker window. Ask the 1093 // window to delete this entry. Do it this way if we 1094 // can instead of the above way because it doesn't reset 1095 // the selection (even though we set selection below, this 1096 // still causes problems). 1097 BMessage delmsg(B_DELETE_PROPERTY); 1098 BMessage entryspec('sref'); 1099 entryspec.AddRef("refs", fRef); 1100 entryspec.AddString("property", "Entry"); 1101 delmsg.AddSpecifier(&entryspec); 1102 fTrackerMessenger.SendMessage(&delmsg); 1103 } 1104 1105 // If the next file was found, open it. If it was not, 1106 // we have no choice but to close this window. 1107 if (foundRef) { 1108 TMailWindow *window = static_cast<TMailApp *>(be_app)->FindWindow(nextRef); 1109 if (window == NULL) 1110 OpenMessage(&nextRef, fHeaderView->fCharacterSetUserSees); 1111 else 1112 window->Activate(); 1113 1114 SetTrackerSelectionToCurrent(); 1115 1116 if (window == NULL) 1117 break; 1118 } 1119 1120 fSent = true; 1121 BMessage msg(B_CLOSE_REQUESTED); 1122 PostMessage(&msg); 1123 break; 1124 } 1125 1126 case M_CLOSE_READ: 1127 { 1128 BMessage message(B_CLOSE_REQUESTED); 1129 message.AddString("status", "Read"); 1130 PostMessage(&message); 1131 break; 1132 } 1133 case M_CLOSE_SAVED: 1134 { 1135 BMessage message(B_CLOSE_REQUESTED); 1136 message.AddString("status", "Saved"); 1137 PostMessage(&message); 1138 break; 1139 } 1140 case M_CLOSE_SAME: 1141 { 1142 BMessage message(B_CLOSE_REQUESTED); 1143 message.AddString("status", ""); 1144 message.AddString("same", ""); 1145 PostMessage(&message); 1146 break; 1147 } 1148 case M_CLOSE_CUSTOM: 1149 if (msg->HasString("status")) 1150 { 1151 const char *str; 1152 msg->FindString("status", (const char**) &str); 1153 BMessage message(B_CLOSE_REQUESTED); 1154 message.AddString("status", str); 1155 PostMessage(&message); 1156 } 1157 else 1158 { 1159 BRect r = Frame(); 1160 r.left += ((r.Width() - STATUS_WIDTH) / 2); 1161 r.right = r.left + STATUS_WIDTH; 1162 r.top += 40; 1163 r.bottom = r.top + STATUS_HEIGHT; 1164 1165 BString string = "could not read"; 1166 BNode node(fRef); 1167 if (node.InitCheck() == B_OK) 1168 ReadAttrString(&node, B_MAIL_ATTR_STATUS, &string); 1169 1170 new TStatusWindow(r, this, string.String()); 1171 } 1172 break; 1173 1174 case M_STATUS: 1175 { 1176 const char* attribute; 1177 if (msg->FindString("attribute", &attribute) != B_OK) 1178 break; 1179 1180 BMessage message(B_CLOSE_REQUESTED); 1181 message.AddString("status", attribute); 1182 PostMessage(&message); 1183 break; 1184 } 1185 case M_HEADER: 1186 { 1187 bool showHeader = !fHeader->IsMarked(); 1188 fApp->SetShowHeader(showHeader); 1189 fHeader->SetMarked(showHeader); 1190 1191 BMessage message(M_HEADER); 1192 message.AddBool("header", showHeader); 1193 PostMessage(&message, fContentView->fTextView); 1194 break; 1195 } 1196 case M_RAW: 1197 { 1198 bool raw = !(fRaw->IsMarked()); 1199 fRaw->SetMarked(raw); 1200 BMessage message(M_RAW); 1201 message.AddBool("raw", raw); 1202 PostMessage(&message, fContentView->fTextView); 1203 break; 1204 } 1205 case M_SEND_NOW: 1206 case M_SAVE_AS_DRAFT: 1207 Send(msg->what == M_SEND_NOW); 1208 break; 1209 1210 case M_SAVE: 1211 { 1212 char *str; 1213 if (msg->FindString("address", (const char **)&str) == B_NO_ERROR) 1214 { 1215 char *arg = (char *)malloc(strlen("META:email ") + strlen(str) + 1); 1216 BVolumeRoster volumeRoster; 1217 BVolume volume; 1218 volumeRoster.GetBootVolume(&volume); 1219 1220 BQuery query; 1221 query.SetVolume(&volume); 1222 sprintf(arg, "META:email=%s", str); 1223 query.SetPredicate(arg); 1224 query.Fetch(); 1225 1226 BEntry entry; 1227 if (query.GetNextEntry(&entry) == B_NO_ERROR) 1228 { 1229 BMessenger tracker("application/x-vnd.Be-TRAK"); 1230 if (tracker.IsValid()) 1231 { 1232 entry_ref ref; 1233 entry.GetRef(&ref); 1234 1235 BMessage open(B_REFS_RECEIVED); 1236 open.AddRef("refs", &ref); 1237 tracker.SendMessage(&open); 1238 } 1239 } 1240 else 1241 { 1242 sprintf(arg, "META:email %s", str); 1243 status_t result = be_roster->Launch("application/x-person", 1, &arg); 1244 if (result != B_NO_ERROR) 1245 (new BAlert("", MDR_DIALECT_CHOICE ( 1246 "Sorry, could not find an application that supports the 'Person' data type.", 1247 "Peopleデータ形式をサポートするアプリケーションが見つかりませんでした。"), 1248 MDR_DIALECT_CHOICE ("OK","了解")))->Go(); 1249 } 1250 free(arg); 1251 } 1252 break; 1253 } 1254 1255 case M_PRINT_SETUP: 1256 PrintSetup(); 1257 break; 1258 1259 case M_PRINT: 1260 Print(); 1261 break; 1262 1263 case M_SELECT: 1264 break; 1265 1266 case M_FIND: 1267 FindWindow::Find(this); 1268 break; 1269 1270 case M_FIND_AGAIN: 1271 FindWindow::FindAgain(this); 1272 break; 1273 1274 case M_QUOTE: 1275 case M_REMOVE_QUOTE: 1276 PostMessage(msg->what, fContentView); 1277 break; 1278 1279 case M_RANDOM_SIG: 1280 { 1281 BList sigList; 1282 BMessage *message; 1283 1284 BVolume volume; 1285 BVolumeRoster().GetBootVolume(&volume); 1286 1287 BQuery query; 1288 query.SetVolume(&volume); 1289 1290 char predicate[128]; 1291 sprintf(predicate, "%s = *", INDEX_SIGNATURE); 1292 query.SetPredicate(predicate); 1293 query.Fetch(); 1294 1295 BEntry entry; 1296 while (query.GetNextEntry(&entry) == B_NO_ERROR) 1297 { 1298 BFile file(&entry, O_RDONLY); 1299 if (file.InitCheck() == B_NO_ERROR) 1300 { 1301 entry_ref ref; 1302 entry.GetRef(&ref); 1303 1304 message = new BMessage(M_SIGNATURE); 1305 message->AddRef("ref", &ref); 1306 sigList.AddItem(message); 1307 } 1308 } 1309 if (sigList.CountItems() > 0) 1310 { 1311 srand(time(0)); 1312 PostMessage((BMessage *)sigList.ItemAt(rand() % sigList.CountItems())); 1313 1314 for (int32 i = 0; (message = (BMessage *)sigList.ItemAt(i)) != NULL; i++) 1315 delete message; 1316 } 1317 break; 1318 } 1319 case M_SIGNATURE: 1320 { 1321 BMessage message(*msg); 1322 PostMessage(&message, fContentView); 1323 fSigAdded = true; 1324 break; 1325 } 1326 case M_SIG_MENU: 1327 { 1328 TMenu *menu; 1329 BMenuItem *item; 1330 menu = new TMenu( "Add Signature", INDEX_SIGNATURE, M_SIGNATURE, true ); 1331 1332 BPoint where; 1333 bool open_anyway = true; 1334 1335 if (msg->FindPoint("where", &where) != B_OK) 1336 { 1337 BRect bounds; 1338 bounds = fSigButton->Bounds(); 1339 where = fSigButton->ConvertToScreen(BPoint((bounds.right-bounds.left)/2, 1340 (bounds.bottom-bounds.top)/2)); 1341 } 1342 else if (msg->FindInt32("buttons") == B_SECONDARY_MOUSE_BUTTON) 1343 open_anyway = false; 1344 1345 if ((item = menu->Go(where, false, open_anyway)) != NULL) 1346 { 1347 item->SetTarget(this); 1348 (dynamic_cast<BInvoker *>(item))->Invoke(); 1349 } 1350 delete menu; 1351 break; 1352 } 1353 1354 case M_ADD: 1355 if (!fPanel) { 1356 BMessenger me(this); 1357 BMessage msg(REFS_RECEIVED); 1358 fPanel = new BFilePanel(B_OPEN_PANEL, &me, &fOpenFolder, false, true, &msg); 1359 } 1360 else if (!fPanel->Window()->IsHidden()) 1361 fPanel->Window()->Activate(); 1362 1363 if (fPanel->Window()->IsHidden()) 1364 fPanel->Window()->Show(); 1365 break; 1366 1367 case M_REMOVE: 1368 PostMessage(msg->what, fEnclosuresView); 1369 break; 1370 1371 case CHARSET_CHOICE_MADE: 1372 if (fIncoming && !fResending) { 1373 // The user wants to see the message they are reading (not 1374 // composing) displayed with a different kind of character set 1375 // for decoding. Reload the whole message and redisplay. For 1376 // messages which are being composed, the character set is 1377 // retrieved from the header view when it is needed. 1378 1379 entry_ref fileRef = *fRef; 1380 int32 characterSet; 1381 msg->FindInt32("charset", &characterSet); 1382 OpenMessage(&fileRef, characterSet); 1383 } 1384 break; 1385 1386 case REFS_RECEIVED: 1387 AddEnclosure(msg); 1388 break; 1389 1390 // 1391 // Navigation Messages 1392 // 1393 case M_PREVMSG: 1394 case M_NEXTMSG: 1395 if (fRef) 1396 { 1397 entry_ref nextRef = *fRef; 1398 if (GetTrackerWindowFile(&nextRef, (msg->what == M_NEXTMSG))) { 1399 TMailWindow *window = static_cast<TMailApp *>(be_app)->FindWindow(nextRef); 1400 if (window == NULL) { 1401 SetCurrentMessageRead(); 1402 OpenMessage(&nextRef, fHeaderView->fCharacterSetUserSees); 1403 } else { 1404 window->Activate(); 1405 1406 //fSent = true; 1407 BMessage msg(B_CLOSE_REQUESTED); 1408 PostMessage(&msg); 1409 } 1410 1411 SetTrackerSelectionToCurrent(); 1412 } 1413 else 1414 beep(); 1415 } 1416 break; 1417 case M_SAVE_POSITION: 1418 if (fRef) 1419 SaveTrackerPosition(fRef); 1420 break; 1421 1422 case M_OPEN_MAIL_FOLDER: 1423 case M_OPEN_MAIL_BOX: 1424 { 1425 BEntry folderEntry; 1426 BPath path; 1427 // Get the user home directory 1428 if (find_directory(B_USER_DIRECTORY, &path) != B_OK) 1429 break; 1430 if (msg->what == M_OPEN_MAIL_FOLDER) 1431 path.Append(kMailFolder); 1432 else 1433 path.Append(kMailboxFolder); 1434 if (folderEntry.SetTo(path.Path()) == B_OK && folderEntry.Exists()) 1435 { 1436 BMessage thePackage(B_REFS_RECEIVED); 1437 BMessenger tracker("application/x-vnd.Be-TRAK"); 1438 1439 entry_ref ref; 1440 folderEntry.GetRef(&ref); 1441 thePackage.AddRef("refs", &ref); 1442 tracker.SendMessage(&thePackage); 1443 } 1444 break; 1445 } 1446 case RESET_BUTTONS: 1447 fChanged = false; 1448 fFieldState = 0; 1449 if (fHeaderView->fTo->TextView()->TextLength()) 1450 fFieldState |= FIELD_TO; 1451 if (fHeaderView->fSubject->TextView()->TextLength()) 1452 fFieldState |= FIELD_SUBJECT; 1453 if (fHeaderView->fCc->TextView()->TextLength()) 1454 fFieldState |= FIELD_CC; 1455 if (fHeaderView->fBcc->TextView()->TextLength()) 1456 fFieldState |= FIELD_BCC; 1457 if (fContentView->fTextView->TextLength()) 1458 fFieldState |= FIELD_BODY; 1459 1460 if (fSaveButton) 1461 fSaveButton->SetEnabled(false); 1462 if (fPrintButton) 1463 fPrintButton->SetEnabled(fFieldState); 1464 if (fSendButton) 1465 fSendButton->SetEnabled((fFieldState & FIELD_TO) || (fFieldState & FIELD_BCC)); 1466 break; 1467 1468 case M_CHECK_SPELLING: 1469 if (gDictCount == 0) 1470 // Give the application time to initialise and load the dictionaries. 1471 snooze (1500000); 1472 if (!gDictCount) 1473 { 1474 beep(); 1475 (new BAlert("", 1476 MDR_DIALECT_CHOICE ( 1477 "The spell check feature requires the optional \"words\" file on your BeOS CD.", 1478 "スペルチェク機能はBeOS CDの optional \"words\" ファイルが必要です"), 1479 MDR_DIALECT_CHOICE ("OK","了解"), 1480 NULL, NULL, B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 1481 B_STOP_ALERT))->Go(); 1482 } 1483 else 1484 { 1485 fSpelling->SetMarked(!fSpelling->IsMarked()); 1486 fContentView->fTextView->EnableSpellCheck(fSpelling->IsMarked()); 1487 } 1488 break; 1489 1490 default: 1491 BWindow::MessageReceived(msg); 1492 } 1493 } 1494 1495 1496 void 1497 TMailWindow::AddEnclosure(BMessage *msg) 1498 { 1499 if (fEnclosuresView == NULL && !fIncoming) 1500 { 1501 BRect r; 1502 r.left = 0; 1503 r.top = fHeaderView->Frame().bottom - 1; 1504 r.right = Frame().Width() + 2; 1505 r.bottom = r.top + ENCLOSURES_HEIGHT; 1506 1507 fEnclosuresView = new TEnclosuresView(r, Frame()); 1508 AddChild(fEnclosuresView, fContentView); 1509 fContentView->ResizeBy(0, -ENCLOSURES_HEIGHT); 1510 fContentView->MoveBy(0, ENCLOSURES_HEIGHT); 1511 } 1512 1513 if (fEnclosuresView == NULL) 1514 return; 1515 1516 if (msg && msg->HasRef("refs")) { 1517 // Add enclosure to view 1518 PostMessage(msg, fEnclosuresView); 1519 1520 fChanged = true; 1521 BEntry entry; 1522 entry_ref ref; 1523 msg->FindRef("refs", &ref); 1524 entry.SetTo(&ref); 1525 entry.GetParent(&entry); 1526 entry.GetRef(&fOpenFolder); 1527 } 1528 } 1529 1530 1531 bool 1532 TMailWindow::QuitRequested() 1533 { 1534 int32 result; 1535 1536 if ((!fIncoming || (fIncoming && fResending)) && fChanged && !fSent 1537 && (strlen(fHeaderView->fTo->Text()) 1538 || strlen(fHeaderView->fSubject->Text()) 1539 || (fHeaderView->fCc && strlen(fHeaderView->fCc->Text())) 1540 || (fHeaderView->fBcc && strlen(fHeaderView->fBcc->Text())) 1541 || (strlen(fContentView->fTextView->Text()) && (!fStartingText || fStartingText && strcmp(fContentView->fTextView->Text(), fStartingText))) 1542 || (fEnclosuresView != NULL && fEnclosuresView->fList->CountItems()))) 1543 { 1544 if (fResending) { 1545 BAlert *alert = new BAlert("", 1546 MDR_DIALECT_CHOICE ( 1547 "Do you wish to send this message before closing?", 1548 "閉じる前に送信しますか?"), 1549 MDR_DIALECT_CHOICE ("Discard","無視"), 1550 MDR_DIALECT_CHOICE ("Cancel","中止"), 1551 MDR_DIALECT_CHOICE ("Send","送信"), 1552 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 1553 B_WARNING_ALERT); 1554 alert->SetShortcut(0,'d'); 1555 alert->SetShortcut(1,B_ESCAPE); 1556 result = alert->Go(); 1557 1558 switch (result) { 1559 case 0: // Discard 1560 break; 1561 case 1: // Cancel 1562 return false; 1563 case 2: // Send 1564 Send(true); 1565 break; 1566 } 1567 } else { 1568 BAlert *alert = new BAlert("", 1569 MDR_DIALECT_CHOICE ( 1570 "Do you wish to save this message as a draft before closing?", 1571 "閉じる前に保存しますか?"), 1572 MDR_DIALECT_CHOICE ("Don't Save","保存しない"), 1573 MDR_DIALECT_CHOICE ("Cancel","中止"), 1574 MDR_DIALECT_CHOICE ("Save","保存"), 1575 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, 1576 B_WARNING_ALERT); 1577 alert->SetShortcut(0,'d'); 1578 alert->SetShortcut(1,B_ESCAPE); 1579 result = alert->Go(); 1580 switch (result) { 1581 case 0: // Don't Save 1582 break; 1583 case 1: // Cancel 1584 return false; 1585 case 2: // Save 1586 Send(false); 1587 break; 1588 } 1589 } 1590 } 1591 1592 BMessage message(WINDOW_CLOSED); 1593 message.AddInt32("kind", MAIL_WINDOW); 1594 message.AddPointer( "window", this ); 1595 be_app->PostMessage(&message); 1596 1597 if ((CurrentMessage()) && (CurrentMessage()->HasString("status"))) { 1598 // User explicitly requests a status to set this message to. 1599 if (!CurrentMessage()->HasString("same")) { 1600 const char *status = CurrentMessage()->FindString("status"); 1601 if (status != NULL) { 1602 BNode node(fRef); 1603 if (node.InitCheck() == B_NO_ERROR) { 1604 node.RemoveAttr(B_MAIL_ATTR_STATUS); 1605 WriteAttrString(&node, B_MAIL_ATTR_STATUS, status); 1606 } 1607 } 1608 } 1609 } else if (fRef) { 1610 // ...Otherwise just set the message read 1611 SetCurrentMessageRead(); 1612 } 1613 1614 return true; 1615 } 1616 1617 1618 void 1619 TMailWindow::Show() 1620 { 1621 if (Lock()) { 1622 if (!fResending && (fIncoming || fReplying)) 1623 fContentView->fTextView->MakeFocus(true); 1624 else 1625 { 1626 BTextView *textView = fHeaderView->fTo->TextView(); 1627 fHeaderView->fTo->MakeFocus(true); 1628 textView->Select(0, textView->TextLength()); 1629 } 1630 Unlock(); 1631 } 1632 BWindow::Show(); 1633 } 1634 1635 1636 void 1637 TMailWindow::Zoom(BPoint /*pos*/, float /*x*/, float /*y*/) 1638 { 1639 float height; 1640 float width; 1641 BScreen screen(this); 1642 BRect r; 1643 BRect s_frame = screen.Frame(); 1644 1645 r = Frame(); 1646 width = 80 * fApp->ContentFont().StringWidth("M") + 1647 (r.Width() - fContentView->fTextView->Bounds().Width() + 6); 1648 if (width > (s_frame.Width() - 8)) 1649 width = s_frame.Width() - 8; 1650 1651 height = max_c(fContentView->fTextView->CountLines(), 20) * 1652 fContentView->fTextView->LineHeight(0) + 1653 (r.Height() - fContentView->fTextView->Bounds().Height()); 1654 if (height > (s_frame.Height() - 29)) 1655 height = s_frame.Height() - 29; 1656 1657 r.right = r.left + width; 1658 r.bottom = r.top + height; 1659 1660 if (abs((int)(Frame().Width() - r.Width())) < 5 1661 && abs((int)(Frame().Height() - r.Height())) < 5) 1662 r = fZoom; 1663 else 1664 { 1665 fZoom = Frame(); 1666 s_frame.InsetBy(6, 6); 1667 1668 if (r.Width() > s_frame.Width()) 1669 r.right = r.left + s_frame.Width(); 1670 if (r.Height() > s_frame.Height()) 1671 r.bottom = r.top + s_frame.Height(); 1672 1673 if (r.right > s_frame.right) 1674 { 1675 r.left -= r.right - s_frame.right; 1676 r.right = s_frame.right; 1677 } 1678 if (r.bottom > s_frame.bottom) 1679 { 1680 r.top -= r.bottom - s_frame.bottom; 1681 r.bottom = s_frame.bottom; 1682 } 1683 if (r.left < s_frame.left) 1684 { 1685 r.right += s_frame.left - r.left; 1686 r.left = s_frame.left; 1687 } 1688 if (r.top < s_frame.top) 1689 { 1690 r.bottom += s_frame.top - r.top; 1691 r.top = s_frame.top; 1692 } 1693 } 1694 1695 ResizeTo(r.Width(), r.Height()); 1696 MoveTo(r.LeftTop()); 1697 } 1698 1699 1700 void 1701 TMailWindow::WindowActivated(bool status) 1702 { 1703 if (status) { 1704 BAutolock locker(sWindowListLock); 1705 sWindowList.RemoveItem(this); 1706 sWindowList.AddItem(this, 0); 1707 } 1708 } 1709 1710 1711 void 1712 TMailWindow::Forward(entry_ref *ref, TMailWindow *window, bool includeAttachments) 1713 { 1714 BEmailMessage *mail = window->Mail(); 1715 if (mail == NULL) 1716 return; 1717 1718 uint32 useAccountFrom = fApp->UseAccountFrom(); 1719 1720 fMail = mail->ForwardMessage(useAccountFrom == ACCOUNT_FROM_MAIL, 1721 includeAttachments); 1722 1723 BFile file(ref, O_RDONLY); 1724 if (file.InitCheck() < B_NO_ERROR) 1725 return; 1726 1727 fHeaderView->fSubject->SetText(fMail->Subject()); 1728 1729 // set mail account 1730 1731 if (useAccountFrom == ACCOUNT_FROM_MAIL) { 1732 fHeaderView->fChain = fMail->Account(); 1733 1734 BMenu *menu = fHeaderView->fAccountMenu; 1735 for (int32 i = menu->CountItems(); i-- > 0;) { 1736 BMenuItem *item = menu->ItemAt(i); 1737 BMessage *msg; 1738 if (item && (msg = item->Message()) != NULL 1739 && msg->FindInt32("id") == fHeaderView->fChain) 1740 item->SetMarked(true); 1741 } 1742 } 1743 1744 if (fMail->CountComponents() > 1) { 1745 // if there are any enclosures to be added, first add the enclosures 1746 // view to the window 1747 AddEnclosure(NULL); 1748 if (fEnclosuresView) 1749 fEnclosuresView->AddEnclosuresFromMail(fMail); 1750 } 1751 1752 fContentView->fTextView->LoadMessage(fMail, false, NULL); 1753 fChanged = false; 1754 fFieldState = 0; 1755 } 1756 1757 1758 class HorizontalLine : public BView { 1759 public: 1760 HorizontalLine(BRect rect) : BView (rect, NULL, B_FOLLOW_ALL, B_WILL_DRAW) {} 1761 virtual void Draw(BRect rect) 1762 { 1763 FillRect(rect,B_SOLID_HIGH); 1764 } 1765 }; 1766 1767 1768 void 1769 TMailWindow::Print() 1770 { 1771 BPrintJob print(Title()); 1772 1773 if (!fApp->HasPrintSettings()) { 1774 if (print.Settings()) { 1775 fApp->SetPrintSettings(print.Settings()); 1776 } else { 1777 PrintSetup(); 1778 if (!fApp->HasPrintSettings()) 1779 return; 1780 } 1781 } 1782 1783 print.SetSettings(new BMessage(fApp->PrintSettings())); 1784 1785 if (print.ConfigJob() == B_NO_ERROR) { 1786 int32 curPage = 1; 1787 int32 lastLine = 0; 1788 BTextView header_view(print.PrintableRect(),"header",print.PrintableRect().OffsetByCopy(BPoint(-print.PrintableRect().left,-print.PrintableRect().top)),B_FOLLOW_ALL_SIDES); 1789 1790 //---------Init the header fields 1791 #define add_header_field(field) {/*header_view.SetFontAndColor(be_bold_font);*/ \ 1792 header_view.Insert(fHeaderView->field->Label()); \ 1793 header_view.Insert(" ");\ 1794 /*header_view.SetFontAndColor(be_plain_font);*/ \ 1795 header_view.Insert(fHeaderView->field->Text()); \ 1796 header_view.Insert("\n");} 1797 add_header_field(fSubject); 1798 add_header_field(fTo); 1799 if ((fHeaderView->fCc != NULL) && (strcmp(fHeaderView->fCc->Text(),"") != 0)) 1800 add_header_field(fCc); 1801 header_view.Insert(fHeaderView->fDate->Text()); 1802 1803 int32 maxLine = fContentView->fTextView->CountLines(); 1804 BRect pageRect = print.PrintableRect(); 1805 BRect curPageRect = pageRect; 1806 1807 print.BeginJob(); 1808 float header_height = header_view.TextHeight(0,header_view.CountLines()); 1809 BBitmap bmap(BRect(0,0,pageRect.Width(),header_height),B_BITMAP_ACCEPTS_VIEWS,B_RGBA32); 1810 bmap.Lock(); 1811 bmap.AddChild(&header_view); 1812 print.DrawView(&header_view,BRect(0,0,pageRect.Width(),header_height),BPoint(0.0,0.0)); 1813 HorizontalLine line(BRect(0,0,pageRect.right,0)); 1814 bmap.AddChild(&line); 1815 print.DrawView(&line,line.Bounds(),BPoint(0,header_height+1)); 1816 bmap.Unlock(); 1817 header_height += 5; 1818 1819 do 1820 { 1821 int32 lineOffset = fContentView->fTextView->OffsetAt(lastLine); 1822 curPageRect.OffsetTo(0, fContentView->fTextView->PointAt(lineOffset).y); 1823 1824 int32 fromLine = lastLine; 1825 lastLine = fContentView->fTextView->LineAt(BPoint(0.0, curPageRect.bottom - ((curPage == 1) ? header_height : 0))); 1826 1827 float curPageHeight = fContentView->fTextView->TextHeight(fromLine, lastLine) + ((curPage == 1) ? header_height : 0); 1828 if(curPageHeight > pageRect.Height()) 1829 curPageHeight = fContentView->fTextView->TextHeight(fromLine, --lastLine) + ((curPage == 1) ? header_height : 0); 1830 1831 curPageRect.bottom = curPageRect.top + curPageHeight - 1.0; 1832 1833 if((curPage >= print.FirstPage()) && 1834 (curPage <= print.LastPage())) 1835 { 1836 print.DrawView(fContentView->fTextView, curPageRect, BPoint(0.0, (curPage == 1) ? header_height : 0.0)); 1837 print.SpoolPage(); 1838 } 1839 1840 curPageRect = pageRect; 1841 lastLine++; 1842 curPage++; 1843 1844 } while (print.CanContinue() && lastLine < maxLine); 1845 1846 print.CommitJob(); 1847 bmap.RemoveChild(&header_view); 1848 bmap.RemoveChild(&line); 1849 } 1850 } 1851 1852 1853 void 1854 TMailWindow::PrintSetup() 1855 { 1856 BPrintJob printJob("mail_print"); 1857 1858 if (fApp->HasPrintSettings()) { 1859 BMessage printSettings = fApp->PrintSettings(); 1860 printJob.SetSettings(new BMessage(printSettings)); 1861 } 1862 1863 if (printJob.ConfigPage() == B_OK) 1864 fApp->SetPrintSettings(printJob.Settings()); 1865 } 1866 1867 1868 void 1869 TMailWindow::SetTo(const char *mailTo, const char *subject, const char *ccTo, 1870 const char *bccTo, const BString *body, BMessage *enclosures) 1871 { 1872 Lock(); 1873 1874 if (mailTo && mailTo[0]) 1875 fHeaderView->fTo->SetText(mailTo); 1876 if (subject && subject[0]) 1877 fHeaderView->fSubject->SetText(subject); 1878 if (ccTo && ccTo[0]) 1879 fHeaderView->fCc->SetText(ccTo); 1880 if (bccTo && bccTo[0]) 1881 fHeaderView->fBcc->SetText(bccTo); 1882 1883 if (body && body->Length()) 1884 { 1885 fContentView->fTextView->SetText(body->String(), body->Length()); 1886 fContentView->fTextView->GoToLine(0); 1887 } 1888 1889 if (enclosures && enclosures->HasRef("refs")) 1890 AddEnclosure(enclosures); 1891 1892 Unlock(); 1893 } 1894 1895 1896 void 1897 TMailWindow::CopyMessage(entry_ref *ref, TMailWindow *src) 1898 { 1899 BNode file(ref); 1900 if (file.InitCheck() == B_OK) { 1901 BString string; 1902 if (fHeaderView->fTo && ReadAttrString(&file, B_MAIL_ATTR_TO, &string) == B_OK) 1903 fHeaderView->fTo->SetText(string.String()); 1904 1905 if (fHeaderView->fSubject && ReadAttrString(&file, B_MAIL_ATTR_SUBJECT, &string) == B_OK) 1906 fHeaderView->fSubject->SetText(string.String()); 1907 1908 if (fHeaderView->fCc && ReadAttrString(&file, B_MAIL_ATTR_CC, &string) == B_OK) 1909 fHeaderView->fCc->SetText(string.String()); 1910 } 1911 1912 TTextView *text = src->fContentView->fTextView; 1913 text_run_array *style = text->RunArray(0, text->TextLength()); 1914 1915 fContentView->fTextView->SetText(text->Text(), text->TextLength(), style); 1916 1917 free(style); 1918 } 1919 1920 1921 void 1922 TMailWindow::Reply(entry_ref *ref, TMailWindow *window, uint32 type) 1923 { 1924 const char *notImplementedString = "<Not Yet Implemented>"; 1925 1926 fRepliedMail = *ref; 1927 SetOriginatingWindow(window); 1928 1929 BEmailMessage *mail = window->Mail(); 1930 if (mail == NULL) 1931 return; 1932 1933 if (type == M_REPLY_ALL) 1934 type = B_MAIL_REPLY_TO_ALL; 1935 else if (type == M_REPLY_TO_SENDER) 1936 type = B_MAIL_REPLY_TO_SENDER; 1937 else 1938 type = B_MAIL_REPLY_TO; 1939 1940 uint32 useAccountFrom = fApp->UseAccountFrom(); 1941 1942 fMail = mail->ReplyMessage(mail_reply_to_mode(type), 1943 useAccountFrom == ACCOUNT_FROM_MAIL, QUOTE); 1944 1945 // set header fields 1946 fHeaderView->fTo->SetText(fMail->To()); 1947 fHeaderView->fCc->SetText(fMail->CC()); 1948 fHeaderView->fSubject->SetText(fMail->Subject()); 1949 1950 int32 chainID; 1951 BFile file(window->fRef, B_READ_ONLY); 1952 if (file.ReadAttr("MAIL:reply_with", B_INT32_TYPE, 0, &chainID, 4) < B_OK) 1953 chainID = -1; 1954 1955 // set mail account 1956 1957 if ((useAccountFrom == ACCOUNT_FROM_MAIL) || (chainID > -1)) { 1958 if (useAccountFrom == ACCOUNT_FROM_MAIL) 1959 fHeaderView->fChain = fMail->Account(); 1960 else 1961 fHeaderView->fChain = chainID; 1962 1963 BMenu *menu = fHeaderView->fAccountMenu; 1964 for (int32 i = menu->CountItems(); i-- > 0;) { 1965 BMenuItem *item = menu->ItemAt(i); 1966 BMessage *msg; 1967 if (item && (msg = item->Message()) != NULL 1968 && msg->FindInt32("id") == fHeaderView->fChain) 1969 item->SetMarked(true); 1970 } 1971 } 1972 1973 // create preamble string 1974 1975 BString replyPreamble = fApp->ReplyPreamble(); 1976 1977 char preamble[1024]; 1978 const char* from = replyPreamble.String(); 1979 char* to = preamble; 1980 1981 while (*from) { 1982 if (*from == '%') { 1983 // insert special content 1984 int32 length; 1985 1986 switch (*++from) { 1987 case 'n': // full name 1988 { 1989 BString fullName(mail->From()); 1990 if (fullName.Length() <= 0) 1991 fullName = "No-From-Address-Available"; 1992 1993 extract_address_name(fullName); 1994 length = fullName.Length(); 1995 memcpy(to, fullName.String(), length); 1996 to += length; 1997 break; 1998 } 1999 2000 case 'e': // eMail address 2001 { 2002 const char *address = mail->From(); 2003 if (address == NULL) 2004 address = "<unknown>"; 2005 length = strlen(address); 2006 memcpy(to, address, length); 2007 to += length; 2008 break; 2009 } 2010 2011 case 'd': // date 2012 { 2013 const char *date = mail->Date(); 2014 if (date == NULL) 2015 date = "No-Date-Available"; 2016 length = strlen(date); 2017 memcpy(to, date, length); 2018 to += length; 2019 break; 2020 } 2021 2022 // ToDo: parse stuff! 2023 case 'f': // first name 2024 case 'l': // last name 2025 length = strlen(notImplementedString); 2026 memcpy(to, notImplementedString, length); 2027 to += length; 2028 break; 2029 2030 default: // Sometimes a % is just a %. 2031 *to++ = *from; 2032 } 2033 } else if (*from == '\\') { 2034 switch (*++from) { 2035 case 'n': 2036 *to++ = '\n'; 2037 break; 2038 2039 default: 2040 *to++ = *from; 2041 } 2042 } else 2043 *to++ = *from; 2044 2045 from++; 2046 } 2047 *to = '\0'; 2048 2049 // insert (if selection) or load (if whole mail) message text into text view 2050 2051 int32 finish, start; 2052 window->fContentView->fTextView->GetSelection(&start, &finish); 2053 if (start != finish) { 2054 char *text = (char *)malloc(finish - start + 1); 2055 if (text == NULL) 2056 return; 2057 2058 window->fContentView->fTextView->GetText(start, finish - start, text); 2059 if (text[strlen(text) - 1] != '\n') { 2060 text[strlen(text)] = '\n'; 2061 finish++; 2062 } 2063 fContentView->fTextView->SetText(text, finish - start); 2064 free(text); 2065 2066 finish = fContentView->fTextView->CountLines() - 1; 2067 for (int32 loop = 0; loop < finish; loop++) { 2068 fContentView->fTextView->GoToLine(loop); 2069 fContentView->fTextView->Insert((const char *)QUOTE); 2070 } 2071 2072 if (fApp->ColoredQuotes()) { 2073 const BFont *font = fContentView->fTextView->Font(); 2074 int32 length = fContentView->fTextView->TextLength(); 2075 2076 TextRunArray style(length / 8 + 8); 2077 2078 FillInQuoteTextRuns(fContentView->fTextView, fContentView->fTextView->Text(), 2079 length, font, &style.Array(), style.MaxEntries()); 2080 2081 fContentView->fTextView->SetRunArray(0, length, &style.Array()); 2082 } 2083 2084 fContentView->fTextView->GoToLine(0); 2085 if (strlen(preamble) > 0) 2086 fContentView->fTextView->Insert(preamble); 2087 } 2088 else 2089 fContentView->fTextView->LoadMessage(mail, true, preamble); 2090 2091 fReplying = true; 2092 } 2093 2094 2095 status_t 2096 TMailWindow::Send(bool now) 2097 { 2098 uint32 characterSetToUse = fApp->MailCharacterSet(); 2099 mail_encoding encodingForBody = quoted_printable; 2100 mail_encoding encodingForHeaders = quoted_printable; 2101 2102 if (!now) { 2103 status_t status; 2104 2105 if ((status = SaveAsDraft()) != B_OK) { 2106 beep(); 2107 (new BAlert("", 2108 MDR_DIALECT_CHOICE ("E-mail draft could not be saved!","ドラフトは保存できませんでした。"), 2109 MDR_DIALECT_CHOICE ("OK","了解")))->Go(); 2110 } 2111 return status; 2112 } 2113 2114 Hide(); 2115 // depending on the system (and I/O) load, this could take a while 2116 // but the user shouldn't be left waiting 2117 2118 if (fHeaderView != NULL) 2119 characterSetToUse = fHeaderView->fCharacterSetUserSees; 2120 2121 // Set up the encoding to use for converting binary to printable ASCII. 2122 // Normally this will be quoted printable, but for some old software, 2123 // particularly Japanese stuff, they only understand base64. They also 2124 // prefer it for the smaller size. Later on this will be reduced to 7bit 2125 // if the encoded text is just 7bit characters. 2126 if (characterSetToUse == B_SJIS_CONVERSION 2127 || characterSetToUse == B_EUC_CONVERSION) 2128 encodingForBody = base64; 2129 else if (characterSetToUse == B_JIS_CONVERSION 2130 || characterSetToUse == B_MAIL_US_ASCII_CONVERSION 2131 || characterSetToUse == B_ISO1_CONVERSION 2132 || characterSetToUse == B_EUC_KR_CONVERSION) 2133 encodingForBody = eight_bit; 2134 2135 // Using quoted printable headers on almost completely non-ASCII Japanese 2136 // is a waste of time. Besides, some stupid cell phone services need 2137 // base64 in the headers. 2138 if (characterSetToUse == B_SJIS_CONVERSION 2139 || characterSetToUse == B_EUC_CONVERSION 2140 || characterSetToUse == B_JIS_CONVERSION 2141 || characterSetToUse == B_EUC_KR_CONVERSION) 2142 encodingForHeaders = base64; 2143 2144 // Count the number of characters in the message body which aren't in the 2145 // currently selected character set. Also see if the resulting encoded 2146 // text can safely use 7 bit characters. 2147 if (fContentView->fTextView->TextLength() > 0) { 2148 // First do a trial encoding with the user's character set. 2149 int32 converterState = 0; 2150 int32 originalLength; 2151 BString tempString; 2152 int32 tempStringLength; 2153 char* tempStringPntr; 2154 originalLength = fContentView->fTextView->TextLength(); 2155 tempStringLength = originalLength * 2156 6 /* Some character sets bloat up on escape codes */; 2157 tempStringPntr = tempString.LockBuffer (tempStringLength); 2158 if (tempStringPntr != NULL && mail_convert_from_utf8(characterSetToUse, 2159 fContentView->fTextView->Text(), &originalLength, 2160 tempStringPntr, &tempStringLength, &converterState, 2161 0x1A /* used for unknown characters */) == B_OK) { 2162 // Check for any characters which don't fit in a 7 bit encoding. 2163 int i; 2164 bool has8Bit = false; 2165 for (i = 0; i < tempStringLength; i++) 2166 if (tempString[i] == 0 || (tempString[i] & 0x80)) { 2167 has8Bit = true; 2168 break; 2169 } 2170 if (!has8Bit) 2171 encodingForBody = seven_bit; 2172 tempString.UnlockBuffer (tempStringLength); 2173 2174 // Count up the number of unencoded characters and warn the user about them. 2175 if (fApp->WarnAboutUnencodableCharacters()) { 2176 int32 offset = 0; 2177 int count = 0; 2178 while (offset >= 0) { 2179 offset = tempString.FindFirst (0x1A, offset); 2180 if (offset >= 0) { 2181 count++; 2182 offset++; // Don't get stuck finding the same character again. 2183 } 2184 } 2185 if (count > 0) { 2186 int32 userAnswer; 2187 BString messageString; 2188 MDR_DIALECT_CHOICE ( 2189 messageString << "Your main text contains " << count << 2190 " unencodable characters. Perhaps a different character " 2191 "set would work better? Hit Send to send it anyway " 2192 "(a substitute character will be used in place of " 2193 "the unencodable ones), or choose Cancel to go back " 2194 "and try fixing it up." 2195 , 2196 messageString << "送信メールの本文には " << count << 2197 " 個のエンコードできない文字があります。" 2198 "違う文字セットを使うほうがよい可能性があります。" 2199 "このまま送信の場合は「送信」ボタンを押してください。" 2200 "その場合、代用文字がUnicode化可能な文字に代わって使われます。" 2201 "文字セットを変更する場合は「中止」ボタンを押して下さい。" 2202 ); 2203 userAnswer = (new BAlert ("Question", messageString.String(), 2204 MDR_DIALECT_CHOICE ("Send","送信"), 2205 MDR_DIALECT_CHOICE ("Cancel","中止"), // Default is cancel. 2206 NULL, B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT)) 2207 ->Go(); 2208 if (userAnswer == 1) 2209 return -1; // Cancel was picked. 2210 } 2211 } 2212 } 2213 } 2214 2215 status_t result; 2216 2217 if (fResending) { 2218 BFile file(fRef, O_RDONLY); 2219 result = file.InitCheck(); 2220 if (result == B_OK) { 2221 BEmailMessage mail(&file); 2222 mail.SetTo(fHeaderView->fTo->Text(), characterSetToUse, encodingForHeaders); 2223 2224 if (fHeaderView->fChain != ~0L) 2225 mail.SendViaAccount(fHeaderView->fChain); 2226 2227 result = mail.Send(now); 2228 } 2229 } else { 2230 if (fMail == NULL) 2231 // the mail will be deleted when the window is closed 2232 fMail = new BEmailMessage; 2233 2234 // Had an embarrassing bug where replying to a message and clearing the 2235 // CC field meant that it got sent out anyway, so pass in empty strings 2236 // when changing the header to force it to remove the header. 2237 2238 fMail->SetTo(fHeaderView->fTo->Text(), characterSetToUse, 2239 encodingForHeaders); 2240 fMail->SetSubject(fHeaderView->fSubject->Text(), characterSetToUse, 2241 encodingForHeaders); 2242 fMail->SetCC(fHeaderView->fCc->Text(), characterSetToUse, 2243 encodingForHeaders); 2244 fMail->SetBCC(fHeaderView->fBcc->Text()); 2245 2246 //--- Add X-Mailer field 2247 { 2248 // get app version 2249 version_info info; 2250 memset(&info, 0, sizeof(version_info)); 2251 2252 app_info appInfo; 2253 if (be_app->GetAppInfo(&appInfo) == B_OK) { 2254 BFile file(&appInfo.ref, B_READ_ONLY); 2255 if (file.InitCheck() == B_OK) { 2256 BAppFileInfo appFileInfo(&file); 2257 if (appFileInfo.InitCheck() == B_OK) 2258 appFileInfo.GetVersionInfo(&info, B_APP_VERSION_KIND); 2259 } 2260 } 2261 2262 char versionString[255]; 2263 sprintf(versionString, 2264 "Mail/Haiku %ld.%ld.%ld", 2265 info.major, info.middle, info.minor); 2266 fMail->SetHeaderField("X-Mailer", versionString); 2267 } 2268 2269 /****/ 2270 2271 // the content text is always added to make sure there is a mail body 2272 fMail->SetBodyTextTo(""); 2273 fContentView->fTextView->AddAsContent(fMail, fApp->WrapMode(), 2274 characterSetToUse, encodingForBody); 2275 2276 if (fEnclosuresView != NULL) { 2277 TListItem *item; 2278 int32 index = 0; 2279 while ((item = (TListItem *)fEnclosuresView->fList->ItemAt(index++)) != NULL) { 2280 if (item->Component()) 2281 continue; 2282 2283 // leave out missing enclosures 2284 BEntry entry(item->Ref()); 2285 if (!entry.Exists()) 2286 continue; 2287 2288 fMail->Attach(item->Ref(), fApp->AttachAttributes()); 2289 } 2290 } 2291 if (fHeaderView->fChain != ~0L) 2292 fMail->SendViaAccount(fHeaderView->fChain); 2293 2294 result = fMail->Send(now); 2295 2296 if (fReplying) { 2297 // Set status of the replied mail 2298 2299 BNode node(&fRepliedMail); 2300 if (node.InitCheck() >= B_OK) { 2301 if (fOriginatingWindow) { 2302 BMessage msg(M_SAVE_POSITION), reply; 2303 fOriginatingWindow->SendMessage(&msg, &reply); 2304 } 2305 WriteAttrString(&node, B_MAIL_ATTR_STATUS, "Replied"); 2306 } 2307 } 2308 } 2309 2310 bool close = false; 2311 char errorMessage[256]; 2312 2313 switch (result) { 2314 case B_NO_ERROR: 2315 close = true; 2316 fSent = true; 2317 2318 // If it's a draft, remove the draft file 2319 if (fDraft) { 2320 BEntry entry(fRef); 2321 entry.Remove(); 2322 } 2323 break; 2324 2325 case B_MAIL_NO_DAEMON: 2326 { 2327 close = true; 2328 fSent = true; 2329 2330 int32 start = (new BAlert("no daemon", 2331 MDR_DIALECT_CHOICE ("The mail_daemon is not running. " 2332 "The message is queued and will be sent when the mail_daemon is started.", 2333 "mail_daemon が開始されていません " 2334 "このメッセージは処理待ちとなり、mail_daemon 開始後に処理されます"), 2335 MDR_DIALECT_CHOICE ("Start Now","ただちに開始する"), 2336 MDR_DIALECT_CHOICE ("Ok","了解")))->Go(); 2337 2338 if (start == 0) { 2339 result = be_roster->Launch("application/x-vnd.Be-POST"); 2340 if (result == B_OK) 2341 BMailDaemon::SendQueuedMail(); 2342 else { 2343 sprintf(errorMessage,"The mail_daemon could not be started:\n\t%s", 2344 strerror(result)); 2345 } 2346 } 2347 break; 2348 } 2349 2350 // case B_MAIL_UNKNOWN_HOST: 2351 // case B_MAIL_ACCESS_ERROR: 2352 // sprintf(errorMessage, "An error occurred trying to connect with the SMTP " 2353 // "host. Check your SMTP host name."); 2354 // break; 2355 // 2356 // case B_MAIL_NO_RECIPIENT: 2357 // sprintf(errorMessage, "You must have either a \"To\" or \"Bcc\" recipient."); 2358 // break; 2359 2360 default: 2361 sprintf(errorMessage, "An error occurred trying to send mail:\n\t%s", 2362 strerror(result)); 2363 break; 2364 } 2365 2366 if (result != B_NO_ERROR && result != B_MAIL_NO_DAEMON) { 2367 beep(); 2368 (new BAlert("", errorMessage, "Ok"))->Go(); 2369 } 2370 if (close) 2371 PostMessage(B_QUIT_REQUESTED); 2372 2373 return result; 2374 } 2375 2376 2377 status_t 2378 TMailWindow::SaveAsDraft() 2379 { 2380 status_t status; 2381 BPath draftPath; 2382 BDirectory dir; 2383 BFile draft; 2384 uint32 flags = 0; 2385 2386 if (fDraft) { 2387 if ((status = draft.SetTo(fRef, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)) != B_OK) 2388 return status; 2389 } else { 2390 // Get the user home directory 2391 if ((status = find_directory(B_USER_DIRECTORY, &draftPath)) != B_OK) 2392 return status; 2393 2394 // Append the relative path of the draft directory 2395 draftPath.Append(kDraftPath); 2396 2397 // Create the file 2398 status = dir.SetTo(draftPath.Path()); 2399 switch (status) { 2400 // Create the directory if it does not exist 2401 case B_ENTRY_NOT_FOUND: 2402 if ((status = dir.CreateDirectory(draftPath.Path(), &dir)) != B_OK) 2403 return status; 2404 case B_OK: 2405 { 2406 char fileName[512], *eofn; 2407 int32 i; 2408 2409 // save as some version of the message's subject 2410 strncpy(fileName, fHeaderView->fSubject->Text(), sizeof(fileName)-10); 2411 fileName[sizeof(fileName)-10]='\0'; // terminate like strncpy doesn't 2412 eofn = fileName + strlen(fileName); 2413 2414 // convert /, \ and : to - 2415 for (char *bad = fileName; (bad = strchr(bad, '/')) != NULL; ++bad) *bad = '-'; 2416 for (char *bad = fileName; (bad = strchr(bad, '\\')) != NULL;++bad) *bad = '-'; 2417 for (char *bad = fileName; (bad = strchr(bad, ':')) != NULL; ++bad) *bad = '-'; 2418 2419 // Create the file; if the name exists, find a unique name 2420 flags = B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS; 2421 for (i = 1; (status = draft.SetTo(&dir, fileName, flags )) != B_OK; i++) { 2422 if( status != B_FILE_EXISTS ) 2423 return status; 2424 sprintf(eofn, "%ld", i ); 2425 } 2426 2427 // Cache the ref 2428 delete fRef; 2429 BEntry entry(&dir, fileName); 2430 fRef = new entry_ref; 2431 entry.GetRef(fRef); 2432 break; 2433 } 2434 default: 2435 return status; 2436 } 2437 } 2438 2439 // Write the content of the message 2440 draft.Write(fContentView->fTextView->Text(), fContentView->fTextView->TextLength()); 2441 2442 // 2443 // Add the header stuff as attributes 2444 // 2445 WriteAttrString(&draft, B_MAIL_ATTR_NAME, fHeaderView->fTo->Text()); 2446 WriteAttrString(&draft, B_MAIL_ATTR_TO, fHeaderView->fTo->Text()); 2447 WriteAttrString(&draft, B_MAIL_ATTR_SUBJECT, fHeaderView->fSubject->Text()); 2448 if (fHeaderView->fCc != NULL) 2449 WriteAttrString(&draft, B_MAIL_ATTR_CC, fHeaderView->fCc->Text()); 2450 if (fHeaderView->fBcc != NULL) 2451 WriteAttrString(&draft, "MAIL:bcc", fHeaderView->fBcc->Text()); 2452 2453 // Add the draft attribute for indexing 2454 uint32 draftAttr = true; 2455 draft.WriteAttr( "MAIL:draft", B_INT32_TYPE, 0, &draftAttr, sizeof(uint32) ); 2456 2457 // Add Attachment paths in attribute 2458 if (fEnclosuresView != NULL) { 2459 TListItem *item; 2460 BPath path; 2461 BString pathStr; 2462 2463 for (int32 i = 0; (item = (TListItem *)fEnclosuresView->fList->ItemAt(i)) != NULL; i++) { 2464 if (i > 0) 2465 pathStr.Append(":"); 2466 2467 BEntry entry(item->Ref(), true); 2468 if (!entry.Exists()) 2469 continue; 2470 2471 entry.GetPath(&path); 2472 pathStr.Append(path.Path()); 2473 } 2474 if (pathStr.Length()) 2475 WriteAttrString(&draft, "MAIL:attachments", pathStr.String()); 2476 } 2477 2478 // Set the MIME Type of the file 2479 BNodeInfo info(&draft); 2480 info.SetType(kDraftType); 2481 2482 fSent = true; 2483 fDraft = true; 2484 fChanged = false; 2485 2486 return B_OK; 2487 } 2488 2489 2490 status_t 2491 TMailWindow::TrainMessageAs(const char *CommandWord) 2492 { 2493 status_t errorCode = -1; 2494 char errorString[1500]; 2495 BEntry fileEntry; 2496 BPath filePath; 2497 BMessage replyMessage; 2498 BMessage scriptingMessage; 2499 team_id serverTeam; 2500 2501 if (fRef == NULL) 2502 goto ErrorExit; // Need to have a real file and name. 2503 errorCode = fileEntry.SetTo(fRef, true /* traverse */); 2504 if (errorCode != B_OK) 2505 goto ErrorExit; 2506 errorCode = fileEntry.GetPath(&filePath); 2507 if (errorCode != B_OK) 2508 goto ErrorExit; 2509 fileEntry.Unset(); 2510 2511 // Get a connection to the spam database server. Launch if needed. 2512 2513 if (!fMessengerToSpamServer.IsValid()) { 2514 // Make sure the server is running. 2515 if (!be_roster->IsRunning (kSpamServerSignature)) { 2516 errorCode = be_roster->Launch (kSpamServerSignature); 2517 if (errorCode != B_OK) { 2518 BPath path; 2519 entry_ref ref; 2520 directory_which places[] = {B_COMMON_BIN_DIRECTORY,B_BEOS_BIN_DIRECTORY}; 2521 for (int32 i = 0; i < 2; i++) { 2522 find_directory(places[i],&path); 2523 path.Append("spamdbm"); 2524 if (!BEntry(path.Path()).Exists()) 2525 continue; 2526 get_ref_for_path(path.Path(),&ref); 2527 if ((errorCode = be_roster->Launch (&ref)) == B_OK) 2528 break; 2529 } 2530 if (errorCode != B_OK) 2531 goto ErrorExit; 2532 } 2533 } 2534 2535 // Set up the messenger to the database server. 2536 errorCode = B_SERVER_NOT_FOUND; 2537 serverTeam = be_roster->TeamFor(kSpamServerSignature); 2538 if (serverTeam < 0) 2539 goto ErrorExit; 2540 2541 fMessengerToSpamServer = BMessenger (kSpamServerSignature, serverTeam, &errorCode); 2542 if (!fMessengerToSpamServer.IsValid()) 2543 goto ErrorExit; 2544 } 2545 2546 // Ask the server to train on the message. Give it the command word and 2547 // the absolute path name to use. 2548 2549 scriptingMessage.MakeEmpty(); 2550 scriptingMessage.what = B_SET_PROPERTY; 2551 scriptingMessage.AddSpecifier(CommandWord); 2552 errorCode = scriptingMessage.AddData("data", B_STRING_TYPE, 2553 filePath.Path(), strlen(filePath.Path()) + 1, false /* fixed size */); 2554 if (errorCode != B_OK) 2555 goto ErrorExit; 2556 replyMessage.MakeEmpty(); 2557 errorCode = fMessengerToSpamServer.SendMessage(&scriptingMessage, 2558 &replyMessage); 2559 if (errorCode != B_OK 2560 || replyMessage.FindInt32("error", &errorCode) != B_OK 2561 || errorCode != B_OK) 2562 goto ErrorExit; // Classification failed in one of many ways. 2563 2564 SetTitleForMessage(); // Update window title to show new spam classification. 2565 return B_OK; 2566 2567 ErrorExit: 2568 beep(); 2569 sprintf(errorString, "Unable to train the message file \"%s\" as %s. " 2570 "Possibly useful error code: %s (%ld).", 2571 filePath.Path(), CommandWord, strerror (errorCode), errorCode); 2572 (new BAlert("", errorString, 2573 MDR_DIALECT_CHOICE("OK","了解")))->Go(); 2574 return errorCode; 2575 } 2576 2577 2578 void 2579 TMailWindow::SetTitleForMessage() 2580 { 2581 // 2582 // Figure out the title of this message and set the title bar 2583 // 2584 BString title = "Mail"; 2585 2586 if (fIncoming) { 2587 if (fMail->GetName(&title) == B_OK) 2588 title << ": \"" << fMail->Subject() << "\""; 2589 else 2590 title = fMail->Subject(); 2591 2592 if (fApp->ShowSpamGUI() && fRef != NULL) { 2593 BString classification; 2594 BNode node (fRef); 2595 char numberString [30]; 2596 BString oldTitle (title); 2597 float spamRatio; 2598 if (node.InitCheck() != B_OK || node.ReadAttrString 2599 ("MAIL:classification", &classification) != B_OK) 2600 classification = "Unrated"; 2601 if (classification != "Spam" && classification != "Genuine") { 2602 // Uncertain, Unrated and other unknown classes, show the ratio. 2603 if (node.InitCheck() == B_OK && sizeof (spamRatio) == 2604 node.ReadAttr("MAIL:ratio_spam", B_FLOAT_TYPE, 0, 2605 &spamRatio, sizeof (spamRatio))) { 2606 sprintf (numberString, "%.4f", spamRatio); 2607 classification << " " << numberString; 2608 } 2609 } 2610 title = ""; 2611 title << "[" << classification << "] " << oldTitle; 2612 } 2613 } 2614 SetTitle(title.String()); 2615 } 2616 2617 2618 // 2619 // Open *another* message in the existing mail window. Some code here is 2620 // duplicated from various constructors. 2621 // The duplicated code should be in a private initializer method -- axeld. 2622 // 2623 2624 status_t 2625 TMailWindow::OpenMessage(entry_ref *ref, uint32 characterSetForDecoding) 2626 { 2627 // 2628 // Set some references to the email file 2629 // 2630 if (fRef) 2631 delete fRef; 2632 fRef = new entry_ref(*ref); 2633 2634 if (fStartingText) 2635 { 2636 free(fStartingText); 2637 fStartingText = NULL; 2638 } 2639 fPrevTrackerPositionSaved = false; 2640 fNextTrackerPositionSaved = false; 2641 2642 fContentView->fTextView->StopLoad(); 2643 delete fMail; 2644 2645 BFile file(fRef, B_READ_ONLY); 2646 status_t err = file.InitCheck(); 2647 if (err != B_OK) 2648 return err; 2649 2650 char mimeType[256]; 2651 BNodeInfo fileInfo(&file); 2652 fileInfo.GetType(mimeType); 2653 2654 // Check if it's a draft file, which contains only the text, and has the 2655 // from, to, bcc, attachments listed as attributes. 2656 if (!strcmp(kDraftType, mimeType)) 2657 { 2658 BNode node(fRef); 2659 off_t size; 2660 BString string; 2661 2662 fMail = new BEmailMessage; // Not really used much, but still needed. 2663 2664 // Load the raw UTF-8 text from the file. 2665 file.GetSize(&size); 2666 fContentView->fTextView->SetText(&file, 0, size); 2667 2668 // Restore Fields from attributes 2669 if (ReadAttrString(&node, B_MAIL_ATTR_TO, &string) == B_OK) 2670 fHeaderView->fTo->SetText(string.String()); 2671 if (ReadAttrString(&node, B_MAIL_ATTR_SUBJECT, &string) == B_OK) 2672 fHeaderView->fSubject->SetText(string.String()); 2673 if (ReadAttrString(&node, B_MAIL_ATTR_CC, &string) == B_OK) 2674 fHeaderView->fCc->SetText(string.String()); 2675 if (ReadAttrString(&node, "MAIL:bcc", &string) == B_OK) 2676 fHeaderView->fBcc->SetText(string.String()); 2677 2678 // Restore attachments 2679 if (ReadAttrString(&node, "MAIL:attachments", &string) == B_OK) 2680 { 2681 BMessage msg(REFS_RECEIVED); 2682 entry_ref enc_ref; 2683 2684 char *s = strtok((char *)string.String(), ":"); 2685 while (s) 2686 { 2687 BEntry entry(s, true); 2688 if (entry.Exists()) 2689 { 2690 entry.GetRef(&enc_ref); 2691 msg.AddRef("refs", &enc_ref); 2692 } 2693 s = strtok(NULL, ":"); 2694 } 2695 AddEnclosure(&msg); 2696 } 2697 PostMessage(RESET_BUTTONS); 2698 fIncoming = false; 2699 fDraft = true; 2700 } 2701 else // A real mail message, parse its headers to get from, to, etc. 2702 { 2703 fMail = new BEmailMessage(fRef, characterSetForDecoding); 2704 fIncoming = true; 2705 fHeaderView->LoadMessage(fMail); 2706 } 2707 2708 err = fMail->InitCheck(); 2709 if (err < B_OK) 2710 { 2711 delete fMail; 2712 fMail = NULL; 2713 return err; 2714 } 2715 2716 SetTitleForMessage(); 2717 2718 if (fIncoming) 2719 { 2720 // 2721 // Put the addresses in the 'Save Address' Menu 2722 // 2723 BMenuItem *item; 2724 while ((item = fSaveAddrMenu->RemoveItem(0L)) != NULL) 2725 delete item; 2726 2727 // create the list of addresses 2728 2729 BList addressList; 2730 get_address_list(addressList, fMail->To(), extract_address); 2731 get_address_list(addressList, fMail->CC(), extract_address); 2732 get_address_list(addressList, fMail->From(), extract_address); 2733 get_address_list(addressList, fMail->ReplyTo(), extract_address); 2734 2735 BMessage *msg; 2736 2737 for (int32 i = addressList.CountItems(); i-- > 0;) { 2738 char *address = (char *)addressList.RemoveItem(0L); 2739 2740 // insert the new address in alphabetical order 2741 int32 index = 0; 2742 while ((item = fSaveAddrMenu->ItemAt(index)) != NULL) { 2743 if (!strcmp(address, item->Label())) { 2744 // item already in list 2745 goto skip; 2746 } 2747 2748 if (strcmp(address, item->Label()) < 0) 2749 break; 2750 2751 index++; 2752 } 2753 2754 msg = new BMessage(M_SAVE); 2755 msg->AddString("address", address); 2756 fSaveAddrMenu->AddItem(new BMenuItem(address, msg), index); 2757 2758 skip: 2759 free(address); 2760 } 2761 2762 // 2763 // Clear out existing contents of text view. 2764 // 2765 fContentView->fTextView->SetText("", (int32)0); 2766 2767 fContentView->fTextView->LoadMessage(fMail, false, NULL); 2768 } 2769 2770 return B_OK; 2771 } 2772 2773 2774 TMailWindow * 2775 TMailWindow::FrontmostWindow() 2776 { 2777 BAutolock locker(sWindowListLock); 2778 if (sWindowList.CountItems() > 0) 2779 return (TMailWindow *)sWindowList.ItemAt(0); 2780 2781 return NULL; 2782 } 2783 2784 2785 // #pragma mark - 2786 2787 2788 void 2789 TMailWindow::_UpdateSizeLimits() 2790 { 2791 float minWidth, maxWidth, minHeight, maxHeight; 2792 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 2793 2794 float height; 2795 fMenuBar->GetPreferredSize(&minWidth, &height); 2796 2797 minHeight = height; 2798 2799 if (fButtonBar) { 2800 fButtonBar->GetPreferredSize(&minWidth, &height); 2801 minHeight += height; 2802 } else { 2803 minWidth = WIND_WIDTH; 2804 } 2805 2806 minHeight += fHeaderView->Bounds().Height() + ENCLOSURES_HEIGHT + 60; 2807 2808 SetSizeLimits(minWidth, RIGHT_BOUNDARY, minHeight, RIGHT_BOUNDARY); 2809 } 2810