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