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