File indexing completed on 2024-05-05 05:45:47

0001 /*
0002     SPDX-FileCopyrightText: 2004-2005 Jeff Snyder <jeff-webcvsspam@caffeinated.me.uk>
0003     SPDX-FileCopyrightText: 2007-2011 Kevin Kofler <kevin.kofler@chello.at>
0004 
0005     SPDX-License-Identifier: GPL-2.0-or-later
0006 */
0007 
0008 // associated header
0009 #include "komparesplitter.h"
0010 
0011 // qt
0012 #include <QStyle>
0013 #include <QString>
0014 #include <QTimer>
0015 #include <QScrollBar>
0016 #include <QMap>
0017 #include <QSplitter>
0018 #include <QApplication>
0019 #include <QPainter>
0020 #include <QPixmap>
0021 #include <QKeyEvent>
0022 #include <QGridLayout>
0023 #include <QResizeEvent>
0024 #include <QChildEvent>
0025 #include <QEvent>
0026 #include <QWheelEvent>
0027 
0028 // kde
0029 
0030 // kompare
0031 #include "komparelistview.h"
0032 #include "viewsettings.h"
0033 #include "kompareconnectwidget.h"
0034 #include <KompareDiff2/DiffModel>
0035 #include <KompareDiff2/Difference>
0036 
0037 using namespace KompareDiff2;
0038 
0039 KompareSplitter::KompareSplitter(ViewSettings* settings, QWidget* parent) :
0040     QSplitter(Qt::Horizontal, parent),
0041     m_settings(settings)
0042 {
0043     QFrame* scrollFrame = static_cast<QFrame*>(parent);
0044 
0045     // Set up the scrollFrame
0046     scrollFrame->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
0047     scrollFrame->setLineWidth(scrollFrame->style()->pixelMetric(QStyle::PM_DefaultFrameWidth));
0048     QGridLayout* pairlayout = new QGridLayout(scrollFrame);
0049     pairlayout->setSpacing(0);
0050     pairlayout->setContentsMargins(0, 0, 0, 0);
0051     m_vScroll = new QScrollBar(Qt::Vertical, scrollFrame);
0052     pairlayout->addWidget(m_vScroll, 0, 1);
0053     m_hScroll = new QScrollBar(Qt::Horizontal, scrollFrame);
0054     pairlayout->addWidget(m_hScroll, 1, 0);
0055 
0056     new KompareListViewFrame(true, m_settings, this, "source");
0057     new KompareListViewFrame(false, m_settings, this, "destination");
0058     pairlayout->addWidget(this, 0, 0);
0059 
0060     // set up our looks
0061     setLineWidth(style()->pixelMetric(QStyle::PM_DefaultFrameWidth));
0062 
0063     setHandleWidth(50);
0064     setChildrenCollapsible(false);
0065     setFrameStyle(QFrame::NoFrame);
0066     setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
0067     setOpaqueResize(true);
0068     setFocusPolicy(Qt::WheelFocus);
0069 
0070     connect(this, &KompareSplitter::configChanged, this, &KompareSplitter::slotConfigChanged);
0071     connect(this, &KompareSplitter::configChanged, this, &KompareSplitter::slotDelayedRepaintHandles);
0072     connect(this, &KompareSplitter::configChanged, this, &KompareSplitter::slotDelayedUpdateScrollBars);
0073 
0074     // scrolling
0075     connect(m_vScroll, &QScrollBar::valueChanged, this, &KompareSplitter::slotScrollToId);
0076     connect(m_vScroll, &QScrollBar::sliderMoved,  this, &KompareSplitter::slotScrollToId);
0077     connect(m_hScroll, &QScrollBar::valueChanged, this, &KompareSplitter::setXOffset);
0078     connect(m_hScroll, &QScrollBar::sliderMoved,  this, &KompareSplitter::setXOffset);
0079 
0080     m_scrollTimer = new QTimer(this);
0081     m_restartTimer = false;
0082     connect(m_scrollTimer, &QTimer::timeout, this, &KompareSplitter::timerTimeout);
0083 
0084     // we need to receive childEvents now so that d->list is ready for when
0085     // slotSetSelection(...) arrives
0086     qApp->sendPostedEvents(this, QEvent::ChildAdded);
0087 
0088     // init stuff
0089     slotUpdateScrollBars();
0090 }
0091 
0092 KompareSplitter::~KompareSplitter()
0093 {
0094 }
0095 
0096 QSplitterHandle* KompareSplitter::createHandle()
0097 {
0098     return new KompareConnectWidgetFrame(m_settings, this);
0099 }
0100 
0101 void KompareSplitter::slotDelayedRepaintHandles()
0102 {
0103     QTimer::singleShot(0, this, &KompareSplitter::slotRepaintHandles);
0104 }
0105 
0106 void KompareSplitter::slotRepaintHandles()
0107 {
0108     const int end = count();
0109     for (int i = 1; i < end; ++i)
0110         handle(i)->update();
0111 }
0112 
0113 void KompareSplitter::timerTimeout()
0114 {
0115     if (m_restartTimer)
0116         m_restartTimer = false;
0117     else
0118         m_scrollTimer->stop();
0119 
0120     slotDelayedRepaintHandles();
0121 
0122     Q_EMIT scrollViewsToId(m_scrollTo);
0123     slotRepaintHandles();
0124     m_vScroll->setValue(m_scrollTo);
0125 }
0126 
0127 void KompareSplitter::slotScrollToId(int id)
0128 {
0129     m_scrollTo = id;
0130 
0131     if (m_restartTimer)
0132         return;
0133 
0134     if (m_scrollTimer->isActive())
0135     {
0136         m_restartTimer = true;
0137     }
0138     else
0139     {
0140         Q_EMIT scrollViewsToId(id);
0141         slotRepaintHandles();
0142         m_vScroll->setValue(id);
0143         m_scrollTimer->start(30);
0144     }
0145 }
0146 
0147 void KompareSplitter::slotDelayedUpdateScrollBars()
0148 {
0149     QTimer::singleShot(0, this, &KompareSplitter::slotUpdateScrollBars);
0150 }
0151 
0152 void KompareSplitter::slotUpdateScrollBars()
0153 {
0154     const int end = count();
0155     for (int i = 0; i < end; ++i) {
0156         KompareListView* lv = listView(i);
0157         int minHScroll = minHScrollId();
0158         if (lv->contentsX() < minHScroll) {
0159             lv->setXOffset(minHScroll);
0160         }
0161     }
0162 
0163     int m_scrollDistance = m_settings->m_scrollNoOfLines * lineHeight();
0164     int m_pageSize = pageSize();
0165 
0166     if (needVScrollBar())
0167     {
0168         m_vScroll->show();
0169 
0170         m_vScroll->blockSignals(true);
0171         m_vScroll->setRange(minVScrollId(),
0172                             maxVScrollId());
0173         m_vScroll->setValue(scrollId());
0174         m_vScroll->setSingleStep(m_scrollDistance);
0175         m_vScroll->setPageStep(m_pageSize);
0176         m_vScroll->blockSignals(false);
0177     }
0178     else
0179     {
0180         m_vScroll->hide();
0181     }
0182 
0183     if (needHScrollBar())
0184     {
0185         m_hScroll->show();
0186         m_hScroll->blockSignals(true);
0187         m_hScroll->setRange(minHScrollId(), maxHScrollId());
0188         m_hScroll->setValue(maxContentsX());
0189         m_hScroll->setSingleStep(10);
0190         m_hScroll->setPageStep(minVisibleWidth() - 10);
0191         m_hScroll->blockSignals(false);
0192     }
0193     else
0194     {
0195         m_hScroll->hide();
0196     }
0197 }
0198 
0199 void KompareSplitter::slotDelayedUpdateVScrollValue()
0200 {
0201     QTimer::singleShot(0, this, &KompareSplitter::slotUpdateVScrollValue);
0202 }
0203 
0204 void KompareSplitter::slotUpdateVScrollValue()
0205 {
0206     m_vScroll->setValue(scrollId());
0207 }
0208 
0209 void KompareSplitter::keyPressEvent(QKeyEvent* e)
0210 {
0211     //keyboard scrolling
0212     switch (e->key()) {
0213     case Qt::Key_Right:
0214     case Qt::Key_L:
0215         m_hScroll->triggerAction(QAbstractSlider::SliderSingleStepAdd);
0216         break;
0217     case Qt::Key_Left:
0218     case Qt::Key_H:
0219         m_hScroll->triggerAction(QAbstractSlider::SliderSingleStepSub);
0220         break;
0221     case Qt::Key_Up:
0222     case Qt::Key_K:
0223         m_vScroll->triggerAction(QAbstractSlider::SliderSingleStepSub);
0224         break;
0225     case Qt::Key_Down:
0226     case Qt::Key_J:
0227         m_vScroll->triggerAction(QAbstractSlider::SliderSingleStepAdd);
0228         break;
0229     case Qt::Key_PageDown:
0230         m_vScroll->triggerAction(QAbstractSlider::SliderPageStepAdd);
0231         break;
0232     case Qt::Key_PageUp:
0233         m_vScroll->triggerAction(QAbstractSlider::SliderPageStepSub);
0234         break;
0235     }
0236     e->accept();
0237     slotRepaintHandles();
0238 }
0239 
0240 void KompareSplitter::wheelEvent(QWheelEvent* e)
0241 {
0242     if (e->angleDelta().y() != 0)
0243     {
0244         if (e->modifiers() & Qt::ControlModifier) {
0245             if (e->angleDelta().y() < 0)   // scroll down one page
0246                 m_vScroll->triggerAction(QAbstractSlider::SliderPageStepAdd);
0247             else // scroll up one page
0248                 m_vScroll->triggerAction(QAbstractSlider::SliderPageStepSub);
0249         } else {
0250             if (e->angleDelta().y() < 0)   // scroll down
0251                 m_vScroll->triggerAction(QAbstractSlider::SliderSingleStepAdd);
0252             else // scroll up
0253                 m_vScroll->triggerAction(QAbstractSlider::SliderSingleStepSub);
0254         }
0255     }
0256     else
0257     {
0258         if (e->modifiers() & Qt::ControlModifier) {
0259             if (e->angleDelta().y() < 0)   // scroll right one page
0260                 m_hScroll->triggerAction(QAbstractSlider::SliderPageStepAdd);
0261             else // scroll left one page
0262                 m_hScroll->triggerAction(QAbstractSlider::SliderPageStepSub);
0263         } else {
0264             if (e->angleDelta().y() < 0)   // scroll to the right
0265                 m_hScroll->triggerAction(QAbstractSlider::SliderSingleStepAdd);
0266             else // scroll to the left
0267                 m_hScroll->triggerAction(QAbstractSlider::SliderSingleStepSub);
0268         }
0269     }
0270     e->accept();
0271     slotDelayedRepaintHandles();
0272 }
0273 
0274 /* FIXME: this should return/the scrollId() from the listview containing the
0275  * /base/ of the diff. but there's bigger issues with that atm.
0276  */
0277 
0278 int KompareSplitter::scrollId()
0279 {
0280     if (widget(0))
0281         return listView(0)->scrollId();
0282     return minVScrollId();
0283 }
0284 
0285 int KompareSplitter::lineHeight()
0286 {
0287     if (widget(0))
0288         return listView(0)->fontMetrics().height();
0289     return 1;
0290 }
0291 
0292 int KompareSplitter::pageSize()
0293 {
0294     if (widget(0)) {
0295         KompareListView* view = listView(0);
0296         return view->visibleHeight() - view->style()->pixelMetric(QStyle::PM_ScrollBarExtent);
0297     }
0298     return 1;
0299 }
0300 
0301 bool KompareSplitter::needVScrollBar()
0302 {
0303     int pagesize = pageSize();
0304     const int end = count();
0305     for (int i = 0; i < end; ++i) {
0306         KompareListView* view = listView(i);
0307         if (view ->contentsHeight() > pagesize)
0308             return true;
0309     }
0310     return false;
0311 }
0312 
0313 int KompareSplitter::minVScrollId()
0314 {
0315 
0316     int min = -1;
0317     int mSId;
0318     const int end = count();
0319     for (int i = 0; i < end; ++i) {
0320         mSId = listView(i)->minScrollId();
0321         if (mSId < min || min == -1)
0322             min = mSId;
0323     }
0324     return (min == -1) ? 0 : min;
0325 }
0326 
0327 int KompareSplitter::maxVScrollId()
0328 {
0329     int max = 0;
0330     int mSId;
0331     const int end = count();
0332     for (int i = 0; i < end; ++i) {
0333         mSId = listView(i)->maxScrollId();
0334         if (mSId > max)
0335             max = mSId;
0336     }
0337     return max;
0338 }
0339 
0340 bool KompareSplitter::needHScrollBar()
0341 {
0342     const int end = count();
0343     for (int i = 0; i < end; ++i) {
0344         KompareListView* view = listView(i);
0345         if (view->contentsWidth() > view->visibleWidth())
0346             return true;
0347     }
0348     return false;
0349 }
0350 
0351 int KompareSplitter::minHScrollId()
0352 {
0353     // hardcode an offset to hide the tree controls
0354     return 6;
0355 }
0356 
0357 int KompareSplitter::maxHScrollId()
0358 {
0359     int max = 0;
0360     int mHSId;
0361     const int end = count();
0362     for (int i = 0; i < end; ++i) {
0363         KompareListView* view = listView(i);
0364         mHSId = view->contentsWidth() - view->visibleWidth();
0365         if (mHSId > max)
0366             max = mHSId;
0367     }
0368     return max;
0369 }
0370 
0371 int KompareSplitter::maxContentsX()
0372 {
0373     int max = 0;
0374     int mCX;
0375     const int end = count();
0376     for (int i = 0; i < end; ++i) {
0377         mCX = listView(i)->contentsX();
0378         if (mCX > max)
0379             max = mCX;
0380     }
0381     return max;
0382 }
0383 
0384 int KompareSplitter::minVisibleWidth()
0385 {
0386     // Why the hell do we want to know this?
0387     // ah yes, it is because we use it to set the "page size" for horiz. scrolling.
0388     // despite the fact that *none* has a pgright and pgleft key :P
0389     // But we do have mousewheels with horizontal scrolling functionality,
0390     // pressing shift and scrolling then goes left and right one page at the time
0391     int min = -1;
0392     int vW;
0393     const int end = count();
0394     for (int i = 0; i < end; ++i) {
0395         vW = listView(i)->visibleWidth();
0396         if (vW < min || min == -1)
0397             min = vW;
0398     }
0399     return (min == -1) ? 0 : min;
0400 }
0401 
0402 KompareListView* KompareSplitter::listView(int index)
0403 {
0404     return static_cast<KompareListViewFrame*>(widget(index))->view();
0405 }
0406 
0407 KompareConnectWidget* KompareSplitter::connectWidget(int index)
0408 {
0409     return static_cast<KompareConnectWidgetFrame*>(handle(index))->wid();
0410 }
0411 
0412 void KompareSplitter::slotSetSelection(const DiffModel* model, const Difference* diff)
0413 {
0414     const int end = count();
0415     for (int i = 0; i < end; ++i) {
0416         connectWidget(i)->slotSetSelection(model, diff);
0417         listView(i)->slotSetSelection(model, diff);
0418         static_cast<KompareListViewFrame*>(widget(i))->slotSetModel(model);
0419     }
0420 
0421     slotDelayedRepaintHandles();
0422     slotDelayedUpdateScrollBars();
0423 }
0424 
0425 void KompareSplitter::slotSetSelection(const Difference* diff)
0426 {
0427     const int end = count();
0428     for (int i = 0; i < end; ++i) {
0429         connectWidget(i)->slotSetSelection(diff);
0430         listView(i)->slotSetSelection(diff);
0431     }
0432 
0433     slotDelayedRepaintHandles();
0434     slotDelayedUpdateScrollBars();
0435 }
0436 
0437 void KompareSplitter::slotApplyDifference(bool apply)
0438 {
0439     const int end = count();
0440     for (int i = 0; i < end; ++i)
0441         listView(i)->slotApplyDifference(apply);
0442     slotDelayedRepaintHandles();
0443 }
0444 
0445 void KompareSplitter::slotApplyAllDifferences(bool apply)
0446 {
0447     const int end = count();
0448     for (int i = 0; i < end; ++i)
0449         listView(i)->slotApplyAllDifferences(apply);
0450     slotDelayedRepaintHandles();
0451     slotScrollToId(m_scrollTo);   // FIXME!
0452 }
0453 
0454 void KompareSplitter::slotApplyDifference(const Difference* diff, bool apply)
0455 {
0456     const int end = count();
0457     for (int i = 0; i < end; ++i)
0458         listView(i)->slotApplyDifference(diff, apply);
0459     slotDelayedRepaintHandles();
0460 }
0461 
0462 void KompareSplitter::slotDifferenceClicked(const Difference* diff)
0463 {
0464     const int end = count();
0465     for (int i = 0; i < end; ++i)
0466         listView(i)->setSelectedDifference(diff, false);
0467     Q_EMIT selectionChanged(diff);
0468 }
0469 
0470 void KompareSplitter::slotConfigChanged()
0471 {
0472     const int end = count();
0473     for (int i = 0; i < end; ++i) {
0474         KompareListView* view = listView(i);
0475         view->setFont(m_settings->m_font);
0476         view->update();
0477     }
0478 }
0479 
0480 #include "moc_komparesplitter.cpp"