Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/android/src/uk/co/lutraconsulting/MMActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ public String importImage(Uri imageUri, String targetPath) {
String fileName = getFileName( imageUri );
File newCopyFile = new File( targetPath + "/" + fileName );
try {
newCopyFile.getParentFile().mkdirs();
newCopyFile.createNewFile();
InputStream fileStream = getContentResolver().openInputStream( imageUri );
copyFile( fileStream, newCopyFile );
Expand Down
78 changes: 73 additions & 5 deletions app/attributes/attributecontroller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,8 @@ void AttributeController::updateOnFeatureChange()
const QVariant newVal = feature.attribute( fieldIndex );
mFormItems[itemData->id()]->setOriginalValue( newVal );
mFormItems[itemData->id()]->setRawValue( newVal ); // we need to set raw value as well, as we use it in form now
if ( mRememberAttributesController && isNewFeature() ) // this is a new feature
itemData->setReusedValue( false );
if ( mRememberAttributesController && isNewFeature() && newVal.toString().isEmpty() )
{
QVariant rememberedValue;
bool shouldUseRememberedValue = mRememberAttributesController->rememberedValue(
Expand All @@ -622,8 +623,35 @@ void AttributeController::updateOnFeatureChange()
);
if ( shouldUseRememberedValue )
{
mFeatureLayerPair.featureRef().setAttribute( fieldIndex, rememberedValue );
itemData->setRawValue( rememberedValue );
QVariant valueToUse = rememberedValue;

if ( itemData->editorWidgetType() == QStringLiteral( "ExternalResource" ) && !rememberedValue.toString().isEmpty() )
{
const QVariantMap config = itemData->editorWidgetConfig();
const FeatureLayerPair parentPair = mParentController ? mParentController->featureLayerPair() : FeatureLayerPair();
const QString targetDir = InputUtils::resolveTargetDir( QgsProject::instance()->homePath(), config, mFeatureLayerPair, parentPair, QgsProject::instance() );
const QString prefix = InputUtils::resolvePrefixForRelativePath( config[ QStringLiteral( "RelativeStorage" ) ].toInt(), QgsProject::instance()->homePath(), targetDir );
const QString src = InputUtils::getAbsolutePath( rememberedValue.toString(), prefix );
const QFileInfo fi( src );

static const QRegularExpression trailingCounter( QStringLiteral( "\\s\\(\\d+\\)$" ) );
QString baseName = fi.completeBaseName();
baseName.remove( trailingCounter );
const QString canonicalName = fi.suffix().isEmpty() ? baseName : QStringLiteral( "%1.%2" ).arg( baseName, fi.suffix() );

const QString dst = CoreUtils::findUniquePath( InputUtils::getAbsolutePath( canonicalName, targetDir ) );

if ( InputUtils::copyFile( src, dst ) )
{
valueToUse = InputUtils::getRelativePath( dst, prefix );
itemData->setReusedCopyPath( dst );
}
}

mFeatureLayerPair.featureRef().setAttribute( fieldIndex, valueToUse );
itemData->setRawValue( valueToUse );
itemData->setOriginalValue( valueToUse );
itemData->setReusedValue( true );
}
}
}
Expand Down Expand Up @@ -791,7 +819,8 @@ void AttributeController::recalculateDefaultValues(

bool shouldApplyDefaultValue =
!defaultDefinition.expression().isEmpty() &&
( isFirstUpdateOfNewFeature || ( isFormValueChange && defaultDefinition.applyOnUpdate() ) );
( isFirstUpdateOfNewFeature || ( isFormValueChange && defaultDefinition.applyOnUpdate() ) ) &&
!item->isReusedValue();

if ( shouldApplyDefaultValue )
{
Expand Down Expand Up @@ -1211,6 +1240,8 @@ bool AttributeController::deleteFeature()

bool AttributeController::rollback()
{
discardReusedPhotoCopies( true );

if ( !mFeatureLayerPair.layer() )
return false;

Expand Down Expand Up @@ -1293,6 +1324,11 @@ bool AttributeController::save()
disconnect( mFeatureLayerPair.layer(), &QgsVectorLayer::featureAdded, this, &AttributeController::onFeatureAdded );
}

if ( rv )
{
discardReusedPhotoCopies( false );
}

// Store the feature attributes for future use
if ( featureIsNew && mRememberAttributesController )
{
Expand Down Expand Up @@ -1497,6 +1533,7 @@ bool AttributeController::setFormValue( const QUuid &id, QVariant value )
QgsField field = item->field();
QVariant val( value );

item->setReusedValue( false );
item->setRawValue( val );
emit formDataChanged( item->id(), { AttributeFormModel::RawValue } );

Expand Down Expand Up @@ -1576,6 +1613,36 @@ void AttributeController::onFeatureAdded( QgsFeatureId newFeatureId )
emit featureIdChanged();
}

void AttributeController::discardReusedPhotoCopies( bool force )
{
QMap<QUuid, std::shared_ptr<FormItem>>::const_iterator formItemsIterator = mFormItems.constBegin();
while ( formItemsIterator != mFormItems.constEnd() )
{
std::shared_ptr<FormItem> item = formItemsIterator.value();
const QString copyPath = item->reusedCopyPath();
if ( !copyPath.isEmpty() )
{
bool stillReferenced = false;
if ( !force )
{
const QVariantMap config = item->editorWidgetConfig();
const FeatureLayerPair parentPair = mParentController ? mParentController->featureLayerPair() : FeatureLayerPair();
const QString targetDir = InputUtils::resolveTargetDir( QgsProject::instance()->homePath(), config, mFeatureLayerPair, parentPair, QgsProject::instance() );
const QString prefix = InputUtils::resolvePrefixForRelativePath( config[ QStringLiteral( "RelativeStorage" ) ].toInt(), QgsProject::instance()->homePath(), targetDir );
const QString currentPath = InputUtils::getAbsolutePath( mFeatureLayerPair.feature().attribute( item->fieldIndex() ).toString(), prefix );
stillReferenced = ( currentPath == copyPath );
}

if ( force || !stillReferenced )
{
InputUtils::removeFile( copyPath );
}
item->setReusedCopyPath( QString() );
}
++formItemsIterator;
}
}

void AttributeController::renamePhotos()
{
const QStringList photoNameFormat = QgsProject::instance()->entryList( QStringLiteral( "Mergin" ), QStringLiteral( "PhotoNaming/%1" ).arg( mFeatureLayerPair.layer()->id() ) );
Expand Down Expand Up @@ -1608,7 +1675,7 @@ void AttributeController::renamePhotos()
continue;
}

if ( item->originalValue() != mFeatureLayerPair.feature().attribute( item->fieldIndex() ) )
if ( item->isReusedValue() || item->originalValue() != mFeatureLayerPair.feature().attribute( item->fieldIndex() ) )
{
const QString expString = QgsProject::instance()->readEntry( QStringLiteral( "Mergin" ), QStringLiteral( "PhotoNaming/%1/%2" ).arg( mFeatureLayerPair.layer()->id(), field.name() ) );
QgsExpression exp( expString );
Expand Down Expand Up @@ -1656,6 +1723,7 @@ void AttributeController::renamePhotos()
{
const QString newValue = InputUtils::getRelativePath( dst, prefix );
setFormValue( item->id(), newValue );
item->setReusedCopyPath( QString() );
expressionContext.setFeature( featureLayerPair().featureRef() );
}
else
Expand Down
1 change: 1 addition & 0 deletions app/attributes/attributecontroller.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ class AttributeController : public QObject
*/
bool allowTabs( QgsAttributeEditorContainer *container );

void discardReusedPhotoCopies( bool force );
//! renames photos if necessary
void renamePhotos();
//! save temporary sketched image to original image
Expand Down
20 changes: 20 additions & 0 deletions app/attributes/attributedata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,26 @@ void FormItem::setOriginalValue( const QVariant &originalValue )
mOriginalValue = originalValue;
}

bool FormItem::isReusedValue() const
{
return mIsReusedValue;
}

void FormItem::setReusedValue( bool reused )
{
mIsReusedValue = reused;
}

QString FormItem::reusedCopyPath() const
{
return mReusedCopyPath;
}

void FormItem::setReusedCopyPath( const QString &path )
{
mReusedCopyPath = path;
}

QgsRelation FormItem::relation() const
{
return mRelation;
Expand Down
8 changes: 8 additions & 0 deletions app/attributes/attributedata.h
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,12 @@ class FormItem
QVariant rawValue() const;
void setRawValue( const QVariant &rawValue );

bool isReusedValue() const;
void setReusedValue( bool reused );

QString reusedCopyPath() const;
void setReusedCopyPath( const QString &path );

QgsRelation relation() const;
QString fieldError() const;

Expand All @@ -178,6 +184,8 @@ class FormItem
bool mVisible = false;
QVariant mOriginalValue; // original unmodified value
QVariant mRawValue;
bool mIsReusedValue = false;
QString mReusedCopyPath;

const QgsRelation mRelation; // Only used for FormItemType::Relation
};
Expand Down
4 changes: 4 additions & 0 deletions app/inpututils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,8 @@ QString InputUtils::resolveTargetDir( const QString &homePath, const QVariantMap
{
QString result = evaluateExpression( pair, parentPair, activeProject, expression );
sanitizePath( result );
if ( !result.isEmpty() && !QDir::isAbsolutePath( result ) )
result = QDir( homePath ).absoluteFilePath( result );
return result;
}
else
Expand All @@ -1029,6 +1031,8 @@ QString InputUtils::resolveTargetDir( const QString &homePath, const QVariantMap
}
else
{
if ( !QDir::isAbsolutePath( defaultRoot ) )
defaultRoot = QDir( homePath ).absoluteFilePath( defaultRoot );
return defaultRoot;
}
}
Expand Down
2 changes: 2 additions & 0 deletions app/ios/iosviewdelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ - ( void )picker:( PHPickerViewController * )picker didFinishPicking:( NSArray<P
BOOL writeSuccess = NO;
if ( url && !error )
{
[[NSFileManager defaultManager] createDirectoryAtPath:targetDir withIntermediateDirectories:YES attributes:nil error:nil];

NSError *copyError = nil;
[[NSFileManager defaultManager] copyItemAtURL:url toURL:[NSURL fileURLWithPath:imagePath] error:&copyError];
writeSuccess = ( copyError == nil );
Expand Down
11 changes: 9 additions & 2 deletions app/test/testutilsfunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ void TestUtilsFunctions::resolveTargetDir()
{
QString homePath = TestUtils::testDataDir();
QString DEFAULT_ROOT( "DEFAULT/ROOT/PATH" ); // can be not existing path
QString ABSOLUTE_DEFAULT_ROOT( "/absolute/default/root/path" ); // can be not existing path

QgsProject *activeProject = nullptr;
QVariantMap config;
Expand All @@ -344,10 +345,16 @@ void TestUtilsFunctions::resolveTargetDir()
QString resultDir = mUtils->resolveTargetDir( homePath, config, pair, FeatureLayerPair(), activeProject );
QCOMPARE( resultDir, homePath );

// case 2: defined default root config, no expression
// case 2: defined default root config as a relative path, no expression - resolved against homePath
config.insert( QStringLiteral( "DefaultRoot" ), DEFAULT_ROOT );
QString resultDir2 = mUtils->resolveTargetDir( homePath, config, pair, FeatureLayerPair(), activeProject );
QCOMPARE( resultDir2, DEFAULT_ROOT );
QCOMPARE( resultDir2, QStringLiteral( "%1/%2" ).arg( homePath, DEFAULT_ROOT ) );
config.clear();

// case 2b: defined default root config as an already-absolute path, no expression - stays unchanged
config.insert( QStringLiteral( "DefaultRoot" ), ABSOLUTE_DEFAULT_ROOT );
QString resultDir2b = mUtils->resolveTargetDir( homePath, config, pair, FeatureLayerPair(), activeProject );
QCOMPARE( resultDir2b, ABSOLUTE_DEFAULT_ROOT );
config.clear();

// case 3: defined expression in config->"PropertyCollection" -> "properties" -> "propertyRootPath" -> "expression"
Expand Down
Loading