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