subcommanderblog.wordpress.com

Subcommander Development Blog

Archive for the ‘submerge’ Category

Improving submerge wheel scrolling

leave a comment »

submerge (subcommanders diff and merge tool) uses the mouse wheel to quickly jump from one difference to the next or the previous difference. Let’s call this jump scrolling.

To achieve this I handle the mouse wheel event in the parent widget of the of the widget that really displays the files. To do normal scrolling, i.e. line by line, one has to move the mouse above the scroll bar which handles the mouse wheel in the usual way.

Sometimes this is a bit annoying if you just like to scroll a few lines up or down to review the visible difference. You move the wheel and … ups, you are at another difference in the file, maybe several pages from the difference you just reviewed. So the idea is to allow normal scrolling by using a keyboard modifier. Maybe I will even switch the default, and change jump scrolling to shift + wheel.

I’m currently reading “Clean Code” from Robert C. Martin, although it doesn’t offer new revolutionary ideas, it is always good to be remembered of things you already know but don’t follow consistently.  So let’s see if I can also improve the readability of the code, at least a bit :-).

Here is the code as it was before. A few notes about the code:

  • Diff3Widget is the parent widget that shows all versions of the file we diff  (old file, new file etc.).
  • _diffInfo is a member that holds the actual diff information.
  • the diff information contains several block. A block represents a number of equal lines in the two file or a number lines that are different in both files. That’s the reason why diff number and block number are not the same. Hmm, not sure if this is clear ;-)

 

void Diff3Widget::wheelEvent( QWheelEvent* e )
{
  if( ! _diffInfo )
  {
    e->ignore();
    return;
  }

  //printf( "wheel delta: %d (%p)\n", e->delta(), e );

  int b = 0;

  if( e->delta() > 0 )
  {
    // forward, down
    if( _diffInfo->hasPrevDiff() )
    {
      b = _diffInfo->prevDiff();
    }
  }
  else
  {
    // backward, up
    if( _diffInfo->hasNextDiff() )
    {
      b = _diffInfo->nextDiff();
    }
  }

  if( b == 0 )
  {
    e->ignore();
    return;
  }

  jumpToBlock( b );
  setActiveDiff( _diffInfo->getActiveDiff() );

  emit diffChanged( _diffInfo->getActiveDiff() );

  e->accept();
}

 

I ended up with the code below. It uses two members that display the files in submerge (_single is the original file (the third file) if any and _double which contains the left and right file) to do the normal scrolling. I also dropped the _diffInfo pointer check. I had already a null implementation for the diff info and I now initialize the member with it so I do not have to check for a NULL pointer.

 

void Diff3Widget::wheelEvent( QWheelEvent* e )
{
  if( shouldScrollNormal(e->modifiers()) )
    scrollNormal(e->delta());
  else
    scrollToDiff(e->delta());

  e->accept();
}

void Diff3Widget::scrollNormal( int delta )
{
  QScrollBar* sbSingle = _single->getVScrollBar();
  QScrollBar* sbDouble = _double->getVScrollBar();
  sbSingle->setValue(sbSingle->value()-delta);
  sbDouble->setValue(sbDouble->value()-delta);
}

bool Diff3Widget::shouldScrollNormal( Qt::KeyboardModifiers modifiers )
{
  if( modifiers == Qt::ShiftModifier )
    return true;

  if( _diffInfo->getDiffCnt() == 0 )
    return true;

  return false;
}

void Diff3Widget::scrollToDiff( int delta )
{
  if( delta > 0 )
    scrollToPrevDiff();
  else
    scrollToNextDiff();
}

void Diff3Widget::scrollToPrevDiff()
{
  if( !_diffInfo->hasPrevDiff() )
    return;

  jumpToBlock( _diffInfo->prevDiff() );
  setActiveDiff( _diffInfo->getActiveDiff() );
}

void Diff3Widget::scrollToNextDiff()
{
  if( !_diffInfo->hasNextDiff() )
    return;

  jumpToBlock( _diffInfo->nextDiff() );
  setActiveDiff( _diffInfo->getActiveDiff() );
}

 

I split up the original method into several methods and reduced the code in each method to what is interesting at that level. Clearly better than before but I think there are still a few things you can suggest to improve the code. Anything?

What’s missing is some test code… how would you test this?

Advertisements

Written by hauner

Saturday, 24 January, 2009 at 19:17

Posted in submerge

Subcommander FAQ

leave a comment »

Someone was searching the web for ‘subcommander’ and ‘proxy’ and came here. Looks like it is not obvious how to run subcommander using a http proxy. I guess that is right :(

So I started a Subcommander FAQ using the new wiki feature on tigris.org. The faq is here: http://subcommander.tigris.org/wiki/faq and I added a first question and of course an answer  ;-)

Written by hauner

Saturday, 17 January, 2009 at 18:37

Posted in subcommander, submerge

Tagged with

Linux .desktop files and the Subcommander SVG Logo

leave a comment »

While adding Linux .desktop files for subcommander and submerge I got a bit sidetracked and tried to convert the Subcommander logo to an svg file. The original file is a psp vector image file (from an older PaintShopPro version). At first I tried to find a file format that would let me export it and import it again into Inkscape (on a Mac). Shouldn’t be to difficult.

No way! Both programs understand a number of different formats but nothing that really worked. Because I did not want to spend my day trying different image converters I simply started to re-create the logo in Inkscape. Its just two bezier curves. After a while of fiddeling around with Inkscape (I have never done anything usfull with Inkscape before) I finally created something that is not 100% the same but at least 98% ;-)

How do you like the white logo? (a 265×265 png exported from Inkscape)

logowhite

White Subcommander Logo

Back to the .desktop files. I don’t run Linux so anyone has an idea where to install them or the icons? Google came up with something like the list below but I’m not sure if this is universal for all Linux distributions or if it the same for KDE and Gnome.

  • /usr/share/applications/subcommander.desktop
  • /usr/share/applications/submerge.desktop
  • /usr/share/icons/???

So if you know, let me know :)

Written by hauner

Thursday, 1 January, 2009 at 19:11

Posted in subcommander, submerge

submerge

leave a comment »

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 :)

submerge with a curved connection between left and right file

MacOSX: submerge with a curved connection between left and right file

Written by hauner

Sunday, 22 June, 2008 at 11:25

Posted in submerge

Tagged with