summaryrefslogtreecommitdiffstats
path: root/lib/kofficecore/KoFilterChain.h
blob: a1f3d239ffb295190758dcf863981cfbbe9042e2 (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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
/* This file is part of the KOffice libraries
   Copyright (C) 2001 Werner Trobin <trobin@kde.org>

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public
   License version 2 as published by the Free Software Foundation.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Library General Public License
   along with this library; see the file COPYING.LIB.  If not, write to
   the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
*/

#ifndef __koffice_filter_chain_h__
#define __koffice_filter_chain_h__

#include <tqcstring.h>
#include <tqasciidict.h>
#include <tqptrlist.h>
#include <tqstringlist.h>

#include <KoFilter.h>
#include <KoQueryTrader.h>
#include <KoStoreDevice.h>
#include <koffice_export.h>

class KTempFile;
class KoFilterManager;
class KoDocument;
class TQStrList;

namespace KOffice {
    class Graph;
}

/**
 * @brief This class represents a chain of plain @ref KoFilter instances.
 *
 * Instances of this class are shared, so please just hold
 * KoFilterChain::Ptr pointers to it.
 *
 * @author Werner Trobin <trobin@kde.org>
 * @todo the class has no constructor and therefore cannot initialize its private class
 */
class KOFFICECORE_EXPORT KoFilterChain : public TDEShared
{
    // Only KOffice::Graph is allowed to construct instances and
    // add chain links.
    friend class KOffice::Graph;
    friend class KoFilterManager;

public:
    typedef TDESharedPtr<KoFilterChain> Ptr;

    virtual ~KoFilterChain();

    /**
     * The filter manager returned may be 0!
     */
    const KoFilterManager* manager() const { return m_manager; }

    /**
     * Starts the filtering process.
     * @return The return status of the conversion. KoFilter::OK
     * if everything is alright.
     */
    KoFilter::ConversionStatus invokeChain();

    /**
     * Tells the @ref KoFilterManager the output file of the
     * filter chain in case of an import operation. If it's
     * TQString() we directly manipulated the document.
     */
    TQString chainOutput() const;

    /**
     * Get the current file to read from. This part of the API
     * is for the filters in our chain.
     */
    TQString inputFile();
    /**
     * Get the current file to write to. This part of the API
     * is for the filters in our chain.
     */
    TQString outputFile();

    /**
     * Get a file from a storage. May return 0!
     * This part of the API is for the filters in our chain.
     * If you call it multiple times with the same stream name
     * the stream will be closed and re-opened.
     * Note: @em Don't delete that @ref KoStoreDevice we return.
     * @param name The name of the stream inside the storage
     * @param mode Whether we want to read or write from/to the stream
     * @return The storage device to access the stream. May be 0!
     */
    KoStoreDevice* storageFile( const TQString& name = "root", KoStore::Mode mode = KoStore::Read );

    /**
     * This method allows your filter to work directly on the
     * @ref KoDocument of the application.
     * This part of the API is for the filters in our chain.
     * @return The document containing the data. May return 0 on error.
     */
    KoDocument* inputDocument();
    /**
     * This method allows your filter to work directly on the
     * @ref KoDocument of the application.
     * This part of the API is for the filters in our chain.
     * @return The document you have to write to. May return 0 on error.
     */
    KoDocument* outputDocument();


    // debugging
    void dump() const;

private:
    // ### API for KOffice::Graph:
    // Construct a filter chain belonging to some KoFilterManager.
    // The parent filter manager may be 0.
    KoFilterChain( const KoFilterManager* manager );

    void appendChainLink( KoFilterEntry::Ptr filterEntry, const TQCString& from, const TQCString& to );
    void prependChainLink( KoFilterEntry::Ptr filterEntry, const TQCString& from, const TQCString& to );

    // ### API for KoEmbeddingFilter
    // This is needed as the embedding filter might have to influence
    // the way we change directories (e.g. in the olefilter case)
    // The ugly friend methods are needed, but I'd welcome and suggestions for
    // better design :}
    friend void KoEmbeddingFilter::filterChainEnterDirectory( const TQString& directory ) const;
    void enterDirectory( const TQString& directory );
    friend void KoEmbeddingFilter::filterChainLeaveDirectory() const;
    void leaveDirectory();

    // These methods are friends of KoFilterManager and provide access
    // to a private part of its API. As I don't want to include
    // koFilterManager.h in this header the direction is "int" here.
    TQString filterManagerImportFile() const;
    TQString filterManagerExportFile() const;
    KoDocument* filterManagerKoDocument() const;
    int filterManagerDirection() const;
    KoFilterChain* const filterManagerParentChain() const;


    // Helper methods which keep track of all the temp files, documents,
    // storages,... and properly delete them as soon as they are not
    // needed anymore.
    void manageIO();
    void finalizeIO();

    bool createTempFile( KTempFile** tempFile, bool autoDelete = true );

    void inputFileHelper( KoDocument* document, const TQString& alternativeFile );
    void outputFileHelper( bool autoDelete );
    KoStoreDevice* storageNewStreamHelper( KoStore** storage, KoStoreDevice** device, const TQString& name );
    KoStoreDevice* storageHelper( const TQString& file, const TQString& streamName,
                                  KoStore::Mode mode, KoStore** storage, KoStoreDevice** device );
    void storageInit( const TQString& file, KoStore::Mode mode, KoStore** storage );
    KoStoreDevice* storageInitEmbedding( const TQString& name );
    KoStoreDevice* storageCreateFirstStream( const TQString& streamName, KoStore** storage, KoStoreDevice** device );
    KoStoreDevice* storageCleanupHelper( KoStore** storage );

    KoDocument* createDocument( const TQString& file );
    KoDocument* createDocument( const TQCString& mimeType );

    /**
     * A small private helper class with represents one single filter
     * (one link of the chain)
     * @internal
     */
    class ChainLink
    {

    public:
        ChainLink( KoFilterChain* chain, KoFilterEntry::Ptr filterEntry,
                   const TQCString& from, const TQCString& to );

        KoFilter::ConversionStatus invokeFilter( const ChainLink* const parentChainLink );

        TQCString from() const { return m_from; }
        TQCString to() const { return m_to; }

        // debugging
        void dump() const;

        // This hack is only needed due to crappy Microsoft design and
        // circular dependencies in their embedded files :}
        int lruPartIndex() const;

    private:
        ChainLink( const ChainLink& rhs );
        ChainLink& operator=( const ChainLink& rhs );

        void setupCommunication( const KoFilter* const parentFilter ) const;
        void setupConnections( const KoFilter* sender, const TQStrList& sigs,
                               const KoFilter* receiver, const TQStrList& sl0ts ) const;

        KoFilterChain* m_chain;
        KoFilterEntry::Ptr m_filterEntry;
        TQCString m_from, m_to;

        // This hack is only needed due to crappy Microsoft design and
        // circular dependencies in their embedded files :}
        KoFilter* m_filter;

        class Private;
        Private* d;
    };

    // "A whole is that which has beginning, middle, and end" - Aristotle
    // ...but we also need to signal "Done" state, Mr. Aristotle
    enum Whole { Beginning = 1, Middle = 2, End = 4, Done = 8 };

    // Don't copy or assign filter chains
    KoFilterChain( const KoFilterChain& rhs );
    KoFilterChain& operator=( const KoFilterChain& rhs );

    const KoFilterManager* const m_manager;
    TQPtrList<ChainLink> m_chainLinks;

    // stuff needed for bookkeeping
    int m_state;

    TQString m_inputFile;              // Did we pass around plain files?
    TQString m_outputFile;

    KoStore* m_inputStorage;          // ...or was it a storage+device?
    KoStoreDevice* m_inputStorageDevice;
    KoStore* m_outputStorage;
    KoStoreDevice* m_outputStorageDevice;

    KoDocument* m_inputDocument;      // ...or even documents?
    KoDocument* m_outputDocument;

    KTempFile* m_inputTempFile;
    KTempFile* m_outputTempFile;

    // These two flags keep track of the input/output the
    // filter (=user) asked for
    enum IOState { Nil, File, Storage, Document };
    IOState m_inputQueried, m_outputQueried;

    // This stack keeps track of directories we have to enter and
    // leave due to internal embedding a la OLE filters. This serves
    // as a kind of "memory" even if we didn't initialize the store yet.
    // I know that it's ugly, and I'll try to clean up that hack
    // sooner or later (Werner)
    TQStringList m_internalEmbeddingDirectories;

    class Private;
    Private* d;
};


// As we use quite generic classnames...
namespace KOffice
{
    class Vertex;
    template<class T> class PriorityQueue;

    /**
     * An internal class representing a filter (=edge) in the filter graph.
     * @internal
     */
    class Edge
    {

    public:
        // creates a new edge to "vertex" with the given weight.
        Edge( Vertex* vertex, KoFilterEntry::Ptr filterEntry );
        ~Edge() {}

        unsigned int weight() const { return m_filterEntry ? m_filterEntry->weight : 0; }
        KoFilterEntry::Ptr filterEntry() const { return m_filterEntry; }
        const Vertex* vertex() const { return m_vertex; }

        // Relaxes the "connected" vertex (i.e. the weight of the
        // connected vertex = "predec.->key()" (parameter) + weight of this edge
        // As this will only be called once we calculate the weight
        // of the edge "on the fly"
        // Note: We have to pass the queue as we have to call keyDecreased :}
        void relax( const Vertex* predecessor, PriorityQueue<Vertex>& queue );

        // debugging
        void dump( const TQCString& indent ) const;

    private:
        Edge( const Edge& rhs );
        Edge& operator=( const Edge& rhs );

        Vertex* m_vertex;
        KoFilterEntry::Ptr m_filterEntry;

        class Private;
        Private* d;
    };


    /**
     * An internal class representing a mime type (=node, vertex) in the filter graph.
     * @internal
     */
    class Vertex
    {

    public:
        Vertex( const TQCString& mimeType );
        ~Vertex() {}

        TQCString mimeType() const { return m_mimeType; }

        // Current "weight" of the vertex - will be "relaxed" when
        // running the shortest path algorithm. Returns true if it
        // really has been "relaxed"
        bool setKey( unsigned int key );
        unsigned int key() const { return m_weight; }
        // Can be used to set the key back to "Infinity" (UINT_MAX)
        // and reset the predecessor of this vertex
        void reset();

        // Position in the heap, needed for a fast keyDecreased operation
        void setIndex( int index ) { m_index=index; }
        int index() const { return m_index; }

        // predecessor on the way from the source to the destination,
        // needed for the shortest path algorithm
        void setPredecessor( const Vertex* predecessor ) { m_predecessor=predecessor; }
        const Vertex* predecessor() const { return m_predecessor; }

        // Adds an outgoing edge to the vertex, transfers ownership
        void addEdge( const Edge* edge );
        // Finds the lightest(!) edge pointing to the given vertex, if any (0 if not found)
        // This means it will always search the whole list of edges
        const Edge* findEdge( const Vertex* vertex ) const;

        // This method is called when we need to relax all "our" edges.
        // We need to pass the queue as we have to notify it about key changes - ugly :(
        void relaxVertices( PriorityQueue<Vertex>& queue );

        // debugging
        void dump( const TQCString& indent ) const;

    private:
        Vertex( const Vertex& rhs );
        Vertex& operator=( const Vertex& rhs );

        TQPtrList<Edge> m_edges;
        const Vertex* m_predecessor;
        TQCString m_mimeType;
        unsigned int m_weight; // "key" inside the queue
        int m_index; // position inside the queue, needed for a fast keyDecreased()

        class Private;
        Private* d;
    };


    /**
     * The main worker behind the scenes. Manages the creation of the graph,
     * processing the information in it, and creating the filter chains.
     * @internal
     */
    class Graph
    {

    public:
        Graph( const TQCString& from );
        ~Graph() {}

        bool isValid() const { return m_graphValid; }

        TQCString sourceMimeType() const { return m_from; }
        void setSourceMimeType( const TQCString& from );

        // Creates a chain from "from" to the "to" mimetype
        // If the "to" mimetype isEmpty() then we try to find the
        // closest KOffice mimetype and use that as destination.
        // After such a search "to" will contain the dest. mimetype (return value)
        // if the search was successful. Might return 0!
        KoFilterChain::Ptr chain( const KoFilterManager* manager, TQCString& to ) const;

        // debugging
        void dump() const;

    private:
        Graph( const Graph& rhs );
        Graph& operator=( const Graph& rhs );

        void buildGraph();
        void shortestPaths();
        TQCString findKOfficePart() const;

        TQAsciiDict<Vertex> m_vertices;
        TQCString m_from;
        bool m_graphValid;

        class Private;
        Private* d;
    };

} // namespace KOffice

#endif // __koffice_filter_chain_h__