summaryrefslogtreecommitdiffstats
path: root/src/message-stack.h
blob: f86ae7b94b3bc604288d2f660a7dad4643465a0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// SPDX-License-Identifier: GPL-2.0-or-later
/** \file
 * Raw stack of active status messages
 */

/*
 * Authors:
 *   MenTaLguY <mental@rydia.net>
 *   Jon A. Cruz <jon@joncruz.org>
 *
 * Copyright (C) 2004 MenTaLguY
 * Copyright (C) 2011 Jon A. Cruz
 *
 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
 */

#ifndef SEEN_INKSCAPE_MESSAGE_STACK_H
#define SEEN_INKSCAPE_MESSAGE_STACK_H

#include <cstdarg>
#include <cstddef>
#include <glib.h> // G_GNUC_PRINTF is the only thing worth having from here
#include <glibmm/ustring.h>
#include <sigc++/sigc++.h>

#include "message.h"

namespace Inkscape {

/**
 * A class which holds a stack of displayed messages.  
 *
 * Messages can be pushed onto the top of the stack, and removed 
 * from any point in the stack by their id.
 *
 * Messages may also be "flashed", meaning that they will be
 * automatically removed from the stack a fixed period of time
 * after they are pushed.
 *
 * "Flashed" warnings and errors will persist longer than normal
 * messages.
 *
 * There is no simple "pop" operation provided, since these
 * stacks are intended to be shared by many different clients;
 * assuming that the message you pushed is still on top is an
 * invalid and unsafe assumption.
 */
class MessageStack final
{
public:
    MessageStack();
    ~MessageStack();

    MessageStack(MessageStack const &) = delete; // no copy
    void operator=(MessageStack const &) = delete; // no assign

    /** @brief returns the type of message currently at the top of the stack */
    MessageType currentMessageType() {
        return _messages ? _messages->type : NORMAL_MESSAGE;
    }
    /** @brief returns the text of the message currently at the top of
      *        the stack
      */
    char const *currentMessage() {
        return _messages ? _messages->message : nullptr;
    }

    /** @brief connects to the "changed" signal which is emitted whenever
      *        the topmost message on the stack changes.
      */
    sigc::connection connectChanged(sigc::slot<void, MessageType, char const *> slot)
    {
        return _changed_signal.connect(slot);
    }

    /** @brief pushes a message onto the stack
      *
      * @param type the message type
      * @param message the message text
      *
      * @return the id of the pushed message
      */
    MessageId push(MessageType type, char const *message);

    /** @brief pushes a message onto the stack using printf-like formatting
      *
      * @param type the message type
      * @param format a printf-style format string
      *
      * @return the id of the pushed message
      */
    MessageId pushF(MessageType type, char const *format, ...) G_GNUC_PRINTF(3,4);

    /** @brief pushes a message onto the stack using printf-like formatting,
      *        using a stdarg argument list
      *
      * @param type the message type
      * @param format a printf-style format string
      * @param args the subsequent printf-style arguments
      *
      * @return the id of the pushed message
      */
    MessageId pushVF(MessageType type, char const *format, va_list args);

    /** @brief removes a message from the stack, given its id
      *
      * This method will remove a message from the stack if it has not
      * already been removed.  It may be removed from any part of the stack.
      * 
      * @param id the message id to remove
      */
    void cancel(MessageId id);

    /**
     * Temporarily pushes a message onto the stack.
     *
     * @param type the message type
     * @param message the message text
     *
     * @return the id of the pushed message
     */
    MessageId flash(MessageType type, char const *message);

    /**
     * Temporarily pushes a message onto the stack.
     *
     * @param type the message type
     * @param message the message text
     *
     * @return the id of the pushed message
     */
    MessageId flash(MessageType type, Glib::ustring const &message);


    /** @brief temporarily pushes a message onto the stack using
      *        printf-like formatting
      *
      * @param type the message type
      * @param format a printf-style format string
      *
      * @return the id of the pushed message
      */
    MessageId flashF(MessageType type, char const *format, ...) G_GNUC_PRINTF(3,4);

    /** @brief temporarily pushes a message onto the stack using
      *        printf-like formatting, using a stdarg argument list
      *
      * @param type the message type
      * @param format a printf-style format string
      * @param args the printf-style arguments
      *
      * @return the id of the pushed message
      */
    MessageId flashVF(MessageType type, char const *format, va_list args);

private:
    struct Message {
        Message *next;
        MessageStack *stack;
        MessageId id;
        MessageType type;
        gchar *message;
        guint timeout_id;
    };

    /// pushes a message onto the stack with an optional timeout
    MessageId _push(MessageType type, unsigned int lifetime, char const *message);

    Message *_discard(Message *m); ///< frees a message struct and returns the next such struct in the list
    void _emitChanged(); ///< emits the "changed" signal
    static int _timeout(void* data); ///< callback to expire flashed messages

    sigc::signal<void, MessageType, char const *> _changed_signal;
    Message *_messages; ///< the stack of messages as a linked list
    MessageId _next_id; ///< the next message id to assign
};

}

#endif
/*
  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:fileencoding=utf-8:textwidth=99 :