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