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