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