~ubuntu-branches/debian/squeeze/tasks/squeeze

« back to all changes in this revision

Viewing changes to libkoto/koto-undo-manager.c

  • Committer: Bazaar Package Importer
  • Author(s): Ross Burton, Loic Minier
  • Date: 2008-02-20 17:24:42 UTC
  • mfrom: (1.1.8 upstream)
  • Revision ID: james.westby@ubuntu.com-20080220172442-rso0p0yngsf414dy
Tags: 0.13-1
[ Loic Minier ]

* New upstream release.
* Bump up Standards-Version to 3.7.3.
* Rework to build a Hildon flavour of the package.
  - New binary package tasks-hildon; add conflicts with tasks and update
    descriptions; add install file; this package must be the first in the
    control file as to permit the dbg symbols for the standard tasks package
    to be in tasks-dbg instead of the tasks-hildon ones.
  - Add build hooks for configure, build, and install to add flavour /
    package specific rules and configure flags.
  - Build-dep on libhildon-1-dev, libdbus-1-dev, libhildonmime-dev for the
    Hildon flavour.
* Add explicit Copyright statement to debian/copyright.
* Use ${binary:Version} instead of ${Source-Version}; build-dep on dpkg-dev
  >= 1.13.19.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 *  Authors: Iain Holmes <iain@gnome.org>
 
4
 *
 
5
 *  Copyright 2002 - 2006 Iain Holmes
 
6
 *
 
7
 *  This file is free software; you can redistribute it and/or
 
8
 *  modify it under the terms of version 2 of the GNU Library General Public
 
9
 *  License as published by the Free Software Foundation;
 
10
 *
 
11
 *  This library is distributed in the hope that it will be useful,
 
12
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 *  Library General Public License for more details.
 
15
 *
 
16
 *  You should have received a copy of the GNU Library General Public
 
17
 *  License along with this library; if not, write to the
 
18
 *  Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
19
 *  Boston, MA 02111-1307, USA.
 
20
 *
 
21
 */
 
22
 
 
23
#ifdef HAVE_CONFIG_H
 
24
#include <config.h>
 
25
#endif
 
26
 
 
27
#include <glib/gi18n.h>
 
28
 
 
29
#include "koto-undo-manager.h"
 
30
 
 
31
G_DEFINE_TYPE (KotoUndoManager, koto_undo_manager, G_TYPE_OBJECT);
 
32
 
 
33
#define GET_PRIVATE(o)  \
 
34
   (G_TYPE_INSTANCE_GET_PRIVATE ((o), KOTO_TYPE_UNDO_MANAGER, KotoUndoManagerPrivate))
 
35
 
 
36
enum {
 
37
        CHANGED,
 
38
        LAST_SIGNAL
 
39
};
 
40
 
 
41
static guint signals[LAST_SIGNAL];
 
42
 
 
43
struct _KotoUndoManagerPrivate {
 
44
        GList *contexts, *undo, *redo;
 
45
        
 
46
        KotoUndoContext *working;
 
47
};
 
48
 
 
49
struct _KotoUndoContext {
 
50
        char *name;
 
51
        int count;
 
52
 
 
53
        GList *undoables;
 
54
};
 
55
 
 
56
static void
 
57
context_free (KotoUndoContext *ctxt)
 
58
{
 
59
        GList *u;
 
60
 
 
61
        g_free (ctxt->name);
 
62
        for (u = ctxt->undoables; u; u = u->next) {
 
63
                KotoUndoable *undoable = u->data;
 
64
                
 
65
                koto_undoable_free (undoable);
 
66
        }
 
67
        
 
68
        g_list_free (ctxt->undoables);
 
69
 
 
70
        g_free (ctxt);
 
71
}
 
72
 
 
73
static KotoUndoContext *
 
74
context_new (const char *name)
 
75
{
 
76
        KotoUndoContext *ctxt;
 
77
 
 
78
        ctxt = g_new (KotoUndoContext, 1);
 
79
 
 
80
        ctxt->name = g_strdup (name);
 
81
        ctxt->count = 0;
 
82
        ctxt->undoables = NULL;
 
83
 
 
84
        return ctxt;
 
85
}
 
86
 
 
87
static void
 
88
context_undo (KotoUndoContext *ctxt)
 
89
{
 
90
        GList *p;
 
91
 
 
92
        for (p = ctxt->undoables; p; p = p->next) {
 
93
                KotoUndoable *u = p->data;
 
94
 
 
95
                if (u->undo) {
 
96
                        u->undo (u->closure);
 
97
                }
 
98
        }
 
99
}
 
100
 
 
101
static void
 
102
context_redo (KotoUndoContext *ctxt)
 
103
{
 
104
        GList *p;
 
105
 
 
106
        for (p = g_list_last (ctxt->undoables); p; p = p->prev) {
 
107
                KotoUndoable *u = p->data;
 
108
 
 
109
                if (u->redo) {
 
110
                        u->redo (u->closure);
 
111
                }
 
112
        }
 
113
}
 
