Skip to content

Commit 64d646a

Browse files
committed
Replaced image file error dialogs with box in image area
Less intrusive and disruptive, more convenient and more options. Buttons to remove or replace the file or open the relocation dialog. Other changes: - Pre-selected directories based on existing photos when adding more or replacing one - Check whether images can be loaded when adding them - Includes translation Closes #167
1 parent 1bafdb3 commit 64d646a

File tree

8 files changed

+428
-91
lines changed

8 files changed

+428
-91
lines changed

src/dialogs/ascent_dialog.cpp

+110-8
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,20 @@ void AscentDialog::handle_removeHikers()
423423
*/
424424
void AscentDialog::handle_addPhotos()
425425
{
426-
QStringList filepaths = openFileDialogForPhotosSelection(this);
426+
// Determine path at which file dialog will open
427+
int photoIndexForDir = -1;
428+
QItemSelectionModel* selectionModel = photosListView->selectionModel();
429+
if (selectionModel->hasSelection()) { // Use first selected photo if possible
430+
photoIndexForDir = selectionModel->selectedRows().first().row();
431+
} else if (!photosModel.isEmpty()) { // Otherwise, use first photo if it exists
432+
photoIndexForDir = 0;
433+
}
434+
QString preSelectedDir = QString();
435+
if (photoIndexForDir >= 0) {
436+
preSelectedDir = QFileInfo(photosModel.getFilepathAt(photoIndexForDir)).path();
437+
}
438+
439+
QStringList filepaths = openFileDialogForMultiPhotoSelection(this, preSelectedDir);
427440
if (filepaths.isEmpty()) return;
428441

429442
QList<Photo> photos = QList<Photo>();
@@ -681,17 +694,106 @@ static BufferRowIndex openAscentDialogAndStore(QWidget* parent, Database* db, Di
681694

682695

683696
/**
684-
* Opens a file dialog for selecting photos.
697+
* Opens a file dialog for selecting a single image file.
685698
*
686-
* @param parent The parent window.
687-
* @return The selected filepaths.
699+
* @param parent The parent window.
700+
* @param preSelectedDir The directory to open the file dialog in initially, or an empty QString.
701+
* @param overrideWindowTitle The window title to use, or an empty QString to use the default one.
702+
* @return The selected filepath, or an empty QString if the dialog was cancelled.
703+
*/
704+
QString openFileDialogForSinglePhotoSelection(QWidget* parent, QString preSelectedDir, QString overrideWindowTitle)
705+
{
706+
QString caption = AscentDialog::tr("Select photo of ascent");
707+
if (!overrideWindowTitle.isEmpty()) caption = overrideWindowTitle;
708+
QString filter = getImageFileDialogFilterString();
709+
QString filepath = QFileDialog::getOpenFileName(parent, caption, preSelectedDir, filter);
710+
711+
QStringList checkedPath = checkFilepathsAndAskUser(parent, {filepath});
712+
if (checkedPath.isEmpty()) return QString();
713+
return checkedPath.first();
714+
}
715+
716+
/**
717+
* Opens a file dialog for selecting multiple image files.
718+
*
719+
* @param parent The parent window.
720+
* @param preSelectedDir The directory to open the file dialog in initially, or an empty QString.
721+
* @param overrideWindowTitle The window title to use, or an empty QString to use the default one.
722+
* @return The selected filepaths, or an empty QStringList if the dialog was cancelled.
688723
*/
689-
QStringList openFileDialogForPhotosSelection(QWidget* parent)
724+
QStringList openFileDialogForMultiPhotoSelection(QWidget* parent, QString preSelectedDir, QString overrideWindowTitle)
690725
{
691726
QString caption = AscentDialog::tr("Select photos of ascent");
692-
QString preSelectedDir = QString();
693-
QString filter = AscentDialog::tr("Images") + " (*.jpg *.jpeg *.png *.bmp *.gif *.pbm *.pgm *.ppm *.xbm *.xpm);;"
694-
+ AscentDialog::tr("All files") + " (*.*)";
727+
if (!overrideWindowTitle.isEmpty()) caption = overrideWindowTitle;
728+
QString filter = getImageFileDialogFilterString();
695729
QStringList filepaths = QFileDialog::getOpenFileNames(parent, caption, preSelectedDir, filter);
730+
731+
filepaths = checkFilepathsAndAskUser(parent, filepaths);
732+
733+
return filepaths;
734+
}
735+
736+
/**
737+
* Returns the translated filter string to use for image file dialogs.
738+
* @return The translated filter string.
739+
*/
740+
QString getImageFileDialogFilterString()
741+
{
742+
return AscentDialog::tr("Images") + " (*.jpg *.jpeg *.png *.bmp *.gif *.pbm *.pgm *.ppm *.xbm *.xpm);;"
743+
+ AscentDialog::tr("All files") + " (*.*)";
744+
}
745+
746+
/**
747+
* Checks whether the given filepaths can be read as images and asks the user whether to add them
748+
* anyway if they cannot.
749+
*
750+
* @param parent The parent window.
751+
* @param filepaths The filepaths to check.
752+
* @return The filepaths which are unproblematic or confirmed by the user. Empty if the user cancels.
753+
*/
754+
QStringList checkFilepathsAndAskUser(QWidget* parent, QStringList filepaths)
755+
{
756+
bool noToAll = false;
757+
758+
for (int i = 0; i < filepaths.size(); i++) {
759+
QString filepath = filepaths.at(i);
760+
QImageReader reader = QImageReader(filepath);
761+
if (reader.canRead()) continue;
762+
763+
if (noToAll) {
764+
filepaths.remove(i--); // Remove this path and skip the dialog
765+
continue;
766+
}
767+
768+
QString title = AscentDialog::tr("File error");
769+
QString message = AscentDialog::tr("Image file cannot be read:\n%1"
770+
"\nReason: %2."
771+
"\n\nDo you want to use it anyway?")
772+
.arg(filepath, reader.errorString());
773+
auto buttons = QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel;
774+
if (filepaths.size() - 1 > i) {
775+
buttons |= QMessageBox::YesToAll | QMessageBox::NoToAll;
776+
}
777+
auto pressedButton = QMessageBox::warning(parent, title, message, buttons);
778+
779+
if (pressedButton == QMessageBox::Yes) {
780+
// Do nothing
781+
}
782+
else if (pressedButton == QMessageBox::YesToAll) {
783+
break; // Leave loop, no more paths need to be checked or removed
784+
}
785+
else if (pressedButton == QMessageBox::No) {
786+
filepaths.remove(i--); // Remove this path
787+
}
788+
else if (pressedButton == QMessageBox::NoToAll) {
789+
filepaths.remove(i--); // Remove this path and set flag
790+
noToAll = true;
791+
}
792+
else if (pressedButton == QMessageBox::Cancel) {
793+
return QStringList(); // Return with empty list
794+
}
795+
else assert(false);
796+
}
797+
696798
return filepaths;
697799
}

src/dialogs/ascent_dialog.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,10 @@ BufferRowIndex openDuplicateAscentDialogAndStore (QWidget* parent, Database* db,
9999
void openEditAscentDialogAndStore (QWidget* parent, Database* db, BufferRowIndex bufferRowIndex);
100100
void openDeleteAscentDialogAndExecute (QWidget* parent, Database* db, BufferRowIndex bufferRowIndex);
101101

102-
QStringList openFileDialogForPhotosSelection(QWidget* parent);
102+
QString openFileDialogForSinglePhotoSelection (QWidget* parent, QString preSelectedDir = QString(), QString overrideWindowTitle = QString());
103+
QStringList openFileDialogForMultiPhotoSelection (QWidget* parent, QString preSelectedDir = QString(), QString overrideWindowTitle = QString());
104+
QString getImageFileDialogFilterString();
105+
QStringList checkFilepathsAndAskUser(QWidget* parent, QStringList filepaths);
103106

104107

105108

src/dialogs/list/photo_list.cpp

+22
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,28 @@ void PhotosOfAscent::addPhotos(const QList<Photo>& photos)
5757
endInsertRows();
5858
}
5959

60+
61+
/**
62+
* Indicates whether the photos list is currently empty.
63+
*
64+
* @return True if there are currently no photos in the list, false otherwise.
65+
*/
66+
bool PhotosOfAscent::isEmpty() const
67+
{
68+
return list.isEmpty();
69+
}
70+
71+
/**
72+
* Returns the file path of the photo at the given row index.
73+
*
74+
* @param rowIndex The row index of the photo.
75+
* @return The file path of the photo.
76+
*/
77+
const QString& PhotosOfAscent::getFilepathAt(int rowIndex) const
78+
{
79+
return list[rowIndex].filepath;
80+
}
81+
6082
/**
6183
* Returns the description of the photo at the given row index.
6284
*

src/dialogs/list/photo_list.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,10 @@ class PhotosOfAscent : public QAbstractItemModel {
4444
public:
4545
PhotosOfAscent();
4646

47-
void addPhotos(const QList<Photo>& photo);
47+
void addPhotos(const QList<Photo>& photo);
48+
49+
bool isEmpty() const;
50+
const QString& getFilepathAt(int rowIndex) const;
4851
const QString& getDescriptionAt(int rowIndex) const;
4952
void setDescriptionAt(int rowIndex, QString description);
5053
void removePhotoAt(int rowIndex);

src/ui/ascent_viewer.ui

+103-2
Original file line numberDiff line numberDiff line change
@@ -1093,11 +1093,11 @@
10931093
<rect>
10941094
<x>0</x>
10951095
<y>0</y>
1096-
<width>666</width>
1096+
<width>709</width>
10971097
<height>804</height>
10981098
</rect>
10991099
</property>
1100-
<layout class="QVBoxLayout" name="imageScrollAreaLayout">
1100+
<layout class="QHBoxLayout" name="imageScrollAreaLayout">
11011101
<property name="spacing">
11021102
<number>0</number>
11031103
</property>
@@ -1113,6 +1113,107 @@
11131113
<property name="bottomMargin">
11141114
<number>0</number>
11151115
</property>
1116+
<item>
1117+
<widget class="QGroupBox" name="imageErrorGroupBox">
1118+
<property name="sizePolicy">
1119+
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
1120+
<horstretch>0</horstretch>
1121+
<verstretch>0</verstretch>
1122+
</sizepolicy>
1123+
</property>
1124+
<property name="font">
1125+
<font>
1126+
<pointsize>10</pointsize>
1127+
</font>
1128+
</property>
1129+
<property name="title">
1130+
<string>Image could not be loaded</string>
1131+
</property>
1132+
<property name="alignment">
1133+
<set>Qt::AlignCenter</set>
1134+
</property>
1135+
<layout class="QVBoxLayout" name="verticalLayout">
1136+
<property name="spacing">
1137+
<number>10</number>
1138+
</property>
1139+
<property name="leftMargin">
1140+
<number>10</number>
1141+
</property>
1142+
<property name="topMargin">
1143+
<number>10</number>
1144+
</property>
1145+
<property name="rightMargin">
1146+
<number>10</number>
1147+
</property>
1148+
<property name="bottomMargin">
1149+
<number>10</number>
1150+
</property>
1151+
<item>
1152+
<widget class="QLabel" name="imageErrorLabel">
1153+
<property name="font">
1154+
<font>
1155+
<pointsize>9</pointsize>
1156+
</font>
1157+
</property>
1158+
<property name="text">
1159+
<string notr="true">This image file cannot be shown:
1160+
&lt;FILENAME&gt;
1161+
Reason: File not found.
1162+
1163+
You can remove the image, replace the file, or mass relocate image files in the whole database.
1164+
&lt;CAUTION: This text is always replaced from the code.&gt;</string>
1165+
</property>
1166+
<property name="wordWrap">
1167+
<bool>true</bool>
1168+
</property>
1169+
</widget>
1170+
</item>
1171+
<item>
1172+
<layout class="QHBoxLayout" name="imageErrorButtonLayout">
1173+
<property name="spacing">
1174+
<number>10</number>
1175+
</property>
1176+
<item>
1177+
<widget class="QPushButton" name="imageErrorRemoveButton">
1178+
<property name="font">
1179+
<font>
1180+
<pointsize>9</pointsize>
1181+
</font>
1182+
</property>
1183+
<property name="text">
1184+
<string>Remove</string>
1185+
</property>
1186+
</widget>
1187+
</item>
1188+
<item>
1189+
<widget class="QPushButton" name="imageErrorReplaceButton">
1190+
<property name="font">
1191+
<font>
1192+
<pointsize>9</pointsize>
1193+
</font>
1194+
</property>
1195+
<property name="text">
1196+
<string>Replace</string>
1197+
</property>
1198+
</widget>
1199+
</item>
1200+
<item>
1201+
<widget class="QPushButton" name="imageErrorRelocateButton">
1202+
<property name="font">
1203+
<font>
1204+
<pointsize>9</pointsize>
1205+
</font>
1206+
</property>
1207+
<property name="text">
1208+
<string>Mass relocate</string>
1209+
</property>
1210+
</widget>
1211+
</item>
1212+
</layout>
1213+
</item>
1214+
</layout>
1215+
</widget>
1216+
</item>
11161217
</layout>
11171218
</widget>
11181219
</widget>

0 commit comments

Comments
 (0)