~sil2100/unity-2d/precise-security

« back to all changes in this revision

Viewing changes to libunity-2d-private/src/focuspath.cpp

  • Committer: Aurelien Gateau
  • Date: 2010-11-10 08:57:29 UTC
  • mto: This revision was merged to the branch mainline in revision 284.
  • Revision ID: aurelien.gateau@canonical.com-20101110085729-fl1ye7impkqhm0w6
Added a section about const correct-ness

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Copyright (C) 2011 Canonical, Ltd.
3
 
 *
4
 
 * Authors:
5
 
 *  Renato Araujo Oliveira Filho <renato.filho@canonical.com>
6
 
 *
7
 
 * This program is free software; you can redistribute it and/or modify
8
 
 * it under the terms of the GNU General Public License as published by
9
 
 * the Free Software Foundation; version 3.
10
 
 *
11
 
 * This program 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
14
 
 * GNU General Public License for more details.
15
 
 *
16
 
 * You should have received a copy of the GNU General Public License
17
 
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 
 */
19
 
 
20
 
#include "focuspath.h"
21
 
 
22
 
#include <QtCore/qmath.h>
23
 
#include <QEvent>
24
 
#include <QKeyEvent>
25
 
 
26
 
/*!
27
 
    \qmlclass FocusPath
28
 
    \brief FocusPath is used to help keyboard navigation between QML elements
29
 
 
30
 
    The following example shows how to use FocusPath no navigate through a QML Grid element cells.
31
 
 
32
 
    \qml
33
 
    FocusPath {
34
 
        item: myGrid
35
 
        columns: myGrid.columns
36
 
    }
37
 
 
38
 
    Grid {
39
 
        id: myGrid
40
 
        columns: 4
41
 
        Repeater {
42
 
            model: myModel
43
 
            FocusScope {
44
 
                Focupath.index: index
45
 
                Rectangle {
46
 
                    width: 100
47
 
                    height: 100
48
 
                    color: activeFocus ? "red" : "blue"
49
 
                }
50
 
            }
51
 
        }
52
 
    }
53
 
    \endqml
54
 
 
55
 
    The FocusPath class should be used with the attached property 'index' to determiner the focus path order.
56
 
*/
57
 
 
58
 
/*!
59
 
    \qmlproperty Component item
60
 
 
61
 
    The parent item for navigation
62
 
*/
63
 
 
64
 
/*!
65
 
    \qmlproperty int columns
66
 
 
67
 
    Number of columns in the item element, this will allow vertical naviagtion
68
 
*/
69
 
 
70
 
/*!
71
 
    \qmlproperty Flow flow
72
 
 
73
 
   Holds the flow of the path
74
 
*/
75
 
 
76
 
/*!
77
 
    \qmlproperty Direction direction
78
 
 
79
 
   Holds the allowed navigation direction
80
 
*/
81
 
 
82
 
/*!
83
 
    \qmlproperty int index
84
 
 
85
 
    Attached property used to determine the focus sequence
86
 
*/
87
 
 
88
 
/*!
89
 
    \qmlproperty bool skip
90
 
 
91
 
    Attached property to remove item from the focus path
92
 
*/
93
 
 
94
 
FocusPath::FocusPath(QObject *parent)
95
 
    : QObject(parent),
96
 
      m_item(0),
97
 
      m_columns(0),
98
 
      m_rows(0),
99
 
      m_currentPosition(-1),
100
 
      m_flow(FocusPath::LeftToRight),
101
 
      m_direction(FocusPath::HorizontalAndVertical)
102
 
{
103
 
}
104
 
 
105
 
FocusPath::~FocusPath()
106
 
{
107
 
    updateItem(NULL);
108
 
}
109
 
 
110
 
QDeclarativeItem* FocusPath::item() const
111
 
{
112
 
    return m_item;
113
 
}
114
 
 
115
 
int FocusPath::columns() const
116
 
