1 /* === This file is part of Calamares - <https://calamares.io> ===
2  *
3  *   SPDX-FileCopyrightText: 2014 Aurélien Gâteau <agateau@kde.org>
4  *   SPDX-FileCopyrightText: 2018-2019 Adriaan de Groot <groot@kde.org>
5  *   SPDX-License-Identifier: GPL-3.0-or-later
6  *
7  *   Calamares is Free Software: see the License-Identifier above.
8  *
9  */
10 
11 #include "core/PartitionModel.h"
12 
13 #include "core/ColorUtils.h"
14 #include "core/KPMHelpers.h"
15 #include "core/PartitionInfo.h"
16 
17 #include "partition/FileSystem.h"
18 #include "partition/PartitionQuery.h"
19 #include "utils/Logger.h"
20 
21 // CalaPM
22 #include <kpmcore/core/device.h>
23 #include <kpmcore/core/partition.h>
24 #include <kpmcore/core/partitiontable.h>
25 #include <kpmcore/fs/filesystem.h>
26 
27 // KF5
28 #include <KFormat>
29 
30 // Qt
31 #include <QColor>
32 
33 using CalamaresUtils::Partition::isPartitionFreeSpace;
34 using CalamaresUtils::Partition::isPartitionNew;
35 
36 //- ResetHelper --------------------------------------------
ResetHelper(PartitionModel * model)37 PartitionModel::ResetHelper::ResetHelper( PartitionModel* model )
38     : m_model( model )
39 {
40     m_model->m_lock.lock();
41     m_model->beginResetModel();
42 }
43 
~ResetHelper()44 PartitionModel::ResetHelper::~ResetHelper()
45 {
46     // We need to unlock the mutex before emitting the reset signal,
47     // because the reset will cause clients to start looking at the
48     // (new) data.
49     m_model->m_lock.unlock();
50     m_model->endResetModel();
51 }
52 
53 //- PartitionModel -----------------------------------------
PartitionModel(QObject * parent)54 PartitionModel::PartitionModel( QObject* parent )
55     : QAbstractItemModel( parent )
56     , m_device( nullptr )
57 {
58 }
59 
60 void
init(Device * device,const OsproberEntryList & osproberEntries)61 PartitionModel::init( Device* device, const OsproberEntryList& osproberEntries )
62 {
63     QMutexLocker lock( &m_lock );
64     beginResetModel();
65     m_device = device;
66     m_osproberEntries = osproberEntries;
67     endResetModel();
68 }
69 
70 int
columnCount(const QModelIndex &) const71 PartitionModel::columnCount( const QModelIndex& ) const
72 {
73     return ColumnCount;
74 }
75 
76 int
rowCount(const QModelIndex & parent) const77 PartitionModel::rowCount( const QModelIndex& parent ) const
78 {
79     Partition* parentPartition = partitionForIndex( parent );
80     if ( parentPartition )
81     {
82         return parentPartition->children().count();
83     }
84     PartitionTable* table = m_device->partitionTable();
85     return table ? table->children().count() : 0;
86 }
87 
88 QModelIndex
index(int row,int column,const QModelIndex & parent) const89 PartitionModel::index( int row, int column, const QModelIndex& parent ) const
90 {
91     PartitionNode* parentPartition = parent.isValid() ? static_cast< PartitionNode* >( partitionForIndex( parent ) )
92                                                       : static_cast< PartitionNode* >( m_device->partitionTable() );
93     if ( !parentPartition )
94     {
95         return QModelIndex();
96     }
97     auto lst = parentPartition->children();
98     if ( row < 0 || row >= lst.count() )
99     {
100         return QModelIndex();
101     }
102     if ( column < 0 || column >= ColumnCount )
103     {
104         return QModelIndex();
105     }
106     Partition* partition = parentPartition->children().at( row );
107     return createIndex( row, column, partition );
108 }
109 
110 QModelIndex
parent(const QModelIndex & child) const111 PartitionModel::parent( const QModelIndex& child ) const
112 {
113     if ( !child.isValid() )
114     {
115         return QModelIndex();
116     }
117     Partition* partition = partitionForIndex( child );
118     if ( !partition )
119     {
120         return QModelIndex();
121     }
122     PartitionNode* parentNode = partition->parent();
123     if ( parentNode == m_device->partitionTable() )
124     {
125         return QModelIndex();
126     }
127 
128     int row = 0;
129     for ( auto p : m_device->partitionTable()->children() )
130     {
131         if ( parentNode == p )
132         {
133             return createIndex( row, 0, parentNode );
134         }
135         ++row;
136     }
137     cWarning() << "No parent found!";
138     return QModelIndex();
139 }
140 
141 QVariant
data(const QModelIndex & index,int role) const142 PartitionModel::data( const QModelIndex& index, int role ) const
143 {
144     Partition* partition = partitionForIndex( index );
145     if ( !partition )
146     {
147         return QVariant();
148     }
149 
150     switch ( role )
151     {
152     case Qt::DisplayRole:
153     {
154         int col = index.column();
155         if ( col == NameColumn )
156         {
157             if ( isPartitionFreeSpace( partition ) )
158             {
159                 return tr( "Free Space" );
160             }
161             else
162             {
163                 return isPartitionNew( partition ) ? tr( "New partition" ) : partition->partitionPath();
164             }
165         }
166         if ( col == FileSystemColumn )
167         {
168             return CalamaresUtils::Partition::prettyNameForFileSystemType( partition->fileSystem().type() );
169         }
170         if ( col == FileSystemLabelColumn )
171         {
172             return partition->fileSystem().label();
173         }
174         if ( col == MountPointColumn )
175         {
176             return PartitionInfo::mountPoint( partition );
177         }
178         if ( col == SizeColumn )
179         {
180             qint64 size = ( partition->lastSector() - partition->firstSector() + 1 ) * m_device->logicalSize();
181             return KFormat().formatByteSize( size );
182         }
183         cDebug() << "Unknown column" << col;
184         return QVariant();
185     }
186     case Qt::DecorationRole:
187         if ( index.column() == NameColumn )
188         {
189             return ColorUtils::colorForPartition( partition );
190         }
191         else
192         {
193             return QVariant();
194         }
195     case Qt::ToolTipRole:
196     {
197         int col = index.column();
198         QString name;
199         if ( col == NameColumn )
200         {
201             if ( isPartitionFreeSpace( partition ) )
202             {
203                 name = tr( "Free Space" );
204             }
205             else
206             {
207                 name = isPartitionNew( partition ) ? tr( "New partition" ) : partition->partitionPath();
208             }
209         }
210         QString prettyFileSystem
211             = CalamaresUtils::Partition::prettyNameForFileSystemType( partition->fileSystem().type() );
212         qint64 size = ( partition->lastSector() - partition->firstSector() + 1 ) * m_device->logicalSize();
213         QString prettySize = KFormat().formatByteSize( size );
214         return QVariant( name + " " + prettyFileSystem + " " + prettySize );
215     }
216     case SizeRole:
217         return ( partition->lastSector() - partition->firstSector() + 1 ) * m_device->logicalSize();
218     case IsFreeSpaceRole:
219         return isPartitionFreeSpace( partition );
220 
221     case IsPartitionNewRole:
222         return isPartitionNew( partition );
223 
224     case FileSystemLabelRole:
225         if ( partition->fileSystem().supportGetLabel() != FileSystem::cmdSupportNone
226              && !partition->fileSystem().label().isEmpty() )
227         {
228             return partition->fileSystem().label();
229         }
230         return QVariant();
231 
232     case FileSystemTypeRole:
233         return partition->fileSystem().type();
234 
235     case PartitionPathRole:
236         return partition->partitionPath();
237 
238     case PartitionPtrRole:
239         return QVariant::fromValue( (void*)partition );
240 
241     // Osprober roles:
242     case OsproberNameRole:
243         foreach ( const OsproberEntry& osproberEntry, m_osproberEntries )
244             if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone
245                  && !partition->fileSystem().uuid().isEmpty() && osproberEntry.uuid == partition->fileSystem().uuid() )
246             {
247                 return osproberEntry.prettyName;
248             }
249         return QVariant();
250     case OsproberPathRole:
251         foreach ( const OsproberEntry& osproberEntry, m_osproberEntries )
252             if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone
253                  && !partition->fileSystem().uuid().isEmpty() && osproberEntry.uuid == partition->fileSystem().uuid() )
254             {
255                 return osproberEntry.path;
256             }
257         return QVariant();
258     case OsproberCanBeResizedRole:
259         foreach ( const OsproberEntry& osproberEntry, m_osproberEntries )
260             if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone
261                  && !partition->fileSystem().uuid().isEmpty() && osproberEntry.uuid == partition->fileSystem().uuid() )
262             {
263                 return osproberEntry.canBeResized;
264             }
265         return QVariant();
266     case OsproberRawLineRole:
267         foreach ( const OsproberEntry& osproberEntry, m_osproberEntries )
268             if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone
269                  && !partition->fileSystem().uuid().isEmpty() && osproberEntry.uuid == partition->fileSystem().uuid() )
270             {
271                 return osproberEntry.line;
272             }
273         return QVariant();
274     case OsproberHomePartitionPathRole:
275         foreach ( const OsproberEntry& osproberEntry, m_osproberEntries )
276             if ( partition->fileSystem().supportGetUUID() != FileSystem::cmdSupportNone
277                  && !partition->fileSystem().uuid().isEmpty() && osproberEntry.uuid == partition->fileSystem().uuid() )
278             {
279                 return osproberEntry.homePath;
280             }
281         return QVariant();
282         // end Osprober roles.
283 
284     default:
285         return QVariant();
286     }
287 }
288 
289 QVariant
headerData(int section,Qt::Orientation,int role) const290 PartitionModel::headerData( int section, Qt::Orientation, int role ) const
291 {
292     if ( role != Qt::DisplayRole )
293     {
294         return QVariant();
295     }
296 
297     switch ( section )
298     {
299     case NameColumn:
300         return tr( "Name" );
301     case FileSystemColumn:
302         return tr( "File System" );
303     case FileSystemLabelColumn:
304         return tr( "File System Label" );
305     case MountPointColumn:
306         return tr( "Mount Point" );
307     case SizeColumn:
308         return tr( "Size" );
309     default:
310         cDebug() << "Unknown column" << section;
311         return QVariant();
312     }
313 }
314 
315 Partition*
partitionForIndex(const QModelIndex & index) const316 PartitionModel::partitionForIndex( const QModelIndex& index ) const
317 {
318     QMutexLocker lock( &m_lock );
319     if ( !index.isValid() )
320     {
321         return nullptr;
322     }
323     return reinterpret_cast< Partition* >( index.internalPointer() );
324 }
325 
326 
327 void
update()328 PartitionModel::update()
329 {
330     Q_EMIT dataChanged( index( 0, 0 ), index( rowCount() - 1, columnCount() - 1 ) );
331 }
332