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