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