2 * Copyright 2010-2017, 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.
9 * Michael Davidson, slaad@bong.com.au
10 * Mikael Eiman, mikael@eiman.tv
11 * Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
12 * Brian Hill, supernova@tycho.email
14 #include "NotificationWindow.h"
19 #include <Application.h>
22 #include <Directory.h>
24 #include <FindDirectory.h>
25 #include <GroupLayout.h>
26 #include <NodeMonitor.h>
27 #include <Notifications.h>
29 #include <PropertyInfo.h>
31 #include "AppGroupView.h"
35 #undef B_TRANSLATION_CONTEXT
36 #define B_TRANSLATION_CONTEXT "NotificationWindow"
39 property_info main_prop_list
[] = {
40 {"message", {B_GET_PROPERTY
, 0}, {B_INDEX_SPECIFIER
, 0},
42 {"message", {B_COUNT_PROPERTIES
, 0}, {B_DIRECT_SPECIFIER
, 0},
44 {"message", {B_CREATE_PROPERTY
, 0}, {B_DIRECT_SPECIFIER
, 0},
46 {"message", {B_SET_PROPERTY
, 0}, {B_INDEX_SPECIFIER
, 0},
53 NotificationWindow::NotificationWindow()
55 BWindow(BRect(0, 0, -1, -1), B_TRANSLATE_MARK("Notification"),
56 B_BORDERED_WINDOW_LOOK
, B_FLOATING_ALL_WINDOW_FEEL
, B_AVOID_FRONT
57 | B_AVOID_FOCUS
| B_NOT_CLOSABLE
| B_NOT_ZOOMABLE
| B_NOT_MINIMIZABLE
58 | B_NOT_RESIZABLE
| B_NOT_MOVABLE
| B_AUTO_UPDATE_SIZE_LIMITS
,
62 status_t result
= find_directory(B_USER_CACHE_DIRECTORY
, &fCachePath
);
63 fCachePath
.Append("Notifications");
65 result
= cacheDir
.SetTo(fCachePath
.Path());
66 if(result
== B_ENTRY_NOT_FOUND
)
67 cacheDir
.CreateDirectory(fCachePath
.Path(), NULL
);
69 SetLayout(new BGroupLayout(B_VERTICAL
, 0));
73 // Start the message loop
79 NotificationWindow::~NotificationWindow()
81 appfilter_t::iterator aIt
;
82 for (aIt
= fAppFilters
.begin(); aIt
!= fAppFilters
.end(); aIt
++)
88 NotificationWindow::QuitRequested()
90 appview_t::iterator aIt
;
91 for (aIt
= fAppViews
.begin(); aIt
!= fAppViews
.end(); aIt
++) {
92 aIt
->second
->RemoveSelf();
96 BMessenger(be_app
).SendMessage(B_QUIT_REQUESTED
);
97 return BWindow::QuitRequested();
102 NotificationWindow::WorkspaceActivated(int32
/*workspace*/, bool active
)
104 // Ensure window is in the correct position
111 NotificationWindow::FrameResized(float width
, float height
)
118 NotificationWindow::ScreenChanged(BRect frame
, color_space mode
)
125 NotificationWindow::MessageReceived(BMessage
* message
)
127 switch (message
->what
) {
133 case kNotificationMessage
:
138 BMessage
reply(B_REPLY
);
139 BNotification
* notification
= new BNotification(message
);
141 if (notification
->InitCheck() == B_OK
) {
143 if (message
->FindInt64("timeout", &timeout
) != B_OK
)
145 BString
sourceSignature(notification
->SourceSignature());
146 BString
sourceName(notification
->SourceName());
149 appfilter_t::iterator it
=
150 fAppFilters
.find(sourceSignature
.String());
152 AppUsage
* appUsage
= NULL
;
153 if (it
== fAppFilters
.end()) {
154 if (sourceSignature
.Length() > 0
155 && sourceName
.Length() > 0) {
156 appUsage
= new AppUsage(sourceName
.String(),
157 sourceSignature
.String(), true);
158 fAppFilters
[sourceSignature
.String()] = appUsage
;
159 // TODO save back to settings file
163 appUsage
= it
->second
;
164 allow
= appUsage
->Allowed();
168 BString
groupName(notification
->Group());
169 appview_t::iterator aIt
= fAppViews
.find(groupName
);
170 AppGroupView
* group
= NULL
;
171 if (aIt
== fAppViews
.end()) {
172 group
= new AppGroupView(this,
173 groupName
== "" ? NULL
: groupName
.String());
174 fAppViews
[groupName
] = group
;
175 GetLayout()->AddView(group
);
179 NotificationView
* view
= new NotificationView(notification
,
182 group
->AddInfo(view
);
186 reply
.AddInt32("error", B_OK
);
188 reply
.AddInt32("error", B_NOT_ALLOWED
);
190 reply
.what
= B_MESSAGE_NOT_UNDERSTOOD
;
191 reply
.AddInt32("error", B_ERROR
);
194 message
->SendReply(&reply
);
197 case kRemoveGroupView
:
199 AppGroupView
* view
= NULL
;
200 if (message
->FindPointer("view", (void**)&view
) != B_OK
)
203 // It's possible that between sending this message, and us receiving
204 // it, the view has become used again, in which case we shouldn't
206 if (view
->HasChildren())
209 // this shouldn't happen
210 if (fAppViews
.erase(view
->Group()) < 1)
220 BWindow::MessageReceived(message
);
226 NotificationWindow::IconSize()
233 NotificationWindow::Timeout()
240 NotificationWindow::Width()
247 NotificationWindow::_ShowHide()
249 if (fAppViews
.empty() && !IsHidden()) {
262 NotificationWindow::SetPosition()
266 BRect bounds
= DecoratorFrame();
267 float width
= Bounds().Width() + 1;
268 float height
= Bounds().Height() + 1;
270 float leftOffset
= Frame().left
- bounds
.left
;
271 float topOffset
= Frame().top
- bounds
.top
+ 1;
272 float rightOffset
= bounds
.right
- Frame().right
;
273 float bottomOffset
= bounds
.bottom
- Frame().bottom
;
274 // Size of the borders around the window
276 float x
= Frame().left
;
277 float y
= Frame().top
;
278 // If we can't guess, don't move...
281 BRect frame
= deskbar
.Frame();
283 switch (deskbar
.Location()) {
285 // Put it just under, top right corner
286 y
= frame
.bottom
+ topOffset
;
287 x
= frame
.right
- width
+ rightOffset
;
289 case B_DESKBAR_BOTTOM
:
290 // Put it just above, lower left corner
291 y
= frame
.top
- height
- bottomOffset
;
292 x
= frame
.right
- width
+ rightOffset
;
294 case B_DESKBAR_RIGHT_TOP
:
295 x
= frame
.left
- width
- rightOffset
;
296 y
= frame
.top
- topOffset
+ 1;
298 case B_DESKBAR_LEFT_TOP
:
299 x
= frame
.right
+ leftOffset
;
300 y
= frame
.top
- topOffset
+ 1;
302 case B_DESKBAR_RIGHT_BOTTOM
:
303 y
= frame
.bottom
- height
+ bottomOffset
;
304 x
= frame
.left
- width
- rightOffset
;
306 case B_DESKBAR_LEFT_BOTTOM
:
307 y
= frame
.bottom
- height
+ bottomOffset
;
308 x
= frame
.right
+ leftOffset
;
319 NotificationWindow::_LoadSettings(bool startMonitor
)
324 if (find_directory(B_USER_SETTINGS_DIRECTORY
, &path
) != B_OK
)
327 path
.Append(kSettingsFile
);
329 BFile
file(path
.Path(), B_READ_ONLY
| B_CREATE_FILE
);
330 settings
.Unflatten(&file
);
332 _LoadGeneralSettings(settings
);
333 _LoadDisplaySettings(settings
);
334 _LoadAppFilters(settings
);
338 BEntry
entry(path
.Path());
339 entry
.GetNodeRef(&nref
);
341 if (watch_node(&nref
, B_WATCH_ALL
, BMessenger(this)) != B_OK
) {
342 BAlert
* alert
= new BAlert(B_TRANSLATE("Warning"),
343 B_TRANSLATE("Couldn't start general settings monitor.\n"
344 "Live filter changes disabled."), B_TRANSLATE("OK"));
345 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
353 NotificationWindow::_LoadAppFilters(BMessage
& settings
)
358 if (settings
.GetInfo("app_usage", &type
, &count
) != B_OK
)
361 for (int32 i
= 0; i
< count
; i
++) {
362 AppUsage
* app
= new AppUsage();
363 if (settings
.FindFlat("app_usage", i
, app
) == B_OK
)
364 fAppFilters
[app
->Signature()] = app
;
372 NotificationWindow::_LoadGeneralSettings(BMessage
& settings
)
374 if (settings
.FindBool(kAutoStartName
, &fShouldRun
) == B_OK
) {
375 if (fShouldRun
== false) {
376 // We should not start. Quit the app!
377 be_app_messenger
.SendMessage(B_QUIT_REQUESTED
);
382 if (settings
.FindInt32(kTimeoutName
, &fTimeout
) != B_OK
)
383 fTimeout
= kDefaultTimeout
;
385 // Convert from seconds to microseconds
390 NotificationWindow::_LoadDisplaySettings(BMessage
& settings
)
393 float originalWidth
= fWidth
;
395 if (settings
.FindFloat(kWidthName
, &fWidth
) != B_OK
)
396 fWidth
= kDefaultWidth
;
397 if (originalWidth
!= fWidth
)
398 GetLayout()->SetExplicitSize(BSize(fWidth
, B_SIZE_UNSET
));
400 if (settings
.FindInt32(kIconSizeName
, &setting
) != B_OK
)
401 fIconSize
= kDefaultIconSize
;
403 fIconSize
= (icon_size
)setting
;
405 // Notify the views about the change
406 appview_t::iterator aIt
;
407 for (aIt
= fAppViews
.begin(); aIt
!= fAppViews
.end(); ++aIt
) {
408 AppGroupView
* view
= aIt
->second
;