1/*
2 * Copyright 2009-2013, Axel Dörfler, axeld@pinc-software.de.
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include <new>
8
9#include <Directory.h>
10#include <E-mail.h>
11#include <Entry.h>
12#include <FindDirectory.h>
13#include <InterfaceDefs.h>
14#include <MailDaemon.h>
15#include <mail_util.h>
16#include <MenuItem.h>
17#include <Message.h>
18#include <Node.h>
19#include <Path.h>
20#include <PopUpMenu.h>
21#include <String.h>
22
23
24static BPoint
25mouse_position()
26{
27	// Returns the mouse position in screen coordinates
28	BPoint position;
29	get_mouse(&position, NULL);
30	return position;
31}
32
33
34static void
35add_status_item(BMenu* menu, const char* name)
36{
37	if (menu->FindItem(name) != NULL)
38		return;
39
40	// Sort items alphabetically
41	int32 index;
42	for (index = 0; index < menu->CountItems(); index++) {
43		if (strcmp(menu->ItemAt(index)->Label(), name) > 0)
44			break;
45	}
46
47	menu->AddItem(new BMenuItem(name, NULL), index);
48}
49
50
51static void
52retrieve_status_items(BMenu* menu)
53{
54	BPath path;
55	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
56		return;
57
58	path.Append("Mail/status");
59
60	BDirectory directory(path.Path());
61
62	entry_ref ref;
63	while (directory.GetNextRef(&ref) == B_OK) {
64		if (!strcmp(ref.name, ".") || !strcmp(ref.name, ".."))
65			continue;
66
67		add_status_item(menu, ref.name);
68	}
69
70	add_status_item(menu, "New");
71	add_status_item(menu, "Read");
72	add_status_item(menu, "Replied");
73}
74
75
76// #pragma mark -
77
78
79extern "C" void
80process_refs(entry_ref dir, BMessage* message, void* /*reserved*/)
81{
82	BPopUpMenu* menu = new BPopUpMenu("status");
83	retrieve_status_items(menu);
84
85	BMenuItem* item = menu->Go(mouse_position() - BPoint(5, 5), false, true);
86	if (item == NULL)
87		return;
88
89	BString status = item->Label();
90	//TODO:This won't work anymore when the menu gets translated! Use index!
91
92	entry_ref ref;
93	for (int i = 0; message->FindRef("refs", i, &ref) == B_OK; i++) {
94		BNode node(&ref);
95		BString type;
96
97		if (node.InitCheck() == B_OK
98			&& node.ReadAttrString("BEOS:TYPE", &type) == B_OK
99			&& (type == B_MAIL_TYPE || type == B_PARTIAL_MAIL_TYPE)) {
100			BString previousStatus;
101			read_flags previousRead;
102
103			// Update the MAIL:read flag
104			if (status == "New") {
105				if (read_read_attr(node, previousRead) != B_OK ||
106					previousRead != B_UNREAD)
107					write_read_attr(node, B_UNREAD);
108			}
109			else if (status == "Read") {
110				// if we're marking it via the add-on, we haven't really read it
111				// so use B_SEEN instead of B_READ
112				// Check both B_SEEN and B_READ
113				// (so we don't overwrite B_READ with B_SEEN)
114				if (read_read_attr(node, previousRead) != B_OK ||
115					(previousRead != B_SEEN && previousRead != B_READ)) {
116					int32 account;
117					if (node.ReadAttr(B_MAIL_ATTR_ACCOUNT_ID, B_INT32_TYPE,
118							0LL, &account, sizeof(account)) == sizeof(account))
119						BMailDaemon().MarkAsRead(account, ref, B_SEEN);
120					else
121						write_read_attr(node, B_SEEN);
122				}
123			}
124			// ignore "Replied"; no matching MAIL:read status
125
126			// We want to keep the previous behavior of updating the status
127			// string, but write_read_attr will only change the status string
128			// if it's one of "New", "Seen", or "Read" (and not, for example,
129			// "Replied"), so we change the status string here
130			// Only update the attribute if there is an actual change
131			if (node.ReadAttrString(B_MAIL_ATTR_STATUS, &previousStatus) != B_OK
132				|| previousStatus != status)
133				node.WriteAttrString(B_MAIL_ATTR_STATUS, &status);
134		}
135	}
136}
137
138