~ubuntu-branches/ubuntu/oneiric/partitionmanager/oneiric

« back to all changes in this revision

Viewing changes to src/gui/partresizerwidget.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Mercatante
  • Date: 2009-01-23 17:57:36 UTC
  • Revision ID: james.westby@ubuntu.com-20090123175736-2ltrhgg3m55dokbm
Tags: upstream-1.0.0~beta1a
ImportĀ upstreamĀ versionĀ 1.0.0~beta1a

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
 *   Copyright (C) 2008 by Volker Lanz <vl@fidra.de>                       *
 
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 "gui/partresizerwidget.h"
 
21
#include "gui/partwidget.h"
 
22
 
 
23
#include "core/partition.h"
 
24
#include "core/device.h"
 
25
 
 
26
#include "fs/filesystem.h"
 
27
 
 
28
#include <QPainter>
 
29
#include <QMouseEvent>
 
30
#include <QPaintEvent>
 
31
#include <QResizeEvent>
 
32
 
 
33
#include <kdebug.h>
 
34
#include <kcolorscheme.h>
 
35
 
 
36
const qint32 PartResizerWidget::m_HandleWidth = 16;
 
37
const qint32 PartResizerWidget::m_HandleHeight = 59;
 
38
 
 
39
/** Creates a new PartResizerWidget
 
40
 
 
41
        Initializing is mostly done in init().
 
42
 
 
43
        @param parent pointer to the parent widget
 
44
*/
 
45
PartResizerWidget::PartResizerWidget(QWidget* parent) :
 
46
        QWidget(parent),
 
47
        m_Device(NULL),
 
48
        m_Partition(NULL),
 
49
        m_PartWidget(NULL),
 
50
        m_SectorsBefore(0),
 
51
        m_SectorsAfter(0),
 
52
        m_TotalSectors(-1),
 
53
        m_MinimumSectors(-1),
 
54
        m_MaximumSectors(-1),
 
55
        m_MaxFirstSector(-1),
 
56
        m_MinLastSector(-1),
 
57
        m_LeftHandle(this),
 
58
        m_RightHandle(this),
 
59
        m_DraggedWidget(NULL),
 
60
        m_Hotspot(0),
 
61
        m_MoveAllowed(true),
 
62
        m_ReadOnly(false)
 
63
{
 
64
}
 
65
 
 
66
/** Intializes the PartResizerWidget
 
67
        @param d the Device the Partition is on
 
68
        @param p the Partition to show and/or resize
 
69
        @param freeBefore number of sectors free before the Partition
 
70
        @param freeAfter number of sectors free after the Partition
 
71
*/
 
72
void PartResizerWidget::init(Device& d, Partition& p, qint64 freeBefore, qint64 freeAfter)
 
73
{
 
74
        setDevice(d);
 
75
        setPartition(p);
 
76
 
 
77
        setSectorsBefore(freeBefore);
 
78
        setSectorsAfter(freeAfter);
 
79
 
 
80
        setTotalSectors(sectorsBefore() + partition().length() + sectorsAfter());
 
81
 
 
82
        setMinimumSectors(qMax(partition().sectorsUsed(), partition().minimumSectors()));
 
83
        setMaximumSectors(qMin(totalSectors(), partition().maximumSectors()));
 
84
 
 
85
        /** @todo get real pixmaps for the handles */
 
86
        QPixmap pixmap(handleWidth(), handleHeight());
 
87
        pixmap.fill(QColor(0x44, 0x44, 0x44));
 
88
 
 
89
        leftHandle().setPixmap(pixmap);
 
90
        rightHandle().setPixmap(pixmap);
 
91
        leftHandle().setFixedSize(handleWidth(), handleHeight());
 
92
        rightHandle().setFixedSize(handleWidth(), handleHeight());
 
93
 
 
94
        delete m_PartWidget;
 
95
        m_PartWidget = new PartWidget(this, NULL, &partition());
 
96
 
 
97
        if (!readOnly())
 
98
        {
 
99
                leftHandle().setCursor(Qt::SizeHorCursor);
 
100
                rightHandle().setCursor(Qt::SizeHorCursor);
 
101
 
 
102
                if (moveAllowed())
 
103
                        partWidget().setCursor(Qt::SizeAllCursor);
 
104
 
 
105
                partWidget().setToolTip(QString());
 
106
        }
 
107
 
 
108
        updatePositions();
 
109
}
 
