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