File: | src/servers/notification/NotificationWindow.cpp |
Warning: | line 208, column 6 Potential leak of memory pointed to by 'view' |
1 | /* | |||
2 | * Copyright 2010, Haiku, Inc. All Rights Reserved. | |||
3 | * Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved. | |||
4 | * Copyright 2004-2008, Michael Davidson. All Rights Reserved. | |||
5 | * Copyright 2004-2007, Mikael Eiman. All Rights Reserved. | |||
6 | * Distributed under the terms of the MIT License. | |||
7 | * | |||
8 | * Authors: | |||
9 | * Michael Davidson, slaad@bong.com.au | |||
10 | * Mikael Eiman, mikael@eiman.tv | |||
11 | * Pier Luigi Fiorini, pierluigi.fiorini@gmail.com | |||
12 | */ | |||
13 | #include "NotificationWindow.h" | |||
14 | ||||
15 | #include <algorithm> | |||
16 | ||||
17 | #include <Alert.h> | |||
18 | #include <Application.h> | |||
19 | #include <Catalog.h> | |||
20 | #include <Deskbar.h> | |||
21 | #include <Directory.h> | |||
22 | #include <File.h> | |||
23 | #include <FindDirectory.h> | |||
24 | #include <GroupLayout.h> | |||
25 | #include <NodeMonitor.h> | |||
26 | #include <Notifications.h> | |||
27 | #include <Path.h> | |||
28 | #include <PropertyInfo.h> | |||
29 | ||||
30 | #include "AppGroupView.h" | |||
31 | #include "AppUsage.h" | |||
32 | ||||
33 | ||||
34 | #undef B_TRANSLATION_CONTEXT"NotificationWindow" | |||
35 | #define B_TRANSLATION_CONTEXT"NotificationWindow" "NotificationWindow" | |||
36 | ||||
37 | ||||
38 | property_info main_prop_list[] = { | |||
39 | {"message", {B_GET_PROPERTY, 0}, {B_INDEX_SPECIFIER, 0}, | |||
40 | "get a message"}, | |||
41 | {"message", {B_COUNT_PROPERTIES, 0}, {B_DIRECT_SPECIFIER, 0}, | |||
42 | "count messages"}, | |||
43 | {"message", {B_CREATE_PROPERTY, 0}, {B_DIRECT_SPECIFIER, 0}, | |||
44 | "create a message"}, | |||
45 | {"message", {B_SET_PROPERTY, 0}, {B_INDEX_SPECIFIER, 0}, | |||
46 | "modify a message"}, | |||
47 | {0} | |||
48 | }; | |||
49 | ||||
50 | ||||
51 | const float kCloseSize = 6; | |||
52 | const float kExpandSize = 8; | |||
53 | const float kPenSize = 1; | |||
54 | const float kEdgePadding = 2; | |||
55 | const float kSmallPadding = 2; | |||
56 | ||||
57 | NotificationWindow::NotificationWindow() | |||
58 | : | |||
59 | BWindow(BRect(0, 0, -1, -1), B_TRANSLATE_MARK("Notification")("Notification"), | |||
60 | B_BORDERED_WINDOW_LOOK, B_FLOATING_ALL_WINDOW_FEEL, B_AVOID_FRONT | |||
61 | | B_AVOID_FOCUS | B_NOT_CLOSABLE | B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE | |||
62 | | B_NOT_RESIZABLE | B_NOT_MOVABLE | B_AUTO_UPDATE_SIZE_LIMITS, | |||
63 | B_ALL_WORKSPACES0xffffffff) | |||
64 | { | |||
65 | SetLayout(new BGroupLayout(B_VERTICAL, 0)); | |||
66 | ||||
67 | _LoadSettings(true); | |||
68 | ||||
69 | // Start the message loop | |||
70 | Hide(); | |||
71 | Show(); | |||
72 | } | |||
73 | ||||
74 | ||||
75 | NotificationWindow::~NotificationWindow() | |||
76 | { | |||
77 | appfilter_t::iterator aIt; | |||
78 | for (aIt = fAppFilters.begin(); aIt != fAppFilters.end(); aIt++) | |||
79 | delete aIt->second; | |||
80 | } | |||
81 | ||||
82 | ||||
83 | bool | |||
84 | NotificationWindow::QuitRequested() | |||
85 | { | |||
86 | appview_t::iterator aIt; | |||
87 | for (aIt = fAppViews.begin(); aIt != fAppViews.end(); aIt++) { | |||
88 | aIt->second->RemoveSelf(); | |||
89 | delete aIt->second; | |||
90 | } | |||
91 | ||||
92 | BMessenger(be_app).SendMessage(B_QUIT_REQUESTED); | |||
93 | return BWindow::QuitRequested(); | |||
94 | } | |||
95 | ||||
96 | ||||
97 | void | |||
98 | NotificationWindow::WorkspaceActivated(int32 /*workspace*/, bool active) | |||
99 | { | |||
100 | // Ensure window is in the correct position | |||
101 | if (active) | |||
102 | SetPosition(); | |||
103 | } | |||
104 | ||||
105 | ||||
106 | void | |||
107 | NotificationWindow::FrameResized(float width, float height) | |||
108 | { | |||
109 | SetPosition(); | |||
110 | } | |||
111 | ||||
112 | ||||
113 | void | |||
114 | NotificationWindow::ScreenChanged(BRect frame, color_space mode) | |||
115 | { | |||
116 | SetPosition(); | |||
117 | } | |||
118 | ||||
119 | ||||
120 | void | |||
121 | NotificationWindow::MessageReceived(BMessage* message) | |||
122 | { | |||
123 | switch (message->what) { | |||
| ||||
124 | case B_NODE_MONITOR: | |||
125 | { | |||
126 | _LoadSettings(); | |||
127 | break; | |||
128 | } | |||
129 | case B_COUNT_PROPERTIES: | |||
130 | { | |||
131 | BMessage reply(B_REPLY); | |||
132 | BMessage specifier; | |||
133 | const char* property = NULL__null; | |||
134 | bool messageOkay = true; | |||
135 | ||||
136 | if (message->FindMessage("specifiers", 0, &specifier) != B_OK((int)0)) | |||
137 | messageOkay = false; | |||
138 | if (specifier.FindString("property", &property) != B_OK((int)0)) | |||
139 | messageOkay = false; | |||
140 | if (strcmp(property, "message") != 0) | |||
141 | messageOkay = false; | |||
142 | ||||
143 | if (messageOkay) | |||
144 | reply.AddInt32("result", fViews.size()); | |||
145 | else { | |||
146 | reply.what = B_MESSAGE_NOT_UNDERSTOOD; | |||
147 | reply.AddInt32("error", B_ERROR(-1)); | |||
148 | } | |||
149 | ||||
150 | message->SendReply(&reply); | |||
151 | break; | |||
152 | } | |||
153 | case B_CREATE_PROPERTY: | |||
154 | case kNotificationMessage: | |||
155 | { | |||
156 | BMessage reply(B_REPLY); | |||
157 | BNotification* notification = new BNotification(message); | |||
158 | ||||
159 | if (notification->InitCheck() == B_OK((int)0)) { | |||
160 | bigtime_t timeout; | |||
161 | if (message->FindInt64("timeout", &timeout) != B_OK((int)0)) | |||
162 | timeout = -1; | |||
163 | BMessenger messenger = message->ReturnAddress(); | |||
164 | app_info info; | |||
165 | ||||
166 | if (messenger.IsValid()) | |||
167 | be_roster->GetRunningAppInfo(messenger.Team(), &info); | |||
168 | else | |||
169 | be_roster->GetAppInfo("application/x-vnd.Be-SHEL", &info); | |||
170 | ||||
171 | NotificationView* view = new NotificationView(this, | |||
172 | notification, timeout); | |||
173 | ||||
174 | bool allow = false; | |||
175 | appfilter_t::iterator it = fAppFilters.find(info.signature); | |||
176 | ||||
177 | if (it == fAppFilters.end()) { | |||
178 | AppUsage* appUsage = new AppUsage(notification->Group(), | |||
179 | true); | |||
180 | ||||
181 | appUsage->Allowed(notification->Title(), | |||
182 | notification->Type()); | |||
183 | fAppFilters[info.signature] = appUsage; | |||
184 | allow = true; | |||
185 | } else { | |||
186 | allow = it->second->Allowed(notification->Title(), | |||
187 | notification->Type()); | |||
188 | } | |||
189 | ||||
190 | if (allow) { | |||
191 | BString groupName(notification->Group()); | |||
192 | appview_t::iterator aIt = fAppViews.find(groupName); | |||
193 | AppGroupView* group = NULL__null; | |||
194 | if (aIt == fAppViews.end()) { | |||
195 | group = new AppGroupView(this, | |||
196 | groupName == "" ? NULL__null : groupName.String()); | |||
197 | fAppViews[groupName] = group; | |||
198 | GetLayout()->AddView(group); | |||
199 | } else | |||
200 | group = aIt->second; | |||
201 | ||||
202 | group->AddInfo(view); | |||
203 | ||||
204 | _ShowHide(); | |||
205 | ||||
206 | reply.AddInt32("error", B_OK((int)0)); | |||
207 | } else | |||
208 | reply.AddInt32("error", B_NOT_ALLOWED((-2147483647 - 1) + 15)); | |||
| ||||
209 | } else { | |||
210 | reply.what = B_MESSAGE_NOT_UNDERSTOOD; | |||
211 | reply.AddInt32("error", B_ERROR(-1)); | |||
212 | } | |||
213 | ||||
214 | message->SendReply(&reply); | |||
215 | break; | |||
216 | } | |||
217 | case kRemoveView: | |||
218 | { | |||
219 | NotificationView* view = NULL__null; | |||
220 | if (message->FindPointer("view", (void**)&view) != B_OK((int)0)) | |||
221 | return; | |||
222 | ||||
223 | views_t::iterator it = find(fViews.begin(), fViews.end(), view); | |||
224 | ||||
225 | if (it != fViews.end()) | |||
226 | fViews.erase(it); | |||
227 | break; | |||
228 | } | |||
229 | case kRemoveGroupView: | |||
230 | { | |||
231 | AppGroupView* view = NULL__null; | |||
232 | if (message->FindPointer("view", (void**)&view) != B_OK((int)0)) | |||
233 | return; | |||
234 | ||||
235 | // It's possible that between sending this message, and us receiving | |||
236 | // it, the view has become used again, in which case we shouldn't | |||
237 | // delete it. | |||
238 | if (view->HasChildren()) | |||
239 | return; | |||
240 | ||||
241 | // this shouldn't happen | |||
242 | if (fAppViews.erase(view->Group()) < 1) | |||
243 | break; | |||
244 | ||||
245 | view->RemoveSelf(); | |||
246 | delete view; | |||
247 | ||||
248 | _ShowHide(); | |||
249 | break; | |||
250 | } | |||
251 | default: | |||
252 | BWindow::MessageReceived(message); | |||
253 | } | |||
254 | } | |||
255 | ||||
256 | ||||
257 | BHandler* | |||
258 | NotificationWindow::ResolveSpecifier(BMessage* msg, int32 index, | |||
259 | BMessage* spec, int32 form, const char* prop) | |||
260 | { | |||
261 | BPropertyInfo prop_info(main_prop_list); | |||
262 | BHandler* handler = NULL__null; | |||
263 | ||||
264 | if (strcmp(prop,"message") == 0) { | |||
265 | switch (msg->what) { | |||
266 | case B_CREATE_PROPERTY: | |||
267 | { | |||
268 | msg->PopSpecifier(); | |||
269 | handler = this; | |||
270 | break; | |||
271 | } | |||
272 | case B_SET_PROPERTY: | |||
273 | case B_GET_PROPERTY: | |||
274 | { | |||
275 | int32 i; | |||
276 | ||||
277 | if (spec->FindInt32("index", &i) != B_OK((int)0)) | |||
278 | i = -1; | |||
279 | ||||
280 | if (i >= 0 && i < (int32)fViews.size()) { | |||
281 | msg->PopSpecifier(); | |||
282 | handler = fViews[i]; | |||
283 | } else | |||
284 | handler = NULL__null; | |||
285 | break; | |||
286 | } | |||
287 | case B_COUNT_PROPERTIES: | |||
288 | msg->PopSpecifier(); | |||
289 | handler = this; | |||
290 | break; | |||
291 | default: | |||
292 | break; | |||
293 | } | |||
294 | } | |||
295 | ||||
296 | if (!handler) | |||
297 | handler = BWindow::ResolveSpecifier(msg, index, spec, form, prop); | |||
298 | ||||
299 | return handler; | |||
300 | } | |||
301 | ||||
302 | ||||
303 | icon_size | |||
304 | NotificationWindow::IconSize() | |||
305 | { | |||
306 | return fIconSize; | |||
307 | } | |||
308 | ||||
309 | ||||
310 | int32 | |||
311 | NotificationWindow::Timeout() | |||
312 | { | |||
313 | return fTimeout; | |||
314 | } | |||
315 | ||||
316 | ||||
317 | float | |||
318 | NotificationWindow::Width() | |||
319 | { | |||
320 | return fWidth; | |||
321 | } | |||
322 | ||||
323 | ||||
324 | void | |||
325 | NotificationWindow::_ShowHide() | |||
326 | { | |||
327 | if (fAppViews.empty() && !IsHidden()) { | |||
328 | Hide(); | |||
329 | return; | |||
330 | } | |||
331 | ||||
332 | if (IsHidden()) { | |||
333 | SetPosition(); | |||
334 | Show(); | |||
335 | } | |||
336 | } | |||
337 | ||||
338 | ||||
339 | void | |||
340 | NotificationWindow::NotificationViewSwapped(NotificationView* stale, | |||
341 | NotificationView* fresh) | |||
342 | { | |||
343 | views_t::iterator it = find(fViews.begin(), fViews.end(), stale); | |||
344 | ||||
345 | if (it != fViews.end()) | |||
346 | *it = fresh; | |||
347 | } | |||
348 | ||||
349 | ||||
350 | void | |||
351 | NotificationWindow::SetPosition() | |||
352 | { | |||
353 | Layout(true); | |||
354 | ||||
355 | BRect bounds = DecoratorFrame(); | |||
356 | float width = Bounds().Width() + 1; | |||
357 | float height = Bounds().Height() + 1; | |||
358 | ||||
359 | float leftOffset = Frame().left - bounds.left; | |||
360 | float topOffset = Frame().top - bounds.top + 1; | |||
361 | float rightOffset = bounds.right - Frame().right; | |||
362 | float bottomOffset = bounds.bottom - Frame().bottom; | |||
363 | // Size of the borders around the window | |||
364 | ||||
365 | float x = Frame().left, y = Frame().top; | |||
366 | // If we can't guess, don't move... | |||
367 | ||||
368 | BDeskbar deskbar; | |||
369 | BRect frame = deskbar.Frame(); | |||
370 | ||||
371 | switch (deskbar.Location()) { | |||
372 | case B_DESKBAR_TOP: | |||
373 | // Put it just under, top right corner | |||
374 | y = frame.bottom + topOffset; | |||
375 | x = frame.right - width + rightOffset; | |||
376 | break; | |||
377 | case B_DESKBAR_BOTTOM: | |||
378 | // Put it just above, lower left corner | |||
379 | y = frame.top - height - bottomOffset; | |||
380 | x = frame.right - width + rightOffset; | |||
381 | break; | |||
382 | case B_DESKBAR_RIGHT_TOP: | |||
383 | x = frame.left - width - rightOffset; | |||
384 | y = frame.top - topOffset + 1; | |||
385 | break; | |||
386 | case B_DESKBAR_LEFT_TOP: | |||
387 | x = frame.right + leftOffset; | |||
388 | y = frame.top - topOffset + 1; | |||
389 | break; | |||
390 | case B_DESKBAR_RIGHT_BOTTOM: | |||
391 | y = frame.bottom - height + bottomOffset; | |||
392 | x = frame.left - width - rightOffset; | |||
393 | break; | |||
394 | case B_DESKBAR_LEFT_BOTTOM: | |||
395 | y = frame.bottom - height + bottomOffset; | |||
396 | x = frame.right + leftOffset; | |||
397 | break; | |||
398 | default: | |||
399 | break; | |||
400 | } | |||
401 | ||||
402 | MoveTo(x, y); | |||
403 | } | |||
404 | ||||
405 | ||||
406 | void | |||
407 | NotificationWindow::_LoadSettings(bool startMonitor) | |||
408 | { | |||
409 | BPath path; | |||
410 | BMessage settings; | |||
411 | ||||
412 | if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK((int)0)) | |||
413 | return; | |||
414 | ||||
415 | path.Append(kSettingsFile); | |||
416 | ||||
417 | BFile file(path.Path(), B_READ_ONLY0x0000); | |||
418 | settings.Unflatten(&file); | |||
419 | ||||
420 | _LoadGeneralSettings(settings); | |||
421 | _LoadDisplaySettings(settings); | |||
422 | _LoadAppFilters(settings); | |||
423 | ||||
424 | if (startMonitor) { | |||
425 | node_ref nref; | |||
426 | BEntry entry(path.Path()); | |||
427 | entry.GetNodeRef(&nref); | |||
428 | ||||
429 | if (watch_node(&nref, B_WATCH_ALL, BMessenger(this)) != B_OK((int)0)) { | |||
430 | BAlert* alert = new BAlert(B_TRANSLATE("Warning")BLocaleRoster::Default()->GetCatalog()->GetString(("Warning" ), "NotificationWindow"), | |||
431 | B_TRANSLATE("Couldn't start general settings monitor.\n"BLocaleRoster::Default()->GetCatalog()->GetString(("Couldn't start general settings monitor.\n" "Live filter changes disabled."), "NotificationWindow") | |||
432 | "Live filter changes disabled.")BLocaleRoster::Default()->GetCatalog()->GetString(("Couldn't start general settings monitor.\n" "Live filter changes disabled."), "NotificationWindow"), B_TRANSLATE("OK")BLocaleRoster::Default()->GetCatalog()->GetString(("OK" ), "NotificationWindow")); | |||
433 | alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE); | |||
434 | alert->Go(); | |||
435 | } | |||
436 | } | |||
437 | } | |||
438 | ||||
439 | ||||
440 | void | |||
441 | NotificationWindow::_LoadAppFilters(BMessage& settings) | |||
442 | { | |||
443 | type_code type; | |||
444 | int32 count = 0; | |||
445 | ||||
446 | if (settings.GetInfo("app_usage", &type, &count) != B_OK((int)0)) | |||
447 | return; | |||
448 | ||||
449 | for (int32 i = 0; i < count; i++) { | |||
450 | AppUsage* app = new AppUsage(); | |||
451 | settings.FindFlat("app_usage", i, app); | |||
452 | fAppFilters[app->Name()] = app; | |||
453 | } | |||
454 | } | |||
455 | ||||
456 | ||||
457 | void | |||
458 | NotificationWindow::_LoadGeneralSettings(BMessage& settings) | |||
459 | { | |||
460 | bool shouldRun; | |||
461 | if (settings.FindBool(kAutoStartName, &shouldRun) == B_OK((int)0)) { | |||
462 | if (shouldRun == false) { | |||
463 | // We should not start. Quit the app! | |||
464 | be_app_messenger.SendMessage(B_QUIT_REQUESTED); | |||
465 | } | |||
466 | } | |||
467 | if (settings.FindInt32(kTimeoutName, &fTimeout) != B_OK((int)0)) | |||
468 | fTimeout = kDefaultTimeout; | |||
469 | ||||
470 | // Notify the view about the change | |||
471 | views_t::iterator it; | |||
472 | for (it = fViews.begin(); it != fViews.end(); ++it) { | |||
473 | NotificationView* view = (*it); | |||
474 | view->Invalidate(); | |||
475 | } | |||
476 | } | |||
477 | ||||
478 | ||||
479 | void | |||
480 | NotificationWindow::_LoadDisplaySettings(BMessage& settings) | |||
481 | { | |||
482 | int32 setting; | |||
483 | ||||
484 | if (settings.FindFloat(kWidthName, &fWidth) != B_OK((int)0)) | |||
485 | fWidth = kDefaultWidth; | |||
486 | GetLayout()->SetExplicitMaxSize(BSize(fWidth, B_SIZE_UNSET)); | |||
487 | GetLayout()->SetExplicitMinSize(BSize(fWidth, B_SIZE_UNSET)); | |||
488 | ||||
489 | if (settings.FindInt32(kIconSizeName, &setting) != B_OK((int)0)) | |||
490 | fIconSize = kDefaultIconSize; | |||
491 | else | |||
492 | fIconSize = (icon_size)setting; | |||
493 | ||||
494 | // Notify the view about the change | |||
495 | views_t::iterator it; | |||
496 | for (it = fViews.begin(); it != fViews.end(); ++it) { | |||
497 | NotificationView* view = (*it); | |||
498 | view->Invalidate(); | |||
499 | } | |||
500 | } |