Notifications preflet and notification_server

Notifications preflet:
-Use sliders instead of text fields for width and timeout
-Remove icon size choice (mini icon looks horrible)
-Consolidate both "Enable" checkboxes into one
-Fix Revert button, remove Apply button, add Defaults button
-All changes to settings saved immediately
-Live example notification message shown when settings changes are made
-Add setting for individual apps to specify whether their notifications
	should be muted
-Remove history list (to be implemented later)

BNotification class:
-BNotification records the signature and name of application that
	created it
-New functions to get source application signature and name

Notification Server:
-Notification pop up view layout fixes and bold font size fix
-Remove notifications history from AppUsage class (will be saved in
	cache instead)
-Remove vector of NotificationView objects which isn't needed
-Get source application info from notification object, not the received
	message which is not reliable
This commit is contained in:
Brian Hill 2017-09-23 11:40:04 -04:00
parent 7cb920e579
commit 6aa0587222
31 changed files with 1045 additions and 880 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _NOTIFICATION_H
@ -34,6 +34,9 @@ public:
static BArchivable* Instantiate(BMessage* archive);
virtual status_t Archive(BMessage* archive, bool deep = true) const;
const char* SourceSignature() const;
const char* SourceName() const;
notification_type Type() const;
const char* Group() const;
@ -73,6 +76,8 @@ public:
private:
status_t fInitStatus;
BString fSourceSignature;
BString fSourceName;
notification_type fType;
BString fGroup;
BString fTitle;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
@ -17,16 +17,13 @@
#include <String.h>
class BMessage;
class NotificationReceived;
typedef std::map<BString, NotificationReceived*> notification_t;
class AppUsage : public BFlattenable {
public:
AppUsage();
AppUsage(const char* name,
const char* signature,
bool allow = true);
~AppUsage();
virtual bool AllowsTypeCode(type_code code) const;
virtual status_t Flatten(void* buffer, ssize_t numBytes) const;
@ -36,17 +33,15 @@ public:
virtual status_t Unflatten(type_code code, const void* buffer,
ssize_t numBytes);
const char* Name();
bool Allowed(const char* title, notification_type type);
const char* AppName();
const char* Signature();
bool Allowed();
NotificationReceived* NotificationAt(int32 index);
int32 Notifications();
void AddNotification(NotificationReceived* notification);
void SetAllowed(bool allow);
private:
BString fName;
BString fAppName;
BString fSignature;
bool fAllow;
notification_t fNotifications;
};
#endif // _APP_USAGE_H

View File

@ -1,11 +1,12 @@
/*
* Copyright 2010-2014, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _NOTIFICATIONS_H
#define _NOTIFICATIONS_H
#include <Mime.h>
#include <String.h>
#define kNotificationServerSignature "application/x-vnd.Haiku-notification_server"
@ -18,18 +19,18 @@ extern const char* kSettingsFile;
// General settings
extern const char* kAutoStartName;
extern const char* kTimeoutName;
extern const char* kWidthName;
extern const char* kIconSizeName;
// General default settings
const bool kDefaultAutoStart = true;
const int32 kDefaultTimeout = 10;
// Display settings
extern const char* kWidthName;
extern const char* kIconSizeName;
extern const char* kLayoutName;
// Display default settings
const int32 kMinimumTimeout = 3;
const int32 kMaximumTimeout = 30;
const float kDefaultWidth = 300.0f;
const float kMinimumWidth = 300.0f;
const float kMaximumWidth = 1000.0f;
const int32 kWidthStep = 50;
const icon_size kDefaultIconSize = B_LARGE_ICON;
#endif // _NOTIFICATIONS_H

View File

@ -1,10 +1,11 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Stephan Aßmus, superstippi@gmx.de
* Brian Hill, supernova@tycho.email
*/
@ -20,6 +21,7 @@
#include <Bitmap.h>
#include <Message.h>
#include <NodeInfo.h>
#include <Path.h>
#include <Roster.h>
@ -32,19 +34,24 @@ BNotification::BNotification(notification_type type)
fFile(NULL),
fBitmap(NULL)
{
team_info teamInfo;
get_team_info(B_CURRENT_TEAM, &teamInfo);
app_info appInfo;
be_roster->GetRunningAppInfo(teamInfo.team, &appInfo);
int32 iconSize = B_LARGE_ICON;
fBitmap = new BBitmap(BRect(0, 0, iconSize - 1, iconSize - 1), 0, B_RGBA32);
if (fBitmap) {
team_info teamInfo;
get_team_info(B_CURRENT_TEAM, &teamInfo);
app_info appInfo;
be_roster->GetRunningAppInfo(teamInfo.team, &appInfo);
if (BNodeInfo::GetTrackerIcon(&appInfo.ref, fBitmap,
icon_size(iconSize)) != B_OK) {
delete fBitmap;
fBitmap = NULL;
}
}
fSourceSignature = appInfo.signature;
BPath path(&appInfo.ref);
if (path.InitCheck() == B_OK)
fSourceName = path.Leaf();
}
@ -56,6 +63,14 @@ BNotification::BNotification(BMessage* archive)
fFile(NULL),
fBitmap(NULL)
{
BString appName;
if (archive->FindString("_appname", &appName) == B_OK)
fSourceName = appName;
BString signature;
if (archive->FindString("_signature", &signature) == B_OK)
fSourceSignature = signature;
int32 type;
if (archive->FindInt32("_type", &type) == B_OK)
fType = (notification_type)type;
@ -166,6 +181,12 @@ BNotification::Archive(BMessage* archive, bool deep) const
{
status_t status = BArchivable::Archive(archive, deep);
if (status == B_OK)
status = archive->AddString("_appname", fSourceName);
if (status == B_OK)
status = archive->AddString("_signature", fSourceSignature);
if (status == B_OK)
status = archive->AddInt32("_type", (int32)fType);
@ -220,6 +241,28 @@ BNotification::Archive(BMessage* archive, bool deep) const
}
/*! \brief Returns source application signature.
\return Source application signature.
*/
const char*
BNotification::SourceSignature() const
{
return fSourceSignature;
}
/*! \brief Returns source application name.
\return Source application name.
*/
const char*
BNotification::SourceName() const
{
return fSourceName;
}
/*! \brief Notification's type.
\return A value of the notification_type enum that represents

View File

@ -0,0 +1,66 @@
/*
* Copyright 2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Brian Hill, supernova@tycho.email
*/
#include "AppRefFilter.h"
#include <string.h>
/* This class filters only applications in an open file panel */
AppRefFilter::AppRefFilter()
:
BRefFilter()
{
}
bool
AppRefFilter::Filter(const entry_ref *ref, BNode *node,
struct stat_beos *st, const char *filetype)
{
char* type = NULL;
const char *constFileType;
// resolve symlinks
bool isSymlink = strcmp("application/x-vnd.Be-symlink", filetype) == 0;
if (isSymlink) {
BEntry linkedEntry(ref, true);
if (linkedEntry.InitCheck()!=B_OK)
return false;
BNode linkedNode(&linkedEntry);
if (linkedNode.InitCheck()!=B_OK)
return false;
BNodeInfo linkedNodeInfo(&linkedNode);
if (linkedNodeInfo.InitCheck()!=B_OK)
return false;
type = new char[B_ATTR_NAME_LENGTH];
if (linkedNodeInfo.GetType(type)!=B_OK) {
delete[] type;
return false;
}
constFileType = type;
} else
constFileType = filetype;
bool pass = false;
//folders
if (strcmp("application/x-vnd.Be-directory", constFileType) == 0)
pass = true;
//volumes
else if (strcmp("application/x-vnd.Be-volume", constFileType) == 0)
pass = true;
//apps
else if (strcmp("application/x-vnd.Be-elfexecutable", constFileType) == 0)
pass = true;
//hack for Haiku? Some apps are defined by MIME this way
else if (strcmp("application/x-vnd.be-elfexecutable", constFileType) == 0)
pass = true;
if (isSymlink)
delete[] type;
return pass;
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef EP_APPREFFILTER_H
#define EP_APPREFFILTER_H
#include <FilePanel.h>
#include <NodeInfo.h>
class AppRefFilter : public BRefFilter {
public:
AppRefFilter();
virtual bool Filter(const entry_ref *ref,
BNode *node,
struct stat_beos *st,
const char *filetype);
};
#endif

View File

@ -1,152 +0,0 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
*/
#include <stdio.h>
#include <stdlib.h>
#include <Alert.h>
#include <Catalog.h>
#include <Directory.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <Menu.h>
#include <MenuItem.h>
#include <MenuField.h>
#include <Message.h>
#include <Mime.h>
#include <Node.h>
#include <Path.h>
#include <SpaceLayoutItem.h>
#include <TextControl.h>
#include <notification/Notifications.h>
#include "DisplayView.h"
#include "SettingsHost.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "DisplayView"
DisplayView::DisplayView(SettingsHost* host)
:
SettingsPane("display", host)
{
// Window width
fWindowWidth = new BTextControl(B_TRANSLATE("Window width:"), NULL,
new BMessage(kSettingChanged));
// Icon size
fIconSize = new BMenu("iconSize");
fIconSize->AddItem(new BMenuItem(B_TRANSLATE("Mini icon"),
new BMessage(kSettingChanged)));
fIconSize->AddItem(new BMenuItem(B_TRANSLATE("Large icon"),
new BMessage(kSettingChanged)));
fIconSize->SetLabelFromMarked(true);
fIconSizeField = new BMenuField(B_TRANSLATE("Icon size:"), fIconSize);
BLayoutBuilder::Grid<>(this, B_USE_DEFAULT_SPACING, B_USE_DEFAULT_SPACING)
.Add(fWindowWidth->CreateLabelLayoutItem(), 0, 0)
.Add(fWindowWidth->CreateTextViewLayoutItem(), 1, 0)
.Add(fIconSizeField->CreateLabelLayoutItem(), 0, 1)
.Add(fIconSizeField->CreateMenuBarLayoutItem(), 1, 1)
.AddGlue(0, 2)
.SetInsets(B_USE_WINDOW_SPACING);
}
void
DisplayView::AttachedToWindow()
{
fWindowWidth->SetTarget(this);
fIconSize->SetTargetForItems(this);
}
void
DisplayView::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kSettingChanged:
SettingsPane::MessageReceived(msg);
break;
default:
BView::MessageReceived(msg);
}
}
status_t
DisplayView::Load(BMessage& settings)
{
#if 0
BPath path;
if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) != B_OK)
return B_ERROR;
path.Append(kSettingsFile);
BFile file(path.Path(), B_READ_ONLY);
BMessage settings;
settings.Unflatten(&file);
#endif
char buffer[255];
int32 setting;
BMenuItem* item = NULL;
float width;
if (settings.FindFloat(kWidthName, &width) != B_OK)
width = kDefaultWidth;
(void)sprintf(buffer, "%.2f", width);
fWindowWidth->SetText(buffer);
icon_size iconSize;
if (settings.FindInt32(kIconSizeName, &setting) != B_OK)
iconSize = kDefaultIconSize;
else
iconSize = (icon_size)setting;
if (iconSize == B_MINI_ICON)
item = fIconSize->ItemAt(0);
else
item = fIconSize->ItemAt(1);
if (item)
item->SetMarked(true);
return B_OK;
}
status_t
DisplayView::Save(BMessage& settings)
{
float width = atof(fWindowWidth->Text());
settings.AddFloat(kWidthName, width);
icon_size iconSize = kDefaultIconSize;
switch (fIconSize->IndexOf(fIconSize->FindMarked())) {
case 0:
iconSize = B_MINI_ICON;
break;
default:
iconSize = B_LARGE_ICON;
}
settings.AddInt32(kIconSizeName, (int32)iconSize);
return B_OK;
}
status_t
DisplayView::Revert()
{
return B_ERROR;
}

