~ubuntu-branches/ubuntu/utopic/kde-workspace/utopic-proposed

« back to all changes in this revision

Viewing changes to plasma/desktop/applets/tasks/taskitemlayout.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Michał Zając
  • Date: 2011-07-09 08:31:15 UTC
  • Revision ID: james.westby@ubuntu.com-20110709083115-ohyxn6z93mily9fc
Tags: upstream-4.6.90
Import upstream version 4.6.90

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2008 by Christian Mollekopf chrigi_1@fastmail.fm        *
 
3
 *                                                                         *
 
4
 *   This program is free software; you can redistribute it and/or modify  *
 
5
 *   it under the terms of the GNU General Public License as published by  *
 
6
 *   the Free Software Foundation; either version 2 of the License, or     *
 
7
 *   (at your option) any later version.                                   *
 
8
 *                                                                         *
 
9
 *   This program is distributed in the hope that it will be useful,       *
 
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 
12
 *   GNU General Public License for more details.                          *
 
13
 *                                                                         *
 
14
 *   You should have received a copy of the GNU General Public License     *
 
15
 *   along with this program; if not, write to the                         *
 
16
 *   Free Software Foundation, Inc.,                                       *
 
17
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA .        *
 
18
 ***************************************************************************/
 
19
 
 
20
#include "taskitemlayout.h"
 
21
 
 
22
//Taskmanager
 
23
#include <taskmanager/taskmanager.h>
 
24
#include <taskmanager/abstractgroupableitem.h>
 
25
#include <taskmanager/groupmanager.h>
 
26
 
 
27
// Qt
 
28
#include <QGraphicsScene>
 
29
#include <QGraphicsGridLayout>
 
30
 
 
31
// KDE
 
32
#include <KDebug>
 
33
 
 
34
#include <math.h>
 
35
 
 
36
#include "windowtaskitem.h"
 
37
#include "taskgroupitem.h"
 
38
 
 
39
 
 
40
//GroupItem Constructor
 
41
TaskItemLayout::TaskItemLayout(TaskGroupItem *parent, Tasks *applet)
 
42
    : QGraphicsGridLayout(0),
 
43
      m_hasSpacer(false),
 
44
      m_spacer(0),
 
45
      m_groupItem(parent),
 
46
      m_rowSize(1),
 
47
      m_maxRows(1),
 
48
      m_forceRows(false),
 
49
      m_applet(applet),
 
50
      m_layoutOrientation(Qt::Horizontal)
 
51
{
 
52
    setContentsMargins(0,0,0,0);
 
53
    setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding));
 
54
    setMaximumSize(INT_MAX,INT_MAX);
 
55
    //kDebug();
 
56
    foreach (AbstractTaskItem *item, m_groupItem->members()) {
 
57
        addTaskItem(item);
 
58
    }
 
59
}
 
60
 
 
61
TaskItemLayout::~TaskItemLayout()
 
62
{
 
63
    //kDebug();
 
64
}
 
65
 
 
66
void TaskItemLayout::setOrientation(Plasma::FormFactor orientation)
 
67
{
 
68
    Qt::Orientation oldOrientation = m_layoutOrientation;
 
69
 
 
70
    if (orientation == Plasma::Vertical) {
 
71
        m_layoutOrientation = Qt::Vertical;
 
72
    } else {
 
73
        m_layoutOrientation = Qt::Horizontal;
 
74
    }
 
75
 
 
76
    if (m_layoutOrientation != oldOrientation) {
 
77
        layoutItems();
 
78
    }
 
79
}
 
80
 
 
81
 
 
82
 
 
83
void TaskItemLayout::addTaskItem(AbstractTaskItem * item)
 