110
 
 
111
qint64 PartResizerWidget::sectorsPerPixel() const
 
112
{
 
113
        return totalSectors() / (width() - 2 * handleWidth());
 
114
}
 
115
 
 
116
int PartResizerWidget::partWidgetStart() const
 
117
{
 
118
        return handleWidth() + sectorsBefore() / sectorsPerPixel();
 
119
}
 
120
 
 
121
int PartResizerWidget::partWidgetWidth() const
 
122
{
 
123
        return partition().length() / sectorsPerPixel();
 
124
}
 
125
 
 
126
void PartResizerWidget::updatePositions()
 
127
{
 
128
        partWidget().move(partWidgetStart(), 0);
 
129
        partWidget().resize(partWidgetWidth(), height() - 1);
 
130
        leftHandle().move(partWidgetStart() - leftHandle().width(), 0);
 
131
        rightHandle().move(partWidgetStart() + partWidgetWidth(), 0);
 
132
 
 
133
        partWidget().update();
 
134
}
 
135
 
 
136
void PartResizerWidget::resizeEvent(QResizeEvent* event)
 
137
{
 
138
        updatePositions();
 
139
        QWidget::resizeEvent(event);
 
140
}
 
141
 
 
142
void PartResizerWidget::paintEvent(QPaintEvent*)
 
143
{
 
144
        QPainter painter(this);
 
145
        painter.setPen(Qt::NoPen);
 
146
        painter.setBrush(QColor(0x99, 0x99, 0x99));
 
147
        painter.drawRect(QRect(handleWidth(), 0, width() - (2 * handleWidth()) - 1, height() - 1));
 
148
}
 
149
 
 
150
void PartResizerWidget::mousePressEvent(QMouseEvent* event)
 
151
{
 
152
        if (readOnly())
 
153
                return;
 
154
 
 
155
        if (event->button() == Qt::LeftButton)
 
156
        {
 
157
                m_DraggedWidget = static_cast<QWidget*>(childAt(event->pos()));
 
158
 
 
159
                if (m_DraggedWidget != NULL)
 
160
                {
 
161
                        if (partWidget().isAncestorOf(m_DraggedWidget))
 
162
                                m_DraggedWidget = &partWidget();
 
163
 
 
164
                        m_Hotspot = m_DraggedWidget->mapFromParent(event->pos()).x();
 
165
                }
 
166
        }
 
167
}
 
168
 
 
169
void PartResizerWidget::mouseMoveEvent(QMouseEvent* event)
 
170
{
 
171
        int x = event->pos().x() - m_Hotspot;
 
172
 
 
173
        if (draggedWidget() == &leftHandle())
 
174
        {
 
175
                const qint64 newSectorsBefore = qMax(x * sectorsPerPixel(), 0LL);
 
176
                updateSectorsBefore(newSectorsBefore);
 
177
        }
 
178
        else if (draggedWidget() == &rightHandle())
 
179
        {
 
180
                const qint64 newSectorsAfter = qMax((width() - rightHandle().width() - x) * sectorsPerPixel(), 0LL);
 
181
                updateSectorsAfter(newSectorsAfter);
 
182
        }
 
183
        else if (draggedWidget() == &partWidget())
 
184
        {
 
185
                x -= handleWidth();
 
186
                qint64 newSectorsBefore = qMax(x * sectorsPerPixel(), 0LL);
 
187
                qint64 newSectorsAfter = sectorsAfter() + sectorsBefore() - newSectorsBefore;
 
188
 
 
189
                if (newSectorsAfter < 0)
 
190
                {
 
191
                        newSectorsAfter = 0;
 
192
                        newSectorsBefore = sectorsBefore() + sectorsAfter();
 
193
                }
 
194
 
 
195
                if (newSectorsBefore != sectorsBefore() && newSectorsAfter != sectorsAfter())
 
196
                        updateSectors(newSectorsBefore, newSectorsAfter);
 
197
        }
 
198
}
 
