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