6 Copyright (c) 1991-2001, Be Incorporated. All rights reserved.
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:
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.
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
23 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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.
29 BeMail(TM), Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or
30 registered trademarks of Be Incorporated in the United States and other
31 countries. Other brand product names are registered trademarks or trademarks
32 of their respective holders. All rights reserved.
36 #include "MailWindow.h"
43 #include <sys/utsname.h>
46 #include <AppFileInfo.h>
50 #include <CharacterSet.h>
51 #include <CharacterSetRoster.h>
52 #include <Clipboard.h>
56 #include <IconUtils.h>
57 #include <LayoutBuilder.h>
60 #include <PathMonitor.h>
63 #include <Resources.h>
67 #include <StringView.h>
70 #include <VolumeRoster.h>
75 #include <MailMessage.h>
76 #include <MailSettings.h>
77 #include <MailDaemon.h>
78 #include <mail_util.h>
80 #include <CharacterSetRoster.h>
83 #include "Enclosures.h"
85 #include "FindWindow.h"
89 #include "MailPopUpMenu.h"
90 #include "MailSupport.h"
92 #include "QueryMenu.h"
93 #include "Signature.h"
97 #include "Utilities.h"
100 #define B_TRANSLATION_CONTEXT "Mail"
103 using namespace BPrivate
;
106 const char* kUndoStrings
[] = {
115 const char* kRedoStrings
[] = {
125 // Text for both the main menu and the pop-up menu.
126 static const char* kSpamMenuItemTextArray
[] = {
127 "Mark as spam and move to trash", // M_TRAIN_SPAM_AND_DELETE
128 "Mark as spam", // M_TRAIN_SPAM
129 "Unmark this message", // M_UNTRAIN
130 "Mark as genuine" // M_TRAIN_GENUINE
133 static const uint32 kMsgQuitAndKeepAllStatus
= 'Casm';
135 static const char* kQueriesDirectory
= "mail/queries";
136 static const char* kAttrQueryInitialMode
= "_trk/qryinitmode";
137 // taken from src/kits/tracker/Attributes.h
138 static const char* kAttrQueryInitialString
= "_trk/qryinitstr";
139 static const char* kAttrQueryInitialNumAttrs
= "_trk/qryinitnumattrs";
140 static const char* kAttrQueryInitialAttrs
= "_trk/qryinitattrs";
141 static const uint32 kAttributeItemMain
= 'Fatr';
142 // taken from src/kits/tracker/FindPanel.h
143 static const uint32 kByNameItem
= 'Fbyn';
144 // taken from src/kits/tracker/FindPanel.h
145 static const uint32 kByAttributeItem
= 'Fbya';
146 // taken from src/kits/tracker/FindPanel.h
147 static const uint32 kByForumlaItem
= 'Fbyq';
148 // taken from src/kits/tracker/FindPanel.h
151 // static bitmap cache
152 BObjectList
<TMailWindow::BitmapItem
> TMailWindow::sBitmapCache
;
153 BLocker
TMailWindow::sBitmapCacheLock
;
155 // static list for tracking of Windows
156 BList
TMailWindow::sWindowList
;
157 BLocker
TMailWindow::sWindowListLock
;
160 class HorizontalLine
: public BView
{
162 HorizontalLine(BRect rect
)
164 BView (rect
, NULL
, B_FOLLOW_ALL
, B_WILL_DRAW
)
168 virtual void Draw(BRect rect
)
170 FillRect(rect
, B_SOLID_HIGH
);
178 TMailWindow::TMailWindow(BRect rect
, const char* title
, TMailApp
* app
,
179 const entry_ref
* ref
, const char* to
, const BFont
* font
, bool resending
,
180 BMessenger
* messenger
)
182 BWindow(rect
, title
, B_DOCUMENT_WINDOW
, B_AUTO_UPDATE_SIZE_LIMITS
),
190 fLeaveStatusMenu(NULL
),
193 fEnclosuresView(NULL
),
194 fPrevTrackerPositionSaved(false),
195 fNextTrackerPositionSaved(false),
198 fResending(resending
),
202 fOriginatingWindow(NULL
),
206 fKeepStatusOnQuit
= false;
208 if (messenger
!= NULL
)
209 fTrackerMessenger
= *messenger
;
211 BFile
file(ref
, B_READ_ONLY
);
213 fRef
= new entry_ref(*ref
);
218 fAutoMarkRead
= fApp
->AutoMarkRead();
219 fMenuBar
= new BMenuBar("menuBar");
223 BMenu
* menu
= new BMenu(B_TRANSLATE("File"));
225 BMessage
* msg
= new BMessage(M_NEW
);
226 msg
->AddInt32("type", M_NEW
);
227 BMenuItem
* item
= new BMenuItem(B_TRANSLATE("New mail message"), msg
, 'N');
229 item
->SetTarget(be_app
);
231 // Cheap hack - only show the drafts menu when composing messages. Insert
232 // a "true || " in the following IF statement if you want the old BeMail
233 // behaviour. The difference is that without live draft menu updating you
234 // can open around 100 e-mails (the BeOS maximum number of open files)
235 // rather than merely around 20, since each open draft-monitoring query
236 // sucks up one file handle per mounted BFS disk volume. Plus mail file
237 // opening speed is noticably improved! ToDo: change this to populate the
238 // Draft menu with the file names on demand - when the user clicks on it;
239 // don't need a live query since the menu isn't staying up for more than a
243 QueryMenu
* queryMenu
= new QueryMenu(B_TRANSLATE("Open draft"), false);
244 queryMenu
->SetTargetForItems(be_app
);
246 queryMenu
->SetPredicate("MAIL:draft==1");
247 menu
->AddItem(queryMenu
);
250 if (!fIncoming
|| resending
) {
251 menu
->AddItem(fSendLater
= new BMenuItem(B_TRANSLATE("Save as draft"),
252 new BMessage(M_SAVE_AS_DRAFT
), 'S'));
255 if (!resending
&& fIncoming
) {
256 menu
->AddSeparatorItem();
258 BMenu
* subMenu
= new BMenu(B_TRANSLATE("Close and "));
261 read_read_attr(file
, flag
);
263 if (flag
== B_UNREAD
) {
264 subMenu
->AddItem(item
= new BMenuItem(
265 B_TRANSLATE_COMMENT("Leave as 'New'",
266 "Do not translate New - this is non-localizable e-mail status"),
267 new BMessage(kMsgQuitAndKeepAllStatus
), 'W', B_SHIFT_KEY
));
270 file
.ReadAttrString(B_MAIL_ATTR_STATUS
, &status
);
273 if (status
.Length() > 0)
274 label
.SetToFormat(B_TRANSLATE("Leave as '%s'"),
277 label
= B_TRANSLATE("Leave same");
279 subMenu
->AddItem(item
= new BMenuItem(label
.String(),
280 new BMessage(B_QUIT_REQUESTED
), 'W'));
281 AddShortcut('W', B_COMMAND_KEY
| B_SHIFT_KEY
,
282 new BMessage(kMsgQuitAndKeepAllStatus
));
285 subMenu
->AddItem(new BMenuItem(B_TRANSLATE("Move to trash"),
286 new BMessage(M_DELETE
), 'T', B_CONTROL_KEY
));
287 AddShortcut('T', B_SHIFT_KEY
| B_COMMAND_KEY
,
288 new BMessage(M_DELETE_NEXT
));
290 subMenu
->AddSeparatorItem();
292 subMenu
->AddItem(new BMenuItem(B_TRANSLATE("Set to Saved"),
293 new BMessage(M_CLOSE_SAVED
), 'W', B_CONTROL_KEY
));
295 if (add_query_menu_items(subMenu
, INDEX_STATUS
, M_STATUS
,
296 B_TRANSLATE("Set to %s")) > 0)
297 subMenu
->AddSeparatorItem();
299 subMenu
->AddItem(new BMenuItem(B_TRANSLATE("Set to" B_UTF8_ELLIPSIS
),
300 new BMessage(M_CLOSE_CUSTOM
)));
303 subMenu
->AddItem(new BMenuItem(new TMenu(
304 B_TRANSLATE("Set to" B_UTF8_ELLIPSIS
), INDEX_STATUS
, M_STATUS
,
306 new BMessage(M_CLOSE_CUSTOM
)));
308 menu
->AddItem(subMenu
);
310 fLeaveStatusMenu
= subMenu
;
312 menu
->AddSeparatorItem();
313 menu
->AddItem(new BMenuItem(B_TRANSLATE("Close"),
314 new BMessage(B_CLOSE_REQUESTED
), 'W'));
317 menu
->AddSeparatorItem();
318 menu
->AddItem(fPrint
= new BMenuItem(
319 B_TRANSLATE("Page setup" B_UTF8_ELLIPSIS
),
320 new BMessage(M_PRINT_SETUP
)));
321 menu
->AddItem(fPrint
= new BMenuItem(
322 B_TRANSLATE("Print" B_UTF8_ELLIPSIS
),
323 new BMessage(M_PRINT
), 'P'));
324 fMenuBar
->AddItem(menu
);
326 menu
->AddSeparatorItem();
327 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Quit"),
328 new BMessage(B_QUIT_REQUESTED
), 'Q'));
329 item
->SetTarget(be_app
);
333 menu
= new BMenu(B_TRANSLATE("Edit"));
334 menu
->AddItem(fUndo
= new BMenuItem(B_TRANSLATE("Undo"),
335 new BMessage(B_UNDO
), 'Z', 0));
336 fUndo
->SetTarget(NULL
, this);
337 menu
->AddItem(fRedo
= new BMenuItem(B_TRANSLATE("Redo"),
338 new BMessage(M_REDO
), 'Z', B_SHIFT_KEY
));
339 fRedo
->SetTarget(NULL
, this);
340 menu
->AddSeparatorItem();
341 menu
->AddItem(fCut
= new BMenuItem(B_TRANSLATE("Cut"),
342 new BMessage(B_CUT
), 'X'));
343 fCut
->SetTarget(NULL
, this);
344 menu
->AddItem(fCopy
= new BMenuItem(B_TRANSLATE("Copy"),
345 new BMessage(B_COPY
), 'C'));
346 fCopy
->SetTarget(NULL
, this);
347 menu
->AddItem(fPaste
= new BMenuItem(B_TRANSLATE("Paste"),
348 new BMessage(B_PASTE
),
350 fPaste
->SetTarget(NULL
, this);
351 menu
->AddSeparatorItem();
352 menu
->AddItem(item
= new BMenuItem(B_TRANSLATE("Select all"),
353 new BMessage(M_SELECT
), 'A'));
354 menu
->AddSeparatorItem();
355 item
->SetTarget(NULL
, this);
356 menu
->AddItem(new BMenuItem(B_TRANSLATE("Find" B_UTF8_ELLIPSIS
),
357 new BMessage(M_FIND
), 'F'));
358 menu
->AddItem(new BMenuItem(B_TRANSLATE("Find again"),
359 new BMessage(M_FIND_AGAIN
), 'G'));
361 menu
->AddSeparatorItem();
362 fQuote
= new BMenuItem(B_TRANSLATE("Quote"),
363 new BMessage(M_QUOTE
), '\'');
364 menu
->AddItem(fQuote
);
365 fRemoveQuote
= new BMenuItem(B_TRANSLATE("Remove quote"),
366 new BMessage(M_REMOVE_QUOTE
), '\'', B_SHIFT_KEY
);
367 menu
->AddItem(fRemoveQuote
);
369 menu
->AddSeparatorItem();
370 fSpelling
= new BMenuItem(B_TRANSLATE("Check spelling"),
371 new BMessage(M_CHECK_SPELLING
), ';');
372 menu
->AddItem(fSpelling
);
373 if (fApp
->StartWithSpellCheckOn())
374 PostMessage(M_CHECK_SPELLING
);
376 menu
->AddSeparatorItem();
377 menu
->AddItem(item
= new BMenuItem(
378 B_TRANSLATE("Settings" B_UTF8_ELLIPSIS
),
379 new BMessage(M_PREFS
),','));
380 item
->SetTarget(be_app
);
381 fMenuBar
->AddItem(menu
);
382 menu
->AddItem(item
= new BMenuItem(
383 B_TRANSLATE("Accounts" B_UTF8_ELLIPSIS
),
384 new BMessage(M_ACCOUNTS
),'-'));
385 item
->SetTarget(be_app
);
389 if (!resending
&& fIncoming
) {
390 menu
= new BMenu(B_TRANSLATE("View"));
391 menu
->AddItem(fHeader
= new BMenuItem(B_TRANSLATE("Show header"),
392 new BMessage(M_HEADER
), 'H'));
393 menu
->AddItem(fRaw
= new BMenuItem(B_TRANSLATE("Show raw message"),
394 new BMessage(M_RAW
)));
395 fMenuBar
->AddItem(menu
);
400 menu
= new BMenu(B_TRANSLATE("Message"));
402 if (!resending
&& fIncoming
) {
404 menu
->AddItem(new BMenuItem(B_TRANSLATE("Reply"),
405 new BMessage(M_REPLY
),'R'));
406 menu
->AddItem(new BMenuItem(B_TRANSLATE("Reply to sender"),
407 new BMessage(M_REPLY_TO_SENDER
),'R',B_OPTION_KEY
));
408 menu
->AddItem(new BMenuItem(B_TRANSLATE("Reply to all"),
409 new BMessage(M_REPLY_ALL
), 'R', B_SHIFT_KEY
));
411 menu
->AddSeparatorItem();
413 menu
->AddItem(new BMenuItem(B_TRANSLATE("Forward"),
414 new BMessage(M_FORWARD
), 'J'));
415 menu
->AddItem(new BMenuItem(B_TRANSLATE("Forward without attachments"),
416 new BMessage(M_FORWARD_WITHOUT_ATTACHMENTS
)));
417 menu
->AddItem(menuItem
= new BMenuItem(B_TRANSLATE("Resend"),
418 new BMessage(M_RESEND
)));
419 menu
->AddItem(menuItem
= new BMenuItem(B_TRANSLATE("Copy to new"),
420 new BMessage(M_COPY_TO_NEW
), 'D'));
422 menu
->AddSeparatorItem();
423 fDeleteNext
= new BMenuItem(B_TRANSLATE("Move to trash"),
424 new BMessage(M_DELETE_NEXT
), 'T');
425 menu
->AddItem(fDeleteNext
);
426 menu
->AddSeparatorItem();
428 fPrevMsg
= new BMenuItem(B_TRANSLATE("Previous message"),
429 new BMessage(M_PREVMSG
), B_UP_ARROW
);
430 menu
->AddItem(fPrevMsg
);
431 fNextMsg
= new BMenuItem(B_TRANSLATE("Next message"),
432 new BMessage(M_NEXTMSG
), B_DOWN_ARROW
);
433 menu
->AddItem(fNextMsg
);
435 menu
->AddItem(fSendNow
= new BMenuItem(B_TRANSLATE("Send message"),
436 new BMessage(M_SEND_NOW
), 'M'));
439 menu
->AddSeparatorItem();
440 fSignature
= new TMenu(B_TRANSLATE("Add signature"),
441 INDEX_SIGNATURE
, M_SIGNATURE
);
442 menu
->AddItem(new BMenuItem(fSignature
));
443 menu
->AddItem(item
= new BMenuItem(
444 B_TRANSLATE("Edit signatures" B_UTF8_ELLIPSIS
),
445 new BMessage(M_EDIT_SIGNATURE
)));
446 item
->SetTarget(be_app
);
447 menu
->AddSeparatorItem();
448 menu
->AddItem(fAdd
= new BMenuItem(
449 B_TRANSLATE("Add enclosure" B_UTF8_ELLIPSIS
),
450 new BMessage(M_ADD
), 'E'));
451 menu
->AddItem(fRemove
= new BMenuItem(
452 B_TRANSLATE("Remove enclosure"),
453 new BMessage(M_REMOVE
), 'T'));
457 menu
->AddSeparatorItem();
458 fSaveAddrMenu
= new BMenu(B_TRANSLATE("Save address"));
459 menu
->AddItem(fSaveAddrMenu
);
464 fEncodingMenu
= new BMenu(B_TRANSLATE("Encoding"));
466 BMenuItem
* automaticItem
= NULL
;
467 if (!resending
&& fIncoming
) {
468 // Reading a message, display the Automatic item
469 msg
= new BMessage(CHARSET_CHOICE_MADE
);
470 msg
->AddInt32("charset", B_MAIL_NULL_CONVERSION
);
471 automaticItem
= new BMenuItem(B_TRANSLATE("Automatic"), msg
);
472 fEncodingMenu
->AddItem(automaticItem
);
473 fEncodingMenu
->AddSeparatorItem();
476 uint32 defaultCharSet
= resending
|| !fIncoming
477 ? fApp
->MailCharacterSet() : B_MAIL_NULL_CONVERSION
;
478 bool markedCharSet
= false;
480 BCharacterSetRoster roster
;
481 BCharacterSet charSet
;
482 while (roster
.GetNextCharacterSet(&charSet
) == B_OK
) {
483 BString
name(charSet
.GetPrintName());
484 const char* mime
= charSet
.GetMIMEName();
486 name
<< " (" << mime
<< ")";
489 if (mime
== NULL
|| strcasecmp(mime
, "UTF-8") != 0)
490 convertID
= charSet
.GetConversionID();
492 convertID
= B_MAIL_UTF8_CONVERSION
;
494 msg
= new BMessage(CHARSET_CHOICE_MADE
);
495 msg
->AddInt32("charset", convertID
);
496 fEncodingMenu
->AddItem(item
= new BMenuItem(name
.String(), msg
));
497 if (convertID
== defaultCharSet
&& !markedCharSet
) {
498 item
->SetMarked(true);
499 markedCharSet
= true;
503 msg
= new BMessage(CHARSET_CHOICE_MADE
);
504 msg
->AddInt32("charset", B_MAIL_US_ASCII_CONVERSION
);
505 fEncodingMenu
->AddItem(item
= new BMenuItem("US-ASCII", msg
));
506 if (defaultCharSet
== B_MAIL_US_ASCII_CONVERSION
&& !markedCharSet
) {
507 item
->SetMarked(true);
508 markedCharSet
= true;
511 if (automaticItem
!= NULL
&& !markedCharSet
)
512 automaticItem
->SetMarked(true);
514 menu
->AddSeparatorItem();
515 menu
->AddItem(fEncodingMenu
);
516 fMenuBar
->AddItem(menu
);
517 fEncodingMenu
->SetRadioMode(true);
518 fEncodingMenu
->SetTargetForItems(this);
522 if (!resending
&& fIncoming
&& fApp
->ShowSpamGUI()) {
523 menu
= new BMenu("Spam filtering");
524 menu
->AddItem(new BMenuItem("Mark as spam and move to trash",
525 new BMessage(M_TRAIN_SPAM_AND_DELETE
), 'K'));
526 menu
->AddItem(new BMenuItem("Mark as spam",
527 new BMessage(M_TRAIN_SPAM
), 'K', B_OPTION_KEY
));
528 menu
->AddSeparatorItem();
529 menu
->AddItem(new BMenuItem("Unmark this message",
530 new BMessage(M_UNTRAIN
)));
531 menu
->AddSeparatorItem();
532 menu
->AddItem(new BMenuItem("Mark as genuine",
533 new BMessage(M_TRAIN_GENUINE
), 'K', B_SHIFT_KEY
));
534 fMenuBar
->AddItem(menu
);
539 fQueryMenu
= new BMenu(B_TRANSLATE("Queries"));
540 fMenuBar
->AddItem(fQueryMenu
);
542 _RebuildQueryMenu(true);
548 if (!fApp
->ShowToolBar())
551 fHeaderView
= new THeaderView(fIncoming
, resending
,
552 fApp
->DefaultAccount());
554 fContentView
= new TContentView(fIncoming
, const_cast<BFont
*>(font
),
555 false, fApp
->ColoredQuotes());
556 // TContentView needs to be properly const, for now cast away constness
558 BLayoutBuilder::Group
<>(this, B_VERTICAL
, 0)
561 .AddGroup(B_VERTICAL
, 0)
563 .SetInsets(B_USE_WINDOW_SPACING
, B_USE_DEFAULT_SPACING
)
568 fHeaderView
->SetTo(to
);
570 AddShortcut('n', B_COMMAND_KEY
, new BMessage(M_NEW
));
572 // If auto-signature, add signature to the text here.
574 BString signature
= fApp
->Signature();
576 if (!fIncoming
&& strcmp(signature
.String(), B_TRANSLATE("None")) != 0) {
577 if (strcmp(signature
.String(), B_TRANSLATE("Random")) == 0)
578 PostMessage(M_RANDOM_SIG
);
580 // Create a query to find this signature
582 BVolumeRoster().GetBootVolume(&volume
);
585 query
.SetVolume(&volume
);
586 query
.PushAttr(INDEX_SIGNATURE
);
587 query
.PushString(signature
.String());
591 // If we find the named query, add it to the text.
593 if (query
.GetNextEntry(&entry
) == B_NO_ERROR
) {
595 file
.SetTo(&entry
, O_RDWR
);
596 if (file
.InitCheck() == B_NO_ERROR
) {
600 BMessage
msg(M_SIGNATURE
);
601 msg
.AddRef("ref", &ref
);
605 char tempString
[2048];
606 query
.GetPredicate (tempString
, sizeof (tempString
));
607 printf ("Query failed, was looking for: %s\n", tempString
);
612 OpenMessage(ref
, _CurrentCharacterSet());
614 AddShortcut('q', B_SHIFT_KEY
, new BMessage(kMsgQuitAndKeepAllStatus
));
619 TMailWindow::_RetrieveVectorIcon(int32 id
)
621 // Lock access to the list
622 BAutolock
lock(sBitmapCacheLock
);
623 if (!lock
.IsLocked())
626 // Check for the bitmap in the cache first
628 for (int32 i
= 0; (item
= sBitmapCache
.ItemAt(i
)) != NULL
; i
++) {
633 // If it's not in the cache, try to load it
634 BResources
* res
= BApplication::AppResources();
638 const void* data
= res
->LoadResource(B_VECTOR_ICON_TYPE
, id
, &size
);
643 BBitmap
* bitmap
= new BBitmap(BRect(0, 0, 21, 21), B_RGBA32
);
644 status_t status
= BIconUtils::GetVectorIcon((uint8
*)data
, size
, bitmap
);
645 if (status
== B_OK
) {
646 item
= (BitmapItem
*)malloc(sizeof(BitmapItem
));
649 sBitmapCache
.AddItem(item
);
658 TMailWindow::BuildToolBar()
660 fToolBar
= new BToolBar();
661 fToolBar
->AddAction(M_NEW
, this, _RetrieveVectorIcon(11), NULL
,
663 fToolBar
->AddSeparator();
666 fToolBar
->AddAction(M_SEND_NOW
, this, _RetrieveVectorIcon(1), NULL
,
667 B_TRANSLATE("Send"));
668 } else if (!fIncoming
) {
669 fToolBar
->AddAction(M_SEND_NOW
, this, _RetrieveVectorIcon(1), NULL
,
670 B_TRANSLATE("Send"));
671 fToolBar
->SetActionEnabled(M_SEND_NOW
, false);
672 fToolBar
->AddAction(M_SIG_MENU
, this, _RetrieveVectorIcon(2), NULL
,
673 B_TRANSLATE("Signature"));
674 fToolBar
->AddAction(M_SAVE_AS_DRAFT
, this, _RetrieveVectorIcon(3), NULL
,
675 B_TRANSLATE("Save"));
676 fToolBar
->SetActionEnabled(M_SAVE_AS_DRAFT
, false);
677 fToolBar
->AddAction(M_PRINT
, this, _RetrieveVectorIcon(5), NULL
,
678 B_TRANSLATE("Print"));
679 fToolBar
->SetActionEnabled(M_PRINT
, false);
680 fToolBar
->AddAction(M_DELETE
, this, _RetrieveVectorIcon(4), NULL
,
681 B_TRANSLATE("Trash"));
683 fToolBar
->AddAction(M_REPLY
, this, _RetrieveVectorIcon(8), NULL
,
684 B_TRANSLATE("Reply"));
685 fToolBar
->AddAction(M_FORWARD
, this, _RetrieveVectorIcon(9), NULL
,
686 B_TRANSLATE("Forward"));
687 fToolBar
->AddAction(M_PRINT
, this, _RetrieveVectorIcon(5), NULL
,
688 B_TRANSLATE("Print"));
689 fToolBar
->AddAction(M_DELETE_NEXT
, this, _RetrieveVectorIcon(4), NULL
,
690 B_TRANSLATE("Trash"));
691 if (fApp
->ShowSpamGUI()) {
692 fToolBar
->AddAction(M_SPAM_BUTTON
, this, _RetrieveVectorIcon(10),
693 NULL
, B_TRANSLATE("Spam"));
695 fToolBar
->AddSeparator();
696 fToolBar
->AddAction(M_NEXTMSG
, this, _RetrieveVectorIcon(6), NULL
,
697 B_TRANSLATE("Next"));
698 fToolBar
->AddAction(M_UNREAD
, this, _RetrieveVectorIcon(12), NULL
,
699 B_TRANSLATE("Unread"));
700 fToolBar
->SetActionVisible(M_UNREAD
, false);
701 fToolBar
->AddAction(M_READ
, this, _RetrieveVectorIcon(13), NULL
,
702 B_TRANSLATE(" Read "));
703 fToolBar
->SetActionVisible(M_READ
, false);
704 fToolBar
->AddAction(M_PREVMSG
, this, _RetrieveVectorIcon(7), NULL
,
705 B_TRANSLATE("Previous"));
707 if (!fTrackerMessenger
.IsValid()) {
708 fToolBar
->SetActionEnabled(M_NEXTMSG
, false);
709 fToolBar
->SetActionEnabled(M_PREVMSG
, false);
720 TMailWindow::UpdateViews()
722 uint8 showToolBar
= fApp
->ShowToolBar();
724 // Show/Hide Button Bar
726 if (fToolBar
->IsHidden())
729 bool showLabel
= showToolBar
== kShowToolBar
;
730 _UpdateLabel(M_NEW
, B_TRANSLATE("New"), showLabel
);
731 _UpdateLabel(M_SEND_NOW
, B_TRANSLATE("Send"), showLabel
);
732 _UpdateLabel(M_SIG_MENU
, B_TRANSLATE("Signature"), showLabel
);
733 _UpdateLabel(M_SAVE_AS_DRAFT
, B_TRANSLATE("Save"), showLabel
);
734 _UpdateLabel(M_PRINT
, B_TRANSLATE("Print"), showLabel
);
735 _UpdateLabel(M_DELETE
, B_TRANSLATE("Trash"), showLabel
);
736 _UpdateLabel(M_REPLY
, B_TRANSLATE("Reply"), showLabel
);
737 _UpdateLabel(M_FORWARD
, B_TRANSLATE("Forward"), showLabel
);
738 _UpdateLabel(M_DELETE_NEXT
, B_TRANSLATE("Trash"), showLabel
);
739 _UpdateLabel(M_SPAM_BUTTON
, B_TRANSLATE("Spam"), showLabel
);
740 _UpdateLabel(M_NEXTMSG
, B_TRANSLATE("Next"), showLabel
);
741 _UpdateLabel(M_UNREAD
, B_TRANSLATE("Unread"), showLabel
);
742 _UpdateLabel(M_READ
, B_TRANSLATE(" Read "), showLabel
);
743 _UpdateLabel(M_PREVMSG
, B_TRANSLATE("Previous"), showLabel
);
744 } else if (!fToolBar
->IsHidden())
750 TMailWindow::UpdatePreferences()
752 fAutoMarkRead
= fApp
->AutoMarkRead();
758 TMailWindow::~TMailWindow()
760 fApp
->SetLastWindowFrame(Frame());
764 delete fOriginatingWindow
;
767 BAutolock
locker(sWindowListLock
);
768 sWindowList
.RemoveItem(this);
773 TMailWindow::GetMailNodeRef(node_ref
& nodeRef
) const
779 return node
.GetNodeRef(&nodeRef
);
784 TMailWindow::GetTrackerWindowFile(entry_ref
* ref
, bool next
) const
786 // Position was already saved
787 if (next
&& fNextTrackerPositionSaved
) {
791 if (!next
&& fPrevTrackerPositionSaved
) {
796 if (!fTrackerMessenger
.IsValid())
799 // Ask the tracker what the next/prev file in the window is.
800 // Continue asking for the next reference until a valid
801 // email file is found (ignoring other types).
802 entry_ref nextRef
= *ref
;
803 bool foundRef
= false;
805 BMessage
request(B_GET_PROPERTY
);
812 spc
.AddString("property", "Entry");
813 spc
.AddRef("data", &nextRef
);
815 request
.AddSpecifier(&spc
);
817 if (fTrackerMessenger
.SendMessage(&request
, &reply
) != B_OK
)
820 if (reply
.FindRef("result", &nextRef
) != B_OK
)
824 BNode
node(&nextRef
);
825 if (node
.InitCheck() != B_OK
)
828 if (BNodeInfo(&node
).GetType(fileType
) != B_OK
)
831 if (strcasecmp(fileType
, B_MAIL_TYPE
) == 0
832 || strcasecmp(fileType
, B_PARTIAL_MAIL_TYPE
) == 0)
842 TMailWindow::SaveTrackerPosition(entry_ref
* ref
)
844 // if only one of them is saved, we're not going to do it again
845 if (fNextTrackerPositionSaved
|| fPrevTrackerPositionSaved
)
848 fNextRef
= fPrevRef
= *ref
;
850 fNextTrackerPositionSaved
= GetTrackerWindowFile(&fNextRef
, true);
851 fPrevTrackerPositionSaved
= GetTrackerWindowFile(&fPrevRef
, false);
856 TMailWindow::SetOriginatingWindow(BWindow
* window
)
858 delete fOriginatingWindow
;
859 fOriginatingWindow
= new BMessenger(window
);
864 TMailWindow::SetTrackerSelectionToCurrent()
866 BMessage
setSelection(B_SET_PROPERTY
);
867 setSelection
.AddSpecifier("Selection");
868 setSelection
.AddRef("data", fRef
);
870 fTrackerMessenger
.SendMessage(&setSelection
);
875 TMailWindow::PreserveReadingPos(bool save
)
877 BScrollBar
* scroll
= fContentView
->TextView()->ScrollBar(B_VERTICAL
);
878 if (scroll
== NULL
|| fRef
== NULL
)
882 float pos
= scroll
->Value();
884 const char* name
= "MAIL:read_pos";
886 node
.WriteAttr(name
, B_FLOAT_TYPE
, 0, &pos
, sizeof(pos
));
890 if (node
.ReadAttr(name
, B_FLOAT_TYPE
, 0, &pos
, sizeof(pos
)) == sizeof(pos
)) {
892 scroll
->SetValue(pos
);
899 TMailWindow::MarkMessageRead(entry_ref
* message
, read_flags flag
)
902 status_t status
= node
.InitCheck();
907 if (node
.ReadAttr(B_MAIL_ATTR_ACCOUNT_ID
, B_INT32_TYPE
, 0, &account
,
908 sizeof(account
)) < 0)
911 // don't wait for the server write the attribute directly
912 write_read_attr(node
, flag
);
914 // preserve the read position in the node attribute
915 PreserveReadingPos(true);
917 BMailDaemon().MarkAsRead(account
, *message
, flag
);
922 TMailWindow::FrameResized(float width
, float height
)
924 fContentView
->FrameResized(width
, height
);
929 TMailWindow::MenusBeginning()
935 bool gotToField
= !fHeaderView
->IsToEmpty();
936 bool gotCcField
= !fHeaderView
->IsCcEmpty();
937 bool gotBccField
= !fHeaderView
->IsBccEmpty();
938 bool gotSubjectField
= !fHeaderView
->IsSubjectEmpty();
939 bool gotText
= fContentView
->TextView()->Text()[0] != 0;
940 fSendNow
->SetEnabled(gotToField
|| gotBccField
);
941 fSendLater
->SetEnabled(fChanged
&& (gotToField
|| gotCcField
942 || gotBccField
|| gotSubjectField
|| gotText
));
944 be_clipboard
->Lock();
945 fPaste
->SetEnabled(be_clipboard
->Data()->HasData("text/plain",
947 && (fEnclosuresView
== NULL
|| !fEnclosuresView
->fList
->IsFocus()));
948 be_clipboard
->Unlock();
950 fQuote
->SetEnabled(false);
951 fRemoveQuote
->SetEnabled(false);
953 fAdd
->SetEnabled(true);
954 fRemove
->SetEnabled(fEnclosuresView
!= NULL
955 && fEnclosuresView
->fList
->CurrentSelection() >= 0);
958 bool enable
= !fHeaderView
->IsToEmpty();
959 fSendNow
->SetEnabled(enable
);
960 //fSendLater->SetEnabled(enable);
962 if (fHeaderView
->ToControl()->HasFocus()) {
963 fHeaderView
->ToControl()->GetSelection(&start
, &finish
);
965 fCut
->SetEnabled(start
!= finish
);
966 be_clipboard
->Lock();
967 fPaste
->SetEnabled(be_clipboard
->Data()->HasData(
968 "text/plain", B_MIME_TYPE
));
969 be_clipboard
->Unlock();
971 fCut
->SetEnabled(false);
972 fPaste
->SetEnabled(false);
975 fCut
->SetEnabled(false);
976 fPaste
->SetEnabled(false);
980 fPrint
->SetEnabled(fContentView
->TextView()->TextLength());
982 BTextView
* textView
= dynamic_cast<BTextView
*>(CurrentFocus());
984 && (dynamic_cast<AddressTextControl
*>(textView
->Parent()) != NULL
985 || dynamic_cast<BTextControl
*>(textView
->Parent()) != NULL
)) {
986 // one of To:, Subject:, Account:, Cc:, Bcc:
987 textView
->GetSelection(&start
, &finish
);
988 } else if (fContentView
->TextView()->IsFocus()) {
989 fContentView
->TextView()->GetSelection(&start
, &finish
);
991 fQuote
->SetEnabled(true);
992 fRemoveQuote
->SetEnabled(true);
996 fCopy
->SetEnabled(start
!= finish
);
998 fCut
->SetEnabled(start
!= finish
);
1001 bool isRedo
= false;
1002 undo_state undoState
= B_UNDO_UNAVAILABLE
;
1004 BTextView
* focusTextView
= dynamic_cast<BTextView
*>(CurrentFocus());
1005 if (focusTextView
!= NULL
)
1006 undoState
= focusTextView
->UndoState(&isRedo
);
1008 // fUndo->SetLabel((isRedo)
1009 // ? kRedoStrings[undoState] : kUndoStrings[undoState]);
1010 fUndo
->SetEnabled(undoState
!= B_UNDO_UNAVAILABLE
);
1012 if (fLeaveStatusMenu
!= NULL
&& fRef
!= NULL
) {
1013 BFile
file(fRef
, B_READ_ONLY
);
1015 file
.ReadAttrString(B_MAIL_ATTR_STATUS
, &status
);
1017 BMenuItem
* LeaveStatus
= fLeaveStatusMenu
->FindItem(B_QUIT_REQUESTED
);
1018 if (LeaveStatus
== NULL
)
1019 LeaveStatus
= fLeaveStatusMenu
->FindItem(kMsgQuitAndKeepAllStatus
);
1021 if (LeaveStatus
!= NULL
&& status
.Length() > 0) {
1023 label
.SetToFormat(B_TRANSLATE("Leave as '%s'"), status
.String());
1024 LeaveStatus
->SetLabel(label
.String());
1031 TMailWindow::MessageReceived(BMessage
* msg
)
1033 bool wasReadMsg
= false;
1034 switch (msg
->what
) {
1035 case B_MAIL_BODY_FETCHED
:
1037 status_t status
= msg
->FindInt32("status");
1038 if (status
!= B_OK
) {
1039 fprintf(stderr
, "Body could not be fetched: %s\n", strerror(status
));
1040 PostMessage(B_QUIT_REQUESTED
);
1045 if (msg
->FindRef("ref", &ref
) != B_OK
)
1050 // reload the current message
1051 OpenMessage(&ref
, _CurrentCharacterSet());
1057 int32 prevState
= fFieldState
;
1058 int32 fieldMask
= msg
->FindInt32("bitmask");
1061 if (msg
->FindPointer("source", &source
) == B_OK
) {
1064 if (fieldMask
== FIELD_BODY
)
1065 length
= ((TTextView
*)source
)->TextLength();
1067 length
= ((AddressTextControl
*)source
)->TextLength();
1070 fFieldState
|= fieldMask
;
1072 fFieldState
&= ~fieldMask
;
1075 // Has anything changed?
1076 if (prevState
!= fFieldState
|| !fChanged
) {
1077 // Change Buttons to reflect this
1078 fToolBar
->SetActionEnabled(M_SAVE_AS_DRAFT
, fFieldState
);
1079 fToolBar
->SetActionEnabled(M_PRINT
, fFieldState
);
1080 fToolBar
->SetActionEnabled(M_SEND_NOW
, (fFieldState
& FIELD_TO
)
1081 || (fFieldState
& FIELD_BCC
));
1085 // Update title bar if "subject" has changed
1086 if (!fIncoming
&& (fieldMask
& FIELD_SUBJECT
) != 0) {
1087 // If no subject, set to "Mail"
1088 if (fHeaderView
->IsSubjectEmpty())
1089 SetTitle(B_TRANSLATE_SYSTEM_NAME("Mail"));
1091 SetTitle(fHeaderView
->Subject());
1096 PostMessage(msg
, fEnclosuresView
);
1100 PostMessage(msg
, fContentView
);
1105 BMessage
message(M_NEW
);
1106 message
.AddInt32("type", msg
->what
);
1107 be_app
->PostMessage(&message
);
1114 A popup from a button is good only when the behavior has some
1115 consistency and there is some visual indication that a menu
1116 will be shown when clicked. A workable implementation would
1117 have an extra button attached to the main one which has a
1118 downward-pointing arrow. Mozilla Thunderbird's 'Get Mail'
1119 button is a good example of this.
1121 TODO: Replace this code with a split toolbar button
1124 if (msg
->FindInt32("buttons", (int32
*)&buttons
) == B_OK
1125 && buttons
== B_SECONDARY_MOUSE_BUTTON
) {
1126 BPopUpMenu
menu("Spam Actions", false, false);
1127 for (int i
= 0; i
< 4; i
++)
1128 menu
.AddItem(new BMenuItem(kSpamMenuItemTextArray
[i
],
1129 new BMessage(M_TRAIN_SPAM_AND_DELETE
+ i
)));
1132 msg
->FindPoint("where", &where
);
1134 if ((item
= menu
.Go(where
, false, false)) != NULL
)
1135 PostMessage(item
->Message());
1138 // Default action for left clicking on the spam button.
1139 PostMessage(new BMessage(M_TRAIN_SPAM_AND_DELETE
));
1144 case M_TRAIN_SPAM_AND_DELETE
:
1145 PostMessage(M_DELETE_NEXT
);
1147 TrainMessageAs("Spam");
1151 TrainMessageAs("Uncertain");
1154 case M_TRAIN_GENUINE
:
1155 TrainMessageAs("Genuine");
1160 // TODO: This needs removed in favor of a split toolbar button.
1161 // See comments for Spam button
1163 if (msg
->FindInt32("buttons", (int32
*)&buttons
) == B_OK
1164 && buttons
== B_SECONDARY_MOUSE_BUTTON
) {
1165 BPopUpMenu
menu("Reply To", false, false);
1166 menu
.AddItem(new BMenuItem(B_TRANSLATE("Reply"),
1167 new BMessage(M_REPLY
)));
1168 menu
.AddItem(new BMenuItem(B_TRANSLATE("Reply to sender"),
1169 new BMessage(M_REPLY_TO_SENDER
)));
1170 menu
.AddItem(new BMenuItem(B_TRANSLATE("Reply to all"),
1171 new BMessage(M_REPLY_ALL
)));
1174 msg
->FindPoint("where", &where
);
1177 if ((item
= menu
.Go(where
, false, false)) != NULL
) {
1178 item
->SetTarget(this);
1179 PostMessage(item
->Message());
1187 // TODO: This needs removed in favor of a split toolbar button.
1188 // See comments for Spam button
1190 if (msg
->FindInt32("buttons", (int32
*)&buttons
) == B_OK
1191 && buttons
== B_SECONDARY_MOUSE_BUTTON
) {
1192 BPopUpMenu
menu("Forward", false, false);
1193 menu
.AddItem(new BMenuItem(B_TRANSLATE("Forward"),
1194 new BMessage(M_FORWARD
)));
1195 menu
.AddItem(new BMenuItem(
1196 B_TRANSLATE("Forward without attachments"),
1197 new BMessage(M_FORWARD_WITHOUT_ATTACHMENTS
)));
1200 msg
->FindPoint("where", &where
);
1203 if ((item
= menu
.Go(where
, false, false)) != NULL
) {
1204 item
->SetTarget(this);
1205 PostMessage(item
->Message());
1213 case M_REPLY_TO_SENDER
:
1214 case M_FORWARD_WITHOUT_ATTACHMENTS
:
1218 BMessage
message(M_NEW
);
1219 message
.AddRef("ref", fRef
);
1220 message
.AddPointer("window", this);
1221 message
.AddInt32("type", msg
->what
);
1222 be_app
->PostMessage(&message
);
1229 if (msg
->what
== M_DELETE_NEXT
&& (modifiers() & B_SHIFT_KEY
) != 0)
1230 msg
->what
= M_DELETE_PREV
;
1232 bool foundRef
= false;
1234 if ((msg
->what
== M_DELETE_PREV
|| msg
->what
== M_DELETE_NEXT
)
1236 // Find the next message that should be displayed
1238 foundRef
= GetTrackerWindowFile(&nextRef
,
1239 msg
->what
== M_DELETE_NEXT
);
1242 read_flags flag
= (fAutoMarkRead
== true) ? B_READ
: B_SEEN
;
1243 MarkMessageRead(fRef
, flag
);
1246 if (!fTrackerMessenger
.IsValid() || !fIncoming
) {
1247 // Not associated with a tracker window. Create a new
1248 // messenger and ask the tracker to delete this entry
1249 if (fDraft
|| fIncoming
) {
1250 BMessenger
tracker("application/x-vnd.Be-TRAK");
1251 if (tracker
.IsValid()) {
1252 BMessage
msg('Ttrs');
1253 msg
.AddRef("refs", fRef
);
1254 tracker
.SendMessage(&msg
);
1256 BAlert
* alert
= new BAlert("",
1257 B_TRANSLATE("Need Tracker to move items to trash"),
1258 B_TRANSLATE("Sorry"));
1259 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1264 // This is associated with a tracker window. Ask the
1265 // window to delete this entry. Do it this way if we
1266 // can instead of the above way because it doesn't reset
1267 // the selection (even though we set selection below, this
1268 // still causes problems).
1269 BMessage
delmsg(B_DELETE_PROPERTY
);
1270 BMessage
entryspec('sref');
1271 entryspec
.AddRef("refs", fRef
);
1272 entryspec
.AddString("property", "Entry");
1273 delmsg
.AddSpecifier(&entryspec
);
1274 fTrackerMessenger
.SendMessage(&delmsg
);
1277 // If the next file was found, open it. If it was not,
1278 // we have no choice but to close this window.
1281 = static_cast<TMailApp
*>(be_app
)->FindWindow(nextRef
);
1283 OpenMessage(&nextRef
, _CurrentCharacterSet());
1287 SetTrackerSelectionToCurrent();
1294 BMessage
msg(B_CLOSE_REQUESTED
);
1301 BMessage
message(B_CLOSE_REQUESTED
);
1302 message
.AddString("status", "Read");
1303 PostMessage(&message
);
1308 BMessage
message(B_QUIT_REQUESTED
);
1309 message
.AddString("status", "Saved");
1310 PostMessage(&message
);
1313 case kMsgQuitAndKeepAllStatus
:
1314 fKeepStatusOnQuit
= true;
1315 be_app
->PostMessage(B_QUIT_REQUESTED
);
1317 case M_CLOSE_CUSTOM
:
1318 if (msg
->HasString("status")) {
1319 BMessage
message(B_CLOSE_REQUESTED
);
1320 message
.AddString("status", msg
->GetString("status"));
1321 PostMessage(&message
);
1324 r
.left
+= ((r
.Width() - STATUS_WIDTH
) / 2);
1325 r
.right
= r
.left
+ STATUS_WIDTH
;
1327 r
.bottom
= r
.top
+ STATUS_HEIGHT
;
1329 BString string
= "could not read";
1331 if (node
.InitCheck() == B_OK
)
1332 node
.ReadAttrString(B_MAIL_ATTR_STATUS
, &string
);
1334 new TStatusWindow(r
, this, string
.String());
1340 const char* attribute
;
1341 if (msg
->FindString("attribute", &attribute
) != B_OK
)
1344 BMessage
message(B_CLOSE_REQUESTED
);
1345 message
.AddString("status", attribute
);
1346 PostMessage(&message
);
1351 bool showHeader
= !fHeader
->IsMarked();
1352 fHeader
->SetMarked(showHeader
);
1354 BMessage
message(M_HEADER
);
1355 message
.AddBool("header", showHeader
);
1356 PostMessage(&message
, fContentView
->TextView());
1361 bool raw
= !(fRaw
->IsMarked());
1362 fRaw
->SetMarked(raw
);
1363 BMessage
message(M_RAW
);
1364 message
.AddBool("raw", raw
);
1365 PostMessage(&message
, fContentView
->TextView());
1369 case M_SAVE_AS_DRAFT
:
1370 Send(msg
->what
== M_SEND_NOW
);
1375 const char* address
;
1376 if (msg
->FindString("address", (const char**)&address
) != B_OK
)
1379 BVolumeRoster volumeRoster
;
1383 bool foundEntry
= false;
1385 char* arg
= (char*)malloc(strlen("META:email=")
1386 + strlen(address
) + 1);
1387 sprintf(arg
, "META:email=%s", address
);
1389 // Search a Person file with this email address
1390 while (volumeRoster
.GetNextVolume(&volume
) == B_NO_ERROR
) {
1391 if (!volume
.KnowsQuery())
1394 query
.SetVolume(&volume
);
1395 query
.SetPredicate(arg
);
1398 if (query
.GetNextEntry(&entry
) == B_NO_ERROR
) {
1399 BMessenger
tracker("application/x-vnd.Be-TRAK");
1400 if (tracker
.IsValid()) {
1404 BMessage
open(B_REFS_RECEIVED
);
1405 open
.AddRef("refs", &ref
);
1406 tracker
.SendMessage(&open
);
1411 // Try next volume, if any
1417 // Ask to open a new Person file with this address pre-filled
1419 status_t result
= be_roster
->Launch("application/x-person",
1422 if (result
!= B_NO_ERROR
) {
1423 BAlert
* alert
= new BAlert("", B_TRANSLATE(
1424 "Sorry, could not find an application that "
1425 "supports the 'Person' data type."),
1427 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1436 PreserveReadingPos(false);
1451 FindWindow::Find(this);
1455 FindWindow::FindAgain(this);
1459 case M_REMOVE_QUOTE
:
1460 PostMessage(msg
->what
, fContentView
);
1469 BVolumeRoster().GetBootVolume(&volume
);
1472 query
.SetVolume(&volume
);
1474 char predicate
[128];
1475 sprintf(predicate
, "%s = *", INDEX_SIGNATURE
);
1476 query
.SetPredicate(predicate
);
1480 while (query
.GetNextEntry(&entry
) == B_NO_ERROR
) {
1481 BFile
file(&entry
, O_RDONLY
);
1482 if (file
.InitCheck() == B_NO_ERROR
) {
1486 message
= new BMessage(M_SIGNATURE
);
1487 message
->AddRef("ref", &ref
);
1488 sigList
.AddItem(message
);
1491 if (sigList
.CountItems() > 0) {
1493 PostMessage((BMessage
*)sigList
.ItemAt(rand()
1494 % sigList
.CountItems()));
1496 for (int32 i
= 0; (message
= (BMessage
*)sigList
.ItemAt(i
))
1504 BMessage
message(*msg
);
1505 PostMessage(&message
, fContentView
);
1513 menu
= new TMenu("Add Signature", INDEX_SIGNATURE
, M_SIGNATURE
,
1517 if (msg
->FindPoint("where", &where
) != B_OK
) {
1518 BRect bounds
= fToolBar
->Bounds();
1519 where
= fToolBar
->ConvertToScreen(BPoint(
1520 (bounds
.right
- bounds
.left
) / 2,
1521 (bounds
.bottom
- bounds
.top
) / 2));
1524 if ((item
= menu
->Go(where
, false, true)) != NULL
) {
1525 item
->SetTarget(this);
1526 (dynamic_cast<BInvoker
*>(item
))->Invoke();
1534 BMessenger
me(this);
1535 BMessage
msg(REFS_RECEIVED
);
1536 fPanel
= new BFilePanel(B_OPEN_PANEL
, &me
, &fOpenFolder
, false,
1538 } else if (!fPanel
->Window()->IsHidden()) {
1539 fPanel
->Window()->Activate();
1542 if (fPanel
->Window()->IsHidden())
1543 fPanel
->Window()->Show();
1547 PostMessage(msg
->what
, fEnclosuresView
);
1550 case CHARSET_CHOICE_MADE
:
1553 if (msg
->FindInt32("charset", &charSet
) != B_OK
)
1556 BMessage
update(FIELD_CHANGED
);
1557 update
.AddInt32("bitmask", 0);
1558 // just enable the save button
1559 PostMessage(&update
);
1561 if (fIncoming
&& !fResending
) {
1562 // The user wants to see the message they are reading (not
1563 // composing) displayed with a different kind of character set
1564 // for decoding. Reload the whole message and redisplay. For
1565 // messages which are being composed, the character set is
1566 // retrieved from the header view when it is needed.
1568 entry_ref fileRef
= *fRef
;
1569 OpenMessage(&fileRef
, charSet
);
1579 // Navigation Messages
1582 MarkMessageRead(fRef
, B_SEEN
);
1583 _UpdateReadButton();
1584 PostMessage(M_NEXTMSG
);
1588 _UpdateReadButton();
1589 msg
->what
= M_NEXTMSG
;
1595 entry_ref orgRef
= *fRef
;
1596 entry_ref nextRef
= *fRef
;
1597 if (GetTrackerWindowFile(&nextRef
, (msg
->what
== M_NEXTMSG
))) {
1598 TMailWindow
* window
= static_cast<TMailApp
*>(be_app
)
1599 ->FindWindow(nextRef
);
1600 if (window
== NULL
) {
1602 read_flags currentFlag
;
1603 if (read_read_attr(node
, currentFlag
) != B_OK
)
1604 currentFlag
= B_UNREAD
;
1605 if (fAutoMarkRead
== true)
1606 MarkMessageRead(fRef
, B_READ
);
1607 else if (currentFlag
!= B_READ
&& !wasReadMsg
)
1608 MarkMessageRead(fRef
, B_SEEN
);
1610 OpenMessage(&nextRef
, _CurrentCharacterSet());
1614 PostMessage(B_CLOSE_REQUESTED
);
1617 SetTrackerSelectionToCurrent();
1620 PostMessage(B_CLOSE_REQUESTED
);
1625 MarkMessageRead(&orgRef
, B_READ
);
1629 case M_SAVE_POSITION
:
1631 SaveTrackerPosition(fRef
);
1637 if (!fHeaderView
->IsToEmpty())
1638 fFieldState
|= FIELD_TO
;
1639 if (!fHeaderView
->IsSubjectEmpty())
1640 fFieldState
|= FIELD_SUBJECT
;
1641 if (!fHeaderView
->IsCcEmpty())
1642 fFieldState
|= FIELD_CC
;
1643 if (!fHeaderView
->IsBccEmpty())
1644 fFieldState
|= FIELD_BCC
;
1645 if (fContentView
->TextView()->TextLength() != 0)
1646 fFieldState
|= FIELD_BODY
;
1648 fToolBar
->SetActionEnabled(M_SAVE_AS_DRAFT
, false);
1649 fToolBar
->SetActionEnabled(M_PRINT
, fFieldState
);
1650 fToolBar
->SetActionEnabled(M_SEND_NOW
, (fFieldState
& FIELD_TO
)
1651 || (fFieldState
& FIELD_BCC
));
1654 case M_CHECK_SPELLING
:
1655 if (gDictCount
== 0)
1656 // Give the application time to init and load dictionaries.
1660 BAlert
* alert
= new BAlert("",
1661 B_TRANSLATE("Mail couldn't find its dictionary."),
1662 B_TRANSLATE("OK"), NULL
, NULL
, B_WIDTH_AS_USUAL
,
1663 B_OFFSET_SPACING
, B_STOP_ALERT
);
1664 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1667 fSpelling
->SetMarked(!fSpelling
->IsMarked());
1668 fContentView
->TextView()->EnableSpellCheck(
1669 fSpelling
->IsMarked());
1673 case M_EDIT_QUERIES
:
1676 if (_GetQueryPath(&path
) < B_OK
)
1679 // the user used this command, make sure the folder actually
1680 // exists - if it didn't inform the user what to do with it
1681 BEntry
entry(path
.Path());
1682 bool showAlert
= false;
1683 if (!entry
.Exists()) {
1685 create_directory(path
.Path(), 0777);
1689 if (folderEntry
.SetTo(path
.Path()) == B_OK
1690 && folderEntry
.Exists()) {
1691 BMessage
openFolderCommand(B_REFS_RECEIVED
);
1692 BMessenger
tracker("application/x-vnd.Be-TRAK");
1695 folderEntry
.GetRef(&ref
);
1696 openFolderCommand
.AddRef("refs", &ref
);
1697 tracker
.SendMessage(&openFolderCommand
);
1701 // just some patience before Tracker pops up the folder
1703 BAlert
* alert
= new BAlert(B_TRANSLATE("helpful message"),
1704 B_TRANSLATE("Put your favorite e-mail queries and query "
1705 "templates in this folder."), B_TRANSLATE("OK"), NULL
, NULL
,
1706 B_WIDTH_AS_USUAL
, B_IDEA_ALERT
);
1707 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
1714 case B_PATH_MONITOR
:
1715 _RebuildQueryMenu();
1719 BWindow::MessageReceived(msg
);
1725 TMailWindow::AddEnclosure(BMessage
* msg
)
1727 if (fEnclosuresView
== NULL
&& !fIncoming
) {
1730 r
.top
= fHeaderView
->Frame().bottom
- 1;
1731 r
.right
= Frame().Width() + 2;
1732 r
.bottom
= r
.top
+ ENCLOSURES_HEIGHT
;
1734 fEnclosuresView
= new TEnclosuresView(r
, Frame());
1735 AddChild(fEnclosuresView
, fContentView
);
1736 fContentView
->ResizeBy(0, -ENCLOSURES_HEIGHT
);
1737 fContentView
->MoveBy(0, ENCLOSURES_HEIGHT
);
1740 if (fEnclosuresView
== NULL
)
1743 if (msg
&& msg
->HasRef("refs")) {
1744 // Add enclosure to view
1745 PostMessage(msg
, fEnclosuresView
);
1750 msg
->FindRef("refs", &ref
);
1752 entry
.GetParent(&entry
);
1753 entry
.GetRef(&fOpenFolder
);
1759 TMailWindow::QuitRequested()
1763 if ((!fIncoming
|| (fIncoming
&& fResending
)) && fChanged
&& !fSent
1764 && (!fHeaderView
->IsToEmpty()
1765 || !fHeaderView
->IsSubjectEmpty()
1766 || !fHeaderView
->IsCcEmpty()
1767 || !fHeaderView
->IsBccEmpty()
1768 || (fContentView
->TextView() != NULL
1769 && strlen(fContentView
->TextView()->Text()))
1770 || (fEnclosuresView
!= NULL
1771 && fEnclosuresView
->fList
->CountItems()))) {
1773 BAlert
* alert
= new BAlert("", B_TRANSLATE(
1774 "Send this message before closing?"),
1775 B_TRANSLATE("Cancel"),
1776 B_TRANSLATE("Don't send"),
1777 B_TRANSLATE("Send"),
1778 B_WIDTH_AS_USUAL
, B_OFFSET_SPACING
, B_WARNING_ALERT
);
1779 alert
->SetShortcut(0, B_ESCAPE
);
1780 alert
->SetShortcut(1, 'd');
1781 alert
->SetShortcut(2, 's');
1782 result
= alert
->Go();
1787 case 1: // Don't send
1794 BAlert
* alert
= new BAlert("",
1795 B_TRANSLATE("Save this message as a draft before closing?"),
1796 B_TRANSLATE("Cancel"),
1797 B_TRANSLATE("Don't save"),
1798 B_TRANSLATE("Save"),
1799 B_WIDTH_AS_USUAL
, B_OFFSET_SPACING
, B_WARNING_ALERT
);
1800 alert
->SetShortcut(0, B_ESCAPE
);
1801 alert
->SetShortcut(1, 'd');
1802 alert
->SetShortcut(2, 's');
1803 result
= alert
->Go();
1807 case 1: // Don't Save
1816 BMessage
message(WINDOW_CLOSED
);
1817 message
.AddInt32("kind", MAIL_WINDOW
);
1818 message
.AddPointer("window", this);
1819 be_app
->PostMessage(&message
);
1821 if (CurrentMessage() && CurrentMessage()->HasString("status")) {
1822 // User explicitly requests a status to set this message to.
1823 if (!CurrentMessage()->HasString("same")) {
1824 const char* status
= CurrentMessage()->FindString("status");
1825 if (status
!= NULL
) {
1827 if (node
.InitCheck() == B_NO_ERROR
) {
1828 node
.RemoveAttr(B_MAIL_ATTR_STATUS
);
1829 WriteAttrString(&node
, B_MAIL_ATTR_STATUS
, status
);
1833 } else if (fRef
!= NULL
&& !fKeepStatusOnQuit
) {
1834 // ...Otherwise just set the message read
1835 if (fAutoMarkRead
== true)
1836 MarkMessageRead(fRef
, B_READ
);
1839 read_flags currentFlag
;
1840 if (read_read_attr(node
, currentFlag
) != B_OK
)
1841 currentFlag
= B_UNREAD
;
1842 if (currentFlag
== B_UNREAD
)
1843 MarkMessageRead(fRef
, B_SEEN
);
1847 BPrivate::BPathMonitor::StopWatching(BMessenger(this, this));
1857 if (!fResending
&& (fIncoming
|| fReplying
)) {
1858 fContentView
->TextView()->MakeFocus(true);
1860 fHeaderView
->ToControl()->MakeFocus(true);
1861 fHeaderView
->ToControl()->SelectAll();
1870 TMailWindow::Zoom(BPoint
/*pos*/, float /*x*/, float /*y*/)
1875 BRect rect
= Frame();
1876 width
= 80 * fApp
->ContentFont().StringWidth("M")
1877 + (rect
.Width() - fContentView
->TextView()->Bounds().Width() + 6);
1879 BScreen
screen(this);
1880 BRect screenFrame
= screen
.Frame();
1881 if (width
> (screenFrame
.Width() - 8))
1882 width
= screenFrame
.Width() - 8;
1884 height
= max_c(fContentView
->TextView()->CountLines(), 20)
1885 * fContentView
->TextView()->LineHeight(0)
1886 + (rect
.Height() - fContentView
->TextView()->Bounds().Height());
1887 if (height
> (screenFrame
.Height() - 29))
1888 height
= screenFrame
.Height() - 29;
1890 rect
.right
= rect
.left
+ width
;
1891 rect
.bottom
= rect
.top
+ height
;
1893 if (abs((int)(Frame().Width() - rect
.Width())) < 5
1894 && abs((int)(Frame().Height() - rect
.Height())) < 5) {
1898 screenFrame
.InsetBy(6, 6);
1900 if (rect
.Width() > screenFrame
.Width())
1901 rect
.right
= rect
.left
+ screenFrame
.Width();
1902 if (rect
.Height() > screenFrame
.Height())
1903 rect
.bottom
= rect
.top
+ screenFrame
.Height();
1905 if (rect
.right
> screenFrame
.right
) {
1906 rect
.left
-= rect
.right
- screenFrame
.right
;
1907 rect
.right
= screenFrame
.right
;
1909 if (rect
.bottom
> screenFrame
.bottom
) {
1910 rect
.top
-= rect
.bottom
- screenFrame
.bottom
;
1911 rect
.bottom
= screenFrame
.bottom
;
1913 if (rect
.left
< screenFrame
.left
) {
1914 rect
.right
+= screenFrame
.left
- rect
.left
;
1915 rect
.left
= screenFrame
.left
;
1917 if (rect
.top
< screenFrame
.top
) {
1918 rect
.bottom
+= screenFrame
.top
- rect
.top
;
1919 rect
.top
= screenFrame
.top
;
1923 ResizeTo(rect
.Width(), rect
.Height());
1924 MoveTo(rect
.LeftTop());
1929 TMailWindow::WindowActivated(bool status
)
1932 BAutolock
locker(sWindowListLock
);
1933 sWindowList
.RemoveItem(this);
1934 sWindowList
.AddItem(this, 0);
1940 TMailWindow::Forward(entry_ref
* ref
, TMailWindow
* window
,
1941 bool includeAttachments
)
1943 BEmailMessage
* mail
= window
->Mail();
1947 uint32 useAccountFrom
= fApp
->UseAccountFrom();
1949 fMail
= mail
->ForwardMessage(useAccountFrom
== ACCOUNT_FROM_MAIL
,
1950 includeAttachments
);
1952 BFile
file(ref
, O_RDONLY
);
1953 if (file
.InitCheck() < B_NO_ERROR
)
1956 fHeaderView
->SetSubject(fMail
->Subject());
1960 if (useAccountFrom
== ACCOUNT_FROM_MAIL
)
1961 fHeaderView
->SetAccount(fMail
->Account());
1963 if (fMail
->CountComponents() > 1) {
1964 // if there are any enclosures to be added, first add the enclosures
1965 // view to the window
1967 if (fEnclosuresView
)
1968 fEnclosuresView
->AddEnclosuresFromMail(fMail
);
1971 fContentView
->TextView()->LoadMessage(fMail
, false, NULL
);
1978 TMailWindow::Print()
1980 BPrintJob
print(Title());
1982 if (!fApp
->HasPrintSettings()) {
1983 if (print
.Settings()) {
1984 fApp
->SetPrintSettings(print
.Settings());
1987 if (!fApp
->HasPrintSettings())
1992 print
.SetSettings(new BMessage(fApp
->PrintSettings()));
1994 if (print
.ConfigJob() == B_OK
) {
1997 BTextView
header_view(print
.PrintableRect(), "header",
1998 print
.PrintableRect().OffsetByCopy(BPoint(
1999 -print
.PrintableRect().left
, -print
.PrintableRect().top
)),
2000 B_FOLLOW_ALL_SIDES
);
2002 //---------Init the header fields
2003 #define add_header_field(label, field) { \
2004 /*header_view.SetFontAndColor(be_bold_font);*/ \
2005 header_view.Insert(label); \
2006 header_view.Insert(" "); \
2007 /*header_view.SetFontAndColor(be_plain_font);*/ \
2008 header_view.Insert(field); \
2009 header_view.Insert("\n"); \
2012 add_header_field("Subject:", fHeaderView
->Subject());
2013 add_header_field("To:", fHeaderView
->To());
2014 if (!fHeaderView
->IsCcEmpty())
2015 add_header_field(B_TRANSLATE("Cc:"), fHeaderView
->Cc());
2017 if (!fHeaderView
->IsDateEmpty())
2018 header_view
.Insert(fHeaderView
->Date());
2020 int32 maxLine
= fContentView
->TextView()->CountLines();
2021 BRect pageRect
= print
.PrintableRect();
2022 BRect curPageRect
= pageRect
;
2025 float header_height
= header_view
.TextHeight(0,
2026 header_view
.CountLines());
2028 BRect
rect(0, 0, pageRect
.Width(), header_height
);
2029 BBitmap
bmap(rect
, B_BITMAP_ACCEPTS_VIEWS
, B_RGBA32
);
2031 bmap
.AddChild(&header_view
);
2032 print
.DrawView(&header_view
, rect
, BPoint(0.0, 0.0));
2033 HorizontalLine
line(BRect(0, 0, pageRect
.right
, 0));
2034 bmap
.AddChild(&line
);
2035 print
.DrawView(&line
, line
.Bounds(), BPoint(0, header_height
+ 1));
2040 int32 lineOffset
= fContentView
->TextView()->OffsetAt(lastLine
);
2041 curPageRect
.OffsetTo(0,
2042 fContentView
->TextView()->PointAt(lineOffset
).y
);
2044 int32 fromLine
= lastLine
;
2045 lastLine
= fContentView
->TextView()->LineAt(
2046 BPoint(0.0, curPageRect
.bottom
- ((curPage
== 1)
2047 ? header_height
: 0)));
2049 float curPageHeight
= fContentView
->TextView()->TextHeight(
2050 fromLine
, lastLine
) + (curPage
== 1 ? header_height
: 0);
2052 if (curPageHeight
> pageRect
.Height()) {
2053 curPageHeight
= fContentView
->TextView()->TextHeight(
2054 fromLine
, --lastLine
) + (curPage
== 1 ? header_height
: 0);
2056 curPageRect
.bottom
= curPageRect
.top
+ curPageHeight
- 1.0;
2058 if (curPage
>= print
.FirstPage() && curPage
<= print
.LastPage()) {
2059 print
.DrawView(fContentView
->TextView(), curPageRect
,
2060 BPoint(0.0, curPage
== 1 ? header_height
: 0.0));
2064 curPageRect
= pageRect
;
2068 } while (print
.CanContinue() && lastLine
< maxLine
);
2071 bmap
.RemoveChild(&header_view
);
2072 bmap
.RemoveChild(&line
);
2078 TMailWindow::PrintSetup()
2080 BPrintJob
printJob("mail_print");
2082 if (fApp
->HasPrintSettings()) {
2083 BMessage printSettings
= fApp
->PrintSettings();
2084 printJob
.SetSettings(new BMessage(printSettings
));
2087 if (printJob
.ConfigPage() == B_OK
)
2088 fApp
->SetPrintSettings(printJob
.Settings());
2093 TMailWindow::SetTo(const char* mailTo
, const char* subject
, const char* ccTo
,
2094 const char* bccTo
, const BString
* body
, BMessage
* enclosures
)
2098 if (mailTo
!= NULL
&& mailTo
[0])
2099 fHeaderView
->SetTo(mailTo
);
2100 if (subject
!= NULL
&& subject
[0])
2101 fHeaderView
->SetSubject(subject
);
2102 if (ccTo
!= NULL
&& ccTo
[0])
2103 fHeaderView
->SetCc(ccTo
);
2104 if (bccTo
!= NULL
&& bccTo
[0])
2105 fHeaderView
->SetBcc(bccTo
);
2107 if (body
!= NULL
&& body
->Length()) {
2108 fContentView
->TextView()->SetText(body
->String(), body
->Length());
2109 fContentView
->TextView()->GoToLine(0);
2112 if (enclosures
&& enclosures
->HasRef("refs"))
2113 AddEnclosure(enclosures
);
2120 TMailWindow::CopyMessage(entry_ref
* ref
, TMailWindow
* src
)
2123 if (file
.InitCheck() == B_OK
) {
2125 if (file
.ReadAttrString(B_MAIL_ATTR_TO
, &string
) == B_OK
)
2126 fHeaderView
->SetTo(string
);
2128 if (file
.ReadAttrString(B_MAIL_ATTR_SUBJECT
, &string
) == B_OK
)
2129 fHeaderView
->SetSubject(string
);
2131 if (file
.ReadAttrString(B_MAIL_ATTR_CC
, &string
) == B_OK
)
2132 fHeaderView
->SetCc(string
);
2135 TTextView
* text
= src
->fContentView
->TextView();
2136 text_run_array
* style
= text
->RunArray(0, text
->TextLength());
2138 fContentView
->TextView()->SetText(text
->Text(), text
->TextLength(), style
);
2145 TMailWindow::Reply(entry_ref
* ref
, TMailWindow
* window
, uint32 type
)
2147 fRepliedMail
= *ref
;
2148 SetOriginatingWindow(window
);
2150 BEmailMessage
* mail
= window
->Mail();
2154 if (type
== M_REPLY_ALL
)
2155 type
= B_MAIL_REPLY_TO_ALL
;
2156 else if (type
== M_REPLY_TO_SENDER
)
2157 type
= B_MAIL_REPLY_TO_SENDER
;
2159 type
= B_MAIL_REPLY_TO
;
2161 uint32 useAccountFrom
= fApp
->UseAccountFrom();
2163 fMail
= mail
->ReplyMessage(mail_reply_to_mode(type
),
2164 useAccountFrom
== ACCOUNT_FROM_MAIL
, QUOTE
);
2166 // set header fields
2167 fHeaderView
->SetTo(fMail
->To());
2168 fHeaderView
->SetCc(fMail
->CC());
2169 fHeaderView
->SetSubject(fMail
->Subject());
2172 BFile
file(window
->fRef
, B_READ_ONLY
);
2173 if (file
.ReadAttr("MAIL:reply_with", B_INT32_TYPE
, 0, &accountID
,
2174 sizeof(int32
)) != B_OK
)
2179 if ((useAccountFrom
== ACCOUNT_FROM_MAIL
) || (accountID
> -1)) {
2180 if (useAccountFrom
== ACCOUNT_FROM_MAIL
)
2181 fHeaderView
->SetAccount(fMail
->Account());
2183 fHeaderView
->SetAccount(accountID
);
2186 // create preamble string
2188 BString preamble
= fApp
->ReplyPreamble();
2191 mail
->GetName(&name
);
2192 if (name
.Length() <= 0)
2193 name
= B_TRANSLATE("(Name unavailable)");
2195 BString
address(mail
->From());
2196 if (address
.Length() <= 0)
2197 address
= B_TRANSLATE("(Address unavailable)");
2199 BString
date(mail
->HeaderField("Date"));
2200 if (date
.Length() <= 0)
2201 date
= B_TRANSLATE("(Date unavailable)");
2203 preamble
.ReplaceAll("%n", name
);
2204 preamble
.ReplaceAll("%e", address
);
2205 preamble
.ReplaceAll("%d", date
);
2206 preamble
.ReplaceAll("\\n", "\n");
2208 // insert (if selection) or load (if whole mail) message text into text view
2210 int32 finish
, start
;
2211 window
->fContentView
->TextView()->GetSelection(&start
, &finish
);
2212 if (start
!= finish
) {
2213 char* text
= (char*)malloc(finish
- start
+ 1);
2217 window
->fContentView
->TextView()->GetText(start
, finish
- start
, text
);
2218 if (text
[strlen(text
) - 1] != '\n') {
2219 text
[strlen(text
)] = '\n';
2222 fContentView
->TextView()->SetText(text
, finish
- start
);
2225 finish
= fContentView
->TextView()->CountLines();
2226 for (int32 loop
= 0; loop
< finish
; loop
++) {
2227 fContentView
->TextView()->GoToLine(loop
);
2228 fContentView
->TextView()->Insert((const char*)QUOTE
);
2231 if (fApp
->ColoredQuotes()) {
2232 const BFont
* font
= fContentView
->TextView()->Font();
2233 int32 length
= fContentView
->TextView()->TextLength();
2235 TextRunArray
style(length
/ 8 + 8);
2237 FillInQuoteTextRuns(fContentView
->TextView(), NULL
,
2238 fContentView
->TextView()->Text(), length
, font
, &style
.Array(),
2239 style
.MaxEntries());
2241 fContentView
->TextView()->SetRunArray(0, length
, &style
.Array());
2244 fContentView
->TextView()->GoToLine(0);
2245 if (preamble
.Length() > 0)
2246 fContentView
->TextView()->Insert(preamble
);
2248 fContentView
->TextView()->LoadMessage(mail
, true, preamble
);
2256 TMailWindow::Send(bool now
)
2259 status_t status
= SaveAsDraft();
2260 if (status
!= B_OK
) {
2262 BAlert
* alert
= new BAlert("", B_TRANSLATE("E-mail draft could "
2263 "not be saved!"), B_TRANSLATE("OK"));
2264 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
2270 uint32 characterSetToUse
= _CurrentCharacterSet();
2271 mail_encoding encodingForBody
= quoted_printable
;
2272 mail_encoding encodingForHeaders
= quoted_printable
;
2274 // Set up the encoding to use for converting binary to printable ASCII.
2275 // Normally this will be quoted printable, but for some old software,
2276 // particularly Japanese stuff, they only understand base64. They also
2277 // prefer it for the smaller size. Later on this will be reduced to 7bit
2278 // if the encoded text is just 7bit characters.
2279 if (characterSetToUse
== B_SJIS_CONVERSION
2280 || characterSetToUse
== B_EUC_CONVERSION
)
2281 encodingForBody
= base64
;
2282 else if (characterSetToUse
== B_JIS_CONVERSION
2283 || characterSetToUse
== B_MAIL_US_ASCII_CONVERSION
2284 || characterSetToUse
== B_ISO1_CONVERSION
2285 || characterSetToUse
== B_EUC_KR_CONVERSION
)
2286 encodingForBody
= eight_bit
;
2288 // Using quoted printable headers on almost completely non-ASCII Japanese
2289 // is a waste of time. Besides, some stupid cell phone services need
2290 // base64 in the headers.
2291 if (characterSetToUse
== B_SJIS_CONVERSION
2292 || characterSetToUse
== B_EUC_CONVERSION
2293 || characterSetToUse
== B_JIS_CONVERSION
2294 || characterSetToUse
== B_EUC_KR_CONVERSION
)
2295 encodingForHeaders
= base64
;
2297 // Count the number of characters in the message body which aren't in the
2298 // currently selected character set. Also see if the resulting encoded
2299 // text can safely use 7 bit characters.
2300 if (fContentView
->TextView()->TextLength() > 0) {
2301 // First do a trial encoding with the user's character set.
2302 int32 converterState
= 0;
2303 int32 originalLength
;
2305 int32 tempStringLength
;
2306 char* tempStringPntr
;
2307 originalLength
= fContentView
->TextView()->TextLength();
2308 tempStringLength
= originalLength
* 6;
2309 // Some character sets bloat up on escape codes
2310 tempStringPntr
= tempString
.LockBuffer (tempStringLength
);
2311 if (tempStringPntr
!= NULL
&& mail_convert_from_utf8(characterSetToUse
,
2312 fContentView
->TextView()->Text(), &originalLength
,
2313 tempStringPntr
, &tempStringLength
, &converterState
,
2314 0x1A /* used for unknown characters */) == B_OK
) {
2315 // Check for any characters which don't fit in a 7 bit encoding.
2317 bool has8Bit
= false;
2318 for (i
= 0; i
< tempStringLength
; i
++) {
2319 if (tempString
[i
] == 0 || (tempString
[i
] & 0x80)) {
2325 encodingForBody
= seven_bit
;
2326 tempString
.UnlockBuffer (tempStringLength
);
2328 // Count up the number of unencoded characters and warn the user
2329 if (fApp
->WarnAboutUnencodableCharacters()) {
2330 // TODO: ideally, the encoding should be silently changed to
2331 // one that can express this character
2334 while (offset
>= 0) {
2335 offset
= tempString
.FindFirst (0x1A, offset
);
2339 // Don't get stuck finding the same character again.
2344 BString messageString
;
2345 BString countString
;
2346 countString
<< count
;
2347 messageString
<< B_TRANSLATE("Your main text contains %ld"
2348 " unencodable characters. Perhaps a different "
2349 "character set would work better? Hit Send to send it "
2351 "(a substitute character will be used in place of "
2352 "the unencodable ones), or choose Cancel to go back "
2353 "and try fixing it up.");
2354 messageString
.ReplaceFirst("%ld", countString
);
2355 BAlert
* alert
= new BAlert("Question", messageString
.String(),
2356 B_TRANSLATE("Send"),
2357 B_TRANSLATE("Cancel"),
2358 NULL
, B_WIDTH_AS_USUAL
, B_OFFSET_SPACING
,
2360 alert
->SetShortcut(1, B_ESCAPE
);
2361 userAnswer
= alert
->Go();
2363 if (userAnswer
== 1) {
2364 // Cancel was picked.
2373 // depending on the system (and I/O) load, this could take a while
2374 // but the user shouldn't be left waiting
2379 BFile
file(fRef
, O_RDONLY
);
2380 result
= file
.InitCheck();
2381 if (result
== B_OK
) {
2382 BEmailMessage
mail(&file
);
2383 mail
.SetTo(fHeaderView
->To(), characterSetToUse
,
2384 encodingForHeaders
);
2386 if (fHeaderView
->AccountID() != ~0L)
2387 mail
.SendViaAccount(fHeaderView
->AccountID());
2389 result
= mail
.Send(now
);
2393 // the mail will be deleted when the window is closed
2394 fMail
= new BEmailMessage
;
2396 // Had an embarrassing bug where replying to a message and clearing the
2397 // CC field meant that it got sent out anyway, so pass in empty strings
2398 // when changing the header to force it to remove the header.
2400 fMail
->SetTo(fHeaderView
->To(), characterSetToUse
, encodingForHeaders
);
2401 fMail
->SetSubject(fHeaderView
->Subject(), characterSetToUse
,
2402 encodingForHeaders
);
2403 fMail
->SetCC(fHeaderView
->Cc(), characterSetToUse
, encodingForHeaders
);
2404 fMail
->SetBCC(fHeaderView
->Bcc());
2406 //--- Add X-Mailer field
2410 memset(&info
, 0, sizeof(version_info
));
2413 if (be_app
->GetAppInfo(&appInfo
) == B_OK
) {
2414 BFile
file(&appInfo
.ref
, B_READ_ONLY
);
2415 if (file
.InitCheck() == B_OK
) {
2416 BAppFileInfo
appFileInfo(&file
);
2417 if (appFileInfo
.InitCheck() == B_OK
)
2418 appFileInfo
.GetVersionInfo(&info
, B_APP_VERSION_KIND
);
2422 char versionString
[255];
2423 sprintf(versionString
,
2424 "Mail/Haiku %" B_PRIu32
".%" B_PRIu32
".%" B_PRIu32
,
2425 info
.major
, info
.middle
, info
.minor
);
2426 fMail
->SetHeaderField("X-Mailer", versionString
);
2431 // the content text is always added to make sure there is a mail body
2432 fMail
->SetBodyTextTo("");
2433 fContentView
->TextView()->AddAsContent(fMail
, fApp
->WrapMode(),
2434 characterSetToUse
, encodingForBody
);
2436 if (fEnclosuresView
!= NULL
) {
2439 while ((item
= (TListItem
*)fEnclosuresView
->fList
->ItemAt(index
++))
2441 if (item
->Component())
2444 // leave out missing enclosures
2445 BEntry
entry(item
->Ref());
2446 if (!entry
.Exists())
2449 fMail
->Attach(item
->Ref(), fApp
->AttachAttributes());
2452 if (fHeaderView
->AccountID() != ~0L)
2453 fMail
->SendViaAccount(fHeaderView
->AccountID());
2455 result
= fMail
->Send(now
);
2458 // Set status of the replied mail
2460 BNode
node(&fRepliedMail
);
2461 if (node
.InitCheck() >= B_OK
) {
2462 if (fOriginatingWindow
) {
2463 BMessage
msg(M_SAVE_POSITION
), reply
;
2464 fOriginatingWindow
->SendMessage(&msg
, &reply
);
2466 WriteAttrString(&node
, B_MAIL_ATTR_STATUS
, "Replied");
2472 BString errorMessage
;
2479 // If it's a draft, remove the draft file
2486 case B_MAIL_NO_DAEMON
:
2491 BAlert
* alert
= new BAlert("no daemon",
2492 B_TRANSLATE("The mail_daemon is not running. The message is "
2493 "queued and will be sent when the mail_daemon is started."),
2494 B_TRANSLATE("Start now"), B_TRANSLATE("OK"));
2495 alert
->SetShortcut(1, B_ESCAPE
);
2496 int32 start
= alert
->Go();
2500 result
= daemon
.Launch();
2501 if (result
== B_OK
) {
2502 daemon
.SendQueuedMail();
2505 << B_TRANSLATE("The mail_daemon could not be "
2507 << strerror(result
);
2513 // case B_MAIL_UNKNOWN_HOST:
2514 // case B_MAIL_ACCESS_ERROR:
2515 // sprintf(errorMessage,
2516 // "An error occurred trying to connect with the SMTP "
2517 // "host. Check your SMTP host name.");
2520 // case B_MAIL_NO_RECIPIENT:
2521 // sprintf(errorMessage,
2522 // "You must have either a \"To\" or \"Bcc\" recipient.");
2526 errorMessage
<< "An error occurred trying to send mail:\n\t"
2527 << strerror(result
);
2531 if (result
!= B_NO_ERROR
&& result
!= B_MAIL_NO_DAEMON
) {
2533 BAlert
* alert
= new BAlert("", errorMessage
.String(),
2535 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
2539 PostMessage(B_QUIT_REQUESTED
);
2541 // The window was hidden earlier
2550 TMailWindow::SaveAsDraft()
2558 status_t status
= draft
.SetTo(fRef
,
2559 B_WRITE_ONLY
| B_CREATE_FILE
| B_ERASE_FILE
);
2563 // Get the user home directory
2564 status_t status
= find_directory(B_USER_DIRECTORY
, &draftPath
);
2568 // Append the relative path of the draft directory
2569 draftPath
.Append(kDraftPath
);
2572 status
= dir
.SetTo(draftPath
.Path());
2574 // Create the directory if it does not exist
2575 case B_ENTRY_NOT_FOUND
:
2576 if ((status
= dir
.CreateDirectory(draftPath
.Path(), &dir
))
2581 char fileName
[B_FILE_NAME_LENGTH
];
2582 // save as some version of the message's subject
2583 if (fHeaderView
->IsSubjectEmpty()) {
2584 strlcpy(fileName
, B_TRANSLATE("Untitled"),
2587 strlcpy(fileName
, fHeaderView
->Subject(), sizeof(fileName
));
2590 uint32 originalLength
= strlen(fileName
);
2592 // convert /, \ and : to -
2593 for (char* bad
= fileName
; (bad
= strchr(bad
, '/')) != NULL
;
2597 for (char* bad
= fileName
; (bad
= strchr(bad
, '\\')) != NULL
;
2601 for (char* bad
= fileName
; (bad
= strchr(bad
, ':')) != NULL
;
2606 // Create the file; if the name exists, find a unique name
2607 flags
= B_WRITE_ONLY
| B_CREATE_FILE
| B_FAIL_IF_EXISTS
;
2610 status
= draft
.SetTo(&dir
, fileName
, flags
);
2613 char appendix
[B_FILE_NAME_LENGTH
];
2614 sprintf(appendix
, " %" B_PRId32
, i
++);
2615 int32 pos
= min_c(sizeof(fileName
) - strlen(appendix
),
2617 sprintf(fileName
+ pos
, "%s", appendix
);
2618 } while (status
== B_FILE_EXISTS
);
2624 fRef
= new entry_ref
;
2625 BEntry
entry(&dir
, fileName
);
2634 // Write the content of the message
2635 draft
.Write(fContentView
->TextView()->Text(),
2636 fContentView
->TextView()->TextLength());
2638 // Add the header stuff as attributes
2639 WriteAttrString(&draft
, B_MAIL_ATTR_NAME
, fHeaderView
->To());
2640 WriteAttrString(&draft
, B_MAIL_ATTR_TO
, fHeaderView
->To());
2641 WriteAttrString(&draft
, B_MAIL_ATTR_SUBJECT
, fHeaderView
->Subject());
2642 if (!fHeaderView
->IsCcEmpty())
2643 WriteAttrString(&draft
, B_MAIL_ATTR_CC
, fHeaderView
->Cc());
2644 if (!fHeaderView
->IsBccEmpty())
2645 WriteAttrString(&draft
, B_MAIL_ATTR_BCC
, fHeaderView
->Bcc());
2648 if (fHeaderView
->AccountName() != NULL
) {
2649 WriteAttrString(&draft
, B_MAIL_ATTR_ACCOUNT
,
2650 fHeaderView
->AccountName());
2654 BMenuItem
* menuItem
= fEncodingMenu
->FindMarked();
2655 if (menuItem
!= NULL
)
2656 WriteAttrString(&draft
, "MAIL:encoding", menuItem
->Label());
2658 // Add the draft attribute for indexing
2659 uint32 draftAttr
= true;
2660 draft
.WriteAttr("MAIL:draft", B_INT32_TYPE
, 0, &draftAttr
, sizeof(uint32
));
2662 // Add Attachment paths in attribute
2663 if (fEnclosuresView
!= NULL
) {
2667 for (int32 i
= 0; (item
= (TListItem
*)fEnclosuresView
->fList
->ItemAt(i
))
2670 pathStr
.Append(":");
2672 BEntry
entry(item
->Ref(), true);
2673 if (!entry
.Exists())
2677 entry
.GetPath(&path
);
2678 pathStr
.Append(path
.Path());
2680 if (pathStr
.Length())
2681 draft
.WriteAttrString("MAIL:attachments", &pathStr
);
2684 // Set the MIME Type of the file
2685 BNodeInfo
info(&draft
);
2686 info
.SetType(kDraftType
);
2691 fToolBar
->SetActionEnabled(M_SAVE_AS_DRAFT
, false);
2698 TMailWindow::TrainMessageAs(const char* commandWord
)
2700 status_t errorCode
= -1;
2703 BMessage replyMessage
;
2704 BMessage scriptingMessage
;
2708 goto ErrorExit
; // Need to have a real file and name.
2709 errorCode
= fileEntry
.SetTo(fRef
, true);
2710 if (errorCode
!= B_OK
)
2712 errorCode
= fileEntry
.GetPath(&filePath
);
2713 if (errorCode
!= B_OK
)
2717 // Get a connection to the spam database server. Launch if needed.
2719 if (!fMessengerToSpamServer
.IsValid()) {
2720 // Make sure the server is running.
2721 if (!be_roster
->IsRunning (kSpamServerSignature
)) {
2722 errorCode
= be_roster
->Launch (kSpamServerSignature
);
2723 if (errorCode
!= B_OK
) {
2726 directory_which places
[] = {B_SYSTEM_NONPACKAGED_BIN_DIRECTORY
,
2727 B_SYSTEM_BIN_DIRECTORY
};
2728 for (int32 i
= 0; i
< 2; i
++) {
2729 find_directory(places
[i
],&path
);
2730 path
.Append("spamdbm");
2731 if (!BEntry(path
.Path()).Exists())
2733 get_ref_for_path(path
.Path(),&ref
);
2735 errorCode
= be_roster
->Launch(&ref
);
2736 if (errorCode
== B_OK
)
2739 if (errorCode
!= B_OK
)
2744 // Set up the messenger to the database server.
2745 errorCode
= B_SERVER_NOT_FOUND
;
2746 serverTeam
= be_roster
->TeamFor(kSpamServerSignature
);
2750 fMessengerToSpamServer
= BMessenger (kSpamServerSignature
, serverTeam
,
2753 if (!fMessengerToSpamServer
.IsValid())
2757 // Ask the server to train on the message. Give it the command word and
2758 // the absolute path name to use.
2760 scriptingMessage
.MakeEmpty();
2761 scriptingMessage
.what
= B_SET_PROPERTY
;
2762 scriptingMessage
.AddSpecifier(commandWord
);
2763 errorCode
= scriptingMessage
.AddData("data", B_STRING_TYPE
,
2764 filePath
.Path(), strlen(filePath
.Path()) + 1, false);
2765 if (errorCode
!= B_OK
)
2767 replyMessage
.MakeEmpty();
2768 errorCode
= fMessengerToSpamServer
.SendMessage(&scriptingMessage
,
2770 if (errorCode
!= B_OK
2771 || replyMessage
.FindInt32("error", &errorCode
) != B_OK
2772 || errorCode
!= B_OK
)
2773 goto ErrorExit
; // Classification failed in one of many ways.
2775 SetTitleForMessage();
2776 // Update window title to show new spam classification.
2781 char errorString
[1500];
2782 snprintf(errorString
, sizeof(errorString
), "Unable to train the message "
2783 "file \"%s\" as %s. Possibly useful error code: %s (%" B_PRId32
").",
2784 filePath
.Path(), commandWord
, strerror(errorCode
), errorCode
);
2785 BAlert
* alert
= new BAlert("", errorString
, B_TRANSLATE("OK"));
2786 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
2794 TMailWindow::SetTitleForMessage()
2796 // Figure out the title of this message and set the title bar
2797 BString title
= B_TRANSLATE_SYSTEM_NAME("Mail");
2800 if (fMail
->GetName(&title
) == B_OK
)
2801 title
<< ": \"" << fMail
->Subject() << "\"";
2803 title
= fMail
->Subject();
2806 title
.Prepend("Downloading: ");
2808 if (fApp
->ShowSpamGUI() && fRef
!= NULL
) {
2809 BString classification
;
2811 char numberString
[30];
2812 BString
oldTitle(title
);
2814 if (node
.InitCheck() != B_OK
|| node
.ReadAttrString(
2815 "MAIL:classification", &classification
) != B_OK
)
2816 classification
= "Unrated";
2817 if (classification
!= "Spam" && classification
!= "Genuine") {
2818 // Uncertain, Unrated and other unknown classes, show the ratio.
2819 if (node
.InitCheck() == B_OK
&& node
.ReadAttr("MAIL:ratio_spam",
2820 B_FLOAT_TYPE
, 0, &spamRatio
, sizeof(spamRatio
))
2821 == sizeof(spamRatio
)) {
2822 sprintf(numberString
, "%.4f", spamRatio
);
2823 classification
<< " " << numberString
;
2827 title
<< "[" << classification
<< "] " << oldTitle
;
2834 /*! Open *another* message in the existing mail window. Some code here is
2835 duplicated from various constructors.
2836 TODO: The duplicated code should be moved to a private initializer method
2839 TMailWindow::OpenMessage(const entry_ref
* ref
, uint32 characterSetForDecoding
)
2844 // Set some references to the email file
2846 fRef
= new entry_ref(*ref
);
2848 fPrevTrackerPositionSaved
= false;
2849 fNextTrackerPositionSaved
= false;
2851 fContentView
->TextView()->StopLoad();
2855 BFile
file(fRef
, B_READ_ONLY
);
2856 status_t err
= file
.InitCheck();
2861 BNodeInfo
fileInfo(&file
);
2862 fileInfo
.GetType(mimeType
);
2864 if (strcmp(mimeType
, B_PARTIAL_MAIL_TYPE
) == 0) {
2865 BMessenger
listener(this);
2866 status_t status
= BMailDaemon().FetchBody(*ref
, &listener
);
2868 fprintf(stderr
, "Could not fetch body: %s\n", strerror(status
));
2869 fileInfo
.GetType(mimeType
);
2870 _SetDownloading(true);
2872 _SetDownloading(false);
2874 // Check if it's a draft file, which contains only the text, and has the
2875 // from, to, bcc, attachments listed as attributes.
2876 if (strcmp(kDraftType
, mimeType
) == 0) {
2881 fMail
= new BEmailMessage
; // Not really used much, but still needed.
2883 // Load the raw UTF-8 text from the file.
2884 file
.GetSize(&size
);
2885 fContentView
->TextView()->SetText(&file
, 0, size
);
2887 // Restore Fields from attributes
2888 if (node
.ReadAttrString(B_MAIL_ATTR_TO
, &string
) == B_OK
)
2889 fHeaderView
->SetTo(string
);
2890 if (node
.ReadAttrString(B_MAIL_ATTR_SUBJECT
, &string
) == B_OK
)
2891 fHeaderView
->SetSubject(string
);
2892 if (node
.ReadAttrString(B_MAIL_ATTR_CC
, &string
) == B_OK
)
2893 fHeaderView
->SetCc(string
);
2894 if (node
.ReadAttrString(B_MAIL_ATTR_BCC
, &string
) == B_OK
)
2895 fHeaderView
->SetBcc(string
);
2898 if (node
.ReadAttrString(B_MAIL_ATTR_ACCOUNT
, &string
) == B_OK
)
2899 fHeaderView
->SetAccount(string
);
2902 if (node
.ReadAttrString("MAIL:encoding", &string
) == B_OK
) {
2903 BMenuItem
* encodingItem
= fEncodingMenu
->FindItem(string
.String());
2904 if (encodingItem
!= NULL
)
2905 encodingItem
->SetMarked(true);
2908 // Restore attachments
2909 if (node
.ReadAttrString("MAIL:attachments", &string
) == B_OK
) {
2910 BMessage
msg(REFS_RECEIVED
);
2913 char* s
= strtok((char*)string
.String(), ":");
2915 BEntry
entry(s
, true);
2916 if (entry
.Exists()) {
2917 entry
.GetRef(&enc_ref
);
2918 msg
.AddRef("refs", &enc_ref
);
2920 s
= strtok(NULL
, ":");
2925 // restore the reading position if available
2926 PostMessage(M_READ_POS
);
2928 PostMessage(RESET_BUTTONS
);
2932 // A real mail message, parse its headers to get from, to, etc.
2933 fMail
= new BEmailMessage(fRef
, characterSetForDecoding
);
2935 fHeaderView
->SetFromMessage(fMail
);
2938 err
= fMail
->InitCheck();
2945 SetTitleForMessage();
2948 // Put the addresses in the 'Save Address' Menu
2950 while ((item
= fSaveAddrMenu
->RemoveItem((int32
)0)) != NULL
)
2953 // create the list of addresses
2956 get_address_list(addressList
, fMail
->To(), extract_address
);
2957 get_address_list(addressList
, fMail
->CC(), extract_address
);
2958 get_address_list(addressList
, fMail
->From(), extract_address
);
2959 get_address_list(addressList
, fMail
->ReplyTo(), extract_address
);
2963 for (int32 i
= addressList
.CountItems(); i
-- > 0;) {
2964 char* address
= (char*)addressList
.RemoveItem((int32
)0);
2966 // insert the new address in alphabetical order
2968 while ((item
= fSaveAddrMenu
->ItemAt(index
)) != NULL
) {
2969 if (!strcmp(address
, item
->Label())) {
2970 // item already in list
2974 if (strcmp(address
, item
->Label()) < 0)
2980 msg
= new BMessage(M_SAVE
);
2981 msg
->AddString("address", address
);
2982 fSaveAddrMenu
->AddItem(new BMenuItem(address
, msg
), index
);
2988 // Clear out existing contents of text view.
2989 fContentView
->TextView()->SetText("", (int32
)0);
2991 fContentView
->TextView()->LoadMessage(fMail
, false, NULL
);
2993 if (fApp
->ShowToolBar())
2994 _UpdateReadButton();
3002 TMailWindow::FrontmostWindow()
3004 BAutolock
locker(sWindowListLock
);
3005 if (sWindowList
.CountItems() > 0)
3006 return (TMailWindow
*)sWindowList
.ItemAt(0);
3016 TMailWindow::_GetQueryPath(BPath
* queryPath
) const
3018 // get the user home directory and from there the query folder
3019 status_t ret
= find_directory(B_USER_DIRECTORY
, queryPath
);
3021 ret
= queryPath
->Append(kQueriesDirectory
);
3028 TMailWindow::_RebuildQueryMenu(bool firstTime
)
3030 while (fQueryMenu
->ItemAt(0)) {
3031 BMenuItem
* item
= fQueryMenu
->RemoveItem((int32
)0);
3035 fQueryMenu
->AddItem(new BMenuItem(B_TRANSLATE("Edit queries"
3037 new BMessage(M_EDIT_QUERIES
), 'E', B_SHIFT_KEY
));
3039 bool queryItemsAdded
= false;
3042 if (_GetQueryPath(&queryPath
) < B_OK
)
3045 BDirectory
queryDir(queryPath
.Path());
3048 BPrivate::BPathMonitor::StartWatching(queryPath
.Path(),
3049 B_WATCH_RECURSIVELY
, BMessenger(this, this));
3052 // If we find the named query, add it to the menu.
3054 while (queryDir
.GetNextEntry(&entry
) == B_OK
) {
3055 char name
[B_FILE_NAME_LENGTH
+ 1];
3056 entry
.GetName(name
);
3058 char* queryString
= _BuildQueryString(&entry
);
3059 if (queryString
== NULL
)
3062 queryItemsAdded
= true;
3064 QueryMenu
* queryMenu
= new QueryMenu(name
, false);
3065 queryMenu
->SetTargetForItems(be_app
);
3066 queryMenu
->SetPredicate(queryString
);
3067 fQueryMenu
->AddItem(queryMenu
);
3072 if (queryItemsAdded
)
3073 fQueryMenu
->AddItem(new BSeparatorItem(), 1);
3078 TMailWindow::_BuildQueryString(BEntry
* entry
) const
3081 if (node
.InitCheck() != B_OK
)
3085 if (node
.ReadAttr(kAttrQueryInitialMode
, B_INT32_TYPE
, 0, (int32
*)&mode
,
3086 sizeof(int32
)) <= 0) {
3090 BString queryString
;
3092 case kByForumlaItem
:
3095 if (node
.ReadAttrString(kAttrQueryInitialString
, &buffer
) == B_OK
)
3096 queryString
<< buffer
;
3103 if (node
.ReadAttrString(kAttrQueryInitialString
, &buffer
) == B_OK
)
3104 queryString
<< "(name==*" << buffer
<< "*)";
3108 case kByAttributeItem
:
3111 if (node
.ReadAttr(kAttrQueryInitialNumAttrs
, B_INT32_TYPE
, 0,
3112 (int32
*)&count
, sizeof(int32
)) <= 0) {
3117 if (node
.GetAttrInfo(kAttrQueryInitialAttrs
, &info
) != B_OK
)
3123 char* buffer
= new char[info
.size
];
3124 if (node
.ReadAttr(kAttrQueryInitialAttrs
, B_MESSAGE_TYPE
, 0,
3125 buffer
, (size_t)info
.size
) == info
.size
) {
3127 if (message
.Unflatten(buffer
) == B_OK
) {
3128 for (int32 index
= 0; /*index < count*/; index
++) {
3131 if (message
.FindString("menuSelection", index
, &field
)
3133 || message
.FindString("attrViewText", index
, &value
)
3138 // ignore the mime type, we'll force it to be email
3140 if (strcmp(field
, "BEOS:TYPE") != 0) {
3141 // TODO: check if subMenu contains the type of
3142 // comparison we are suppose to make here
3143 queryString
<< "(" << field
<< "==\""
3146 int32 logicMenuSelectedIndex
;
3147 if (message
.FindInt32("logicalRelation", index
,
3148 &logicMenuSelectedIndex
) == B_OK
) {
3149 if (logicMenuSelectedIndex
== 0)
3150 queryString
<< "&&";
3151 else if (logicMenuSelectedIndex
== 1)
3152 queryString
<< "||";
3171 if (queryString
.Length() == 0)
3174 // force it to check for email only
3175 if (queryString
.FindFirst("text/x-email") < 0) {
3177 temp
<< "(" << queryString
<< "&&(BEOS:TYPE==\"text/x-email\"))";
3181 return strdup(queryString
.String());
3186 TMailWindow::_AddReadButton()
3190 read_flags flag
= B_UNREAD
;
3191 read_read_attr(node
, flag
);
3193 if (flag
== B_READ
) {
3194 fToolBar
->SetActionVisible(M_UNREAD
, true);
3195 fToolBar
->SetActionVisible(M_READ
, false);
3197 fToolBar
->SetActionVisible(M_UNREAD
, false);
3198 fToolBar
->SetActionVisible(M_READ
, true);
3204 TMailWindow::_UpdateReadButton()
3206 if (fApp
->ShowToolBar()) {
3207 if (!fAutoMarkRead
&& fIncoming
)
3215 TMailWindow::_UpdateLabel(uint32 command
, const char* label
, bool show
)
3217 BButton
* button
= fToolBar
->FindButton(command
);
3218 if (button
!= NULL
) {
3219 button
->SetLabel(show
? label
: NULL
);
3220 button
->SetToolTip(show
? NULL
: label
);
3226 TMailWindow::_SetDownloading(bool downloading
)
3228 fDownloading
= downloading
;
3233 TMailWindow::_CurrentCharacterSet() const
3235 uint32 defaultCharSet
= fResending
|| !fIncoming
3236 ? fApp
->MailCharacterSet() : B_MAIL_NULL_CONVERSION
;
3238 BMenuItem
* marked
= fEncodingMenu
->FindMarked();
3240 return defaultCharSet
;
3242 return marked
->Message()->GetInt32("charset", defaultCharSet
);