2017-12-20 14:39:09 +01:00
|
|
|
/* === This file is part of Calamares - <https://github.com/calamares> ===
|
2014-10-06 18:30:23 +02:00
|
|
|
*
|
2016-01-11 17:30:33 +01:00
|
|
|
* Copyright 2014-2016, Teo Mrnjavac <teo@kde.org>
|
2014-10-06 18:30:23 +02:00
|
|
|
*
|
|
|
|
* Calamares 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 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* Calamares 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 Calamares. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "PartitionSplitterWidget.h"
|
|
|
|
|
2016-01-11 15:10:50 +01:00
|
|
|
#include "core/ColorUtils.h"
|
|
|
|
#include "core/PartitionIterator.h"
|
2016-02-17 16:41:05 +01:00
|
|
|
#include "core/KPMHelpers.h"
|
2016-01-11 15:10:50 +01:00
|
|
|
|
2014-10-06 18:30:23 +02:00
|
|
|
#include "utils/Logger.h"
|
2015-11-20 15:28:53 +01:00
|
|
|
#include "utils/CalamaresUtilsGui.h"
|
2014-10-06 18:30:23 +02:00
|
|
|
|
2016-01-11 15:10:50 +01:00
|
|
|
#include <kpmcore/core/device.h>
|
|
|
|
#include <kpmcore/core/partition.h>
|
|
|
|
|
2014-10-06 18:30:23 +02:00
|
|
|
#include <QApplication>
|
|
|
|
#include <QPainter>
|
|
|
|
#include <QMouseEvent>
|
|
|
|
#include <QStyleOption>
|
|
|
|
|
2016-01-11 15:54:10 +01:00
|
|
|
static const int VIEW_HEIGHT = qMax( CalamaresUtils::defaultFontHeight() + 8, // wins out with big fonts
|
2017-09-08 13:50:09 +02:00
|
|
|
int( CalamaresUtils::defaultFontHeight() * 0.6 ) + 22 ); // wins out with small fonts
|
2014-10-06 18:30:23 +02:00
|
|
|
static const int CORNER_RADIUS = 3;
|
2016-01-11 15:54:10 +01:00
|
|
|
static const int EXTENDED_PARTITION_MARGIN = qMax( 4, VIEW_HEIGHT / 6 );
|
2014-10-06 18:30:23 +02:00
|
|
|
|
|
|
|
PartitionSplitterWidget::PartitionSplitterWidget( QWidget* parent )
|
|
|
|
: QWidget( parent )
|
2016-02-22 18:51:44 +01:00
|
|
|
, m_itemToResize( PartitionSplitterItem::null() )
|
|
|
|
, m_itemToResizeNext( PartitionSplitterItem::null() )
|
2015-06-14 00:45:38 +02:00
|
|
|
, m_itemMinSize( 0 )
|
|
|
|
, m_itemMaxSize( 0 )
|
|
|
|
, m_itemPrefSize( 0 )
|
2017-09-08 13:50:09 +02:00
|
|
|
, m_resizing( false )
|
2015-06-14 00:45:38 +02:00
|
|
|
, m_resizeHandleX( 0 )
|
2014-10-06 18:30:23 +02:00
|
|
|
, HANDLE_SNAP( QApplication::startDragDistance() )
|
2016-02-26 13:39:41 +01:00
|
|
|
, m_drawNestedPartitions( false )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
setMouseTracking( true );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
2016-02-10 17:02:10 +01:00
|
|
|
PartitionSplitterWidget::init( Device* dev, bool drawNestedPartitions )
|
2016-01-11 15:10:50 +01:00
|
|
|
{
|
2016-02-10 17:02:10 +01:00
|
|
|
m_drawNestedPartitions = drawNestedPartitions;
|
2016-02-17 16:46:51 +01:00
|
|
|
QVector< PartitionSplitterItem > allPartitionItems;
|
2016-01-11 15:10:50 +01:00
|
|
|
PartitionSplitterItem* extendedPartitionItem = nullptr;
|
|
|
|
for ( auto it = PartitionIterator::begin( dev );
|
|
|
|
it != PartitionIterator::end( dev ); ++it )
|
|
|
|
{
|
|
|
|
PartitionSplitterItem newItem = {
|
|
|
|
( *it )->partitionPath(),
|
|
|
|
ColorUtils::colorForPartition( *it ),
|
2016-02-17 16:41:05 +01:00
|
|
|
KPMHelpers::isPartitionFreeSpace( *it ),
|
2016-01-11 15:10:50 +01:00
|
|
|
( *it )->capacity(),
|
2016-02-23 14:43:12 +01:00
|
|
|
PartitionSplitterItem::Normal,
|
2016-01-11 15:10:50 +01:00
|
|
|
{}
|
|
|
|
};
|
|
|
|
|
2016-02-17 16:24:39 +01:00
|
|
|
// If we don't draw child partitions of a partitions as child partitions, we
|
|
|
|
// need to flatten the items tree into an items list
|
2016-02-10 17:02:10 +01:00
|
|
|
if ( drawNestedPartitions )
|
|
|
|
{
|
|
|
|
if ( ( *it )->roles().has( PartitionRole::Logical ) && extendedPartitionItem )
|
|
|
|
extendedPartitionItem->children.append( newItem );
|
|
|
|
else
|
|
|
|
{
|
|
|
|
allPartitionItems.append( newItem );
|
|
|
|
if ( ( *it )->roles().has( PartitionRole::Extended ) )
|
|
|
|
extendedPartitionItem = &allPartitionItems.last();
|
|
|
|
}
|
|
|
|
}
|
2016-01-11 15:10:50 +01:00
|
|
|
else
|
|
|
|
{
|
2016-02-10 17:02:10 +01:00
|
|
|
if ( !( *it )->roles().has( PartitionRole::Extended ) )
|
|
|
|
allPartitionItems.append( newItem );
|
2016-01-11 15:10:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
setupItems( allPartitionItems );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-02-17 16:46:51 +01:00
|
|
|
PartitionSplitterWidget::setupItems( const QVector<PartitionSplitterItem>& items )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
2016-02-22 18:51:44 +01:00
|
|
|
m_itemToResize = PartitionSplitterItem::null();
|
|
|
|
m_itemToResizeNext = PartitionSplitterItem::null();
|
2014-10-06 18:30:23 +02:00
|
|
|
m_itemToResizePath.clear();
|
|
|
|
|
2015-03-19 17:18:24 +01:00
|
|
|
m_items.clear();
|
2014-10-06 18:30:23 +02:00
|
|
|
m_items = items;
|
|
|
|
repaint();
|
2016-09-01 14:21:05 +02:00
|
|
|
for ( const PartitionSplitterItem& item : items )
|
2014-10-06 18:30:23 +02:00
|
|
|
cDebug() << "PSI added item" << item.itemPath << "size" << item.size;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PartitionSplitterWidget::setSplitPartition( const QString& path,
|
|
|
|
qint64 minSize,
|
|
|
|
qint64 maxSize,
|
2017-09-08 13:50:09 +02:00
|
|
|
qint64 preferredSize )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
cDebug() << Q_FUNC_INFO << "path:" << path
|
|
|
|
<< "\nminSize:" << minSize
|
|
|
|
<< "\nmaxSize:" << maxSize
|
|
|
|
<< "\nprfSize:" << preferredSize;
|
|
|
|
|
2016-02-26 13:13:01 +01:00
|
|
|
if ( m_itemToResize && m_itemToResizeNext )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
2016-01-12 12:48:39 +01:00
|
|
|
cDebug() << "NOTICE: trying to split partition but partition to split is already set.";
|
|
|
|
|
|
|
|
// We need to remove the itemToResizeNext from wherever it is
|
|
|
|
for ( int i = 0; i < m_items.count(); ++i )
|
|
|
|
{
|
2016-02-22 18:51:44 +01:00
|
|
|
if ( m_items[ i ].itemPath == m_itemToResize.itemPath &&
|
2016-02-23 10:36:30 +01:00
|
|
|
m_items[ i ].status == PartitionSplitterItem::Resizing &&
|
2016-01-12 12:48:39 +01:00
|
|
|
i + 1 < m_items.count() )
|
|
|
|
{
|
2016-02-22 18:51:44 +01:00
|
|
|
m_items[ i ].size = m_items[ i ].size + m_itemToResizeNext.size;
|
2016-02-23 10:36:30 +01:00
|
|
|
m_items[ i ].status = PartitionSplitterItem::Normal;
|
2016-01-12 12:48:39 +01:00
|
|
|
m_items.removeAt( i + 1 );
|
2016-02-22 18:51:44 +01:00
|
|
|
m_itemToResizeNext = PartitionSplitterItem::null();
|
2016-01-12 12:48:39 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if ( !m_items[ i ].children.isEmpty() )
|
|
|
|
{
|
|
|
|
for ( int j = 0; j < m_items[ i ].children.count(); ++j )
|
|
|
|
{
|
2016-02-22 18:51:44 +01:00
|
|
|
if ( m_items[ i ].children[ j ].itemPath == m_itemToResize.itemPath &&
|
2016-01-12 12:48:39 +01:00
|
|
|
j + 1 < m_items[ i ].children.count() )
|
|
|
|
{
|
2016-01-12 13:05:11 +01:00
|
|
|
m_items[ i ].children[ j ].size =
|
2016-02-22 18:51:44 +01:00
|
|
|
m_items[ i ].children[ j ].size + m_itemToResizeNext.size;
|
2016-02-23 10:36:30 +01:00
|
|
|
m_items[ i ].children[ j ].status = PartitionSplitterItem::Normal;
|
2016-01-12 12:48:39 +01:00
|
|
|
m_items[ i ].children.removeAt( j + 1 );
|
2016-02-22 18:51:44 +01:00
|
|
|
m_itemToResizeNext = PartitionSplitterItem::null();
|
2016-01-12 12:48:39 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-02-22 18:51:44 +01:00
|
|
|
if ( m_itemToResizeNext.isNull() )
|
2016-01-12 12:48:39 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
m_itemToResize = PartitionSplitterItem::null();
|
2016-01-12 12:48:39 +01:00
|
|
|
m_itemToResizePath.clear();
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
PartitionSplitterItem itemToResize = _findItem( m_items,
|
|
|
|
[ path ]( PartitionSplitterItem& item ) -> bool
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
2016-02-23 10:36:30 +01:00
|
|
|
if ( path == item.itemPath )
|
|
|
|
{
|
|
|
|
item.status = PartitionSplitterItem::Resizing;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2014-10-06 18:30:23 +02:00
|
|
|
} );
|
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
if ( itemToResize.isNull() )
|
2014-10-06 18:30:23 +02:00
|
|
|
return;
|
2016-02-22 18:51:44 +01:00
|
|
|
cDebug() << "itemToResize:" << itemToResize.itemPath;
|
2014-10-06 18:30:23 +02:00
|
|
|
|
|
|
|
m_itemToResize = itemToResize;
|
|
|
|
m_itemToResizePath = path;
|
|
|
|
|
|
|
|
if ( preferredSize > maxSize )
|
|
|
|
preferredSize = maxSize;
|
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
qint64 newSize = m_itemToResize.size - preferredSize;
|
|
|
|
m_itemToResize.size = preferredSize;
|
2016-02-23 10:36:30 +01:00
|
|
|
int opCount = _eachItem( m_items,
|
|
|
|
[ preferredSize ]( PartitionSplitterItem& item ) -> bool
|
|
|
|
{
|
|
|
|
if ( item.status == PartitionSplitterItem::Resizing )
|
|
|
|
{
|
|
|
|
item.size = preferredSize;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
} );
|
|
|
|
cDebug() << "each splitter item opcount:" << opCount;
|
2014-10-06 18:30:23 +02:00
|
|
|
m_itemMinSize = minSize;
|
|
|
|
m_itemMaxSize = maxSize;
|
|
|
|
m_itemPrefSize = preferredSize;
|
|
|
|
|
|
|
|
for ( int i = 0; i < m_items.count(); ++i )
|
|
|
|
{
|
2016-02-22 18:51:44 +01:00
|
|
|
if ( m_items[ i ].itemPath == itemToResize.itemPath )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
m_items.insert( i+1,
|
2016-02-23 10:36:30 +01:00
|
|
|
{ "",
|
|
|
|
QColor( "#c0392b" ),
|
|
|
|
false,
|
|
|
|
newSize,
|
|
|
|
PartitionSplitterItem::ResizingNext,
|
|
|
|
{} } );
|
2016-02-22 18:51:44 +01:00
|
|
|
m_itemToResizeNext = m_items[ i+1 ];
|
2014-10-06 18:30:23 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if ( !m_items[ i ].children.isEmpty() )
|
|
|
|
{
|
|
|
|
for ( int j = 0; j < m_items[ i ].children.count(); ++j )
|
|
|
|
{
|
2016-02-22 18:51:44 +01:00
|
|
|
if ( m_items[ i ].children[ j ].itemPath == itemToResize.itemPath )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
m_items[ i ].children.insert( j+1,
|
2016-02-23 10:36:30 +01:00
|
|
|
{ "",
|
|
|
|
QColor( "#c0392b" ),
|
|
|
|
false,
|
|
|
|
newSize,
|
|
|
|
PartitionSplitterItem::ResizingNext,
|
|
|
|
{} } );
|
2016-02-22 18:51:44 +01:00
|
|
|
m_itemToResizeNext = m_items[ i ].children[ j+1 ];
|
2014-10-06 18:30:23 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2016-02-22 18:51:44 +01:00
|
|
|
if ( !m_itemToResizeNext.isNull() )
|
2014-10-06 18:30:23 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
emit partitionResized( m_itemToResize.itemPath,
|
|
|
|
m_itemToResize.size,
|
|
|
|
m_itemToResizeNext.size );
|
2014-10-06 18:30:23 +02:00
|
|
|
|
|
|
|
cDebug() << "Items updated. Status:";
|
|
|
|
foreach ( const PartitionSplitterItem& item, m_items )
|
2016-02-23 10:36:30 +01:00
|
|
|
cDebug() << "item" << item.itemPath << "size" << item.size << "status:" << item.status;
|
2016-02-22 16:39:06 +01:00
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
cDebug() << "m_itemToResize: " << !m_itemToResize.isNull() << m_itemToResize.itemPath;
|
|
|
|
cDebug() << "m_itemToResizeNext:" << !m_itemToResizeNext.isNull() << m_itemToResizeNext.itemPath;
|
2016-02-22 16:39:06 +01:00
|
|
|
|
|
|
|
repaint();
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qint64
|
|
|
|
PartitionSplitterWidget::splitPartitionSize() const
|
|
|
|
{
|
|
|
|
if ( !m_itemToResize )
|
|
|
|
return -1;
|
2016-02-22 18:51:44 +01:00
|
|
|
return m_itemToResize.size;
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
qint64
|
|
|
|
PartitionSplitterWidget::newPartitionSize() const
|
|
|
|
{
|
|
|
|
if ( !m_itemToResizeNext )
|
|
|
|
return -1;
|
2016-02-22 18:51:44 +01:00
|
|
|
return m_itemToResizeNext.size;
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
QSize
|
|
|
|
PartitionSplitterWidget::sizeHint() const
|
|
|
|
{
|
|
|
|
return QSize( -1, VIEW_HEIGHT );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-11 15:54:10 +01:00
|
|
|
QSize
|
|
|
|
PartitionSplitterWidget::minimumSizeHint() const
|
|
|
|
{
|
|
|
|
return sizeHint();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2014-10-06 18:30:23 +02:00
|
|
|
void
|
|
|
|
PartitionSplitterWidget::paintEvent( QPaintEvent* event )
|
|
|
|
{
|
2019-04-17 11:57:46 +02:00
|
|
|
Q_UNUSED( event )
|
2017-09-08 13:50:09 +02:00
|
|
|
|
2016-01-11 15:57:57 +01:00
|
|
|
QPainter painter( this );
|
2014-10-06 18:30:23 +02:00
|
|
|
painter.fillRect( rect(), palette().window() );
|
|
|
|
painter.setRenderHint( QPainter::Antialiasing );
|
2016-01-11 15:54:10 +01:00
|
|
|
|
2016-01-12 14:04:44 +01:00
|
|
|
drawPartitions( &painter, rect(), m_items );
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PartitionSplitterWidget::mousePressEvent( QMouseEvent* event )
|
|
|
|
{
|
2016-01-12 14:04:44 +01:00
|
|
|
if ( m_itemToResize &&
|
|
|
|
m_itemToResizeNext &&
|
|
|
|
event->button() == Qt::LeftButton )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
if ( qAbs( event->x() - m_resizeHandleX ) < HANDLE_SNAP )
|
|
|
|
m_resizing = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PartitionSplitterWidget::mouseMoveEvent( QMouseEvent* event )
|
|
|
|
{
|
|
|
|
if ( m_resizing )
|
|
|
|
{
|
|
|
|
qint64 start = 0;
|
2016-02-22 18:51:44 +01:00
|
|
|
QString itemPath = m_itemToResize.itemPath;
|
2016-02-17 17:17:50 +01:00
|
|
|
for ( auto it = m_items.constBegin();
|
|
|
|
it != m_items.constEnd(); ++it )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
2016-02-17 17:17:50 +01:00
|
|
|
if ( it->itemPath == itemPath )
|
2014-10-06 18:30:23 +02:00
|
|
|
break;
|
2016-02-17 17:17:50 +01:00
|
|
|
else if ( !it->children.isEmpty() )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
bool done = false;
|
2016-02-17 17:17:50 +01:00
|
|
|
for ( auto jt = it->children.constBegin();
|
|
|
|
jt != it->children.constEnd(); ++jt )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
2016-02-17 17:17:50 +01:00
|
|
|
if ( jt->itemPath == itemPath )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
2016-02-17 17:17:50 +01:00
|
|
|
start += jt->size;
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
if ( done )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
2016-02-17 17:17:50 +01:00
|
|
|
start += it->size;
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
qint64 total = 0;
|
2016-02-17 17:17:50 +01:00
|
|
|
for ( auto it = m_items.constBegin(); it != m_items.constEnd(); ++it )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
2016-02-17 17:17:50 +01:00
|
|
|
total += it->size;
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int ew = rect().width(); //effective width
|
|
|
|
qreal bpp = total / static_cast< qreal >( ew ); //bytes per pixel
|
|
|
|
|
|
|
|
qreal mx = event->x() * bpp - start;
|
|
|
|
|
|
|
|
// make sure we are within resize range
|
|
|
|
mx = qBound( static_cast< qreal >( m_itemMinSize ),
|
|
|
|
mx,
|
|
|
|
static_cast< qreal >( m_itemMaxSize ) );
|
|
|
|
|
|
|
|
qint64 span = m_itemPrefSize;
|
|
|
|
qreal percent = mx / span;
|
2016-02-22 18:51:44 +01:00
|
|
|
qint64 oldsize = m_itemToResize.size;
|
2014-10-06 18:30:23 +02:00
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
m_itemToResize.size = qRound64( span * percent );
|
|
|
|
m_itemToResizeNext.size -= m_itemToResize.size - oldsize;
|
2016-02-23 10:36:30 +01:00
|
|
|
_eachItem( m_items,
|
|
|
|
[ this ]( PartitionSplitterItem& item ) -> bool
|
|
|
|
{
|
|
|
|
if ( item.status == PartitionSplitterItem::Resizing )
|
|
|
|
{
|
|
|
|
item.size = m_itemToResize.size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if ( item.status == PartitionSplitterItem::ResizingNext )
|
|
|
|
{
|
|
|
|
item.size = m_itemToResizeNext.size;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
} );
|
2014-10-06 18:30:23 +02:00
|
|
|
|
|
|
|
repaint();
|
|
|
|
|
2016-02-17 17:17:50 +01:00
|
|
|
emit partitionResized( itemPath,
|
2016-02-22 18:51:44 +01:00
|
|
|
m_itemToResize.size,
|
|
|
|
m_itemToResizeNext.size );
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-01-12 14:04:44 +01:00
|
|
|
if ( m_itemToResize && m_itemToResizeNext )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
if ( qAbs( event->x() - m_resizeHandleX ) < HANDLE_SNAP )
|
|
|
|
setCursor( Qt::SplitHCursor );
|
|
|
|
else if ( cursor().shape() != Qt::ArrowCursor )
|
|
|
|
setCursor( Qt::ArrowCursor );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PartitionSplitterWidget::mouseReleaseEvent( QMouseEvent* event )
|
|
|
|
{
|
2019-04-17 11:57:46 +02:00
|
|
|
Q_UNUSED( event )
|
2017-09-08 13:50:09 +02:00
|
|
|
|
2014-10-06 18:30:23 +02:00
|
|
|
m_resizing = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PartitionSplitterWidget::drawSection( QPainter* painter, const QRect& rect_, int x, int width,
|
|
|
|
const PartitionSplitterItem& item )
|
|
|
|
{
|
|
|
|
QColor color = item.color;
|
|
|
|
bool isFreeSpace = item.isFreeSpace;
|
|
|
|
|
|
|
|
QRect rect = rect_;
|
|
|
|
const int y = rect.y();
|
|
|
|
const int rectHeight = rect.height();
|
|
|
|
const int radius = qMax( 1, CORNER_RADIUS - ( height() - rectHeight ) / 2 );
|
|
|
|
painter->setClipRect( x, y, width, rectHeight );
|
|
|
|
painter->translate( 0.5, 0.5 );
|
|
|
|
|
|
|
|
rect.adjust( 0, 0, -1, -1 );
|
|
|
|
const QColor borderColor = color.darker();
|
|
|
|
painter->setPen( borderColor );
|
|
|
|
painter->setBrush( color );
|
|
|
|
painter->drawRoundedRect( rect, radius, radius );
|
|
|
|
|
|
|
|
// Draw shade
|
|
|
|
if ( !isFreeSpace )
|
|
|
|
rect.adjust( 2, 2, -2, -2 );
|
|
|
|
|
|
|
|
QLinearGradient gradient( 0, 0, 0, rectHeight / 2 );
|
|
|
|
|
|
|
|
qreal c = isFreeSpace ? 0 : 1;
|
|
|
|
gradient.setColorAt( 0, QColor::fromRgbF( c, c, c, 0.3 ) );
|
|
|
|
gradient.setColorAt( 1, QColor::fromRgbF( c, c, c, 0 ) );
|
|
|
|
|
|
|
|
painter->setPen( Qt::NoPen );
|
|
|
|
painter->setBrush( gradient );
|
|
|
|
painter->drawRoundedRect( rect, radius, radius );
|
|
|
|
|
|
|
|
painter->translate( -0.5, -0.5 );
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PartitionSplitterWidget::drawResizeHandle( QPainter* painter,
|
|
|
|
const QRect& rect_,
|
|
|
|
int x )
|
|
|
|
{
|
|
|
|
if ( !m_itemToResize )
|
|
|
|
return;
|
|
|
|
|
|
|
|
painter->setPen( Qt::NoPen );
|
|
|
|
painter->setBrush( Qt::black );
|
|
|
|
painter->setClipRect( rect_ );
|
|
|
|
|
|
|
|
painter->setRenderHint( QPainter::Antialiasing, true );
|
|
|
|
|
2016-01-13 16:23:12 +01:00
|
|
|
qreal h = VIEW_HEIGHT; // Put the arrow in the center regardless of inner box height
|
2014-10-06 18:30:23 +02:00
|
|
|
int scaleFactor = qRound( height() / static_cast< qreal >( VIEW_HEIGHT ) );
|
|
|
|
QList< QPair< qreal, qreal > > arrow_offsets = {
|
|
|
|
qMakePair( 0, h / 2 - 1 ),
|
|
|
|
qMakePair( 4, h / 2 - 1 ),
|
|
|
|
qMakePair( 4, h / 2 - 3 ),
|
|
|
|
qMakePair( 8, h / 2 ),
|
|
|
|
qMakePair( 4, h / 2 + 3 ),
|
|
|
|
qMakePair( 4, h / 2 + 1 ),
|
|
|
|
qMakePair( 0, h / 2 + 1 )
|
|
|
|
};
|
|
|
|
for ( int i = 0; i < arrow_offsets.count(); ++i )
|
|
|
|
{
|
|
|
|
arrow_offsets[ i ] = qMakePair( arrow_offsets[ i ].first * scaleFactor,
|
|
|
|
( arrow_offsets[ i ].second - h/2 ) * scaleFactor + h/2 );
|
|
|
|
}
|
|
|
|
|
|
|
|
auto p1 = arrow_offsets[ 0 ];
|
2016-02-22 18:51:44 +01:00
|
|
|
if ( m_itemToResize.size > m_itemMinSize )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
auto arrow = QPainterPath( QPointF( x + -1 * p1.first, p1.second ) );
|
|
|
|
for ( auto p : arrow_offsets )
|
|
|
|
arrow.lineTo( x + -1 * p.first + 1, p.second );
|
|
|
|
painter->drawPath( arrow );
|
|
|
|
}
|
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
if ( m_itemToResize.size < m_itemMaxSize )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
auto arrow = QPainterPath( QPointF( x + p1.first, p1.second ) );
|
|
|
|
for ( auto p : arrow_offsets )
|
|
|
|
arrow.lineTo( x + p.first, p.second );
|
|
|
|
painter->drawPath( arrow );
|
|
|
|
}
|
|
|
|
|
|
|
|
painter->setRenderHint( QPainter::Antialiasing, false );
|
|
|
|
painter->setPen( Qt::black );
|
2017-09-08 13:50:09 +02:00
|
|
|
painter->drawLine( x, 0, x, int(h) - 1 );
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PartitionSplitterWidget::drawPartitions( QPainter* painter,
|
|
|
|
const QRect& rect,
|
2016-02-17 16:46:51 +01:00
|
|
|
const QVector< PartitionSplitterItem >& itemList )
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
2016-02-17 16:24:39 +01:00
|
|
|
const int count = itemList.count();
|
2014-10-06 18:30:23 +02:00
|
|
|
const int totalWidth = rect.width();
|
|
|
|
|
2016-02-17 16:24:39 +01:00
|
|
|
auto pair = computeItemsVector( itemList );
|
2016-02-17 16:46:51 +01:00
|
|
|
QVector< PartitionSplitterItem >& items = pair.first;
|
2016-02-17 16:24:39 +01:00
|
|
|
qreal total = pair.second;
|
2014-10-06 18:30:23 +02:00
|
|
|
|
|
|
|
int x = rect.x();
|
|
|
|
for ( int row = 0; row < count; ++row )
|
|
|
|
{
|
|
|
|
const PartitionSplitterItem& item = items[ row ];
|
2017-09-08 13:50:09 +02:00
|
|
|
qreal width;
|
2014-10-06 18:30:23 +02:00
|
|
|
if ( row < count - 1 )
|
2016-02-17 16:24:39 +01:00
|
|
|
width = totalWidth * ( item.size / total );
|
2014-10-06 18:30:23 +02:00
|
|
|
else
|
|
|
|
// Make sure we fill the last pixel column
|
|
|
|
width = rect.right() - x + 1;
|
|
|
|
|
2017-09-08 13:50:09 +02:00
|
|
|
drawSection( painter, rect, x, int(width), item );
|
2014-10-06 18:30:23 +02:00
|
|
|
if ( !item.children.isEmpty() )
|
|
|
|
{
|
|
|
|
QRect subRect(
|
|
|
|
x + EXTENDED_PARTITION_MARGIN,
|
|
|
|
rect.y() + EXTENDED_PARTITION_MARGIN,
|
2017-09-08 13:50:09 +02:00
|
|
|
int(width) - 2 * EXTENDED_PARTITION_MARGIN,
|
2014-10-06 18:30:23 +02:00
|
|
|
rect.height() - 2 * EXTENDED_PARTITION_MARGIN
|
|
|
|
);
|
|
|
|
drawPartitions( painter, subRect, item.children );
|
|
|
|
}
|
|
|
|
|
2016-01-13 16:15:19 +01:00
|
|
|
// If an item to resize and the following new item both exist,
|
|
|
|
// and this is not the very first partition,
|
|
|
|
// and the partition preceding this one is the item to resize...
|
2016-01-12 14:04:44 +01:00
|
|
|
if ( m_itemToResize &&
|
2016-01-13 16:12:08 +01:00
|
|
|
m_itemToResizeNext &&
|
2016-01-13 16:15:19 +01:00
|
|
|
row > 0 &&
|
2016-02-22 15:51:26 +01:00
|
|
|
!items[ row - 1 ].isFreeSpace &&
|
|
|
|
!items[ row - 1 ].itemPath.isEmpty() &&
|
2016-02-22 18:51:44 +01:00
|
|
|
items[ row - 1 ].itemPath == m_itemToResize.itemPath )
|
2016-01-13 16:17:38 +01:00
|
|
|
{
|
2016-01-13 16:12:08 +01:00
|
|
|
m_resizeHandleX = x;
|
2016-01-13 16:17:38 +01:00
|
|
|
drawResizeHandle( painter, rect, m_resizeHandleX );
|
|
|
|
}
|
2014-10-06 18:30:23 +02:00
|
|
|
|
|
|
|
x += width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
PartitionSplitterItem
|
2016-02-17 16:46:51 +01:00
|
|
|
PartitionSplitterWidget::_findItem( QVector< PartitionSplitterItem >& items,
|
2016-02-23 10:36:30 +01:00
|
|
|
std::function< bool ( PartitionSplitterItem& ) > condition ) const
|
2014-10-06 18:30:23 +02:00
|
|
|
{
|
|
|
|
for ( auto it = items.begin(); it != items.end(); ++it)
|
|
|
|
{
|
|
|
|
if ( condition( *it ) )
|
2016-02-22 18:51:44 +01:00
|
|
|
return *it;
|
2014-10-06 18:30:23 +02:00
|
|
|
|
2016-02-22 18:51:44 +01:00
|
|
|
PartitionSplitterItem candidate = _findItem( it->children, condition );
|
|
|
|
if ( !candidate.isNull() )
|
2014-10-06 18:30:23 +02:00
|
|
|
return candidate;
|
|
|
|
}
|
2016-02-22 18:51:44 +01:00
|
|
|
return PartitionSplitterItem::null();
|
2014-10-06 18:30:23 +02:00
|
|
|
}
|
2016-02-17 16:24:39 +01:00
|
|
|
|
|
|
|
|
2016-02-23 10:36:30 +01:00
|
|
|
int
|
|
|
|
PartitionSplitterWidget::_eachItem( QVector< PartitionSplitterItem >& items,
|
|
|
|
std::function< bool ( PartitionSplitterItem& ) > operation ) const
|
|
|
|
{
|
|
|
|
int opCount = 0;
|
|
|
|
for ( auto it = items.begin(); it != items.end(); ++it)
|
|
|
|
{
|
|
|
|
if ( operation( *it ) )
|
|
|
|
opCount++;
|
|
|
|
|
|
|
|
opCount += _eachItem( it->children, operation );
|
|
|
|
}
|
|
|
|
return opCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-02-17 16:46:51 +01:00
|
|
|
QPair< QVector< PartitionSplitterItem >, qreal >
|
|
|
|
PartitionSplitterWidget::computeItemsVector( const QVector< PartitionSplitterItem >& originalItems ) const
|
2016-02-17 16:24:39 +01:00
|
|
|
{
|
2016-02-17 16:46:51 +01:00
|
|
|
QVector< PartitionSplitterItem > items;
|
2016-02-17 16:24:39 +01:00
|
|
|
|
|
|
|
qreal total = 0;
|
|
|
|
for ( int row = 0; row < originalItems.count(); ++row )
|
|
|
|
{
|
2016-02-17 16:36:13 +01:00
|
|
|
if ( originalItems[ row ].children.isEmpty() )
|
2016-02-17 16:24:39 +01:00
|
|
|
{
|
|
|
|
items += originalItems[ row ];
|
|
|
|
total += originalItems[ row ].size;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PartitionSplitterItem thisItem = originalItems[ row ];
|
2016-02-17 16:46:51 +01:00
|
|
|
QPair< QVector< PartitionSplitterItem >, qreal > pair = computeItemsVector( thisItem.children );
|
2016-02-17 16:36:13 +01:00
|
|
|
thisItem.children = pair.first;
|
2017-09-08 13:50:09 +02:00
|
|
|
thisItem.size = qint64(pair.second);
|
2016-02-17 16:24:39 +01:00
|
|
|
items += thisItem;
|
2016-02-17 16:36:13 +01:00
|
|
|
total += thisItem.size;
|
2016-02-17 16:24:39 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// The sizes we have are perfect, but now we have to hardcode a minimum size for small
|
|
|
|
// partitions and compensate for it in the total.
|
|
|
|
qreal adjustedTotal = total;
|
|
|
|
for ( int row = 0; row < items.count(); ++row )
|
|
|
|
{
|
|
|
|
if ( items[ row ].size < 0.01 * total ) // If this item is smaller than 1% of everything,
|
|
|
|
{ // force its width to 1%.
|
|
|
|
adjustedTotal -= items[ row ].size;
|
2017-09-08 13:50:09 +02:00
|
|
|
items[ row ].size = qint64(0.01 * total);
|
2016-02-17 16:24:39 +01:00
|
|
|
adjustedTotal += items[ row ].size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return qMakePair( items, adjustedTotal );
|
|
|
|
}
|