diff --git a/Scribus.pro b/Scribus.pro
index 6918634c5b233dc1a3e69503c15d0a4a4eab308b..2fe7062e223a094f5efdc2704e124e4399d2ca67 100644
--- a/Scribus.pro
+++ b/Scribus.pro
@@ -1124,6 +1124,7 @@ FORMS += scribus/ui/aboutplugins.ui \
          scribus/ui/resourcemanagerbase.ui \
          scribus/ui/resourcemanagerlicensebase.ui \
          scribus/ui/rotationsetter.ui \
+         scribus/ui/searchbase.ui \
          scribus/ui/selectobjects.ui \
          scribus/ui/shortcutwidget.ui \
          scribus/ui/smcellstylewidget.ui \
diff --git a/scribus/CMakeLists.txt b/scribus/CMakeLists.txt
index 0fb1968990eb4b01c001167df5188429d4a0f03d..f0bfe93bbe90836492524174558c65d7de10c28f 100644
--- a/scribus/CMakeLists.txt
+++ b/scribus/CMakeLists.txt
@@ -182,6 +182,7 @@ set(SCRIBUS_UI_SRC
 	ui/replaceonecolor.ui
 	ui/resourcemanagerbase.ui
 	ui/resourcemanagerlicensebase.ui
+	ui/searchbase.ui
 	ui/selectobjects.ui
 	ui/shortcutwidget.ui
 	ui/smcellstylewidget.ui
diff --git a/scribus/appmodehelper.cpp b/scribus/appmodehelper.cpp
index 64fd09f8e5c373919037e7f213e20df1424c3cc4..80a7614f7b169514e41d72bd25c4b34a00653f6b 100644
--- a/scribus/appmodehelper.cpp
+++ b/scribus/appmodehelper.cpp
@@ -205,14 +205,7 @@ void AppModeHelper::setApplicationMode(ScribusMainWindow* scmw, ScribusDoc* doc,
 	{
 		case modeNormal:
 			{
-				bool editSearchReplace = false;
-				if (currItem != nullptr)
-				{
-					editSearchReplace |= currItem->isTextFrame();
-					editSearchReplace |= (currItem->itemText.length() > 0);
-					editSearchReplace |= (doc->m_Selection->count() == 1);
-				}
-				(*a_scrActions)["editSearchReplace"]->setEnabled(editSearchReplace);
+				(*a_scrActions)["editSearchReplace"]->setEnabled(true);
 
 				(*a_scrActions)["editCut"]->setEnabled(currItem != nullptr);
 				(*a_scrActions)["editCopy"]->setEnabled(currItem != nullptr);
@@ -287,20 +280,21 @@ void AppModeHelper::setApplicationMode(ScribusMainWindow* scmw, ScribusDoc* doc,
 					enableTextActions(true, currItem->currentCharStyle().font().scName());
 					currItem->asTextFrame()->toggleEditModeActions();
 				}
+				bool isTextFrame = ((currItem != nullptr) && (currItem->asTextFrame()));
 				if (ScMimeData::clipboardHasScribusData())
 				{
-					bool textFrameEditMode = ((currItem != nullptr) && (currItem->asTextFrame()));
-					(*a_scrActions)["editPaste"]->setEnabled( textFrameEditMode || (currItem == nullptr) );
+					(*a_scrActions)["editPaste"]->setEnabled( isTextFrame || (currItem == nullptr) );
 				}
 				setTextEditMode(true);
 
+				(*a_scrActions)["editSearchReplace"]->setEnabled(isTextFrame);
+
 				if (currItem != nullptr)
 				{
 					(*a_scrActions)["editCut"]->setEnabled(currItem->HasSel);
 					(*a_scrActions)["editCopy"]->setEnabled(currItem->HasSel);
 					(*a_scrActions)["editClearContents"]->setEnabled(currItem->HasSel);
 					(*a_scrActions)["editTruncateContents"]->setEnabled(currItem->HasSel && currItem->isTextFrame());
-					(*a_scrActions)["editSearchReplace"]->setEnabled(true);
 				}
 			}
 			break;
@@ -461,6 +455,7 @@ void AppModeHelper::enableActionsForSelection(ScribusMainWindow* scmw, ScribusDo
 	(*a_scrActions)["editEditWithImageEditor"]->setEnabled(isImageFrame && currItem->imageIsAvailable && currItem->isRaster);
 	(*a_scrActions)["editEditRenderSource"]->setEnabled(isImageFrame && currItem && (currItem->asLatexFrame() || currItem->asOSGFrame()));
 	(*a_scrActions)["itemAdjustFrameHeightToText"]->setEnabled(SelectedType==PageItem::TextFrame && currItem->itemText.length() >0);
+
 	if (!isImageFrame)
 	{
 		(*a_scrActions)["itemImageIsVisible"]->setChecked(false);
@@ -513,7 +508,6 @@ void AppModeHelper::enableActionsForSelection(ScribusMainWindow* scmw, ScribusDo
 			(*a_scrActions)["editCopyContents"]->setEnabled(false);
 			(*a_scrActions)["editClearContents"]->setEnabled(false);
 			(*a_scrActions)["editTruncateContents"]->setEnabled(false);
-			(*a_scrActions)["editSearchReplace"]->setEnabled(false);
 			(*a_scrActions)["extrasHyphenateText"]->setEnabled(false);
 			(*a_scrActions)["extrasDeHyphenateText"]->setEnabled(false);
 
@@ -538,7 +532,6 @@ void AppModeHelper::enableActionsForSelection(ScribusMainWindow* scmw, ScribusDo
 			(*a_scrActions)["editCopy"]->setEnabled(!inAnEditMode);
 			(*a_scrActions)["editClearContents"]->setEnabled(true);
 			(*a_scrActions)["editTruncateContents"]->setEnabled(false);
-			(*a_scrActions)["editSearchReplace"]->setEnabled(false);
 			(*a_scrActions)["extrasHyphenateText"]->setEnabled(false);
 			(*a_scrActions)["extrasDeHyphenateText"]->setEnabled(false);
 			(*a_scrActions)["itemDuplicate"]->setEnabled(true);
@@ -586,7 +579,6 @@ void AppModeHelper::enableActionsForSelection(ScribusMainWindow* scmw, ScribusDo
 			//scrMenuMgr->setMenuEnabled("EditContents", true);
 			(*a_scrActions)["editClearContents"]->setEnabled(true);
 			(*a_scrActions)["editTruncateContents"]->setEnabled(true);
-			(*a_scrActions)["editSearchReplace"]->setEnabled(currItem->itemText.length() != 0);
 			(*a_scrActions)["extrasHyphenateText"]->setEnabled(true);
 			(*a_scrActions)["extrasDeHyphenateText"]->setEnabled(true);
 			//		scrMenuMgr->setMenuEnabled("Item", true);
@@ -687,7 +679,6 @@ void AppModeHelper::enableActionsForSelection(ScribusMainWindow* scmw, ScribusDo
 			(*a_scrActions)["editCopy"]->setEnabled(!inAnEditMode);
 			(*a_scrActions)["editClearContents"]->setEnabled(false);
 			(*a_scrActions)["editTruncateContents"]->setEnabled(false);
-			(*a_scrActions)["editSearchReplace"]->setEnabled(false);
 			(*a_scrActions)["extrasHyphenateText"]->setEnabled(false);
 			(*a_scrActions)["extrasDeHyphenateText"]->setEnabled(false);
 			//		scrMenuMgr->setMenuEnabled("Item", true);
@@ -734,7 +725,6 @@ void AppModeHelper::enableActionsForSelection(ScribusMainWindow* scmw, ScribusDo
 			(*a_scrActions)["editCopy"]->setEnabled(!inAnEditMode);
 			(*a_scrActions)["editClearContents"]->setEnabled(false);
 			(*a_scrActions)["editTruncateContents"]->setEnabled(false);
-			(*a_scrActions)["editSearchReplace"]->setEnabled(false);
 
 			(*a_scrActions)["extrasHyphenateText"]->setEnabled(false);
 			(*a_scrActions)["extrasDeHyphenateText"]->setEnabled(false);
@@ -827,7 +817,6 @@ void AppModeHelper::enableActionsForSelection(ScribusMainWindow* scmw, ScribusDo
 			(*a_scrActions)["itemConvertToTextFrame"]->setEnabled(false);
 			(*a_scrActions)["itemConvertToSymbolFrame"]->setEnabled(false);
 		}
-		(*a_scrActions)["editSearchReplace"]->setEnabled(false);
 
 		bool hPoly = true;
 		for (int i = 0; i < docSelectionCount; ++i)
diff --git a/scribus/scribus.cpp b/scribus/scribus.cpp
index e71a3c3dc2237049130e61096863176c39fc0ec4..030d77f68d7b3598dca317e2c4c58b1b40c40855 100644
--- a/scribus/scribus.cpp
+++ b/scribus/scribus.cpp
@@ -8453,26 +8453,14 @@ void ScribusMainWindow::EditTabs()
 
 void ScribusMainWindow::SearchText()
 {
-	bool wasModeEdit = (doc->appMode == modeEdit);
-
-	PageItem *currItem = doc->m_Selection->itemAt(0);
-	if (!wasModeEdit)
-	{
-		view->requestMode(modeEdit);
-		currItem->itemText.setCursorPosition(0);
-	}
-	
-	SearchReplace* dia = new SearchReplace(this, doc, currItem);
-	if (wasModeEdit)
+	SearchReplace dia(this, doc);
+	if (doc->appMode == modeEdit)
 	{
-		QString selText = currItem->itemText.selectedText();
-		if (!selText.isEmpty())
-			dia->setSearchedText(selText);
+		PageItem *currItem = doc->m_Selection->itemAt(0);
+		dia.processCurrentSelection(currItem->itemText.selectedText());
 	}
-	dia->exec();
-	dia->disconnect();
-	delete dia;
-	//slotSelect();
+	dia.exec();
+	dia.disconnect();
 }
 
 /* call gimp and wait upon completion */
diff --git a/scribus/text/storytext.cpp b/scribus/text/storytext.cpp
index e59d3845e3d15dad5af527d62c615cf0ab94973a..2b35536cd6089cf848f21d54dd3e3ad64c1848b1 100644
--- a/scribus/text/storytext.cpp
+++ b/scribus/text/storytext.cpp
@@ -750,24 +750,19 @@ void StoryText::replaceSelection(const QString& newText)
 	int selLength = selectionLength();
 
 	int lengthDiff = newText.length() - selLength;
-	if (lengthDiff == 0)
-	{
-		for (int i = 0; i < selLength; ++i)
-			replaceChar(selStart + i, newText[i]);
-	}
-	else if (lengthDiff > 0)
+
+	if (lengthDiff > 0)
 	{
-		for (int i = 0; i < selLength; ++i)
-			replaceChar(selStart + i, newText[i]);
-		for (int i = selLength; i < newText.length(); ++i)
-			insertChars(selStart + i, newText.mid(i, 1), true);
+		// extend the selected string with its last char until the lenghts match
+		auto lastChar = selectedText().mid(selLength -1, 1);
+		insertChars(selStart + selLength -1, lastChar.repeated(lengthDiff), true);
 	}
-	else
-	{
-		for (int i = 0; i < newText.length(); ++i)
-			replaceChar(selStart + i, newText[i]);
+
+	for (int i = 0; i < newText.length(); ++i)
+		replaceChar(selStart + i, newText[i]);
+
+	if (lengthDiff < 0)
 		removeChars(selStart + newText.length(), -lengthDiff);
-	}
 
 	deselectAll();
 	if (newText.length() > 0)
diff --git a/scribus/ui/search.cpp b/scribus/ui/search.cpp
index 673486af5c09db03c6568a65c7232e61da4d02a3..4f3693392d7c1925b06bd1d6173e6b20c5b9c54e 100644
--- a/scribus/ui/search.cpp
+++ b/scribus/ui/search.cpp
@@ -6,19 +6,15 @@ for which a new license (GPL+exception) is in place.
 */
 #include "search.h"
 
-#include <QCheckBox>
-#include <QComboBox>
-#include <QGroupBox>
-#include <QHBoxLayout>
-#include <QGridLayout>
-#include <QVBoxLayout>
-#include <QLabel>
-#include <QLineEdit>
 #include <QListView>
-#include <QMessageBox>
-#include <QPixmap>
-#include <QPushButton>
+
+#include <QApplication>
+#include <QDesktopWidget>
 #include <QScopedValueRollback>
+#include <QCursor>
+#include <QSizePolicy>
+
+#include <QTimer>
 
 #include "appmodes.h"
 #include "canvas.h"
@@ -36,834 +32,811 @@ for which a new license (GPL+exception) is in place.
 #include "scrspinbox.h"
 #include "selection.h"
 #include "shadebutton.h"
+#include "styles/paragraphstyle.h"
+#include "styles/charstyle.h"
 #include "styleselect.h"
 #include "ui/storyeditor.h"
 #include "util.h"
 #include "util_text.h"
 
-SearchReplace::SearchReplace( QWidget* parent, ScribusDoc *doc, PageItem* ite, bool mode )
-	: QDialog( parent )
+SearchReplace::SearchReplace(QWidget* parent, ScribusDoc *doc)
+	: QDialog(parent)
 {
-	m_item = ite;
 	m_doc = doc;
-	m_itemMode = mode;
 
-	setModal(true);
-	setWindowTitle( tr( "Search/Replace" ) );
+	setupUi(this);
+
 	setWindowIcon(IconManager::instance().loadIcon("AppIcon.png"));
 
-	SearchReplaceLayout = new QVBoxLayout( this );
-	SearchReplaceLayout->setMargin(10);
-	SearchReplaceLayout->setSpacing(5);
-	SelLayout = new QHBoxLayout;
-	SelLayout->setMargin(0);
-	SelLayout->setSpacing(5);
-	Search = new QGroupBox( this );
-	Search->setTitle( tr( "Search for:" ) );
-	SearchLayout = new QGridLayout( Search );
-	SearchLayout->setMargin(5);
-	SearchLayout->setSpacing(2);
-	SearchLayout->setAlignment( Qt::AlignTop );
-	SText = new QCheckBox( Search );
-	SText->setText( tr( "Text" ) );
-	SearchLayout->addWidget( SText, 0, 0 );
-	SStyle = new QCheckBox( Search );
-	SStyle->setText( tr( "Style" ) );
-	SearchLayout->addWidget( SStyle, 1, 0 );
-	SAlign = new QCheckBox( Search );
-	SAlign->setText( tr( "Alignment" ) );
-	SearchLayout->addWidget( SAlign, 2, 0 );
-	SFont = new QCheckBox( Search );
-	SFont->setText( tr( "Font" ) );
-	SearchLayout->addWidget( SFont, 3, 0 );
-	SSize = new QCheckBox( Search );
-	SSize->setText( tr( "Font Size" ) );
-	SearchLayout->addWidget( SSize, 4, 0 );
-	SEffect = new QCheckBox( Search );
-	SEffect->setText( tr( "Font Effects" ) );
-	SearchLayout->addWidget( SEffect, 5, 0 );
-	SFill = new QCheckBox( Search);
-	SFill->setText( tr( "Fill Color" ) );
-	SearchLayout->addWidget( SFill, 6, 0 );
-	SFillS = new QCheckBox( Search );
-	SFillS->setText( tr( "Fill Shade" ) );
-	SearchLayout->addWidget( SFillS, 7, 0 );
-	SStroke = new QCheckBox( Search );
-	SStroke->setText( tr( "Stroke Color" ) );
-	SearchLayout->addWidget( SStroke, 8, 0 );
-	SStrokeS = new QCheckBox( Search );
-	SStrokeS->setText( tr( "Stroke Shade" ) );
-	SearchLayout->addWidget( SStrokeS, 9, 0 );
-	STextVal = new QLineEdit( Search );
-	STextVal->setEnabled(false);
-	SearchLayout->addWidget( STextVal, 0, 1 );
-	SStyleVal = new QComboBox( Search );
-	SStyleVal->setEditable(false);
-	for (int x = 0; x < doc->paragraphStyles().count(); ++x)
-		SStyleVal->addItem(doc->paragraphStyles()[x].name());
-	QListView *tmpView = dynamic_cast<QListView*>(SStyleVal->view()); Q_ASSERT(tmpView);
+	{
+		QSizePolicy sp = messageLabel->sizePolicy();
+		sp.setRetainSizeWhenHidden(true);
+		messageLabel->setSizePolicy(sp);
+	}
+	hideMessage();
+
+	for (int i = 0; i < doc->paragraphStyles().count(); ++i)
+	{
+		const auto name = doc->paragraphStyles()[i].name();
+		searchStyleComboBox->addItem(name);
+		replaceStyleComboBox->addItem(name);
+	}
+	searchStyleComboBox->setCurrentIndex(findParagraphStyle(doc, doc->currentStyle));
+	replaceStyleComboBox->setCurrentIndex(findParagraphStyle(doc, doc->currentStyle));
+	/*
+	QListView *tmpView = dynamic_cast<QListView*>(searchStyleComboBox->view()); Q_ASSERT(tmpView);
 	int tmpWidth = tmpView->sizeHintForColumn(0);
 	if (tmpWidth > 0)
 		tmpView->setMinimumWidth(tmpWidth + 24);
-	SStyleVal->setCurrentIndex(findParagraphStyle(doc, doc->currentStyle));
-	SStyleVal->setEnabled(false);
-	SearchLayout->addWidget( SStyleVal, 1, 1 );
-	SAlignVal = new QComboBox( Search );
-	SAlignVal->setEditable(false);
-	QString tmp_sty[] = { tr("Left"), tr("Center"), tr("Right"), tr("Block"), tr("Forced")};
-	size_t ar_sty = sizeof(tmp_sty) / sizeof(*tmp_sty);
-	for (uint a = 0; a < ar_sty; ++a)
-		SAlignVal->addItem( tmp_sty[a] );
-	tmpView = dynamic_cast<QListView*>(SAlignVal->view()); Q_ASSERT(tmpView);
+	tmpView = dynamic_cast<QListView*>(replaceStyleComboBox->view()); Q_ASSERT(tmpView);
 	tmpWidth = tmpView->sizeHintForColumn(0);
 	if (tmpWidth > 0)
 		tmpView->setMinimumWidth(tmpWidth + 24);
-	SAlignVal->setEnabled(false);
-	SearchLayout->addWidget( SAlignVal, 2, 1 );
-	SFontVal = new FontCombo(Search);
-	SFontVal->setMaximumSize(190, 30);
-	setCurrentComboItem(SFontVal, doc->currentStyle.charStyle().font().scName());
-	SFontVal->setEnabled(false);
-	SearchLayout->addWidget( SFontVal, 3, 1 );
-	SSizeVal = new ScrSpinBox( 0.5, 2048, Search, 0 );
-	SSizeVal->setValue( doc->currentStyle.charStyle().fontSize() / 10.0 );
-	SSizeVal->setEnabled(false);
-	SearchLayout->addWidget( SSizeVal, 4, 1 );
-	SEffVal = new StyleSelect( Search );
-	SEffVal->setStyle(0);
-	SEffVal->setEnabled(false);
-	SearchLayout->addWidget( SEffVal, 5, 1, Qt::AlignLeft );
-	SFillVal = new ColorCombo( Search );
-	SFillVal->setEditable(false);
-	SFillVal->setPixmapType(ColorCombo::fancyPixmaps);
-	SFillVal->setColors(doc->PageColors, true);
-	SFillVal->setMinimumWidth(SFillVal->view()->maximumViewportSize().width() + 24);
-	setCurrentComboItem(SFillVal, doc->currentStyle.charStyle().fillColor());
-	SFillVal->setEnabled(false);
-	SearchLayout->addWidget( SFillVal, 6, 1 );
-	SFillSVal = new ShadeButton(Search);
-	SFillSVal->setEnabled(false);
-	SearchLayout->addWidget( SFillSVal, 7, 1, Qt::AlignLeft );
-	SStrokeVal = new ColorCombo( Search );
-	SStrokeVal->setEditable(false);
-	SStrokeVal->setPixmapType(ColorCombo::fancyPixmaps);
-	SStrokeVal->setColors(doc->PageColors, true);
-	SStrokeVal->view()->setMinimumWidth(SStrokeVal->view()->maximumViewportSize().width() + 24);
-	setCurrentComboItem(SStrokeVal, doc->currentStyle.charStyle().strokeColor());
-	SStrokeVal->setEnabled(false);
-	SearchLayout->addWidget( SStrokeVal, 8, 1 );
-	SStrokeSVal =  new ShadeButton(Search);
-	SStrokeSVal->setEnabled(false);
-	SearchLayout->addWidget( SStrokeSVal, 9, 1, Qt::AlignLeft );
-	SelLayout->addWidget( Search );
-
-	Replace = new QGroupBox( this );
-	Replace->setTitle( tr( "Replace with:" ) );
-	ReplaceLayout = new QGridLayout( Replace );
-	ReplaceLayout->setSpacing( 2 );
-	ReplaceLayout->setMargin( 5 );
-	ReplaceLayout->setAlignment( Qt::AlignTop );
-	RText = new QCheckBox( Replace );
-	RText->setText( tr( "Text" ) );
-	ReplaceLayout->addWidget( RText, 0, 0 );
-	RStyle = new QCheckBox( Replace );
-	RStyle->setText( tr( "Style" ) );
-	ReplaceLayout->addWidget( RStyle, 1, 0 );
-	RAlign = new QCheckBox( Replace );
-	RAlign->setText( tr( "Alignment" ) );
-	ReplaceLayout->addWidget( RAlign, 2, 0 );
-	RFont = new QCheckBox( Replace );
-	RFont->setText( tr( "Font" ) );
-	ReplaceLayout->addWidget( RFont, 3, 0 );
-	RSize = new QCheckBox( Replace );
-	RSize->setText( tr( "Font Size" ) );
-	ReplaceLayout->addWidget( RSize, 4, 0 );
-	REffect = new QCheckBox( Replace );
-	REffect->setText( tr( "Font Effects" ) );
-	ReplaceLayout->addWidget( REffect, 5, 0 );
-	RFill = new QCheckBox( Replace );
-	RFill->setText( tr( "Fill Color" ) );
-	ReplaceLayout->addWidget( RFill, 6, 0 );
-	RFillS = new QCheckBox( Replace );
-	RFillS->setText( tr( "Fill Shade" ) );
-	ReplaceLayout->addWidget( RFillS, 7, 0 );
-	RStroke = new QCheckBox( Replace );
-	RStroke->setText( tr( "Stroke Color" ) );
-	ReplaceLayout->addWidget( RStroke, 8, 0 );
-	RStrokeS = new QCheckBox( Replace );
-	RStrokeS->setText( tr( "Stroke Shade" ) );
-	ReplaceLayout->addWidget( RStrokeS, 9, 0 );
-	RTextVal = new QLineEdit( Replace );
-	RTextVal->setEnabled(false);
-	ReplaceLayout->addWidget( RTextVal, 0, 1 );
-	RStyleVal = new QComboBox( Replace );
-	RStyleVal->setEditable(false);
-	for (int x = 0; x < doc->paragraphStyles().count(); ++x)
-		RStyleVal->addItem(doc->paragraphStyles()[x].name());
-	tmpView = dynamic_cast<QListView*>(RStyleVal->view()); Q_ASSERT(tmpView);
+	*/
+
+	{
+		QStringList alignment{tr("Left"), tr("Center"), tr("Right"), tr("Block"), tr("Forced")};
+		searchAlignmentComboBox->addItems(alignment);
+		replaceAlignmentComboBox->addItems(alignment);
+	}
+
+	/*
+	tmpView = dynamic_cast<QListView*>(searchAlignmentComboBox->view()); Q_ASSERT(tmpView);
 	tmpWidth = tmpView->sizeHintForColumn(0);
 	if (tmpWidth > 0)
 		tmpView->setMinimumWidth(tmpWidth + 24);
-	RStyleVal->setCurrentIndex(findParagraphStyle(doc, doc->currentStyle));
-	RStyleVal->setEnabled(false);
-	ReplaceLayout->addWidget( RStyleVal, 1, 1 );
-	RAlignVal = new QComboBox( Replace );
-	RAlignVal->setEditable(false);
-	for (uint a = 0; a < ar_sty; ++a)
-		RAlignVal->addItem(tmp_sty[a]);
-	tmpView = dynamic_cast<QListView*>(RAlignVal->view()); Q_ASSERT(tmpView);
+	tmpView = dynamic_cast<QListView*>(replaceAlignmentComboBox->view()); Q_ASSERT(tmpView);
 	tmpWidth = tmpView->sizeHintForColumn(0);
 	if (tmpWidth > 0)
 		tmpView->setMinimumWidth(tmpWidth + 24);
-	RAlignVal->setEnabled(false);
-	ReplaceLayout->addWidget( RAlignVal, 2, 1 );
-	RFontVal = new FontCombo(Replace);
-	RFontVal->setMaximumSize(190, 30);
-	setCurrentComboItem(RFontVal, doc->currentStyle.charStyle().font().scName());
-	RFontVal->setEnabled(false);
-	ReplaceLayout->addWidget( RFontVal, 3, 1 );
-	RSizeVal = new ScrSpinBox( 0.5, 2048, Replace, 0 );
-	RSizeVal->setValue( doc->currentStyle.charStyle().fontSize() / 10.0 );
-	RSizeVal->setEnabled(false);
-	ReplaceLayout->addWidget( RSizeVal, 4, 1 );
-	REffVal = new StyleSelect( Replace );
-	REffVal->setStyle(0);
-	REffVal->setEnabled(false);
-	ReplaceLayout->addWidget( REffVal, 5, 1, Qt::AlignLeft );
-	RFillVal = new ColorCombo( true, Replace );
-	RFillVal->setEditable(false);
-	RFillVal->setPixmapType(ColorCombo::fancyPixmaps);
-	RFillVal->setColors(doc->PageColors, true);
-	RFillVal->view()->setMinimumWidth(RFillVal->view()->maximumViewportSize().width() + 24);
-	setCurrentComboItem(RFillVal, doc->currentStyle.charStyle().fillColor());
-	RFillVal->setEnabled(false);
-	ReplaceLayout->addWidget( RFillVal, 6, 1 );
-	RFillSVal = new ShadeButton(Replace);
-	RFillSVal->setEnabled(false);
-	ReplaceLayout->addWidget( RFillSVal, 7, 1, Qt::AlignLeft );
-	RStrokeVal = new ColorCombo( true, Replace );
-	RStrokeVal->setEditable(false);
-	RStrokeVal->setPixmapType(ColorCombo::fancyPixmaps);
-	RStrokeVal->setColors(doc->PageColors, true);
-	RStrokeVal->view()->setMinimumWidth(RStrokeVal->view()->maximumViewportSize().width() + 24);
-	setCurrentComboItem(RStrokeVal, doc->currentStyle.charStyle().strokeColor());
-	RStrokeVal->setEnabled(false);
-	ReplaceLayout->addWidget( RStrokeVal, 8, 1 );
-	RStrokeSVal = new ShadeButton(Replace);;
-	RStrokeSVal->setEnabled(false);
-	ReplaceLayout->addWidget( RStrokeSVal, 9, 1, Qt::AlignLeft );
-	SelLayout->addWidget( Replace );
-	SearchReplaceLayout->addLayout( SelLayout );
-
-	OptsLayout = new QHBoxLayout;
-	OptsLayout->setSpacing( 5 );
-	OptsLayout->setMargin( 0 );
-	Word = new QCheckBox( tr( "&Whole Word" ), this );
-	if (mode)
-		Word->setEnabled(false);
-	OptsLayout->addWidget( Word );
-	CaseIgnore = new QCheckBox( tr( "&Ignore Case, Diacritics and Kashida" ), this );
-	if (mode)
-		CaseIgnore->setEnabled(false);
-	OptsLayout->addWidget( CaseIgnore );
-	SearchReplaceLayout->addLayout( OptsLayout );
-
-	ButtonsLayout = new QHBoxLayout;
-	ButtonsLayout->setSpacing( 5 );
-	ButtonsLayout->setMargin( 0 );
-	DoSearch = new QPushButton( tr( "&Search" ), this );
-	DoSearch->setDefault( true );
-	ButtonsLayout->addWidget( DoSearch );
-	DoReplace = new QPushButton( tr( "&Replace" ), this );
-	DoReplace->setEnabled(false);
-	ButtonsLayout->addWidget( DoReplace );
-	AllReplace = new QPushButton( tr( "Replace &All" ), this );
-	AllReplace->setEnabled(false);
-	ButtonsLayout->addWidget( AllReplace );
-	clearButton = new QPushButton( tr("C&lear"), this);
-	ButtonsLayout->addWidget(clearButton);
-	Leave = new QPushButton( tr( "&Close" ), this );
-	ButtonsLayout->addWidget( Leave );
-	SearchReplaceLayout->addLayout( ButtonsLayout );
-
-	resize(minimumSizeHint());
-
- // signals and slots connections
-	connect( Leave, SIGNAL( clicked() ), this, SLOT( writePrefs() ) );
-	connect( DoSearch, SIGNAL( clicked() ), this, SLOT( slotSearch() ) );
-	connect( DoReplace, SIGNAL( clicked() ), this, SLOT( slotReplace() ) );
-	connect( AllReplace, SIGNAL( clicked() ), this, SLOT( slotReplaceAll() ) );
-	connect( STextVal, SIGNAL( textChanged(QString) ), this, SLOT( updateSearchButtonState() ) );
-	connect( SText, SIGNAL( clicked() ), this, SLOT( enableTxSearch() ) );
-	connect( SStyle, SIGNAL( clicked() ), this, SLOT( enableStyleSearch() ) );
-	connect( SAlign, SIGNAL( clicked() ), this, SLOT( enableAlignSearch() ) );
-	connect( SFont, SIGNAL( clicked() ), this, SLOT( enableFontSearch() ) );
-	connect( SSize, SIGNAL( clicked() ), this, SLOT( enableSizeSearch() ) );
-	connect( SEffect, SIGNAL( clicked() ), this, SLOT( enableEffSearch() ) );
-	connect( SFill, SIGNAL( clicked() ), this, SLOT( enableFillSearch() ) );
-	connect( SFillS, SIGNAL( clicked() ), this, SLOT( enableFillSSearch() ) );
-	connect( SStrokeS, SIGNAL( clicked() ), this, SLOT( enableStrokeSSearch() ) );
-	connect( SStroke, SIGNAL( clicked() ), this, SLOT( enableStrokeSearch() ) );
-	connect( RText, SIGNAL( clicked() ), this, SLOT( enableTxReplace() ) );
-	connect( RStyle, SIGNAL( clicked() ), this, SLOT( enableStyleReplace() ) );
-	connect( RAlign, SIGNAL( clicked() ), this, SLOT( enableAlignReplace() ) );
-	connect( RFont, SIGNAL( clicked() ), this, SLOT( enableFontReplace() ) );
-	connect( RSize, SIGNAL( clicked() ), this, SLOT( enableSizeReplace() ) );
-	connect( REffect, SIGNAL( clicked() ), this, SLOT( enableEffReplace() ) );
-	connect( RFill, SIGNAL( clicked() ), this, SLOT( enableFillReplace() ) );
-	connect( RStroke, SIGNAL( clicked() ), this, SLOT( enableStrokeReplace() ) );
-	connect( RFillS, SIGNAL( clicked() ), this, SLOT( enableFillSReplace() ) );
-	connect( RStrokeS, SIGNAL( clicked() ), this, SLOT( enableStrokeSReplace() ) );
-	connect(clearButton, SIGNAL(clicked()), this, SLOT(clear()));
+	*/
+	/*
+	searchFontComboBox->setMaximumSize(190, 30);
+	setCurrentComboItem(searchFontComboBox, doc->currentStyle.charStyle().font().scName());
+	searchFontSizeSpinBox->setValue( doc->currentStyle.charStyle().fontSize() / 10.0 );
+	replaceFontComboBox->setMaximumSize(190, 30);
+	setCurrentComboItem(replaceFontComboBox, doc->currentStyle.charStyle().font().scName());
+	replaceFontSizeSpinBox->setValue( doc->currentStyle.charStyle().fontSize() / 10.0 );
+	*/
+
+	searchFillColorComboBox->setPixmapType(ColorCombo::fancyPixmaps);
+	searchFillColorComboBox->setColors(doc->PageColors, true);
+	searchFillColorComboBox->setMinimumWidth(searchFillColorComboBox->view()->maximumViewportSize().width() + 24);
+	setCurrentComboItem(searchFillColorComboBox, doc->currentStyle.charStyle().fillColor());
+
+	searchStrokeColorComboBox->setPixmapType(ColorCombo::fancyPixmaps);
+	searchStrokeColorComboBox->setColors(doc->PageColors, true);
+	searchStrokeColorComboBox->view()->setMinimumWidth(searchStrokeColorComboBox->view()->maximumViewportSize().width() + 24);
+	setCurrentComboItem(searchStrokeColorComboBox, doc->currentStyle.charStyle().strokeColor());
+
+	replaceFillColorComboBox->setPixmapType(ColorCombo::fancyPixmaps);
+	replaceFillColorComboBox->setColors(doc->PageColors, true);
+	replaceFillColorComboBox->view()->setMinimumWidth(replaceFillColorComboBox->view()->maximumViewportSize().width() + 24);
+	setCurrentComboItem(replaceFillColorComboBox, doc->currentStyle.charStyle().fillColor());
+	replaceStrokeColorComboBox->setPixmapType(ColorCombo::fancyPixmaps);
+	replaceStrokeColorComboBox->setColors(doc->PageColors, true);
+	replaceStrokeColorComboBox->view()->setMinimumWidth(replaceStrokeColorComboBox->view()->maximumViewportSize().width() + 24);
+	setCurrentComboItem(replaceStrokeColorComboBox, doc->currentStyle.charStyle().strokeColor());
+
+	connect(collapseButton, &QPushButton::clicked, this, &SearchReplace::slotCollapseFormat);
+
+	connect(closeButton, &QPushButton::clicked, this, &SearchReplace::accept);
+	connect(searchButton, &QPushButton::clicked, this, &SearchReplace::slotSearch);
+	connect(replaceButton, &QPushButton::clicked, this, &SearchReplace::slotReplace);
+	connect(clearButton, &QPushButton::clicked, this, &SearchReplace::clear);
+	connect(replaceAllButton, &QPushButton::clicked, this, &SearchReplace::slotReplaceAll);
+
+	connect(searchStyleCheckBox, &QCheckBox::clicked, [this](){enableStyleSearch(); updateButtonState();});
+	connect(searchAlignmentCheckBox, &QCheckBox::clicked, [this](){enableAlignmentSearch(); updateButtonState();});
+	connect(searchFontCheckBox, &QCheckBox::clicked, [this](){enableFontSearch(); updateButtonState();});
+	connect(searchFontSizeCheckBox, &QCheckBox::clicked, [this](){enableFontSizeSearch(); updateButtonState();});
+	connect(searchFontEffectsCheckBox, &QCheckBox::clicked, [this](){enableFontEffectsSearch(); updateButtonState();});
+	connect(searchFillColorCheckBox, &QCheckBox::clicked, [this](){enableFillColorSearch(); updateButtonState();});
+	connect(searchFillShadeCheckBox, &QCheckBox::clicked, [this](){enableFillShadeSearch(); updateButtonState();});
+	connect(searchStrokeColorCheckBox, &QCheckBox::clicked, [this](){enableStrokeColorSearch(); updateButtonState();});
+	connect(searchStrokeShadeCheckBox, &QCheckBox::clicked, [this](){enableStrokeShadeSearch(); updateButtonState();});
+
+	connect(searchTextValue, &QLineEdit::textChanged, [this]() {enableTextSearch(); updateButtonState();});
+
+	connect(replaceStyleCheckBox, &QCheckBox::clicked, this, &SearchReplace::enableStyleReplace);
+	connect(replaceAlignmentCheckBox, &QCheckBox::clicked, this, &SearchReplace::enableAlignmentReplace);
+	connect(replaceFontCheckBox, &QCheckBox::clicked, this, &SearchReplace::enableFontReplace);
+	connect(replaceFontSizeCheckBox, &QCheckBox::clicked, this, &SearchReplace::enableFontSizeReplace);
+	connect(replaceFontEffectsCheckBox, &QCheckBox::clicked, this, &SearchReplace::enableFontEffectsReplace);
+	connect(replaceFillColorCheckBox, &QCheckBox::clicked, this, &SearchReplace::enableFillColorReplace);
+	connect(replaceFillShadeCheckBox, &QCheckBox::clicked, this, &SearchReplace::enableFillShadeReplace);
+	connect(replaceStrokeColorCheckBox, &QCheckBox::clicked, this, &SearchReplace::enableStrokeColorReplace);
+	connect(replaceStrokeShadeCheckBox, &QCheckBox::clicked, this, &SearchReplace::enableStrokeShadeReplace);
+
+	connect(replaceTextValue, &QLineEdit::textChanged, this, &SearchReplace::updateButtonState);
 
 	//tooltips
-	DoSearch->setToolTip( tr( "Search for text or formatting in the current text" ) );
-	DoReplace->setToolTip( tr( "Replace the searched for formatting with the replacement values" ) );
-	AllReplace->setToolTip( tr( "Replace all found instances" ) );
+	collapseButton->setToolTip( tr( "Collapse or expand the formatting options" ) );
+	searchButton->setToolTip( tr( "Search for text or formatting in the current text" ) );
+	replaceButton->setToolTip( tr( "Replace the searched for formatting with the replacement values" ) );
+	replaceAllButton->setToolTip( tr( "Replace all found instances" ) );
 	clearButton->setToolTip( tr( "Clear all search and replace options" ) );
-	Leave->setToolTip( tr( "Close search and replace" ) );
-
- // tab order
-	setTabOrder( SText, SStyle );
-	setTabOrder( SStyle, SFont );
-	setTabOrder( SFont, SSize );
-	setTabOrder( SSize, SEffect );
-	setTabOrder( SEffect, SFill );
-	setTabOrder( SFill, SStroke );
-	setTabOrder( SStroke, STextVal );
-	setTabOrder( STextVal, SStyleVal );
-	setTabOrder( SStyleVal, SAlignVal );
-	setTabOrder( SAlignVal, SFontVal );
-	setTabOrder( SFontVal, SSizeVal );
-	setTabOrder( SSizeVal, SEffVal );
-	setTabOrder( SEffVal, SFillVal );
-	setTabOrder( SFillVal, SStrokeVal );
-	setTabOrder( SStrokeVal, RText );
-	setTabOrder( RText, RStyle );
-	setTabOrder( RStyle, RFont );
-	setTabOrder( RFont, RSize );
-	setTabOrder( RSize, REffect );
-	setTabOrder( REffect, RFill );
-	setTabOrder( RFill, RStroke );
-	setTabOrder( RStroke, RTextVal );
-	setTabOrder( RTextVal, RStyleVal );
-	setTabOrder( RStyleVal, RAlignVal );
-	setTabOrder( RAlignVal, RFontVal );
-	setTabOrder( RFontVal, RSizeVal );
-	setTabOrder( RSizeVal, REffVal );
-	setTabOrder( REffVal, RFillVal );
-	setTabOrder( RFillVal, RStrokeVal );
-	setTabOrder( RStrokeVal, Word );
-	setTabOrder( Word, CaseIgnore );
-	setTabOrder( CaseIgnore, DoSearch );
-	setTabOrder( DoSearch, DoReplace );
-	setTabOrder( DoReplace, AllReplace );
-	setTabOrder( AllReplace, Leave );
+	closeButton->setToolTip( tr( "Close search and replace" ) );
 
 	m_prefs = PrefsManager::instance().prefsFile->getContext("SearchReplace");
 	readPrefs();
+
+	collapseFormat();
 }
 
-void SearchReplace::slotSearch()
+void SearchReplace::processCurrentSelection(QString selection)
 {
-	doSearch();
+	selection = selection.trimmed();
+	if (!selection.isEmpty())
+	{
+		// \r newlines from the frames and QChar::ParagraphSeparator for the story editor
+		if (selection.contains("\r") || selection.contains(QChar::ParagraphSeparator))
+			// TODO: add a "Current selection only" checkbox
+			/* enableSelectionSearch()*/ ;
+		else
+			searchTextValue->setText(selection);
+	}
+}
 
-	if (m_itemMode)
-		m_item->update();
-	if (!m_found)
-		showNotFoundMessage();
+QPair<int, int> SearchReplace::cursorPosition()
+{
+	return {m_selectionStart, m_selectionEnd};
 }
 
-void SearchReplace::doSearch()
+void SearchReplace::slotSearch()
 {
-	int maxChar = m_item->itemText.length() - 1;
-	DoReplace->setEnabled(false);
-	AllReplace->setEnabled(false);
-	if (m_itemMode)
+	if (m_storyEditorMode)
+		searchInStoryEditor();
+	else
+		searchOnCanvas();
+}
+
+void SearchReplace::searchInStoryEditor()
+{
+	hideMessage();
+
+	auto options = getSearchOptions();
+
+	SEditor* storyTextEdit = m_doc->scMW()->CurrStED->Editor;
+	StoryText& storyText = storyTextEdit->StyledText;
+	QTextCursor cursor = storyTextEdit->textCursor();
+	// TODO: as soon as we have them, use optional and const auto [start, end] = ...
+	auto pos = searchStory(storyText, cursor.position(), storyText.length(), options);
+
+	if (pos.first < 0)
 	{
-		m_item->itemText.deselectAll();
-		m_item->HasSel = false;
+		showMessage(tr("Reached the end of the selection."));
+		pos = searchStory(storyText, 0, storyText.length(), options);
 	}
 
-	QString fCol = "";
-	QString sCol = "";
-	QString sFont = "";
-	QString sText = "";
-	int sStyle = 0;
-	int sAlign = 0;
-	int sSize = 0;
-	int sEff = 0;
-	int sFillSh = 100;
-	int sStrokeSh = 100;
-	bool searchForReplace = false;
-	bool rep = false;
-	bool found = true;
-
-	m_found = true;
-
-	if ((RFill->isChecked()) || (RStroke->isChecked()) || (RStyle->isChecked()) || (RFont->isChecked())
-		|| (RStrokeS->isChecked()) || (RFillS->isChecked()) || (RSize->isChecked()) || (RText->isChecked())
-		|| (REffect->isChecked())  || (RAlign->isChecked()))
-		rep = true;
-	if ((SFill->isChecked()) || (SStroke->isChecked()) || (SStyle->isChecked()) || (SFont->isChecked())
-			|| (SStrokeS->isChecked()) || (SFillS->isChecked()) || (SSize->isChecked()) || (SText->isChecked())
-			|| (SEffect->isChecked())  || (SAlign->isChecked()))
-		searchForReplace = true;
-	if (SText->isChecked())
-		sText = STextVal->text();
-	if (CaseIgnore->isChecked())
-		sText = sText.toLower();
-	if (SEffect->isChecked())
-		sEff = SEffVal->getStyle();
-	if (SFill->isChecked())
-		fCol = SFillVal->currentText();
-	if (SFillS->isChecked())
-		sFillSh = SFillSVal->getValue();
-	if (SStroke->isChecked())
-		sCol = SStrokeVal->currentText();
-	if (SStrokeS->isChecked())
-		sStrokeSh = SStrokeSVal->getValue();
-	if (SFont->isChecked())
-		sFont = SFontVal->currentText();
-	if (SStyle->isChecked())
-		sStyle = SStyleVal->currentIndex();
-	if (SAlign->isChecked())
-		sAlign = SAlignVal->currentIndex();
-	if (SSize->isChecked())
-		sSize = qRound(SSizeVal->value() * 10);
-	if (sText.length() > 0)
-		found = false;
-	
-	int a, textLen(0);
-	int cursorPos = m_item->itemText.cursorPosition();
-	m_replStart = cursorPos;
+	if (pos.first >= 0)
+	{
+		m_selectionStart = pos.first;
+		m_selectionEnd = pos.second + 1;
+		cursor.setPosition(pos.first);
+		cursor.setPosition(pos.second + 1, QTextCursor::KeepAnchor);
+		storyTextEdit->setTextCursor(cursor);
+	}
+	else
+		showMessage(tr("No match found."));
+}
 
-	if (m_itemMode)
+void SearchReplace::searchOnCanvas()
+{
+	hideMessage();
+
+	auto options = getSearchOptions();
+
+	initCanvasSelection();
+
+	PageItem* pageItem;
+	int itemsToBeProcessed = m_pageItems.size();
+	bool matchFound{false};
+
+	while ((pageItem = currentPageItem()))
 	{
-		Qt::CaseSensitivity cs = Qt::CaseSensitive;
-		if (CaseIgnore->isChecked())
-			cs = Qt::CaseInsensitive;
+		if (itemsToBeProcessed < 0)
+			break;
+		--itemsToBeProcessed;
 
-		for (a = cursorPos; a < m_item->itemText.length(); ++a)
+		StoryText& storyText = pageItem->itemText;
+
+		if (m_doc->appMode != modeEdit)
 		{
-			found = true;
-			if (SText->isChecked())
-			{
-				a = m_item->itemText.indexOf(sText, a, cs, &textLen);
-				found = (a >= 0);
-				if (!found) break;
-
-				if (Word->isChecked() && (a > 0) && m_item->itemText.text(a - 1).isLetterOrNumber())
-					found = false;
-				if (Word->isChecked())
-				{
-					int lastChar = qMin(a + textLen, maxChar);
-					found = ((lastChar == maxChar) || !m_item->itemText.text(lastChar).isLetterOrNumber());
-				}
-				if (!found) continue;
-			}
-			if (SSize->isChecked())
-			{
-				if (m_item->itemText.charStyle(a).fontSize() != sSize)
-					found = false;
-			}
-			if (SFont->isChecked())
-			{
-				if (m_item->itemText.charStyle(a).font().scName() != sFont)
-					found = false;
-			}
-
-			if (SStyle->isChecked())
-			{
-				if (m_item->itemText.paragraphStyle(a).parent() != m_doc->paragraphStyles()[sStyle].name())
-					found = false;
-			}
-
-			if (SAlign->isChecked())
-			{
-				if (m_item->itemText.paragraphStyle(a).alignment() != sAlign)
-					found = false;
-			}
-			if (SStroke->isChecked())
-			{
-				if (m_item->itemText.charStyle(a).strokeColor() != sCol)
-					found = false;
-			}
-			if (SStrokeS->isChecked())
-			{
-				if (m_item->itemText.charStyle(a).strokeShade() != sStrokeSh)
-					found = false;
-			}
-			if (SFillS->isChecked())
-			{
-				if (m_item->itemText.charStyle(a).fillShade() != sFillSh)
-					found = false;
-			}
-			if (SEffect->isChecked())
-			{
-				if ((m_item->itemText.charStyle(a).effects() & ScStyle_UserStyles) != sEff)
-					found = false;
-			}
-			if (SFill->isChecked())
-			{
-				if (m_item->itemText.charStyle(a).fillColor() != fCol)
-					found = false;
-			}
-			if (found && searchForReplace)
-			{
-				m_item->itemText.select(a, textLen);
-				m_item->HasSel = true;
-				if (rep)
-				{
-					DoReplace->setEnabled(true);
-					AllReplace->setEnabled(true);
-				}
-				m_item->itemText.setCursorPosition(a + textLen);
-
-				if (!SText->isChecked())
-					break;
-
-				m_replStart = a;
-				break;
-			}
-			if (SText->isChecked())
-			{
-				for (int xx = m_replStart; xx < a + 1; ++xx)
-					m_item->itemText.select(qMin(xx, maxChar), 1, false);
-				m_item->HasSel = false;
-			}
+			m_doc->view()->requestMode(modeEdit);
+			pageItem->itemText.setCursorPosition(0);
 		}
-		if (found && !m_replacingAll)
+
+		auto pos = searchStory(storyText, storyText.cursorPosition(), storyText.length(), options);
+
+		if (pos.first < 0)
 		{
-			QPointF textCanvasPos;
-			int foundPos = m_item->itemText.cursorPosition();
-			if (m_item->itemText.selectionLength() > 0)
-				foundPos = m_item->itemText.startOfSelection();
-			bool cPosFound = m_doc->textCanvasPosition(m_item, foundPos, textCanvasPos);
-			if (cPosFound)
-			{
-				QRectF updateRect;
-				PageItem* textItem = m_item->frameOfChar(foundPos);
-				if (textItem != m_item)
-				{
-					updateRect = m_item->getVisualBoundingRect();
-					updateRect = updateRect.united(textItem->getVisualBoundingRect());
-					int selLength = m_item->itemText.selectionLength();
-					m_item->itemText.deselectAll();
-					m_item->HasSel = false;
-					m_doc->m_Selection->delaySignalsOn();
-					m_doc->m_Selection->removeItem(m_item);
-					m_doc->m_Selection->addItem(textItem);
-					m_item = textItem;
-					m_item->itemText.deselectAll();
-					if (selLength > 0)
-						m_item->itemText.select(foundPos, selLength);
-					m_item->itemText.setCursorPosition(foundPos + selLength);
-					m_item->HasSel = true;
-					m_doc->m_Selection->delaySignalsOff();
-				}
-				QRectF visibleCanvasRect = m_doc->view()->visibleCanvasRect();
-				if (!visibleCanvasRect.contains(textCanvasPos))
-					m_doc->view()->setCanvasCenterPos(textCanvasPos.x(), textCanvasPos.y());
-				if (!updateRect.isEmpty())
-					m_doc->regionsChanged()->update(updateRect.adjusted(-10.0, -10.0, 10.0, 10.0));
-			}
+			nextPageItem();
+			pageItem = currentPageItem();
+
+			if (m_endReached)
+				showMessage(tr("Reached the end of the selection."));
+
+			pos = searchStory(storyText, storyText.cursorPosition(), storyText.length(), options);
 		}
-		if ((!found) || (a == m_item->itemText.length()))
+
+		if (pos.first >= 0)
 		{
-			m_doc->DoDrawing = true;
-			m_item->update();
-			DoReplace->setEnabled(false);
-			AllReplace->setEnabled(false);
-			m_item->itemText.setCursorPosition(0);
-			m_found = false;
+			storyText.select(pos.first, pos.second + 1 - pos.first);
+			storyText.setCursorPosition(pos.second + 1);
+			matchFound = true;
+
+			break;
 		}
 	}
-	else if (m_doc->scMW()->CurrStED != nullptr)
+	updatePageItemSelection(pageItem);
+
+	if (!matchFound)
+		showMessage(tr("No match found."));
+}
+
+void SearchReplace::initCanvasSelection()
+{
+	if (m_doc->Items->count() == 0)
+		// the document has no items: do nothing
+		return;
+
+	if (m_doc->m_Selection->count() == 0)
 	{
-		found = false;
-		SEditor* storyTextEdit = m_doc->scMW()->CurrStED->Editor;
-		if (storyTextEdit->StyledText.length() == 0)
-			return;
+		// TODO: search the whole document or the whole page?
+		readAllPageItems();
+		selectPageItem(m_pageItems.at(0));
+	}
+	else
+		readSelectedPageItems();
+}
 
-		QTextCursor cursor = storyTextEdit->textCursor();
-		int position  = cursor.position();
-		StoryText& styledText = storyTextEdit->StyledText;
-		int firstChar = -1, lastChar = styledText.length();
-		if (SText->isChecked())
-		{
-			Qt::CaseSensitivity cs = Qt::CaseSensitive;
-			if (CaseIgnore->isChecked())
-				cs = Qt::CaseInsensitive;
-
-			for (int i = position; i < styledText.length(); ++i)
-			{
-				i = styledText.indexOf(sText, i, cs, &textLen);
-				found = (i >= 0);
-				if (!found)
-					break;
-
-				if (Word->isChecked() && (i > 0) && styledText.text(i - 1).isLetterOrNumber())
-					found = false;
-				if (Word->isChecked())
-				{
-					int lastChar = qMin(i + textLen, maxChar);
-					found = ((lastChar == maxChar) || !styledText.text(lastChar).isLetterOrNumber());
-				}
-				if (!found) continue;
-
-				int selStart = i;
-				for (int ap = 0; ap < textLen; ++ap)
-				{
-					const ParagraphStyle& parStyle = storyTextEdit->StyledText.paragraphStyle(selStart + ap);
-					const CharStyle& charStyle = storyTextEdit->StyledText.charStyle(selStart + ap);
-					if (SSize->isChecked() && (charStyle.fontSize() != sSize))
-						found = false;
-					if (SFont->isChecked() && (charStyle.font().scName() != sFont))
-						found = false;
-					if (SStyle->isChecked() && (parStyle.parent() != m_doc->paragraphStyles()[sStyle].name()))
-						found = false;
-					if (SAlign->isChecked() && (parStyle.alignment() != sAlign))
-						found = false;
-					if (SFill->isChecked() && (charStyle.fillColor() != fCol))
-						found = false;
-					if (SStroke->isChecked() && (charStyle.strokeColor() != sCol))
-						found = false;
-					if (SStrokeS->isChecked() && (charStyle.strokeShade() != sStrokeSh))
-						found = false;
-					if (SFillS->isChecked() && (charStyle.fillShade() != sFillSh))
-						found = false;
-					if (SEffect->isChecked() && ((charStyle.effects() & ScStyle_UserStyles) != sEff))
-						found = false;
-				}
-
-				if (found)
-				{
-					firstChar = i;
-					lastChar = i + textLen;
-					break;
-				}
-			}
-		}
-		else
+void SearchReplace::readAllPageItems()
+{
+	if (m_pageItems.size() > 0)
+		return;
+
+	QMap<int, std::vector<PageItem*>> pages;
+	PageItem* pageItem;
+	for (int i = 0; i < m_doc->Items->count(); i++)
+	{
+		pageItem = m_doc->Items->at(i);
+
+		if (!(pageItem->isTextFrame()))
+			continue;
+
+		// text chains are automatically searched until the end
+		if (!(pageItem->firstInChain()))
+			continue;
+
+		pages[pageItem->OwnPage].push_back(pageItem);
+	}
+
+	// TODO: add it as compareCoordinates to util.h, or pageItem.h... or implement the < operator for PageItem.
+	auto sortCoordinates = [](const PageItem* a, const PageItem* b) {
+		return a->yPos() == b->yPos() ?
+				(a->xPos() < b->xPos()) :
+				(a->yPos() < b->yPos());
+	};
+	for (int i = m_doc->currentPageNumber(); i < m_doc->DocPages.count(); i++)
+	{
+		auto pageItems = pages.value(i, {});
+		if (pageItems.empty())
+			continue;
+
+		std::sort(pageItems.begin(), pageItems.end(), sortCoordinates);
+
+		m_pageItems.insert(m_pageItems.end(), pageItems.begin(), pageItems.end() );
+	}
+
+	for (int i = 0; i < m_doc->currentPageNumber(); i++)
+	{
+		auto pageItems = pages.value(i, {});
+		if (pageItems.empty())
+			continue;
+
+		std::sort(pageItems.begin(), pageItems.end(), sortCoordinates);
+
+		m_pageItems.insert(m_pageItems.end(), pageItems.begin(), pageItems.end() );
+	}
+}
+
+// TODO: if only one frame is selected: add an option to search for the whole story (automatically enabled; currently it just follows the story)
+void SearchReplace::readSelectedPageItems()
+{
+	// TODO: sort by position?
+	// only read once
+	if (m_pageItems.size() > 0)
+		return;
+
+	m_currentPageItem = 0;
+
+	if (m_doc->m_Selection->count() == 0)
+		return;
+
+	for (int i = 0 ; i < m_doc->m_Selection->count(); i++)
+	{
+		auto pageItem = m_doc->m_Selection->itemAt(i);
+
+		if (!(pageItem->isTextFrame()))
+			continue;
+
+		m_pageItems.push_back(pageItem);
+	}
+}
+
+// TODO: as soon as available, use std::optional
+PageItem* SearchReplace::currentPageItem()
+{
+	return m_pageItems.at(m_currentPageItem);
+}
+
+void SearchReplace::nextPageItem()
+{
+	m_endReached = false;
+	++m_currentPageItem;
+	if (m_currentPageItem >= m_pageItems.size())
+	{
+		m_currentPageItem = 0;
+		m_endReached = true;
+	}
+
+	auto pageItem = m_pageItems.at(m_currentPageItem);
+
+	if (!m_endReached && pageItem == m_doc->m_Selection->itemAt(0))
+		return;
+
+	selectPageItem(pageItem);
+}
+
+void SearchReplace::selectPageItem(PageItem* pageItem)
+{
+	m_doc->m_Selection->clear();
+	m_doc->m_Selection->addItem(pageItem);
+	m_doc->view()->requestMode(modeNormal);
+}
+
+SearchReplace::MatchRange SearchReplace::getMatchRange(const StoryText& storyText, int position, const int length, const Options& options)
+{
+	if (length == 0)
+		return {-1, 0, 1};
+
+	int matchStart{position};
+	int matchEnd{position};
+
+	if (options.textEnabled)
+	{
+		// match the text
+		int matchLength{0};
+
+		Qt::CaseSensitivity cs = options.ignoreCase ? Qt::CaseInsensitive : Qt::CaseSensitive;
+		matchStart = storyText.indexOf(options.text, matchStart, cs, &matchLength);
+
+		if (matchStart < 0)
+			// there is no match after position
+			return {-1, 0, length + 1};
+
+		// TODO: can it be longer than length?
+		// if yes qMin(i + matchLength, length[ -1])
+		matchEnd = matchStart + matchLength - 1;
+
+		if (options.wholeWords)
 		{
-			for (int i = position; i < styledText.length(); ++i)
-			{
-				found = true;
-				const ParagraphStyle& parStyle = storyTextEdit->StyledText.paragraphStyle(i);
-				const CharStyle& charStyle = styledText.charStyle(i);
-				if (SSize->isChecked() && (charStyle.fontSize() != sSize))
-					found = false;
-				if (SFont->isChecked() && (charStyle.font().scName() != sFont))
-					found = false;
-				if (SStyle->isChecked() && (parStyle.parent() != m_doc->paragraphStyles()[sStyle].name()))
-					found = false;
-				if (SAlign->isChecked() && (parStyle.alignment() != sAlign))
-					found = false;
-				if (SFill->isChecked() && (charStyle.fillColor() != fCol))
-					found = false;
-				if (SFillS->isChecked() && (charStyle.fillShade() != sFillSh))
-					found = false;
-				if (SStroke->isChecked() && (charStyle.strokeColor() != sCol))
-					found = false;
-				if (SStrokeS->isChecked() && (charStyle.strokeShade() != sStrokeSh))
-					found = false;
-				if (SEffect->isChecked() && ((charStyle.effects() & ScStyle_UserStyles) != sEff))
-					found = false;
-				if (found && (firstChar < 0))
-					firstChar = i;
-				else if ((firstChar >= 0) && !found)
-				{
-					lastChar = i;
-					break;
-				}
-				// When searching paragraph styles break at the end of each found paragraph
-				if (SStyle->isChecked() && (firstChar >= 0) && styledText.text(i) == SpecialChars::PARSEP)
-				{
-					lastChar = i;
-					break;
-				}
-			}
+			if (matchStart > 0 && storyText.text(matchStart - 1).isLetterOrNumber())
+				return {-1, 0, matchEnd + 1};
+			if (matchEnd + 1 < length && storyText.text(matchEnd + 1).isLetterOrNumber())
+				return {-1, 0, matchEnd + 1};
 		}
-		found = (firstChar >= 0);
-		if (found)
+	}
+
+	// check the first char or the matched text
+	// TODO: is it possible/worth to include all contiguous matching when not textEnabled? (move in here the loop below)
+	bool formatMatching{true};
+	for (int i = matchStart; i <= matchEnd; ++i)
+	{
+		const auto& parStyle = storyText.paragraphStyle(i);
+		const auto& charStyle = storyText.charStyle(i);
+		formatMatching = isFormatMatching(parStyle, charStyle, options);
+		if (!formatMatching)
+			break;
+	}
+	if (!formatMatching)
+	{
+		// skip all contiguous non matching chars
+		for (int i = matchEnd; i <= length; ++i)
 		{
-			cursor.setPosition(firstChar);
-			cursor.setPosition(lastChar, QTextCursor::KeepAnchor);
-			storyTextEdit->setTextCursor(cursor);
+			const auto& parStyle = storyText.paragraphStyle(i);
+			const auto& charStyle = storyText.charStyle(i);
+			formatMatching = isFormatMatching(parStyle, charStyle, options);
+			if (formatMatching)
+				break;
+			matchEnd = i;
 		}
-		if (found && searchForReplace)
+		return {-1, 0, matchEnd + 1};
+	}
+
+	if (!options.textEnabled)
+	{
+		// include all contiguous chars with matching format
+		for (int i = matchEnd + 1; i < length; ++i)
 		{
-			// m_doc->scMW()->CurrStED->updateProps(); FIXME
-			if (rep)
-			{
-				DoReplace->setEnabled(true);
-				AllReplace->setEnabled(true);
-			}
-			m_matchesFound++;
-			m_firstMatchPosition = storyTextEdit->textCursor().selectionStart();
+			const auto& parStyle = storyText.paragraphStyle(i);
+			const auto& charStyle = storyText.charStyle(i);
+			if (!(formatMatching = isFormatMatching(parStyle, charStyle, options)))
+				break;
+			matchEnd = i;
 		}
+	}
+
+	return {matchStart, matchEnd, matchEnd + 1};
+}
+
+// TODO: return std::optional<QPair> as soon as we have it.
+QPair<int, int> SearchReplace::searchStory(const StoryText& storyText, const int start, const int length, const Options& options)
+{
+	if (length == 0)
+		return {-1, -1};
+
+	int matchStart{start};
+	int matchEnd{start};
+	bool found{false};
+
+	while (!found && matchStart < length)
+	{
+		const auto match = getMatchRange(storyText, matchStart, length, options);
+		if (match.start == -1)
+			matchStart = match.nextPosition;
 		else
 		{
-			m_found = false;
-			QTextCursor cursor = storyTextEdit->textCursor();
-			cursor.clearSelection();
-			cursor.movePosition(QTextCursor::Start, QTextCursor::MoveAnchor);
-			storyTextEdit->setTextCursor(cursor);
+			matchStart = match.start;
+			matchEnd = match.end;
+			found = true;
 		}
 	}
+
+	if (!found)
+		return {-1, -1};
+
+	return {matchStart, matchEnd};
+}
+
+void SearchReplace::updatePageItemSelection(PageItem* pageItem)
+{
+	StoryText& storyText = pageItem->itemText;
+
+	int foundPos = storyText.cursorPosition();
+	if (storyText.selectionLength() > 0)
+		foundPos = storyText.startOfSelection();
+
+	QPointF textCanvasPos;
+	if (!m_doc->textCanvasPosition(pageItem, foundPos, textCanvasPos))
+		return;
+
+	QRectF updateRect;
+	PageItem* textItem = pageItem->frameOfChar(foundPos);
+	if (textItem != pageItem)
+	{
+		updateRect = pageItem->getVisualBoundingRect();
+		updateRect = updateRect.united(textItem->getVisualBoundingRect());
+		int selLength = pageItem->itemText.selectionLength();
+		pageItem->itemText.deselectAll();
+		pageItem->HasSel = false;
+		m_doc->m_Selection->delaySignalsOn();
+		m_doc->m_Selection->removeItem(pageItem);
+		m_doc->m_Selection->addItem(textItem);
+		pageItem = textItem;
+		pageItem->itemText.deselectAll();
+		if (selLength > 0)
+			pageItem->itemText.select(foundPos, selLength);
+		pageItem->itemText.setCursorPosition(foundPos + selLength);
+		pageItem->HasSel = true;
+		m_doc->m_Selection->delaySignalsOff();
+	}
+	QRectF visibleCanvasRect = m_doc->view()->visibleCanvasRect();
+	if (!visibleCanvasRect.contains(textCanvasPos))
+		m_doc->view()->setCanvasCenterPos(textCanvasPos.x(), textCanvasPos.y());
+	if (!updateRect.isEmpty())
+		m_doc->regionsChanged()->update(updateRect.adjusted(-10.0, -10.0, 10.0, 10.0));
+	pageItem->update();
+}
+
+bool SearchReplace::isFormatMatching(const ParagraphStyle& parStyle, const CharStyle& charStyle, const Options& options)
+{
+	if (options.paragraphStyleEnabled &&
+			parStyle.parent() != m_doc->paragraphStyles()[options.paragraphStyle].name())
+		return false;
+	if (options.alignmentEnabled &&
+			parStyle.alignment() != options.alignment)
+		return false;
+	if (options.fontEnabled &&
+			charStyle.font().scName() != options.font)
+		return false;
+	if (options.fontSizeEnabled &&
+			charStyle.fontSize() != options.fontSize)
+		return false;
+	if (options.fontEffectsEnabled &&
+			(charStyle.effects() & ScStyle_UserStyles) != options.fontEffects)
+		return false;
+	if (options.fillColorEnabled &&
+			charStyle.fillColor() != options.fillColor)
+		return false;
+	if (options.fillShadeEnabled &&
+			charStyle.fillShade() != options.fillShade)
+		return false;
+	if (options.strokeColorEnabled &&
+			charStyle.strokeColor() != options.strokeColor)
+		return false;
+	if (options.strokeShadeEnabled &&
+			charStyle.strokeShade() != options.strokeShade)
+		return false;
+	return true;
 }
 
 void SearchReplace::slotReplace()
 {
-	doReplace();
+	if (m_storyEditorMode)
+		replaceInStoryEditor();
+	else
+		replaceOnCanvas();
+}
 
-	if (m_itemMode)
-		m_item->update();
-	if (!m_found)
-		showNotFoundMessage();
+void SearchReplace::replaceInStoryEditor()
+{
+	auto options = getSearchOptions();
+
+	SEditor* storyTextEdit = m_doc->scMW()->CurrStED->Editor;
+	StoryText& storyText =  storyTextEdit->StyledText;
+
+	int selectionStart = storyTextEdit->textCursor().selectionStart();
+	int selectionEnd = storyTextEdit->textCursor().selectionEnd();
+
+	MatchRange match = getMatchRange(storyText, selectionStart, storyText.length(), options);
+
+	if (selectionStart == match.start && selectionEnd - 1 == match.end)
+		replaceSelectionInStoryEditor();
+
+
+	searchInStoryEditor();
 }
 
-void SearchReplace::doReplace()
+void SearchReplace::replaceOnCanvas()
 {
-	if (m_itemMode)
+
+	auto options = getSearchOptions();
+
+	initCanvasSelection();
+
+	auto pageItem = currentPageItem();
+	StoryText& storyText = pageItem->itemText;
+
+	int selectionStart = storyText.startOfSelection();
+	int selectionEnd = storyText.endOfSelection();
+
+	if (selectionEnd < 0)
 	{
-		if (RText->isChecked())
-		{
-			QString repl = RTextVal->text();
-			m_item->itemText.replaceSelection(repl);
-		}
-		if (RStyle->isChecked())
-		{
-			int oldMode = m_doc->appMode;
-			m_doc->appMode = modeEdit;
-			m_doc->itemSelection_SetNamedParagraphStyle(m_doc->paragraphStyles()[RStyleVal->currentIndex()].name());
-			m_doc->appMode = oldMode;
-		}
-		if (RAlign->isChecked())
-		{
-			int oldMode = m_doc->appMode;
-			m_doc->appMode = modeEdit;
-			m_doc->itemSelection_SetAlignment(RAlignVal->currentIndex());
-			m_doc->appMode = oldMode;
-		}
-		if (RFill->isChecked())
-			m_doc->itemSelection_SetFillColor(RFillVal->currentText());
-		if (RFillS->isChecked())
-			m_doc->itemSelection_SetFillShade(RFillSVal->getValue());
-		if (RStroke->isChecked())
-			m_doc->itemSelection_SetStrokeColor(RStrokeVal->currentText());
-		if (RStrokeS->isChecked())
-			m_doc->itemSelection_SetStrokeShade(RStrokeSVal->getValue());
-		if (RFont->isChecked())
-			m_doc->itemSelection_SetFont(RFontVal->currentText());
-		if (RSize->isChecked())
-			m_doc->itemSelection_SetFontSize(qRound(RSizeVal->value() * 10.0));
-		if (REffect->isChecked() && (m_item->itemText.isSelected()))
-		{
-			int s = REffVal->getStyle() & ScStyle_UserStyles;
-			m_doc->currentStyle.charStyle().setFeatures(static_cast<StyleFlag>(s).featureList()); // ???
-			for (int i = 0; i < m_item->itemText.length(); ++i)
-			{
-				if (m_item->itemText.selected(i))
-				{
-					CharStyle newFeatures;
-					newFeatures.setFeatures(static_cast<StyleFlag>(s).featureList());
-					m_item->itemText.applyCharStyle(i, 1, newFeatures);
-				}
-			}
-		}
-		m_item->itemText.deselectAll();
+		searchOnCanvas();
+		return;
 	}
-	else if (m_doc->scMW()->CurrStED != nullptr)
+
+	MatchRange match = getMatchRange(storyText, selectionStart, storyText.length(), options);
+
+	if (selectionStart == match.start && selectionEnd - 1 == match.end)
+		replaceSelectionOnCanvas();
+
+	searchOnCanvas();
+}
+
+
+void SearchReplace::replaceSelectionInStoryEditor()
+{
+	StoryEditor* se = m_doc->scMW()->CurrStED;
+	auto options = getReplaceOptions();
+
+	// TODO: undo does not work
+	if (options.textEnabled)
 	{
-		StoryEditor* se = m_doc->scMW()->CurrStED;
-		if (RText->isChecked())
+		disconnect(se->Editor, SIGNAL(cursorPositionChanged()), se, SLOT(updateProps()));
+		int SelStart = se->Editor->textCursor().selectionStart();
+		int SelEnd = se->Editor->textCursor().selectionEnd();
+		se->Editor->textCursor().setPosition(SelStart);
+		se->Editor->textCursor().setPosition(SelEnd, QTextCursor::KeepAnchor);
+		se->Editor->textCursor().removeSelectedText();
+		QString newText = options.text;
+		se->Editor->insertPlainText(newText);
+		if (newText.length() > 0)
 		{
-			disconnect(se->Editor, SIGNAL(cursorPositionChanged()), se, SLOT(updateProps()));
-			int SelStart = se->Editor->textCursor().selectionStart();
-			int SelEnd = se->Editor->textCursor().selectionEnd();
-//			se->Editor->insChars(RTextVal->text());
-			se->Editor->textCursor().setPosition(SelStart);
-			se->Editor->textCursor().setPosition(SelEnd, QTextCursor::KeepAnchor);
-			se->Editor->textCursor().removeSelectedText();
-//FIXME		se->Editor->setEffects(se->Editor->CurrentEffects);
-			QString newText = RTextVal->text();
-			se->Editor->insertPlainText(newText);
-			if (newText.length() > 0)
-			{
-				QTextCursor textCursor = se->Editor->textCursor();
-				textCursor.setPosition(SelStart);
-				textCursor.setPosition(SelStart + newText.length(), QTextCursor::KeepAnchor);
-				se->Editor->setTextCursor(textCursor);
-			}
-			connect(se->Editor, SIGNAL(cursorPositionChanged()), se, SLOT(updateProps()));
-//			se->newAlign(se->Editor->currentParaStyle);
+			QTextCursor textCursor = se->Editor->textCursor();
+			textCursor.setPosition(SelStart);
+			textCursor.setPosition(SelStart + newText.length(), QTextCursor::KeepAnchor);
+			se->Editor->setTextCursor(textCursor);
 		}
-		if (RStyle->isChecked())
-			se->newStyle(m_doc->paragraphStyles()[RStyleVal->currentIndex()].name());
-		if (RAlign->isChecked())
-			se->newAlign(RAlignVal->currentIndex());
-		if (RFill->isChecked())
-			se->newTxFill(RFillVal->currentIndex(), -1);
-		if (RFillS->isChecked())
-			se->newTxFill(-1, RFillSVal->getValue());
-		if (RStroke->isChecked())
-			se->newTxStroke(RStrokeVal->currentIndex(), -1);
-		if (RStrokeS->isChecked())
-			se->newTxStroke(-1, RStrokeSVal->getValue());
-		if (RFont->isChecked())
-			se->newTxFont(RFontVal->currentText());
-		if (RSize->isChecked())
-			se->newTxSize(RSizeVal->value());
-		if (REffect->isChecked())
-			se->newTxStyle(REffVal->getStyle());
-
-		QTextCursor textCursor = se->Editor->textCursor();
-		int selStart = textCursor.selectionStart();
-		int selEnd   = textCursor.selectionEnd();
-		int selPos   = qMax(selStart, selEnd);
-		textCursor.setPosition(selPos);
-		se->Editor->setTextCursor(textCursor);
+		connect(se->Editor, SIGNAL(cursorPositionChanged()), se, SLOT(updateProps()));
 	}
-	DoReplace->setEnabled(false);
-	AllReplace->setEnabled(false);
-	doSearch();
+	if (options.paragraphStyleEnabled)
+		se->newStyle(m_doc->paragraphStyles()[options.paragraphStyle].name());
+	if (options.alignmentEnabled)
+		se->newAlign(options.alignment);
+	if (options.fillColorEnabled)
+		se->newTxFill(options.fillColorId, -1);
+	if (options.fillShadeEnabled)
+		se->newTxFill(-1, options.fillShade);
+	if (options.strokeColorEnabled)
+		se->newTxStroke(options.strokeColorId, -1);
+	if (options.strokeShadeEnabled)
+		se->newTxStroke(-1, options.strokeShade);
+	if (options.fontEnabled)
+		se->newTxFont(options.font);
+	if (options.fontSizeEnabled)
+		se->newTxSize(options.fontSize / 10);
+	if (options.fontEffectsEnabled)
+		se->newTxStyle(options.fontEffects);
+
+	// TODO: not sure that this is needed, since we then jump away...
+	QTextCursor textCursor = se->Editor->textCursor();
+	int selStart = textCursor.selectionStart();
+	int selEnd   = textCursor.selectionEnd();
+	int selPos   = qMax(selStart, selEnd);
+	textCursor.setPosition(selPos);
+	se->Editor->setTextCursor(textCursor);
 }
 
-int SearchReplace::firstMatchCursorPosition()
+void SearchReplace::replaceSelectionOnCanvas()
 {
-	return m_firstMatchPosition;
+	auto options = getReplaceOptions();
+
+	auto pageItem = currentPageItem();
+
+	if (options.textEnabled)
+	{
+		// TODO: undo does not work
+		pageItem->itemText.replaceSelection(options.text);
+	}
+
+	if (options.paragraphStyleEnabled)
+	{
+		int oldMode = m_doc->appMode;
+		m_doc->appMode = modeEdit;
+		m_doc->itemSelection_SetNamedParagraphStyle(m_doc->paragraphStyles()[options.paragraphStyle].name());
+		m_doc->appMode = oldMode;
+	}
+
+	if (options.alignmentEnabled)
+	{
+		int oldMode = m_doc->appMode;
+		m_doc->appMode = modeEdit;
+		m_doc->itemSelection_SetAlignment(options.alignment);
+		m_doc->appMode = oldMode;
+	}
+
+	if (options.fontEnabled)
+		m_doc->itemSelection_SetFont(options.font);
+
+	if (options.fontSizeEnabled)
+		m_doc->itemSelection_SetFontSize(options.fontSize);
+
+	// TODO: undo does not work
+	if (options.fontEffectsEnabled && (pageItem->itemText.isSelected()))
+	{
+		int start = pageItem->itemText.startOfSelection();
+		int end = pageItem->itemText.endOfSelection();
+		int s = options.fontEffects & ScStyle_UserStyles;
+		m_doc->currentStyle.charStyle().setFeatures(static_cast<StyleFlag>(s).featureList()); // ???
+		for (int i = start; i < end; ++i)
+		{
+			CharStyle newFeatures;
+			newFeatures.setFeatures(static_cast<StyleFlag>(s).featureList());
+			pageItem->itemText.applyCharStyle(i, 1, newFeatures);
+		}
+	}
+
+	if (options.fillColorEnabled)
+		m_doc->itemSelection_SetFillColor(options.fillColor);
+
+	if (options.fillShadeEnabled)
+		m_doc->itemSelection_SetFillShade(options.fillShade);
+
+	if (options.strokeColorEnabled)
+		m_doc->itemSelection_SetStrokeColor(options.strokeColor);
+
+	if (options.strokeShadeEnabled)
+		m_doc->itemSelection_SetStrokeShade(options.strokeShade);
+
+	// TODO: we need less than that... can we have a subset that updates the screen without making sure that the selection is visible?
+	updatePageItemSelection(pageItem);
 }
 
-void SearchReplace::setSearchedText(const QString& text)
+void SearchReplace::slotReplaceAll()
 {
-	if (SText->isChecked())
-		STextVal->setText(text);
+	// TODO: what does this do?
+	// QScopedValueRollback<bool> replaceAllRollback(m_replacingAll, true);
+	if (m_storyEditorMode)
+		replaceAllInStoryEditor();
+	else
+		replaceAllOnCanvas();
 }
 
-void SearchReplace::slotReplaceAll()
+void SearchReplace::replaceAllInStoryEditor()
 {
-	QScopedValueRollback<bool> replaceAllRollback(m_replacingAll, true);
+	auto options = getSearchOptions();
 
-	if (m_itemMode)
-		m_doc->DoDrawing = false;
+	SEditor* storyTextEdit = m_doc->scMW()->CurrStED->Editor;
+	StoryText& storyText =  storyTextEdit->StyledText;
+
+	bool found{false};
+
+	MatchRange match = getMatchRange(storyText, 0, storyText.length(), options);
 
-	do
+	while (match.start >= 0)
 	{
-		doReplace();
+		found = true;
+
+		QTextCursor cursor = storyTextEdit->textCursor();
+		cursor.setPosition(match.start);
+		cursor.setPosition(match.end + 1, QTextCursor::KeepAnchor);
+		storyTextEdit->setTextCursor(cursor);
+
+		replaceSelectionInStoryEditor();
+
+		match = getMatchRange(storyText, match.start + 1, storyText.length(), options);
 	}
-	while (m_found);
 
-	if (m_itemMode)
+	if (!found)
+		showMessage(tr("No match found."));
+}
+
+void SearchReplace::replaceAllOnCanvas()
+{
+	m_doc->DoDrawing = false;
+
+	auto options = getSearchOptions();
+
+	initCanvasSelection();
+
+	bool found{false};
+	for (auto pageItem: m_pageItems)
 	{
-		m_doc->DoDrawing = true;
-		if (m_item->isTextFrame())
-			m_item->asTextFrame()->invalidateLayout(true);
-		m_doc->regionsChanged()->update(QRectF());
+		StoryText& storyText = pageItem->itemText;
+
+		MatchRange match = getMatchRange(storyText, 0, storyText.length(), options);
+
+		while (match.start >= 0)
+		{
+			found = true;
+
+			storyText.select(match.start, match.end + 1 - match.start);
+			storyText.setCursorPosition(match.end + 1);
+
+			replaceSelectionOnCanvas();
+
+			match = getMatchRange(storyText, match.start + 1, storyText.length(), options);
+		}
 	}
 
-	showNotFoundMessage();
+	if (!found)
+		showMessage(tr("No match found."));
+
+	m_doc->DoDrawing = true;
+	for (auto pageItem: m_pageItems)
+		pageItem->asTextFrame()->invalidateLayout(true);
+	m_doc->regionsChanged()->update(QRectF());
+}
+
+
+void SearchReplace::slotCollapseFormat()
+{
+	m_stateCollapsed = !m_stateCollapsed;
+	collapseFormat();
+}
+
+void SearchReplace::collapseFormat()
+{
+	searchGroupBox->setVisible(!m_stateCollapsed);
+	replaceGroupBox->setVisible(!m_stateCollapsed);
+	// move the following line to setCollapseLabel() if we have to catch languageChange()
+	collapseButton->setText(m_stateCollapsed ? tr("More...") : tr("Less..."));
+	// It's important to call QApplication::processEvents() before calling adjustSize()
+	// https://stackoverflow.com/questions/1675499/how-do-i-auto-adjust-the-size-of-a-qdialog-depending-on-the-text-length-of-one-o#comment82074153_1679399
+	QTimer::singleShot(0, [this](){adjustSize();});
 }
 
 void SearchReplace::showNotFoundMessage()
@@ -881,338 +854,444 @@ void SearchReplace::showNotFoundMessage()
 	}
 }
 
-void SearchReplace::enableTxSearch()
+void SearchReplace::enableTextSearch()
 {
-	bool setter = SText->isChecked();
-	STextVal->setEnabled(setter);
-	Word->setEnabled(setter);
-	CaseIgnore->setEnabled(setter);
-	if (setter)
-		STextVal->setFocus();
-	updateSearchButtonState();
+	bool enabled = !searchTextValue->text().isEmpty();
+	wholeWordsCheckBox->setEnabled(enabled);
+	ignoreCaseCheckBox->setEnabled(enabled);
 }
 
 void SearchReplace::enableStyleSearch()
 {
-	SStyleVal->setEnabled(SStyleVal->count() ? SStyle->isChecked() : false);
-	updateSearchButtonState();
+	searchStyleComboBox->setEnabled(searchStyleCheckBox->isChecked());
 }
 
-void SearchReplace::enableAlignSearch()
+void SearchReplace::enableAlignmentSearch()
 {
-	SAlignVal->setEnabled(SAlign->isChecked());
-	updateSearchButtonState();
+	searchAlignmentComboBox->setEnabled(searchAlignmentCheckBox->isChecked());
 }
 
 void SearchReplace::enableFontSearch()
 {
-	SFontVal->setEnabled(SFont->isChecked());
-	updateSearchButtonState();
-}
-
-void SearchReplace::enableSizeSearch()
-{
-	SSizeVal->setEnabled(SSize->isChecked());
-	updateSearchButtonState();
+	searchFontComboBox->setEnabled(searchFontCheckBox->isChecked());
 }
 
-void SearchReplace::enableEffSearch()
+void SearchReplace::enableFontSizeSearch()
 {
-	SEffVal->setEnabled(SEffect->isChecked());
-	updateSearchButtonState();
+	searchFontSizeSpinBox->setEnabled(searchFontSizeCheckBox->isChecked());
 }
 
-void SearchReplace::enableFillSearch()
+void SearchReplace::enableFontEffectsSearch()
 {
-	SFillVal->setEnabled(SFill->isChecked());
-	updateSearchButtonState();
+	searchFontEffects->setEnabled(searchFontEffectsCheckBox->isChecked());
 }
 
-void SearchReplace::enableFillSSearch()
+void SearchReplace::enableFillColorSearch()
 {
-	SFillSVal->setEnabled(SFillS->isChecked());
-	updateSearchButtonState();
+	searchFillColorComboBox->setEnabled(searchFillColorCheckBox->isChecked());
 }
 
-void SearchReplace::enableStrokeSearch()
+void SearchReplace::enableFillShadeSearch()
 {
-	SStrokeVal->setEnabled(SStroke->isChecked());
-	updateSearchButtonState();
+	searchFillShadeToolButton->setEnabled(searchFillShadeCheckBox->isChecked());
 }
 
-void SearchReplace::enableStrokeSSearch()
+void SearchReplace::enableStrokeColorSearch()
 {
-	SStrokeSVal->setEnabled(SStrokeS->isChecked());
-	updateSearchButtonState();
+	searchStrokeColorComboBox->setEnabled(searchStrokeColorCheckBox->isChecked());
 }
 
-void SearchReplace::enableTxReplace()
+void SearchReplace::enableStrokeShadeSearch()
 {
-	RTextVal->setEnabled(RText->isChecked());
-	if (RText->isChecked())
-		RTextVal->setFocus();
-	updateReplaceButtonsState();
+	searchStrokeShadeToolButton->setEnabled(searchStrokeShadeCheckBox->isChecked());
 }
 
 void SearchReplace::enableStyleReplace()
 {
-	RStyleVal->setEnabled(RStyle->isChecked());
-	updateReplaceButtonsState();
+	replaceStyleComboBox->setEnabled(replaceStyleCheckBox->isChecked());
 }
 
-void SearchReplace::enableAlignReplace()
+void SearchReplace::enableAlignmentReplace()
 {
-	RAlignVal->setEnabled(RAlign->isChecked());
-	updateReplaceButtonsState();
+	replaceAlignmentComboBox->setEnabled(replaceAlignmentCheckBox->isChecked());
 }
 
 void SearchReplace::enableFontReplace()
 {
-	RFontVal->setEnabled(RFont->isChecked());
-	updateReplaceButtonsState();
+	replaceFontComboBox->setEnabled(replaceFontCheckBox->isChecked());
 }
 
-void SearchReplace::enableSizeReplace()
+void SearchReplace::enableFontSizeReplace()
 {
-	RSizeVal->setEnabled(RSize->isChecked());
-	updateReplaceButtonsState();
+	replaceFontSizeSpinBox->setEnabled(replaceFontSizeCheckBox->isChecked());
 }
 
-void SearchReplace::enableEffReplace()
+void SearchReplace::enableFontEffectsReplace()
 {
-	REffVal->setEnabled(REffect->isChecked());
-	updateReplaceButtonsState();
+	replaceFontEffects->setEnabled(replaceFontEffectsCheckBox->isChecked());
 }
 
-void SearchReplace::enableFillReplace()
+void SearchReplace::enableFillColorReplace()
 {
-	RFillVal->setEnabled(RFill->isChecked());
-	updateReplaceButtonsState();
+	replaceFillColorComboBox->setEnabled(replaceFillColorCheckBox->isChecked());
 }
 
-void SearchReplace::enableFillSReplace()
+void SearchReplace::enableFillShadeReplace()
 {
-	RFillSVal->setEnabled(RFillS->isChecked());
-	updateReplaceButtonsState();
+	replaceFillShadeToolButton->setEnabled(replaceFillShadeCheckBox->isChecked());
 }
 
-void SearchReplace::enableStrokeReplace()
+void SearchReplace::enableStrokeColorReplace()
 {
-	RStrokeVal->setEnabled(RStroke->isChecked());
-	updateReplaceButtonsState();
+	replaceStrokeColorComboBox->setEnabled(replaceStrokeColorCheckBox->isChecked());
 }
 
-void SearchReplace::enableStrokeSReplace()
+void SearchReplace::enableStrokeShadeReplace()
 {
-	RStrokeSVal->setEnabled(RStrokeS->isChecked());
-	updateReplaceButtonsState();
+	replaceStrokeShadeToolButton->setEnabled(replaceStrokeShadeCheckBox->isChecked());
 }
 
 void SearchReplace::clear()
 {
-	SAlign->setChecked(false);
-	SStroke->setChecked(false);
-	SFill->setChecked(false);
-	SStrokeS->setChecked(false);
-	SFillS->setChecked(false);
-	SSize->setChecked(false);
-	SFont->setChecked(false);
-	SStyle->setChecked(false);
-	SText->setChecked(false);
-	SEffect->setChecked(false);
-	REffect->setChecked(false);
-	STextVal->setText("");
-	int currentParaStyle = findParagraphStyle(m_doc, m_doc->currentStyle);
-	SStyleVal->setCurrentIndex(currentParaStyle);
-	RAlignVal->setCurrentIndex(m_doc->currentStyle.alignment());
-	setCurrentComboItem(SFontVal, m_doc->currentStyle.charStyle().font().scName());
-	setCurrentComboItem(SFillVal, m_doc->currentStyle.charStyle().fillColor());
-	setCurrentComboItem(SStrokeVal, m_doc->currentStyle.charStyle().strokeColor());
-	SSizeVal->setValue(m_doc->currentStyle.charStyle().fontSize() / 10.0);
-	RStroke->setChecked(false);
-	RStrokeS->setChecked(false);
-	RFill->setChecked(false);
-	RFillS->setChecked(false);
-	RSize->setChecked(false);
-	RFont->setChecked(false);
-	RStyle->setChecked(false);
-	RText->setChecked(false);
-	RTextVal->setText("");
-	RStyleVal->setCurrentIndex(currentParaStyle);
-	RAlignVal->setCurrentIndex(m_doc->currentStyle.alignment());
-	setCurrentComboItem(RFontVal, m_doc->currentStyle.charStyle().font().scName());
-	setCurrentComboItem(RFillVal, m_doc->currentStyle.charStyle().fillColor());
-	setCurrentComboItem(RStrokeVal, m_doc->currentStyle.charStyle().strokeColor());
-	RSizeVal->setValue(m_doc->currentStyle.charStyle().fontSize() / 10.0);
-	Word->setChecked(false);
-	CaseIgnore->setChecked(false);
-	enableTxSearch();
-	enableStyleSearch();
-	enableFontSearch();
-	enableSizeSearch();
-	enableEffSearch();
-	enableFillSearch();
-	enableFillSSearch();
-	enableStrokeSearch();
-	enableStrokeSSearch();
-	enableTxReplace();
-	enableStyleReplace();
-	enableFontReplace();
-	enableSizeReplace();
-	enableEffReplace();
-	enableFillReplace();
-	enableFillSReplace();
-	enableStrokeReplace();
-	enableStrokeSReplace();
+	searchStyleCheckBox->setChecked(false);
+	searchAlignmentCheckBox->setChecked(false);
+	searchFontCheckBox->setChecked(false);
+	searchFontSizeCheckBox->setChecked(false);
+	searchFontEffectsCheckBox->setChecked(false);
+	searchFillColorCheckBox->setChecked(false);
+	searchFillShadeCheckBox->setChecked(false);
+	searchStrokeColorCheckBox->setChecked(false);
+	searchStrokeShadeCheckBox->setChecked(false);
+
+	replaceStyleCheckBox->setChecked(false);
+	replaceAlignmentCheckBox->setChecked(false);
+	replaceFontCheckBox->setChecked(false);
+	replaceFontSizeCheckBox->setChecked(false);
+	replaceFontEffectsCheckBox->setChecked(false);
+	replaceFillColorCheckBox->setChecked(false);
+	replaceFillShadeCheckBox->setChecked(false);
+	replaceStrokeColorCheckBox->setChecked(false);
+	replaceStrokeShadeCheckBox->setChecked(false);
+
+	auto currentParaStyle = findParagraphStyle(m_doc, m_doc->currentStyle);
+
+	searchTextValue->setText("");
+	searchStyleComboBox->setCurrentIndex(currentParaStyle);
+	searchAlignmentComboBox->setCurrentIndex(m_doc->currentStyle.alignment());
+	setCurrentComboItem(searchFontComboBox, m_doc->currentStyle.charStyle().font().scName());
+	searchFontSizeSpinBox->setValue(m_doc->currentStyle.charStyle().fontSize() / 10.0);
+	setCurrentComboItem(searchFillColorComboBox, m_doc->currentStyle.charStyle().fillColor());
+	setCurrentComboItem(searchStrokeColorComboBox, m_doc->currentStyle.charStyle().strokeColor());
+
+	replaceTextValue->setText("");
+	replaceStyleComboBox->setCurrentIndex(currentParaStyle);
+	replaceAlignmentComboBox->setCurrentIndex(m_doc->currentStyle.alignment());
+	setCurrentComboItem(replaceFontComboBox, m_doc->currentStyle.charStyle().font().scName());
+	replaceFontSizeSpinBox->setValue(m_doc->currentStyle.charStyle().fontSize() / 10.0);
+	setCurrentComboItem(replaceFillColorComboBox, m_doc->currentStyle.charStyle().fillColor());
+	setCurrentComboItem(replaceStrokeColorComboBox, m_doc->currentStyle.charStyle().strokeColor());
+
+
+	wholeWordsCheckBox->setChecked(false);
+	ignoreCaseCheckBox->setChecked(false);
+	
+	searchStyleComboBox->setEnabled(false);
+	searchAlignmentComboBox->setEnabled(false);
+	searchFontComboBox->setEnabled(false);
+	searchFontSizeSpinBox->setEnabled(false);
+	searchFontEffects->setEnabled(false);
+	searchFillColorComboBox->setEnabled(false);
+	searchFillShadeToolButton->setEnabled(false);
+	searchStrokeColorComboBox->setEnabled(false);
+	searchStrokeShadeToolButton->setEnabled(false);
+
+	replaceStyleComboBox->setEnabled(false);
+	replaceAlignmentComboBox->setEnabled(false);
+	replaceFontComboBox->setEnabled(false);
+	replaceFontSizeSpinBox->setEnabled(false);
+	replaceFontEffects->setEnabled(false);
+	replaceFillColorComboBox->setEnabled(false);
+	replaceFillShadeToolButton->setEnabled(false);
+	replaceStrokeColorComboBox->setEnabled(false);
+	replaceStrokeShadeToolButton->setEnabled(false);
+
+	searchButton->setEnabled(false);
+	replaceButton->setEnabled(false);
+	replaceAllButton->setEnabled(false);
+}
+
+bool SearchReplace::anySearchChecked()
+{
+	return
+		!searchTextValue->text().isEmpty() ||
+		searchStyleCheckBox->isChecked() ||
+		searchAlignmentCheckBox->isChecked() ||
+		searchFontCheckBox->isChecked() ||
+		searchFontSizeCheckBox->isChecked() ||
+		searchFontEffectsCheckBox->isChecked() ||
+		searchFillColorCheckBox->isChecked() ||
+		searchFillShadeCheckBox->isChecked() ||
+		searchStrokeColorCheckBox->isChecked() ||
+		searchStrokeShadeCheckBox->isChecked();
+}
+
+bool SearchReplace::anyReplaceChecked()
+{
+	return
+		replaceStyleCheckBox->isChecked() ||
+		replaceAlignmentCheckBox->isChecked() ||
+		replaceFontCheckBox->isChecked() ||
+		replaceFontSizeCheckBox->isChecked() ||
+		replaceFontEffectsCheckBox->isChecked() ||
+		replaceFillColorCheckBox->isChecked() ||
+		replaceFillShadeCheckBox->isChecked() ||
+		replaceStrokeColorCheckBox->isChecked() ||
+		replaceStrokeShadeCheckBox->isChecked();
 }
 
-void SearchReplace::updateReplaceButtonsState()
+SearchReplace::Options SearchReplace::getSearchOptions()
 {
-	bool replaceEnabled = false;
-	if (RFill->isChecked() || RStroke->isChecked() || RStyle->isChecked() || RFont->isChecked()  ||
-		RStrokeS->isChecked() || RFillS->isChecked() || RSize->isChecked() || REffect->isChecked() ||
-		RAlign->isChecked())
+	Options options{};
+
+	options.textEnabled = !searchTextValue->text().isEmpty();
+	options.ignoreCase = ignoreCaseCheckBox->isChecked();
+	options.wholeWords = wholeWordsCheckBox->isChecked();
+	options.paragraphStyleEnabled = searchStyleCheckBox->isChecked();
+	options.alignmentEnabled = searchAlignmentCheckBox->isChecked();
+	options.fontEnabled = searchFontCheckBox->isChecked();
+	options.fontSizeEnabled = searchFontSizeCheckBox->isChecked();
+	options.fontEffectsEnabled = searchFontEffectsCheckBox->isChecked();
+	options.fillColorEnabled = searchFillColorCheckBox->isChecked();
+	options.fillShadeEnabled = searchFillShadeCheckBox->isChecked();
+	options.strokeColorEnabled = searchStrokeColorCheckBox->isChecked();
+	options.strokeShadeEnabled = searchStrokeShadeCheckBox->isChecked();
+
+	if (options.textEnabled)
+		options.text = searchTextValue->text();
+	if (options.paragraphStyleEnabled)
+		options.paragraphStyle = searchStyleComboBox->currentIndex();
+	if (options.alignmentEnabled)
+		options.alignment = searchAlignmentComboBox->currentIndex();
+	if (options.fontEnabled)
+		options.font = searchFontComboBox->currentText();
+	if (options.fontSizeEnabled)
+		options.fontSize = qRound(searchFontSizeSpinBox->value() * 10);
+	if (options.fontEffectsEnabled)
+		options.fontEffects = searchFontEffects->getStyle();
+	if (options.fillColorEnabled)
+		options.fillColor = searchFillColorComboBox->currentText();
+	if (options.fillShadeEnabled)
+		options.fillShade = searchFillShadeToolButton->getValue();
+	if (options.strokeColorEnabled)
 	{
-		replaceEnabled = true;
+		options.strokeColor = searchStrokeColorComboBox->currentText();
 	}
-	replaceEnabled |= RText->isChecked();
-	if (m_itemMode)
-		replaceEnabled &= (m_item->itemText.isSelected());
-	else if (m_doc->scMW()->CurrStED != nullptr)
-		replaceEnabled &= m_doc->scMW()->CurrStED->Editor->textCursor().hasSelection();
-	else
-		replaceEnabled = false;
-	replaceEnabled &= m_found;
-	DoReplace->setEnabled(replaceEnabled);
-	AllReplace->setEnabled(replaceEnabled);
+	if (options.strokeShadeEnabled)
+		options.strokeShade = searchStrokeShadeToolButton->getValue();
+
+	return options;
 }
 
-void SearchReplace::updateSearchButtonState()
+SearchReplace::Options SearchReplace::getReplaceOptions()
 {
-	bool searchEnabled = false;
-	if (SFill->isChecked() || SStroke->isChecked() || SStyle->isChecked() || SFont->isChecked() ||
-		SStrokeS->isChecked() || SFillS->isChecked() || SSize->isChecked() || SEffect->isChecked() ||
-		SAlign->isChecked())
+	Options options{};
+
+	// replace if there is a text or if no format is selected
+	options.textEnabled = !replaceTextValue->text().isEmpty() || !anyReplaceChecked();
+	options.paragraphStyleEnabled = replaceStyleCheckBox->isChecked();
+	options.alignmentEnabled = replaceAlignmentCheckBox->isChecked();
+	options.fontEnabled = replaceFontCheckBox->isChecked();
+	options.fontSizeEnabled = replaceFontSizeCheckBox->isChecked();
+	options.fontEffectsEnabled = replaceFontEffectsCheckBox->isChecked();
+	options.fillColorEnabled = replaceFillColorCheckBox->isChecked();
+	options.fillShadeEnabled = replaceFillShadeCheckBox->isChecked();
+	options.strokeColorEnabled = replaceStrokeColorCheckBox->isChecked();
+	options.strokeShadeEnabled = replaceStrokeShadeCheckBox->isChecked();
+
+	if (options.textEnabled)
+		options.text = replaceTextValue->text();
+	if (options.paragraphStyleEnabled)
+		options.paragraphStyle = replaceStyleComboBox->currentIndex();
+	if (options.alignmentEnabled)
+		options.alignment = replaceAlignmentComboBox->currentIndex();
+	if (replaceFontCheckBox->isChecked())
+		options.font = replaceFontComboBox->currentText();
+	if (options.fontSizeEnabled)
+		options.fontSize = qRound(replaceFontSizeSpinBox->value() * 10);
+	if (options.fontEffectsEnabled)
+		options.fontEffects = replaceFontEffects->getStyle();
+	if (options.fillColorEnabled)
+	{
+		options.fillColor = replaceFillColorComboBox->currentText();
+		options.fillColorId = searchFillColorComboBox->currentIndex();
+	}
+	if (options.fillShadeEnabled)
+		options.fillShade = replaceFillShadeToolButton->getValue();
+	if (options.strokeColorEnabled)
 	{
-		searchEnabled = true;
+		options.strokeColor = replaceStrokeColorComboBox->currentText();
+		options.strokeColorId = searchStrokeColorComboBox->currentIndex();
 	}
-	searchEnabled |= (SText->isChecked() && !STextVal->text().isEmpty());
-	DoSearch->setEnabled(searchEnabled);
+	if (options.strokeShadeEnabled)
+		options.strokeShade = replaceStrokeShadeToolButton->getValue();
+
+
+	return options;
+}
+
+void SearchReplace::updateButtonState()
+{
+	bool enabled = anySearchChecked();
+
+	searchButton->setEnabled(enabled);
+	replaceButton->setEnabled(enabled);
+	replaceAllButton->setEnabled(enabled);
+
+	hideMessage();
+}
+
+void SearchReplace::hideMessage()
+{
+	messageLabel->setVisible(false);
+}
+
+void SearchReplace::showMessage(const QString& message)
+{
+	messageLabel->setText(message);
+	messageLabel->setVisible(true);
 }
 
 void SearchReplace::readPrefs()
 {
-	SStroke->setChecked(m_prefs->getBool("SStroke", false));
-	SFill->setChecked(m_prefs->getBool("SFill", false));
-	SStrokeS->setChecked(m_prefs->getBool("SStrokeS", false));
-	SFillS->setChecked(m_prefs->getBool("SFillS", false));
-	SSize->setChecked(m_prefs->getBool("SSize", false));
-	SFont->setChecked(m_prefs->getBool("SFont", false));
-	SStyle->setChecked(m_prefs->getBool("SStyle", false));
-	SAlign->setChecked(m_prefs->getBool("SAlign", false));
-	SText->setChecked(m_prefs->getBool("SText", true));
-	SEffect->setChecked(m_prefs->getBool("SEffect", false));
-	REffect->setChecked(m_prefs->getBool("REffect", false));
-	STextVal->setText(m_prefs->get("STextVal", ""));
+	m_stateCollapsed = m_prefs->getBool("collapsed", false);
+	{
+		QRect screen = QApplication::desktop()->availableGeometry(this);
+		// by default in the middle of the screen
+		int left = m_prefs->getInt("left", screen.width() > width() ? (screen.width() - width()) / 2 : 0);
+		int top = m_prefs->getInt("top", screen.height() > height() ? (screen.height() - height()) / 2 : 0);
+		// ensure that it's in the screen
+		if (left + width() > screen.width())
+			left = screen.width() - left;
+		if (left < 0)
+			left = 0;
+		move(left, top);
+	}
+	searchStrokeColorCheckBox->setChecked(m_prefs->getBool("SStroke", false));
+	searchFillColorCheckBox->setChecked(m_prefs->getBool("SFill", false));
+	searchStrokeShadeCheckBox->setChecked(m_prefs->getBool("SStrokeS", false));
+	searchFillShadeCheckBox->setChecked(m_prefs->getBool("SFillS", false));
+	searchFontSizeCheckBox->setChecked(m_prefs->getBool("SSize", false));
+	searchFontCheckBox->setChecked(m_prefs->getBool("SFont", false));
+	replaceStyleCheckBox->setChecked(m_prefs->getBool("SStyle", false));
+	searchAlignmentCheckBox->setChecked(m_prefs->getBool("SAlign", false));
+	searchFontEffectsCheckBox->setChecked(m_prefs->getBool("SEffect", false));
+	replaceFontEffectsCheckBox->setChecked(m_prefs->getBool("REffect", false));
+	searchTextValue->setText(m_prefs->get("STextVal", ""));
 	int tmp = m_prefs->getInt("SStyleVal", findParagraphStyle(m_doc, m_doc->currentStyle));
-	if (tmp < 0 || tmp >= SStyleVal->count())
+	if (tmp < 0 || tmp >= searchStyleComboBox->count())
 		tmp = 0;
-	SStyleVal->setCurrentIndex(tmp);
+	searchStyleComboBox->setCurrentIndex(tmp);
 	tmp = m_prefs->getInt("SAlignVal", m_doc->currentStyle.alignment());
-	if (tmp < 0 || tmp >= SAlignVal->count())
+	if (tmp < 0 || tmp >= searchAlignmentComboBox->count())
 		tmp = 0;
-	SAlignVal->setCurrentIndex(tmp);
-	setCurrentComboItem(SFontVal, m_prefs->get("SFontVal", m_doc->currentStyle.charStyle().font().scName()));
-	setCurrentComboItem(SFillVal, m_prefs->get("SFillVal", m_doc->currentStyle.charStyle().fillColor()));
-	setCurrentComboItem(SStrokeVal, m_prefs->get("SStrokeVal", m_doc->currentStyle.charStyle().strokeColor()));
-	SSizeVal->setValue(m_prefs->getDouble("SSizeVal", m_doc->currentStyle.charStyle().fontSize() / 10.0));
-	RStroke->setChecked(m_prefs->getBool("RStroke", false));
-	RStrokeS->setChecked(m_prefs->getBool("RStrokeS", false));
-	RFill->setChecked(m_prefs->getBool("RFill", false));
-	RFillS->setChecked(m_prefs->getBool("RFillS", false));
-	RSize->setChecked(m_prefs->getBool("RSize", false));
-	RFont->setChecked(m_prefs->getBool("RFont", false));
-	RStyle->setChecked(m_prefs->getBool("RStyle", false));
-	RAlign->setChecked(m_prefs->getBool("RAlign", false));
-	RText->setChecked(m_prefs->getBool("RText", true));
-	RTextVal->setText(m_prefs->get("RTextVal", ""));
+	searchAlignmentComboBox->setCurrentIndex(tmp);
+	setCurrentComboItem(searchFontComboBox, m_prefs->get("SFontVal", m_doc->currentStyle.charStyle().font().scName()));
+	setCurrentComboItem(searchFillColorComboBox, m_prefs->get("SFillVal", m_doc->currentStyle.charStyle().fillColor()));
+	setCurrentComboItem(searchStrokeColorComboBox, m_prefs->get("SStrokeVal", m_doc->currentStyle.charStyle().strokeColor()));
+	searchFontSizeSpinBox->setValue(m_prefs->getDouble("SSizeVal", m_doc->currentStyle.charStyle().fontSize() / 10.0));
+	replaceStrokeColorCheckBox->setChecked(m_prefs->getBool("RStroke", false));
+	replaceStrokeShadeCheckBox->setChecked(m_prefs->getBool("RStrokeS", false));
+	replaceFillColorCheckBox->setChecked(m_prefs->getBool("RFill", false));
+	replaceFillShadeCheckBox->setChecked(m_prefs->getBool("RFillS", false));
+	replaceFontSizeCheckBox->setChecked(m_prefs->getBool("RSize", false));
+	replaceFontCheckBox->setChecked(m_prefs->getBool("RFont", false));
+	replaceStyleCheckBox->setChecked(m_prefs->getBool("RStyle", false));
+	replaceAlignmentCheckBox->setChecked(m_prefs->getBool("RAlign", false));
+	replaceTextValue->setText(m_prefs->get("RTextVal", ""));
 	tmp = m_prefs->getInt("RStyleVal", findParagraphStyle(m_doc, m_doc->currentStyle));
-	if (tmp < 0 || tmp >= RStyleVal->count())
+	if (tmp < 0 || tmp >= replaceStyleComboBox->count())
 		tmp = 0;
-	RStyleVal->setCurrentIndex(tmp);
+	replaceStyleComboBox->setCurrentIndex(tmp);
 	tmp = m_prefs->getInt("RAlignVal", m_doc->currentStyle.alignment());
-	if (tmp < 0 || tmp >= RAlignVal->count())
+	if (tmp < 0 || tmp >= replaceAlignmentComboBox->count())
 		tmp = 0;
-	RAlignVal->setCurrentIndex(tmp);
-	setCurrentComboItem(RFontVal, m_prefs->get("RFontVal", m_doc->currentStyle.charStyle().font().scName()));
-	setCurrentComboItem(RFillVal, m_prefs->get("RFillVal", m_doc->currentStyle.charStyle().fillColor()));
-	setCurrentComboItem(RStrokeVal, m_prefs->get("RStrokeVal", m_doc->currentStyle.charStyle().strokeColor()));
-	RSizeVal->setValue(m_prefs->getDouble("RSizeVal", m_doc->currentStyle.charStyle().fontSize() / 10.0));
-	Word->setChecked(m_prefs->getBool("Word", false));
-	CaseIgnore->setChecked(m_prefs->getBool("CaseIgnore", false));
-
-	enableTxSearch();
+	replaceAlignmentComboBox->setCurrentIndex(tmp);
+	setCurrentComboItem(replaceFontComboBox, m_prefs->get("RFontVal", m_doc->currentStyle.charStyle().font().scName()));
+	setCurrentComboItem(replaceFillColorComboBox, m_prefs->get("RFillVal", m_doc->currentStyle.charStyle().fillColor()));
+	setCurrentComboItem(replaceStrokeColorComboBox, m_prefs->get("RStrokeVal", m_doc->currentStyle.charStyle().strokeColor()));
+	replaceFontSizeSpinBox->setValue(m_prefs->getDouble("RSizeVal", m_doc->currentStyle.charStyle().fontSize() / 10.0));
+	wholeWordsCheckBox->setChecked(m_prefs->getBool("Word", false));
+	ignoreCaseCheckBox->setChecked(m_prefs->getBool("IgnoreCase", false));
+
+	enableTextSearch();
 	enableStyleSearch();
-	enableAlignSearch();
+	enableAlignmentSearch();
 	enableFontSearch();
-	enableSizeSearch();
-	enableEffSearch();
-	enableFillSearch();
-	enableFillSSearch();
-	enableStrokeSearch();
-	enableStrokeSSearch();
-	enableTxReplace();
+	enableFontSizeSearch();
+	enableFontEffectsSearch();
+	enableFillColorSearch();
+	enableFillShadeSearch();
+	enableStrokeColorSearch();
+	enableStrokeShadeSearch();
 	enableStyleReplace();
-	enableAlignReplace();
+	enableAlignmentReplace();
 	enableFontReplace();
-	enableSizeReplace();
-	enableEffReplace();
-	enableFillReplace();
-	enableFillSReplace();
-	enableStrokeReplace();
-	enableStrokeSReplace();
-
-	if (SText->isChecked() && RText->isChecked())
-		STextVal->setFocus();
+	enableFontSizeReplace();
+	enableFontEffectsReplace();
+	enableFillColorReplace();
+	enableFillShadeReplace();
+	enableStrokeColorReplace();
+	enableStrokeShadeReplace();
+
+	updateButtonState();
+
+	searchTextValue->setFocus();
+}
+
+void SearchReplace::accept()
+{
+	writePrefs();
+	QDialog::accept();
 }
 
 void SearchReplace::writePrefs()
 {
-	m_prefs->set("SStroke", SStroke->isChecked());
-	m_prefs->set("SFill", SFill->isChecked());
-	m_prefs->set("SStrokeS", SStrokeS->isChecked());
-	m_prefs->set("SFillS", SFillS->isChecked());
-	m_prefs->set("SSize", SSize->isChecked());
-	m_prefs->set("SFont", SFont->isChecked());
-	m_prefs->set("SStyle", SStyle->isChecked());
-	m_prefs->set("SAlign", SAlign->isChecked());
-	m_prefs->set("SText", SText->isChecked());
-	m_prefs->set("SEffect", SEffect->isChecked());
-	m_prefs->set("REffect", REffect->isChecked());
-	m_prefs->set("STextVal", STextVal->text());
-	m_prefs->set("SStyleVal", SStyleVal->currentIndex());
-	m_prefs->set("SAlignVal", SAlignVal->currentIndex());
-	m_prefs->set("SFontVal", SFontVal->currentText());
-	m_prefs->set("SSizeVal", SSizeVal->value());
-	m_prefs->set("SFillVal", SFillVal->currentText());
-	m_prefs->set("SStrokeVal", SStrokeVal->currentText());
-	m_prefs->set("RStroke", RStroke->isChecked());
-	m_prefs->set("RStrokeS", RStrokeS->isChecked());
-	m_prefs->set("RFill", RFill->isChecked());
-	m_prefs->set("RFillS", RFillS->isChecked());
-	m_prefs->set("RSize", RSize->isChecked());
-	m_prefs->set("RFont", RFont->isChecked());
-	m_prefs->set("RStyle", RStyle->isChecked());
-	m_prefs->set("RAlign", RAlign->isChecked());
-	m_prefs->set("RText", RText->isChecked());
-	m_prefs->set("RTextVal", RTextVal->text());
-	m_prefs->set("RStyleVal", RStyleVal->currentText());
-	m_prefs->set("RAlignVal", RAlignVal->currentIndex());
-	m_prefs->set("RFontVal", RFontVal->currentText());
-	m_prefs->set("RSizeVal", RSizeVal->value());
-	m_prefs->set("RFillVal", RFillVal->currentText());
-	m_prefs->set("RStrokeVal", RStrokeVal->currentText());
-	m_prefs->set("Word", Word->isChecked());
-	m_prefs->set("CaseIgnore", CaseIgnore->isChecked());
-	accept();
+	m_prefs->set("collapsed", m_stateCollapsed);
+	m_prefs->set("left", pos().x());
+	m_prefs->set("top", pos().y());
+	m_prefs->set("SStroke", searchStrokeColorCheckBox->isChecked());
+	m_prefs->set("SStroke", searchStrokeColorCheckBox->isChecked());
+	m_prefs->set("SFill", searchFillColorCheckBox->isChecked());
+	m_prefs->set("SStrokeS", searchStrokeShadeCheckBox->isChecked());
+	m_prefs->set("SFillS", replaceFillShadeCheckBox->isChecked());
+	m_prefs->set("SSize", searchFontSizeCheckBox->isChecked());
+	m_prefs->set("SFont", searchFontCheckBox->isChecked());
+	m_prefs->set("SStyle", replaceStyleCheckBox->isChecked());
+	m_prefs->set("SAlign", searchAlignmentCheckBox->isChecked());
+	m_prefs->set("SEffect", searchFontEffectsCheckBox->isChecked());
+	m_prefs->set("REffect", replaceFontEffectsCheckBox->isChecked());
+	m_prefs->set("STextVal", searchTextValue->text());
+	m_prefs->set("SStyleVal", searchStyleComboBox->currentIndex());
+	m_prefs->set("SAlignVal", searchAlignmentComboBox->currentIndex());
+	m_prefs->set("SFontVal", searchFontComboBox->currentText());
+	m_prefs->set("SSizeVal", searchFontSizeSpinBox->value());
+	m_prefs->set("SFillVal", searchFillColorComboBox->currentText());
+	m_prefs->set("SStrokeVal", searchStrokeColorComboBox->currentText());
+	m_prefs->set("RStroke", replaceStrokeColorCheckBox->isChecked());
+	m_prefs->set("RStrokeS", replaceStrokeShadeCheckBox->isChecked());
+	m_prefs->set("RFill", replaceFillColorCheckBox->isChecked());
+	m_prefs->set("RFillS", replaceFillShadeCheckBox->isChecked());
+	m_prefs->set("RSize", replaceFontSizeCheckBox->isChecked());
+	m_prefs->set("RFont", replaceFontCheckBox->isChecked());
+	m_prefs->set("RStyle", replaceStyleCheckBox->isChecked());
+	m_prefs->set("RAlign", replaceAlignmentCheckBox->isChecked());
+	m_prefs->set("RTextVal", replaceTextValue->text());
+	m_prefs->set("RStyleVal", replaceStyleComboBox->currentText());
+	m_prefs->set("RAlignVal", replaceAlignmentComboBox->currentIndex());
+	m_prefs->set("RFontVal", replaceFontComboBox->currentText());
+	m_prefs->set("RSizeVal", replaceFontSizeSpinBox->value());
+	m_prefs->set("RFillVal", replaceFillColorComboBox->currentText());
+	m_prefs->set("RStrokeVal", replaceStrokeColorComboBox->currentText());
+	m_prefs->set("Word", wholeWordsCheckBox->isChecked());
+	m_prefs->set("IgnoreCase", ignoreCaseCheckBox->isChecked());
 }
diff --git a/scribus/ui/search.h b/scribus/ui/search.h
index 0d0a42e20dbe9cb35d4a17421713f5c7c8302c62..52e684db55c07f84be2322932081ff18de7b383b 100644
--- a/scribus/ui/search.h
+++ b/scribus/ui/search.h
@@ -7,145 +7,173 @@ for which a new license (GPL+exception) is in place.
 #ifndef SEARCHREPLACE_H
 #define SEARCHREPLACE_H
 
-#include <QDialog>
-class QVBoxLayout;
-class QHBoxLayout;
-class QGridLayout;
-class QCheckBox;
-class QComboBox;
-class QGroupBox;
-class QLineEdit;
-class QPushButton;
-class QLabel;
+#include "ui_searchbase.h"
 
 #include "scribusapi.h"
-class ScrSpinBox;
-class FontCombo;
-class StyleSelect;
-class ShadeButton;
+
 class PrefsContext;
-class ColorCombo;
+class ParagraphStyle;
+class CharStyle;
+
 class ScribusDoc;
 class PageItem;
 
-class SCRIBUS_API SearchReplace : public QDialog
+class SCRIBUS_API SearchReplace : public QDialog, Ui::SearchReplaceBase
 {
 	Q_OBJECT
 
+private:
+	struct Options 
+	{
+		bool textEnabled{false};
+		bool ignoreCase{false};
+		bool wholeWords{false};
+		bool paragraphStyleEnabled{false};
+		bool alignmentEnabled{false};
+		bool fontEnabled{false};
+		bool fontSizeEnabled{false};
+		bool fontEffectsEnabled{false};
+		bool fillColorEnabled{false};
+		bool fillShadeEnabled{false};
+		bool strokeColorEnabled{false};
+		bool strokeShadeEnabled{false};
+
+		QString text{};
+		int paragraphStyle{0};
+		int alignment{0};
+		QString font{};
+		int fontSize{0};
+		int fontEffects{0};
+		QString fillColor{};
+		int fillColorId{0};
+		int fillShade{100};
+		QString strokeColor{};
+		int strokeColorId{0};
+		int strokeShade{100};
+	};
+
+	// TODO: as soon as we have c++ 17: std::optional<int> start
+	struct MatchRange
+	{
+		int start;
+		int end;
+		int nextPosition;
+	};
+
 public:
-	SearchReplace( QWidget* parent, ScribusDoc *doc, PageItem* ite, bool mode = true );
+	// mode is false when calling from the story editor
+	SearchReplace( QWidget* parent, ScribusDoc *doc);
 	~SearchReplace() {};
 
-	int firstMatchCursorPosition();
-	void setSearchedText(const QString& text);
-
-	QLabel* SText1;
-	QLabel* RText1;
-	QGroupBox* Search;
-	QCheckBox* SStroke;
-	QCheckBox* SFill;
-	QCheckBox* SStrokeS;
-	QCheckBox* SFillS;
-	QCheckBox* SSize;
-	QCheckBox* SFont;
-	QCheckBox* SStyle;
-	QCheckBox* SAlign;
-	QCheckBox* SText;
-	QCheckBox* SEffect;
-	QCheckBox* REffect;
-	QLineEdit* STextVal;
-	QComboBox* SStyleVal;
-	QComboBox* SAlignVal;
-	FontCombo* SFontVal;
-	ScrSpinBox* SSizeVal;
-	ColorCombo* SFillVal;
-	ShadeButton *SFillSVal;
-	ColorCombo* SStrokeVal;
-	ShadeButton *SStrokeSVal;
-	QGroupBox* Replace;
-	QCheckBox* RStroke;
-	QCheckBox* RStrokeS;
-	QCheckBox* RFill;
-	QCheckBox* RFillS;
-	QCheckBox* RSize;
-	QCheckBox* RFont;
-	QCheckBox* RStyle;
-	QCheckBox* RAlign;
-	QCheckBox* RText;
-	QLineEdit* RTextVal;
-	QComboBox* RStyleVal;
-	QComboBox* RAlignVal;
-	FontCombo* RFontVal;
-	ScrSpinBox* RSizeVal;
-	ColorCombo* RFillVal;
-	ShadeButton *RFillSVal;
-	ColorCombo* RStrokeVal;
-	ShadeButton *RStrokeSVal;
-	StyleSelect* SEffVal;
-	StyleSelect* REffVal;
-	QCheckBox* Word;
-	QCheckBox* CaseIgnore;
-	QPushButton* DoSearch;
-	QPushButton* DoReplace;
-	QPushButton* AllReplace;
-	QPushButton* clearButton;
-	QPushButton* Leave;
+	void setStoryEditorMode(bool mode = true) {m_storyEditorMode = true;}
+	QPair<int, int> cursorPosition();
+	//! \brief fill the text field with the current selection, if the selection does not contain a newline.
+	void processCurrentSelection(QString selection);
 
 public slots:
-	virtual void slotSearch();
-	virtual void slotReplace();
-	virtual void slotReplaceAll();
-	virtual void enableTxSearch();
-	virtual void enableStyleSearch();
-	virtual void enableAlignSearch();
-	virtual void enableFontSearch();
-	virtual void enableSizeSearch();
-	virtual void enableEffSearch();
-	virtual void enableFillSearch();
-	virtual void enableFillSSearch();
-	virtual void enableStrokeSearch();
-	virtual void enableStrokeSSearch();
-	virtual void enableTxReplace();
-	virtual void enableStyleReplace();
-	virtual void enableAlignReplace();
-	virtual void enableFontReplace();
-	virtual void enableSizeReplace();
-	virtual void enableEffReplace();
-	virtual void enableFillReplace();
-	virtual void enableFillSReplace();
-	virtual void enableStrokeReplace();
-	virtual void enableStrokeSReplace();
-	virtual void updateReplaceButtonsState();
-	virtual void updateSearchButtonState();
-	virtual void writePrefs();
-	virtual void clear();
-
-protected:
-	PageItem*   m_item;
+	void slotSearch();
+	void slotReplace();
+	void slotReplaceAll();
+	void slotCollapseFormat();
+	void enableTextSearch();
+	void enableStyleSearch();
+	void enableAlignmentSearch();
+	void enableFontSearch();
+	void enableFontSizeSearch();
+	void enableFontEffectsSearch();
+	void enableFillColorSearch();
+	void enableFillShadeSearch();
+	void enableStrokeColorSearch();
+	void enableStrokeShadeSearch();
+	void enableStyleReplace();
+	void enableAlignmentReplace();
+	void enableFontReplace();
+	void enableFontSizeReplace();
+	void enableFontEffectsReplace();
+	void enableFillColorReplace();
+	void enableFillShadeReplace();
+	void enableStrokeColorReplace();
+	void enableStrokeShadeReplace();
+	void updateButtonState();
+	void accept() override;
+	void clear();
+
+private:
+	PageItem*   m_item{nullptr};
 	ScribusDoc* m_doc;
 
+	bool m_storyEditorMode{false};
+
+	bool m_stateCollapsed{true};
+
 	uint m_replStart;
 	PrefsContext* m_prefs;
 	bool m_found { false };
 	bool m_itemMode;
 	bool m_replacingAll { false };
 
-	QVBoxLayout* SearchReplaceLayout;
-	QHBoxLayout* SelLayout;
-	QGridLayout* SearchLayout;
-	QGridLayout* ReplaceLayout;
-	QHBoxLayout* OptsLayout;
-	QHBoxLayout* ButtonsLayout;
+	void collapseFormat();
+	void readPrefs();
+	void writePrefs();
+
+	void hideMessage();
+	void showMessage(const QString& message);
+
+	void searchInStoryEditor();
+	void searchOnCanvas();
+	//! /brief Search storyText starting from start.
+	//! /param length The length of the story
+	//! /return The start and end position of the selection or (-1, -1) if nothing found.
+	QPair<int, int> searchStory(const StoryText& storyText, int start, const int length, const Options& options);
+
+	//! \brief Check if there is a match starting at position
+	//!
+	//! When textEnabled, the resulting start position might be different than position.
+	MatchRange getMatchRange(const StoryText& storyText, int position, const int length, const Options& options);
+	//! If the current selection matches the search, replace the selection.
+	// Otherwise do a search.
+	void replaceInStoryEditor();
+	void replaceOnCanvas();
+	//! \brief if the current selection matches the search, replace the selection.
+	// otherwise do a search.
+	void replaceSelectionInStoryEditor();
+	void replaceSelectionOnCanvas();
+	void replaceAllInStoryEditor();
+	void replaceAllOnCanvas();
+
+	//! \brief Initialize pageItems based on the current selection
+	void initCanvasSelection();
+	//! \brief Put all the document's text frames into pageItems
+	void readAllPageItems();
+	//! \brief Put all the selected text frames into pageItems
+	void readSelectedPageItems();
+	PageItem* currentPageItem();
+	void nextPageItem();
+	void selectPageItem(PageItem* pageItem);
+
+	size_t m_currentPageItem{0};
+	std::vector<PageItem*> m_pageItems{};
+	bool m_endReached{false};
+
+	void updatePageItemSelection(PageItem* pageItem);
+
+	void showNotFoundMessage();
 
-	virtual void doSearch();
-	virtual void doReplace();
-	virtual void showNotFoundMessage();
 
-	virtual void readPrefs();
+	// \brief is any search checkbox checked?
+	bool anySearchChecked();
+	// \brief is any replace checkbox checked?
+	bool anyReplaceChecked();
+	Options getSearchOptions();
+	Options getReplaceOptions();
+	// \brief Check for all search options but the text
+	bool isFormatMatching(const ParagraphStyle& parStyle, const CharStyle& charStyle, const Options& options);
 
 	/// Number of matches found thus far in a search
-	int m_matchesFound { 0 };
-	int m_firstMatchPosition { -1 };
+	int m_matchesFound {0};
+	// TODO: isn't 0, 0 good enough?
+	int m_selectionStart{-1};
+	int m_selectionEnd{-1};
 
 };
 
diff --git a/scribus/ui/searchbase.ui b/scribus/ui/searchbase.ui
new file mode 100644
index 0000000000000000000000000000000000000000..877ad4864bdd3a85ce47aac4f2cf9b4809207a8d
--- /dev/null
+++ b/scribus/ui/searchbase.ui
@@ -0,0 +1,594 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>SearchReplaceBase</class>
+ <widget class="QDialog" name="SearchReplaceBase">
+  <property name="windowModality">
+   <enum>Qt::ApplicationModal</enum>
+  </property>
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>651</width>
+    <height>459</height>
+   </rect>
+  </property>
+  <property name="sizePolicy">
+   <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+    <horstretch>0</horstretch>
+    <verstretch>0</verstretch>
+   </sizepolicy>
+  </property>
+  <property name="windowTitle">
+   <string>Search/Replace</string>
+  </property>
+  <property name="modal">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_4">
+     <item>
+      <layout class="QGridLayout" name="gridLayout_2">
+       <item row="0" column="0">
+        <widget class="QLabel" name="searchTextLabel">
+         <property name="text">
+          <string>Search:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLineEdit" name="searchTextValue">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>250</width>
+           <height>0</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+       <item row="1" column="1">
+        <widget class="QCheckBox" name="wholeWordsCheckBox">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>&amp;Whole Word</string>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <widget class="QCheckBox" name="ignoreCaseCheckBox">
+         <property name="enabled">
+          <bool>false</bool>
+         </property>
+         <property name="text">
+          <string>&amp;Ignore Case, Diacritics and Kashida</string>
+         </property>
+        </widget>
+       </item>
+      </layout>
+     </item>
+     <item>
+      <layout class="QGridLayout" name="gridLayout_4">
+       <item row="0" column="0">
+        <widget class="QLabel" name="replaceTextLabel">
+         <property name="text">
+          <string>Replace:</string>
+         </property>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <widget class="QLineEdit" name="replaceTextValue">
+         <property name="enabled">
+          <bool>true</bool>
+         </property>
+         <property name="minimumSize">
+          <size>
+           <width>250</width>
+           <height>0</height>
+          </size>
+         </property>
+        </widget>
+       </item>
+       <item row="2" column="1">
+        <layout class="QHBoxLayout" name="horizontalLayout_2">
+         <item>
+          <spacer name="horizontalSpacer_2">
+           <property name="orientation">
+            <enum>Qt::Horizontal</enum>
+           </property>
+           <property name="sizeHint" stdset="0">
+            <size>
+             <width>40</width>
+             <height>20</height>
+            </size>
+           </property>
+          </spacer>
+         </item>
+         <item>
+          <widget class="QPushButton" name="collapseButton">
+           <property name="text">
+            <string>More...</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </item>
+       <item row="1" column="1">
+        <spacer name="horizontalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QLabel" name="messageLabel">
+     <property name="enabled">
+      <bool>true</bool>
+     </property>
+     <property name="text">
+      <string>Feedback</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QGroupBox" name="searchGroupBox">
+       <property name="title">
+        <string/>
+       </property>
+       <layout class="QGridLayout" name="gridLayout">
+        <item row="11" column="1">
+         <widget class="ColorCombo" name="searchStrokeColorComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="5" column="1">
+         <widget class="QComboBox" name="searchAlignmentComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="11" column="0">
+         <widget class="QCheckBox" name="searchStrokeColorCheckBox">
+          <property name="text">
+           <string>Stroke Colour</string>
+          </property>
+         </widget>
+        </item>
+        <item row="10" column="1">
+         <widget class="ShadeButton" name="searchFillShadeToolButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+        <item row="7" column="1">
+         <widget class="ScrSpinBox" name="searchFontSizeSpinBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="minimum">
+           <double>0.500000000000000</double>
+          </property>
+          <property name="maximum">
+           <double>2048.000000000000000</double>
+          </property>
+         </widget>
+        </item>
+        <item row="5" column="0">
+         <widget class="QCheckBox" name="searchAlignmentCheckBox">
+          <property name="text">
+           <string>Alignment</string>
+          </property>
+         </widget>
+        </item>
+        <item row="9" column="1">
+         <widget class="ColorCombo" name="searchFillColorComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="12" column="0">
+         <widget class="QCheckBox" name="searchStrokeShadeCheckBox">
+          <property name="text">
+           <string>Stroke Shade</string>
+          </property>
+         </widget>
+        </item>
+        <item row="8" column="1">
+         <widget class="StyleSelect" name="searchFontEffects" native="true">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="7" column="0">
+         <widget class="QCheckBox" name="searchFontSizeCheckBox">
+          <property name="text">
+           <string>Font Size</string>
+          </property>
+         </widget>
+        </item>
+        <item row="9" column="0">
+         <widget class="QCheckBox" name="searchFillColorCheckBox">
+          <property name="text">
+           <string>Fill Colour</string>
+          </property>
+         </widget>
+        </item>
+        <item row="6" column="0">
+         <widget class="QCheckBox" name="searchFontCheckBox">
+          <property name="text">
+           <string>Font</string>
+          </property>
+         </widget>
+        </item>
+        <item row="8" column="0">
+         <widget class="QCheckBox" name="searchFontEffectsCheckBox">
+          <property name="text">
+           <string>Font Effects</string>
+          </property>
+         </widget>
+        </item>
+        <item row="6" column="1">
+         <widget class="FontCombo" name="searchFontComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="12" column="1">
+         <widget class="ShadeButton" name="searchStrokeShadeToolButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="1">
+         <widget class="QComboBox" name="searchStyleComboBox">
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+         </widget>
+        </item>
+        <item row="10" column="0">
+         <widget class="QCheckBox" name="searchFillShadeCheckBox">
+          <property name="text">
+           <string>Fill Shade</string>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="0">
+         <widget class="QCheckBox" name="searchStyleCheckBox">
+          <property name="text">
+           <string>Style</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <widget class="QGroupBox" name="replaceGroupBox">
+       <property name="title">
+        <string/>
+       </property>
+       <layout class="QGridLayout" name="gridLayout_3">
+        <item row="5" column="0">
+         <widget class="QCheckBox" name="replaceFontEffectsCheckBox">
+          <property name="text">
+           <string>Font Effects</string>
+          </property>
+         </widget>
+        </item>
+        <item row="8" column="0">
+         <widget class="QCheckBox" name="replaceStrokeColorCheckBox">
+          <property name="text">
+           <string>Stroke Colour</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0">
+         <widget class="QCheckBox" name="replaceStyleCheckBox">
+          <property name="text">
+           <string>Style</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="0">
+         <widget class="QCheckBox" name="replaceAlignmentCheckBox">
+          <property name="text">
+           <string>Alignment</string>
+          </property>
+         </widget>
+        </item>
+        <item row="6" column="0">
+         <widget class="QCheckBox" name="replaceFillColorCheckBox">
+          <property name="text">
+           <string>Fill Colour</string>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="0">
+         <widget class="QCheckBox" name="replaceFontCheckBox">
+          <property name="text">
+           <string>Font</string>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="0">
+         <widget class="QCheckBox" name="replaceFontSizeCheckBox">
+          <property name="text">
+           <string>Font Size</string>
+          </property>
+         </widget>
+        </item>
+        <item row="9" column="0">
+         <widget class="QCheckBox" name="replaceStrokeShadeCheckBox">
+          <property name="text">
+           <string>Stroke Shade</string>
+          </property>
+         </widget>
+        </item>
+        <item row="7" column="0">
+         <widget class="QCheckBox" name="replaceFillShadeCheckBox">
+          <property name="text">
+           <string>Fill Shade</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
+         <widget class="QComboBox" name="replaceStyleComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="sizePolicy">
+           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+            <horstretch>0</horstretch>
+            <verstretch>0</verstretch>
+           </sizepolicy>
+          </property>
+         </widget>
+        </item>
+        <item row="5" column="1">
+         <widget class="StyleSelect" name="replaceFontEffects" native="true">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="4" column="1">
+         <widget class="ScrSpinBox" name="replaceFontSizeSpinBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="minimum">
+           <double>0.500000000000000</double>
+          </property>
+          <property name="maximum">
+           <double>2048.000000000000000</double>
+          </property>
+         </widget>
+        </item>
+        <item row="3" column="1">
+         <widget class="FontCombo" name="replaceFontComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="1">
+         <widget class="QComboBox" name="replaceAlignmentComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="6" column="1">
+         <widget class="ColorCombo" name="replaceFillColorComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="7" column="1">
+         <widget class="ShadeButton" name="replaceFillShadeToolButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+        <item row="8" column="1">
+         <widget class="ColorCombo" name="replaceStrokeColorComboBox">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+         </widget>
+        </item>
+        <item row="9" column="1">
+         <widget class="ShadeButton" name="replaceStrokeShadeToolButton">
+          <property name="enabled">
+           <bool>false</bool>
+          </property>
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QPushButton" name="searchButton">
+       <property name="text">
+        <string>&amp;Search</string>
+       </property>
+       <property name="default">
+        <bool>true</bool>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="replaceButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>&amp;Replace</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="replaceAllButton">
+       <property name="enabled">
+        <bool>false</bool>
+       </property>
+       <property name="text">
+        <string>&amp;ReplaceAll</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <spacer name="horizontalSpacer">
+       <property name="orientation">
+        <enum>Qt::Horizontal</enum>
+       </property>
+       <property name="sizeHint" stdset="0">
+        <size>
+         <width>40</width>
+         <height>20</height>
+        </size>
+       </property>
+      </spacer>
+     </item>
+     <item>
+      <widget class="QPushButton" name="clearButton">
+       <property name="text">
+        <string>C&amp;lear</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QPushButton" name="closeButton">
+       <property name="text">
+        <string>&amp;Close</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>ScrSpinBox</class>
+   <extends>QDoubleSpinBox</extends>
+   <header>ui/scrspinbox.h</header>
+  </customwidget>
+  <customwidget>
+   <class>StyleSelect</class>
+   <extends>QWidget</extends>
+   <header>ui/styleselect.h</header>
+   <container>1</container>
+  </customwidget>
+  <customwidget>
+   <class>FontCombo</class>
+   <extends>QComboBox</extends>
+   <header>ui/fontcombo.h</header>
+  </customwidget>
+  <customwidget>
+   <class>ColorCombo</class>
+   <extends>QComboBox</extends>
+   <header>ui/colorcombo.h</header>
+  </customwidget>
+  <customwidget>
+   <class>ShadeButton</class>
+   <extends>QToolButton</extends>
+   <header>ui/shadebutton.h</header>
+  </customwidget>
+ </customwidgets>
+ <tabstops>
+  <tabstop>searchTextValue</tabstop>
+  <tabstop>replaceTextValue</tabstop>
+  <tabstop>searchButton</tabstop>
+  <tabstop>replaceButton</tabstop>
+  <tabstop>replaceAllButton</tabstop>
+  <tabstop>wholeWordsCheckBox</tabstop>
+  <tabstop>ignoreCaseCheckBox</tabstop>
+  <tabstop>collapseButton</tabstop>
+  <tabstop>searchStyleCheckBox</tabstop>
+  <tabstop>searchStyleComboBox</tabstop>
+  <tabstop>replaceStyleCheckBox</tabstop>
+  <tabstop>replaceStyleComboBox</tabstop>
+  <tabstop>searchAlignmentCheckBox</tabstop>
+  <tabstop>searchAlignmentComboBox</tabstop>
+  <tabstop>replaceAlignmentCheckBox</tabstop>
+  <tabstop>replaceAlignmentComboBox</tabstop>
+  <tabstop>searchFontCheckBox</tabstop>
+  <tabstop>searchFontComboBox</tabstop>
+  <tabstop>replaceFontCheckBox</tabstop>
+  <tabstop>replaceFontComboBox</tabstop>
+  <tabstop>searchFontSizeCheckBox</tabstop>
+  <tabstop>searchFontSizeSpinBox</tabstop>
+  <tabstop>replaceFontSizeCheckBox</tabstop>
+  <tabstop>replaceFontSizeSpinBox</tabstop>
+  <tabstop>searchFontEffectsCheckBox</tabstop>
+  <tabstop>replaceFontEffectsCheckBox</tabstop>
+  <tabstop>searchFillColorCheckBox</tabstop>
+  <tabstop>searchFillColorComboBox</tabstop>
+  <tabstop>replaceFillColorCheckBox</tabstop>
+  <tabstop>replaceFillColorComboBox</tabstop>
+  <tabstop>searchFillShadeCheckBox</tabstop>
+  <tabstop>searchFillShadeToolButton</tabstop>
+  <tabstop>replaceFillShadeCheckBox</tabstop>
+  <tabstop>replaceFillShadeToolButton</tabstop>
+  <tabstop>searchStrokeColorCheckBox</tabstop>
+  <tabstop>searchStrokeColorComboBox</tabstop>
+  <tabstop>replaceStrokeColorCheckBox</tabstop>
+  <tabstop>replaceStrokeColorComboBox</tabstop>
+  <tabstop>searchStrokeShadeCheckBox</tabstop>
+  <tabstop>searchStrokeShadeToolButton</tabstop>
+  <tabstop>replaceStrokeShadeCheckBox</tabstop>
+  <tabstop>replaceStrokeShadeToolButton</tabstop>
+  <tabstop>clearButton</tabstop>
+  <tabstop>closeButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/scribus/ui/storyeditor.cpp b/scribus/ui/storyeditor.cpp
index 645e625154d8158d96351e7e4874b5fc4ee53df3..286631b7d8e3632b003337385f53f3f78a4c9088 100644
--- a/scribus/ui/storyeditor.cpp
+++ b/scribus/ui/storyeditor.cpp
@@ -3032,16 +3032,24 @@ void StoryEditor::SearchText()
 {
 	m_blockUpdate = true;
 	EditorBar->setRepaint(false);
-	QScopedPointer<SearchReplace> dia(new SearchReplace(this, m_doc, m_item, false));
-	if (dia->exec())
+	SearchReplace dia(this, m_doc);
+
+	dia.setStoryEditorMode();
+
+	dia.processCurrentSelection(Editor->textCursor().selectedText());
+
+	if (dia.exec())
 	{
-		int pos = dia->firstMatchCursorPosition();
-		if (pos >= 0)
+		// TODO: when available: const auto [start, end] = ...
+		const auto pos = dia.cursorPosition();
+		if (pos.first >= 0)
 		{
-			QTextCursor tCursor = Editor->textCursor();
-			tCursor.setPosition(pos);
-			Editor->setTextCursor(tCursor);
-			Editor->SelStack.push(std::make_tuple(pos, -1, Editor->verticalScrollBar()->value()));
+			// TODO: why isn't enough to set the cursor in the search dialog?
+			QTextCursor cursor = Editor->textCursor();
+			cursor.setPosition(pos.first);
+			cursor.setPosition(pos.second, QTextCursor::KeepAnchor);
+			Editor->setTextCursor(cursor);
+			Editor->SelStack.push(std::make_tuple(pos.first, pos.second, Editor->verticalScrollBar()->value()));
 		}
 	}
 	qApp->processEvents();