199
 
 
200
/** Updates the start and end sector of the Partition.
 
201
        @param newSectorsBefore new value for free sectors before the Partition
 
202
        @param newSectorsAfter new value for free sectors after the Partition
 
203
        @return true on success
 
204
*/
 
205
bool PartResizerWidget::updateSectors(qint64 newSectorsBefore, qint64 newSectorsAfter)
 
206
{
 
207
        Q_ASSERT(newSectorsBefore >= 0);
 
208
        Q_ASSERT(newSectorsAfter >= 0);
 
209
        Q_ASSERT(newSectorsBefore + newSectorsAfter + partition().length() == totalSectors());
 
210
 
 
211
        if (newSectorsBefore < 0 || newSectorsAfter < 0)
 
212
        {
 
213
                kWarning() << "new sectors before partition: " << newSectorsBefore;
 
214
                kWarning() << "new sectors after partition: " << newSectorsBefore;
 
215
                return false;
 
216
        }
 
217
 
 
218
        if (newSectorsBefore + newSectorsAfter + partition().length() != totalSectors())
 
219
        {
 
220
                kWarning() << "total sectors: " << totalSectors();
 
221
                kWarning() << "new sectors before partition: " << newSectorsBefore;
 
222
                kWarning() << "new sectors after partition: " << newSectorsBefore;
 
223
                kWarning() << "partition length: " << partition().length();
 
224
                return false;
 
225
        }
 
226
 
 
227
        if (!moveAllowed())
 
228
                return false;
 
229
 
 
230
        const qint64 oldBefore = sectorsBefore();
 
231
        const qint64 oldAfter = sectorsAfter();
 
232
 
 
233
        // Two hacky things about updating free sectors before and after a partition in one go:
 
234
        // 1) We have to call updateSectorsBefore and updateSectorsAfter with length-checking disabled,
 
235
        //    because the partition might in between those calls get smaller or bigger than
 
236
        //    allowed. The second call will, of course, restore the original length.
 
237
        // 2) If the user moves the mouse fast enough, it's possible to move the beginning past the
 
238
        //    end or the end in front of the beginning of the partition in between the calls. Both
 
239
        //    methods won't allow that and return false in that case. We try moving the beginning first and
 
240
        //    just move the end first if that fails.
 
241
        if (!updateSectorsBefore(newSectorsBefore, false))
 
242
        {
 
243
                updateSectorsAfter(newSectorsAfter, false);
 
244
                updateSectorsBefore(newSectorsBefore, false);
 
245
        }
 
246
        else
 
247
                updateSectorsAfter(newSectorsAfter, false);
 
248
 
 
249
        bool rval = false;
 
250
 
 
251
        if (oldBefore != sectorsBefore())
 
252
        {
 
253
                rval = true;
 
254
                emit sectorsBeforeChanged(sectorsBefore());
 
255
        }
 
256
 
 
257
        if (oldAfter != sectorsAfter())
 
258
        {
 
259
                rval = true;
 
260
                emit sectorsAfterChanged(sectorsAfter());
 
261
        }
 
262
 
 
263
        if (rval)
 
264
                updatePositions();
 
265
 
 
266
        return rval;
 
267
}
 
268
 
 
269
void PartResizerWidget::mouseReleaseEvent(QMouseEvent* event)
 
270
{
 
271
        if (event->button() == Qt::LeftButton)
 
272
                m_DraggedWidget = NULL;
 
273
}
 
274
 
 
275
/** Updates the free sectors before the Partition
 
276
        @param newSectorsBefore new value for number of sectors free before Partition
 
277
        @param enableLengthCheck true if the method is supposed to do some sanity checking on the Partition length
 
278
        @return true on success
 
279
*/
 
280
bool PartResizerWidget::updateSectorsBefore(qint64 newSectorsBefore, bool enableLengthCheck)
 
