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