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