281
{
 
282
        Q_ASSERT(newSectorsBefore >= 0);
 
283
 
 
284
        if (newSectorsBefore < 0)
 
285
        {
 
286
                kWarning() << "new sectors before partition: " << newSectorsBefore;
 
287
                return false;
 
288
        }
 
289
 
 
290
        const qint64 oldSectorsBefore = sectorsBefore();
 
291
        const qint64 newLength = partition().length() + oldSectorsBefore - newSectorsBefore;
 
292
 
 
293
        if (enableLengthCheck)
 
294
        {
 
295
                if (newLength < minimumSectors())
 
296
                        newSectorsBefore -= minimumSectors() - newLength;
 
297
 
 
298
                if (newLength > maximumSectors())
 
299
                        newSectorsBefore += newLength - maximumSectors();
 
300
        }
 
301
        else if (newLength < 0)
 
302
                return false;
 
303
 
 
304
        qint64 newFirstSector = partition().firstSector() + newSectorsBefore - oldSectorsBefore;
 
305
 
 
306
        if (maxFirstSector() > -1 && newFirstSector > maxFirstSector())
 
307
        {
 
308
                newSectorsBefore -= newFirstSector - maxFirstSector();
 
309
                newFirstSector = maxFirstSector();
 
310
        }
 
311
 
 
312
        if (newSectorsBefore >= 0 && newSectorsBefore != oldSectorsBefore && (partition().children().size() == 0 || checkSnap(*partition().children().first(), oldSectorsBefore - newSectorsBefore)))
 
313
        {
 
314
                setSectorsBefore(newSectorsBefore);
 
315
 
 
316
                partition().setFirstSector(newFirstSector);
 
317
                partition().fileSystem().setFirstSector(newFirstSector);
 
318
 
 
319
                resizeLogicals();
 
320
 
 
321
                emit sectorsBeforeChanged(sectorsBefore());
 
322
                emit lengthChanged(partition().length());
 
323
 
 
324
                updatePositions();
 
325
 
 
326
                return true;
 
327
        }
 
328
 
 
329
        return false;
 
330
}
 
331
 
 
332
bool PartResizerWidget::checkSnap(const Partition& child, qint64 delta) const
 
333
{
 
334
        if (!partition().roles().has(PartitionRole::Extended))
 
335
                return true;
 
336
 
 
337
        if (child.roles().has(PartitionRole::Unallocated))
 
338
                return true;
 
339
 
 
340
        return qAbs(delta) >= device().cylinderSize();
 
341
}
 
342
 
 
343
void PartResizerWidget::resizeLogicals()
 
344
{
 
345
        if (!partition().roles().has(PartitionRole::Extended) || partition().children().size() == 0)
 
346
                return;
 
347
 
 
348
        Q_ASSERT(device().partitionTable());
 
349
 
 
350
        device().partitionTable()->removeUnallocated(&partition());
 
351
        device().partitionTable()->insertUnallocated(device(), &partition(), partition().firstSector());
 
352
 
 
353
        partWidget().updateChildren();
 
354
}
 
355
 
 
356
/** Updates the number of free sectors after the Partition.
 
357
        @param newSectorsAfter new value for number of sectors free after Partition
 
358
        @param enableLengthCheck true if the method is supposed to do some sanity checking on the Partition length
 
359
        @return true on success
 
360
*/
 
361
bool PartResizerWidget::updateSectorsAfter(qint64 newSectorsAfter, bool enableLengthCheck)
 
