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