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; 2177 2178 if ((status = SaveAsDraft()) != B_OK) { 2179 beep(); 2180 (new BAlert("", 2181 MDR_DIALECT_CHOICE ("E-mail draft could not be saved!","ドラフトは保存できませんでした。"), 2182 MDR_DIALECT_CHOICE ("OK","了解")))->Go(); 2183 } 2184 return status; 2185 } 2186 2187 Hide(); 2188 // depending on the system (and I/O) load, this could take a while 2189 // but the user shouldn't be left waiting 2190 2191 if (fHeaderView != NULL) 2192 characterSetToUse = fHeaderView->fCharacterSetUserSees; 2193 2194 // Set up the encoding to use for converting binary to printable ASCII. 2195 // Normally this will be quoted printable, but for some old software, 2196 // particularly Japanese stuff, they only understand base64. They also 2197 // prefer it for the smaller size. Later on this will be reduced to 7bit 2198 // if the encoded text is just 7bit characters. 2199 if (characterSetToUse == B_SJIS_CONVERSION 2200 || characterSetToUse == B_EUC_CONVERSION) 2201 encodingForBody = base64; 2202 else if (characterSetToUse == B_JIS_CONVERSION 2203 || characterSetToUse == B_MAIL_US_ASCII_CONVERSION 2204 || characterSetToUse == B_ISO1_CONVERSION 2205 || characterSetToUse == B_EUC_KR_CONVERSION) 2206 encodingForBody = eight_bit; 2207 2208 // Using quoted printable headers on almost completely non-ASCII Japanese 2209 // is a waste of time. Besides, some stupid cell phone services need 2210 // base64 in the headers. 2211 if (characterSetToUse == B_SJIS_CONVERSION 2212 || characterSetToUse == B_EUC_CONVERSION 2213 || characterSetToUse == B_JIS_CONVERSION 2214 || characterSetToUse == B_EUC_KR_CONVERSION) 2215 encodingForHeaders = base64; 2216 2217 // Count the number of characters in the message body which aren't in the 2218 // currently selected character set. Also see if the resulting encoded 2219 // text can safely use 7 bit characters. 2220 if (fContentView->fTextView->TextLength() > 0) { 2221 // First do a trial encoding with the user's character set. 2222 int32 converterState = 0; 2223 int32 originalLength; 2224 BString tempString; 2225 int32 tempStringLength; 2226 char* tempStringPntr; 2227 originalLength = fContentView->fTextView->TextLength(); 2228 tempStringLength = originalLength * 2229 6 /* Some character sets bloat up on escape codes */; 2230 tempStringPntr = tempString.LockBuffer (tempStringLength); 2231 if (tempStringPntr != NULL && mail_convert_from_utf8(characterSetToUse, 2232 fContentView->fTextView->Text(), &originalLength, 2233 tempStringPntr, &tempStringLength, &converterState, 2234 0x1A /* used for unknown characters */) == B_OK) { 2235 // Check for any characters which don't fit in a 7 bit encoding. 2236 int i; 2237 bool has8Bit = false; 2238 for (i = 0; i < tempStringLength; i++) 2239 if (tempString[i] == 0 || (tempString[i] & 0x80)) { 2240 has8Bit = true; 2241 break; 2242 } 2243 if (!has8Bit) 2244 encodingForBody = seven_bit; 2245 tempString.UnlockBuffer (tempStringLength); 2246 2247 // Count up the number of unencoded characters and warn the user about them. 2248 if (fApp->WarnAboutUnencodableCharacters()) { 2249 int32 offset = 0; 2250 int count = 0; 2251 while (offset >= 0) { 2252 offset = tempString.FindFirst (0x1A, offset); 2253 if (offset >= 0) { 2254 count++; 2255 offset++; // Don't get stuck finding the same character again. 2256 } 2257 } 2258 if (count > 0) { 2259 int32 userAnswer; 2260 BString messageString; 2261 MDR_DIALECT_CHOICE ( 2262 messageString << "Your main text contains " << count << 2263 " unencodable characters. Perhaps a different character " 2264 "set would work better? Hit Send to send it anyway " 2265 "(a substitute character will be used in place of " 2266 "the unencodable ones), or choose Cancel to go back " 2267 "and try fixing it up." 2268 , 2269 messageString << "送信メールの本文には " << count << 2270 " 個のエンコードできない文字があります。" 2271 "違う文字セットを使うほうがよい可能性があります。" 2272 "このまま送信の場合は「送信」ボタンを押してください。" 2273 "その場合、代用文字がUnicode化可能な文字に代わって使われます。" 2274 "文字セットを変更する場合は「中止」ボタンを押して下さい。" 2275 ); 2276 userAnswer = (new BAlert ("Question", messageString.String(), 2277 MDR_DIALECT_CHOICE ("Send","送信"), 2278 MDR_DIALECT_CHOICE ("Cancel","中止"), // Default is cancel. 2279 NULL, B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT)) 2280 ->Go(); 2281 if (userAnswer == 1) 2282 return -1; // Cancel was picked. 2283 } 2284 } 2285 } 2286 } 2287 2288 status_t result; 2289 2290 if (fResending) { 2291 BFile file(fRef, O_RDONLY); 2292 result = file.InitCheck(); 2293 if (result == B_OK) { 2294 BEmailMessage mail(&file); 2295 mail.SetTo(fHeaderView->fTo->Text(), characterSetToUse, encodingForHeaders); 2296 2297 if (fHeaderView->fChain != ~0L) 2298 mail.SendViaAccount(fHeaderView->fChain); 2299 2300 result = mail.Send(now); 2301 } 2302 } else { 2303 if (fMail == NULL) 2304 // the mail will be deleted when the window is closed 2305 fMail = new BEmailMessage; 2306 2307 // Had an embarrassing bug where replying to a message and clearing the 2308 // CC field meant that it got sent out anyway, so pass in empty strings 2309 // when changing the header to force it to remove the header. 2310 2311 fMail->SetTo(fHeaderView->fTo->Text(), characterSetToUse, 2312 encodingForHeaders); 2313 fMail->SetSubject(fHeaderView->fSubject->Text(), characterSetToUse, 2314 encodingForHeaders); 2315 fMail->SetCC(fHeaderView->fCc->Text(), characterSetToUse, 2316 encodingForHeaders); 2317 fMail->SetBCC(fHeaderView->fBcc->Text()); 2318 2319 //--- Add X-Mailer field 2320 { 2321 // get app version 2322 version_info info; 2323 memset(&info, 0, sizeof(version_info)); 2324 2325 app_info appInfo; 2326 if (be_app->GetAppInfo(&appInfo) == B_OK) { 2327 BFile file(&appInfo.ref, B_READ_ONLY); 2328 if (file.InitCheck() == B_OK) { 2329 BAppFileInfo appFileInfo(&file); 2330 if (appFileInfo.InitCheck() == B_OK) 2331 appFileInfo.GetVersionInfo(&info, B_APP_VERSION_KIND); 2332 } 2333 } 2334 2335 char versionString[255]; 2336 sprintf(versionString, 2337 "Mail/Haiku %ld.%ld.%ld", 2338 info.major, info.middle, info.minor); 2339 fMail->SetHeaderField("X-Mailer", versionString); 2340 } 2341 2342 /****/ 2343 2344 // the content text is always added to make sure there is a mail body 2345 fMail->SetBodyTextTo(""); 2346 fContentView->fTextView->AddAsContent(fMail, fApp->WrapMode(), 2347 characterSetToUse, encodingForBody); 2348 2349 if (fEnclosuresView != NULL) { 2350 TListItem *item; 2351 int32 index = 0; 2352 while ((item = (TListItem *)fEnclosuresView->fList->ItemAt(index++)) != NULL) { 2353 if (item->Component()) 2354 continue; 2355 2356 // leave out missing enclosures 2357 BEntry entry(item->Ref()); 2358 if (!entry.Exists()) 2359 continue; 2360 2361 fMail->Attach(item->Ref(), fApp->AttachAttributes()); 2362 } 2363 } 2364 if (fHeaderView->fChain != ~0L) 2365 fMail->SendViaAccount(fHeaderView->fChain); 2366 2367 result = fMail->Send(now); 2368 2369 if (fReplying) { 2370 // Set status of the replied mail 2371 2372 BNode node(&fRepliedMail); 2373 if (node.InitCheck() >= B_OK) { 2374 if (fOriginatingWindow) { 2375 BMessage msg(M_SAVE_POSITION), reply; 2376 fOriginatingWindow->SendMessage(&msg, &reply); 2377 } 2378 WriteAttrString(&node, B_MAIL_ATTR_STATUS, "Replied"); 2379 } 2380 } 2381 } 2382 2383 bool close = false; 2384 char errorMessage[256]; 2385 2386 switch (result) { 2387 case B_NO_ERROR: 2388 close = true; 2389 fSent = true; 2390 2391 // If it's a draft, remove the draft file 2392 if (fDraft) { 2393 BEntry entry(fRef); 2394 entry.Remove(); 2395 } 2396 break; 2397 2398 case B_MAIL_NO_DAEMON: 2399 { 2400 close = true; 2401 fSent = true; 2402 2403 int32 start = (new BAlert("no daemon", 2404 MDR_DIALECT_CHOICE ("The mail_daemon is not running. " 2405 "The message is queued and will be sent when the mail_daemon is started.", 2406 "mail_daemon が開始されていません " 2407 "このメッセージは処理待ちとなり、mail_daemon 開始後に処理されます"), 2408 MDR_DIALECT_CHOICE ("Start Now","ただちに開始する"), 2409 MDR_DIALECT_CHOICE ("Ok","了解")))->Go(); 2410 2411 if (start == 0) { 2412 result = be_roster->Launch("application/x-vnd.Be-POST"); 2413 if (result == B_OK) 2414 BMailDaemon::SendQueuedMail(); 2415 else { 2416 sprintf(errorMessage,"The mail_daemon could not be started:\n\t%s", 2417 strerror(result)); 2418 } 2419 } 2420 break; 2421 } 2422 2423 // case B_MAIL_UNKNOWN_HOST: 2424 // case B_MAIL_ACCESS_ERROR: 2425 // sprintf(errorMessage, "An error occurred trying to connect with the SMTP " 2426 // "host. Check your SMTP host name."); 2427 // break; 2428 // 2429 // case B_MAIL_NO_RECIPIENT: 2430 // sprintf(errorMessage, "You must have either a \"To\" or \"Bcc\" recipient."); 2431 // break; 2432 2433 default: 2434 sprintf(errorMessage, "An error occurred trying to send mail:\n\t%s", 2435 strerror(result)); 2436 break; 2437 } 2438 2439 if (result != B_NO_ERROR && result != B_MAIL_NO_DAEMON) { 2440 beep(); 2441 (new BAlert("", errorMessage, "Ok"))->Go(); 2442 } 2443 if (close) 2444 PostMessage(B_QUIT_REQUESTED); 2445 2446 return result; 2447 } 2448 2449 2450 status_t 2451 TMailWindow::SaveAsDraft() 2452 { 2453 status_t status; 2454 BPath draftPath; 2455 BDirectory dir; 2456 BFile draft; 2457 uint32 flags = 0; 2458 2459 if (fDraft) { 2460 if ((status = draft.SetTo(fRef, B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE)) != B_OK) 2461 return status; 2462 } else { 2463 // Get the user home directory 2464 if ((status = find_directory(B_USER_DIRECTORY, &draftPath)) != B_OK) 2465 return status; 2466 2467 // Append the relative path of the draft directory 2468 draftPath.Append(kDraftPath); 2469 2470 // Create the file 2471 status = dir.SetTo(draftPath.Path()); 2472 switch (status) { 2473 // Create the directory if it does not exist 2474 case B_ENTRY_NOT_FOUND: 2475 if ((status = dir.CreateDirectory(draftPath.Path(), &dir)) != B_OK) 2476 return status; 2477 case B_OK: 2478 { 2479 char fileName[512], *eofn; 2480 int32 i; 2481 2482 // save as some version of the message's subject 2483 strncpy(fileName, fHeaderView->fSubject->Text(), sizeof(fileName)-10); 2484 fileName[sizeof(fileName)-10]='\0'; // terminate like strncpy doesn't 2485 eofn = fileName + strlen(fileName); 2486 2487 // convert /, \ and : to - 2488 for (char *bad = fileName; (bad = strchr(bad, '/')) != NULL; ++bad) *bad = '-'; 2489 for (char *bad = fileName; (bad = strchr(bad, '\\')) != NULL;++bad) *bad = '-'; 2490 for (char *bad = fileName; (bad = strchr(bad, ':')) != NULL; ++bad) *bad = '-'; 2491 2492 // Create the file; if the name exists, find a unique name 2493 flags = B_WRITE_ONLY | B_CREATE_FILE | B_FAIL_IF_EXISTS; 2494 for (i = 1; (status = draft.SetTo(&dir, fileName, flags )) != B_OK; i++) { 2495 if( status != B_FILE_EXISTS ) 2496 return status; 2497 sprintf(eofn, "%ld", i ); 2498 } 2499 2500 // Cache the ref 2501 delete fRef; 2502 BEntry entry(&dir, fileName); 2503 fRef = new entry_ref; 2504 entry.GetRef(fRef); 2505 break; 2506 } 2507 default: 2508 return status; 2509 } 2510 } 2511 2512 // Write the content of the message 2513 draft.Write(fContentView->fTextView->Text(), fContentView->fTextView->TextLength()); 2514 2515 // 2516 // Add the header stuff as attributes 2517 // 2518 WriteAttrString(&draft, B_MAIL_ATTR_NAME, fHeaderView->fTo->Text()); 2519 WriteAttrString(&draft, B_MAIL_ATTR_TO, fHeaderView->fTo->Text()); 2520 WriteAttrString(&draft, B_MAIL_ATTR_SUBJECT, fHeaderView->fSubject->Text()); 2521 if (fHeaderView->fCc != NULL) 2522 WriteAttrString(&draft, B_MAIL_ATTR_CC, fHeaderView->fCc->Text()); 2523 if (fHeaderView->fBcc != NULL) 2524 WriteAttrString(&draft, "MAIL:bcc", fHeaderView->fBcc->Text()); 2525 2526 // Add the draft attribute for indexing 2527 uint32 draftAttr = true; 2528 draft.WriteAttr( "MAIL:draft", B_INT32_TYPE, 0, &draftAttr, sizeof(uint32) ); 2529 2530 // Add Attachment paths in attribute 2531 if (fEnclosuresView != NULL) { 2532 TListItem *item; 2533 BPath path; 2534 BString pathStr; 2535 2536 for (int32 i = 0; (item = (TListItem *)fEnclosuresView->fList->ItemAt(i)) != NULL; i++) { 2537 if (i > 0) 2538 pathStr.Append(":"); 2539 2540 BEntry entry(item->Ref(), true); 2541 if (!entry.Exists()) 2542 continue; 2543 2544 entry.GetPath(&path); 2545 pathStr.Append(path.Path()); 2546 } 2547 if (pathStr.Length()) 2548 WriteAttrString(&draft, "MAIL:attachments", pathStr.String()); 2549 } 2550 2551 // Set the MIME Type of the file 2552 BNodeInfo info(&draft); 2553 info.SetType(kDraftType); 2554 2555 fSent = true; 2556 fDraft = true; 2557 fChanged = false; 2558 2559 return B_OK; 2560 } 2561 2562 2563 status_t 2564 TMailWindow::TrainMessageAs(const char *CommandWord) 2565 { 2566 status_t errorCode = -1; 2567 char errorString[1500]; 2568 BEntry fileEntry; 2569 BPath filePath; 2570 BMessage replyMessage; 2571 BMessage scriptingMessage; 2572 team_id serverTeam; 2573 2574 if (fRef == NULL) 2575 goto ErrorExit; // Need to have a real file and name. 2576 errorCode = fileEntry.SetTo(fRef, true /* traverse */); 2577 if (errorCode != B_OK) 2578 goto ErrorExit; 2579 errorCode = fileEntry.GetPath(&filePath); 2580 if (errorCode != B_OK) 2581 goto ErrorExit; 2582 fileEntry.Unset(); 2583 2584 // Get a connection to the spam database server. Launch if needed. 2585 2586 if (!fMessengerToSpamServer.IsValid()) { 2587 // Make sure the server is running. 2588 if (!be_roster->IsRunning (kSpamServerSignature)) { 2589 errorCode = be_roster->Launch (kSpamServerSignature); 2590 if (errorCode != B_OK) { 2591 BPath path; 2592 entry_ref ref; 2593 directory_which places[] = {B_COMMON_BIN_DIRECTORY,B_BEOS_BIN_DIRECTORY}; 2594 for (int32 i = 0; i < 2; i++) { 2595 find_directory(places[i],&path); 2596 path.Append("spamdbm"); 2597 if (!BEntry(path.Path()).Exists()) 2598 continue; 2599 get_ref_for_path(path.Path(),&ref); 2600 if ((errorCode = be_roster->Launch (&ref)) == B_OK) 2601 break; 2602 } 2603 if (errorCode != B_OK) 2604 goto ErrorExit; 2605 } 2606 } 2607 2608 // Set up the messenger to the database server. 2609 errorCode = B_SERVER_NOT_FOUND; 2610 serverTeam = be_roster->TeamFor(kSpamServerSignature); 2611 if (serverTeam < 0) 2612 goto ErrorExit; 2613 2614 fMessengerToSpamServer = BMessenger (kSpamServerSignature, serverTeam, &errorCode); 2615 if (!fMessengerToSpamServer.IsValid()) 2616 goto ErrorExit; 2617 } 2618 2619 // Ask the server to train on the message. Give it the command word and 2620 // the absolute path name to use. 2621 2622 scriptingMessage.MakeEmpty(); 2623 scriptingMessage.what = B_SET_PROPERTY; 2624 scriptingMessage.AddSpecifier(CommandWord); 2625 errorCode = scriptingMessage.AddData("data", B_STRING_TYPE, 2626 filePath.Path(), strlen(filePath.Path()) + 1, false /* fixed size */); 2627 if (errorCode != B_OK) 2628 goto ErrorExit; 2629 replyMessage.MakeEmpty(); 2630 errorCode = fMessengerToSpamServer.SendMessage(&scriptingMessage, 2631 &replyMessage); 2632 if (errorCode != B_OK 2633 || replyMessage.FindInt32("error", &errorCode) != B_OK 2634 || errorCode != B_OK) 2635 goto ErrorExit; // Classification failed in one of many ways. 2636 2637 SetTitleForMessage(); // Update window title to show new spam classification. 2638 return B_OK; 2639 2640 ErrorExit: 2641 beep(); 2642 sprintf(errorString, "Unable to train the message file \"%s\" as %s. " 2643 "Possibly useful error code: %s (%ld).", 2644 filePath.Path(), CommandWord, strerror (errorCode), errorCode); 2645 (new BAlert("", errorString, 2646 MDR_DIALECT_CHOICE("OK","了解")))->Go(); 2647 return errorCode; 2648 } 2649 2650 2651 void 2652 TMailWindow::SetTitleForMessage() 2653 { 2654 // 2655 // Figure out the title of this message and set the title bar 2656 // 2657 BString title = "Mail"; 2658 2659 if (fIncoming) { 2660 if (fMail->GetName(&title) == B_OK) 2661 title << ": \"" << fMail->Subject() << "\""; 2662 else 2663 title = fMail->Subject(); 2664 2665 if (fApp->ShowSpamGUI() && fRef != NULL) { 2666 BString classification; 2667 BNode node (fRef); 2668 char numberString [30]; 2669 BString oldTitle (title); 2670 float spamRatio; 2671 if (node.InitCheck() != B_OK || node.ReadAttrString 2672 ("MAIL:classification", &classification) != B_OK) 2673 classification = "Unrated"; 2674 if (classification != "Spam" && classification != "Genuine") { 2675 // Uncertain, Unrated and other unknown classes, show the ratio. 2676 if (node.InitCheck() == B_OK && sizeof (spamRatio) == 2677 node.ReadAttr("MAIL:ratio_spam", B_FLOAT_TYPE, 0, 2678 &spamRatio, sizeof (spamRatio))) { 2679 sprintf (numberString, "%.4f", spamRatio); 2680 classification << " " << numberString; 2681 } 2682 } 2683 title = ""; 2684 title << "[" << classification << "] " << oldTitle; 2685 } 2686 } 2687 SetTitle(title.String()); 2688 } 2689 2690 2691 // 2692 // Open *another* message in the existing mail window. Some code here is 2693 // duplicated from various constructors. 2694 // The duplicated code should be in a private initializer method -- axeld. 2695 // 2696 2697 status_t 2698 TMailWindow::OpenMessage(entry_ref *ref, uint32 characterSetForDecoding) 2699 { 2700 // 2701 // Set some references to the email file 2702 // 2703 if (fRef) 2704 delete fRef; 2705 fRef = new entry_ref(*ref); 2706 2707 if (fStartingText) 2708 { 2709 free(fStartingText); 2710 fStartingText = NULL; 2711 } 2712 fPrevTrackerPositionSaved = false; 2713 fNextTrackerPositionSaved = false; 2714 2715 fContentView->fTextView->StopLoad(); 2716 delete fMail; 2717 2718 BFile file(fRef, B_READ_ONLY); 2719 status_t err = file.InitCheck(); 2720 if (err != B_OK) 2721 return err; 2722 2723 char mimeType[256]; 2724 BNodeInfo fileInfo(&file); 2725 fileInfo.GetType(mimeType); 2726 2727 // Check if it's a draft file, which contains only the text, and has the 2728 // from, to, bcc, attachments listed as attributes. 2729 if (!strcmp(kDraftType, mimeType)) 2730 { 2731 BNode node(fRef); 2732 off_t size; 2733 BString string; 2734 2735 fMail = new BEmailMessage; // Not really used much, but still needed. 2736 2737 // Load the raw UTF-8 text from the file. 2738 file.GetSize(&size); 2739 fContentView->fTextView->SetText(&file, 0, size); 2740 2741 // Restore Fields from attributes 2742 if (ReadAttrString(&node, B_MAIL_ATTR_TO, &string) == B_OK) 2743 fHeaderView->fTo->SetText(string.String()); 2744 if (ReadAttrString(&node, B_MAIL_ATTR_SUBJECT, &string) == B_OK) 2745 fHeaderView->fSubject->SetText(string.String()); 2746 if (ReadAttrString(&node, B_MAIL_ATTR_CC, &string) == B_OK) 2747 fHeaderView->fCc->SetText(string.String()); 2748 if (ReadAttrString(&node, "MAIL:bcc", &string) == B_OK) 2749 fHeaderView->fBcc->SetText(string.String()); 2750 2751 // Restore attachments 2752 if (ReadAttrString(&node, "MAIL:attachments", &string) == B_OK) 2753 { 2754 BMessage msg(REFS_RECEIVED); 2755 entry_ref enc_ref; 2756 2757 char *s = strtok((char *)string.String(), ":"); 2758 while (s) 2759 { 2760 BEntry entry(s, true); 2761 if (entry.Exists()) 2762 { 2763 entry.GetRef(&enc_ref); 2764 msg.AddRef("refs", &enc_ref); 2765 } 2766 s = strtok(NULL, ":"); 2767 } 2768 AddEnclosure(&msg); 2769 } 2770 PostMessage(RESET_BUTTONS); 2771 fIncoming = false; 2772 fDraft = true; 2773 } 2774 else // A real mail message, parse its headers to get from, to, etc. 2775 { 2776 fMail = new BEmailMessage(fRef, characterSetForDecoding); 2777 fIncoming = true; 2778 fHeaderView->LoadMessage(fMail); 2779 } 2780 2781 err = fMail->InitCheck(); 2782 if (err < B_OK) 2783 { 2784 delete fMail; 2785 fMail = NULL; 2786 return err; 2787 } 2788 2789 SetTitleForMessage(); 2790 2791 if (fIncoming) 2792 { 2793 // 2794 // Put the addresses in the 'Save Address' Menu 2795 // 2796 BMenuItem *item; 2797 while ((item = fSaveAddrMenu->RemoveItem(0L)) != NULL) 2798 delete item; 2799 2800 // create the list of addresses 2801 2802 BList addressList; 2803 get_address_list(addressList, fMail->To(), extract_address); 2804 get_address_list(addressList, fMail->CC(), extract_address); 2805 get_address_list(addressList, fMail->From(), extract_address); 2806 get_address_list(addressList, fMail->ReplyTo(), extract_address); 2807 2808 BMessage *msg; 2809 2810 for (int32 i = addressList.CountItems(); i-- > 0;) { 2811 char *address = (char *)addressList.RemoveItem(0L); 2812 2813 // insert the new address in alphabetical order 2814 int32 index = 0; 2815 while ((item = fSaveAddrMenu->ItemAt(index)) != NULL) { 2816 if (!strcmp(address, item->Label())) { 2817 // item already in list 2818 goto skip; 2819 } 2820 2821 if (strcmp(address, item->Label()) < 0) 2822 break; 2823 2824 index++; 2825 } 2826 2827 msg = new BMessage(M_SAVE); 2828 msg->AddString("address", address); 2829 fSaveAddrMenu->AddItem(new BMenuItem(address, msg), index); 2830 2831 skip: 2832 free(address); 2833 } 2834 2835 // 2836 // Clear out existing contents of text view. 2837 // 2838 fContentView->fTextView->SetText("", (int32)0); 2839 2840 fContentView->fTextView->LoadMessage(fMail, false, NULL); 2841 } 2842 2843 return B_OK; 2844 } 2845 2846 2847 TMailWindow * 2848 TMailWindow::FrontmostWindow() 2849 { 2850 BAutolock locker(sWindowListLock); 2851 if (sWindowList.CountItems() > 0) 2852 return (TMailWindow *)sWindowList.ItemAt(0); 2853 2854 return NULL; 2855 } 2856 2857 2858 /* 2859 // Copied from src/kits/tracker/FindPanel.cpp. 2860 uint32 2861 TMailWindow::InitialMode(const BNode *node) 2862 { 2863 if (!node || node->InitCheck() != B_OK) 2864 return kByNameItem; 2865 2866 uint32 result; 2867 if (node->ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, 2868 (int32 *)&result, sizeof(int32)) <= 0) 2869 return kByNameItem; 2870 2871 return result; 2872 } 2873 2874 2875 // Copied from src/kits/tracker/FindPanel.cpp. 2876 int32 2877 TMailWindow::InitialAttrCount(const BNode *node) 2878 { 2879 if (!node || node->InitCheck() != B_OK) 2880 return 1; 2881 2882 int32 result; 2883 if (node->ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 2884 &result, sizeof(int32)) <= 0) 2885 return 1; 2886 2887 return result; 2888 }*/ 2889 2890 2891 // #pragma mark - 2892 2893 2894 void 2895 TMailWindow::_UpdateSizeLimits() 2896 { 2897 float minWidth, maxWidth, minHeight, maxHeight; 2898 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); 2899 2900 float height; 2901 fMenuBar->GetPreferredSize(&minWidth, &height); 2902 2903 minHeight = height; 2904 2905 if (fButtonBar) { 2906 fButtonBar->GetPreferredSize(&minWidth, &height); 2907 minHeight += height; 2908 } else { 2909 minWidth = WIND_WIDTH; 2910 } 2911 2912 minHeight += fHeaderView->Bounds().Height() + ENCLOSURES_HEIGHT + 60; 2913 2914 SetSizeLimits(minWidth, RIGHT_BOUNDARY, minHeight, RIGHT_BOUNDARY); 2915 } 2916 2917 2918 status_t 2919 TMailWindow::_GetQueryPath(BPath* queryPath) const 2920 { 2921 // get the user home directory and from there the query folder 2922 status_t ret = find_directory(B_USER_DIRECTORY, queryPath); 2923 if (ret == B_OK) 2924 ret = queryPath->Append(kQueriesDirectory); 2925 2926 return ret; 2927 } 2928 2929 2930 void 2931 TMailWindow::_RebuildQueryMenu(bool firstTime) 2932 { 2933 while (fQueryMenu->ItemAt(0)) { 2934 BMenuItem* item = fQueryMenu->RemoveItem((int32)0); 2935 delete item; 2936 } 2937 2938 fQueryMenu->AddItem(new BMenuItem(MDR_DIALECT_CHOICE("Edit Queries" B_UTF8_ELLIPSIS,"???" B_UTF8_ELLIPSIS), 2939 new BMessage(M_EDIT_QUERIES), 'E', B_SHIFT_KEY)); 2940 2941 bool queryItemsAdded = false; 2942 2943 BPath queryPath; 2944 if (_GetQueryPath(&queryPath) < B_OK) 2945 return; 2946 2947 BDirectory queryDir(queryPath.Path()); 2948 2949 if (firstTime) { 2950 #ifdef __HAIKU__ 2951 BPrivate::BPathMonitor::StartWatching(queryPath.Path(), 2952 B_WATCH_RECURSIVELY, BMessenger(this, this)); 2953 #endif 2954 } 2955 2956 // If we find the named query, add it to the menu. 2957 BEntry entry; 2958 while (queryDir.GetNextEntry(&entry) == B_OK) { 2959 char name[B_FILE_NAME_LENGTH + 1]; 2960 entry.GetName(name); 2961 2962 char* queryString = _BuildQueryString(&entry); 2963 if (queryString == NULL) 2964 continue; 2965 2966 queryItemsAdded = true; 2967 2968 QueryMenu* queryMenu = new QueryMenu(name, false); 2969 queryMenu->SetTargetForItems(be_app); 2970 queryMenu->SetPredicate(queryString); 2971 fQueryMenu->AddItem(queryMenu); 2972 2973 free(queryString); 2974 } 2975 2976 if (queryItemsAdded) 2977 fQueryMenu->AddItem(new BSeparatorItem(), 1); 2978 } 2979 2980 2981 char* 2982 TMailWindow::_BuildQueryString(BEntry* entry) const 2983 { 2984 BNode node(entry); 2985 if (node.InitCheck() != B_OK) 2986 return NULL; 2987 2988 uint32 mode; 2989 if (node.ReadAttr(kAttrQueryInitialMode, B_INT32_TYPE, 0, (int32*)&mode, 2990 sizeof(int32)) <= 0) { 2991 mode = kByNameItem; 2992 } 2993 2994 BString queryString; 2995 switch (mode) { 2996 case kByForumlaItem: 2997 { 2998 BString buffer; 2999 if (node.ReadAttrString(kAttrQueryInitialString, &buffer) == B_OK) 3000 queryString << buffer; 3001 break; 3002 } 3003 3004 case kByNameItem: 3005 { 3006 BString buffer; 3007 if (node.ReadAttrString(kAttrQueryInitialString, &buffer) == B_OK) 3008 queryString << "(name==*" << buffer << "*)"; 3009 break; 3010 } 3011 3012 case kByAttributeItem: 3013 { 3014 int32 count = 1; 3015 if (node.ReadAttr(kAttrQueryInitialNumAttrs, B_INT32_TYPE, 0, 3016 (int32 *)&mode, sizeof(int32)) <= 0) { 3017 count = 1; 3018 } 3019 3020 attr_info info; 3021 if (node.GetAttrInfo(kAttrQueryInitialAttrs, &info) != B_OK) 3022 break; 3023 3024 if (count > 1 ) 3025 queryString << "("; 3026 3027 char *buffer = new char[info.size]; 3028 if (node.ReadAttr(kAttrQueryInitialAttrs, B_MESSAGE_TYPE, 0, 3029 buffer, (size_t)info.size) == info.size) { 3030 BMessage message; 3031 if (message.Unflatten(buffer) == B_OK) { 3032 for (int32 index = 0; /*index < count*/; index++) { 3033 const char *field; 3034 const char *value; 3035 if (message.FindString("menuSelection", index, &field) 3036 != B_OK 3037 || message.FindString("attrViewText", index, &value) 3038 != B_OK) { 3039 break; 3040 } 3041 3042 // ignore the mime type, we'll force it to be email 3043 // later 3044 if (strcmp(field, "BEOS:TYPE") != 0) { 3045 // TODO: check if subMenu contains the type of 3046 // comparison we are suppose to make here 3047 queryString << "(" << field << "==\"" 3048 << value << "\")"; 3049 3050 int32 logicMenuSelectedIndex; 3051 if (message.FindInt32("logicalRelation", index, 3052 &logicMenuSelectedIndex) == B_OK) { 3053 if (logicMenuSelectedIndex == 0) 3054 queryString << "&&"; 3055 else if (logicMenuSelectedIndex == 1) 3056 queryString << "||"; 3057 } else 3058 break; 3059 } 3060 } 3061 } 3062 } 3063 3064 if (count > 1 ) 3065 queryString << ")"; 3066 3067 delete [] buffer; 3068 break; 3069 } 3070 3071 default: 3072 break; 3073 } 3074 3075 if (queryString.Length() == 0) 3076 return NULL; 3077 3078 // force it to check for email only 3079 if (queryString.FindFirst("text/x-email") < 0 ) { 3080 BString temp; 3081 temp << "(" << queryString << "&&(BEOS:TYPE==\"text/x-email\"))"; 3082 queryString = temp; 3083 } 3084 3085 return strdup(queryString.String()); 3086 } 3087 3088