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