Logo Search packages:      
Sourcecode: partitionmanager version File versions  Download package

partresizerwidget.cpp

/***************************************************************************
 *   Copyright (C) 2008 by Volker Lanz <vl@fidra.de>                       *
 *                                                                         *
 *   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 of the License, 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            *
 ***************************************************************************/

#include "gui/partresizerwidget.h"
#include "gui/partwidget.h"

#include "core/partition.h"
#include "core/device.h"

#include "fs/filesystem.h"

#include <QPainter>
#include <QMouseEvent>
#include <QPaintEvent>
#include <QResizeEvent>

#include <kdebug.h>
#include <kcolorscheme.h>

const qint32 PartResizerWidget::m_HandleWidth = 16;
const qint32 PartResizerWidget::m_HandleHeight = 59;

/** Creates a new PartResizerWidget

      Initializing is mostly done in init().

      @param parent pointer to the parent widget
*/
00045 PartResizerWidget::PartResizerWidget(QWidget* parent) :
      QWidget(parent),
      m_Device(NULL),
      m_Partition(NULL),
      m_PartWidget(NULL),
      m_SectorsBefore(0),
      m_SectorsAfter(0),
      m_TotalSectors(-1),
      m_MinimumSectors(-1),
      m_MaximumSectors(-1),
      m_MaxFirstSector(-1),
      m_MinLastSector(-1),
      m_LeftHandle(this),
      m_RightHandle(this),
      m_DraggedWidget(NULL),
      m_Hotspot(0),
      m_MoveAllowed(true),
      m_ReadOnly(false)
{
}

/** Intializes the PartResizerWidget
      @param d the Device the Partition is on
      @param p the Partition to show and/or resize
      @param freeBefore number of sectors free before the Partition
      @param freeAfter number of sectors free after the Partition
*/
00072 void PartResizerWidget::init(Device& d, Partition& p, qint64 freeBefore, qint64 freeAfter)
{
      setDevice(d);
      setPartition(p);

      setSectorsBefore(freeBefore);
      setSectorsAfter(freeAfter);

      setTotalSectors(sectorsBefore() + partition().length() + sectorsAfter());

      setMinimumSectors(qMax(partition().sectorsUsed(), partition().minimumSectors()));
      setMaximumSectors(qMin(totalSectors(), partition().maximumSectors()));

      /** @todo get real pixmaps for the handles */
      QPixmap pixmap(handleWidth(), handleHeight());
      pixmap.fill(QColor(0x44, 0x44, 0x44));

      leftHandle().setPixmap(pixmap);
      rightHandle().setPixmap(pixmap);
      leftHandle().setFixedSize(handleWidth(), handleHeight());
      rightHandle().setFixedSize(handleWidth(), handleHeight());

      delete m_PartWidget;
      m_PartWidget = new PartWidget(this, NULL, &partition());

      if (!readOnly())
      {
            leftHandle().setCursor(Qt::SizeHorCursor);
            rightHandle().setCursor(Qt::SizeHorCursor);

            if (moveAllowed())
                  partWidget().setCursor(Qt::SizeAllCursor);

            partWidget().setToolTip(QString());
      }

      updatePositions();
}

qint64 PartResizerWidget::sectorsPerPixel() const
{
      return totalSectors() / (width() - 2 * handleWidth());
}

int PartResizerWidget::partWidgetStart() const
{
      return handleWidth() + sectorsBefore() / sectorsPerPixel();
}

int PartResizerWidget::partWidgetWidth() const
{
      return partition().length() / sectorsPerPixel();
}

void PartResizerWidget::updatePositions()
{
      partWidget().move(partWidgetStart(), 0);
      partWidget().resize(partWidgetWidth(), height() - 1);
      leftHandle().move(partWidgetStart() - leftHandle().width(), 0);
      rightHandle().move(partWidgetStart() + partWidgetWidth(), 0);

      partWidget().update();
}

void PartResizerWidget::resizeEvent(QResizeEvent* event)
{
      updatePositions();
      QWidget::resizeEvent(event);
}

void PartResizerWidget::paintEvent(QPaintEvent*)
{
      QPainter painter(this);
      painter.setPen(Qt::NoPen);
      painter.setBrush(QColor(0x99, 0x99, 0x99));
      painter.drawRect(QRect(handleWidth(), 0, width() - (2 * handleWidth()) - 1, height() - 1));
}

void PartResizerWidget::mousePressEvent(QMouseEvent* event)
{
      if (readOnly())
            return;

      if (event->button() == Qt::LeftButton)
      {
            m_DraggedWidget = static_cast<QWidget*>(childAt(event->pos()));

            if (m_DraggedWidget != NULL)
            {
                  if (partWidget().isAncestorOf(m_DraggedWidget))
                        m_DraggedWidget = &partWidget();

                  m_Hotspot = m_DraggedWidget->mapFromParent(event->pos()).x();
            }
      }
}