{
117
 
    return m_columns;
118
 
}
119
 
 
120
 
int FocusPath::currentIndex() const
121
 
{
122
 
    if (m_currentPosition >= 0 && m_currentPosition < m_path.size()) {
123
 
        return m_path[m_currentPosition].first;
124
 
    } else {
125
 
        return -1;
126
 
    }
127
 
}
128
 
 
129
 
QDeclarativeItem* FocusPath::currentItem() const
130
 
{
131
 
    if (m_currentPosition >= 0 && m_currentPosition < m_path.size()) {
132
 
        return m_path[m_currentPosition].second;
133
 
    }
134
 
 
135
 
    return 0;
136
 
}
137
 
 
138
 
FocusPath::Flow FocusPath::flow() const
139
 
{
140
 
    return m_flow;
141
 
}
142
 
 
143
 
FocusPath::NavigationDirection FocusPath::direction() const
144
 
{
145
 
    return m_direction;
146
 
}
147
 
 
148
 
QList<PathItem > FocusPath::path() const
149
 
{
150
 
    return m_path;
151
 
}
152
 
 
153
 
void FocusPath::setItem(QDeclarativeItem* item)
154
 
{
155
 
    if (m_item != item) {
156
 
        updateItem(item);
157
 
        Q_EMIT itemChanged();
158
 
    }
159
 
}
160
 
 
161
 
void FocusPath::setColumns(int columns)
162
 
{
163
 
    if (m_columns != columns) {
164
 
        m_columns = columns;
165
 
        if (m_columns) {
166
 
            m_rows = qCeil(m_path.size() / m_columns);
167
 
        } else {
168
 
            m_rows = 0;
169
 
        }
170
 
        Q_EMIT columnsChanged();
171
 
    }
172
 
}
173
 
 
174
 
void FocusPath::setCurrentIndex(int index)
175
 
{
176
 
    QList<PathItem>::const_iterator i = m_path.begin();
177
 
    int newPosition = 0;
178
 
    while(i != m_path.end()) {
179
 
        if ((*i).first == index) {
180
 
            updatePosition(newPosition);
181
 
            return;
182
 
        }
183
 
        newPosition++;
184
 
        i++;
185
 
    }
186
 
}
187
 
 
188
 
void FocusPath::updatePosition(int index)
189
 
{
190
 
    if ((m_currentPosition != index) &&
191
 
        (index >= 0) &&
192
 
        (index < m_path.size())) {
193
 
        QDeclarativeItem* focus = m_path[index].second;
194
 
        Q_ASSERT(focus);
195
 
        focus->setFocus(true);
196
 
        m_currentPosition = index;
197
 
        Q_EMIT currentIndexChanged();
198
 
        Q_EMIT currentItemChanged();
199
 
    }
200
 
}
201
 
 
202
 
void FocusPath::setFlow(FocusPath::Flow value)
203
 
{
204
 
    if (m_flow != value) {
205
 
        m_flow = value;
206
 
        Q_EMIT flowChanged();
207
 
    }
208
 
}
209
 
 
210
 
void FocusPath::setDirection(FocusPath::NavigationDirection value)
211
 
{
212
 
    if (m_direction != value) {
213
 
        m_direction = value;
214
 
        Q_EMIT directionChanged();
215
 
    }
216
 
}
217
 
 
218
 
void FocusPath::onInfoChanged()
219
 
{
220
 
    FocusPathAttached *info = qobject_cast<FocusPathAttached *> (sender());
221
 
    QDeclarativeItem *item = qobject_cast<QDeclarativeItem *>(info->parent());
222
 
 
223
 
    removeItem(item);
224
 
    addItem(item);
225
 
}
226
 
 
227
 
void FocusPath::onItemChanged()
228
 
{
229
 
    QDeclarativeItem *item = qobject_cast<QDeclarativeItem *>(sender());
230
 
 
231
 
    removeItem(item);
232
 
    addItem(item);
233
 
}
234
 
 
235
 
