/*
 * MessageStack - manages stack of active status messages
 *
 * Authors:
 *   MenTaLguY <mental@rydia.net>
 *
 * Copyright (C) 2004 MenTaLguY
 *
 * Released under GNU GPL, read the file 'COPYING' for more information
 */

#include <string.h>
#include <glib/gstrfuncs.h>
#include "message-stack.h"

namespace Inkscape {

MessageStack::MessageStack()
: _messages(NULL), _next_id(1)
{
}

MessageStack::~MessageStack()
{
    while (_messages) {
        _messages = _discard(_messages);
    }
}

MessageId MessageStack::push(MessageType type, gchar const *message) {
    return _push(type, 0, message);
}

MessageId MessageStack::pushF(MessageType type, gchar const *format, ...)
{
    va_list args;
    va_start(args, format);
    MessageId id=pushVF(type, format, args);
    va_end(args);
    return id;
}

MessageId MessageStack::pushVF(MessageType type, gchar const *format, va_list args)
{
    MessageId id;
    gchar *message=g_strdup_vprintf(format, args);
    id = push(type, message);
    g_free(message);
    return id;
}

void MessageStack::cancel(MessageId id) {
    Message **ref;
    for ( ref = &_messages ; *ref ; ref = &(*ref)->next ) {
        if ( (*ref)->id == id ) {
            *ref = _discard(*ref);
            _emitChanged();
            break;
        }
    }
}

MessageId MessageStack::flash(MessageType type, gchar const *message) {
    switch (type) {
    case INFORMATION_MESSAGE: // stay rather long so as to seem permanent, but eventually disappear
        return _push(type, 6000 + 80*strlen(message), message);
        break;
    case ERROR_MESSAGE: // pretty important stuff, but temporary
        return _push(type, 4000 + 60*strlen(message), message);
        break;
    case WARNING_MESSAGE: // a bit less important than error
        return _push(type, 2000 + 40*strlen(message), message);
        break;
    case IMMEDIATE_MESSAGE: // same length as normal, higher priority
        return _push(type, 1000 + 20*strlen(message), message);
        break;
    case NORMAL_MESSAGE: // something ephemeral
    default:
        return _push(type, 1000 + 20*strlen(message), message);
        break;
    }
}

MessageId MessageStack::flashF(MessageType type, gchar const *format, ...) {
    va_list args;
    va_start(args, format);
    MessageId id = flashVF(type, format, args);
    va_end(args);
    return id;
}

MessageId MessageStack::flashVF(MessageType type, gchar const *format, va_list args)
{
    gchar *message=g_strdup_vprintf(format, args);
    MessageId id = flash(type, message);
    g_free(message);
    return id;
}

MessageId MessageStack::_push(MessageType type, guint lifetime, gchar const *message)
{
    Message *m=new Message;
    MessageId id=_next_id++;

    m->stack = this;
    m->id = id;
    m->type = type;
    m->message = g_strdup(message);

    if (lifetime) {
        m->timeout_id = g_timeout_add(lifetime, &MessageStack::_timeout, m);
    } else {
        m->timeout_id = 0;
    }

    m->next = _messages;
    _messages = m;

    _emitChanged();

    return id;
}

MessageStack::Message *MessageStack::_discard(MessageStack::Message *m)
{
    Message *next=m->next;
    if (m->timeout_id) {
        g_source_remove(m->timeout_id);
        m->timeout_id = 0;
    }
    g_free(m->message);
    m->message = NULL;
    m->stack = NULL;
    delete m;
    return next;
}

void MessageStack::_emitChanged() {
    if (_messages) {
        _changed_signal.emit(_messages->type, _messages->message);
    } else {
        _changed_signal.emit(NORMAL_MESSAGE, NULL);
    }
}

gboolean MessageStack::_timeout(gpointer data) {
    Message *m=reinterpret_cast<Message *>(data);
    m->timeout_id = 0;
    m->stack->cancel(m->id);
    return FALSE;
}

}

/*
  Local Variables:
  mode:c++
  c-file-style:"stroustrup"
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
  indent-tabs-mode:nil
  fill-column:99
  End:
*/
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
