root/src/kits/app/Notification.cpp
/*
 * 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
 */


#include <Notification.h>

#include <new>

#include <stdlib.h>
#include <string.h>

#include <notification/Notifications.h>

#include <Bitmap.h>
#include <Message.h>
#include <NodeInfo.h>
#include <Path.h>
#include <Roster.h>


BNotification::BNotification(notification_type type)
        :
        BArchivable(),
        fInitStatus(B_OK),
        fType(type),
        fProgress(0.f),
        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) {
                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();
}


BNotification::BNotification(BMessage* archive)
        :
        BArchivable(archive),
        fInitStatus(B_OK),
        fProgress(0.0f),
        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;
        else
                fInitStatus = B_ERROR;

        BString group;
        if (archive->FindString("_group", &group) == B_OK)
                SetGroup(group);

        BString title;
        if (archive->FindString("_title", &title) == B_OK)
                SetTitle(title);

        BString content;
        if (archive->FindString("_content", &content) == B_OK)
                SetContent(content);

        BString messageID;
        if (archive->FindString("_messageID", &messageID) == B_OK)
                SetMessageID(messageID);

        float progress;
        if (type == B_PROGRESS_NOTIFICATION
                && archive->FindFloat("_progress", &progress) == B_OK)
                SetProgress(progress);

        BString onClickApp;
        if (archive->FindString("_onClickApp", &onClickApp) == B_OK)
                SetOnClickApp(onClickApp);

        entry_ref onClickFile;
        if (archive->FindRef("_onClickFile", &onClickFile) == B_OK)
                SetOnClickFile(&onClickFile);

        entry_ref onClickRef;
        int32 index = 0;
        while (archive->FindRef("_onClickRef", index++, &onClickRef) == B_OK)
                AddOnClickRef(&onClickRef);

        BString onClickArgv;
        index = 0;
        while (archive->FindString("_onClickArgv", index++, &onClickArgv) == B_OK)
                AddOnClickArg(onClickArgv);

        status_t ret = B_OK;
        BMessage icon;
        if ((ret = archive->FindMessage("_icon", &icon)) == B_OK) {
                BBitmap bitmap(&icon);
                ret = bitmap.InitCheck();
                if (ret == B_OK)
                        ret = SetIcon(&bitmap);
        }
}


BNotification::~BNotification()
{
        delete fFile;
        delete fBitmap;

        for (int32 i = fRefs.CountItems() - 1; i >= 0; i--)
                delete (entry_ref*)fRefs.ItemAtFast(i);

        for (int32 i = fArgv.CountItems() - 1; i >= 0; i--)
                free(fArgv.ItemAtFast(i));
}


status_t
BNotification::InitCheck() const
{
        return fInitStatus;
}


BArchivable*
BNotification::Instantiate(BMessage* archive)
{
        if (validate_instantiation(archive, "BNotification"))
                return new(std::nothrow) BNotification(archive);

        return NULL;
}


status_t
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);

        if (status == B_OK && Group() != NULL)
                status = archive->AddString("_group", Group());

        if (status == B_OK && Title() != NULL)
                status = archive->AddString("_title", Title());

        if (status == B_OK && Content() != NULL)
                status = archive->AddString("_content", Content());

        if (status == B_OK && MessageID() != NULL)
                status = archive->AddString("_messageID", MessageID());

        if (status == B_OK && Type() == B_PROGRESS_NOTIFICATION)
                status = archive->AddFloat("_progress", Progress());

        if (status == B_OK && OnClickApp() != NULL)
                status = archive->AddString("_onClickApp", OnClickApp());

        if (status == B_OK && OnClickFile() != NULL)
                status = archive->AddRef("_onClickFile", OnClickFile());

        if (status == B_OK) {
                for (int32 i = 0; i < CountOnClickRefs(); i++) {
                        status = archive->AddRef("_onClickRef", OnClickRefAt(i));
                        if (status != B_OK)
                                break;
                }
        }

        if (status == B_OK) {
                for (int32 i = 0; i < CountOnClickArgs(); i++) {
                        status = archive->AddString("_onClickArgv", OnClickArgAt(i));
                        if (status != B_OK)
                                break;
                }
        }

        if (status == B_OK) {
                const BBitmap* icon = Icon();
                if (icon != NULL) {
                        BMessage iconArchive;
                        status = icon->Archive(&iconArchive);
                        if (status == B_OK)
                                archive->AddMessage("_icon", &iconArchive);
                }
        }

        return status;
}