void PartResizerWidget::mouseMoveEvent(QMouseEvent* event)
{
      int x = event->pos().x() - m_Hotspot;

      if (draggedWidget() == &leftHandle())
      {
            const qint64 newSectorsBefore = qMax(x * sectorsPerPixel(), 0LL);
            updateSectorsBefore(newSectorsBefore);
      }
      else if (draggedWidget() == &rightHandle())
      {
            const qint64 newSectorsAfter = qMax((width() - rightHandle().width() - x) * sectorsPerPixel(), 0LL);
            updateSectorsAfter(newSectorsAfter);
      }
      else if (draggedWidget() == &partWidget())
      {
            x -= handleWidth();
            qint64 newSectorsBefore = qMax(x * sectorsPerPixel(), 0LL);
            qint64 newSectorsAfter = sectorsAfter() + sectorsBefore() - newSectorsBefore;

            if (newSectorsAfter < 0)
            {
                  newSectorsAfter = 0;
                  newSectorsBefore = sectorsBefore() + sectorsAfter();
            }

            if (newSectorsBefore != sectorsBefore() && newSectorsAfter != sectorsAfter())
                  updateSectors(newSectorsBefore, newSectorsAfter);
      }
}

/** Updates the start and end sector of the Partition.
      @param newSectorsBefore new value for free sectors before the Partition
      @param newSectorsAfter new value for free sectors after the Partition
      @return true on success
*/
00205 bool PartResizerWidget::updateSectors(qint64 newSectorsBefore, qint64 newSectorsAfter)
{
      Q_ASSERT(newSectorsBefore >= 0);
      Q_ASSERT(newSectorsAfter >= 0);
      Q_ASSERT(newSectorsBefore + newSectorsAfter + partition().length() == totalSectors());

      if (newSectorsBefore < 0 || newSectorsAfter < 0)
      {
            kWarning() << "new sectors before partition: " << newSectorsBefore;
            kWarning() << "new sectors after partition: " << newSectorsBefore;
            return false;
      }

      if (newSectorsBefore + newSectorsAfter + partition().length() != totalSectors())
      {
            kWarning() << "total sectors: " << totalSectors();
            kWarning() << "new sectors before partition: " << newSectorsBefore;
            kWarning() << "new sectors after partition: " << newSectorsBefore;
            kWarning() << "partition length: " << partition().length();
            return false;
      }

      if (!moveAllowed())
            return false;

      const qint64 oldBefore = sectorsBefore();
      const qint64 oldAfter = sectorsAfter();

      // Two hacky things about updating free sectors before and after a partition in one go:
      // 1) We have to call updateSectorsBefore and updateSectorsAfter with length-checking disabled,
      //    because the partition might in between those calls get smaller or bigger than
      //    allowed. The second call will, of course, restore the original length.
      // 2) If the user moves the mouse fast enough, it's possible to move the beginning past the
      //    end or the end in front of the beginning of the partition in between the calls. Both
      //    methods won't allow that and return false in that case. We try moving the beginning first and
      //    just move the end first if that fails.
      if (!updateSectorsBefore(newSectorsBefore, false))
      {
            updateSectorsAfter(newSectorsAfter, false);
            updateSectorsBefore(newSectorsBefore, false);
      }
      else
            updateSectorsAfter(newSectorsAfter, false);

      bool rval = false;

      if (oldBefore != sectorsBefore())
      {
            rval = true;
            emit sectorsBeforeChanged(sectorsBefore());
      }

      if (oldAfter != sectorsAfter())
      {
            rval = true;
            emit sectorsAfterChanged(sectorsAfter());
      }

      if (rval)
            updatePositions();

      return rval;
}

void PartResizerWidget::mouseReleaseEvent(QMouseEvent* event)
{
      if (event->button() == Qt::LeftButton)
            m_DraggedWidget = NULL;
}

/** Updates the free sectors before the Partition
      @param newSectorsBefore new value for number of sectors free before Partition
      @param enableLengthCheck true if the method is supposed to do some sanity checking on the Partition length
      @return true on success
*/
00280 bool PartResizerWidget::updateSectorsBefore(qint64 newSectorsBefore, bool enableLengthCheck)
{
      Q_ASSERT(newSectorsBefore >= 0);

      if (newSectorsBefore < 0)
      {
            kWarning() << "new sectors before partition: " << newSectorsBefore;
            return false;
      }

      const qint64 oldSectorsBefore = sectorsBefore();
      const qint64 newLength = partition().length() + oldSectorsBefore - newSectorsBefore;

      if (enableLengthCheck)
      {
            if (newLength < minimumSectors())
                  newSectorsBefore -= minimumSectors() - newLength;

            if (newLength > maximumSectors())
                  newSectorsBefore += newLength - maximumSectors();
      }
      else if (newLength < 0)
            return false;

      qint64 newFirstSector = partition().firstSector() + newSectorsBefore - oldSectorsBefore;

      if (maxFirstSector() > -1 && newFirstSector > maxFirstSector())
      {
            newSectorsBefore -= newFirstSector - maxFirstSector();
            newFirstSector = maxFirstSector();
      }

      if (newSectorsBefore >= 0 && newSectorsBefore != oldSectorsBefore && (partition().children().size() == 0 || checkSnap(*partition().children().first(), oldSectorsBefore - newSectorsBefore)))
      {
            setSectorsBefore(newSectorsBefore);

            partition().setFirstSector(newFirstSector);
            partition().fileSystem().setFirstSector(newFirstSector);

            resizeLogicals();

            emit sectorsBeforeChanged(sectorsBefore());
            emit lengthChanged(partition().length());

            updatePositions();

            return true;
      }

      return false;
}

