docs/ikteam: Delete most files.
[haiku.git] / src / servers / notification / NotificationWindow.cpp
blobbad3683f980ecfb9b10a3e5265ff9dd021abbdf2
1 /*
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.
8 * Authors:
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"
16 #include <algorithm>
18 #include <Alert.h>
19 #include <Application.h>
20 #include <Catalog.h>
21 #include <Deskbar.h>
22 #include <Directory.h>
23 #include <File.h>
24 #include <FindDirectory.h>
25 #include <GroupLayout.h>
26 #include <NodeMonitor.h>
27 #include <Notifications.h>
28 #include <Path.h>
29 #include <PropertyInfo.h>
31 #include "AppGroupView.h"
32 #include "AppUsage.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},
41 "get a message"},
42 {"message", {B_COUNT_PROPERTIES, 0}, {B_DIRECT_SPECIFIER, 0},
43 "count messages"},
44 {"message", {B_CREATE_PROPERTY, 0}, {B_DIRECT_SPECIFIER, 0},
45 "create a message"},
46 {"message", {B_SET_PROPERTY, 0}, {B_INDEX_SPECIFIER, 0},
47 "modify a message"},
49 { 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,
59 B_ALL_WORKSPACES),
60 fShouldRun(true)
62 status_t result = find_directory(B_USER_CACHE_DIRECTORY, &fCachePath);
63 fCachePath.Append("Notifications");
64 BDirectory cacheDir;
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));
71 _LoadSettings(true);
73 // Start the message loop
74 Hide();
75 Show();
79 NotificationWindow::~NotificationWindow()
81 appfilter_t::iterator aIt;
82 for (aIt = fAppFilters.begin(); aIt != fAppFilters.end(); aIt++)
83 delete aIt->second;
87 bool
88 NotificationWindow::QuitRequested()
90 appview_t::iterator aIt;
91 for (aIt = fAppViews.begin(); aIt != fAppViews.end(); aIt++) {
92 aIt->second->RemoveSelf();
93 delete aIt->second;
96 BMessenger(be_app).SendMessage(B_QUIT_REQUESTED);
97 return BWindow::QuitRequested();
101 void
102 NotificationWindow::WorkspaceActivated(int32 /*workspace*/, bool active)
104 // Ensure window is in the correct position
105 if (active)
106 SetPosition();
110 void
111 NotificationWindow::FrameResized(float width, float height)
113 SetPosition();
117 void
118 NotificationWindow::ScreenChanged(BRect frame, color_space mode)
120 SetPosition();
124 void
125 NotificationWindow::MessageReceived(BMessage* message)
127 switch (message->what) {
128 case B_NODE_MONITOR:
130 _LoadSettings();
131 break;
133 case kNotificationMessage:
135 if (!fShouldRun)
136 break;
138 BMessage reply(B_REPLY);
139 BNotification* notification = new BNotification(message);
141 if (notification->InitCheck() == B_OK) {
142 bigtime_t timeout;
143 if (message->FindInt64("timeout", &timeout) != B_OK)
144 timeout = fTimeout;
145 BString sourceSignature(notification->SourceSignature());
146 BString sourceName(notification->SourceName());
148 bool allow = false;
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
161 allow = true;
162 } else {
163 appUsage = it->second;
164 allow = appUsage->Allowed();
167 if (allow) {
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);
176 } else
177 group = aIt->second;
179 NotificationView* view = new NotificationView(notification,
180 timeout, fIconSize);
182 group->AddInfo(view);
184 _ShowHide();
186 reply.AddInt32("error", B_OK);
187 } else
188 reply.AddInt32("error", B_NOT_ALLOWED);
189 } else {
190 reply.what = B_MESSAGE_NOT_UNDERSTOOD;
191 reply.AddInt32("error", B_ERROR);
194 message->SendReply(&reply);
195 break;
197 case kRemoveGroupView:
199 AppGroupView* view = NULL;
200 if (message->FindPointer("view", (void**)&view) != B_OK)
201 return;
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
205 // delete it.
206 if (view->HasChildren())
207 return;
209 // this shouldn't happen
210 if (fAppViews.erase(view->Group()) < 1)
211 break;
213 view->RemoveSelf();
214 delete view;
216 _ShowHide();
217 break;
219 default:
220 BWindow::MessageReceived(message);
225 icon_size
226 NotificationWindow::IconSize()
228 return fIconSize;
232 int32
233 NotificationWindow::Timeout()
235 return fTimeout;
239 float
240 NotificationWindow::Width()
242 return fWidth;
246 void
247 NotificationWindow::_ShowHide()
249 if (fAppViews.empty() && !IsHidden()) {
250 Hide();
251 return;
254 if (IsHidden()) {
255 SetPosition();
256 Show();
261 void
262 NotificationWindow::SetPosition()
264 Layout(true);
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...
280 BDeskbar deskbar;
281 BRect frame = deskbar.Frame();
283 switch (deskbar.Location()) {
284 case B_DESKBAR_TOP:
285 // Put it just under, top right corner
286 y = frame.bottom + topOffset;
287 x = frame.right - width + rightOffset;
288 break;
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;
293 break;
294 case B_DESKBAR_RIGHT_TOP:
295 x = frame.left - width - rightOffset;
296 y = frame.top - topOffset + 1;
297 break;
298 case B_DESKBAR_LEFT_TOP:
299 x = frame.right + leftOffset;
300 y = frame.top - topOffset + 1;
301 break;
302 case B_DESKBAR_RIGHT_BOTTOM:
303 y = frame.bottom - height + bottomOffset;
304 x = frame.left - width - rightOffset;
305 break;
306 case B_DESKBAR_LEFT_BOTTOM:
307 y = frame.bottom - height + bottomOffset;
308 x = frame.right + leftOffset;
309 break;
310 default:
311 break;
314 MoveTo(x, y);
318 void
319 NotificationWindow::_LoadSettings(bool startMonitor)
321 BPath path;
322 BMessage settings;
324 if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
325 return;
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);
336 if (startMonitor) {
337 node_ref nref;
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);
346 alert->Go(NULL);
352 void
353 NotificationWindow::_LoadAppFilters(BMessage& settings)
355 type_code type;
356 int32 count = 0;
358 if (settings.GetInfo("app_usage", &type, &count) != B_OK)
359 return;
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;
365 else
366 delete app;
371 void
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);
379 } else
380 fShouldRun = true;
382 if (settings.FindInt32(kTimeoutName, &fTimeout) != B_OK)
383 fTimeout = kDefaultTimeout;
384 fTimeout *= 1000000;
385 // Convert from seconds to microseconds
389 void
390 NotificationWindow::_LoadDisplaySettings(BMessage& settings)
392 int32 setting;
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;
402 else
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;
409 view->Invalidate();