114
 
 
115
static void
 
116
finalize (GObject *object)
 
117
{
 
118
        KotoUndoManager *manager;
 
119
        KotoUndoManagerPrivate *priv;
 
120
        GList *p;
 
121
 
 
122
        manager = KOTO_UNDO_MANAGER (object);
 
123
        priv = manager->priv;
 
124
 
 
125
        for (p = priv->contexts; p; p = p->next) {
 
126
                KotoUndoContext *ctxt = p->data;
 
127
                context_free (ctxt);
 
128
        }
 
129
        g_list_free (priv->contexts);
 
130
 
 
131
        G_OBJECT_CLASS (koto_undo_manager_parent_class)->finalize (object);
 
132
}
 
133
 
 
134
static void
 
135
koto_undo_manager_class_init (KotoUndoManagerClass *klass)
 
136
{
 
137
        GObjectClass *object_class;
 
138
 
 
139
        g_type_class_add_private (klass, sizeof (KotoUndoManagerPrivate));
 
140
 
 
141
        object_class = G_OBJECT_CLASS (klass);
 
142
 
 
143
        object_class->finalize = finalize;
 
144
 
 
145
        signals[CHANGED] = g_signal_new ("changed",
 
146
                                         G_TYPE_FROM_CLASS (klass),
 
147
                                         G_SIGNAL_RUN_FIRST |
 
148
                                         G_SIGNAL_NO_RECURSE,
 
149
                                         G_STRUCT_OFFSET (KotoUndoManagerClass, changed),
 
150
                                         NULL, NULL,
 
151
                                         g_cclosure_marshal_VOID__VOID,
 
152
                                         G_TYPE_NONE, 0);
 
153
 
 
154
}
 
155
 
 
156
static void
 
157
koto_undo_manager_init (KotoUndoManager *manager)
 
158
{
 
159
        manager->priv = GET_PRIVATE (manager);
 
160
}
 
161
 
 
162
KotoUndoManager *
 
163
koto_undo_manager_new (void)
 
164
{
 
165
        KotoUndoManager *manager;
 
166
 
 
167
        manager = g_object_new (KOTO_TYPE_UNDO_MANAGER, NULL);
 
168
        return manager;
 
169
}
 
170
 
 
171
gboolean
 
172
koto_undo_manager_can_undo (KotoUndoManager *manager)
 
173
{
 
174
        return (manager->priv->undo != NULL);
 
175
}
 
176
 
 
177
gboolean
 
178
koto_undo_manager_can_redo (KotoUndoManager *manager)
 
179
{
 
180
        return (manager->priv->redo != NULL);
 
181
}
 
182
 
 
183
/* FIXME: Should these return copies of the names 
 
184
   so that they're threadsafe? */
 
185
const char *
 
186
koto_undo_manager_get_undo_name (KotoUndoManager *manager)
 
187
{
 
188
        if (manager->priv->undo) {
 
189
                KotoUndoContext *ctxt = manager->priv->undo->data;
 
190
                return ctxt->name;
 
191
        } else {
 
192
                return "";
 
193
        }
 
194
}
 
195
 
 
196
const char *
 
197
koto_undo_manager_get_redo_name (KotoUndoManager *manager)
 
198
{
 
199
        if (manager->priv->redo) {
 
200
                KotoUndoContext *ctxt = manager->priv->redo->data;
 
201
                return ctxt->name;
 
202
        } else {
 
203
                return "";
 
204
        }
 
205
}
 
206
 
 
207
KotoUndoContext *
 
208
koto_undo_manager_context_begin (KotoUndoManager *manager,
 
209
                                   const char *name)
 
210
{
 
211
        KotoUndoContext *ctxt;
 
212
 
 
213
        if (manager->priv->working != NULL) {
 
214
                manager->priv->working->count++;
 
215
 
 
216
                return manager->priv->working;
 
217
        }
 
218
 
 
219
        ctxt = context_new (name);
 
220
        ctxt->count++;
 
221
        manager->priv->working = ctxt;
 
222
 
 
223
        return ctxt;
 
224
}
 
225
 
 
226
KotoUndoContext *
 
227
koto_undo_manager_context_begin_formatted (KotoUndoManager *manager,
 
228
                                           const char *format, ...)
 
229
{
 
230
        KotoUndoContext *ctxt;
 
231
        va_list args;
 
232
        char *name;
 
233
        
 
234
        va_start (args, format);
 
235
        
 
236
        name = g_strdup_vprintf (format, args);
 
237
        
 
238
        ctxt = koto_undo_manager_context_begin (manager, name);
 
239
        g_free (name);
 
240
 
 
241
        va_end (args);
 
242
 
 
243
        return ctxt;
 
244
}
 
245
 
 
246
void
 
247
koto_undo_manager_context_end (KotoUndoManager *manager,
 
248
                                 KotoUndoContext *ctxt)
 