bool PartResizerWidget::checkSnap(const Partition& child, qint64 delta) const
{
      if (!partition().roles().has(PartitionRole::Extended))
            return true;

      if (child.roles().has(PartitionRole::Unallocated))
            return true;

      return qAbs(delta) >= device().cylinderSize();
}

void PartResizerWidget::resizeLogicals()
{
      if (!partition().roles().has(PartitionRole::Extended) || partition().children().size() == 0)
            return;

      Q_ASSERT(device().partitionTable());

      device().partitionTable()->removeUnallocated(&partition());
      device().partitionTable()->insertUnallocated(device(), &partition(), partition().firstSector());

      partWidget().updateChildren();
}

/** Updates the number of free sectors after the Partition.
      @param newSectorsAfter new value for number of sectors free after Partition
      @param enableLengthCheck true if the method is supposed to do some sanity checking on the Partition length
      @return true on success
*/
00361 bool PartResizerWidget::updateSectorsAfter(qint64 newSectorsAfter, bool enableLengthCheck)
{
      Q_ASSERT(newSectorsAfter >= 0);

      if (newSectorsAfter < 0)
      {
            kWarning() << "new sectors after partition: " << newSectorsAfter;
            return false;
      }

      const qint64 oldSectorsAfter = sectorsAfter();
      const qint64 newLength = partition().length() + oldSectorsAfter - newSectorsAfter;

      if (enableLengthCheck)
      {
            if (newLength < minimumSectors())
                  newSectorsAfter -= minimumSectors() - newLength;

            if (newLength > maximumSectors())
                  newSectorsAfter += newLength - maximumSectors();
      }
      else if (newLength < 0)
            return false;

      qint64 newLastSector = partition().lastSector() + oldSectorsAfter - newSectorsAfter;

      if (minLastSector() > -1 && newLastSector < minLastSector())
      {
            newSectorsAfter += newLastSector - minLastSector();
            newLastSector = minLastSector();
      }

      if (newSectorsAfter >= 0 && newSectorsAfter != oldSectorsAfter && (partition().children().size() == 0 || checkSnap(*partition().children().last(), oldSectorsAfter - newSectorsAfter)))
      {
            setSectorsAfter(newSectorsAfter);

            partition().setLastSector(newLastSector);
            partition().fileSystem().setLastSector(newLastSector);

            resizeLogicals();

            emit sectorsAfterChanged(sectorsAfter());
            emit lengthChanged(partition().length());

            updatePositions();

            return true;
      }

      return false;
}

/** Updates the Partition's length
      @param newLength the new length
      @return true on success
*/
00417 bool PartResizerWidget::updateLength(qint64 newLength)
{
      newLength = qBound(minimumSectors(), newLength, qMin(totalSectors(), maximumSectors()));

      if (newLength == partition().length())
            return false;

      const qint64 oldLength = partition().length();
      qint64 delta = newLength - oldLength;

      qint64 tmp = qMin(delta, sectorsAfter());
      delta -= tmp;

      if (tmp != 0)
      {
            setSectorsAfter(sectorsAfter() - tmp);
            partition().setLastSector(partition().lastSector() + tmp);
            partition().fileSystem().setLastSector(partition().lastSector() + tmp);
            emit sectorsAfterChanged(sectorsAfter());
      }

      tmp = qMin(delta, sectorsBefore());;
      delta -= tmp;

      if (tmp != 0)
      {
            setSectorsBefore(sectorsBefore() - tmp);
            partition().setFirstSector(partition().firstSector() - tmp);
            partition().fileSystem().setFirstSector(partition().firstSector() - tmp);
            emit sectorsBeforeChanged(sectorsBefore());
      }

      if (partition().length() != oldLength)
      {
            emit lengthChanged(partition().length());
            updatePositions();

            return true;
      }

      return false;
}

/** Sets the minimum sectors the Partition can be long.
      @note This value can never be less than 0 and never be higher than totalSectors()
      @param s the new minimum length
*/
00464 void PartResizerWidget::setMinimumSectors(qint64 s)
{
      m_MinimumSectors = qBound(0LL, s, totalSectors());
}

/** Sets the maximum sectors the Partition can be long.
      @note This value can never be less than 0 and never by higher than totalSectors()
      @param s the new maximum length
*/
00473 void PartResizerWidget::setMaximumSectors(qint64 s)
{
      m_MaximumSectors = qBound(0LL, s, totalSectors());
}

/** Sets if moving the Partition is allowed.
      @param b true if moving is allowed
*/
00481 void PartResizerWidget::setMoveAllowed(bool b)
{
      m_MoveAllowed = b;

      if (!b)
            partWidget().setCursor(Qt::ArrowCursor);
}

Generated by  Doxygen 1.6.0   Back to index