poppler-optcontent.cc 12 KB
Newer Older
1
2
3
/* poppler-optcontent.cc: qt interface to poppler
 *
 * Copyright (C) 2007, Brad Hards <bradh@kde.org>
4
 * Copyright (C) 2008, Pino Toscano <pino@kde.org>
5
 * Copyright (C) 2008, Carlos Garcia Campos <carlosgc@gnome.org>
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA.
 */

Pino Toscano's avatar
Pino Toscano committed
22
#include "poppler-optcontent.h"
23

Pino Toscano's avatar
Pino Toscano committed
24
#include "poppler-optcontent-private.h"
25
26
27
28

#include "poppler-private.h"

#include <QtCore/QDebug>
29
#include <QtCore/QtAlgorithms>
30

Pino Toscano's avatar
Pino Toscano committed
31
32
#include "poppler/OptionalContent.h"

33
34
35
namespace Poppler
{

36
  RadioButtonGroup::RadioButtonGroup( OptContentModelPrivate *ocModel, Array *rbarray )
37
38
39
40
41
42
43
  {
    for (int i = 0; i < rbarray->getLength(); ++i) {
      Object ref;
      rbarray->getNF( i, &ref );
      if ( ! ref.isRef() ) {
	qDebug() << "expected ref, but got:" << ref.getType();
      }
44
      OptContentItem *item = ocModel->itemFromRef( QString::number(ref.getRefNum() ) );
45
46
47
48
49
50
51
52
53
54
55
56
      itemsInGroup.append( item );
    }
    for (int i = 0; i < itemsInGroup.size(); ++i) {
      OptContentItem *item = itemsInGroup.at(i);
      item->appendRBGroup( this );
    }
  }

  RadioButtonGroup::~RadioButtonGroup()
  {
  }

57
  QSet<OptContentItem *> RadioButtonGroup::setItemOn( OptContentItem *itemToSetOn )
58
  {
59
    QSet<OptContentItem *> changedItems;
60
61
62
    for (int i = 0; i < itemsInGroup.size(); ++i) {
      OptContentItem *thisItem = itemsInGroup.at(i);
      if (thisItem != itemToSetOn) {
63
64
65
        QSet<OptContentItem *> newChangedItems;
        thisItem->setState(OptContentItem::Off, newChangedItems);
        changedItems += newChangedItems;
66
67
      }
    }
68
    return changedItems;
69
70
71
72
73
74
75
76
  }



  OptContentItem::OptContentItem( OptionalContentGroup *group )
  {
    m_group = group;
    m_parent = 0;
77
78
    m_name = UnicodeParsedString( group->getName() );
    if ( group->getState() == OptionalContentGroup::On ) {
79
80
81
82
      m_state = OptContentItem::On;
    } else {
      m_state = OptContentItem::Off;
    }
83
84
    m_stateBackup = m_state;
    m_enabled = true;
85
86
87
88
89
90
91
92
  }

  OptContentItem::OptContentItem( const QString &label )
  {
    m_parent = 0;
    m_name = label;
    m_group = 0;
    m_state = OptContentItem::HeadingOnly;
93
94
    m_stateBackup = m_state;
    m_enabled = true;
95
96
  }

Brad Hards's avatar
Brad Hards committed
97
  OptContentItem::OptContentItem() :
98
    m_parent( 0 ), m_enabled(true)
99
100
101
102
103
104
105
106
107
108
109
110
111
  {
  }

  OptContentItem::~OptContentItem()
  {
  }