/*!
236
 
  Reset focus, moving the current focus to the first element
237
 
*/
238
 
 
239
 
void FocusPath::reset()
240
 
{
241
 
    if (m_path.size() > 0) {
242
 
        QGraphicsItem* gi = m_path[0].second;
243
 
        if (gi->flags() & QGraphicsItem::ItemIsFocusScope) {
244
 
            gi->setFocus();
245
 
            m_path[0].second->setFocus(true);
246
 
        }
247
 
    }
248
 
    if (m_columns) {
249
 
        m_rows = qCeil(m_path.size() / m_columns);
250
 
    }
251
 
    m_currentPosition = 0;
252
 
    Q_EMIT currentIndexChanged();
253
 
    Q_EMIT currentItemChanged();
254
 
}
255
 
 
256
 
void FocusPath::addItem(QDeclarativeItem *item)
257
 
{
258
 
    if (item->flags() & QGraphicsItem::ItemIsFocusScope) {
259
 
        QObject *attached = qmlAttachedPropertiesObject<FocusPath>(item);
260
 
        FocusPathAttached *info = static_cast<FocusPathAttached *>(attached);
261
 
 
262
 
        if (!info->skip() && item->isVisible()) {
263
 
            QList<PathItem>::iterator i = m_path.begin();
264
 
            int itemPos = 0;
265
 
 
266
 
            for(; i != m_path.end(); i++) {
267
 
                if (info->index() < (*i).first) {
268
 
                    break;
269
 
                }
270
 
                itemPos++;
271
 
            }
272
 
 
273
 
            if (i == m_path.begin()) {
274
 
                m_path.prepend(qMakePair(info->index(), item));
275
 
            } else if (i == m_path.end()) {
276
 
                m_path.append(qMakePair(info->index(), item));
277
 
            } else {
278
 
                m_path.insert(i, qMakePair(info->index(), item));
279
 
            }
280
 
 
281
 
            if (m_currentPosition == -1) {
282
 
                updatePosition(0);
283
 
            } else if (itemPos <= m_currentPosition) {
284
 
                m_currentPosition++;
285
 
            }
286
 
 
287
 
            if (m_columns) {
288
 
                m_rows = qCeil(m_path.size() / m_columns);
289
 
            }
290
 
 
291
 
        }
292
 
 
293
 
        m_items << item;
294
 
        QObject::connect(info, SIGNAL(indexChanged()), this, SLOT(onInfoChanged()));
295
 
        QObject::connect(info, SIGNAL(skipChanged()), this, SLOT(onInfoChanged()));
296
 
        QObject::connect(item, SIGNAL(visibleChanged()), this, SLOT(onItemChanged()));
297
 
    }
298
 
}
299
 
 
300
 
void FocusPath::removeItem(QDeclarativeItem *item)
301
 
{
302
 
    QList<PathItem>::iterator i = m_path.begin();
303
 
    int itemPos = 0;
304
 
    for(int index=0; i != m_path.end(); i++, index++) {
305
 
        if ((*i).second == item) {
306
 
            m_path.erase(i);
307
 
            if (itemPos == m_currentPosition) {
308
 
                int oldPosition = m_currentPosition;
309
 
                /* Check if this position is the last one */
310
 
                if (oldPosition >= m_path.size()) {
311
 
                    oldPosition = m_path.size() - 1;
312
 
                }
313
 
                /* invalidade current position to allow update */
314
 
                m_currentPosition = -1;
315
 
                updatePosition(oldPosition);
316
 
            } else if (itemPos < m_currentPosition) {
317
 
                m_currentPosition--;
318
 
            }
319
 
 
320
 
            if (m_columns) {
321
 
                m_rows = qCeil(m_path.size() / m_columns);
322
 
            }
323
 
            break;
324
 
        }
325
 
        itemPos++;
326
 
    }
327
 
 
328
 
    QObject *attached = qmlAttachedPropertiesObject<FocusPath>(item);
329
 
    FocusPathAttached *info = static_cast<FocusPathAttached *>(attached);
330
 
 
331
 
    if (info) {
332
 
        QObject::disconnect(info, SIGNAL(indexChanged()), this, SLOT(onInfoChanged()));
333
 
        QObject::disconnect(info, SIGNAL(skipChanged()), this, SLOT(onInfoChanged()));
334
 
    }
335
 
 
336
 
    QObject::disconnect(item, SIGNAL(visibleChanged()), this, SLOT(onItemChanged()));
337
 
    m_items.removeOne(item);
338
 
}
339
 
 
340
 
