xref: /haiku/src/preferences/notifications/NotificationsView.cpp (revision 7a74a5df454197933bc6e80a542102362ee98703)
1 /*
2  * Copyright 2010, Haiku, Inc. All Rights Reserved.
3  * Copyright 2009, Pier Luigi Fiorini.
4  * Distributed under the terms of the MIT License.
5  *
6  * Authors:
7  *		Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
8  */
9 
10 #include <Alert.h>
11 #include <Catalog.h>
12 #include <Directory.h>
13 #include <FindDirectory.h>
14 #include <GroupLayout.h>
15 #include <GroupLayoutBuilder.h>
16 #include <Window.h>
17 #include <CheckBox.h>
18 #include <TextControl.h>
19 #include <Path.h>
20 #include <Notification.h>
21 #include <notification/Notifications.h>
22 #include <notification/NotificationReceived.h>
23 
24 #include <ColumnListView.h>
25 #include <ColumnTypes.h>
26 
27 #include "NotificationsView.h"
28 
29 #undef B_TRANSLATION_CONTEXT
30 #define B_TRANSLATION_CONTEXT "NotificationView"
31 
32 const float kEdgePadding = 5.0;
33 const float kCLVTitlePadding = 8.0;
34 
35 const int32 kApplicationSelected = '_ASL';
36 const int32 kNotificationSelected = '_NSL';
37 
38 const int32 kCLVDeleteRow = 'av02';
39 
40 // Applications column indexes
41 const int32 kAppIndex = 0;
42 const int32 kAppEnabledIndex = 1;
43 
44 // Notifications column indexes
45 const int32 kTitleIndex = 0;
46 const int32 kDateIndex = 1;
47 const int32 kTypeIndex = 2;
48 const int32 kAllowIndex = 3;
49 
50 const int32 kSettingChanged = '_STC';
51 
52 
53 NotificationsView::NotificationsView()
54 	:
55 	BView("apps", B_WILL_DRAW)
56 {
57 	BRect rect(0, 0, 100, 100);
58 
59 	// Search application field
60 	fSearch = new BTextControl(B_TRANSLATE("Search:"), NULL,
61 		new BMessage(kSettingChanged));
62 
63 	// Applications list
64 	fApplications = new BColumnListView(rect, B_TRANSLATE("Applications"),
65 		0, B_WILL_DRAW, B_FANCY_BORDER, true);
66 	fApplications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
67 
68 	fAppCol = new BStringColumn(B_TRANSLATE("Application"), 200,
69 		be_plain_font->StringWidth(B_TRANSLATE("Application")) +
70 		(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
71 	fApplications->AddColumn(fAppCol, kAppIndex);
72 
73 	fAppEnabledCol = new BStringColumn(B_TRANSLATE("Enabled"), 10,
74 		be_plain_font->StringWidth(B_TRANSLATE("Enabled")) +
75 		(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
76 	fApplications->AddColumn(fAppEnabledCol, kAppEnabledIndex);
77 
78 	// Notifications list
79 	fNotifications = new BColumnListView(rect, B_TRANSLATE("Notifications"),
80 		0, B_WILL_DRAW, B_FANCY_BORDER, true);
81 	fNotifications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
82 
83 	fTitleCol = new BStringColumn(B_TRANSLATE("Title"), 100,
84 		be_plain_font->StringWidth(B_TRANSLATE("Title")) +
85 		(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
86 	fNotifications->AddColumn(fTitleCol, kTitleIndex);
87 
88 	fDateCol = new BDateColumn(B_TRANSLATE("Last Received"), 100,
89 		be_plain_font->StringWidth(B_TRANSLATE("Last Received")) +
90 		(kCLVTitlePadding * 2), rect.Width(), B_ALIGN_LEFT);
91 	fNotifications->AddColumn(fDateCol, kDateIndex);
92 
93 	fTypeCol = new BStringColumn(B_TRANSLATE("Type"), 100,
94 		be_plain_font->StringWidth(B_TRANSLATE("Type")) +
95 		(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
96 	fNotifications->AddColumn(fTypeCol, kTypeIndex);
97 
98 	fAllowCol = new BStringColumn(B_TRANSLATE("Allowed"), 100,
99 		be_plain_font->StringWidth(B_TRANSLATE("Allowed")) +
100 		(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
101 	fNotifications->AddColumn(fAllowCol, kAllowIndex);
102 
103 	// Load the applications list
104 	_LoadAppUsage();
105 	_PopulateApplications();
106 
107 	// Calculate inset
108 	float inset = ceilf(be_plain_font->Size() * 0.7f);
109 
110 	// Set layout
111 	SetLayout(new BGroupLayout(B_VERTICAL));
112 
113 	// Add views
114 	AddChild(BGroupLayoutBuilder(B_VERTICAL, inset)
115 		.AddGroup(B_HORIZONTAL)
116 			.AddGlue()
117 			.Add(fSearch)
118 		.End()
119 		.Add(fApplications)
120 		.Add(fNotifications)
121 		.SetInsets(inset, inset, inset, inset)
122 	);
123 }
124 
125 
126 void
127 NotificationsView::AttachedToWindow()
128 {
129 	fApplications->SetTarget(this);
130 	fApplications->SetInvocationMessage(new BMessage(kApplicationSelected));
131 
132 	fNotifications->SetTarget(this);
133 	fNotifications->SetInvocationMessage(new BMessage(kNotificationSelected));
134 
135 #if 0
136 	fNotifications->AddFilter(new BMessageFilter(B_ANY_DELIVERY,
137 		B_ANY_SOURCE, B_KEY_DOWN, CatchDelete));
138 #endif
139 }
140 
141 
142 void
143 NotificationsView::MessageReceived(BMessage* msg)
144 {
145 	switch (msg->what) {
146 		case kApplicationSelected:
147 		{
148 			BRow* row = fApplications->CurrentSelection();
149 			if (row == NULL)
150 				return;
151 			BStringField* appName
152 				= dynamic_cast<BStringField*>(row->GetField(kAppIndex));
153 			if (appName == NULL)
154 				break;
155 
156 			appusage_t::iterator it = fAppFilters.find(appName->String());
157 			if (it != fAppFilters.end())
158 				_Populate(it->second);
159 
160 			break;
161 		}
162 		case kNotificationSelected:
163 			break;
164 		default:
165 			BView::MessageReceived(msg);
166 			break;
167 	}
168 }
169 
170 
171 status_t
172 NotificationsView::_LoadAppUsage()
173 {
174 	BPath path;
175 
176 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
177 		return B_ERROR;
178 
179 	path.Append(kSettingsDirectory);
180 
181 	if (create_directory(path.Path(), 0755) != B_OK) {
182 		BAlert* alert = new BAlert("",
183 			B_TRANSLATE("There was a problem saving the preferences.\n"
184 				"It's possible you don't have write access to the "
185 				"settings directory."), B_TRANSLATE("OK"), NULL, NULL,
186 			B_WIDTH_AS_USUAL, B_STOP_ALERT);
187 		alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
188 		(void)alert->Go();
189 		return B_ERROR;
190 	}
191 
192 	path.Append(kFiltersSettings);
193 
194 	BFile file(path.Path(), B_READ_ONLY);
195 	BMessage settings;
196 	if (settings.Unflatten(&file) != B_OK)
197 		return B_ERROR;
198 
199 	type_code type;
200 	int32 count = 0;
201 
202 	if (settings.GetInfo("app_usage", &type, &count) != B_OK)
203 		return B_ERROR;
204 
205 	// Clean filters
206 	appusage_t::iterator auIt;
207 	for (auIt = fAppFilters.begin(); auIt != fAppFilters.end(); auIt++)
208 		delete auIt->second;
209 	fAppFilters.clear();
210 
211 	// Add new filters
212 	for (int32 i = 0; i < count; i++) {
213 		AppUsage* app = new AppUsage();
214 		settings.FindFlat("app_usage", i, app);
215 		fAppFilters[app->Name()] = app;
216 	}
217 
218 	return B_OK;
219 }
220 
221 
222 void
223 NotificationsView::_PopulateApplications()
224 {
225 	appusage_t::iterator it;
226 
227 	fApplications->Clear();
228 
229 	for (it = fAppFilters.begin(); it != fAppFilters.end(); ++it) {
230 		BRow* row = new BRow();
231 		row->SetField(new BStringField(it->first.String()), kAppIndex);
232 		fApplications->AddRow(row);
233 	}
234 }
235 
236 
237 void
238 NotificationsView::_Populate(AppUsage* usage)
239 {
240 	// Sanity check
241 	if (!usage)
242 		return;
243 
244 	int32 size = usage->Notifications();
245 
246 	if (usage->Allowed() == false)
247 		fBlockAll->SetValue(B_CONTROL_ON);
248 
249 	fNotifications->Clear();
250 
251 	for (int32 i = 0; i < size; i++) {
252 		NotificationReceived* notification = usage->NotificationAt(i);
253 		time_t updated = notification->LastReceived();
254 		const char* allow = notification->Allowed() ? B_TRANSLATE("Yes")
255 			: B_TRANSLATE("No");
256 		const char* type = "";
257 
258 		switch (notification->Type()) {
259 			case B_INFORMATION_NOTIFICATION:
260 				type = B_TRANSLATE("Information");
261 				break;
262 			case B_IMPORTANT_NOTIFICATION:
263 				type = B_TRANSLATE("Important");
264 				break;
265 			case B_ERROR_NOTIFICATION:
266 				type = B_TRANSLATE("Error");
267 				break;
268 			case B_PROGRESS_NOTIFICATION:
269 				type = B_TRANSLATE("Progress");
270 				break;
271 			default:
272 				type = B_TRANSLATE("Unknown");
273 		}
274 
275 		BRow* row = new BRow();
276 		row->SetField(new BStringField(notification->Title()), kTitleIndex);
277 		row->SetField(new BDateField(&updated), kDateIndex);
278 		row->SetField(new BStringField(type), kTypeIndex);
279 		row->SetField(new BStringField(allow), kAllowIndex);
280 
281 		fNotifications->AddRow(row);
282 	}
283 }
284