249
{
 
250
        /* TODO: handle start then end without any adds correctly */
 
251
        KotoUndoManagerPrivate *priv;
 
252
 
 
253
        priv = manager->priv;
 
254
 
 
255
        ctxt->count--;
 
256
 
 
257
        if (ctxt->count > 0) {
 
258
                return;
 
259
        }
 
260
 
 
261
        if (priv->contexts == NULL) {
 
262
                priv->contexts = g_list_append (NULL, ctxt);
 
263
                priv->undo = priv->contexts;
 
264
        } else {
 
265
                GList *pruned = priv->redo;
 
266
                GList *n;
 
267
 
 
268
                if (pruned != NULL) {
 
269
                        GList *p;
 
270
 
 
271
                        /* Disconnect pruned */
 
272
                        if (priv->redo && priv->redo->prev) {
 
273
                                priv->redo->prev->next = NULL;
 
274
                        }
 
275
 
 
276
                        /* If redo->prev == NULL then we've pruned everything */
 
277
                        if (priv->redo && priv->redo->prev == NULL) {
 
278
                                priv->contexts = NULL;
 
279
                        }
 
280
 
 
281
                        priv->redo = NULL;
 
282
                        pruned->prev = NULL;
 
283
 
 
284
                        for (p = pruned; p; p = p->next) {
 
285
                                KotoUndoContext *c = p->data;
 
286
 
 
287
                                context_free (c);
 
288
                        }
 
289
                        g_list_free (pruned);
 
290
                }
 
291
 
 
292
                /* Append our new context and move the position pointer on */
 
293
                n = g_list_append (priv->undo, ctxt);
 
294
                if (priv->undo == NULL) {
 
295
                        priv->undo = n;
 
296
                } else {
 
297
                        priv->undo = priv->undo->next;
 
298
                }
 
299
                /* If priv->contexts is NULL, then we pruned everything and need
 
300
                   to start a new list */
 
301
                if (priv->contexts == NULL) {
 
302
                        priv->contexts = priv->undo;
 
303
                }
 
304
                priv->redo = NULL;
 
305
        }
 
306
 
 
307
        priv->working = NULL;
 
308
 
 
309
        g_signal_emit (manager, signals[CHANGED], 0);
 
310
}
 
311
 
 
312
void
 
313
koto_undo_manager_context_cancel (KotoUndoManager *manager,
 
314
                                    KotoUndoContext *ctxt)
 
315
{
 
316
        g_return_if_fail (manager->priv->working == ctxt);
 
317
 
 
318
        manager->priv->working = NULL;
 
319
        context_free (ctxt);
 
320
}
 
321
 
 
322
void
 
323
koto_undo_manager_undo (KotoUndoManager *manager)
 
324
{
 
325
        
 
326
        if (manager->priv->undo) {
 
327
                context_undo ((KotoUndoContext *) manager->priv->undo->data);
 
328
                manager->priv->redo = manager->priv->undo;
 
329
                manager->priv->undo = manager->priv->undo->prev;
 
330
 
 
331
                g_signal_emit (manager, signals[CHANGED], 0);
 
332
        }
 
333
}
 
334
 
 
335
void
 
336
koto_undo_manager_redo (KotoUndoManager *manager)
 
337
{
 
338
        if (manager->priv->redo) {
 
339
                context_redo ((KotoUndoContext *) manager->priv->redo->data);
 
340
                manager->priv->undo = manager->priv->redo;
 
341
                manager->priv->redo = manager->priv->redo->next;
 
342
 
 
343
                g_signal_emit (manager, signals[CHANGED], 0);
 
344
        }
 
345
}
 
346
 
 
347
GList *
 
348
koto_undo_manager_get_history (KotoUndoManager *manager)
 
349
{
 
350
        GList *history = NULL;
 
351
        GList *p;
 
352
        KotoUndoHistory *hist;
 
353
 
 
354
        hist = g_new (KotoUndoHistory, 1);
 
355
        hist->name = g_strdup (_("Original Sample"));
 
356
        hist->current = FALSE;
 
357
        hist->ctxt = NULL;
 
358
 
 
359
        history = g_list_prepend (history, hist);
 
360
 
 
361
        for (p = manager->priv->contexts; p; p = p->next) {
 
362
                KotoUndoContext *ctxt = p->data;
 
363
 
 
364
                hist = g_new (KotoUndoHistory, 1);
 
365
                hist->name = g_strdup (ctxt->name);
 
366
                hist->ctxt = ctxt;
 
367
 
 
368
                history = g_list_append (history, hist);
 
369
                if (p == manager->priv->undo) {
 
370
                        hist->current = TRUE;
 
371
                } else {
 
372
                        hist->current = FALSE;
 
373
                }
 
374
        }
 
375
 
 
376
        return history;
 
377
}
 
378
 
 
379
void
 
380
koto_undo_context_add (KotoUndoContext *ctxt,
 
381
                         KotoUndoable *undoable)
 
382
{
 
383
        ctxt->undoables = g_list_prepend (ctxt->undoables, undoable);
 
384
}