84
{
 
85
    //kDebug();
 
86
    if (!item) {
 
87
        kDebug() << "invalid item";
 
88
        return;
 
89
    }
 
90
 
 
91
    if (m_itemPositions.contains(item)) {
 
92
        //kDebug() << "already in this layout";
 
93
        return;
 
94
    }
 
95
 
 
96
    if (m_groupItem->scene() && !item->scene()) {
 
97
        //kDebug() << "layout widget got scene"<<m_groupItem->scene()<< "add item to scene" <<item->scene();
 
98
        m_groupItem->scene()->addItem(item);
 
99
        //kDebug() << "itemScene" << item->scene();
 
100
    }
 
101
 
 
102
    if (!insert(m_groupItem->indexOf(item, false), item)) {
 
103
        kDebug() << "error on  insert";
 
104
        return;
 
105
    }
 
106
 
 
107
    item->show();
 
108
    //kDebug() << "end";
 
109
}
 
110
 
 
111
void TaskItemLayout::removeTaskItem(AbstractTaskItem *item)
 
112
{
 
113
    if (!remove(item)) {
 
114
        return;
 
115
    }
 
116
 
 
117
    //kDebug();
 
118
 
 
119
    if (m_groupItem->scene()) {
 
120
        //kDebug() << "got scene";
 
121
        m_groupItem->scene()->removeItem(item);
 
122
    } else {
 
123
        kDebug() << "No Scene available";
 
124
    }
 
125
    //kDebug() << "done";
 
126
}
 
127
 
 
128
bool TaskItemLayout::insert(int index, AbstractTaskItem *item)
 
129
{
 
130
    //kDebug() << item->text() << index;
 
131
    if (!item) {
 
132
        kDebug() << "error";
 
133
        return false;
 
134
    }
 
135
 
 
136
    int listIndex;
 
137
    for (listIndex = 0; listIndex < m_itemPositions.size(); listIndex++) {
 
138
        if (index <= m_groupItem->indexOf(m_itemPositions.at(listIndex), false)) {
 
139
            break;
 
140
        }
 
141
    }
 
142
 
 
143
    if (m_itemPositions.removeAll(item) == 0) {
 
144
        connect(item, SIGNAL(destroyed(AbstractTaskItem*)), this, SLOT(remove(AbstractTaskItem*)));
 
145
    }
 
146
 
 
147
    m_itemPositions.insert(listIndex, item);
 
148
 
 
149
    layoutItems();
 
150
    return true;
 
151
}
 
152
 
 
153
bool TaskItemLayout::remove(AbstractTaskItem* item)
 
154
{
 
155
    if (!item) {
 
156
        kDebug() << "null Item";
 
157
        layoutItems();
 
158
        return false;
 
159
    }
 
160
 
 
161
    disconnect(item, 0, this, 0);
 
162
    m_itemPositions.removeAll(item);
 
163
    layoutItems();
 
164
    return true;
 
165
}
 
166
 
 
167
 
 
168
/** size including expanded groups*/
 
169
int TaskItemLayout::size()
 
170
{
 
171
    int groupSize = 0;
 
172
 
 
173
    foreach (AbstractTaskItem *item, m_groupItem->members()) {
 
174
        if (!item->abstractItem()) {
 
175
            // this item is a startup task or the task no longer exists
 
176
            kDebug() << "Error, invalid item in groupMembers";
 
177
            continue;
 
178
        }
 
179
 
 
180
        if (item->abstractItem()->itemType() == TaskManager::GroupItemType) {
 
181
            TaskGroupItem *group = static_cast<TaskGroupItem*>(item);
 
182
            if (!group->collapsed()) {
 
183
                TaskItemLayout *layout = dynamic_cast<TaskItemLayout*>(group->tasksLayout());
 
184
                if (!layout) {
 
185
                    kDebug() << "Error group has no layout";
 
186
                    continue;
 
187
                }
 
188
 
 
189
                // increase number of items since expanded groups occupy several spaces
 
190
                groupSize += layout->size();
 
191
                continue;
 
192
            }
 
193
        }
 
194
 
 
195
        ++groupSize;
 
196
    }
 
197
 
 
198
    //kDebug() << "group size" << groupSize;
 
199
    return groupSize;
 
200
}
 