bool FocusPath::eventFilter(QObject* obj, QEvent* event)
341
 
{
342
 
    if (obj == m_item) {
343
 
        switch(event->type()) {
344
 
            case QEvent::KeyPress: {
345
 
                int nextFocus = m_currentPosition;
346
 
 
347
 
                QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
348
 
                switch(keyEvent->key()) {
349
 
                    case Qt::Key_Right:
350
 
                        if ((m_direction & FocusPath::Horizontal) == FocusPath::Horizontal) {
351
 
                            switch(m_flow) {
352
 
                                case FocusPath::LeftToRight:
353
 
                                    nextFocus += 1;
354
 
                                    break;
355
 
                                case FocusPath::RightToLeft:
356
 
                                    nextFocus -= 1;
357
 
                                    break;
358
 
                                case FocusPath::TopToBottom:
359
 
                                    nextFocus += m_rows;
360
 
                                    break;
361
 
                            }
362
 
                        } else {
363
 
                            nextFocus = -1;
364
 
                        }
365
 
                        break;
366
 
                    case Qt::Key_Left:
367
 
                        if ((m_direction & FocusPath::Horizontal) == FocusPath::Horizontal) {
368
 
                            switch(m_flow) {
369
 
                                case FocusPath::LeftToRight:
370
 
                                    nextFocus -= 1;
371
 
                                    break;
372
 
                                case FocusPath::RightToLeft:
373
 
                                    nextFocus += 1;
374
 
                                    break;
375
 
                                case FocusPath::TopToBottom:
376
 
                                    nextFocus -= m_rows;
377
 
                                    break;
378
 
                            }
379
 
                        } else {
380
 
                            nextFocus = -1;
381
 
                        }
382
 
                        break;
383
 
                    case Qt::Key_Up:
384
 
                        if (((m_direction & FocusPath::Vertical) == FocusPath::Vertical) && (m_columns >= 0)) {
385
 
                            nextFocus = (m_flow == FocusPath::TopToBottom) ? nextFocus - 1 : nextFocus - m_columns;
386
 
                        } else {
387
 
                            nextFocus = -1;
388
 
                        }
389
 
                        break;
390
 
                    case Qt::Key_Down:
391
 
                        if (((m_direction & FocusPath::Vertical) == FocusPath::Vertical) && (m_columns >= 0)) {
392
 
                            nextFocus = (m_flow == FocusPath::TopToBottom) ? nextFocus + 1 : nextFocus + m_columns;
393
 
                        } else {
394
 
                            nextFocus = -1;
395
 
                        }
396
 
                        break;
397
 
                    default:
398
 
                        nextFocus = -1;
399
 
                        break;
400
 
                }
401
 
 
402
 
                if ((nextFocus >= 0) && (nextFocus < m_path.size())) {
403
 
                    updatePosition(nextFocus);
404
 
                    return true;
405
 
                }
406
 
                break;
407
 
            }
408
 
            default:
409
 
               break;
410
 
        }
411
 
    }
412
 
    return false;
413
 
}
414
 
 
415
 
void FocusPath::onChildrenChanged()
416
 