  void OptContentItem::appendRBGroup( RadioButtonGroup *rbgroup )
  {
    m_rbGroups.append( rbgroup );
  }


112
  bool OptContentItem::setState(ItemState state, QSet<OptContentItem *> &changedItems)
113
114
  {
    m_state = state;
115
    m_stateBackup = m_state;
116
    changedItems.insert(this);
117
118
119
120
121
122
123
    QSet<OptContentItem *> empty;
    Q_FOREACH (OptContentItem *child, m_children) {
      ItemState oldState = child->m_stateBackup;
      child->setState(state == OptContentItem::On ? child->m_stateBackup : OptContentItem::Off, empty);
      child->m_enabled = state == OptContentItem::On;
      child->m_stateBackup = oldState;
    }
124
125
126
127
128
129
130
    if (!m_group) {
      return false;
    }
    if ( state == OptContentItem::On ) {
      m_group->setState( OptionalContentGroup::On );
      for (int i = 0; i < m_rbGroups.size(); ++i) {
	RadioButtonGroup *rbgroup = m_rbGroups.at(i);
131
        changedItems += rbgroup->setItemOn( this );
132
133
134
135
136
137
138
139
140
141
142
143
144
      }
    } else if ( state == OptContentItem::Off ) {
      m_group->setState( OptionalContentGroup::Off );
    }
    return true;
  }

  void OptContentItem::addChild( OptContentItem *child )
  {
    m_children += child;
    child->setParent( this );
  }

145
146
147
148
149
150
151
152
153
154
155
156
  QSet<OptContentItem*> OptContentItem::recurseListChildren(bool includeMe) const
  {
    QSet<OptContentItem*> ret;
    if (includeMe) {
      ret.insert(const_cast<OptContentItem*>(this));
    }
    Q_FOREACH (OptContentItem *child, m_children) {
      ret += child->recurseListChildren(true);
    }
    return ret;
  }

157
158
  OptContentModelPrivate::OptContentModelPrivate( OptContentModel *qq, OCGs *optContent )
    : q(qq)
159
160
161
162
163
164
165
  {
    m_rootNode = new OptContentItem();
    GooList *ocgs = optContent->getOCGs();

    for (int i = 0; i < ocgs->getLength(); ++i) {
      OptionalContentGroup *ocg = static_cast<OptionalContentGroup*>(ocgs->get(i));
      OptContentItem *node = new OptContentItem( ocg );
166
      m_optContentItems.insert( QString::number(ocg->getRef().num), node);
167
168
169
170
171
172
173
    }

    if ( optContent->getOrderArray() == 0 ) {
      // no Order array, so drop them all at the top level
      QMapIterator<QString, OptContentItem*> i(m_optContentItems);
      while ( i.hasNext() ) {
	i.next();
174
	addChild( m_rootNode, i.value() );
175
176
177
178
179
      }
    } else {
      parseOrderArray( m_rootNode, optContent->getOrderArray() );
    }

Brad Hards's avatar
Brad Hards committed
180
    parseRBGroupsArray( optContent->getRBGroupsArray() );
181
182
  }

Brad Hards's avatar
Brad Hards committed
183
  OptContentModelPrivate::~OptContentModelPrivate()
184
185
186
187
188
189
  {
    qDeleteAll( m_optContentItems );
    qDeleteAll( m_rbgroups );
    delete m_rootNode;
  }

Brad Hards's avatar
Brad Hards committed
190
  void OptContentModelPrivate::parseOrderArray( OptContentItem *parentNode, Array *orderArray )
191
192
193
194
195
196
197
198
199
  {
    OptContentItem *lastItem = parentNode;
    for (int i = 0; i < orderArray->getLength(); ++i) {
      Object orderItem;
      orderArray->get(i, &orderItem);
      if ( orderItem.isDict() ) {
	Object item;
	orderArray->getNF(i, &item);
	if (item.isRef() ) {
Pino Toscano's avatar
Pino Toscano committed
200
          OptContentItem *ocItem = m_optContentItems.value(QString::number(item.getRefNum()), 0);
201
202
203
204
	  if (ocItem) {
	    addChild( parentNode, ocItem );
	    lastItem = ocItem;
	  } else {
Pino Toscano's avatar
Pino Toscano committed
205
            qDebug() << "could not find group for object" << item.getRefNum();
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
	  }
	}
	item.free();
      } else if ( (orderItem.isArray()) && (orderItem.arrayGetLength() > 0) ) {
	parseOrderArray(lastItem, orderItem.getArray());
      } else if ( orderItem.isString() ) {
	GooString *label = orderItem.getString();
	OptContentItem *header = new OptContentItem ( UnicodeParsedString ( label ) );
	addChild( parentNode, header );
	parentNode = header;
	lastItem = header;
      } else {
	qDebug() << "something unexpected";
      }	
      orderItem.free();
    }
  }

224
  void OptContentModelPrivate::parseRBGroupsArray( Array *rBGroupArray )
225
226
227
228
229
230
231
232
233
234
235
236
237
238
  {
    if (! rBGroupArray) {
      return;
    }
    // This is an array of array(s)
    for (int i = 0; i < rBGroupArray->getLength(); ++i) {
      Object rbObj;
      rBGroupArray->get(i, &rbObj);
      if ( ! rbObj.isArray() ) {
	qDebug() << "expected inner array, got:" << rbObj.getType();
	return;
      }
      Array *rbarray = rbObj.getArray();
      RadioButtonGroup *rbg = new RadioButtonGroup( this, rbarray );
239
      m_rbgroups.append( rbg );
240
241
242
243
      rbObj.free();
    }
  }

Brad Hards's avatar
Brad Hards committed
244
245
246
  OptContentModel::OptContentModel( OCGs *optContent, QObject *parent)
    : QAbstractItemModel(parent)
  {
247
    d = new OptContentModelPrivate( this, optContent );
Brad Hards's avatar
Brad Hards committed
248
249
250
251
252
253
254
  }

