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