View File

@ -1,34 +0,0 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _DISPLAY_VIEW_H
#define _DISPLAY_VIEW_H
#include "SettingsPane.h"
class BTextControl;
class BMenu;
class BMenuField;
class DisplayView : public SettingsPane {
public:
DisplayView(SettingsHost* host);
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* msg);
// SettingsPane hooks
status_t Load(BMessage&);
status_t Save(BMessage&);
status_t Revert();
private:
BTextControl* fWindowWidth;
BMenu* fIconSize;
BMenuField* fIconSizeField;
};
#endif // _DISPLAY_VIEW_H

View File

@ -1,10 +1,11 @@
/*
* Copyright 2010-2013, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Brian Hill, supernova@tycho.email
*/
#include <stdio.h>
@ -13,9 +14,9 @@
#include <vector>
#include <Alert.h>
#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <Directory.h>
#include <File.h>
#include <FindDirectory.h>
@ -26,70 +27,87 @@
#include <Query.h>
#include <Roster.h>
#include <String.h>
#include <StringView.h>
#include <SymLink.h>
#include <TextControl.h>
#include <Volume.h>
#include <VolumeRoster.h>
#include <notification/Notifications.h>
#include "GeneralView.h"
#include "NotificationsConstants.h"
#include "SettingsHost.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "GeneralView"
const int32 kToggleNotifications = '_TSR';
const uint32 kToggleNotifications = '_TSR';
const uint32 kWidthChanged = '_WIC';
const uint32 kTimeoutChanged = '_TIC';
const uint32 kServerChangeTriggered = '_SCT';
const BString kSampleMessageID("NotificationsSample");
GeneralView::GeneralView(SettingsHost* host)
:
SettingsPane("general", host)
{
// Notifications
// Notification server
fNotificationBox = new BCheckBox("server",
B_TRANSLATE("Enable notifications"),
new BMessage(kToggleNotifications));
BBox* box = new BBox("box");
box->SetLabel(fNotificationBox);
// Autostart
fAutoStart = new BCheckBox("autostart",
B_TRANSLATE("Enable notifications at startup"),
new BMessage(kSettingChanged));
// Window width
int32 minWidth = int32(kMinimumWidth / kWidthStep);
int32 maxWidth = int32(kMaximumWidth / kWidthStep);
fWidthSlider = new BSlider("width", B_TRANSLATE("Window width:"),
new BMessage(kWidthChanged), minWidth, maxWidth, B_HORIZONTAL);
fWidthSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
fWidthSlider->SetHashMarkCount(maxWidth - minWidth + 1);
BString minWidthLabel;
minWidthLabel << int32(kMinimumWidth);
BString maxWidthLabel;
maxWidthLabel << int32(kMaximumWidth);
fWidthSlider->SetLimitLabels(
B_TRANSLATE_COMMENT(minWidthLabel.String(), "Slider low text"),
B_TRANSLATE_COMMENT(maxWidthLabel.String(), "Slider high text"));
// Display time
fTimeout = new BTextControl(B_TRANSLATE("Hide notifications from screen"
" after"), NULL, new BMessage(kSettingChanged));
BStringView* displayTimeLabel = new BStringView("dt_label",
B_TRANSLATE("seconds of inactivity"));
// Default position
// TODO: Here will come a screen representation with the four corners
// clickable
fDurationSlider = new BSlider("duration", B_TRANSLATE("Duration:"),
new BMessage(kTimeoutChanged), kMinimumTimeout, kMaximumTimeout,
B_HORIZONTAL);
fDurationSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
fDurationSlider->SetHashMarkCount(kMaximumTimeout - kMinimumTimeout + 1);
BString minLabel;
minLabel << kMinimumTimeout;
BString maxLabel;
maxLabel << kMaximumTimeout;
fDurationSlider->SetLimitLabels(
B_TRANSLATE_COMMENT(minLabel.String(), "Slider low text"),
B_TRANSLATE_COMMENT(maxLabel.String(), "Slider high text"));
box->AddChild(BLayoutBuilder::Group<>(B_VERTICAL)
.SetInsets(B_USE_DEFAULT_SPACING)
.Add(fWidthSlider)
.Add(fDurationSlider)
.AddGlue()
.View());
BLayoutBuilder::Group<>(this, B_VERTICAL)
.AddGroup(B_HORIZONTAL, B_USE_WINDOW_SPACING)
.Add(fNotificationBox)
.AddGlue()
.End()
.AddGroup(B_VERTICAL, B_USE_WINDOW_SPACING)
.Add(fAutoStart)
.AddGroup(B_HORIZONTAL)
.AddGroup(B_HORIZONTAL, 2)
.Add(fTimeout)
.Add(displayTimeLabel)
.End()
.End()
.End()
.SetInsets(B_USE_WINDOW_SPACING)
.AddGlue();
.Add(box)
.End();
}
void
GeneralView::AttachedToWindow()
{
BView::AttachedToWindow();
fNotificationBox->SetTarget(this);
fAutoStart->SetTarget(this);
fTimeout->SetTarget(this);
fWidthSlider->SetTarget(this);
fDurationSlider->SetTarget(this);
}
@ -99,70 +117,23 @@ GeneralView::MessageReceived(BMessage* msg)
switch (msg->what) {
case kToggleNotifications:
{
entry_ref ref;
// Check if server is available
if (!_CanFindServer(&ref)) {
BAlert* alert = new BAlert(B_TRANSLATE("Notifications"),
B_TRANSLATE("The notifications server cannot be"
" found, this means your InfoPopper installation was"
" not successfully completed."), B_TRANSLATE("OK"),
NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
(void)alert->Go();
return;
}
if (fNotificationBox->Value() == B_CONTROL_OFF) {
// Server team
team_id team = be_roster->TeamFor(kNotificationServerSignature);
// Establish a connection to infopopper_server
status_t ret = B_ERROR;
BMessenger messenger(kNotificationServerSignature, team, &ret);
if (ret != B_OK) {
BAlert* alert = new BAlert(B_TRANSLATE(
"Notifications"), B_TRANSLATE("Notifications "
"cannot be stopped, because the server can't be"
" reached."), B_TRANSLATE("OK"), NULL, NULL,
B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
(void)alert->Go();
return;
}
// Send quit message
if (messenger.SendMessage(B_QUIT_REQUESTED) != B_OK) {
BAlert* alert = new BAlert(B_TRANSLATE(
"Notifications"), B_TRANSLATE("Cannot disable"
" notifications because the server can't be "
"reached."), B_TRANSLATE("OK"),
NULL, NULL, B_WIDTH_AS_USUAL, B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
(void)alert->Go();
return;
}
} else if (!_IsServerRunning()) {
// Start server
status_t err = be_roster->Launch(kNotificationServerSignature);
if (err != B_OK) {
BAlert* alert = new BAlert(B_TRANSLATE(
"Notifications"), B_TRANSLATE("Cannot enable"
" notifications because the server cannot be "
"found.\nThis means your InfoPopper installation"
" was not successfully completed."),
B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
B_STOP_ALERT);
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
(void)alert->Go();
return;
}
}
SettingsPane::SettingsChanged(false);
_EnableControls();
break;
}
case kSettingChanged:
SettingsPane::MessageReceived(msg);
case kWidthChanged: {
int32 value = fWidthSlider->Value() * 50;
_SetWidthLabel(value);
SettingsPane::SettingsChanged(true);
break;
}
case kTimeoutChanged:
{
int32 value = fDurationSlider->Value();
_SetTimeoutLabel(value);
SettingsPane::SettingsChanged(true);
break;
}
default:
BView::MessageReceived(msg);
break;
@ -173,34 +144,46 @@ GeneralView::MessageReceived(BMessage* msg)
status_t
GeneralView::Load(BMessage& settings)
{
char buffer[255];
bool autoStart = settings.GetBool(kAutoStartName, true);
fNotificationBox->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF);
fNotificationBox->SetValue(_IsServerRunning() ? B_CONTROL_ON : B_CONTROL_OFF);
if (settings.FindFloat(kWidthName, &fOriginalWidth) != B_OK
|| fOriginalWidth > kMaximumWidth
|| fOriginalWidth < kMinimumWidth)
fOriginalWidth = kDefaultWidth;
bool autoStart;
if (settings.FindBool(kAutoStartName, &autoStart) != B_OK)
autoStart = kDefaultAutoStart;
fAutoStart->SetValue(autoStart ? B_CONTROL_ON : B_CONTROL_OFF);
if (settings.FindInt32(kTimeoutName, &fOriginalTimeout) != B_OK
|| fOriginalTimeout > kMaximumTimeout
|| fOriginalTimeout < kMinimumTimeout)
fOriginalTimeout = kDefaultTimeout;
// TODO need to save again if values outside of expected range
int32 setting;
if (settings.FindInt32(kIconSizeName, &setting) != B_OK)
fOriginalIconSize = kDefaultIconSize;
else
fOriginalIconSize = (icon_size)setting;
int32 timeout;
if (settings.FindInt32(kTimeoutName, &timeout) != B_OK)
timeout = kDefaultTimeout;
(void)sprintf(buffer, "%" B_PRId32, timeout);
fTimeout->SetText(buffer);
return B_OK;
_EnableControls();
return Revert();
}
status_t
GeneralView::Save(BMessage& settings)
{
bool autoStart = (fAutoStart->Value() == B_CONTROL_ON);
bool autoStart = (fNotificationBox->Value() == B_CONTROL_ON);
settings.AddBool(kAutoStartName, autoStart);
int32 timeout = atol(fTimeout->Text());
int32 timeout = fDurationSlider->Value();
settings.AddInt32(kTimeoutName, timeout);
float width = fWidthSlider->Value() * 50;
settings.AddFloat(kWidthName, width);
icon_size iconSize = B_LARGE_ICON;
settings.AddInt32(kIconSizeName, (int32)iconSize);
return B_OK;
}
@ -208,44 +191,97 @@ GeneralView::Save(BMessage& settings)
status_t
GeneralView::Revert()
{
return B_ERROR;
fDurationSlider->SetValue(fOriginalTimeout);
_SetTimeoutLabel(fOriginalTimeout);
fWidthSlider->SetValue(fOriginalWidth / 50);
_SetWidthLabel(fOriginalWidth);
return B_OK;
}
bool
GeneralView::_CanFindServer(entry_ref* ref)
GeneralView::RevertPossible()
{
// Try searching with be_roster
if (be_roster->FindApp(kNotificationServerSignature, ref) == B_OK)
int32 timeout = fDurationSlider->Value();
if (fOriginalTimeout != timeout)
return true;
int32 width = fWidthSlider->Value() * 50;
if (fOriginalWidth != width)
return true;
// Try with a query and take the first result
BVolumeRoster vroster;
BVolume volume;
char volName[B_FILE_NAME_LENGTH];
vroster.Rewind();
while (vroster.GetNextVolume(&volume) == B_OK) {
if ((volume.InitCheck() != B_OK) || !volume.KnowsQuery())
continue;
volume.GetName(volName);
BQuery *query = new BQuery();
query->SetPredicate("(BEOS:APP_SIG==\"" kNotificationServerSignature
"\")");
query->SetVolume(&volume);
query->Fetch();
if (query->GetNextRef(ref) == B_OK)
return true;
}
return false;
}
status_t
GeneralView::Defaults()
{
fDurationSlider->SetValue(kDefaultTimeout);
_SetTimeoutLabel(kDefaultTimeout);
fWidthSlider->SetValue(kDefaultWidth / 50);
_SetWidthLabel(kDefaultWidth);
return B_OK;
}
bool
GeneralView::DefaultsPossible()
{
int32 timeout = fDurationSlider->Value();
if (kDefaultTimeout != timeout)
return true;
int32 width = fWidthSlider->Value() * 50;
if (kDefaultWidth != width)
return true;
return false;
}
bool
GeneralView::UseDefaultRevertButtons()
{
return true;
}
void
GeneralView::_EnableControls()
{
bool enabled = fNotificationBox->Value() == B_CONTROL_ON;
fWidthSlider->SetEnabled(enabled);
fDurationSlider->SetEnabled(enabled);
}
void
GeneralView::_SetTimeoutLabel(int32 value)
{
BString label(B_TRANSLATE("Timeout:"));
label.Append(" ");
label << value;
label.Append(" ").Append(B_TRANSLATE("seconds"));
fDurationSlider->SetLabel(label.String());
}
void
GeneralView::_SetWidthLabel(int32 value)
{
BString label(B_TRANSLATE("Width:"));
label.Append(" ");
label << value;
label.Append(" ").Append(B_TRANSLATE("pixels"));
fWidthSlider->SetLabel(label.String());
}
bool
GeneralView::_IsServerRunning()
{

View File

@ -1,16 +1,24 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _GENERAL_VIEW_H
#define _GENERAL_VIEW_H
#include <Button.h>
#include <CheckBox.h>
#include <Menu.h>
#include <MenuField.h>
#include <Mime.h>
#include <RadioButton.h>
#include <Slider.h>
#include <StringView.h>
#include <TextControl.h>
#include "SettingsPane.h"
class BCheckBox;
class BStringView;
class BTextControl;
class GeneralView : public SettingsPane {
public:
@ -23,14 +31,23 @@ public:
status_t Load(BMessage&);
status_t Save(BMessage&);
status_t Revert();
bool RevertPossible();
status_t Defaults();
bool DefaultsPossible();
bool UseDefaultRevertButtons();
private:
BCheckBox* fNotificationBox;
BCheckBox* fAutoStart;
BTextControl* fTimeout;
BCheckBox* fHideAll;
BSlider* fDurationSlider;
BSlider* fWidthSlider;
int32 fOriginalTimeout;
float fOriginalWidth;
icon_size fOriginalIconSize;
bool _CanFindServer(entry_ref* ref);
void _EnableControls();
void _SetWidthLabel(int32 value);
void _SetTimeoutLabel(int32 value);
bool _IsServerRunning();
};

View File

@ -1,18 +1,20 @@
SubDir HAIKU_TOP src preferences notifications ;
UsePrivateHeaders shared ;
UseHeaders [ FDirName $(HAIKU_TOP) src servers notification ] : true ;
UsePrivateHeaders interface ;
UsePrivateHeaders interface notification ;
Application Notifications :
AppRefFilter.cpp
Notifications.cpp
PrefletWin.cpp
PrefletView.cpp
SettingsPane.cpp
GeneralView.cpp
DisplayView.cpp
NotificationsView.cpp
: be translation libcolumnlistview.a libnotification.a [ TargetLibstdc++ ] localestub
../../servers/notification/NotificationView.cpp
../../servers/notification/AppGroupView.cpp
: be translation tracker libcolumnlistview.a libnotification.a [ TargetLibstdc++ ] localestub
: Notifications.rdef
;
@ -22,7 +24,6 @@ Depends Notifications : libnotification.a ;
DoCatalogs Notifications :
x-vnd.Haiku-Notifications
:
DisplayView.cpp
GeneralView.cpp
PrefletWin.cpp
PrefletView.cpp

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*

View File

@ -0,0 +1,25 @@
/*
* Copyright 2017 Haiku Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef NOTIFICATIONS_CONSTANTS_H
#define NOTIFICATIONS_CONSTANTS_H
// interface messages
const uint32 kDefaults = '_DFT';
const uint32 kRevert = '_RVT';
const uint32 kApply = '_APY';
const uint32 kApplyWithExample = '_APE';
const uint32 kApplicationSelected = '_ASL';
const uint32 kAddApplication = '_AAP';
const uint32 kAddApplicationRef = '_AAR';
const uint32 kRemoveApplication = '_RAP';
const uint32 kMuteChanged = '_MCH';
// user interface
const float kEdgePadding = 5.0;
const float kCLVTitlePadding = 8.0;
#endif /* NOTIFICATIONS_CONSTANTS_H */

View File

@ -1,13 +1,15 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Brian Hill, supernova@tycho.email
*/
#include <Alert.h>
#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <ColumnListView.h>
@ -23,90 +25,113 @@
#include <notification/Notifications.h>
#include <notification/NotificationReceived.h>
#include "NotificationsConstants.h"
#include "NotificationsView.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "NotificationView"
const float kEdgePadding = 5.0;
const float kCLVTitlePadding = 8.0;
const int32 kApplicationSelected = '_ASL';
const int32 kNotificationSelected = '_NSL';
const int32 kCLVDeleteRow = 'av02';
// Applications column indexes
const int32 kAppIndex = 0;
const int32 kAppNameIndex = 0;
const int32 kAppEnabledIndex = 1;
// Notifications column indexes
const int32 kTitleIndex = 0;
const int32 kDateIndex = 1;
const int32 kTypeIndex = 2;
const int32 kAllowIndex = 3;
AppRow::AppRow(const char* name, const char* signature, bool allowed)
:
BRow(),
fName(name),
fSignature(signature),
fAllowed(allowed)
{
SetField(new BStringField(fName.String()), kAppNameIndex);
BString text = fAllowed ? B_TRANSLATE("Allowed") : B_TRANSLATE("Muted");
SetField(new BStringField(text.String()), kAppEnabledIndex);
}
void
AppRow::SetAllowed(bool allowed)
{
fAllowed = allowed;
RefreshEnabledField();
}
void
AppRow::RefreshEnabledField()
{
BStringField* field = (BStringField*)GetField(kAppEnabledIndex);
BString text = fAllowed ? B_TRANSLATE("Allowed") : B_TRANSLATE("Muted");
field->SetString(text.String());
Invalidate();
}
NotificationsView::NotificationsView(SettingsHost* host)
:
SettingsPane("apps", host)
SettingsPane("apps", host),
fSelectedRow(NULL)
{
BRect rect(0, 0, 100, 100);
// Search application field
fSearch = new BTextControl(B_TRANSLATE("Search:"), NULL,
new BMessage(kSettingChanged));
// Applications list
fApplications = new BColumnListView(B_TRANSLATE("Applications"),
0, B_FANCY_BORDER, true);
0, B_FANCY_BORDER, false);
fApplications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
fApplications->SetSelectionMessage(new BMessage(kApplicationSelected));
fAppCol = new BStringColumn(B_TRANSLATE("Application"), 200,
be_plain_font->StringWidth(B_TRANSLATE("Application")) +
(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
fApplications->AddColumn(fAppCol, kAppIndex);
float colWidth = be_plain_font->StringWidth(B_TRANSLATE("Application"))
+ (kCLVTitlePadding * 2);
fAppCol = new BStringColumn(B_TRANSLATE("Application"), colWidth * 2,
colWidth, colWidth * 4, B_TRUNCATE_END, B_ALIGN_LEFT);
fApplications->AddColumn(fAppCol, kAppNameIndex);
fAppEnabledCol = new BStringColumn(B_TRANSLATE("Enabled"), 10,
be_plain_font->StringWidth(B_TRANSLATE("Enabled")) +
(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
colWidth = be_plain_font->StringWidth(B_TRANSLATE("Status"))
+ (kCLVTitlePadding * 2);
fAppEnabledCol = new BStringColumn(B_TRANSLATE("Status"), colWidth * 1.5,
colWidth, colWidth * 3, B_TRUNCATE_END, B_ALIGN_LEFT);
fApplications->AddColumn(fAppEnabledCol, kAppEnabledIndex);
// Notifications list
fNotifications = new BColumnListView(B_TRANSLATE("Notifications"),
0, B_FANCY_BORDER, true);
fNotifications->SetSelectionMode(B_SINGLE_SELECTION_LIST);
fTitleCol = new BStringColumn(B_TRANSLATE("Title"), 100,
be_plain_font->StringWidth(B_TRANSLATE("Title")) +
(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
fNotifications->AddColumn(fTitleCol, kTitleIndex);
fDateCol = new BDateColumn(B_TRANSLATE("Last received"), 100,
be_plain_font->StringWidth(B_TRANSLATE("Last received")) +
(kCLVTitlePadding * 2), rect.Width(), B_ALIGN_LEFT);
fNotifications->AddColumn(fDateCol, kDateIndex);
fTypeCol = new BStringColumn(B_TRANSLATE("Type"), 100,
be_plain_font->StringWidth(B_TRANSLATE("Type")) +
(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
fNotifications->AddColumn(fTypeCol, kTypeIndex);
fAllowCol = new BStringColumn(B_TRANSLATE("Allowed"), 100,
be_plain_font->StringWidth(B_TRANSLATE("Allowed")) +
(kCLVTitlePadding * 2), rect.Width(), B_TRUNCATE_END, B_ALIGN_LEFT);
fNotifications->AddColumn(fAllowCol, kAllowIndex);
fApplications->SetSortColumn(fAppCol, true, true);
fAddButton = new BButton("add_app", B_TRANSLATE("Add" B_UTF8_ELLIPSIS),
new BMessage(kAddApplication));
fRemoveButton = new BButton("add_app", B_TRANSLATE("Remove"),
new BMessage(kRemoveApplication));
fRemoveButton->SetEnabled(false);
fMuteAll = new BCheckBox("block", B_TRANSLATE("Mute notifications from "
"this application"),
new BMessage(kMuteChanged));
// Add views
BLayoutBuilder::Group<>(this, B_VERTICAL)
.AddGroup(B_HORIZONTAL)
.AddGlue()
.Add(fSearch)
.Add(fApplications)
.AddGroup(B_VERTICAL)
.Add(fAddButton)
.Add(fRemoveButton)
.AddGlue()
.End()
.End()
.Add(fApplications)
.Add(fNotifications)
.Add(fMuteAll)
.SetInsets(B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING,
B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING);
// Set button sizes
float maxButtonWidth = std::max(fAddButton->PreferredSize().Width(),
fRemoveButton->PreferredSize().Width());
fAddButton->SetExplicitMaxSize(BSize(maxButtonWidth, B_SIZE_UNSET));
fRemoveButton->SetExplicitMaxSize(BSize(maxButtonWidth, B_SIZE_UNSET));
// File Panel
fPanelFilter = new AppRefFilter();
fAddAppPanel = new BFilePanel(B_OPEN_PANEL, NULL, NULL, B_FILE_NODE, false,
NULL, fPanelFilter);
}
NotificationsView::~NotificationsView()
{
delete fAddAppPanel;
delete fPanelFilter;
}
@ -115,14 +140,11 @@ NotificationsView::AttachedToWindow()
{
fApplications->SetTarget(this);
fApplications->SetInvocationMessage(new BMessage(kApplicationSelected));
fNotifications->SetTarget(this);
fNotifications->SetInvocationMessage(new BMessage(kNotificationSelected));
#if 0
fNotifications->AddFilter(new BMessageFilter(B_ANY_DELIVERY,
B_ANY_SOURCE, B_KEY_DOWN, CatchDelete));
#endif
fAddButton->SetTarget(this);
fRemoveButton->SetTarget(this);
fMuteAll->SetTarget(this);
fAddAppPanel->SetTarget(this);
_RecallItemSettings();
}
@ -132,22 +154,97 @@ NotificationsView::MessageReceived(BMessage* msg)
switch (msg->what) {
case kApplicationSelected:
{
BRow* row = fApplications->CurrentSelection();
if (row == NULL)
return;
BStringField* appName
= dynamic_cast<BStringField*>(row->GetField(kAppIndex));
if (appName == NULL)
break;
appusage_t::iterator it = fAppFilters.find(appName->String());
if (it != fAppFilters.end())
_Populate(it->second);
Window()->Lock();
_ClearItemSettings();
_UpdateSelectedItem();
_RecallItemSettings();
Window()->Unlock();
break;
}
case kNotificationSelected:
case kMuteChanged:
{
bool allowed = fMuteAll->Value() == B_CONTROL_OFF;
fSelectedRow->SetAllowed(allowed);
appusage_t::iterator it = fAppFilters.find(fSelectedRow->Signature());
if (it != fAppFilters.end())
it->second->SetAllowed(allowed);
Window()->PostMessage(kApply);
break;
}
case kAddApplication:
{
BMessage addmsg(kAddApplicationRef);
fAddAppPanel->SetMessage(&addmsg);
fAddAppPanel->Show();
break;
}
case kAddApplicationRef:
{
entry_ref srcRef;
msg->FindRef("refs", &srcRef);
BEntry srcEntry(&srcRef, true);
BPath path(&srcEntry);
BNode node(&srcEntry);
char *buf = new char[B_ATTR_NAME_LENGTH];
ssize_t size;
if ( (size = node.ReadAttr("BEOS:APP_SIG", 0, 0, buf,
B_ATTR_NAME_LENGTH)) > 0 )
{
// Search for already existing app
appusage_t::iterator it = fAppFilters.find(buf);
if (it != fAppFilters.end()) {
BString text(path.Leaf());
text.Append(B_TRANSLATE_COMMENT(" is already listed",
"Alert message"));
BAlert* alert = new BAlert("", text.String(),
B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->Go(NULL);
} else {
AppUsage* appUsage = new AppUsage(path.Leaf(), buf, true);
fAppFilters[appUsage->Signature()] = appUsage;
AppRow* row = new AppRow(appUsage->AppName(),
appUsage->Signature(), appUsage->Allowed());
fApplications->AddRow(row);
fApplications->DeselectAll();
fApplications->AddToSelection(row);
fApplications->ScrollTo(row);
_UpdateSelectedItem();
_RecallItemSettings();
//row->Invalidate();
//fApplications->InvalidateRow(row);
// TODO redraw row properly
Window()->PostMessage(kApply);
}
} else {
BAlert* alert = new BAlert("",
B_TRANSLATE_COMMENT("Application does not have "
"a valid signature", "Alert message"),
B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_AS_USUAL,
B_WARNING_ALERT);
alert->Go(NULL);
}
delete[] buf;
break;
}
case kRemoveApplication:
{
if (fSelectedRow) {
appusage_t::iterator it = fAppFilters.find(fSelectedRow->Signature());
if (it != fAppFilters.end()) {
delete it->second;
fAppFilters.erase(it);
}
fApplications->RemoveRow(fSelectedRow);
delete fSelectedRow;
fSelectedRow = NULL;
_ClearItemSettings();
_UpdateSelectedItem();
_RecallItemSettings();
Window()->PostMessage(kApply);
}
break;
}
default:
BView::MessageReceived(msg);
break;
@ -174,7 +271,7 @@ NotificationsView::Load(BMessage& settings)
for (int32 i = 0; i < count; i++) {
AppUsage* app = new AppUsage();
settings.FindFlat("app_usage", i, app);
fAppFilters[app->Name()] = app;
fAppFilters[app->Signature()] = app;
}
// Load the applications list
@ -196,64 +293,84 @@ NotificationsView::Save(BMessage& storage)
void
NotificationsView::_PopulateApplications()
NotificationsView::_ClearItemSettings()
{
appusage_t::iterator it;
fApplications->Clear();
for (it = fAppFilters.begin(); it != fAppFilters.end(); ++it) {
BRow* row = new BRow();
row->SetField(new BStringField(it->first.String()), kAppIndex);
fApplications->AddRow(row);
}
fMuteAll->SetValue(B_CONTROL_OFF);
}
void
NotificationsView::_Populate(AppUsage* usage)
NotificationsView::_UpdateSelectedItem()
{
// Sanity check
if (!usage)
return;
fSelectedRow = dynamic_cast<AppRow*>(fApplications->CurrentSelection());
}
int32 size = usage->Notifications();
if (usage->Allowed() == false)
fBlockAll->SetValue(B_CONTROL_ON);
fNotifications->Clear();
for (int32 i = 0; i < size; i++) {
NotificationReceived* notification = usage->NotificationAt(i);
time_t updated = notification->LastReceived();
const char* allow = notification->Allowed() ? B_TRANSLATE("Yes")
: B_TRANSLATE("No");
const char* type = "";
switch (notification->Type()) {
case B_INFORMATION_NOTIFICATION:
type = B_TRANSLATE("Information");
break;
case B_IMPORTANT_NOTIFICATION:
type = B_TRANSLATE("Important");
break;
case B_ERROR_NOTIFICATION:
type = B_TRANSLATE("Error");
break;
case B_PROGRESS_NOTIFICATION:
type = B_TRANSLATE("Progress");
break;
default:
type = B_TRANSLATE("Unknown");
}
BRow* row = new BRow();
row->SetField(new BStringField(notification->Title()), kTitleIndex);
row->SetField(new BDateField(&updated), kDateIndex);
row->SetField(new BStringField(type), kTypeIndex);
row->SetField(new BStringField(allow), kAllowIndex);
fNotifications->AddRow(row);
void
NotificationsView::_RecallItemSettings()
{
// No selected item
if(fSelectedRow == NULL)
{
fMuteAll->SetValue(B_CONTROL_OFF);
fMuteAll->SetEnabled(false);
fRemoveButton->SetEnabled(false);
} else {
fMuteAll->SetEnabled(true);
fRemoveButton->SetEnabled(true);
appusage_t::iterator it = fAppFilters.find(fSelectedRow->Signature());
if (it != fAppFilters.end())
fMuteAll->SetValue(!(it->second->Allowed()));
}
}
status_t
NotificationsView::Revert()
{
return B_OK;
}
bool
NotificationsView::RevertPossible()
{
return false;
}
status_t
NotificationsView::Defaults()
{
return B_OK;
}
bool
NotificationsView::DefaultsPossible()
{
return false;
}
bool
NotificationsView::UseDefaultRevertButtons()
{
return false;
}
void
NotificationsView::_PopulateApplications()
{
fApplications->Clear();
appusage_t::iterator it;
for (it = fAppFilters.begin(); it != fAppFilters.end(); ++it) {
AppUsage* appUsage = it->second;
AppRow* row = new AppRow(appUsage->AppName(),
appUsage->Signature(), appUsage->Allowed());
fApplications->AddRow(row);
}
}

View File

@ -1,50 +1,78 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _APPS_VIEW_H
#define _APPS_VIEW_H
#include <FilePanel.h>
#include <ColumnListView.h>
#include <View.h>
#include <notification/AppUsage.h>
#include "AppRefFilter.h"
#include "SettingsPane.h"
typedef std::map<BString, AppUsage *> appusage_t;
class BButton;
class BCheckBox;
class BTextControl;
class BColumnListView;
class BStringColumn;
class BDateColumn;
class AppRow : public BRow {
public:
AppRow(const char* name,
const char* signature, bool allowed);
const char* Name() const { return fName.String(); }
const char* Signature() { return fSignature.String(); };
void SetAllowed(bool allowed);
bool Allowed() { return fAllowed; };
void RefreshEnabledField();
private:
BString fName;
BString fSignature;
bool fAllowed;
};
class NotificationsView : public SettingsPane {
public:
NotificationsView(SettingsHost* host);
~NotificationsView();
virtual void AttachedToWindow();
virtual void MessageReceived(BMessage* msg);
status_t Revert();
bool RevertPossible();
status_t Defaults();
bool DefaultsPossible();
bool UseDefaultRevertButtons();
private:
status_t Load(BMessage&);
status_t Save(BMessage&);
status_t Revert() {return B_OK;} // FIXME implement this
void _ClearItemSettings();
void _UpdateSelectedItem();
void _RecallItemSettings();
void _PopulateApplications();
void _Populate(AppUsage* usage);
appusage_t fAppFilters;
BCheckBox* fBlockAll;
BTextControl* fSearch;
AppRefFilter* fPanelFilter;
BFilePanel* fAddAppPanel;
BButton* fAddButton;
BButton* fRemoveButton;
BCheckBox* fMuteAll;
BColumnListView* fApplications;
AppRow* fSelectedRow;
BStringColumn* fAppCol;
BStringColumn* fAppEnabledCol;
BColumnListView* fNotifications;
BStringColumn* fTitleCol;
BDateColumn* fDateCol;
BStringColumn* fTypeCol;
BStringColumn* fAllowCol;
};
#endif // _APPS_VIEW_H

View File

@ -1,10 +1,11 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Brian Hill, supernova@tycho.email
*/
#include <Catalog.h>
@ -13,9 +14,8 @@
#include <GroupLayoutBuilder.h>
#include <LayoutItem.h>
#include <Message.h>
#include <Window.h>
#include "DisplayView.h"
#include "GeneralView.h"
#include "NotificationsView.h"
#include "PrefletView.h"
#include "SettingsHost.h"
@ -25,30 +25,22 @@
#define B_TRANSLATION_CONTEXT "PrefletView"
const int32 kPageSelected = '_LCH';
PrefletView::PrefletView(SettingsHost* host)
:
BTabView("pages", B_WIDTH_FROM_WIDEST)
{
// Pages
GeneralView* general = new GeneralView(host);
DisplayView* display = new DisplayView(host);
fGeneralView = new GeneralView(host);
NotificationsView* apps = new NotificationsView(host);
// Page selector
BTab* tab = new BTab();
AddTab(general, tab);
AddTab(fGeneralView, tab);
tab->SetLabel(B_TRANSLATE("General"));
tab = new BTab();
AddTab(display, tab);
tab->SetLabel(B_TRANSLATE("Display"));
tab = new BTab();
AddTab(apps, tab);
tab->SetLabel(B_TRANSLATE("Notifications"));
tab->SetLabel(B_TRANSLATE("Applications"));
}
@ -59,15 +51,24 @@ PrefletView::CurrentPage()
}
int32
PrefletView::CountPages() const
{
return 3;
}
BView*
PrefletView::PageAt(int32 index)
{
return TabAt(index)->View();
}
void
PrefletView::Select(int32 index)
{
if (index == Selection())
return;
BTabView::Select(index);
SettingsPane* pane = dynamic_cast<SettingsPane*>(PageAt(index));
bool showButtons = (pane != NULL) && pane->UseDefaultRevertButtons();
BMessage showMessage(kShowButtons);
showMessage.AddBool(kShowButtonsKey, showButtons);
Window()->PostMessage(&showMessage);
}

View File

@ -1,24 +1,32 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _PREFLET_VIEW_H
#define _PREFLET_VIEW_H
#include <Messenger.h>
#include <TabView.h>
class BIconRule;
#include "GeneralView.h"
class SettingsHost;
const int32 kShowButtons = '_SHB';
#define kShowButtonsKey "showButtons"
class PrefletView : public BTabView {
public:
PrefletView(SettingsHost* host);
BView* CurrentPage();
int32 CountPages() const;
BView* PageAt(int32 index);
virtual void Select(int32 index);
private:
GeneralView* fGeneralView;
BMessenger fMessenger;
};
#endif // PREFLETVIEW_H

View File

@ -1,10 +1,11 @@
/*
* Copyright 2010-2014, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Brian Hill, supernova@tycho.email
*/
#include "PrefletWin.h"
@ -14,52 +15,62 @@
#include <Button.h>
#include <Catalog.h>
#include <FindDirectory.h>
#include <LayoutBuilder.h>
#include <Notification.h>
#include <Path.h>
#include <SeparatorView.h>
#include <notification/Notifications.h>
#include "NotificationsConstants.h"
#include "PrefletView.h"
#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PrefletWin"
const int32 kRevert = '_RVT';
const int32 kApply = '_APY';
const BString kSampleMessageID("NotificationsSample");
PrefletWin::PrefletWin()
:
BWindow(BRect(0, 0, 500, 400), B_TRANSLATE_SYSTEM_NAME("Notifications"),
BWindow(BRect(0, 0, 160 + 20 * be_plain_font->Size(), 300),
B_TRANSLATE_SYSTEM_NAME("Notifications"),
B_TITLED_WINDOW, B_NOT_ZOOMABLE | B_ASYNCHRONOUS_CONTROLS
| B_AUTO_UPDATE_SIZE_LIMITS)
{
// Preflet container view
fMainView = new PrefletView(this);
fMainView->SetBorder(B_NO_BORDER);
fMainView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET));
fMainView->SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED));
// Apply and revert buttons
// Defaults button
fDefaults = new BButton("defaults", B_TRANSLATE("Defaults"),
new BMessage(kDefaults));
fDefaults->SetEnabled(false);
// Revert button
fRevert = new BButton("revert", B_TRANSLATE("Revert"),
new BMessage(kRevert));
fRevert->SetEnabled(false);
fApply = new BButton("apply", B_TRANSLATE("Apply"), new BMessage(kApply));
fApply->SetEnabled(false);
// Build the layout
fButtonsView = new BGroupView();
BLayoutBuilder::Group<>(fButtonsView, B_VERTICAL, 0)
.AddGroup(B_HORIZONTAL)
.Add(fDefaults)
.Add(fRevert)
.AddGlue()
.SetInsets(B_USE_WINDOW_SPACING, 0, B_USE_WINDOW_SPACING,
B_USE_WINDOW_SPACING)
.End();
fButtonsLayout = fButtonsView->GroupLayout();
BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
.SetInsets(0, B_USE_DEFAULT_SPACING, 0, 0)
.Add(fMainView)
.Add(new BSeparatorView(B_HORIZONTAL))
.AddGroup(B_HORIZONTAL)
.Add(fRevert)
.AddGlue()
.Add(fApply)
.SetInsets(B_USE_WINDOW_SPACING, B_USE_DEFAULT_SPACING,
B_USE_WINDOW_SPACING, B_USE_WINDOW_SPACING);
.Add(fButtonsView)
.End();
fMainView->SetExplicitMinSize(BSize(Frame().Width(), B_SIZE_UNSET));
ReloadSettings();
@ -74,6 +85,7 @@ PrefletWin::MessageReceived(BMessage* msg)
{
switch (msg->what) {
case kApply:
case kApplyWithExample:
{
BPath path;
@ -85,13 +97,14 @@ PrefletWin::MessageReceived(BMessage* msg)
path.Append(kSettingsFile);
BMessage settingsStore;
for (int32 i = 0; i < fMainView->CountPages(); i++) {
int32 count = fMainView->CountTabs();
for (int32 i = 0; i < count; i++) {
SettingsPane* pane =
dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
if (pane) {
if (pane->Save(settingsStore) == B_OK) {
fApply->SetEnabled(false);
fRevert->SetEnabled(true);
fDefaults->SetEnabled(_DefaultsPossible());
fRevert->SetEnabled(_RevertPossible());
} else
break;
}
@ -109,19 +122,28 @@ PrefletWin::MessageReceived(BMessage* msg)
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
(void)alert->Go();
}
file.Unset();
if (msg->what == kApplyWithExample)
_SendExampleNotification();
break;
}
case kRevert:
for (int32 i = 0; i < fMainView->CountPages(); i++) {
SettingsPane* pane =
dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
if (pane) {
if (pane->Revert() == B_OK)
fRevert->SetEnabled(false);
}
}
case kDefaults:
fDefaults->SetEnabled(false);
_Defaults();
PostMessage(kApply);
break;
case kRevert:
fRevert->SetEnabled(false);
_Revert();
PostMessage(kApply);
break;
case kShowButtons: {
bool show = msg->GetBool(kShowButtonsKey, true);
fButtonsLayout->SetVisible(show);
break;
}
default:
BWindow::MessageReceived(msg);
}
@ -137,9 +159,12 @@ PrefletWin::QuitRequested()
void
PrefletWin::SettingChanged()
PrefletWin::SettingChanged(bool showExample)
{
fApply->SetEnabled(true);
if (showExample)
PostMessage(kApplyWithExample);
else
PostMessage(kApply);
}
@ -158,10 +183,80 @@ PrefletWin::ReloadSettings()
BFile file(path.Path(), B_READ_ONLY);
settings.Unflatten(&file);
for (int32 i = 0; i < fMainView->CountPages(); i++) {
int32 count = fMainView->CountTabs();
for (int32 i = 0; i < count; i++) {
SettingsPane* pane =
dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
if (pane)
pane->Load(settings);
}
fDefaults->SetEnabled(_DefaultsPossible());
}
status_t
PrefletWin::_Revert()
{
int32 count = fMainView->CountTabs();
for (int32 i = 0; i < count; i++) {
SettingsPane* pane =
dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
if (pane)
pane->Revert();
}
return B_OK;
}
bool
PrefletWin::_RevertPossible()
{
int32 count = fMainView->CountTabs();
for (int32 i = 0; i < count; i++) {
SettingsPane* pane =
dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
if (pane && pane->RevertPossible())
return true;
}
return false;
}
status_t
PrefletWin::_Defaults()
{
int32 count = fMainView->CountTabs();
for (int32 i = 0; i < count; i++) {
SettingsPane* pane =
dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
if (pane)
pane->Defaults();
}
return B_OK;
}
bool
PrefletWin::_DefaultsPossible()
{
int32 count = fMainView->CountTabs();
for (int32 i = 0; i < count; i++) {
SettingsPane* pane =
dynamic_cast<SettingsPane*>(fMainView->PageAt(i));
if (pane && pane->DefaultsPossible())
return true;
}
return false;
}
void
PrefletWin::_SendExampleNotification()
{
BNotification notification(B_INFORMATION_NOTIFICATION);
notification.SetMessageID(kSampleMessageID);
notification.SetGroup(B_TRANSLATE("Notifications"));
notification.SetTitle(B_TRANSLATE("Notifications preflet sample"));
notification.SetContent(B_TRANSLATE("This is a test notification message"));
notification.Send();
}

View File

@ -1,11 +1,15 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
#ifndef _PREFLET_WIN_H
#define _PREFLET_WIN_H
#include <GroupView.h>
#include <LayoutBuilder.h>
#include <Message.h>
#include <Window.h>
#include "SettingsHost.h"
@ -21,13 +25,21 @@ public:
virtual bool QuitRequested();
virtual void MessageReceived(BMessage* msg);
virtual void SettingChanged();
virtual void SettingChanged(bool showExample);
void ReloadSettings();
private:
status_t _Revert();
bool _RevertPossible();
status_t _Defaults();
bool _DefaultsPossible();
void _SendExampleNotification();
PrefletView* fMainView;
BButton* fApply;
BGroupView* fButtonsView;
BButton* fDefaults;
BButton* fRevert;
BGroupLayout* fButtonsLayout;
};
#endif // _PREFLET_WIN_H

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
@ -14,7 +14,7 @@ class SettingsHost {
public:
SettingsHost() {}
virtual void SettingChanged() = 0;
virtual void SettingChanged(bool showExample = false) = 0;
};
#endif // _SETTINGS_HOST_H

View File

@ -1,10 +1,11 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*
* Authors:
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Brian Hill, supernova@tycho.email
*/
#include <Message.h>
@ -22,13 +23,7 @@ SettingsPane::SettingsPane(const char* name, SettingsHost* host)
void
SettingsPane::MessageReceived(BMessage* msg)
SettingsPane::SettingsChanged(bool showExample)
{
switch (msg->what) {
case kSettingChanged:
fHost->SettingChanged();
break;
default:
BView::MessageReceived(msg);
}
fHost->SettingChanged(showExample);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2009, Pier Luigi Fiorini.
* Distributed under the terms of the MIT License.
*/
@ -12,17 +12,20 @@ class BNode;
class SettingsHost;
const int32 kSettingChanged = '_STC';
class SettingsPane : public BView {
public:
SettingsPane(const char* name, SettingsHost* host);
virtual void MessageReceived(BMessage* msg);
void SettingsChanged(bool showExample);
virtual status_t Load(BMessage&) = 0;
virtual status_t Save(BMessage&) = 0;
virtual status_t Revert() = 0;
virtual bool RevertPossible() = 0;
virtual status_t Defaults() = 0;
virtual bool DefaultsPossible() = 0;
virtual bool UseDefaultRevertButtons() = 0;
protected:
SettingsHost* fHost;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
@ -9,6 +9,7 @@
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Brian Hill, supernova@tycho.email
*/
#include <algorithm>
@ -22,21 +23,24 @@
#include "NotificationWindow.h"
#include "NotificationView.h"
static const int kHeaderSize = 23;
const float kCloseSize = 6;
const float kEdgePadding = 2;
AppGroupView::AppGroupView(NotificationWindow* win, const char* label)
AppGroupView::AppGroupView(const BMessenger& messenger, const char* label)
:
BGroupView("appGroup", B_VERTICAL, 0),
fLabel(label),
fParent(win),
fMessenger(messenger),
fCollapsed(false),
fCloseClicked(false)
fCloseClicked(false),
fPreviewModeOn(false)
{
SetFlags(Flags() | B_WILL_DRAW);
static_cast<BGroupLayout*>(GetLayout())->SetInsets(0, kHeaderSize, 0, 0);
fHeaderSize = be_plain_font->Size()
+ be_control_look->ComposeSpacing(B_USE_ITEM_SPACING);
static_cast<BGroupLayout*>(GetLayout())->SetInsets(0, fHeaderSize, 0, 0);
}
@ -47,7 +51,7 @@ AppGroupView::Draw(BRect updateRect)
BRect bounds = Bounds();
rgb_color hilite = tint_color(menuColor, B_DARKEN_1_TINT);
rgb_color vlight = tint_color(menuColor, B_LIGHTEN_2_TINT);
bounds.bottom = bounds.top + kHeaderSize;
bounds.bottom = bounds.top + fHeaderSize;
// Draw the header background
SetHighColor(tint_color(menuColor, 1.22));
@ -55,18 +59,17 @@ AppGroupView::Draw(BRect updateRect)
StrokeLine(bounds.LeftTop(), bounds.LeftBottom());
uint32 borders = BControlLook::B_TOP_BORDER
| BControlLook::B_BOTTOM_BORDER | BControlLook::B_RIGHT_BORDER;
be_control_look->DrawButtonBackground(this, bounds, bounds, menuColor,
0, borders);
// Draw the buttons
fCollapseRect.top = (kHeaderSize - kExpandSize) / 2;
fCollapseRect.top = (fHeaderSize - kExpandSize) / 2;
fCollapseRect.left = kEdgePadding * 3;
fCollapseRect.right = fCollapseRect.left + 1.5 * kExpandSize;
fCollapseRect.bottom = fCollapseRect.top + kExpandSize;
fCloseRect = bounds;
fCloseRect.top = (kHeaderSize - kCloseSize) / 2;
fCloseRect.top = (fHeaderSize - kCloseSize) / 2;
// Take off the 1 to line this up with the close button on the
// notification view
fCloseRect.right -= kEdgePadding * 3 - 1;
@ -89,7 +92,9 @@ AppGroupView::Draw(BRect updateRect)
if (fCollapsed)
label << " (" << fInfo.size() << ")";
SetFont(be_bold_font);
BFont boldFont(be_plain_font);
boldFont.SetFace(B_BOLD_FACE);
SetFont(&boldFont);
font_height fontHeight;
GetFontHeight(&fontHeight);
float y = (bounds.top + bounds.bottom - ceilf(fontHeight.ascent)
@ -132,6 +137,10 @@ AppGroupView::_DrawCloseButton(const BRect& updateRect)
void
AppGroupView::MouseDown(BPoint point)
{
// Preview Mode ignores any mouse clicks
if (fPreviewModeOn)
return;
if (BRect(fCloseRect).InsetBySelf(-5, -5).Contains(point)) {
int32 children = fInfo.size();
for (int32 i = 0; i < children; i++) {
@ -144,7 +153,7 @@ AppGroupView::MouseDown(BPoint point)
// Remove ourselves from the parent view
BMessage message(kRemoveGroupView);
message.AddPointer("view", this);
fParent->PostMessage(&message);
fMessenger.SendMessage(&message);
} else if (BRect(fCollapseRect).InsetBySelf(-5, -5).Contains(point)) {
fCollapsed = !fCollapsed;
int32 children = fInfo.size();
@ -186,13 +195,13 @@ AppGroupView::MessageReceived(BMessage* msg)
view->RemoveSelf();
delete view;
fParent->PostMessage(msg);
fMessenger.SendMessage(msg);
if (!this->HasChildren()) {
Hide();
BMessage removeSelfMessage(kRemoveGroupView);
removeSelfMessage.AddPointer("view", this);
fParent->PostMessage(&removeSelfMessage);
fMessenger.SendMessage(&removeSelfMessage);
}
break;
@ -211,17 +220,13 @@ AppGroupView::AddInfo(NotificationView* view)
if (id.Length() > 0) {
int32 children = fInfo.size();
for (int32 i = 0; i < children; i++) {
if (id == fInfo[i]->MessageID()) {
NotificationView* oldView = fInfo[i];
fParent->NotificationViewSwapped(oldView, view);
oldView->RemoveSelf();
delete oldView;
fInfo[i] = view;
found = true;
break;
}
}
@ -246,6 +251,13 @@ AppGroupView::AddInfo(NotificationView* view)
}
void
AppGroupView::SetPreviewModeOn(bool enabled)
{
fPreviewModeOn = enabled;
}
const BString&
AppGroupView::Group() const
{
@ -253,6 +265,14 @@ AppGroupView::Group() const
}
void
AppGroupView::SetGroup(const char* group)
{
fLabel.SetTo(group);
Invalidate();
}
bool
AppGroupView::HasChildren()
{

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
@ -11,6 +11,7 @@
#include <vector>
#include <GroupView.h>
#include <Messenger.h>
#include <String.h>
class BGroupView;
@ -22,7 +23,7 @@ typedef std::vector<NotificationView*> infoview_t;
class AppGroupView : public BGroupView {
public:
AppGroupView(NotificationWindow* win, const char* label);
AppGroupView(const BMessenger& messenger, const char* label);
virtual void MouseDown(BPoint point);
virtual void MessageReceived(BMessage* msg);
@ -32,19 +33,23 @@ public:
int32 ChildrenCount();
void AddInfo(NotificationView* view);
void SetPreviewModeOn(bool enabled);
const BString& Group() const;
void SetGroup(const char* group);
private:
void _DrawCloseButton(const BRect& updateRect);
BString fLabel;
NotificationWindow* fParent;
BMessenger fMessenger;
infoview_t fInfo;
bool fCollapsed;
BRect fCloseRect;
BRect fCollapseRect;
float fHeaderSize;
bool fCloseClicked;
bool fPreviewModeOn;
};
#endif // _APP_GROUP_VIEW_H

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
@ -9,6 +9,7 @@
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Brian Hill, supernova@tycho.email
*/
#include <Message.h>
@ -21,28 +22,22 @@ const type_code kTypeCode = 'ipau';
AppUsage::AppUsage()
:
fName(""),
fAppName(""),
fSignature(""),
fAllow(true)
{
}
AppUsage::AppUsage(const char* name, bool allow)
AppUsage::AppUsage(const char* name, const char* signature, bool allow)
:
fName(name),
fAppName(name),
fSignature(signature),
fAllow(allow)
{
}
AppUsage::~AppUsage()
{
notification_t::iterator nIt;
for (nIt = fNotifications.begin(); nIt != fNotifications.end(); nIt++)
delete nIt->second;
}
bool
AppUsage::AllowsTypeCode(type_code code) const
{
@ -54,13 +49,10 @@ status_t
AppUsage::Flatten(void* buffer, ssize_t numBytes) const
{
BMessage msg;
msg.AddString("signature", fName);
msg.AddString("name", fAppName);
msg.AddString("signature", fSignature);
msg.AddBool("allow", fAllow);
notification_t::const_iterator nIt;
for (nIt = fNotifications.begin(); nIt != fNotifications.end(); nIt++)
msg.AddFlat("notification", nIt->second);
if (numBytes < msg.FlattenedSize())
return B_ERROR;
@ -72,13 +64,10 @@ ssize_t
AppUsage::FlattenedSize() const
{
BMessage msg;
msg.AddString("signature", fName);
msg.AddString("name", fAppName);
msg.AddString("signature", fSignature);
msg.AddBool("allow", fAllow);
notification_t::const_iterator nIt;
for (nIt = fNotifications.begin(); nIt != fNotifications.end(); nIt++)
msg.AddFlat("notification", nIt->second);
return msg.FlattenedSize();
}
@ -110,23 +99,9 @@ AppUsage::Unflatten(type_code code, const void* buffer,
status = msg.Unflatten((const char*)buffer);
if (status == B_OK) {
msg.FindString("signature", &fName);
msg.FindString("name", &fAppName);
msg.FindString("signature", &fSignature);
msg.FindBool("allow", &fAllow);
type_code type;
int32 count = 0;
status = msg.GetInfo("notification", &type, &count);
if (status != B_OK)
return status;
for (int32 i = 0; i < count; i++) {
NotificationReceived *notification = new NotificationReceived();
msg.FindFlat("notification", i, notification);
fNotifications[notification->Title()] = notification;
}
status = B_OK;
}
return status;
@ -134,30 +109,16 @@ AppUsage::Unflatten(type_code code, const void* buffer,
const char*
AppUsage::Name()
AppUsage::AppName()
{
return fName.String();
return fAppName.String();
}
bool
AppUsage::Allowed(const char* title, notification_type type)
const char*
AppUsage::Signature()
{
bool allowed = fAllow;
if (allowed) {
notification_t::iterator nIt = fNotifications.find(title);
if (nIt == fNotifications.end()) {
allowed = true;
fNotifications[title] = new NotificationReceived(title, type);
} else {
allowed = nIt->second->Allowed();
nIt->second->UpdateTimeStamp();
nIt->second->SetType(type);
}
}
return allowed;
return fSignature.String();
}
@ -168,26 +129,8 @@ AppUsage::Allowed()
}
NotificationReceived*
AppUsage::NotificationAt(int32 index)
{
notification_t::iterator nIt = fNotifications.begin();
for (int32 i = 0; i < index; i++)
nIt++;
return nIt->second;
}
int32
AppUsage::Notifications()
{
return fNotifications.size();
}
void
AppUsage::AddNotification(NotificationReceived* notification)
AppUsage::SetAllowed(bool allow)
{
fNotifications[notification->Title()] = notification;
fAllow = allow;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010-2015, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
@ -11,9 +11,11 @@
#include <stdlib.h>
#include <Alert.h>
#include <Beep.h>
#include <Notifications.h>
#include <PropertyInfo.h>
#include <Roster.h>
#include "NotificationWindow.h"
@ -56,9 +58,8 @@ NotificationServer::MessageReceived(BMessage* message)
if (!fWindow)
return;
int32 type = 0;
// Emit a sound for this event
int32 type = 0;
if (message->FindInt32("type", &type) == B_OK) {
if (type < (int32)(sizeof(kSoundNames) / sizeof(const char*)))
system_beep(kSoundNames[type]);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010-2015, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef _NOTIFICATION_SERVER_H

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010-2011, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
@ -11,6 +11,7 @@
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Stephan Aßmus <superstippi@gmx.de>
* Adrien Destugues <pulkomandy@pulkomandy.ath.cx>
* Brian Hill, supernova@tycho.email
*/
@ -35,7 +36,10 @@
#include "NotificationWindow.h"
static const int kIconStripeWidth = 32;
const int kIconStripeWidth = 32;
const float kCloseSize = 6;
const float kEdgePadding = 2;
const float kSmallPadding = 2;
property_info message_prop_list[] = {
{ "type", {B_GET_PROPERTY, B_SET_PROPERTY, 0},
@ -54,23 +58,22 @@ property_info message_prop_list[] = {
};
NotificationView::NotificationView(NotificationWindow* win,
BNotification* notification, bigtime_t timeout)
NotificationView::NotificationView(BNotification* notification, bigtime_t timeout,
float iconSize, bool disableTimeout)
:
BView("NotificationView", B_WILL_DRAW),
fParent(win),
fNotification(notification),
fTimeout(timeout),
fIconSize(iconSize),
fDisableTimeout(disableTimeout),
fRunner(NULL),
fBitmap(NULL),
fCloseClicked(false)
fCloseClicked(false),
fPreviewModeOn(false)
{
if (fNotification->Icon() != NULL)
fBitmap = new BBitmap(fNotification->Icon());
if (fTimeout <= 0)
fTimeout = fParent->Timeout() * 1000000;
BGroupLayout* layout = new BGroupLayout(B_VERTICAL);
SetLayout(layout);
@ -122,11 +125,12 @@ void
NotificationView::AttachedToWindow()
{
SetText();
BMessage msg(kRemoveView);
msg.AddPointer("view", this);
fRunner = new BMessageRunner(BMessenger(Parent()), &msg, fTimeout, 1);
if (!fDisableTimeout) {
BMessage msg(kRemoveView);
msg.AddPointer("view", this);
fRunner = new BMessageRunner(BMessenger(Parent()), &msg, fTimeout, 1);
}
}
@ -238,9 +242,6 @@ NotificationView::Draw(BRect updateRect)
SetDrawingMode(B_OP_ALPHA);
SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
// Icon size
float iconSize = (float)fParent->IconSize();
BRect stripeRect = Bounds();
stripeRect.right = kIconStripeWidth;
SetHighColor(tint_color(ui_color(B_PANEL_BACKGROUND_COLOR),
@ -258,7 +259,7 @@ NotificationView::Draw(BRect updateRect)
// Draw icon
if (fBitmap) {
float ix = 18;
float iy = (Bounds().Height() - iconSize) / 4.0;
float iy = (Bounds().Height() - fIconSize) / 4.0;
// Icon is vertically centered in view
if (fNotification->Type() == B_PROGRESS_NOTIFICATION)
@ -267,7 +268,7 @@ NotificationView::Draw(BRect updateRect)
iy -= (progRect.Height() + kEdgePadding);
}
iconRect.Set(ix, iy, ix + iconSize - 1.0, iy + iconSize - 1.0);
iconRect.Set(ix, iy, ix + fIconSize - 1.0, iy + fIconSize - 1.0);
DrawBitmapAsync(fBitmap, fBitmap->Bounds(), iconRect);
}
@ -279,9 +280,10 @@ NotificationView::Draw(BRect updateRect)
SetFont(&l->font);
// Truncate the string. We have already line-wrapped the text but if
// there is a very long 'word' we can only truncate it.
TruncateString(&(l->text), B_TRUNCATE_END,
BString text(l->text);
TruncateString(&text, B_TRUNCATE_END,
Bounds().Width() - l->location.x);
DrawString(l->text.String(), l->text.Length(), l->location);
DrawString(text.String(), text.Length(), l->location);
}
AppGroupView* groupView = dynamic_cast<AppGroupView*>(Parent());
@ -333,6 +335,10 @@ NotificationView::_DrawCloseButton(const BRect& updateRect)
void
NotificationView::MouseDown(BPoint point)
{
// Preview Mode ignores any mouse clicks
if (fPreviewModeOn)
return;
int32 buttons;
Window()->CurrentMessage()->FindInt32("buttons", &buttons);
@ -447,20 +453,22 @@ NotificationView::SetText(float newMaxWidth)
float iconRight = kIconStripeWidth;
if (fBitmap != NULL)
iconRight += fParent->IconSize();
iconRight += fIconSize;
else
iconRight += 32;
BFont boldFont(be_plain_font);
boldFont.SetFace(B_BOLD_FACE);
font_height fh;
be_bold_font->GetHeight(&fh);
boldFont.GetHeight(&fh);
float fontHeight = ceilf(fh.leading) + ceilf(fh.descent)
+ ceilf(fh.ascent);
float y = 2 * fontHeight;
float y = fontHeight + kEdgePadding * 2;
// Title
LineInfo* titleLine = new LineInfo;
titleLine->text = fNotification->Title();
titleLine->font = *be_bold_font;
titleLine->font = boldFont;
titleLine->location = BPoint(iconRight + kEdgePadding, y);
@ -548,6 +556,13 @@ NotificationView::SetText(float newMaxWidth)
}
void
NotificationView::SetPreviewModeOn(bool enabled)
{
fPreviewModeOn = enabled;
}
const char*
NotificationView::MessageID() const
{

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
@ -18,16 +18,16 @@ class BBitmap;
class BMessageRunner;
class BNotification;
class NotificationWindow;
const uint32 kRemoveView = 'ReVi';
const float kExpandSize = 8;
const float kPenSize = 1;
class NotificationView : public BView {
public:
NotificationView(NotificationWindow* win,
BNotification* notification,
bigtime_t timeout = -1);
NotificationView(BNotification* notification,
bigtime_t timeout, float iconSize,
bool disableTimeout = false);
virtual ~NotificationView();
virtual void AttachedToWindow();
@ -47,6 +47,7 @@ public:
virtual status_t GetSupportedSuites(BMessage* msg);
void SetText(float newMaxWidth = -1);
void SetPreviewModeOn(bool enabled);
const char* MessageID() const;
@ -62,9 +63,10 @@ private:
typedef std::list<LineInfo*> LineInfoList;
NotificationWindow* fParent;
BNotification* fNotification;
bigtime_t fTimeout;
float fIconSize;
bool fDisableTimeout;
BMessageRunner* fRunner;
@ -73,6 +75,7 @@ private:
float fHeight;
rgb_color fStripeColor;
bool fCloseClicked;
bool fPreviewModeOn;
};
#endif // _NOTIFICATION_VIEW_H

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
@ -9,6 +9,7 @@
* Michael Davidson, slaad@bong.com.au
* Mikael Eiman, mikael@eiman.tv
* Pier Luigi Fiorini, pierluigi.fiorini@gmail.com
* Brian Hill, supernova@tycho.email
*/
#include "NotificationWindow.h"
@ -48,20 +49,22 @@ property_info main_prop_list[] = {
};
const float kCloseSize = 6;
const float kExpandSize = 8;
const float kPenSize = 1;
const float kEdgePadding = 2;
const float kSmallPadding = 2;
NotificationWindow::NotificationWindow()
:
BWindow(BRect(0, 0, -1, -1), B_TRANSLATE_MARK("Notification"),
B_BORDERED_WINDOW_LOOK, B_FLOATING_ALL_WINDOW_FEEL, B_AVOID_FRONT
| B_AVOID_FOCUS | B_NOT_CLOSABLE | B_NOT_ZOOMABLE | B_NOT_MINIMIZABLE
| B_NOT_RESIZABLE | B_NOT_MOVABLE | B_AUTO_UPDATE_SIZE_LIMITS,
B_ALL_WORKSPACES)
B_ALL_WORKSPACES),
fShouldRun(true)
{
status_t result = find_directory(B_USER_CACHE_DIRECTORY, &fCachePath);
fCachePath.Append("Notifications");
BDirectory cacheDir;
result = cacheDir.SetTo(fCachePath.Path());
if(result == B_ENTRY_NOT_FOUND)
cacheDir.CreateDirectory(fCachePath.Path(), NULL);
SetLayout(new BGroupLayout(B_VERTICAL, 0));
_LoadSettings(true);
@ -126,62 +129,38 @@ NotificationWindow::MessageReceived(BMessage* message)
_LoadSettings();
break;
}
case B_COUNT_PROPERTIES:
{
BMessage reply(B_REPLY);
BMessage specifier;
const char* property = NULL;
bool messageOkay = true;
if (message->FindMessage("specifiers", 0, &specifier) != B_OK)
messageOkay = false;
if (specifier.FindString("property", &property) != B_OK)
messageOkay = false;
if (strcmp(property, "message") != 0)
messageOkay = false;
if (messageOkay)
reply.AddInt32("result", fViews.size());
else {
reply.what = B_MESSAGE_NOT_UNDERSTOOD;
reply.AddInt32("error", B_ERROR);
}
message->SendReply(&reply);
break;
}
case B_CREATE_PROPERTY:
case kNotificationMessage:
{
if (!fShouldRun)
break;
BMessage reply(B_REPLY);
BNotification* notification = new BNotification(message);
if (notification->InitCheck() == B_OK) {
bigtime_t timeout;
if (message->FindInt64("timeout", &timeout) != B_OK)
timeout = -1;
BMessenger messenger = message->ReturnAddress();
app_info info;
if (messenger.IsValid())
be_roster->GetRunningAppInfo(messenger.Team(), &info);
else
be_roster->GetAppInfo("application/x-vnd.Be-SHEL", &info);
timeout = fTimeout;
BString sourceSignature(notification->SourceSignature());
BString sourceName(notification->SourceName());
bool allow = false;
appfilter_t::iterator it = fAppFilters.find(info.signature);
appfilter_t::iterator it =
fAppFilters.find(sourceSignature.String());
AppUsage* appUsage = NULL;
if (it == fAppFilters.end()) {
AppUsage* appUsage = new AppUsage(notification->Group(),
true);
appUsage->Allowed(notification->Title(),
notification->Type());
fAppFilters[info.signature] = appUsage;
if (sourceSignature.Length() > 0
&& sourceName.Length() > 0) {
appUsage = new AppUsage(sourceName.String(),
sourceSignature.String(), true);
fAppFilters[sourceSignature.String()] = appUsage;
// TODO save back to settings file
}
allow = true;
} else {
allow = it->second->Allowed(notification->Title(),
notification->Type());
appUsage = it->second;
allow = appUsage->Allowed();
}
if (allow) {
@ -196,8 +175,8 @@ NotificationWindow::MessageReceived(BMessage* message)
} else
group = aIt->second;
NotificationView* view = new NotificationView(this,
notification, timeout);
NotificationView* view = new NotificationView(notification,
timeout, fIconSize);
group->AddInfo(view);
@ -214,18 +193,6 @@ NotificationWindow::MessageReceived(BMessage* message)
message->SendReply(&reply);
break;
}
case kRemoveView:
{
NotificationView* view = NULL;
if (message->FindPointer("view", (void**)&view) != B_OK)
return;
views_t::iterator it = find(fViews.begin(), fViews.end(), view);
if (it != fViews.end())
fViews.erase(it);
break;
}
case kRemoveGroupView:
{
AppGroupView* view = NULL;
@ -254,52 +221,6 @@ NotificationWindow::MessageReceived(BMessage* message)
}
BHandler*
NotificationWindow::ResolveSpecifier(BMessage* msg, int32 index,
BMessage* spec, int32 form, const char* prop)
{
BPropertyInfo prop_info(main_prop_list);
BHandler* handler = NULL;
if (strcmp(prop,"message") == 0) {
switch (msg->what) {
case B_CREATE_PROPERTY:
{
msg->PopSpecifier();
handler = this;
break;
}
case B_SET_PROPERTY:
case B_GET_PROPERTY:
{
int32 i;
if (spec->FindInt32("index", &i) != B_OK)
i = -1;
if (i >= 0 && i < (int32)fViews.size()) {
msg->PopSpecifier();
handler = fViews[i];
} else
handler = NULL;
break;
}
case B_COUNT_PROPERTIES:
msg->PopSpecifier();
handler = this;
break;
default:
break;
}
}
if (!handler)
handler = BWindow::ResolveSpecifier(msg, index, spec, form, prop);
return handler;
}
icon_size
NotificationWindow::IconSize()
{
@ -336,17 +257,6 @@ NotificationWindow::_ShowHide()
}
void
NotificationWindow::NotificationViewSwapped(NotificationView* stale,
NotificationView* fresh)
{
views_t::iterator it = find(fViews.begin(), fViews.end(), stale);
if (it != fViews.end())
*it = fresh;
}
void
NotificationWindow::SetPosition()
{
@ -362,7 +272,8 @@ NotificationWindow::SetPosition()
float bottomOffset = bounds.bottom - Frame().bottom;
// Size of the borders around the window
float x = Frame().left, y = Frame().top;
float x = Frame().left;
float y = Frame().top;
// If we can't guess, don't move...
BDeskbar deskbar;
@ -414,7 +325,7 @@ NotificationWindow::_LoadSettings(bool startMonitor)
path.Append(kSettingsFile);
BFile file(path.Path(), B_READ_ONLY);
BFile file(path.Path(), B_READ_ONLY | B_CREATE_FILE);
settings.Unflatten(&file);
_LoadGeneralSettings(settings);
@ -428,10 +339,10 @@ NotificationWindow::_LoadSettings(bool startMonitor)
if (watch_node(&nref, B_WATCH_ALL, BMessenger(this)) != B_OK) {
BAlert* alert = new BAlert(B_TRANSLATE("Warning"),
B_TRANSLATE("Couldn't start general settings monitor.\n"
"Live filter changes disabled."), B_TRANSLATE("OK"));
B_TRANSLATE("Couldn't start general settings monitor.\n"
"Live filter changes disabled."), B_TRANSLATE("OK"));
alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
alert->Go();
alert->Go(NULL);
}
}
}
@ -448,8 +359,10 @@ NotificationWindow::_LoadAppFilters(BMessage& settings)
for (int32 i = 0; i < count; i++) {
AppUsage* app = new AppUsage();
settings.FindFlat("app_usage", i, app);
fAppFilters[app->Name()] = app;
if (settings.FindFlat("app_usage", i, app) == B_OK)
fAppFilters[app->Signature()] = app;
else
delete app;
}
}
@ -457,22 +370,18 @@ NotificationWindow::_LoadAppFilters(BMessage& settings)
void
NotificationWindow::_LoadGeneralSettings(BMessage& settings)
{
bool shouldRun;
if (settings.FindBool(kAutoStartName, &shouldRun) == B_OK) {
if (shouldRun == false) {
if (settings.FindBool(kAutoStartName, &fShouldRun) == B_OK) {
if (fShouldRun == false) {
// We should not start. Quit the app!
be_app_messenger.SendMessage(B_QUIT_REQUESTED);
}
}
} else
fShouldRun = true;
if (settings.FindInt32(kTimeoutName, &fTimeout) != B_OK)
fTimeout = kDefaultTimeout;
// Notify the view about the change
views_t::iterator it;
for (it = fViews.begin(); it != fViews.end(); ++it) {
NotificationView* view = (*it);
view->Invalidate();
}
fTimeout *= 1000000;
// Convert from seconds to microseconds
}
@ -480,21 +389,22 @@ void
NotificationWindow::_LoadDisplaySettings(BMessage& settings)
{
int32 setting;
float originalWidth = fWidth;
if (settings.FindFloat(kWidthName, &fWidth) != B_OK)
fWidth = kDefaultWidth;
GetLayout()->SetExplicitMaxSize(BSize(fWidth, B_SIZE_UNSET));
GetLayout()->SetExplicitMinSize(BSize(fWidth, B_SIZE_UNSET));
if (originalWidth != fWidth)
GetLayout()->SetExplicitSize(BSize(fWidth, B_SIZE_UNSET));
if (settings.FindInt32(kIconSizeName, &setting) != B_OK)
fIconSize = kDefaultIconSize;
else
fIconSize = (icon_size)setting;
// Notify the view about the change
views_t::iterator it;
for (it = fViews.begin(); it != fViews.end(); ++it) {
NotificationView* view = (*it);
// Notify the views about the change
appview_t::iterator aIt;
for (aIt = fAppViews.begin(); aIt != fAppViews.end(); ++aIt) {
AppGroupView* view = aIt->second;
view->Invalidate();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010, Haiku, Inc. All Rights Reserved.
* Copyright 2010-2017, Haiku, Inc. All Rights Reserved.
* Copyright 2008-2009, Pier Luigi Fiorini. All Rights Reserved.
* Copyright 2004-2008, Michael Davidson. All Rights Reserved.
* Copyright 2004-2007, Mikael Eiman. All Rights Reserved.
@ -13,6 +13,7 @@
#include <map>
#include <AppFileInfo.h>
#include <Path.h>
#include <String.h>
#include <Window.h>
@ -26,13 +27,6 @@ struct property_info;
typedef std::map<BString, AppGroupView*> appview_t;
typedef std::map<BString, AppUsage*> appfilter_t;
typedef std::vector<NotificationView*> views_t;
extern const float kEdgePadding;
extern const float kSmallPadding;
extern const float kCloseSize;
extern const float kExpandSize;
extern const float kPenSize;
const uint32 kRemoveGroupView = 'RGVi';
@ -47,8 +41,6 @@ public:
virtual void WorkspaceActivated(int32, bool);
virtual void FrameResized(float width, float height);
virtual void ScreenChanged(BRect frame, color_space mode);
virtual BHandler* ResolveSpecifier(BMessage*, int32, BMessage*,
int32, const char*);
icon_size IconSize();
int32 Timeout();
@ -59,27 +51,20 @@ public:
private:
friend class AppGroupView;
void NotificationViewSwapped(
NotificationView* stale,
NotificationView* fresh);
void SetPosition();
void _LoadSettings(bool startMonitor = false);
void _LoadAppFilters(BMessage& settings);
void _LoadGeneralSettings(BMessage& settings);
void _LoadDisplaySettings(BMessage& settings);
views_t fViews;
appview_t fAppViews;
BString fStatusText;
BString fMessageText;
appfilter_t fAppFilters;
float fWidth;
icon_size fIconSize;
int32 fTimeout;
appfilter_t fAppFilters;
bool fShouldRun;
BPath fCachePath;
};
extern property_info main_prop_list[];