  OptContentModel::~OptContentModel()
  {
    delete d;
  }

255
  void OptContentModelPrivate::setRootNode(OptContentItem *node)
Brad Hards's avatar
Brad Hards committed
256
  {
257
258
259
    delete m_rootNode;
    m_rootNode = node;
    q->reset();
Brad Hards's avatar
Brad Hards committed
260
261
  }

262
263
  QModelIndex OptContentModel::index(int row, int column, const QModelIndex &parent) const
  {
264
    if (row < 0 || column != 0) {
265
266
267
      return QModelIndex();
    }

268
    OptContentItem *parentNode = d->nodeFromIndex( parent );
269
270
271
272
    if (row < parentNode->childList().count()) {
      return createIndex(row, column, parentNode->childList().at(row));
    }
    return QModelIndex();
273
274
275
276
  }

  QModelIndex OptContentModel::parent(const QModelIndex &child) const
  {
277
    OptContentItem *childNode = d->nodeFromIndex( child );
278
279
280
    if (!childNode) {
      return QModelIndex();
    }
281
    return d->indexFromItem(childNode->parent(), child.column());
282
283
  }

284
  QModelIndex OptContentModelPrivate::indexFromItem(OptContentItem *node, int column) const
285
  {
286
    if (!node) {
287
288
      return QModelIndex();
    }
289
    OptContentItem *parentNode = node->parent();
290
291
292
    if (!parentNode) {
      return QModelIndex();
    }
293
294
    const int row = parentNode->childList().indexOf(node);
    return q->createIndex(row, column, node);
295
296
297
298
  }
 
  int OptContentModel::rowCount(const QModelIndex &parent) const
  {
299
    OptContentItem *parentNode = d->nodeFromIndex( parent );
300
301
302
303
304
305
306
307
308
    if (!parentNode) {
      return 0;
    } else {
      return parentNode->childList().count();
    }
  }

  int OptContentModel::columnCount(const QModelIndex &parent) const
  {
309
    return 1;
310
311
312
313
314
  }


