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