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