xref: /haiku/src/preferences/notifications/NotificationsView.cpp (revision 6aa0587222b965a635512f99861a5f6a9ad465a8)
1 /*
2  * Copyright 2010-2017, 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  *		Brian Hill, supernova@tycho.email
9  */
10 
11 #include <Alert.h>
12 #include <Button.h>
13 #include <Catalog.h>
14 #include <CheckBox.h>
15 #include <ColumnListView.h>
16 #include <ColumnTypes.h>
17 #include <Directory.h>
18 #include <FindDirectory.h>
19 #include <LayoutBuilder.h>
20 #include <Notification.h>
21 #include <Path.h>
22 #include <TextControl.h>
23 #include <Window.h>
24 
25 #include <notification/Notifications.h>
26 #include <notification/NotificationReceived.h>
27 
28 #include "NotificationsConstants.h"
29 #include "NotificationsView.h"
30 
31 #undef B_TRANSLATION_CONTEXT
32 #define B_TRANSLATION_CONTEXT "NotificationView"
33 
34 // Applications column indexes
35 const int32 kAppNameIndex = 0;
36 const int32 kAppEnabledIndex = 1;
37 
38 
AppRow(const char * name,const char * signature,bool allowed)39 AppRow::AppRow(const char* name, const char* signature, bool allowed)
40 	:
41 	BRow(),
42 	fName(name),
43 	fSignature(signature),
44 	fAllowed(allowed)
45 {
46 	SetField(new BStringField(fName.String()), kAppNameIndex);
47 	BString text = fAllowed ? B_TRANSLATE("Allowed") : B_TRANSLATE("Muted");
48 	SetField(new BStringField(text.String()), kAppEnabledIndex);
49 }
50 
51 
52 void
SetAllowed(bool allowed)53 AppRow::SetAllowed(bool allowed)
54 {
55 	fAllowed = allowed;
56 	RefreshEnabledField();
57 }
58 
59 
60 void
RefreshEnabledField()61 AppRow::RefreshEnabledField()
62 {
63 	BStringField* field = (BStringField*)GetField(kAppEnabledIndex);
64 	BString text = fAllowed ? B_TRANSLATE("Allowed") : B_TRANSLATE("Muted");
65 	field->SetString(text.String());
66 	Invalidate();
67 }
68 
69 
NotificationsView(SettingsHost * host)70 NotificationsView::NotificationsView(SettingsHost* host)
71 	:
72 	SettingsPane("apps", host),
73 	fSelectedRow(NULL)
74 {
75 	// Applications list
76 	fApplications = new BColumnListView(B_TRANSLATE("Applications"),
77 		0, B_FANCY_BORDER, false);
78 	fApplications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
79 	fApplications->SetSelectionMessage(new BMessage(kApplicationSelected));
80 
81 	float colWidth = be_plain_font->StringWidth(B_TRANSLATE("Application"))
82 		+ (kCLVTitlePadding * 2);
83 	fAppCol = new BStringColumn(B_TRANSLATE("Application"), colWidth * 2,
84 		colWidth, colWidth * 4, B_TRUNCATE_END, B_ALIGN_LEFT);
85 	fApplications->AddColumn(fAppCol, kAppNameIndex);
86 
87 	colWidth = be_plain_font->StringWidth(B_TRANSLATE("Status"))
88 		+ (kCLVTitlePadding * 2);
89 	fAppEnabledCol = new BStringColumn(B_TRANSLATE("Status"), colWidth * 1.5,
90 		colWidth, colWidth * 3, B_TRUNCATE_END, B_ALIGN_LEFT);
91 	fApplications->AddColumn(fAppEnabledCol, kAppEnabledIndex);
92 	fApplications->SetSortColumn(fAppCol, true, true);
93 
94 	fAddButton = new BButton("add_app", B_TRANSLATE("Add" B_UTF8_ELLIPSIS),
95 		new BMessage(kAddApplication));
96 	fRemoveButton = new BButton("add_app", B_TRANSLATE("Remove"),
97 		new BMessage(kRemoveApplication));
98 	fRemoveButton->SetEnabled(false);
99 
100 	fMuteAll = new BCheckBox("block", B_TRANSLATE("Mute notifications from "
101 		"this application"),
102 		new BMessage(kMuteChanged));
103 
104 	// Add views
105 	BLayoutBuilder::Group<>(this, B_VERTICAL)
106 		.AddGroup(B_HORIZONTAL)
107 			.Add(fApplications)
108 			.AddGroup(B_VERTICAL)
109 				.Add(fAddButton)
110 				.Add(fRemoveButton)
111 				.AddGlue()
112 			.End()
113 		.End()
114 		.Add(fMuteAll)
115 		.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
116 			B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING);
117 
118 	// Set button sizes
119 	float maxButtonWidth = std::max(fAddButton->PreferredSize().Width(),
120 		fRemoveButton->PreferredSize().Width());
121 	fAddButton->SetExplicitMaxSize(BSize(maxButtonWidth, B_SIZE_UNSET));
122 	fRemoveButton->SetExplicitMaxSize(BSize(maxButtonWidth, B_SIZE_UNSET));
123 
124 	// File Panel
125 	fPanelFilter = new AppRefFilter();
126 	fAddAppPanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false,
127 		NULL, fPanelFilter);
128 }
129 
130 
~NotificationsView()131 NotificationsView::~NotificationsView()
132 {
133 	delete fAddAppPanel;
134 	delete fPanelFilter;
135 }
136 
137 
138 void
AttachedToWindow()139 NotificationsView::AttachedToWindow()
140 {
141 	fApplications->SetTarget(this);
142 	fApplications->SetInvocationMessage(new BMessage(kApplicationSelected));
143 	fAddButton->SetTarget(this);
144 	fRemoveButton->SetTarget(this);
145 	fMuteAll->SetTarget(this);
146 	fAddAppPanel->SetTarget(this);
147 	_RecallItemSettings();
148 }
149 
150 
151 void
MessageReceived(BMessage * msg)152 NotificationsView::MessageReceived(BMessage* msg)
153 {
154 	switch (msg->what) {
155 		case kApplicationSelected:
156 		{
157 			Window()->Lock();
158 			_ClearItemSettings();
159 			_UpdateSelectedItem();
160 			_RecallItemSettings();
161 			Window()->Unlock();
162 			break;
163 		}
164 		case kMuteChanged:
165 		{
166 			bool allowed = fMuteAll->Value() == B_CONTROL_OFF;
167 			fSelectedRow->SetAllowed(allowed);
168 			appusage_t::iterator it = fAppFilters.find(fSelectedRow->Signature());
169 			if (it != fAppFilters.end())
170 				it->second->SetAllowed(allowed);
171 			Window()->PostMessage(kApply);
172 			break;
173 		}
174 		case kAddApplication:
175 		{
176 			BMessage addmsg(kAddApplicationRef);
177 			fAddAppPanel->SetMessage(&addmsg);
178 			fAddAppPanel->Show();
179 			break;
180 		}
181 		case kAddApplicationRef:
182 		{
183 			entry_ref srcRef;
184 			msg->FindRef("refs", &srcRef);
185 			BEntry srcEntry(&srcRef, true);
186 			BPath path(&srcEntry);
187 			BNode node(&srcEntry);
188 			char *buf = new char[B_ATTR_NAME_LENGTH];
189 			ssize_t size;
190 			if ( (size = node.ReadAttr("BEOS:APP_SIG", 0, 0, buf,
191 				B_ATTR_NAME_LENGTH)) > 0 )
192 			{
193 				// Search for already existing app
194 				appusage_t::iterator it = fAppFilters.find(buf);
195 				if (it != fAppFilters.end()) {
196 					BString text(path.Leaf());
197 					text.Append(B_TRANSLATE_COMMENT(" is already listed",
198 							"Alert message"));
199 					BAlert* alert = new BAlert("", text.String(),
200 						B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
201 						B_WARNING_ALERT);
202 					alert->Go(NULL);
203 				} else {
204 					AppUsage* appUsage = new AppUsage(path.Leaf(), buf, true);
205 					fAppFilters[appUsage->Signature()] = appUsage;
206 					AppRow* row = new AppRow(appUsage->AppName(),
207 						appUsage->Signature(), appUsage->Allowed());
208 					fApplications->AddRow(row);
209 					fApplications->DeselectAll();
210 					fApplications->AddToSelection(row);
211 					fApplications->ScrollTo(row);
212 					_UpdateSelectedItem();
213 					_RecallItemSettings();
214 					//row->Invalidate();
215 					//fApplications->InvalidateRow(row);
216 					// TODO redraw row properly
217 					Window()->PostMessage(kApply);
218 				}
219 			} else {
220 				BAlert* alert = new BAlert("",
221 					B_TRANSLATE_COMMENT("Application does not have "
222 						"a valid signature", "Alert message"),
223 					B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
224 					B_WARNING_ALERT);
225 				alert->Go(NULL);
226 			}
227 			delete[] buf;
228 			break;
229 		}
230 		case kRemoveApplication:
231 		{
232 			if (fSelectedRow) {
233 				appusage_t::iterator it = fAppFilters.find(fSelectedRow->Signature());
234 				if (it != fAppFilters.end()) {
235 					delete it->second;
236 					fAppFilters.erase(it);
237 				}
238 				fApplications->RemoveRow(fSelectedRow);
239 				delete fSelectedRow;
240 				fSelectedRow = NULL;
241 				_ClearItemSettings();
242 				_UpdateSelectedItem();
243 				_RecallItemSettings();
244 				Window()->PostMessage(kApply);
245 			}
246 			break;
247 		}
248 		default:
249 			BView::MessageReceived(msg);
250 			break;
251 	}
252 }
253 
254 
255 status_t
Load(BMessage & settings)256 NotificationsView::Load(BMessage& settings)
257 {
258 	type_code type;
259 	int32 count = 0;
260 
261 	if (settings.GetInfo("app_usage", &type, &count) != B_OK)
262 		return B_ERROR;
263 
264 	// Clean filters
265 	appusage_t::iterator auIt;
266 	for (auIt = fAppFilters.begin(); auIt != fAppFilters.end(); auIt++)
267 		delete auIt->second;
268 	fAppFilters.clear();
269 
270 	// Add new filters
271 	for (int32 i = 0; i < count; i++) {
272 		AppUsage* app = new AppUsage();
273 		settings.FindFlat("app_usage", i, app);
274 		fAppFilters[app->Signature()] = app;
275 	}
276 
277 	// Load the applications list
278 	_PopulateApplications();
279 
280 	return B_OK;
281 }
282 
283 
284 status_t
Save(BMessage & storage)285 NotificationsView::Save(BMessage& storage)
286 {
287 	appusage_t::iterator fIt;
288 	for (fIt = fAppFilters.begin(); fIt != fAppFilters.end(); fIt++)
289 		storage.AddFlat("app_usage", fIt->second);
290 
291 	return B_OK;
292 }
293 
294 
295 void
_ClearItemSettings()296 NotificationsView::_ClearItemSettings()
297 {
298 	fMuteAll->SetValue(B_CONTROL_OFF);
299 }
300 
301 
302 void
_UpdateSelectedItem()303 NotificationsView::_UpdateSelectedItem()
304 {
305 	fSelectedRow = dynamic_cast<AppRow*>(fApplications->CurrentSelection());
306 
307 }
308 
309 
310 void
_RecallItemSettings()311 NotificationsView::_RecallItemSettings()
312 {
313 	// No selected item
314 	if(fSelectedRow == NULL)
315 	{
316 		fMuteAll->SetValue(B_CONTROL_OFF);
317 		fMuteAll->SetEnabled(false);
318 		fRemoveButton->SetEnabled(false);
319 	} else {
320 		fMuteAll->SetEnabled(true);
321 		fRemoveButton->SetEnabled(true);
322 		appusage_t::iterator it = fAppFilters.find(fSelectedRow->Signature());
323 		if (it != fAppFilters.end())
324 			fMuteAll->SetValue(!(it->second->Allowed()));
325 	}
326 }
327 
328 
329 status_t
Revert()330 NotificationsView::Revert()
331 {
332 	return B_OK;
333 }
334 
335 
336 bool
RevertPossible()337 NotificationsView::RevertPossible()
338 {
339 	return false;
340 }
341 
342 
343 status_t
Defaults()344 NotificationsView::Defaults()
345 {
346 	return B_OK;
347 }
348 
349 
350 bool
DefaultsPossible()351 NotificationsView::DefaultsPossible()
352 {
353 	return false;
354 }
355 
356 
357 bool
UseDefaultRevertButtons()358 NotificationsView::UseDefaultRevertButtons()
359 {
360 	return false;
361 }
362 
363 
364 void
_PopulateApplications()365 NotificationsView::_PopulateApplications()
366 {
367 	fApplications->Clear();
368 
369 	appusage_t::iterator it;
370 	for (it = fAppFilters.begin(); it != fAppFilters.end(); ++it) {
371 		AppUsage* appUsage = it->second;
372 		AppRow* row = new AppRow(appUsage->AppName(),
373 			appUsage->Signature(), appUsage->Allowed());
374 		fApplications->AddRow(row);
375 	}
376 }
377