/*************************************************************************** mergeresultwindow.cpp - description ------------------- begin : Sun Apr 14 2002 copyright : (C) 2002-2007 by Joachim Eibl email : joachim.eibl at gmx.de ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "mergeresultwindow.h" #include "optiondialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include int g_bAutoSolve = true; #undef leftInfoWidth #define leftInfoWidth 3 MergeResultWindow::MergeResultWindow( TQWidget* pParent, OptionDialog* pOptionDialog, TQStatusBar* pStatusBar ) : TQWidget( pParent, 0, WRepaintNoErase ) { setFocusPolicy( TQWidget::ClickFocus ); m_firstLine = 0; m_firstColumn = 0; m_nofColumns = 0; m_nofLines = 0; m_totalSize = 0; m_bMyUpdate = false; m_bInsertMode = true; m_scrollDeltaX = 0; m_scrollDeltaY = 0; m_bModified = false; m_eOverviewMode=Overview::eOMNormal; m_pldA = 0; m_pldB = 0; m_pldC = 0; m_sizeA = 0; m_sizeB = 0; m_sizeC = 0; m_pDiff3LineList = 0; m_pTotalDiffStatus = 0; m_pStatusBar = pStatusBar; m_pOptionDialog = pOptionDialog; m_bPaintingAllowed = false; m_delayedDrawTimer = 0; m_cursorXPos=0; m_cursorOldXPos=0; m_cursorYPos=0; m_bCursorOn = true; m_bCursorUpdate = false; connect( &m_cursorTimer, TQT_SIGNAL(timeout()), this, TQT_SLOT( slotCursorUpdate() ) ); m_cursorTimer.start( 500 /*ms*/, true /*single shot*/ ); m_selection.reset(); setMinimumSize( TQSize(20,20) ); setFont( m_pOptionDialog->m_font ); } void MergeResultWindow::init( const LineData* pLineDataA, int sizeA, const LineData* pLineDataB, int sizeB, const LineData* pLineDataC, int sizeC, const Diff3LineList* pDiff3LineList, TotalDiffStatus* pTotalDiffStatus ) { m_firstLine = 0; m_firstColumn = 0; m_nofColumns = 0; m_nofLines = 0; m_bMyUpdate = false; m_bInsertMode = true; m_scrollDeltaX = 0; m_scrollDeltaY = 0; setModified( false ); m_pldA = pLineDataA; m_pldB = pLineDataB; m_pldC = pLineDataC; m_sizeA = sizeA; m_sizeB = sizeB; m_sizeC = sizeC; m_pDiff3LineList = pDiff3LineList; m_pTotalDiffStatus = pTotalDiffStatus; m_selection.reset(); m_cursorXPos=0; m_cursorOldXPos=0; m_cursorYPos=0; merge( g_bAutoSolve, -1 ); g_bAutoSolve = true; update(); updateSourceMask(); int wsc; int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); if (m_pStatusBar) m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") .arg(nofUnsolved).arg(wsc) ); } void MergeResultWindow::reset() { m_pDiff3LineList = 0; m_pTotalDiffStatus = 0; m_pldA = 0; m_pldB = 0; m_pldC = 0; } // Calculate the merge information for the given Diff3Line. // Results will be stored in mergeDetails, bConflict, bLineRemoved and src. void mergeOneLine( const Diff3Line& d, e_MergeDetails& mergeDetails, bool& bConflict, bool& bLineRemoved, int& src, bool bTwoInputs ) { mergeDetails = eDefault; bConflict = false; bLineRemoved = false; src = 0; if ( bTwoInputs ) // Only two input files { if ( d.lineA!=-1 && d.lineB!=-1 ) { if ( d.pFineAB == 0 ) { mergeDetails = eNoChange; src = A; } else { mergeDetails = eBChanged; bConflict = true; } } else { if ( d.lineA!=-1 && d.lineB==-1 ) { mergeDetails = eBDeleted; bConflict = true; } else if ( d.lineA==-1 && d.lineB!=-1 ) { mergeDetails = eBDeleted; bConflict = true; } } return; } // A is base. if ( d.lineA!=-1 && d.lineB!=-1 && d.lineC!=-1 ) { if ( d.pFineAB == 0 && d.pFineBC == 0 && d.pFineCA == 0) { mergeDetails = eNoChange; src = A; } else if( d.pFineAB == 0 && d.pFineBC != 0 && d.pFineCA != 0 ) { mergeDetails = eCChanged; src = C; } else if( d.pFineAB != 0 && d.pFineBC != 0 && d.pFineCA == 0 ) { mergeDetails = eBChanged; src = B; } else if( d.pFineAB != 0 && d.pFineBC == 0 && d.pFineCA != 0 ) { mergeDetails = eBCChangedAndEqual; src = C; } else if( d.pFineAB != 0 && d.pFineBC != 0 && d.pFineCA != 0 ) { mergeDetails = eBCChanged; bConflict = true; } else assert(false); } else if ( d.lineA!=-1 && d.lineB!=-1 && d.lineC==-1 ) { if( d.pFineAB != 0 ) { mergeDetails = eBChanged_CDeleted; bConflict = true; } else { mergeDetails = eCDeleted; bLineRemoved = true; src = C; } } else if ( d.lineA!=-1 && d.lineB==-1 && d.lineC!=-1 ) { if( d.pFineCA != 0 ) { mergeDetails = eCChanged_BDeleted; bConflict = true; } else { mergeDetails = eBDeleted; bLineRemoved = true; src = B; } } else if ( d.lineA==-1 && d.lineB!=-1 && d.lineC!=-1 ) { if( d.pFineBC != 0 ) { mergeDetails = eBCAdded; bConflict = true; } else // B==C { mergeDetails = eBCAddedAndEqual; src = C; } } else if ( d.lineA==-1 && d.lineB==-1 && d.lineC!= -1 ) { mergeDetails = eCAdded; src = C; } else if ( d.lineA==-1 && d.lineB!=-1 && d.lineC== -1 ) { mergeDetails = eBAdded; src = B; } else if ( d.lineA!=-1 && d.lineB==-1 && d.lineC==-1 ) { mergeDetails = eBCDeleted; bLineRemoved = true; src = C; } else assert(false); } bool MergeResultWindow::sameKindCheck( const MergeLine& ml1, const MergeLine& ml2 ) { if ( ml1.bConflict && ml2.bConflict ) { // Both lines have conflicts: If one is only a white space conflict and // the other one is a real conflict, then this line returns false. return ml1.id3l->bAEqC == ml2.id3l->bAEqC && ml1.id3l->bAEqB == ml2.id3l->bAEqB; } else return ( !ml1.bConflict && !ml2.bConflict && ml1.bDelta && ml2.bDelta && ml1.srcSelect == ml2.srcSelect || !ml1.bDelta && !ml2.bDelta ); } void MergeResultWindow::merge(bool bAutoSolve, int defaultSelector, bool bConflictsOnly, bool bWhiteSpaceOnly ) { if ( !bConflictsOnly ) { if(m_bModified) { int result = KMessageBox::warningYesNo(this, i18n("The output has been modified.\n" "If you continue your changes will be lost."), i18n("Warning"), i18n("C&ontinue"), i18n("&Cancel")); if ( result==KMessageBox::No ) return; } m_mergeLineList.clear(); m_totalSize = 0; int lineIdx = 0; Diff3LineList::const_iterator it; for( it=m_pDiff3LineList->begin(); it!=m_pDiff3LineList->end(); ++it, ++lineIdx ) { const Diff3Line& d = *it; MergeLine ml; bool bLineRemoved; mergeOneLine( d, ml.mergeDetails, ml.bConflict, bLineRemoved, ml.srcSelect, m_pldC==0 ); // Automatic solving for only whitespace changes. if ( ml.bConflict && ( m_pldC==0 && (d.bAEqB || d.bWhiteLineA && d.bWhiteLineB) || m_pldC!=0 && (d.bAEqB && d.bAEqC || d.bWhiteLineA && d.bWhiteLineB && d.bWhiteLineC ) ) ) { ml.bWhiteSpaceConflict = true; } ml.d3lLineIdx = lineIdx; ml.bDelta = ml.srcSelect != A; ml.id3l = it; ml.srcRangeLength = 1; MergeLine* back = m_mergeLineList.empty() ? 0 : &m_mergeLineList.back(); bool bSame = back!=0 && sameKindCheck( ml, *back ); if( bSame ) { ++back->srcRangeLength; if ( back->bWhiteSpaceConflict && !ml.bWhiteSpaceConflict ) back->bWhiteSpaceConflict = false; } else { if (back!=0 && back->bWhiteSpaceConflict ) { if ( m_pldC==0 && m_pOptionDialog->m_whiteSpace2FileMergeDefault != 0 ) // Only two inputs { back->srcSelect = m_pOptionDialog->m_whiteSpace2FileMergeDefault; back->bConflict = false; } else if ( m_pldC!=0 && m_pOptionDialog->m_whiteSpace3FileMergeDefault != 0 ) { back->srcSelect = m_pOptionDialog->m_whiteSpace3FileMergeDefault; back->bConflict = false; } } ml.mergeEditLineList.setTotalSizePtr(&m_totalSize); m_mergeLineList.push_back( ml ); } if ( ! ml.bConflict ) { MergeLine& tmpBack = m_mergeLineList.back(); MergeEditLine mel(ml.id3l); mel.setSource( ml.srcSelect, bLineRemoved ); tmpBack.mergeEditLineList.push_back(mel); } else if ( back==0 || ! back->bConflict || !bSame ) { MergeLine& tmpBack = m_mergeLineList.back(); MergeEditLine mel(ml.id3l); mel.setConflict(); tmpBack.mergeEditLineList.push_back(mel); } } } if ( !bAutoSolve ) { // Change all auto selections MergeLineList::iterator mlIt; for( mlIt=m_mergeLineList.begin(); mlIt!=m_mergeLineList.end(); ++mlIt ) { MergeLine& ml = *mlIt; bool bConflict = ml.mergeEditLineList.empty() || ml.mergeEditLineList.begin()->isConflict(); if ( ml.bDelta && ( !bConflictsOnly || bConflict ) && (!bWhiteSpaceOnly || ml.bWhiteSpaceConflict )) { ml.mergeEditLineList.clear(); if ( defaultSelector==-1 && ml.bDelta ) { MergeEditLine mel(ml.id3l);; mel.setConflict(); ml.bConflict = true; ml.mergeEditLineList.push_back(mel); } else { Diff3LineList::const_iterator d3llit=ml.id3l; int j; for( j=0; jlineA : defaultSelector==2 ? d3llit->lineB : defaultSelector==3 ? d3llit->lineC : -1; if ( srcLine != -1 ) { ml.mergeEditLineList.push_back(mel); } ++d3llit; } if ( ml.mergeEditLineList.empty() ) // Make a line nevertheless { MergeEditLine mel(ml.id3l); mel.setRemoved( defaultSelector ); ml.mergeEditLineList.push_back(mel); } } } } } MergeLineList::iterator mlIt; for( mlIt=m_mergeLineList.begin(); mlIt!=m_mergeLineList.end(); ++mlIt ) { MergeLine& ml = *mlIt; // Remove all lines that are empty, because no src lines are there. int oldSrcLine = -1; int oldSrc = -1; MergeEditLineList::iterator melIt; for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ) { MergeEditLine& mel = *melIt; int melsrc = mel.src(); int srcLine = mel.isRemoved() ? -1 : melsrc==1 ? mel.id3l()->lineA : melsrc==2 ? mel.id3l()->lineB : melsrc==3 ? mel.id3l()->lineC : -1; // At least one line remains because oldSrc != melsrc for first line in list // Other empty lines will be removed if ( srcLine == -1 && oldSrcLine==-1 && oldSrc == melsrc ) melIt = ml.mergeEditLineList.erase( melIt ); else ++melIt; oldSrcLine = srcLine; oldSrc = melsrc; } } if ( bAutoSolve && !bConflictsOnly ) { if ( m_pOptionDialog->m_bRunHistoryAutoMergeOnMergeStart ) slotMergeHistory(); if ( m_pOptionDialog->m_bRunRegExpAutoMergeOnMergeStart ) slotRegExpAutoMerge(); if ( m_pldC != 0 && ! doRelevantChangesExist() ) emit noRelevantChangesDetected(); } int nrOfSolvedConflicts = 0; int nrOfUnsolvedConflicts = 0; int nrOfWhiteSpaceConflicts = 0; MergeLineList::iterator i; for ( i = m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) { if ( i->bConflict ) ++nrOfUnsolvedConflicts; else if ( i->bDelta ) ++nrOfSolvedConflicts; if ( i->bWhiteSpaceConflict ) ++nrOfWhiteSpaceConflicts; } m_pTotalDiffStatus->nofUnsolvedConflicts = nrOfUnsolvedConflicts; m_pTotalDiffStatus->nofSolvedConflicts = nrOfSolvedConflicts; m_pTotalDiffStatus->nofWhitespaceConflicts = nrOfWhiteSpaceConflicts; m_cursorXPos=0; m_cursorOldXPos=0; m_cursorYPos=0; //m_firstLine = 0; // Must not set line/column without scrolling there //m_firstColumn = 0; setModified(false); m_currentMergeLineIt = m_mergeLineList.begin(); slotGoTop(); updateAvailabilities(); update(); } void MergeResultWindow::setFirstLine(int firstLine) { m_firstLine = max2(0,firstLine); update(); } void MergeResultWindow::setFirstColumn(int firstCol) { m_firstColumn = max2(0,firstCol); update(); } int MergeResultWindow::getNofColumns() { return m_nofColumns; } int MergeResultWindow::getNofLines() { return m_totalSize; } int MergeResultWindow::getNofVisibleColumns() { TQFontMetrics fm = fontMetrics(); return width()/fm.width('W')-4; } int MergeResultWindow::getNofVisibleLines() { TQFontMetrics fm = fontMetrics(); return (height()-3)/fm.height()-2; } void MergeResultWindow::resizeEvent( TQResizeEvent* e ) { TQWidget::resizeEvent(e); emit resizeSignal(); } Overview::e_OverviewMode MergeResultWindow::getOverviewMode() { return m_eOverviewMode; } void MergeResultWindow::setOverviewMode( Overview::e_OverviewMode eOverviewMode ) { m_eOverviewMode = eOverviewMode; } // Check whether we should ignore current delta when moving to next/previous delta bool MergeResultWindow::checkOverviewIgnore(MergeLineList::iterator &i) { if (m_eOverviewMode == Overview::eOMNormal) return false; if (m_eOverviewMode == Overview::eOMAvsB) return i->mergeDetails == eCAdded || i->mergeDetails == eCDeleted || i->mergeDetails == eCChanged; if (m_eOverviewMode == Overview::eOMAvsC) return i->mergeDetails == eBAdded || i->mergeDetails == eBDeleted || i->mergeDetails == eBChanged; if (m_eOverviewMode == Overview::eOMBvsC) return i->mergeDetails == eBCAddedAndEqual || i->mergeDetails == eBCDeleted || i->mergeDetails == eBCChangedAndEqual; return false; } // Go to prev/next delta/conflict or first/last delta. void MergeResultWindow::go( e_Direction eDir, e_EndPoint eEndPoint ) { assert( eDir==eUp || eDir==eDown ); MergeLineList::iterator i = m_currentMergeLineIt; bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; if( eEndPoint==eEnd ) { if (eDir==eUp) i = m_mergeLineList.begin(); // first mergeline else i = --m_mergeLineList.end(); // last mergeline while ( isItAtEnd(eDir==eUp, i) && ! i->bDelta ) { if ( eDir==eUp ) ++i; // search downwards else --i; // search upwards } } else if ( eEndPoint == eDelta && isItAtEnd(eDir!=eUp, i) ) { do { if ( eDir==eUp ) --i; else ++i; } while ( isItAtEnd(eDir!=eUp, i) && ( i->bDelta == false || checkOverviewIgnore(i) || bSkipWhiteConflicts && i->bWhiteSpaceConflict ) ); } else if ( eEndPoint == eConflict && isItAtEnd(eDir!=eUp, i) ) { do { if ( eDir==eUp ) --i; else ++i; } while ( isItAtEnd(eDir!=eUp, i) && (i->bConflict == false || bSkipWhiteConflicts && i->bWhiteSpaceConflict ) ); } else if ( isItAtEnd(eDir!=eUp, i) && eEndPoint == eUnsolvedConflict ) { do { if ( eDir==eUp ) --i; else ++i; } while ( isItAtEnd(eDir!=eUp, i) && ! i->mergeEditLineList.begin()->isConflict() ); } if ( isVisible() ) setFocus(); setFastSelector( i ); } bool MergeResultWindow::isDeltaAboveCurrent() { bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; if (m_mergeLineList.empty()) return false; MergeLineList::iterator i = m_currentMergeLineIt; if (i == m_mergeLineList.begin()) return false; do { --i; if ( i->bDelta && !checkOverviewIgnore(i) && !( bSkipWhiteConflicts && i->bWhiteSpaceConflict ) ) return true; } while (i!=m_mergeLineList.begin()); return false; } bool MergeResultWindow::isDeltaBelowCurrent() { bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; if (m_mergeLineList.empty()) return false; MergeLineList::iterator i = m_currentMergeLineIt; if (i!=m_mergeLineList.end()) { ++i; for( ; i!=m_mergeLineList.end(); ++i ) { if ( i->bDelta && !checkOverviewIgnore(i) && !( bSkipWhiteConflicts && i->bWhiteSpaceConflict ) ) return true; } } return false; } bool MergeResultWindow::isConflictAboveCurrent() { if (m_mergeLineList.empty()) return false; MergeLineList::iterator i = m_currentMergeLineIt; if (i == m_mergeLineList.begin()) return false; bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; do { --i; if ( i->bConflict && !(bSkipWhiteConflicts && i->bWhiteSpaceConflict) ) return true; } while (i!=m_mergeLineList.begin()); return false; } bool MergeResultWindow::isConflictBelowCurrent() { MergeLineList::iterator i = m_currentMergeLineIt; if (m_mergeLineList.empty()) return false; bool bSkipWhiteConflicts = ! m_pOptionDialog->m_bShowWhiteSpace; if (i!=m_mergeLineList.end()) { ++i; for( ; i!=m_mergeLineList.end(); ++i ) { if ( i->bConflict && !(bSkipWhiteConflicts && i->bWhiteSpaceConflict) ) return true; } } return false; } bool MergeResultWindow::isUnsolvedConflictAtCurrent() { if (m_mergeLineList.empty()) return false; MergeLineList::iterator i = m_currentMergeLineIt; return i->mergeEditLineList.begin()->isConflict(); } bool MergeResultWindow::isUnsolvedConflictAboveCurrent() { if (m_mergeLineList.empty()) return false; MergeLineList::iterator i = m_currentMergeLineIt; if (i == m_mergeLineList.begin()) return false; do { --i; if ( i->mergeEditLineList.begin()->isConflict() ) return true; } while (i!=m_mergeLineList.begin()); return false; } bool MergeResultWindow::isUnsolvedConflictBelowCurrent() { MergeLineList::iterator i = m_currentMergeLineIt; if (m_mergeLineList.empty()) return false; if (i!=m_mergeLineList.end()) { ++i; for( ; i!=m_mergeLineList.end(); ++i ) { if ( i->mergeEditLineList.begin()->isConflict() ) return true; } } return false; } void MergeResultWindow::slotGoTop() { go( eUp, eEnd ); } void MergeResultWindow::slotGoCurrent() { setFastSelector( m_currentMergeLineIt ); } void MergeResultWindow::slotGoBottom() { go( eDown, eEnd ); } void MergeResultWindow::slotGoPrevDelta() { go( eUp, eDelta ); } void MergeResultWindow::slotGoNextDelta() { go( eDown, eDelta ); } void MergeResultWindow::slotGoPrevConflict() { go( eUp, eConflict ); } void MergeResultWindow::slotGoNextConflict() { go( eDown, eConflict ); } void MergeResultWindow::slotGoPrevUnsolvedConflict() { go( eUp, eUnsolvedConflict ); } void MergeResultWindow::slotGoNextUnsolvedConflict() { go( eDown, eUnsolvedConflict ); } /** The line is given as a index in the Diff3LineList. The function calculates the corresponding iterator. */ void MergeResultWindow::slotSetFastSelectorLine( int line ) { MergeLineList::iterator i; for ( i = m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) { if ( line>=i->d3lLineIdx && line < i->d3lLineIdx + i->srcRangeLength ) { //if ( i->bDelta ) { setFastSelector( i ); } break; } } } int MergeResultWindow::getNrOfUnsolvedConflicts( int* pNrOfWhiteSpaceConflicts ) { int nrOfUnsolvedConflicts = 0; if (pNrOfWhiteSpaceConflicts!=0) *pNrOfWhiteSpaceConflicts = 0; MergeLineList::iterator mlIt = m_mergeLineList.begin(); for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) { MergeLine& ml = *mlIt; MergeEditLineList::iterator melIt = ml.mergeEditLineList.begin(); if ( melIt->isConflict() ) { ++nrOfUnsolvedConflicts; if ( ml.bWhiteSpaceConflict && pNrOfWhiteSpaceConflicts!=0 ) ++ *pNrOfWhiteSpaceConflicts; } } return nrOfUnsolvedConflicts; } void MergeResultWindow::showNrOfConflicts() { int nrOfConflicts = 0; MergeLineList::iterator i; for ( i = m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) { if ( i->bConflict || i->bDelta ) ++nrOfConflicts; } TQString totalInfo; if ( m_pTotalDiffStatus->bBinaryAEqB && m_pTotalDiffStatus->bBinaryAEqC ) totalInfo += i18n("All input files are binary equal."); else if ( m_pTotalDiffStatus->bTextAEqB && m_pTotalDiffStatus->bTextAEqC ) totalInfo += i18n("All input files contain the same text."); else { if ( m_pTotalDiffStatus->bBinaryAEqB ) totalInfo += i18n("Files %1 and %2 are binary equal.\n").arg("A").arg("B"); else if ( m_pTotalDiffStatus->bTextAEqB ) totalInfo += i18n("Files %1 and %2 have equal text.\n").arg("A").arg("B"); if ( m_pTotalDiffStatus->bBinaryAEqC ) totalInfo += i18n("Files %1 and %2 are binary equal.\n").arg("A").arg("C"); else if ( m_pTotalDiffStatus->bTextAEqC ) totalInfo += i18n("Files %1 and %2 have equal text.\n").arg("A").arg("C"); if ( m_pTotalDiffStatus->bBinaryBEqC ) totalInfo += i18n("Files %1 and %2 are binary equal.\n").arg("B").arg("C"); else if ( m_pTotalDiffStatus->bTextBEqC ) totalInfo += i18n("Files %1 and %2 have equal text.\n").arg("B").arg("C"); } int nrOfUnsolvedConflicts = getNrOfUnsolvedConflicts(); KMessageBox::information( this, i18n("Total number of conflicts: ") + TQString::number(nrOfConflicts) + i18n("\nNr of automatically solved conflicts: ") + TQString::number(nrOfConflicts-nrOfUnsolvedConflicts) + i18n("\nNr of unsolved conflicts: ") + TQString::number(nrOfUnsolvedConflicts) + "\n"+totalInfo, i18n("Conflicts") ); } void MergeResultWindow::setFastSelector(MergeLineList::iterator i) { if ( i==m_mergeLineList.end() ) return; m_currentMergeLineIt = i; emit setFastSelectorRange( i->d3lLineIdx, i->srcRangeLength ); int line1 = 0; MergeLineList::iterator mlIt = m_mergeLineList.begin(); for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) { if(mlIt==m_currentMergeLineIt) break; line1 += mlIt->mergeEditLineList.size(); } int nofLines = m_currentMergeLineIt->mergeEditLineList.size(); int newFirstLine = getBestFirstLine( line1, nofLines, m_firstLine, getNofVisibleLines() ); if ( newFirstLine != m_firstLine ) { scroll( 0, newFirstLine - m_firstLine ); } if ( m_selection.isEmpty() ) { m_cursorXPos = 0; m_cursorOldXPos = 0; m_cursorYPos = line1; } update(); updateSourceMask(); emit updateAvailabilities(); } void MergeResultWindow::choose( int selector ) { if ( m_currentMergeLineIt==m_mergeLineList.end() ) return; setModified(); // First find range for which this change works. MergeLine& ml = *m_currentMergeLineIt; MergeEditLineList::iterator melIt; // Now check if selector is active for this range already. bool bActive = false; // Remove unneeded lines in the range. for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ) { MergeEditLine& mel = *melIt; if ( mel.src()==selector ) bActive = true; if ( mel.src()==selector || !mel.isEditableText() || mel.isModified() ) melIt = ml.mergeEditLineList.erase( melIt ); else ++melIt; } if ( !bActive ) // Selected source wasn't active. { // Append the lines from selected source here at rangeEnd. Diff3LineList::const_iterator d3llit=ml.id3l; int j; for( j=0; jlineA : mel.src()==2 ? mel.id3l()->lineB : mel.src()==3 ? mel.id3l()->lineC : -1; if ( srcLine == -1 ) melIt = ml.mergeEditLineList.erase( melIt ); else ++melIt; } } if ( ml.mergeEditLineList.empty() ) { // Insert a dummy line: MergeEditLine mel(ml.id3l); if ( bActive ) mel.setConflict(); // All src entries deleted => conflict else mel.setRemoved(selector); // No lines in corresponding src found. ml.mergeEditLineList.push_back(mel); } if ( m_cursorYPos >= m_totalSize ) { m_cursorYPos = m_totalSize-1; m_cursorXPos = 0; } update(); updateSourceMask(); emit updateAvailabilities(); int wsc; int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") .arg(nofUnsolved).arg(wsc) ); } // bConflictsOnly: automatically choose for conflicts only (true) or for everywhere (false) void MergeResultWindow::chooseGlobal(int selector, bool bConflictsOnly, bool bWhiteSpaceOnly ) { resetSelection(); merge( false, selector, bConflictsOnly, bWhiteSpaceOnly ); setModified( true ); update(); int wsc; int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") .arg(nofUnsolved).arg(wsc) ); } void MergeResultWindow::slotAutoSolve() { resetSelection(); merge( true, -1 ); setModified( true ); update(); int wsc; int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") .arg(nofUnsolved).arg(wsc) ); } void MergeResultWindow::slotUnsolve() { resetSelection(); merge( false, -1 ); setModified( true ); update(); int wsc; int nofUnsolved = getNrOfUnsolvedConflicts(&wsc); m_pStatusBar->message( i18n("Number of remaining unsolved conflicts: %1 (of which %2 are whitespace)") .arg(nofUnsolved).arg(wsc) ); } static TQString calcHistoryLead(const TQString& s ) { // Return the start of the line until the first white char after the first non white char. unsigned int i; for( i=0; ibegin(), idxBegin=0; iBegin!=pD3LList->end(); ++iBegin, ++idxBegin ) { if ( historyStart.exactMatch( iBegin->getString(A) ) && historyStart.exactMatch( iBegin->getString(B) ) && ( !bThreeFiles || historyStart.exactMatch( iBegin->getString(C) ) ) ) { historyLead = calcHistoryLead( iBegin->getString(A) ); break; } } // Search for end of history for( iEnd = iBegin, idxEnd = idxBegin; iEnd!=pD3LList->end(); ++iEnd, ++idxEnd ) { TQString sA = iEnd->getString(A); TQString sB = iEnd->getString(B); TQString sC = iEnd->getString(C); if ( ! ((sA.isNull() || historyLead == calcHistoryLead(sA) ) && (sB.isNull() || historyLead == calcHistoryLead(sB) ) && (!bThreeFiles || sC.isNull() || historyLead == calcHistoryLead(sC) ) )) { break; // End of the history } } } bool findParenthesesGroups( const TQString& s, TQStringList& sl ) { sl.clear(); int i=0; std::list startPosStack; int length = s.length(); for( i=0; i(int)parenthesesGroupList.size() ) continue; TQString s = matchedRegExpr.cap( groupIdx ); if ( groupIdx == 0 ) { key += s + " "; continue; } TQString groupRegExp = parenthesesGroupList[groupIdx-1]; if( groupRegExp.find('|')<0 || groupRegExp.find('(')>=0 ) { bool bOk = false; int i = s.toInt( &bOk ); if ( bOk && i>=0 && i<10000 ) s.sprintf("%04d", i); // This should help for correct sorting of numbers. key += s + " "; } else { // Assume that the groupRegExp consists of something like "Jan|Feb|Mar|Apr" // s is the string that managed to match. // Now we want to know at which position it occurred. e.g. Jan=0, Feb=1, Mar=2, etc. TQStringList sl = TQStringList::split( '|', groupRegExp ); int idx = sl.findIndex( s ); if (idx<0) { // Didn't match } else { TQString sIdx; sIdx.sprintf("%02d", idx+1 ); // Up to 99 words in the groupRegExp (more than 12 aren't expected) key += sIdx + " "; } } } return key; } void MergeResultWindow::collectHistoryInformation( int src, Diff3LineList::const_iterator iHistoryBegin, Diff3LineList::const_iterator iHistoryEnd, HistoryMap& historyMap, std::list< HistoryMap::iterator >& hitList // list of iterators ) { std::list< HistoryMap::iterator >::iterator itHitListFront = hitList.begin(); Diff3LineList::const_iterator id3l = iHistoryBegin; TQString historyLead; { const LineData* pld = id3l->getLineData(src); TQString s( pld->pLine, pld->size ); historyLead = calcHistoryLead(s); } TQRegExp historyStart = m_pOptionDialog->m_historyStartRegExp; ++id3l; // Skip line with "$Log ... $" TQRegExp newHistoryEntry = m_pOptionDialog->m_historyEntryStartRegExp; TQStringList parenthesesGroups; findParenthesesGroups( m_pOptionDialog->m_historyEntryStartRegExp, parenthesesGroups ); TQString key; MergeEditLineList melList; bool bPrevLineIsEmpty = true; bool bUseRegExp = !m_pOptionDialog->m_historyEntryStartRegExp.isEmpty(); for(; id3l != iHistoryEnd; ++id3l ) { const LineData* pld = id3l->getLineData(src); if ( !pld ) continue; TQString s( pld->pLine, pld->size ); if (historyLead.isNull()) historyLead = calcHistoryLead(s); TQString sLine = s.mid(historyLead.length()); if ( ( !bUseRegExp && !sLine.stripWhiteSpace().isEmpty() && bPrevLineIsEmpty ) || bUseRegExp && newHistoryEntry.exactMatch( sLine ) ) { if ( !key.isEmpty() && !melList.empty() ) { // Only insert new HistoryMapEntry if key not found; in either case p.first is a valid iterator to element key. std::pair p = historyMap.insert(HistoryMap::value_type(key,HistoryMapEntry())); HistoryMapEntry& hme = p.first->second; if ( src==A ) hme.mellA = melList; if ( src==B ) hme.mellB = melList; if ( src==C ) hme.mellC = melList; if ( p.second ) // Not in list yet? { hitList.insert( itHitListFront, p.first ); } } if ( ! bUseRegExp ) key = sLine; else key = calcHistorySortKey(m_pOptionDialog->m_historyEntryStartSortKeyOrder,newHistoryEntry,parenthesesGroups); melList.clear(); melList.push_back( MergeEditLine(id3l,src) ); } else if ( ! historyStart.exactMatch( s ) ) { melList.push_back( MergeEditLine(id3l,src) ); } bPrevLineIsEmpty = sLine.stripWhiteSpace().isEmpty(); } if ( !key.isEmpty() ) { // Only insert new HistoryMapEntry if key not found; in either case p.first is a valid iterator to element key. std::pair p = historyMap.insert(HistoryMap::value_type(key,HistoryMapEntry())); HistoryMapEntry& hme = p.first->second; if ( src==A ) hme.mellA = melList; if ( src==B ) hme.mellB = melList; if ( src==C ) hme.mellC = melList; if ( p.second ) // Not in list yet? { hitList.insert( itHitListFront, p.first ); } } // End of the history } MergeResultWindow::MergeEditLineList& MergeResultWindow::HistoryMapEntry::choice( bool bThreeInputs ) { if ( !bThreeInputs ) return mellA.empty() ? mellB : mellA; else { if ( mellA.empty() ) return mellC.empty() ? mellB : mellC; // A doesn't exist, return one that exists else if ( ! mellB.empty() && ! mellC.empty() ) { // A, B and C exist return mellA; } else return mellB.empty() ? mellB : mellC; // A exists, return the one that doesn't exist } } bool MergeResultWindow::HistoryMapEntry::staysInPlace( bool bThreeInputs, Diff3LineList::const_iterator& iHistoryEnd ) { // The entry should stay in place if the decision made by the automerger is correct. Diff3LineList::const_iterator& iHistoryLast = iHistoryEnd; --iHistoryLast; if ( !bThreeInputs ) { if ( !mellA.empty() && !mellB.empty() && mellA.begin()->id3l()==mellB.begin()->id3l() && mellA.back().id3l() == iHistoryLast && mellB.back().id3l() == iHistoryLast ) { iHistoryEnd = mellA.begin()->id3l(); return true; } else { return false; } } else { if ( !mellA.empty() && !mellB.empty() && !mellC.empty() && mellA.begin()->id3l()==mellB.begin()->id3l() && mellA.begin()->id3l()==mellC.begin()->id3l() && mellA.back().id3l() == iHistoryLast && mellB.back().id3l() == iHistoryLast && mellC.back().id3l() == iHistoryLast ) { iHistoryEnd = mellA.begin()->id3l(); return true; } else { return false; } } } void MergeResultWindow::slotMergeHistory() { Diff3LineList::const_iterator iD3LHistoryBegin; Diff3LineList::const_iterator iD3LHistoryEnd; int d3lHistoryBeginLineIdx = -1; int d3lHistoryEndLineIdx = -1; // Search for history start, history end in the diff3LineList findHistoryRange( m_pOptionDialog->m_historyStartRegExp, m_pldC!=0, m_pDiff3LineList, iD3LHistoryBegin, iD3LHistoryEnd, d3lHistoryBeginLineIdx, d3lHistoryEndLineIdx ); if ( iD3LHistoryBegin != m_pDiff3LineList->end() ) { // Now collect the historyMap information HistoryMap historyMap; std::list< HistoryMap::iterator > hitList; if (m_pldC==0) { collectHistoryInformation( A, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); collectHistoryInformation( B, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); } else { collectHistoryInformation( A, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); collectHistoryInformation( B, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); collectHistoryInformation( C, iD3LHistoryBegin, iD3LHistoryEnd, historyMap, hitList ); } Diff3LineList::const_iterator iD3LHistoryOrigEnd = iD3LHistoryEnd; bool bHistoryMergeSorting = m_pOptionDialog->m_bHistoryMergeSorting && ! m_pOptionDialog->m_historyEntryStartSortKeyOrder.isEmpty() && ! m_pOptionDialog->m_historyEntryStartRegExp.isEmpty(); if ( m_pOptionDialog->m_maxNofHistoryEntries==-1 ) { // Remove parts from the historyMap and hitList that stay in place if ( bHistoryMergeSorting ) { while ( ! historyMap.empty() ) { HistoryMap::iterator hMapIt = historyMap.begin(); if( hMapIt->second.staysInPlace( m_pldC!=0, iD3LHistoryEnd ) ) historyMap.erase(hMapIt); else break; } } else { while ( ! hitList.empty() ) { HistoryMap::iterator hMapIt = hitList.back(); if( hMapIt->second.staysInPlace( m_pldC!=0, iD3LHistoryEnd ) ) hitList.pop_back(); else break; } } while (iD3LHistoryOrigEnd != iD3LHistoryEnd) { --iD3LHistoryOrigEnd; --d3lHistoryEndLineIdx; } } MergeLineList::iterator iMLLStart = splitAtDiff3LineIdx(d3lHistoryBeginLineIdx); MergeLineList::iterator iMLLEnd = splitAtDiff3LineIdx(d3lHistoryEndLineIdx); // Now join all MergeLines in the history MergeLineList::iterator i = iMLLStart; if ( i != iMLLEnd ) { ++i; while ( i!=iMLLEnd ) { iMLLStart->join(*i); i = m_mergeLineList.erase( i ); } } iMLLStart->mergeEditLineList.clear(); // Now insert the complete history into the first MergeLine of the history iMLLStart->mergeEditLineList.push_back( MergeEditLine( iD3LHistoryBegin, m_pldC == 0 ? B : C ) ); TQString lead = calcHistoryLead( iD3LHistoryBegin->getString(A) ); MergeEditLine mel( m_pDiff3LineList->end() ); mel.setString( lead ); iMLLStart->mergeEditLineList.push_back(mel); int historyCount = 0; if ( bHistoryMergeSorting ) { // Create a sorted history HistoryMap::reverse_iterator hmit; for ( hmit = historyMap.rbegin(); hmit != historyMap.rend(); ++hmit ) { if ( historyCount==m_pOptionDialog->m_maxNofHistoryEntries ) break; ++historyCount; HistoryMapEntry& hme = hmit->second; MergeEditLineList& mell = hme.choice(m_pldC!=0); if (!mell.empty()) iMLLStart->mergeEditLineList.splice( iMLLStart->mergeEditLineList.end(), mell, mell.begin(), mell.end() ); } } else { // Create history in order of appearance std::list< HistoryMap::iterator >::iterator hlit; for ( hlit = hitList.begin(); hlit != hitList.end(); ++hlit ) { if ( historyCount==m_pOptionDialog->m_maxNofHistoryEntries ) break; ++historyCount; HistoryMapEntry& hme = (*hlit)->second; MergeEditLineList& mell = hme.choice(m_pldC!=0); if (!mell.empty()) iMLLStart->mergeEditLineList.splice( iMLLStart->mergeEditLineList.end(), mell, mell.begin(), mell.end() ); } } setFastSelector( iMLLStart ); update(); } } void MergeResultWindow::slotRegExpAutoMerge() { if ( m_pOptionDialog->m_autoMergeRegExp.isEmpty() ) return; TQRegExp vcsKeywords = m_pOptionDialog->m_autoMergeRegExp; MergeLineList::iterator i; for ( i=m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) { if (i->bConflict ) { Diff3LineList::const_iterator id3l = i->id3l; if ( vcsKeywords.exactMatch( id3l->getString(A) ) && vcsKeywords.exactMatch( id3l->getString(B) ) && (m_pldC==0 || vcsKeywords.exactMatch( id3l->getString(C) ))) { MergeEditLine& mel = *i->mergeEditLineList.begin(); mel.setSource( m_pldC==0 ? B : C, false ); splitAtDiff3LineIdx( i->d3lLineIdx+1 ); } } } update(); } // This doesn't detect user modifications and should only be called after automatic merge // This will only do something for three file merge. // Irrelevant changes are those where all contributions from B are already contained in C. // Also irrelevant are conflicts automatically solved (automerge regexp and history automerge) // Precondition: The VCS-keyword would also be C. bool MergeResultWindow::doRelevantChangesExist() { if ( m_pldC==0 || m_mergeLineList.size() <= 1 ) return true; MergeLineList::iterator i; for ( i=m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) { if ( ( i->bConflict && i->mergeEditLineList.begin()->src()!=C ) || i->srcSelect == B ) { return true; } } return false; } // Returns the iterator to the MergeLine after the split MergeResultWindow::MergeLineList::iterator MergeResultWindow::splitAtDiff3LineIdx( int d3lLineIdx ) { MergeLineList::iterator i; for ( i = m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) { if ( i->d3lLineIdx==d3lLineIdx ) { // No split needed, this is the beginning of a MergeLine return i; } else if ( i->d3lLineIdx > d3lLineIdx ) { // The split must be in the previous MergeLine --i; MergeLine& ml = *i; MergeLine newML; ml.split(newML,d3lLineIdx); ++i; return m_mergeLineList.insert( i, newML ); } } // The split must be in the previous MergeLine --i; MergeLine& ml = *i; MergeLine newML; ml.split(newML,d3lLineIdx); ++i; return m_mergeLineList.insert( i, newML ); } void MergeResultWindow::slotSplitDiff( int firstD3lLineIdx, int lastD3lLineIdx ) { if (lastD3lLineIdx>=0) splitAtDiff3LineIdx( lastD3lLineIdx + 1 ); setFastSelector( splitAtDiff3LineIdx(firstD3lLineIdx) ); } void MergeResultWindow::slotJoinDiffs( int firstD3lLineIdx, int lastD3lLineIdx ) { MergeLineList::iterator i; MergeLineList::iterator iMLLStart = m_mergeLineList.end(); MergeLineList::iterator iMLLEnd = m_mergeLineList.end(); for ( i=m_mergeLineList.begin(); i!=m_mergeLineList.end(); ++i ) { MergeLine& ml = *i; if ( firstD3lLineIdx >= ml.d3lLineIdx && firstD3lLineIdx < ml.d3lLineIdx + ml.srcRangeLength ) { iMLLStart = i; } if ( lastD3lLineIdx >= ml.d3lLineIdx && lastD3lLineIdx < ml.d3lLineIdx + ml.srcRangeLength ) { iMLLEnd = i; ++iMLLEnd; break; } } bool bJoined = false; for( i=iMLLStart; i!=iMLLEnd && i!=m_mergeLineList.end(); ) { if ( i==iMLLStart ) { ++i; } else { iMLLStart->join(*i); i = m_mergeLineList.erase( i ); bJoined = true; } } if (bJoined) { iMLLStart->mergeEditLineList.clear(); // Insert a conflict line as placeholder iMLLStart->mergeEditLineList.push_back( MergeEditLine( iMLLStart->id3l ) ); } setFastSelector( iMLLStart ); } void MergeResultWindow::myUpdate(int afterMilliSecs) { killTimer(m_delayedDrawTimer); m_bMyUpdate = true; m_delayedDrawTimer = startTimer( afterMilliSecs ); } void MergeResultWindow::timerEvent(TQTimerEvent*) { killTimer(m_delayedDrawTimer); m_delayedDrawTimer = 0; if ( m_bMyUpdate ) { update(); m_bMyUpdate = false; } if ( m_scrollDeltaX != 0 || m_scrollDeltaY != 0 ) { m_selection.end( m_selection.lastLine + m_scrollDeltaY, m_selection.lastPos + m_scrollDeltaX ); emit scroll( m_scrollDeltaX, m_scrollDeltaY ); killTimer(m_delayedDrawTimer); m_delayedDrawTimer = startTimer(50); } } TQString MergeResultWindow::MergeEditLine::getString( const MergeResultWindow* mrw ) { if ( isRemoved() ) { return TQString(); } if ( ! isModified() ) { int src = m_src; if ( src == 0 ) { return TQString(); } const Diff3Line& d3l = *m_id3l; const LineData* pld = 0; assert( src == A || src == B || src == C ); if ( src == A && d3l.lineA!=-1 ) pld = &mrw->m_pldA[ d3l.lineA ]; else if ( src == B && d3l.lineB!=-1 ) pld = &mrw->m_pldB[ d3l.lineB ]; else if ( src == C && d3l.lineC!=-1 ) pld = &mrw->m_pldC[ d3l.lineC ]; if ( pld == 0 ) { // assert(false); This is no error. return TQString(); } return TQString( pld->pLine, pld->size ); } else { return m_str; } return 0; } /// Converts the cursor-posOnScreen into a text index, considering tabulators. int convertToPosInText( const TQString& s, int posOnScreen, int tabSize ) { int localPosOnScreen = 0; int size=s.length(); for ( int i=0; i=posOnScreen ) return i; // All letters except tabulator have width one. int letterWidth = s[i]!='\t' ? 1 : tabber( localPosOnScreen, tabSize ); localPosOnScreen += letterWidth; if ( localPosOnScreen>posOnScreen ) return i; } return size; } /// Converts the index into the text to a cursor-posOnScreen considering tabulators. int convertToPosOnScreen( const TQString& p, int posInText, int tabSize ) { int posOnScreen = 0; for ( int i=0; i height() ) return; yOffset += topLineYOffset; TQString srcName = " "; if ( bUserModified ) srcName = "m"; else if ( srcSelect == A && mergeDetails != eNoChange ) srcName = "A"; else if ( srcSelect == B ) srcName = "B"; else if ( srcSelect == C ) srcName = "C"; if ( rangeMark & 4 ) { p.fillRect( xOffset, yOffset, width(), fontHeight, m_pOptionDialog->m_currentRangeBgColor ); } if( (srcSelect > 0 || bUserModified ) && !bLineRemoved ) { int outPos = 0; TQString s; int size = str.length(); for ( int i=0; im_tabSize ); for( int j=0; jm_tabSize ),m_pOptionDialog->m_tabSize ); int lastPosInLine = convertToPosOnScreen( str, convertToPosInText( str, m_selection.lastPosInLine(line), m_pOptionDialog->m_tabSize ), m_pOptionDialog->m_tabSize ); int lengthInLine = max2(0,lastPosInLine - firstPosInLine); if (lengthInLine>0) m_selection.bSelectionContainsData = true; if ( lengthInLine < int(s.length()) ) { // Draw a normal line first p.setPen( m_pOptionDialog->m_fgColor ); p.drawText( xOffset, yOffset+fontAscent, s.mid(m_firstColumn), true ); } int firstPosInLine2 = max2( firstPosInLine, m_firstColumn ); int lengthInLine2 = max2(0,lastPosInLine - firstPosInLine2); if( m_selection.lineWithin( line+1 ) ) p.fillRect( xOffset + fontWidth*(firstPosInLine2-m_firstColumn), yOffset, width(), fontHeight, colorGroup().highlight() ); else if ( lengthInLine2>0 ) p.fillRect( xOffset + fontWidth*(firstPosInLine2-m_firstColumn), yOffset, fontWidth*lengthInLine2, fontHeight, colorGroup().highlight() ); p.setPen( colorGroup().highlightedText() ); p.drawText( xOffset + fontWidth*(firstPosInLine2-m_firstColumn), yOffset+fontAscent, s.mid(firstPosInLine2,lengthInLine2), true ); } else { p.setPen( m_pOptionDialog->m_fgColor ); p.drawText( xOffset, yOffset+fontAscent, s.mid(m_firstColumn), true ); } p.setPen( m_pOptionDialog->m_fgColor ); if ( m_cursorYPos==line ) { m_cursorXPos = minMaxLimiter( m_cursorXPos, 0, outPos ); m_cursorXPos = convertToPosOnScreen( str, convertToPosInText( str, m_cursorXPos, m_pOptionDialog->m_tabSize ),m_pOptionDialog->m_tabSize ); } p.drawText( 1, yOffset+fontAscent, srcName, true ); } else if ( bLineRemoved ) { p.setPen( m_pOptionDialog->m_colorForConflict ); p.drawText( xOffset, yOffset+fontAscent, i18n("") ); p.drawText( 1, yOffset+fontAscent, srcName ); if ( m_cursorYPos==line ) m_cursorXPos = 0; } else if ( srcSelect == 0 ) { p.setPen( m_pOptionDialog->m_colorForConflict ); if ( bWhiteSpaceConflict ) p.drawText( xOffset, yOffset+fontAscent, i18n("") ); else p.drawText( xOffset, yOffset+fontAscent, i18n("") ); p.drawText( 1, yOffset+fontAscent, "?" ); if ( m_cursorYPos==line ) m_cursorXPos = 0; } else assert(false); xOffset -= fontWidth; p.setPen( m_pOptionDialog->m_fgColor ); if ( rangeMark & 1 ) // begin mark { p.drawLine( xOffset, yOffset+1, xOffset, yOffset+fontHeight/2 ); p.drawLine( xOffset, yOffset+1, xOffset-2, yOffset+1 ); } else { p.drawLine( xOffset, yOffset, xOffset, yOffset+fontHeight/2 ); } if ( rangeMark & 2 ) // end mark { p.drawLine( xOffset, yOffset+fontHeight/2, xOffset, yOffset+fontHeight-1 ); p.drawLine( xOffset, yOffset+fontHeight-1, xOffset-2, yOffset+fontHeight-1 ); } else { p.drawLine( xOffset, yOffset+fontHeight/2, xOffset, yOffset+fontHeight ); } if ( rangeMark & 4 ) { p.fillRect( xOffset + 3, yOffset, 3, fontHeight, m_pOptionDialog->m_fgColor ); /* p.setPen( blue ); p.drawLine( xOffset+2, yOffset, xOffset+2, yOffset+fontHeight-1 ); p.drawLine( xOffset+3, yOffset, xOffset+3, yOffset+fontHeight-1 );*/ } } void MergeResultWindow::setPaintingAllowed(bool bPaintingAllowed) { m_bPaintingAllowed = bPaintingAllowed; if ( !m_bPaintingAllowed ) { m_currentMergeLineIt = m_mergeLineList.end(); reset(); } } void MergeResultWindow::paintEvent( TQPaintEvent* ) { if (m_pDiff3LineList==0 || !m_bPaintingAllowed) return; bool bOldSelectionContainsData = m_selection.bSelectionContainsData; const TQFontMetrics& fm = fontMetrics(); int fontHeight = fm.height(); int fontWidth = fm.width("W"); int fontAscent = fm.ascent(); if ( !m_bCursorUpdate ) // Don't redraw everything for blinking cursor? { m_selection.bSelectionContainsData = false; if ( size() != m_pixmap.size() ) m_pixmap.resize(size()); MyPainter p(&m_pixmap, m_pOptionDialog->m_bRightToLeftLanguage, width(), fontWidth); p.setFont( font() ); p.TQPainter::fillRect( rect(), m_pOptionDialog->m_bgColor ); //int visibleLines = height() / fontHeight; int lastVisibleLine = m_firstLine + getNofVisibleLines() + 5; int nofColumns = 0; int line = 0; MergeLineList::iterator mlIt = m_mergeLineList.begin(); for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) { MergeLine& ml = *mlIt; if ( line > lastVisibleLine || line + ml.mergeEditLineList.size() < m_firstLine) { line += ml.mergeEditLineList.size(); } else { MergeEditLineList::iterator melIt; for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) { if (line>=m_firstLine && line<=lastVisibleLine) { MergeEditLine& mel = *melIt; MergeEditLineList::iterator melIt1 = melIt; ++melIt1; int rangeMark = 0; if ( melIt==ml.mergeEditLineList.begin() ) rangeMark |= 1; // Begin range mark if ( melIt1==ml.mergeEditLineList.end() ) rangeMark |= 2; // End range mark if ( mlIt == m_currentMergeLineIt ) rangeMark |= 4; // Mark of the current line TQString s; s = mel.getString( this ); if ( convertToPosOnScreen(s,s.length(),m_pOptionDialog->m_tabSize) >nofColumns) nofColumns = s.length(); writeLine( p, line, s, mel.src(), ml.mergeDetails, rangeMark, mel.isModified(), mel.isRemoved(), ml.bWhiteSpaceConflict ); } ++line; } } } if ( line != m_nofLines || nofColumns != m_nofColumns ) { m_nofLines = line; assert( m_nofLines == m_totalSize ); m_nofColumns = nofColumns; emit resizeSignal(); } p.end(); } TQPainter painter(this); int topLineYOffset = 0; int xOffset = fontWidth * leftInfoWidth; int yOffset = ( m_cursorYPos - m_firstLine ) * fontHeight + topLineYOffset; int xCursor = ( m_cursorXPos - m_firstColumn ) * fontWidth + xOffset; if ( !m_bCursorUpdate ) painter.drawPixmap(0,0, m_pixmap); else { if (!m_pOptionDialog->m_bRightToLeftLanguage) painter.drawPixmap(xCursor-2, yOffset, m_pixmap, xCursor-2, yOffset, 5, fontAscent+2 ); else painter.drawPixmap(width()-1-4-(xCursor-2), yOffset, m_pixmap, width()-1-4-(xCursor-2), yOffset, 5, fontAscent+2 ); m_bCursorUpdate = false; } painter.end(); if ( m_bCursorOn && hasFocus() && m_cursorYPos>=m_firstLine ) { MyPainter painter(this, m_pOptionDialog->m_bRightToLeftLanguage, width(), fontWidth); int topLineYOffset = 0; int xOffset = fontWidth * leftInfoWidth; int yOffset = ( m_cursorYPos-m_firstLine ) * fontHeight + topLineYOffset; int xCursor = ( m_cursorXPos - m_firstColumn ) * fontWidth + xOffset; painter.setPen( m_pOptionDialog->m_fgColor ); painter.drawLine( xCursor, yOffset, xCursor, yOffset+fontAscent ); painter.drawLine( xCursor-2, yOffset, xCursor+2, yOffset ); painter.drawLine( xCursor-2, yOffset+fontAscent+1, xCursor+2, yOffset+fontAscent+1 ); } if( !bOldSelectionContainsData && m_selection.bSelectionContainsData ) emit newSelection(); } void MergeResultWindow::updateSourceMask() { int srcMask=0; int enabledMask = 0; if( !hasFocus() || m_pDiff3LineList==0 || !m_bPaintingAllowed || m_currentMergeLineIt == m_mergeLineList.end() ) { srcMask = 0; enabledMask = 0; } else { enabledMask = m_pldC==0 ? 3 : 7; MergeLine& ml = *m_currentMergeLineIt; srcMask = 0; bool bModified = false; MergeEditLineList::iterator melIt; for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) { MergeEditLine& mel = *melIt; if ( mel.src()==1 ) srcMask |= 1; if ( mel.src()==2 ) srcMask |= 2; if ( mel.src()==3 ) srcMask |= 4; if ( mel.isModified() || !mel.isEditableText() ) bModified = true; } if ( ml.mergeDetails == eNoChange ) { srcMask = 0; enabledMask = bModified ? 1 : 0; } } emit sourceMask( srcMask, enabledMask ); } void MergeResultWindow::focusInEvent( TQFocusEvent* e ) { updateSourceMask(); TQWidget::focusInEvent(e); } void MergeResultWindow::convertToLinePos( int x, int y, int& line, int& pos ) { const TQFontMetrics& fm = fontMetrics(); int fontHeight = fm.height(); int fontWidth = fm.width('W'); int xOffset = (leftInfoWidth-m_firstColumn)*fontWidth; int topLineYOffset = 0; int yOffset = topLineYOffset - m_firstLine * fontHeight; line = min2( ( y - yOffset ) / fontHeight, m_totalSize-1 ); if ( ! m_pOptionDialog->m_bRightToLeftLanguage ) pos = ( x - xOffset ) / fontWidth; else pos = ( (width() - 1 - x) - xOffset ) / fontWidth; } void MergeResultWindow::mousePressEvent ( TQMouseEvent* e ) { m_bCursorOn = true; int line; int pos; convertToLinePos( e->x(), e->y(), line, pos ); bool bLMB = e->button() == TQt::LeftButton; bool bMMB = e->button() == TQt::MidButton; bool bRMB = e->button() == TQt::RightButton; if ( bLMB && pos < m_firstColumn || bRMB ) // Fast range selection { m_cursorXPos = 0; m_cursorOldXPos = 0; m_cursorYPos = max2(line,0); int l = 0; MergeLineList::iterator i = m_mergeLineList.begin(); for(i = m_mergeLineList.begin();i!=m_mergeLineList.end(); ++i) { if (l==line) break; l += i->mergeEditLineList.size(); if (l>line) break; } m_selection.reset(); // Disable current selection m_bCursorOn = true; setFastSelector( i ); if (bRMB) { showPopupMenu( TQCursor::pos() ); } } else if ( bLMB ) // Normal cursor placement { pos = max2(pos,0); line = max2(line,0); if ( e->state() & TQt::ShiftButton ) { if (m_selection.firstLine==-1) m_selection.start( line, pos ); m_selection.end( line, pos ); } else { // Selection m_selection.reset(); m_selection.start( line, pos ); m_selection.end( line, pos ); } m_cursorXPos = pos; m_cursorOldXPos = pos; m_cursorYPos = line; update(); //showStatusLine( line, m_winIdx, m_pFilename, m_pDiff3LineList, m_pStatusBar ); } else if ( bMMB ) // Paste clipboard { pos = max2(pos,0); line = max2(line,0); m_selection.reset(); m_cursorXPos = pos; m_cursorOldXPos = pos; m_cursorYPos = line; pasteClipboard( true ); } } void MergeResultWindow::mouseDoubleClickEvent( TQMouseEvent* e ) { if ( e->button() == TQt::LeftButton ) { int line; int pos; convertToLinePos( e->x(), e->y(), line, pos ); m_cursorXPos = pos; m_cursorOldXPos = pos; m_cursorYPos = line; // Get the string data of the current line MergeLineList::iterator mlIt; MergeEditLineList::iterator melIt; calcIteratorFromLineNr( line, mlIt, melIt ); TQString s = melIt->getString( this ); if ( !s.isEmpty() ) { int pos1, pos2; calcTokenPos( s, pos, pos1, pos2, m_pOptionDialog->m_tabSize ); resetSelection(); m_selection.start( line, convertToPosOnScreen( s, pos1, m_pOptionDialog->m_tabSize ) ); m_selection.end( line, convertToPosOnScreen( s, pos2, m_pOptionDialog->m_tabSize ) ); update(); // emit selectionEnd() happens in the mouseReleaseEvent. } } } void MergeResultWindow::mouseReleaseEvent ( TQMouseEvent * e ) { if ( e->button() == TQt::LeftButton ) { killTimer(m_delayedDrawTimer); m_delayedDrawTimer = 0; if (m_selection.firstLine != -1 ) { emit selectionEnd(); } } } void MergeResultWindow::mouseMoveEvent ( TQMouseEvent * e ) { int line; int pos; convertToLinePos( e->x(), e->y(), line, pos ); m_cursorXPos = pos; m_cursorOldXPos = pos; m_cursorYPos = line; if (m_selection.firstLine != -1 ) { m_selection.end( line, pos ); myUpdate(0); //showStatusLine( line, m_winIdx, m_pFilename, m_pDiff3LineList, m_pStatusBar ); // Scroll because mouse moved out of the window const TQFontMetrics& fm = fontMetrics(); int fontWidth = fm.width('W'); int topLineYOffset = 0; int deltaX=0; int deltaY=0; if ( ! m_pOptionDialog->m_bRightToLeftLanguage ) { if ( e->x() < leftInfoWidth*fontWidth ) deltaX=-1; if ( e->x() > width() ) deltaX=+1; } else { if ( e->x() > width()-1-leftInfoWidth*fontWidth ) deltaX=-1; if ( e->x() < fontWidth ) deltaX=+1; } if ( e->y() < topLineYOffset ) deltaY=-1; if ( e->y() > height() ) deltaY=+1; m_scrollDeltaX = deltaX; m_scrollDeltaY = deltaY; if ( deltaX != 0 || deltaY!= 0) { emit scroll( deltaX, deltaY ); } } } void MergeResultWindow::slotCursorUpdate() { m_cursorTimer.stop(); m_bCursorOn = !m_bCursorOn; if ( isVisible() ) { m_bCursorUpdate = true; const TQFontMetrics& fm = fontMetrics(); int fontWidth = fm.width("W"); int topLineYOffset = 0; int xOffset = fontWidth * leftInfoWidth; int yOffset = ( m_cursorYPos - m_firstLine ) * fm.height() + topLineYOffset; int xCursor = ( m_cursorXPos - m_firstColumn ) * fontWidth + xOffset; if (!m_pOptionDialog->m_bRightToLeftLanguage) repaint( xCursor-2, yOffset, 5, fm.ascent()+2 ); else repaint( width()-1-4-(xCursor-2), yOffset, 5, fm.ascent()+2 ); m_bCursorUpdate=false; } m_cursorTimer.start(500,true); } void MergeResultWindow::wheelEvent( TQWheelEvent* e ) { int d = -e->delta()*TQApplication::wheelScrollLines()/120; e->accept(); scroll( 0, min2(d, getNofVisibleLines()) ); } void MergeResultWindow::keyPressEvent( TQKeyEvent* e ) { int y = m_cursorYPos; MergeLineList::iterator mlIt; MergeEditLineList::iterator melIt; calcIteratorFromLineNr( y, mlIt, melIt ); TQString str = melIt->getString( this ); int x = convertToPosInText( str, m_cursorXPos, m_pOptionDialog->m_tabSize ); bool bCtrl = ( e->state() & TQt::ControlButton ) != 0 ; bool bShift = ( e->state() & TQt::ShiftButton ) != 0 ; #ifdef _WIN32 bool bAlt = ( e->state() & TQt::AltButton ) != 0 ; if ( bCtrl && bAlt ){ bCtrl=false; bAlt=false; } // AltGr-Key pressed. #endif bool bYMoveKey = false; // Special keys switch ( e->key() ) { case TQt::Key_Escape: break; //case Key_Tab: break; case TQt::Key_Backtab: break; case TQt::Key_Delete: { if ( deleteSelection2( str, x, y, mlIt, melIt )) break; if( !melIt->isEditableText() ) break; if (x>=(int)str.length()) { if ( yisEditableText() ) { TQString s2 = melIt1->getString( this ); melIt->setString( str + s2 ); // Remove the line if ( mlIt1->mergeEditLineList.size()>1 ) mlIt1->mergeEditLineList.erase( melIt1 ); else melIt1->setRemoved(); } } } else { TQString s = str.left(x); s += str.mid( x+1 ); melIt->setString( s ); setModified(); } break; } case TQt::Key_Backspace: { if ( deleteSelection2( str, x, y, mlIt, melIt )) break; if( !melIt->isEditableText() ) break; if (x==0) { if ( y>0 ) { setModified(); MergeLineList::iterator mlIt1; MergeEditLineList::iterator melIt1; calcIteratorFromLineNr( y-1, mlIt1, melIt1 ); if ( melIt1->isEditableText() ) { TQString s1 = melIt1->getString( this ); melIt1->setString( s1 + str ); // Remove the previous line if ( mlIt->mergeEditLineList.size()>1 ) mlIt->mergeEditLineList.erase( melIt ); else melIt->setRemoved(); --y; x=str.length(); } } } else { TQString s = str.left( x-1 ); s += str.mid( x ); --x; melIt->setString( s ); setModified(); } break; } case TQt::Key_Return: case TQt::Key_Enter: { if( !melIt->isEditableText() ) break; deleteSelection2( str, x, y, mlIt, melIt ); setModified(); TQString indentation; if ( m_pOptionDialog->m_bAutoIndentation ) { // calc last indentation MergeLineList::iterator mlIt1 = mlIt; MergeEditLineList::iterator melIt1 = melIt; for(;;) { const TQString s = melIt1->getString(this); if ( !s.isEmpty() ) { unsigned int i; for( i=0; imergeEditLineList.begin() ) --melIt1; else { if ( mlIt1 == m_mergeLineList.begin() ) break; --mlIt1; melIt1 = mlIt1->mergeEditLineList.end(); --melIt1; } } } MergeEditLine mel(mlIt->id3l); // Associate every mel with an id3l, even if not really valid. mel.setString( indentation + str.mid(x) ); if ( x<(int)str.length() ) // Cut off the old line. { // Since ps possibly points into melIt->str, first copy it into a temporary. TQString temp = str.left(x); melIt->setString( temp ); } ++melIt; mlIt->mergeEditLineList.insert( melIt, mel ); x = indentation.length(); ++y; break; } case TQt::Key_Insert: m_bInsertMode = !m_bInsertMode; break; case TQt::Key_Pause: break; case TQt::Key_Print: break; case TQt::Key_SysReq: break; case TQt::Key_Home: x=0; if(bCtrl){y=0; } break; // cursor movement case TQt::Key_End: x=INT_MAX; if(bCtrl){y=INT_MAX;} break; case TQt::Key_Left: case TQt::Key_Right: if ( (e->key()==TQt::Key_Left) ^ m_pOptionDialog->m_bRightToLeftLanguage ) // operator^: XOR { if ( !bCtrl ) { --x; if(x<0 && y>0){--y; x=INT_MAX;} } else { while( x>0 && (str[x-1]==' ' || str[x-1]=='\t') ) --x; while( x>0 && (str[x-1]!=' ' && str[x-1]!='\t') ) --x; } } else { if ( !bCtrl ) { ++x; if(x>(int)str.length() && ytext(); if( t.isEmpty() || bCtrl ) { e->ignore(); return; } else { if( bCtrl ) { e->ignore(); return; } else { if( !melIt->isEditableText() ) break; deleteSelection2( str, x, y, mlIt, melIt ); setModified(); // Characters to insert TQString s=str; if ( t[0]=='\t' && m_pOptionDialog->m_bReplaceTabs ) { int spaces = (m_cursorXPos / m_pOptionDialog->m_tabSize + 1)*m_pOptionDialog->m_tabSize - m_cursorXPos; t.fill( ' ', spaces ); } if ( m_bInsertMode ) s.insert( x, t ); else s.replace( x, t.length(), t ); melIt->setString( s ); x += t.length(); bShift = false; } } } } y = minMaxLimiter( y, 0, m_totalSize-1 ); calcIteratorFromLineNr( y, mlIt, melIt ); str = melIt->getString( this ); x = minMaxLimiter( x, 0, (int)str.length() ); int newFirstLine = m_firstLine; int newFirstColumn = m_firstColumn; if ( y m_firstLine + getNofVisibleLines() ) newFirstLine = y - getNofVisibleLines(); if (bYMoveKey) x=convertToPosInText( str, m_cursorOldXPos, m_pOptionDialog->m_tabSize ); int xOnScreen = convertToPosOnScreen( str, x, m_pOptionDialog->m_tabSize ); if ( xOnScreen m_firstColumn + getNofVisibleColumns() ) newFirstColumn = xOnScreen - getNofVisibleColumns(); if ( bShift ) { if (m_selection.firstLine==-1) m_selection.start( m_cursorYPos, m_cursorXPos ); m_selection.end( y, xOnScreen ); } else m_selection.reset(); m_cursorYPos = y; m_cursorXPos = xOnScreen; if ( ! bYMoveKey ) m_cursorOldXPos = m_cursorXPos; m_bCursorOn = false; if ( newFirstLine!=m_firstLine || newFirstColumn!=m_firstColumn ) { m_bCursorOn = true; scroll( newFirstColumn-m_firstColumn, newFirstLine-m_firstLine ); return; } m_bCursorOn = true; update(); } void MergeResultWindow::calcIteratorFromLineNr( int line, MergeResultWindow::MergeLineList::iterator& mlIt, MergeResultWindow::MergeEditLineList::iterator& melIt ) { for( mlIt = m_mergeLineList.begin(); mlIt!=m_mergeLineList.end(); ++mlIt) { MergeLine& ml = *mlIt; if ( line > ml.mergeEditLineList.size() ) { line -= ml.mergeEditLineList.size(); } else { for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) { --line; if (line<0) return; } } } assert(false); } TQString MergeResultWindow::getSelection() { TQString selectionString; int line = 0; MergeLineList::iterator mlIt = m_mergeLineList.begin(); for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) { MergeLine& ml = *mlIt; MergeEditLineList::iterator melIt; for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) { MergeEditLine& mel = *melIt; if ( m_selection.lineWithin(line) ) { int outPos = 0; if (mel.isEditableText()) { const TQString str = mel.getString( this ); // Consider tabs for( unsigned int i=0; im_tabSize ); } if( m_selection.within( line, outPos ) ) { selectionString += str[i]; } outPos += spaces; } } else if ( mel.isConflict() ) { selectionString += i18n(""); } if( m_selection.within( line, outPos ) ) { #ifdef _WIN32 selectionString += '\r'; #endif selectionString += '\n'; } } ++line; } } return selectionString; } bool MergeResultWindow::deleteSelection2( TQString& s, int& x, int& y, MergeLineList::iterator& mlIt, MergeEditLineList::iterator& melIt ) { if (m_selection.firstLine!=-1 && m_selection.bSelectionContainsData ) { deleteSelection(); y = m_cursorYPos; calcIteratorFromLineNr( y, mlIt, melIt ); s = melIt->getString( this ); x = convertToPosInText( s, m_cursorXPos, m_pOptionDialog->m_tabSize ); return true; } return false; } void MergeResultWindow::deleteSelection() { if ( m_selection.firstLine==-1 || !m_selection.bSelectionContainsData ) { return; } setModified(); int line = 0; MergeLineList::iterator mlItFirst; MergeEditLineList::iterator melItFirst; TQString firstLineString; int firstLine = -1; int lastLine = -1; MergeLineList::iterator mlIt; for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) { MergeLine& ml = *mlIt; MergeEditLineList::iterator melIt; for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) { MergeEditLine& mel = *melIt; if ( mel.isEditableText() && m_selection.lineWithin(line) ) { if ( firstLine==-1 ) firstLine = line; lastLine = line; } ++line; } } if ( firstLine == -1 ) { return; // Nothing to delete. } line = 0; for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) { MergeLine& ml = *mlIt; MergeEditLineList::iterator melIt, melIt1; for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ) { MergeEditLine& mel = *melIt; melIt1 = melIt; ++melIt1; if ( mel.isEditableText() && m_selection.lineWithin(line) ) { TQString lineString = mel.getString( this ); int firstPosInLine = m_selection.firstPosInLine(line); int lastPosInLine = m_selection.lastPosInLine(line); if ( line==firstLine ) { mlItFirst = mlIt; melItFirst = melIt; int pos = convertToPosInText( lineString, firstPosInLine, m_pOptionDialog->m_tabSize ); firstLineString = lineString.left( pos ); } if ( line==lastLine ) { // This is the last line in the selection int pos = convertToPosInText( lineString, lastPosInLine, m_pOptionDialog->m_tabSize ); firstLineString += lineString.mid( pos ); // rest of line melItFirst->setString( firstLineString ); } if ( line!=firstLine ) { // Remove the line if ( mlIt->mergeEditLineList.size()>1 ) mlIt->mergeEditLineList.erase( melIt ); else melIt->setRemoved(); } } ++line; melIt = melIt1; } } m_cursorYPos = m_selection.beginLine(); m_cursorXPos = m_selection.beginPos(); m_cursorOldXPos = m_cursorXPos; m_selection.reset(); } void MergeResultWindow::pasteClipboard( bool bFromSelection ) { if (m_selection.firstLine != -1 ) deleteSelection(); setModified(); int y = m_cursorYPos; MergeLineList::iterator mlIt; MergeEditLineList::iterator melIt, melItAfter; calcIteratorFromLineNr( y, mlIt, melIt ); melItAfter = melIt; ++melItAfter; TQString str = melIt->getString( this ); int x = convertToPosInText( str, m_cursorXPos, m_pOptionDialog->m_tabSize ); if ( !TQApplication::clipboard()->supportsSelection() ) bFromSelection = false; TQString clipBoard = TQApplication::clipboard()->text( bFromSelection ? TQClipboard::Selection : TQClipboard::Clipboard ); TQString currentLine = str.left(x); TQString endOfLine = str.mid(x); int i; int len = clipBoard.length(); for( i=0; isetString( currentLine ); MergeEditLine mel(mlIt->id3l); // Associate every mel with an id3l, even if not really valid. melIt = mlIt->mergeEditLineList.insert( melItAfter, mel ); currentLine = ""; x=0; ++y; } else { currentLine += c; ++x; } } currentLine += endOfLine; melIt->setString( currentLine ); m_cursorYPos = y; m_cursorXPos = convertToPosOnScreen( currentLine, x, m_pOptionDialog->m_tabSize ); m_cursorOldXPos = m_cursorXPos; update(); } void MergeResultWindow::resetSelection() { m_selection.reset(); update(); } void MergeResultWindow::setModified(bool bModified) { if (bModified != m_bModified) { m_bModified = bModified; emit modifiedChanged(m_bModified); } } /// Saves and returns true when successful. bool MergeResultWindow::saveDocument( const TQString& fileName, TQTextCodec* pEncoding ) { // Are still conflicts somewhere? if ( getNrOfUnsolvedConflicts()>0 ) { KMessageBox::error( this, i18n("Not all conflicts are solved yet.\n" "File not saved.\n"), i18n("Conflicts Left")); return false; } update(); FileAccess file( fileName, true /*bWantToWrite*/ ); if ( m_pOptionDialog->m_bDmCreateBakFiles && file.exists() ) { bool bSuccess = file.createBackup(".orig"); if ( !bSuccess ) { KMessageBox::error( this, file.getStatusText() + i18n("\n\nCreating backup failed. File not saved."), i18n("File Save Error") ); return false; } } TQByteArray dataArray; TQTextStream textOutStream(dataArray, IO_WriteOnly); textOutStream.setCodec( pEncoding ); int line = 0; MergeLineList::iterator mlIt = m_mergeLineList.begin(); for(mlIt = m_mergeLineList.begin();mlIt!=m_mergeLineList.end(); ++mlIt) { MergeLine& ml = *mlIt; MergeEditLineList::iterator melIt; for( melIt = ml.mergeEditLineList.begin(); melIt != ml.mergeEditLineList.end(); ++melIt ) { MergeEditLine& mel = *melIt; if ( mel.isEditableText() ) { TQString str = mel.getString( this ); if (line>0) // Prepend line feed, but not for first line { if ( m_pOptionDialog->m_lineEndStyle == eLineEndDos ) { str.prepend("\r\n"); } else { str.prepend("\n"); } } textOutStream << str; ++line; } } } bool bSuccess = file.writeFile( dataArray.data(), dataArray.size() ); if ( ! bSuccess ) { KMessageBox::error( this, i18n("Error while writing."), i18n("File Save Error") ); return false; } setModified( false ); update(); return true; } TQString MergeResultWindow::getString( int lineIdx ) { MergeResultWindow::MergeLineList::iterator mlIt; MergeResultWindow::MergeEditLineList::iterator melIt; calcIteratorFromLineNr( lineIdx, mlIt, melIt ); TQString s = melIt->getString( this ); return s; } bool MergeResultWindow::findString( const TQString& s, int& d3vLine, int& posInLine, bool bDirDown, bool bCaseSensitive ) { int it = d3vLine; int endIt = bDirDown ? getNofLines() : -1; int step = bDirDown ? 1 : -1; int startPos = posInLine; for( ; it!=endIt; it+=step ) { TQString line = getString( it ); if ( !line.isEmpty() ) { int pos = line.find( s, startPos, bCaseSensitive ); if ( pos != -1 ) { d3vLine = it; posInLine = pos; return true; } startPos = 0; } } return false; } void MergeResultWindow::setSelection( int firstLine, int startPos, int lastLine, int endPos ) { if ( lastLine >= getNofLines() ) { lastLine = getNofLines()-1; TQString s = getString( lastLine ); endPos = s.length(); } m_selection.reset(); m_selection.start( firstLine, convertToPosOnScreen( getString(firstLine), startPos, m_pOptionDialog->m_tabSize ) ); m_selection.end( lastLine, convertToPosOnScreen( getString(lastLine), endPos, m_pOptionDialog->m_tabSize ) ); update(); } Overview::Overview( TQWidget* pParent, OptionDialog* pOptions ) : TQWidget( pParent, 0, WRepaintNoErase ) { m_pDiff3LineList = 0; m_pOptions = pOptions; m_bTripleDiff = false; m_eOverviewMode = eOMNormal; m_nofLines = 1; m_bPaintingAllowed = false; setFixedWidth(20); } void Overview::init( Diff3LineList* pDiff3LineList, bool bTripleDiff ) { m_pDiff3LineList = pDiff3LineList; m_bTripleDiff = bTripleDiff; m_pixmap.resize( TQSize(0,0) ); // make sure that a redraw happens update(); } void Overview::reset() { m_pDiff3LineList = 0; } void Overview::slotRedraw() { m_pixmap.resize( TQSize(0,0) ); // make sure that a redraw happens update(); } void Overview::setRange( int firstLine, int pageHeight ) { m_firstLine = firstLine; m_pageHeight = pageHeight; update(); } void Overview::setFirstLine( int firstLine ) { m_firstLine = firstLine; update(); } void Overview::setOverviewMode( e_OverviewMode eOverviewMode ) { m_eOverviewMode = eOverviewMode; slotRedraw(); } Overview::e_OverviewMode Overview::getOverviewMode() { return m_eOverviewMode; } void Overview::mousePressEvent( TQMouseEvent* e ) { int h = height()-1; int h1 = h * m_pageHeight / max2(1,m_nofLines)+3; if ( h>0 ) emit setLine( ( e->y() - h1/2 )*m_nofLines/h ); } void Overview::mouseMoveEvent( TQMouseEvent* e ) { mousePressEvent(e); } void Overview::setPaintingAllowed( bool bAllowPainting ) { if (m_bPaintingAllowed != bAllowPainting) { m_bPaintingAllowed = bAllowPainting; if ( m_bPaintingAllowed ) update(); else reset(); } } void Overview::drawColumn( TQPainter& p, e_OverviewMode eOverviewMode, int x, int w, int h, int nofLines ) { p.setPen(TQt::black); p.drawLine( x, 0, x, h ); if (nofLines==0) return; int line = 0; int oldY = 0; int oldConflictY = -1; int wrapLineIdx=0; Diff3LineList::const_iterator i; for( i = m_pDiff3LineList->begin(); i!= m_pDiff3LineList->end(); ) { const Diff3Line& d3l = *i; int y = h * (line+1) / nofLines; e_MergeDetails md; bool bConflict; bool bLineRemoved; int src; mergeOneLine( d3l, md, bConflict, bLineRemoved, src, !m_bTripleDiff ); TQColor c = m_pOptions->m_bgColor; bool bWhiteSpaceChange = false; //if( bConflict ) c=m_pOptions->m_colorForConflict; //else if ( eOverviewMode==eOMNormal ) { switch( md ) { case eDefault: case eNoChange: c = m_pOptions->m_bgColor; break; case eBAdded: case eBDeleted: case eBChanged: c = bConflict ? m_pOptions->m_colorForConflict : m_pOptions->m_colorB; bWhiteSpaceChange = d3l.bAEqB || d3l.bWhiteLineA && d3l.bWhiteLineB; break; case eCAdded: case eCDeleted: case eCChanged: bWhiteSpaceChange = d3l.bAEqC || d3l.bWhiteLineA && d3l.bWhiteLineC; c = bConflict ? m_pOptions->m_colorForConflict : m_pOptions->m_colorC; break; case eBCChanged: // conflict case eBCChangedAndEqual: // possible conflict case eBCDeleted: // possible conflict case eBChanged_CDeleted: // conflict case eCChanged_BDeleted: // conflict case eBCAdded: // conflict case eBCAddedAndEqual: // possible conflict c=m_pOptions->m_colorForConflict; break; default: assert(false); break; } } else if ( eOverviewMode==eOMAvsB ) { switch( md ) { case eDefault: case eNoChange: case eCAdded: case eCDeleted: case eCChanged: break; default: c = m_pOptions->m_colorForConflict; bWhiteSpaceChange = d3l.bAEqB || d3l.bWhiteLineA && d3l.bWhiteLineB; break; } } else if ( eOverviewMode==eOMAvsC ) { switch( md ) { case eDefault: case eNoChange: case eBAdded: case eBDeleted: case eBChanged: break; default: c = m_pOptions->m_colorForConflict; bWhiteSpaceChange = d3l.bAEqC || d3l.bWhiteLineA && d3l.bWhiteLineC; break; } } else if ( eOverviewMode==eOMBvsC ) { switch( md ) { case eDefault: case eNoChange: case eBCChangedAndEqual: case eBCDeleted: case eBCAddedAndEqual: break; default: c=m_pOptions->m_colorForConflict; bWhiteSpaceChange = d3l.bBEqC || d3l.bWhiteLineB && d3l.bWhiteLineC; break; } } if (!bWhiteSpaceChange || m_pOptions->m_bShowWhiteSpace ) { // Make sure that lines with conflict are not overwritten. if ( c == m_pOptions->m_colorForConflict ) { p.fillRect(x+1, oldY, w, max2(1,y-oldY), bWhiteSpaceChange ? TQBrush(c,TQt::Dense4Pattern) : TQBrush(c) ); oldConflictY = oldY; } else if ( c!=m_pOptions->m_bgColor && oldY>oldConflictY ) { p.fillRect(x+1, oldY, w, max2(1,y-oldY), bWhiteSpaceChange ? TQBrush(c,TQt::Dense4Pattern) : TQBrush(c) ); } } oldY = y; ++line; if ( m_pOptions->m_bWordWrap ) { ++wrapLineIdx; if(wrapLineIdx>=d3l.linesNeededForDisplay) { wrapLineIdx=0; ++i; } } else { ++i; } } } void Overview::paintEvent( TQPaintEvent* ) { if (m_pDiff3LineList==0 || !m_bPaintingAllowed ) return; int h = height()-1; int w = width(); if ( m_pixmap.size() != size() ) { if ( m_pOptions->m_bWordWrap ) { m_nofLines = 0; Diff3LineList::const_iterator i; for( i = m_pDiff3LineList->begin(); i!= m_pDiff3LineList->end(); ++i ) { m_nofLines += i->linesNeededForDisplay; } } else { m_nofLines = m_pDiff3LineList->size(); } m_pixmap.resize( size() ); TQPainter p(&m_pixmap); p.fillRect( rect(), m_pOptions->m_bgColor ); if ( !m_bTripleDiff || m_eOverviewMode == eOMNormal ) { drawColumn( p, eOMNormal, 0, w, h, m_nofLines ); } else { drawColumn( p, eOMNormal, 0, w/2, h, m_nofLines ); drawColumn( p, m_eOverviewMode, w/2, w/2, h, m_nofLines ); } } TQPainter painter( this ); painter.drawPixmap( 0,0, m_pixmap ); int y1 = h * m_firstLine / m_nofLines-1; int h1 = h * m_pageHeight / m_nofLines+3; painter.setPen(TQt::black); painter.drawRect( 1, y1, w-1, h1 ); } WindowTitleWidget::WindowTitleWidget(OptionDialog* pOptionDialog, TQWidget* pParent) :TQWidget(pParent) { m_pOptionDialog = pOptionDialog; //setAutoFillBackground(true); TQHBoxLayout* pHLayout = new TQHBoxLayout(this); pHLayout->setMargin(2); pHLayout->setSpacing(2); m_pLabel = new TQLabel(i18n("Output")+":", this); pHLayout->addWidget( m_pLabel ); m_pFileNameLineEdit = new TQLineEdit(this); pHLayout->addWidget( m_pFileNameLineEdit, 6 ); m_pFileNameLineEdit->installEventFilter( this ); m_pFileNameLineEdit->setReadOnly( true ); //m_pBrowseButton = new TQPushButton("..."); //pHLayout->addWidget( m_pBrowseButton, 0 ); //connect( m_pBrowseButton, TQT_SIGNAL(clicked()), this, TQT_SLOT(slotBrowseButtonClicked())); m_pModifiedLabel = new TQLabel(i18n("[Modified]"),this); pHLayout->addWidget( m_pModifiedLabel ); m_pModifiedLabel->setMinimumSize( m_pModifiedLabel->sizeHint() ); m_pModifiedLabel->setText(""); pHLayout->addStretch(1); m_pEncodingLabel = new TQLabel(i18n("Encoding for saving")+":",this); pHLayout->addWidget( m_pEncodingLabel ); m_pEncodingSelector = new TQComboBox(this); pHLayout->addWidget( m_pEncodingSelector, 3 ); setEncodings(0,0,0); } void WindowTitleWidget::setFileName( const TQString& fileName ) { m_pFileNameLineEdit->setText( TQDir::convertSeparators(fileName) ); } TQString WindowTitleWidget::getFileName() { return m_pFileNameLineEdit->text(); } void WindowTitleWidget::setEncodings( TQTextCodec* pCodecForA, TQTextCodec* pCodecForB, TQTextCodec* pCodecForC ) { m_pEncodingSelector->clear(); m_codecMap.clear(); // First sort codec names: std::map names; int i; for(i=0;;++i) { TQTextCodec* c = TQTextCodec::codecForIndex(i); if ( c==0 ) break; else names[TQString(c->name())]=c; } i=0; if ( pCodecForA ) { m_pEncodingSelector->insertItem( i18n("Codec from") + " A: " + pCodecForA->name(), i ); m_codecMap[i]=pCodecForA; ++i; } if ( pCodecForB ) { m_pEncodingSelector->insertItem( i18n("Codec from") + " B: " + pCodecForB->name(), i ); m_codecMap[i]=pCodecForB; ++i; } if ( pCodecForC ) { m_pEncodingSelector->insertItem( i18n("Codec from") + " C: " + pCodecForC->name(), i ); m_codecMap[i]=pCodecForC; ++i; } std::map::iterator it; for(it=names.begin();it!=names.end();++it) { m_pEncodingSelector->insertItem( it->first, i ); m_codecMap[i]=it->second; ++i; } m_pEncodingSelector->setMinimumSize( m_pEncodingSelector->sizeHint() ); if ( pCodecForC && pCodecForB && pCodecForA ) { if ( pCodecForA == pCodecForB ) m_pEncodingSelector->setCurrentItem( 2 ); // C else if ( pCodecForA == pCodecForC ) m_pEncodingSelector->setCurrentItem( 1 ); // B else m_pEncodingSelector->setCurrentItem( 2 ); // C } else if ( pCodecForA && pCodecForB ) m_pEncodingSelector->setCurrentItem( 1 ); // B else m_pEncodingSelector->setCurrentItem( 0 ); } TQTextCodec* WindowTitleWidget::getEncoding() { return m_codecMap[ m_pEncodingSelector->currentItem() ]; } void WindowTitleWidget::setEncoding(TQTextCodec* pEncoding) { m_pEncodingSelector->setCurrentText( TQString( pEncoding->name() ) ); } //void WindowTitleWidget::slotBrowseButtonClicked() //{ // TQString current = m_pFileNameLineEdit->text(); // // KURL newURL = KFileDialog::getSaveURL( current, 0, this, i18n("Select file (not saving yet)")); // if ( !newURL.isEmpty() ) // { // m_pFileNameLineEdit->setText( newURL.url() ); // } //} void WindowTitleWidget::slotSetModified( bool bModified ) { m_pModifiedLabel->setText( bModified ? i18n("[Modified]") : "" ); } bool WindowTitleWidget::eventFilter( TQObject* o, TQEvent* e ) { if ( e->type()==TQEvent::FocusIn || e->type()==TQEvent::FocusOut ) { TQPalette p = m_pLabel->palette(); TQColor c1 = m_pOptionDialog->m_fgColor; TQColor c2 = TQt::lightGray; if ( e->type()==TQEvent::FocusOut ) c2 = m_pOptionDialog->m_bgColor; p.setColor(TQColorGroup::Background, c2); setPalette( p ); p.setColor(TQColorGroup::Foreground, c1); m_pLabel->setPalette( p ); m_pEncodingLabel->setPalette( p ); m_pEncodingSelector->setPalette( p ); } if (o == m_pFileNameLineEdit && e->type()==TQEvent::Drop) { TQDropEvent* d = static_cast(e); if ( TQUriDrag::canDecode( d ) ) { TQStringList lst; TQUriDrag::decodeLocalFiles( d, lst ); if ( lst.count() > 0 ) { static_cast(TQT_TQWIDGET(o))->setText( lst[0] ); static_cast(TQT_TQWIDGET(o))->setFocus(); return true; } } } return false; } #include "mergeresultwindow.moc"