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