362
{
 
363
        Q_ASSERT(newSectorsAfter >= 0);
 
364
 
 
365
        if (newSectorsAfter < 0)
 
366
        {
 
367
                kWarning() << "new sectors after partition: " << newSectorsAfter;
 
368
                return false;
 
369
        }
 
370
 
 
371
        const qint64 oldSectorsAfter = sectorsAfter();
 
372
        const qint64 newLength = partition().length() + oldSectorsAfter - newSectorsAfter;
 
373
 
 
374
        if (enableLengthCheck)
 
375
        {
 
376
                if (newLength < minimumSectors())
 
377
                        newSectorsAfter -= minimumSectors() - newLength;
 
378
 
 
379
                if (newLength > maximumSectors())
 
380
                        newSectorsAfter += newLength - maximumSectors();
 
381
        }
 
382
        else if (newLength < 0)
 
383
                return false;
 
384
 
 
385
        qint64 newLastSector = partition().lastSector() + oldSectorsAfter - newSectorsAfter;
 
386
 
 
387
        if (minLastSector() > -1 && newLastSector < minLastSector())
 
388
        {
 
389
                newSectorsAfter += newLastSector - minLastSector();
 
390
                newLastSector = minLastSector();
 
391
        }
 
392
 
 
393
        if (newSectorsAfter >= 0 && newSectorsAfter != oldSectorsAfter && (partition().children().size() == 0 || checkSnap(*partition().children().last(), oldSectorsAfter - newSectorsAfter)))
 
394
        {
 
395
                setSectorsAfter(newSectorsAfter);
 
396
 
 
397
                partition().setLastSector(newLastSector);
 
398
                partition().fileSystem().setLastSector(newLastSector);
 
399
 
 
400
                resizeLogicals();
 
401
 
 
402
                emit sectorsAfterChanged(sectorsAfter());
 
403
                emit lengthChanged(partition().length());
 
404
 
 
405
                updatePositions();
 
406
 
 
407
                return true;
 
408
        }
 
409
 
 
410
        return false;
 
411
}
 
412
 
 
413
/** Updates the Partition's length
 
414
        @param newLength the new length
 
415
        @return true on success
 
416
*/
 
417
bool PartResizerWidget::updateLength(qint64 newLength)
 
418
{
 
419
        newLength = qBound(minimumSectors(), newLength, qMin(totalSectors(), maximumSectors()));
 
420
 
 
421
        if (newLength == partition().length())
 
422
                return false;
 
423
 
 
424
        const qint64 oldLength = partition().length();
 
425
        qint64 delta = newLength - oldLength;
 
426
 
 
427
        qint64 tmp = qMin(delta, sectorsAfter());
 
428
        delta -= tmp;
 
429
 
 
430
        if (tmp != 0)
 
431
        {
 
432
                setSectorsAfter(sectorsAfter() - tmp);
 
433
                partition().setLastSector(partition().lastSector() + tmp);
 
434
                partition().fileSystem().setLastSector(partition().lastSector() + tmp);
 
435
                emit sectorsAfterChanged(sectorsAfter());
 
436
        }
 
437
 
 
438
        tmp = qMin(delta, sectorsBefore());;
 
439
        delta -= tmp;
 
440
 
 
441
        if (tmp != 0)
 
442
        {
 
443
                setSectorsBefore(sectorsBefore() - tmp);
 
444
                partition().setFirstSector(partition().firstSector() - tmp);
 
445
                partition().fileSystem().setFirstSector(partition().firstSector() - tmp);
 
446
                emit sectorsBeforeChanged(sectorsBefore());
 
447
        }
 
448
 
 
449
        if (partition().length() != oldLength)
 
450
        {
 
451
                emit lengthChanged(partition().length());
 
452
                updatePositions();
 
453
 
 
454
                return true;
 
455
        }
 
456
 
 
457
        return false;
 
458
}
 
459
 
 
460
/** Sets the minimum sectors the Partition can be long.
 
461
        @note This value can never be less than 0 and never be higher than totalSectors()
 
462
        @param s the new minimum length
 
463
*/
 
464
void PartResizerWidget::setMinimumSectors(qint64 s)
 
465
{
 
466
        m_MinimumSectors = qBound(0LL, s, totalSectors());
 
467
}
 
468
 
 
469
/** Sets the maximum sectors the Partition can be long.
 
470
        @note This value can never be less than 0 and never by higher than totalSectors()
 
471
        @param s the new maximum length
 
472
*/
 
473
void PartResizerWidget::setMaximumSectors(qint64 s)
 
474
{
 
475
        m_MaximumSectors = qBound(0LL, s, totalSectors());
 
476
}
 
477
 
 
478
/** Sets if moving the Partition is allowed.
 
479
        @param b true if moving is allowed
 
480
*/
 
481
void PartResizerWidget::setMoveAllowed(bool b)
 
482
{
 
483
        m_MoveAllowed = b;
 
484
 
 
485
        if (!b)
 
486
                partWidget().setCursor(Qt::ArrowCursor);
 
487
}