{
417
 
    QList<QGraphicsItem *> items = m_item->childItems();
418
 
 
419
 
    Q_FOREACH(QDeclarativeItem *i, m_items) {
420
 
        QDeclarativeItem *di = qobject_cast<QDeclarativeItem *>(i);
421
 
        if (!items.contains(di)) {
422
 
            removeItem(i);
423
 
        }
424
 
    }
425
 
 
426
 
    Q_FOREACH(QGraphicsItem *i, items) {
427
 
        QGraphicsObject *obj = i->toGraphicsObject();
428
 
        if (obj) {
429
 
            QDeclarativeItem *di = qobject_cast<QDeclarativeItem *>(obj);
430
 
            if (!m_items.contains(di)) {
431
 
                addItem(di);
432
 
            }
433
 
        }
434
 
    }
435
 
}
436
 
 
437
 
void FocusPath::updateItem(QDeclarativeItem* item)
438
 
{
439
 
    if (item == m_item)
440
 
        return;
441
 
 
442
 
    if (m_item) {
443
 
        m_item->removeEventFilter(this);
444
 
        Q_FOREACH(QGraphicsItem *c, m_item->childItems()) {
445
 
            QGraphicsObject *obj = c->toGraphicsObject();
446
 
            if (obj) {
447
 
                QDeclarativeItem *ci = qobject_cast<QDeclarativeItem *>(obj);
448
 
                removeItem(ci);
449
 
            }
450
 
        }
451
 
        Q_ASSERT(m_item);
452
 
        QObject::disconnect(m_item, SIGNAL(destroyed(QObject*)), this, SLOT(onItemDestroyed()));
453
 
        QObject::disconnect(m_item, SIGNAL(childrenChanged()), this, SLOT(onChildrenChanged()));
454
 
    }
455
 
    m_item = item;
456
 
    if (m_item) {
457
 
        Q_FOREACH(QGraphicsItem *c, m_item->childItems()) {
458
 
            QGraphicsObject *obj = c->toGraphicsObject();
459
 
            if (obj) {
460
 
                QDeclarativeItem *ci = qobject_cast<QDeclarativeItem *>(obj);
461
 
                addItem(ci);
462
 
            }
463
 
        }
464
 
        m_item->installEventFilter(this);
465
 
        QObject::connect(m_item, SIGNAL(destroyed(QObject*)), this, SLOT(onItemDestroyed()));
466
 
        QObject::connect(m_item, SIGNAL(childrenChanged()), this, SLOT(onChildrenChanged()));
467
 
    }
468
 
}
469
 
 
470
 
void FocusPath::onItemDestroyed()
471
 
{
472
 
    Q_FOREACH(QDeclarativeItem *i, m_items) {
473
 
        removeItem(i);
474
 
    }
475
 
    m_item = 0;
476
 
}
477
 
 
478
 
FocusPathAttached *FocusPath::qmlAttachedProperties(QObject *object)
479
 
{
480
 
    return new FocusPathAttached(object);
481
 
}
482
 
 
483
 
FocusPathAttached::FocusPathAttached(QObject *object)
484
 
    : QObject(object),
485
 
      m_index(0),
486
 
      m_skip(false)
487
 
{
488
 
}
489
 
 
490
 
int FocusPathAttached::index() const
491
 
{
492
 
    return m_index;
493
 
}
494
 
 
495
 
bool FocusPathAttached::skip() const
496
 
{
497
 
    return m_skip;
498
 
}
499
 
 
500
 
void FocusPathAttached::setIndex(int index)
501
 
{
502
 
    if (m_index != index) {
503
 
        m_index = index;
504
 
        Q_EMIT indexChanged();
505
 
    }
506
 
}
507
 
 
508
 
void FocusPathAttached::setSkip(bool skip)
509
 
{
510
 
    if (m_skip != skip) {
511
 
        m_skip = skip;
512
 
        Q_EMIT skipChanged();
513
 
    }
514
 
}
515
 
 
516
 
#include "focuspath.moc"