201
 
 
202
//return maximum colums set by the user unless the setting is to high and the items would get unusable
 
203
int TaskItemLayout::maximumRows()
 
204
{
 
205
    int maxRows;
 
206
    if (m_itemPositions.isEmpty()) {
 
207
        return 1;
 
208
    }
 
209
 
 
210
    if (m_forceRows) {
 
211
        return m_maxRows;
 
212
    }
 
213
 
 
214
    // in this case rows are columns, columns are rows...
 
215
    //TODO basicPreferredSize isn't the optimal source here because  it changes because of margins probably
 
216
    QSizeF itemSize = m_itemPositions.first()->basicPreferredSize();
 
217
    if (m_layoutOrientation == Qt::Vertical) {
 
218
        maxRows = qMin(qMax(1, int(m_groupItem->geometry().width() / itemSize.width())), m_maxRows);
 
219
    } else {
 
220
        maxRows = qMin(qMax(1, int(m_groupItem->geometry().height() / itemSize.height())), m_maxRows);
 
221
    }
 
222
 
 
223
    //kDebug() << "maximum rows: " << maxRows << m_maxRows << m_groupItem->geometry().height() << itemSize.height();
 
224
    return maxRows;
 
225
}
 
226
 
 
227
//returns a reasonable amount of columns
 
228
int TaskItemLayout::preferredColumns()
 
229
{
 
230
    if (m_forceRows) {
 
231
        m_rowSize = 1;
 
232
    } else {
 
233
        if (m_itemPositions.isEmpty()) {
 
234
            return 1;
 
235
        }
 
236
 
 
237
        //TODO basicPreferredSize isn't the optimal source here because  it changes because of margins probably
 
238
        QSizeF itemSize = m_itemPositions.first()->basicPreferredSize();
 
239
        //kDebug() << itemSize.width() << m_groupItem->geometry().width();
 
240
        if (m_layoutOrientation == Qt::Vertical) {
 
241
            m_rowSize = qMax(1, int(m_groupItem->geometry().height() / itemSize.height()));
 
242
        } else {
 
243
            //Launchers doesn't need the same space as task- and groupitems on horizontal Layouts so the size needs to be adjusted
 
244
            qreal horizontalSpace = m_groupItem->geometry().width();
 
245
            int numberOflaunchers = 0;
 
246
            foreach (AbstractTaskItem *item, m_itemPositions) {
 
247
                if (item->abstractItem() && item->abstractItem()->itemType() == TaskManager::LauncherItemType) {
 
248
                    horizontalSpace -= item->preferredHeight(); //The icon is a square so we can use the height as width
 
249
                    numberOflaunchers++;
 
250
                }
 
251
            }
 
252
            m_rowSize = qMax(1, int(horizontalSpace / itemSize.width()));
 
253
            m_rowSize += numberOflaunchers;
 
254
        }
 
255
    }
 
256
    //kDebug() << "preferred columns: " << qMax(1, m_rowSize);
 
257
    return qMax(1, m_rowSize);
 
258
}
 
259
 
 
260
// <columns,rows>
 
261
QPair<int, int> TaskItemLayout::gridLayoutSize()
 
262
{
 
263
    int groupSize = size();
 
264
    //the basic settings
 
265
    int columns = preferredColumns();
 
266
    int maxRows = maximumRows();
 
267
 
 
268
    //check for adjustments on columns because there isnt room enough yet for all of the items
 
269
    while (ceil(static_cast<float>(groupSize)/static_cast<float>(columns)) > maxRows) {
 
270
        columns++;  // more rows needed than allowed so we add some columns instead
 
271
    }
 
272
    //kDebug() << "groupWidth" << columns << maxRows << m_maxRows;
 
273
    int rows;
 
274
    if (m_forceRows) {
 
275
        rows = maxRows;
 
276
    } else {
 
277
        rows = ceil(static_cast<float>(groupSize) / static_cast<float>(columns)); //actually needed rows
 
278
    }
 
279
 
 
280
    return QPair<int,int>(columns, rows);
 
281
}
 
