Archive for June 2008
submerge
I always liked the way the FileMerge utility on MacOSX connects a left and right file difference with a curved connection. I played with this a couple of years ago but since Qt3 did not support antialiasing out of the box I dropped it again. Without antialiasing a curved line does not look very good.
After spending some time improving the redrawing issues in submerge I mentioned in Qt4 (unfortunately there are still a few pixel glitches, but it is a lot better than before) I implemented a curved connection with antialiasing. A reward for fixing most of the redrawing issues ;-)
Since Qt4 supports antialiasing simply by setting a render hint on the QPainter class it was quite easy to implement this time. Most work was to get access to proper size information for a single difference block. That is how many lines in the left file (OriginalLength) and how many lines in the right file (ModifiedLength) create a difference block and have to be connected. Luckily this information was already calculated by the diff code and I only had to add it to the existing BlockInfo info class.
The drawing code then goes like this:
... Line lline = _left->getLine(curLine); Line rline = _right->getLine(curLine); const BlockInfo& lb = _model->getInfo( lline.getBlockNr() ).getBlockInfo(); ... svn::Offset sl = lb.getStart() + lb.getOriginalLength(); svn::Offset sr = lb.getStart() + lb.getModifiedLength(); ... int top = tpc.getLineY((int)lb.getStart()); int botLeft = tpc.getLineY((int)sl); int botRight = tpc.getLineY((int)sr); QPainterPath path; path.moveTo(0,top); path.lineTo(0,botLeft); path.cubicTo(lnDefWidth-5,botLeft, 5,botRight, lnDefWidth,botRight); path.lineTo(lnDefWidth,top); path.lineTo(0,top); DiffInfo& info = _model->getInfo(block); QColor lcolor = getBgColor( lline.getType(), info.getMergeType(), true ); QColor rcolor = getBgColor( rline.getType(), info.getMergeType(), false ); QLinearGradient gradient(0,0,lnDefWidth,0); gradient.setColorAt(0, lcolor ); gradient.setColorAt(1, rcolor ); QBrush brush(gradient); pp.setRenderHint(QPainter::Antialiasing); pp.setPen(Qt::NoPen); pp.setBrush(brush); pp.drawPath(path); ...
Above you can see the most interesting parts of the code. I have omitted the code that takes care of painting the connection only once for every block.
If the current line is part of a difference block I calculate the bottom y pixel position for the left block part based on the OriginalLength and the right block part based on ModifiedLength.
Then I create a cubic PainterPath that describes the curved connection. lnDefWidth is a constant value the represents the width of the glue area between the left and right file.
Next I create a gradient between the background colors of the left and the right file to get a smooth transition if the colors are different (on merging a selected left or right block gets a different color to give feedback which file was choosen for the merged file).
Finally the connection gets drawn with antialiasing to get a smooth curve.
Below is a submerge screenshot showing what it looks like. The two files are unrelated to get a lot of differences :)