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