282
 
 
283
 
 
284
void TaskItemLayout::layoutItems()
 
285
{
 
286
    //kDebug();
 
287
 
 
288
    QPair<int,int> grid = gridLayoutSize();
 
289
    int columns = qMax(grid.first, 1);
 
290
    //int rows = qMax(grid.second, 1);
 
291
 
 
292
    //kDebug() << "Laying out with" << columns << rows;
 
293
    //kDebug() << "geometry" << m_groupItem->geometry();
 
294
    //int rowHeight = qMax(1, int(m_groupItem->geometry().height() / rows));
 
295
    //kDebug() << "rowHeight" << rowHeight;
 
296
    //int columnWidth = qMax(1, int(m_groupItem->geometry().size().width() / columns));
 
297
    //kDebug() << "column width set to " << columnWidth;
 
298
 
 
299
    //FIXME: resetting column preferred sizesthey shouldn't be taken into account for inexistent ones but they are, probably upstream issue
 
300
    for (int i = 0; i < columnCount(); ++i) {
 
301
        setColumnMaximumWidth(i, 0);
 
302
        setColumnPreferredWidth(i, 0);
 
303
    }
 
304
 
 
305
    for (int i = 0; i < rowCount(); ++i) {
 
306
        setRowMaximumHeight(i, 0);
 
307
        setRowPreferredHeight(i, 0);
 
308
    }
 
309
 
 
310
    //clearLayout
 
311
    while (count()) {
 
312
        removeAt(0);
 
313
    }
 
314
 
 
315
    QSizeF maximumCellSize;
 
316
    if (!m_itemPositions.isEmpty()) {
 
317
        maximumCellSize = m_itemPositions.first()->basicPreferredSize() * 1.8;
 
318
    }
 
319
 
 
320
    setHorizontalSpacing(0);
 
321
    setVerticalSpacing(0);
 
322
 
 
323
    //go through all items of this layoutwidget and populate the layout with items
 
324
    int numberOfItems = 0;
 
325
    foreach (AbstractTaskItem *item, m_itemPositions) {
 
326
        int row;
 
327
        int col;
 
328
        if (m_layoutOrientation == Qt::Vertical) {
 
329
            row = numberOfItems % columns;
 
330
            col = numberOfItems / columns;
 
331
        } else {
 
332
            row = numberOfItems / columns;
 
333
            col = numberOfItems % columns;
 
334
        }
 
335
 
 
336
        //not good if we don't recreate the layout every time
 
337
        //m_layout->setColumnPreferredWidth(col, columnWidth);//Somehow this line is absolutely crucial
 
338
        //m_layout->setRowPreferredHeight(row, rowHeight);//Somehow this line is absolutely crucial
 
339
 
 
340
 
 
341
        //FIXME: this is a glorious hack
 
342
        if (maximumCellSize.isValid()) {
 
343
            if (m_layoutOrientation == Qt::Vertical) {
 
344
                setRowMaximumHeight(row, maximumCellSize.height());
 
345
                setColumnMaximumWidth(col, QWIDGETSIZE_MAX);
 
346
            } else {
 
347
                if (item->abstractItem() && item->abstractItem()->itemType() == TaskManager::LauncherItemType) {
 
348
                    setColumnFixedWidth(col, maximumCellSize.height()); //The Icon size is a sqare, so it needs the same width as height
 
349
                } else {
 
350
                    setColumnMaximumWidth(col, maximumCellSize.width());
 
351
                }
 
352
                setRowMaximumHeight(row, QWIDGETSIZE_MAX);
 
353
            }
 
354
            setRowPreferredHeight(row, maximumCellSize.height());
 
355
            setColumnPreferredWidth(col, maximumCellSize.width());
 
356
        }
 
357
 
 
358
        if (item->abstractItem() &&
 
359
            item->abstractItem()->itemType() == TaskManager::GroupItemType) {
 
360
 
 
361
            TaskGroupItem *group = static_cast<TaskGroupItem*>(item);
 
362
            if (group->collapsed()) {
 
363
                group->unsplitGroup();
 
364
                addItem(item, row, col, 1, 1);
 
365
                numberOfItems++;
 
366
            } else {
 
367
                TaskItemLayout *layout = group->tasksLayout();
 
368
                if (!layout) {
 
369
                    kDebug() << "group has no valid layout";
 
370
                    continue;
 
371
                }
 
372
 
 
373
                int groupRowWidth = m_layoutOrientation == Qt::Vertical ? layout->numberOfRows() : layout->numberOfColumns();
 
374
 
 
375
                if ((columns - col) < groupRowWidth) {
 
376
                    //we need to split the group
 
377
                    int splitIndex = columns - col;//number of items in group that are on this row
 
378
                    if (m_layoutOrientation == Qt::Vertical) {
 
379
                        addItem(item, row, col, splitIndex, 1);
 
380
                    } else {
 
381
                        addItem(item, row, col, 1, splitIndex);
 
382
                    }
 
383
 
 
384
                    //kDebug() << "add normal item: split index = column span " << splitIndex;
 
385
                    TaskGroupItem *splitChild = group->splitGroup(splitIndex);
 
386
                    if (splitChild) {
 
387
                        //also add the second part of the group if there is one
 
388
                        if (m_layoutOrientation == Qt::Vertical) {
 
389
                            addItem(splitChild, 0, col + 1, groupRowWidth - splitIndex, 1);
 
390
                        } else {
 
391
                            addItem(splitChild, row + 1, 0, 1, groupRowWidth - splitIndex);
 
392
                        }
 
393
                    }
 
394
                    //kDebug() << "add split item: column span " << groupRowWidth - splitIndex;
 
395
                } else  {
 
396
                    //Add the normal item
 
397
                    group->unsplitGroup();
 
398
 
 
399
                    if (m_layoutOrientation == Qt::Vertical) {
 
400
                        addItem(item, row, col, groupRowWidth, 1);
 
401
                    } else {
 
402
                        addItem(item, row, col, 1, groupRowWidth);
 
403
                    }
 
404
                    //kDebug() << "add unsplit expanded item over columns " << groupRowWidth;
 
405
                }
 
406
 
 
407
                numberOfItems += groupRowWidth;
 
408
            }
 
409
        } else {
 
410
            addItem(item, row, col, 1, 1);
 
411
            numberOfItems++;
 
412
        }
 
413
 
 
414
        //kDebug() << "addItem at: " << row  <<  col;
 
415
    }
 
416
 
 
417
    updatePreferredSize();
 
418
    //m_groupItem->setLayout(m_layout);
 
419
}
 
