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