File indexing completed on 2025-01-05 05:14:51
0001 /* 0002 SPDX-FileCopyrightText: 2021 Hamed Masafi <hamed.masfi@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "diffwidget.h" 0008 #include "codeeditor.h" 0009 #include <diff.h> 0010 0011 #include <QScrollBar> 0012 #include <QTextBlock> 0013 0014 DiffWidget::DiffWidget(QWidget *parent) 0015 : QWidget{parent} 0016 , mOldFile() 0017 , mNewFile() 0018 { 0019 setupUi(this); 0020 init(); 0021 } 0022 0023 DiffWidget::DiffWidget(QSharedPointer<Git::File> oldFile, QSharedPointer<Git::File> newFile, QWidget *parent) 0024 : QWidget{parent} 0025 , mOldFile(oldFile) 0026 , mNewFile(newFile) 0027 { 0028 setupUi(this); 0029 init(); 0030 } 0031 0032 void DiffWidget::init() 0033 { 0034 createPreviewWidget(); 0035 segmentConnector->setMinimumWidth(80); 0036 segmentConnector->setMaximumWidth(80); 0037 segmentConnector->setLeft(leftCodeEditor); 0038 segmentConnector->setRight(rightCodeEditor); 0039 0040 widgetSegmentsScrollBar->setSegmentConnector(segmentConnector); 0041 0042 connect(leftCodeEditor, &CodeEditor::blockSelected, this, &DiffWidget::oldCodeEditor_blockSelected); 0043 connect(rightCodeEditor, &CodeEditor::blockSelected, this, &DiffWidget::newCodeEditor_blockSelected); 0044 connect(leftCodeEditor->verticalScrollBar(), &QScrollBar::valueChanged, this, &DiffWidget::oldCodeEditor_scroll); 0045 connect(rightCodeEditor->verticalScrollBar(), &QScrollBar::valueChanged, this, &DiffWidget::newCodeEditor_scroll); 0046 connect(splitter, &QSplitter::splitterMoved, this, &DiffWidget::slotSplitterSplitterMoved); 0047 0048 recalculateInfoPaneSize(); 0049 0050 mDefaultOption = leftCodeEditor->document()->defaultTextOption(); 0051 0052 connect(widgetSegmentsScrollBar, &SegmentsScrollBar::hover, this, &DiffWidget::slotSegmentsScrollbarHover); 0053 connect(widgetSegmentsScrollBar, &SegmentsScrollBar::mouseEntered, mPreviewWidget, &QWidget::show); 0054 connect(widgetSegmentsScrollBar, &SegmentsScrollBar::mouseLeaved, mPreviewWidget, &QWidget::hide); 0055 showFilesInfo(true); 0056 } 0057 0058 void DiffWidget::createPreviewWidget() 0059 { 0060 mPreviewWidget = new QWidget(this); 0061 auto layout = new QHBoxLayout(mPreviewWidget); 0062 mPreviewEditorLeft = new CodeEditor(mPreviewWidget); 0063 mPreviewEditorLeft->setShowFoldMarks(false); 0064 mPreviewEditorLeft->setShowTitleBar(false); 0065 mPreviewEditorLeft->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0066 mPreviewEditorLeft->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0067 layout->addWidget(mPreviewEditorLeft); 0068 0069 mPreviewEditorRight = new CodeEditor(mPreviewWidget); 0070 mPreviewEditorRight->setShowFoldMarks(false); 0071 mPreviewEditorRight->setShowTitleBar(false); 0072 mPreviewEditorRight->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0073 mPreviewEditorRight->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 0074 layout->addWidget(mPreviewEditorRight); 0075 0076 layout->setContentsMargins(0, 0, 0, 0); 0077 layout->setSpacing(segmentConnector->width() + 2 * (splitter->handleWidth())); 0078 mPreviewWidget->setLayout(layout); 0079 mPreviewWidget->hide(); 0080 } 0081 QSharedPointer<Git::File> DiffWidget::oldFile() const 0082 { 0083 return mOldFile; 0084 } 0085 0086 void DiffWidget::setOldFileText(const QString &newOldFile) 0087 { 0088 leftCodeEditor->setTitle(newOldFile); 0089 } 0090 0091 void DiffWidget::setOldFile(QSharedPointer<Git::File> newOldFile) 0092 { 0093 mOldFile = newOldFile; 0094 setOldFileText(newOldFile->displayName()); 0095 } 0096 0097 QSharedPointer<Git::File> DiffWidget::newFile() const 0098 { 0099 return mNewFile; 0100 } 0101 0102 void DiffWidget::setNewFileText(const QString &newNewFile) 0103 { 0104 rightCodeEditor->setTitle(newNewFile); 0105 } 0106 0107 void DiffWidget::setNewFile(QSharedPointer<Git::File> newNewFile) 0108 { 0109 mNewFile = newNewFile; 0110 setNewFileText(newNewFile->displayName()); 0111 } 0112 0113 void DiffWidget::compare() 0114 { 0115 if (!mOldFile || !mNewFile) 0116 return; 0117 0118 const auto segments = Diff::diff(mOldFile->content(), mNewFile->content()); 0119 0120 leftCodeEditor->clearAll(); 0121 rightCodeEditor->clearAll(); 0122 0123 mPreviewEditorLeft->clearAll(); 0124 mPreviewEditorRight->clearAll(); 0125 0126 leftCodeEditor->setHighlighting(mOldFile->fileName()); 0127 rightCodeEditor->setHighlighting(mNewFile->fileName()); 0128 0129 mPreviewEditorLeft->setHighlighting(mOldFile->fileName()); 0130 mPreviewEditorRight->setHighlighting(mNewFile->fileName()); 0131 0132 segmentConnector->setSegments(segments); 0133 segmentConnector->update(); 0134 0135 for (const auto &s : segments) { 0136 CodeEditor::BlockType oldBlockType, newBlockType; 0137 switch (s->type) { 0138 case Diff::SegmentType::SameOnBoth: 0139 oldBlockType = newBlockType = CodeEditor::Unchanged; 0140 break; 0141 case Diff::SegmentType::OnlyOnLeft: 0142 oldBlockType = CodeEditor::Removed; 0143 newBlockType = CodeEditor::Added; 0144 break; 0145 case Diff::SegmentType::OnlyOnRight: 0146 oldBlockType = CodeEditor::Removed; 0147 newBlockType = CodeEditor::Added; 0148 break; 0149 case Diff::SegmentType::DifferentOnBoth: 0150 oldBlockType = newBlockType = CodeEditor::Edited; 0151 break; 0152 } 0153 0154 if (mSameSize) { 0155 const int size = qMax(s->oldText.size(), s->newText.size()); 0156 leftCodeEditor->append(s->oldText, oldBlockType, s, size); 0157 rightCodeEditor->append(s->newText, newBlockType, s, size); 0158 0159 mPreviewEditorLeft->append(s->oldText, oldBlockType, s, size); 0160 mPreviewEditorRight->append(s->newText, newBlockType, s, size); 0161 } else { 0162 leftCodeEditor->append(s->oldText, oldBlockType, s); 0163 rightCodeEditor->append(s->newText, newBlockType, s); 0164 0165 mPreviewEditorLeft->append(s->oldText, oldBlockType, s); 0166 mPreviewEditorRight->append(s->newText, newBlockType, s); 0167 } 0168 } 0169 0170 scrollToTop(); 0171 } 0172 0173 void DiffWidget::showHiddenChars(bool show) 0174 { 0175 if (show) { 0176 auto n = mDefaultOption; 0177 n.setFlags(QTextOption::ShowTabsAndSpaces | QTextOption::ShowDocumentTerminator); 0178 leftCodeEditor->document()->setDefaultTextOption(n); 0179 rightCodeEditor->document()->setDefaultTextOption(n); 0180 } else { 0181 leftCodeEditor->document()->setDefaultTextOption(mDefaultOption); 0182 rightCodeEditor->document()->setDefaultTextOption(mDefaultOption); 0183 } 0184 leftCodeEditor->setWordWrapMode(QTextOption::NoWrap); 0185 rightCodeEditor->setWordWrapMode(QTextOption::NoWrap); 0186 } 0187 0188 void DiffWidget::showFilesInfo(bool show) 0189 { 0190 leftCodeEditor->setShowTitleBar(show); 0191 rightCodeEditor->setShowTitleBar(show); 0192 segmentConnector->setTopMargin(show ? leftCodeEditor->titlebarHeight() + 2 : 0); 0193 } 0194 0195 void DiffWidget::showSameSize(bool show) 0196 { 0197 mSameSize = show; 0198 segmentConnector->setSameSize(show); 0199 compare(); 0200 } 0201 0202 void DiffWidget::slotSegmentsScrollbarHover(int y, double pos) 0203 { 0204 mPreviewWidget->show(); 0205 mPreviewWidget->move(mPreviewMargin, qMin(y, widgetSegmentsScrollBar->height() - mPreviewWidgetHeight)); 0206 0207 mPreviewEditorLeft->verticalScrollBar()->setValue(pos * static_cast<double>(mPreviewEditorLeft->verticalScrollBar()->maximum())); 0208 mPreviewEditorRight->verticalScrollBar()->setValue(pos * mPreviewEditorRight->verticalScrollBar()->maximum()); 0209 } 0210 0211 void DiffWidget::slotSplitterSplitterMoved(int, int) 0212 { 0213 recalculateInfoPaneSize(); 0214 } 0215 0216 CodeEditor *DiffWidget::oldCodeEditor() const 0217 { 0218 return leftCodeEditor; 0219 } 0220 0221 CodeEditor *DiffWidget::newCodeEditor() const 0222 { 0223 return rightCodeEditor; 0224 } 0225 0226 void DiffWidget::oldCodeEditor_scroll(int value) 0227 { 0228 static bool b{false}; 0229 if (b) 0230 return; 0231 b = true; 0232 rightCodeEditor->verticalScrollBar()->setValue( 0233 (int)(((float)value / (float)rightCodeEditor->verticalScrollBar()->maximum()) * (float)rightCodeEditor->verticalScrollBar()->maximum())); 0234 b = false; 0235 segmentConnector->update(); 0236 widgetSegmentsScrollBar->update(); 0237 } 0238 0239 void DiffWidget::newCodeEditor_scroll(int value) 0240 { 0241 static bool b{false}; 0242 if (b) 0243 return; 0244 b = true; 0245 leftCodeEditor->verticalScrollBar()->setValue( 0246 (int)(((float)value / (float)leftCodeEditor->verticalScrollBar()->maximum()) * (float)leftCodeEditor->verticalScrollBar()->maximum())); 0247 b = false; 0248 segmentConnector->update(); 0249 widgetSegmentsScrollBar->update(); 0250 } 0251 0252 void DiffWidget::oldCodeEditor_blockSelected() 0253 { 0254 // auto b = _oldCodeEditor->textCursor().block().blockNumber(); 0255 // auto b = _oldCodeEditor->currentSegment(); 0256 // if (b) { 0257 // _segmentConnector->setCurrentSegment(b); 0258 // _newCodeEditor->highlightSegment(b); 0259 // } 0260 } 0261 0262 void DiffWidget::newCodeEditor_blockSelected() 0263 { 0264 // auto b = _newCodeEditor->currentSegment(); 0265 // if (b) { 0266 // _segmentConnector->setCurrentSegment(b); 0267 // _oldCodeEditor->highlightSegment(b); 0268 // } 0269 } 0270 0271 void DiffWidget::recalculateInfoPaneSize() 0272 { 0273 // leftInfoContainer->setMinimumWidth(leftCodeEditor->width()); 0274 // rightInfoContainer->setMinimumWidth(rightCodeEditor->width()); 0275 0276 // leftInfoContainer->setVisible(leftCodeEditor->width()); 0277 // rightInfoContainer->setVisible(rightCodeEditor->width()); 0278 0279 // label->setMinimumWidth(leftCodeEditor->width()); 0280 // label_2->setMinimumWidth(rightCodeEditor->width()); 0281 } 0282 0283 void DiffWidget::resizeEvent(QResizeEvent *event) 0284 { 0285 QWidget::resizeEvent(event); 0286 recalculateInfoPaneSize(); 0287 mPreviewWidget->resize(splitter->width(), mPreviewWidgetHeight); 0288 mPreviewMargin = splitter->mapToParent(QPoint{0, 0}).x(); 0289 } 0290 0291 void DiffWidget::showEvent(QShowEvent *event) 0292 { 0293 Q_UNUSED(event) 0294 recalculateInfoPaneSize(); 0295 } 0296 0297 bool DiffWidget::sameSize() const 0298 { 0299 return mSameSize; 0300 } 0301 0302 void DiffWidget::setSameSize(bool newSameSize) 0303 { 0304 if (mSameSize == newSameSize) 0305 return; 0306 mSameSize = newSameSize; 0307 Q_EMIT sameSizeChanged(); 0308 } 0309 0310 void DiffWidget::scrollToTop() 0311 { 0312 leftCodeEditor->setTextCursor(QTextCursor(leftCodeEditor->document()->findBlockByNumber(0))); 0313 rightCodeEditor->setTextCursor(QTextCursor(rightCodeEditor->document()->findBlockByNumber(0))); 0314 // leftCodeEditor->verticalScrollBar()->setValue(0); 0315 // rightCodeEditor->verticalScrollBar()->setValue(0); 0316 segmentConnector->update(); 0317 } 0318 0319 #include "moc_diffwidget.cpp"