420
 
 
421
 
 
422
void TaskItemLayout::updatePreferredSize()
 
423
{
 
424
    //kDebug() << "column count: " << m_layout->columnCount();
 
425
 
 
426
    if (count() > 0) {
 
427
        QSizeF s = itemAt(0)->preferredSize();
 
428
        //kDebug() << s << columnCount();
 
429
        setPreferredSize(s.width() * columnCount(), s.height() * rowCount());
 
430
    } else {
 
431
        //Empty taskbar, arbitrary small value
 
432
        kDebug() << "Empty layout!!!!!!!!!!!!!!!!!!";
 
433
        if (m_layoutOrientation == Qt::Vertical) {
 
434
            setPreferredSize(/*m_layout->preferredSize().width()*/10, 10); //since we recreate the layout we don't have the previous values
 
435
        } else {
 
436
            setPreferredSize(10, /*m_layout->preferredSize().height()*/10);
 
437
        }
 
438
    }
 
439
    //kDebug() << "preferred size: " << m_layout->preferredSize();
 
440
    m_groupItem->updatePreferredSize();
 
441
}
 
442
 
 
443
void TaskItemLayout::setMaximumRows(int rows)
 
444
{
 
445
    if (rows != m_maxRows) {
 
446
        m_maxRows = rows;
 
447
        layoutItems();
 
448
    }
 
449
}
 
