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