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