450
 
 
451
void TaskItemLayout::setForceRows(bool forceRows)
 
452
{
 
453
    m_forceRows = forceRows;
 
454
}
 
455
 
 
456
int TaskItemLayout::insertionIndexAt(const QPointF &pos)
 
457
{
 
458
    int insertIndex = -1;
 
459
    int row = numberOfRows();
 
460
    int col = numberOfColumns();
 
461
 
 
462
    //if pos is (-1,-1) insert at the end of the panel
 
463
    if (pos.toPoint() == QPoint(-1, -1)) {
 
464
        kDebug() << "Error";
 
465
        return -1;
 
466
    } else {
 
467
        QRectF siblingGeometry;
 
468
 
 
469
        //get correct row
 
470
        for (int i = 0; i < numberOfRows(); i++) {
 
471
            if (m_layoutOrientation == Qt::Vertical) {
 
472
                siblingGeometry = itemAt(0, i)->geometry();//set geometry of single item
 
473
                if (pos.x() <= siblingGeometry.right()) {
 
474
                    row = i;
 
475
                    break;
 
476
                }
 
477
            } else {
 
478
                siblingGeometry = itemAt(i, 0)->geometry();//set geometry of single item
 
479
                if (pos.y() <= siblingGeometry.bottom()) {
 
480
                    row = i;
 
481
                    break;
 
482
                }
 
483
            }
 
484
        }
 
485
        //and column
 
486
        for (int i = 0; i < numberOfColumns(); i++) {
 
487
            if (m_layoutOrientation == Qt::Vertical) {
 
488
                siblingGeometry = itemAt(i, 0)->geometry();//set geometry of single item
 
489
                qreal vertMiddle = (siblingGeometry.top() + siblingGeometry.bottom()) / 2.0;
 
490
                if (pos.y() < vertMiddle) {
 
491
                    col = i;
 
492
                    break;
 
493
                }
 
494
 
 
495
            } else if (itemAt(0, i)) {
 
496
                siblingGeometry = itemAt(0, i)->geometry();//set geometry of single item
 
497
                qreal horizMiddle = (siblingGeometry.left() + siblingGeometry.right()) / 2.0;
 
498
                //kDebug() << "pos middle " << pos.x() << horizMiddle;
 
499
                if (pos.x() < horizMiddle) {
 
500
                    col = i;
 
501
                    break;
 
502
                }
 
503
            }
 
504
        }
 
505
    }
 
506
 
 
507
    //kDebug() << row << col;
 
508
 
 
509
    insertIndex = row * numberOfColumns() + col;
 
510
 
 
511
    if (insertIndex > count()) {
 
512
        insertIndex--;
 
513
        //kDebug() << "correction";
 
514
    }
 
515
 
 
516
    //kDebug() << "insert Index" << insertIndex;
 
517
    return insertIndex;
 
518
}
 
519
 
 
520
int TaskItemLayout::numberOfRows()
 
521
{
 
522
    if (m_layoutOrientation == Qt::Vertical) {
 
523
        return columnCount();
 
524
    } else {
 
525
        return rowCount();
 
526
    }
 
527
}
 
528
 
 
529
int TaskItemLayout::numberOfColumns()
 
530
{
 
531
    if (m_layoutOrientation == Qt::Vertical) {
 
532
        return rowCount();
 
533
    } else {
 
534
        return columnCount();
 
535
    }
 
536
}
 
537
 
 
538
#include "taskitemlayout.moc"
 
539