xref: /haiku/src/preferences/notifications/NotificationsView.cpp (revision b46615c55ad2c8fe6de54412055a0713da3d610a)
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_TRANSLATE_CONTEXT
30 #define B_TRANSLATE_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 	);
122 }
123 
124 
125 void
126 NotificationsView::AttachedToWindow()
127 {
128 	fApplications->SetTarget(this);
129 	fApplications->SetInvocationMessage(new BMessage(kApplicationSelected));
130 
131 	fNotifications->SetTarget(this);
132 	fNotifications->SetInvocationMessage(new BMessage(kNotificationSelected));
133 
134 #if 0
135 	fNotifications->AddFilter(new BMessageFilter(B_ANY_DELIVERY,
136 		B_ANY_SOURCE, B_KEY_DOWN, CatchDelete));
137 #endif
138 }
139 
140 
141 void
142 NotificationsView::MessageReceived(BMessage* msg)
143 {
144 	switch (msg->what) {
145 		case kApplicationSelected:
146 		{
147 			BRow* row = fApplications->CurrentSelection();
148 			if (row == NULL)
149 				return;
150 			BStringField* appName
151 				= dynamic_cast<BStringField*>(row->GetField(kAppIndex));
152 			if (appName == NULL)
153 				break;
154 
155 			appusage_t::iterator it = fAppFilters.find(appName->String());
156 			if (it != fAppFilters.end())
157 				_Populate(it->second);
158 
159 			break;
160 		}
161 		case kNotificationSelected:
162 			break;
163 		default:
164 			BView::MessageReceived(msg);
165 			break;
166 	}
167 }
168 
169 
170 status_t
171 NotificationsView::_LoadAppUsage()
172 {
173 	BPath path;
174 
175 	if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
176 		return B_ERROR;
177 
178 	path.Append(kSettingsDirectory);
179 
180 	if (create_directory(path.Path(), 0755) != B_OK) {
181 		BAlert* alert = new BAlert("",
182 			B_TRANSLATE("There was a problem saving the preferences.\n"
183 				"It's possible you don't have write access to the "
184 				"settings directory."), B_TRANSLATE("OK"), NULL, NULL,
185 			B_WIDTH_AS_USUAL, B_STOP_ALERT);
186 		(void)alert->Go();
187 		return B_ERROR;
188 	}
189 
190 	path.Append(kFiltersSettings);
191 
192 	BFile file(path.Path(), B_READ_ONLY);
193 	BMessage settings;
194 	if (settings.Unflatten(&file) != B_OK)
195 		return B_ERROR;
196 
197 	type_code type;
198 	int32 count = 0;
199 
200 	if (settings.GetInfo("app_usage", &type, &count) != B_OK)
201 		return B_ERROR;
202 
203 	// Clean filters
204 	appusage_t::iterator auIt;
205 	for (auIt = fAppFilters.begin(); auIt != fAppFilters.end(); auIt++)
206 		delete auIt->second;
207 	fAppFilters.clear();
208 
209 	// Add new filters
210 	for (int32 i = 0; i < count; i++) {
211 		AppUsage* app = new AppUsage();
212 		settings.FindFlat("app_usage", i, app);
213 		fAppFilters[app->Name()] = app;
214 	}
215 
216 	return B_OK;
217 }
218 
219 
220 void
221 NotificationsView::_PopulateApplications()
222 {
223 	appusage_t::iterator it;
224 
225 	fApplications->Clear();
226 
227 	for (it = fAppFilters.begin(); it != fAppFilters.end(); ++it) {
228 		BRow* row = new BRow();
229 		row->SetField(new BStringField(it->first.String()), kAppIndex);
230 		fApplications->AddRow(row);
231 	}
232 }
233 
234 
235 void
236 NotificationsView::_Populate(AppUsage* usage)
237 {
238 	// Sanity check
239 	if (!usage)
240 		return;
241 
242 	int32 size = usage->Notifications();
243 
244 	if (usage->Allowed() == false)
245 		fBlockAll->SetValue(B_CONTROL_ON);
246 
247 	fNotifications->Clear();
248 
249 	for (int32 i = 0; i < size; i++) {
250 		NotificationReceived* notification = usage->NotificationAt(i);
251 		time_t updated = notification->LastReceived();
252 		const char* allow = notification->Allowed() ? B_TRANSLATE("Yes")
253 			: B_TRANSLATE("No");
254 		const char* type = "";
255 
256 		switch (notification->Type()) {
257 			case B_INFORMATION_NOTIFICATION:
258 				type = B_TRANSLATE("Information");
259 				break;
260 			case B_IMPORTANT_NOTIFICATION:
261 				type = B_TRANSLATE("Important");
262 				break;
263 			case B_ERROR_NOTIFICATION:
264 				type = B_TRANSLATE("Error");
265 				break;
266 			case B_PROGRESS_NOTIFICATION:
267 				type = B_TRANSLATE("Progress");
268 				break;
269 			default:
270 				type = B_TRANSLATE("Unknown");
271 		}
272 
273 		BRow* row = new BRow();
274 		row->SetField(new BStringField(notification->Title()), kTitleIndex);
275 		row->SetField(new BDateField(&updated), kDateIndex);
276 		row->SetField(new BStringField(type), kTypeIndex);
277 		row->SetField(new BStringField(allow), kAllowIndex);
278 
279 		fNotifications->AddRow(row);
280 	}
281 }
282