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