  QVariant OptContentModel::data(const QModelIndex &index, int role) const
  {
315
    OptContentItem *node = d->nodeFromIndex(index, true);
316
317
318
319
    if (!node) {
      return QVariant();
    }

320
321
322
323
324
325
326
327
328
329
330
331
332
    switch (role) {
      case Qt::DisplayRole:
        return node->name();
        break;
      case Qt::EditRole:
        if (node->state() == OptContentItem::On) {
          return true;
        } else if (node->state() == OptContentItem::Off) {
          return false;
        }
        break;
      case Qt::CheckStateRole:
        if (node->state() == OptContentItem::On) {
Pino Toscano's avatar
Pino Toscano committed
333
          return Qt::Checked;
334
        } else if (node->state() == OptContentItem::Off) {
Pino Toscano's avatar
Pino Toscano committed
335
          return Qt::Unchecked;
336
337
        }
        break;
338
339
340
341
342
343
344
    }

    return QVariant();
  }

  bool OptContentModel::setData ( const QModelIndex & index, const QVariant & value, int role )
  {
345
    OptContentItem *node = d->nodeFromIndex(index, true);
346
347
348
349
    if (!node) {
      return false;
    }

350
351
352
353
354
355
    switch (role) {
      case Qt::CheckStateRole:
      {
        const bool newvalue = value.toBool();
        if (newvalue) {
          if (node->state() != OptContentItem::On) {
356
357
            QSet<OptContentItem *> changedItems;
            node->setState(OptContentItem::On, changedItems);
358
            changedItems += node->recurseListChildren(false);
359
360
361
362
363
364
365
366
            QModelIndexList indexes;
            Q_FOREACH (OptContentItem *item, changedItems) {
              indexes.append(d->indexFromItem(item, 0));
            }
            qStableSort(indexes);
            Q_FOREACH (const QModelIndex &changedIndex, indexes) {
              emit dataChanged(changedIndex, changedIndex);
            }
367
368
369
370
            return true;
          }
        } else {
          if (node->state() != OptContentItem::Off) {
371
372
            QSet<OptContentItem *> changedItems;
            node->setState(OptContentItem::Off, changedItems);
373
            changedItems += node->recurseListChildren(false);
374
375
376
377
378
379
380
381
            QModelIndexList indexes;
            Q_FOREACH (OptContentItem *item, changedItems) {
              indexes.append(d->indexFromItem(item, 0));
            }
            qStableSort(indexes);
            Q_FOREACH (const QModelIndex &changedIndex, indexes) {
              emit dataChanged(changedIndex, changedIndex);
            }
382
383
384
385
            return true;
          }
        }
        break;
386
387
388
389
390
391
392
393
      }
    }

    return false;
  }

  Qt::ItemFlags OptContentModel::flags ( const QModelIndex & index ) const
  {
394
395
396
397
    OptContentItem *node = d->nodeFromIndex(index);
    Qt::ItemFlags itemFlags = Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
    if (node->isEnabled()) {
      itemFlags |= Qt::ItemIsEnabled;
398
    }
399
    return itemFlags;
400
401
  }

402
403
404
405
406
  QVariant OptContentModel::headerData( int section, Qt::Orientation orientation, int role ) const
  {
    return QAbstractItemModel::headerData( section, orientation, role );
  }

Brad Hards's avatar
Brad Hards committed
407
  void OptContentModelPrivate::addChild( OptContentItem *parent, OptContentItem *child )
408
409
410
411
  {
    parent->addChild( child );
  }

412
  OptContentItem* OptContentModelPrivate::itemFromRef( const QString &ref ) const
413
  {
Pino Toscano's avatar
Pino Toscano committed
414
    return m_optContentItems.value(ref, 0);
415
416
  }

417
  OptContentItem* OptContentModelPrivate::nodeFromIndex(const QModelIndex &index, bool canBeNull) const
418
419
420
421
  {
    if (index.isValid()) {
      return static_cast<OptContentItem *>(index.internalPointer());
    } else {
422
      return canBeNull ? 0 : m_rootNode;
423
424
425
426
427
    }
  }
}

#include "poppler-optcontent.moc"