const char*
BNotification::SourceSignature() const
{
        return fSourceSignature;
}


const char*
BNotification::SourceName() const
{
        return fSourceName;
}


notification_type
BNotification::Type() const
{
        return fType;
}


const char*
BNotification::Group() const
{
        if (fGroup == "")
                return NULL;
        return fGroup;
}


void
BNotification::SetGroup(const BString& group)
{
        fGroup = group;
}


const char*
BNotification::Title() const
{
        if (fTitle == "")
                return NULL;
        return fTitle;
}


void
BNotification::SetTitle(const BString& title)
{
        fTitle = title;
}


const char*
BNotification::Content() const
{
        if (fContent == "")
                return NULL;
        return fContent;
}


void
BNotification::SetContent(const BString& content)
{
        fContent = content;
}


const char*
BNotification::MessageID() const
{
        if (fID == "")
                return NULL;
        return fID;
}


void
BNotification::SetMessageID(const BString& id)
{
        fID = id;
}


float
BNotification::Progress() const
{
        if (fType != B_PROGRESS_NOTIFICATION)
                return -1;
        return fProgress;
}


void
BNotification::SetProgress(float progress)
{
        if (progress < 0)
                fProgress = 0;
        else if (progress > 1)
                fProgress = 1;
        else
                fProgress = progress;
}


const char*
BNotification::OnClickApp() const
{
        if (fApp == "")
                return NULL;
        return fApp;
}


void
BNotification::SetOnClickApp(const BString& app)
{
        fApp = app;
}


const entry_ref*
BNotification::OnClickFile() const
{
        return fFile;
}


status_t
BNotification::SetOnClickFile(const entry_ref* file)
{
        delete fFile;

        if (file != NULL) {
                fFile = new(std::nothrow) entry_ref(*file);
                if (fFile == NULL)
                        return B_NO_MEMORY;
        } else
                fFile = NULL;

        return B_OK;
}


status_t
BNotification::AddOnClickRef(const entry_ref* ref)
{
        if (ref == NULL)
                return B_BAD_VALUE;

        entry_ref* clonedRef = new(std::nothrow) entry_ref(*ref);
        if (clonedRef == NULL || !fRefs.AddItem(clonedRef))
                return B_NO_MEMORY;

        return B_OK;
}


int32
BNotification::CountOnClickRefs() const
{
        return fRefs.CountItems();
}


const entry_ref*
BNotification::OnClickRefAt(int32 index) const
{
        return (entry_ref*)fRefs.ItemAt(index);
}


status_t
BNotification::AddOnClickArg(const BString& arg)
{
        char* clonedArg = strdup(arg.String());
        if (clonedArg == NULL || !fArgv.AddItem(clonedArg))
                return B_NO_MEMORY;

        return B_OK;
}


int32
BNotification::CountOnClickArgs() const
{
        return fArgv.CountItems();
}


const char*
BNotification::OnClickArgAt(int32 index) const
{
        return (char*)fArgv.ItemAt(index);
}


const BBitmap*
BNotification::Icon() const
{
        return fBitmap;
}


status_t
BNotification::SetIcon(const BBitmap* icon)
{
        delete fBitmap;

        if (icon != NULL) {
                fBitmap = new(std::nothrow) BBitmap(icon);
                if (fBitmap == NULL)
                        return B_NO_MEMORY;
                return fBitmap->InitCheck();
        }

        fBitmap = NULL;
        return B_OK;
}


status_t
BNotification::Send(bigtime_t timeout)
{
        BMessage msg(kNotificationMessage);

        // Archive notification
        status_t ret = Archive(&msg);

        // Custom time out
        if (ret == B_OK && timeout > 0)
                ret = msg.AddInt64("timeout", timeout);

        // Send message
        if (ret == B_OK) {
                BMessenger server(kNotificationServerSignature);
                ret = server.SendMessage(&msg);
        }

        return ret;
}


void BNotification::_ReservedNotification1() {}
void BNotification::_ReservedNotification2() {}
void BNotification::_ReservedNotification3() {}
void BNotification::_ReservedNotification4() {}
void BNotification::_ReservedNotification5() {}
void BNotification::_ReservedNotification6() {}
void BNotification::_ReservedNotification7() {}
void BNotification::_ReservedNotification8() {}