All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jmeld.ui.BufferDiffPanel Maven / Gradle / Ivy

Go to download

JMeld is a visual diff and merge tool which is very much inspired after the fantastic 'meld', a gnome program program. It is different in a number of ways: 1. It doesn't have (yet) the many features of meld. 2. For now only the filemerge is working (no directorydiff, no subversion/cvs diff) 3. It is written in java and thus platform independent. 4. It is created to be very fast with large documents.

The newest version!
/*
   JMeld is a visual diff and merge tool.
   Copyright (C) 2007  Kees Kuip
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor,
   Boston, MA  02110-1301  USA
 */
package org.jmeld.ui;

import com.jgoodies.forms.layout.*;

import org.jmeld.*;
import org.jmeld.diff.*;
import org.jmeld.ui.search.*;
import org.jmeld.ui.text.*;
import org.jmeld.util.*;
import org.jmeld.util.node.*;

import javax.swing.*;
import javax.swing.text.*;

import java.awt.*;
import java.util.*;
import java.util.List;

public class BufferDiffPanel
    extends AbstractContentPanel
{
  public static final int LEFT = 0;
  public static final int MIDDLE = 1;
  public static final int RIGHT = 2;
  private JMeldPanel mainPanel;
  private FilePanel[] filePanels;
  private JMDiffNode diffNode;
  int filePanelSelectedIndex = -1;
  private JMRevision currentRevision;
  private JMDelta selectedDelta;
  private int selectedLine;
  private ScrollSynchronizer scrollSynchronizer;
  private JMDiff diff;

  BufferDiffPanel(JMeldPanel mainPanel)
  {
    this.mainPanel = mainPanel;

    diff = new JMDiff();
    filePanels = new FilePanel[3];

    init();

    setFocusable(true);
  }

  public void setDiffNode(JMDiffNode diffNode)
  {
    BufferNode bnLeft;
    BufferNode bnRight;

    this.diffNode = diffNode;

    bnLeft = diffNode.getBufferNodeLeft();
    bnRight = diffNode.getBufferNodeRight();

    setBufferDocuments(bnLeft == null ? null : bnLeft.getDocument(),
      bnRight == null ? null : bnRight.getDocument(), diffNode.getDiff(),
      diffNode.getRevision());
  }

  private void setBufferDocuments(BufferDocumentIF bd1, BufferDocumentIF bd2,
      JMDiff diff, JMRevision revision)
  {
    this.diff = diff;

    currentRevision = revision;

    if (bd1 != null)
    {
      filePanels[LEFT].setBufferDocument(bd1);
    }

    if (bd2 != null)
    {
      filePanels[RIGHT].setBufferDocument(bd2);
    }

    if (bd1 != null && bd2 != null)
    {
      filePanels[LEFT].updateFileLabel(bd1.getName(), bd2.getName());
      filePanels[RIGHT].updateFileLabel(bd2.getName(), bd1.getName());
    }

    if (bd1 != null && bd2 != null)
    {
      reDisplay();
    }
  }

  private void reDisplay()
  {
    for (FilePanel fp : filePanels)
    {
      if (fp != null)
      {
        fp.reDisplay();
      }
    }

    mainPanel.repaint();
  }

  public String getTitle()
  {
    String title;
    BufferDocumentIF bd;
    List titles;

    title = "";

    titles = new ArrayList();
    for (FilePanel filePanel : filePanels)
    {
      if (filePanel == null)
      {
        continue;
      }

      bd = filePanel.getBufferDocument();
      if (bd == null)
      {
        continue;
      }

      title = bd.getShortName();
      if (StringUtil.isEmpty(title))
      {
        continue;
      }

      titles.add(title);
    }

    title = "";
    if (titles.size() == 1)
    {
      title = titles.get(0);
    }
    else
    {
      if (titles.get(0).equals(titles.get(1)))
      {
        title = titles.get(0);
      }
      else
      {
        title = titles.get(0) + "-" + titles.get(1);
      }
    }

    return title;
  }

  public boolean revisionChanged(JMDocumentEvent de)
  {
    FilePanel fp;
    BufferDocumentIF bd1;
    BufferDocumentIF bd2;

    if (currentRevision == null)
    {
      diff();
    }
    else
    {
      fp = getFilePanel(de.getDocument());
      if (fp == null)
      {
        return false;
      }

      bd1 = filePanels[LEFT].getBufferDocument();
      bd2 = filePanels[RIGHT].getBufferDocument();

      if (!currentRevision.update(bd1 != null ? bd1.getLines() : null,
        bd2 != null ? bd2.getLines() : null, fp == filePanels[LEFT], de
            .getStartLine(), de.getNumberOfLines()))
      {
        return false;
      }

      reDisplay();
    }

    return true;
  }

  private FilePanel getFilePanel(AbstractBufferDocument document)
  {
    for (FilePanel fp : filePanels)
    {
      if (fp == null)
      {
        continue;
      }

      if (fp.getBufferDocument() == document)
      {
        return fp;
      }
    }

    return null;
  }

  public void diff()
  {
    BufferDocumentIF bd1;
    BufferDocumentIF bd2;

    bd1 = filePanels[LEFT].getBufferDocument();
    bd2 = filePanels[RIGHT].getBufferDocument();

    if (bd1 != null && bd2 != null)
    {
      try
      {
        currentRevision = diff.diff(bd1.getLines(), bd2.getLines(), diffNode
            .getIgnore());

        reDisplay();
      }
      catch (Exception ex)
      {
        ex.printStackTrace();
      }
    }
  }

  private void init()
  {
    FormLayout layout;
    String columns;
    String rows;
    CellConstraints cc;

    columns = "3px, pref, 3px, 0:grow, 5px, min, 60px, 0:grow, 25px, min, 3px, pref, 3px";
    rows = "6px, pref, 3px, fill:0:grow, pref";
    layout = new FormLayout(columns, rows);
    cc = new CellConstraints();

    setLayout(layout);

    filePanels[LEFT] = new FilePanel(this, BufferDocumentIF.ORIGINAL, LEFT);
    filePanels[RIGHT] = new FilePanel(this, BufferDocumentIF.REVISED, RIGHT);

    // panel for file1
    add(new RevisionBar(this, filePanels[LEFT], true), cc.xy(2, 4));
    add(filePanels[LEFT].getSaveButton(), cc.xy(2, 2));
    add(filePanels[LEFT].getFileLabel(), cc.xyw(4, 2, 3));
    add(filePanels[LEFT].getScrollPane(), cc.xyw(4, 4, 3));
    add(filePanels[LEFT].getFilePanelBar(), cc.xyw(4, 5, 3));

    add(new DiffScrollComponent(this, LEFT, RIGHT), cc.xy(7, 4));

    // panel for file2
    add(new RevisionBar(this, filePanels[RIGHT], false), cc.xy(12, 4));
    add(filePanels[RIGHT].getFileLabel(), cc.xyw(8, 2, 3));
    add(filePanels[RIGHT].getScrollPane(), cc.xyw(8, 4, 3));
    add(filePanels[RIGHT].getSaveButton(), cc.xy(12, 2));
    add(filePanels[RIGHT].getFilePanelBar(), cc.xyw(8, 5, 3));

    scrollSynchronizer = new ScrollSynchronizer(this, filePanels[LEFT],
        filePanels[RIGHT]);

    setSelectedPanel(filePanels[LEFT]);
  }

  void toNextDelta(boolean next)
  {
    if (next)
    {
      doDown();
    }
    else
    {
      doUp();
    }
  }

  JMRevision getCurrentRevision()
  {
    return currentRevision;
  }

  @Override
  public boolean checkSave()
  {
    SavePanelDialog dialog;

    if (!isSaveEnabled())
    {
      return true;
    }

    dialog = new SavePanelDialog(mainPanel);
    for (FilePanel filePanel : filePanels)
    {
      if (filePanel != null)
      {
        dialog.add(filePanel.getBufferDocument());
      }
    }

    dialog.show();

    if (dialog.isOK())
    {
      dialog.doSave();
      return true;
    }

    return false;
  }

  @Override
  public void doSave()
  {
    BufferDocumentIF document;

    for (FilePanel filePanel : filePanels)
    {
      if (filePanel == null)
      {
        continue;
      }

      if (!filePanel.isDocumentChanged())
      {
        continue;
      }

      document = filePanel.getBufferDocument();

      try
      {
        document.write();
      }
      catch (JMeldException ex)
      {
        ex.printStackTrace();
        JOptionPane.showMessageDialog(mainPanel, "Can't write file"
                                                 + document.getName(),
          "Problem writing file", JOptionPane.ERROR_MESSAGE);
      }
    }
  }

  @Override
  public boolean isSaveEnabled()
  {
    for (FilePanel filePanel : filePanels)
    {
      if (filePanel != null && filePanel.isDocumentChanged())
      {
        return true;
      }
    }

    return false;
  }

  @Override
  public void doStopSearch()
  {
    for (FilePanel filePanel : filePanels)
    {
      if (filePanel != null)
      {
        filePanel.doStopSearch();
      }
    }
  }

  public SearchCommand getSearchCommand()
  {
    return mainPanel.getSearchCommand();
  }

  @Override
  public SearchHits doSearch()
  {
    FilePanel fp;
    SearchHits searchHits;

    fp = getSelectedPanel();
    if (fp == null)
    {
      return null;
    }

    searchHits = fp.doSearch();

    scrollToSearch(fp, searchHits);

    return searchHits;
  }

  @Override
  public void doNextSearch()
  {
    FilePanel fp;
    SearchHits searchHits;

    fp = getSelectedPanel();
    if (fp == null)
    {
      return;
    }

    searchHits = fp.getSearchHits();
    searchHits.next();
    fp.reDisplay();

    scrollToSearch(fp, searchHits);
  }

  @Override
  public void doPreviousSearch()
  {
    FilePanel fp;
    SearchHits searchHits;

    fp = getSelectedPanel();
    if (fp == null)
    {
      return;
    }

    searchHits = fp.getSearchHits();
    searchHits.previous();
    fp.reDisplay();

    scrollToSearch(fp, searchHits);
  }

  @Override
  public void doRefresh()
  {
    diff();
  }

  @Override
  public void doMergeMode(boolean mergeMode)
  {
    for (FilePanel fp : filePanels)
    {
      if (fp != null)
      {
        fp.getEditor().setFocusable(!mergeMode);
      }
    }
  }

  private void scrollToSearch(FilePanel fp, SearchHits searchHits)
  {
    SearchHit currentHit;
    int line;

    if (searchHits == null)
    {
      return;
    }

    currentHit = searchHits.getCurrent();
    if (currentHit != null)
    {
      line = currentHit.getLine();

      scrollSynchronizer.scrollToLine(fp, line);
      setSelectedLine(line);
    }
  }

  private FilePanel getSelectedPanel()
  {
    if (filePanelSelectedIndex >= 0
        && filePanelSelectedIndex < filePanels.length)
    {
      return filePanels[filePanelSelectedIndex];
    }

    return null;
  }

  void setSelectedPanel(FilePanel fp)
  {
    int index;

    index = -1;
    for (int i = 0; i < filePanels.length; i++)
    {
      if (filePanels[i] == fp)
      {
        index = i;
      }
    }

    if (index != filePanelSelectedIndex)
    {
      if (filePanelSelectedIndex != -1)
      {
        filePanels[filePanelSelectedIndex].setSelected(false);
      }

      filePanelSelectedIndex = index;

      if (filePanelSelectedIndex != -1)
      {
        filePanels[filePanelSelectedIndex].setSelected(true);
      }
    }
  }

  @Override
  public void checkActions()
  {
    mainPanel.checkActions();
  }

  @Override
  public void doLeft()
  {
    runChange(RIGHT, LEFT);
  }

  @Override
  public void doRight()
  {
    runChange(LEFT, RIGHT);
  }

  void runChange(int fromPanelIndex, int toPanelIndex)
  {
    JMDelta delta;
    BufferDocumentIF fromBufferDocument;
    BufferDocumentIF toBufferDocument;
    PlainDocument from;
    String s;
    int fromLine;
    int fromOffset;
    int toOffset;
    int size;
    JMChunk fromChunk;
    JMChunk toChunk;
    JTextComponent toEditor;

    delta = getSelectedDelta();
    if (delta == null)
    {
      return;
    }

    // Some sanity checks.
    if (fromPanelIndex < 0 || fromPanelIndex >= filePanels.length)
    {
      return;
    }

    if (toPanelIndex < 0 || toPanelIndex >= filePanels.length)
    {
      return;
    }

    try
    {
      fromBufferDocument = filePanels[fromPanelIndex].getBufferDocument();
      toBufferDocument = filePanels[toPanelIndex].getBufferDocument();

      // TODO: delta and revision are not yet ready for 3-way merge!
      if (fromPanelIndex < toPanelIndex)
      {
        fromChunk = delta.getOriginal();
        toChunk = delta.getRevised();
      }
      else
      {
        fromChunk = delta.getRevised();
        toChunk = delta.getOriginal();
      }
      toEditor = filePanels[toPanelIndex].getEditor();

      if (fromBufferDocument == null || toBufferDocument == null)
      {
        return;
      }

      fromLine = fromChunk.getAnchor();
      size = fromChunk.getSize();
      fromOffset = fromBufferDocument.getOffsetForLine(fromLine);
      if (fromOffset < 0)
      {
        return;
      }

      toOffset = fromBufferDocument.getOffsetForLine(fromLine + size);
      if (toOffset < 0)
      {
        return;
      }

      from = fromBufferDocument.getDocument();
      s = from.getText(fromOffset, toOffset - fromOffset);

      fromLine = toChunk.getAnchor();
      size = toChunk.getSize();
      fromOffset = toBufferDocument.getOffsetForLine(fromLine);
      if (fromOffset < 0)
      {
        return;
      }

      toOffset = toBufferDocument.getOffsetForLine(fromLine + size);
      if (toOffset < 0)
      {
        return;
      }

      getUndoHandler().start("replace");
      toEditor.setSelectionStart(fromOffset);
      toEditor.setSelectionEnd(toOffset);
      toEditor.replaceSelection(s);
      getUndoHandler().end("replace");

      setSelectedDelta(null);
      setSelectedLine(delta.getOriginal().getAnchor());
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }

  void runDelete(int fromPanelIndex, int toPanelIndex)
  {
    JMDelta delta;
    BufferDocumentIF bufferDocument;
    PlainDocument document;
    String s;
    int fromLine;
    int fromOffset;
    int toOffset;
    int size;
    JMChunk chunk;
    JTextComponent toEditor;

    try
    {
      delta = getSelectedDelta();
      if (delta == null)
      {
        return;
      }

      // Some sanity checks.
      if (fromPanelIndex < 0 || fromPanelIndex >= filePanels.length)
      {
        return;
      }

      if (toPanelIndex < 0 || toPanelIndex >= filePanels.length)
      {
        return;
      }

      bufferDocument = filePanels[fromPanelIndex].getBufferDocument();
      if (fromPanelIndex < toPanelIndex)
      {
        chunk = delta.getOriginal();
      }
      else
      {
        chunk = delta.getRevised();
      }
      toEditor = filePanels[fromPanelIndex].getEditor();

      if (bufferDocument == null)
      {
        return;
      }

      document = bufferDocument.getDocument();
      fromLine = chunk.getAnchor();
      size = chunk.getSize();
      fromOffset = bufferDocument.getOffsetForLine(fromLine);
      if (fromOffset < 0)
      {
        return;
      }

      toOffset = bufferDocument.getOffsetForLine(fromLine + size);
      if (toOffset < 0)
      {
        return;
      }

      getUndoHandler().start("remove");
      toEditor.setSelectionStart(fromOffset);
      toEditor.setSelectionEnd(toOffset);
      toEditor.replaceSelection("");
      getUndoHandler().end("remove");

      setSelectedDelta(null);
      setSelectedLine(delta.getOriginal().getAnchor());
    }
    catch (Exception ex)
    {
      ex.printStackTrace();
    }
  }

  @Override
  public void doDown()
  {
    JMDelta d;
    JMDelta sd;
    List deltas;
    int index;

    if (currentRevision == null)
    {
      return;
    }

    deltas = currentRevision.getDeltas();
    sd = getSelectedDelta();
    index = deltas.indexOf(sd);
    if (index == -1 || sd.getOriginal().getAnchor() != selectedLine)
    {
      // Find the delta that would have been next to the 
      //   disappeared delta:
      d = null;
      for (JMDelta delta : deltas)
      {
        d = delta;
        if (delta.getOriginal().getAnchor() > selectedLine)
        {
          break;
        }
      }

      setSelectedDelta(d);
    }
    else
    {
      // Select the next delta if there is any.
      if (index + 1 < deltas.size())
      {
        setSelectedDelta(deltas.get(index + 1));
      }
    }

    showSelectedDelta();
  }

  @Override
  public void doUp()
  {
    JMDelta d;
    JMDelta sd;
    JMDelta previousDelta;
    List deltas;
    int index;

    if (currentRevision == null)
    {
      return;
    }

    deltas = currentRevision.getDeltas();
    sd = getSelectedDelta();
    index = deltas.indexOf(sd);
    if (index == -1 || sd.getOriginal().getAnchor() != selectedLine)
    {
      // Find the delta that would have been previous to the 
      //   disappeared delta:
      d = null;
      previousDelta = null;
      for (JMDelta delta : deltas)
      {
        d = delta;
        if (delta.getOriginal().getAnchor() > selectedLine)
        {
          if (previousDelta != null)
          {
            d = previousDelta;
          }
          break;
        }

        previousDelta = delta;
      }

      setSelectedDelta(d);
    }
    else
    {
      // Select the next delta if there is any.
      if (index - 1 >= 0)
      {
        setSelectedDelta(deltas.get(index - 1));
      }
    }
    showSelectedDelta();
  }

  public void doGotoDelta(JMDelta delta)
  {
    setSelectedDelta(delta);
    showSelectedDelta();
  }

  public void doGotoLine(int line)
  {
    BufferDocumentIF bd;
    int offset;
    int startOffset;
    int endOffset;
    JViewport viewport;
    JTextComponent editor;
    Point p;
    FilePanel fp;
    Rectangle rect;

    setSelectedLine(line);

    fp = getFilePanel(0);

    bd = fp.getBufferDocument();
    if (bd == null)
    {
      return;
    }

    offset = bd.getOffsetForLine(line);
    viewport = fp.getScrollPane().getViewport();
    editor = fp.getEditor();

    // Don't go anywhere if the line is already visible.
    rect = viewport.getViewRect();
    startOffset = editor.viewToModel(rect.getLocation());
    endOffset = editor.viewToModel(new Point(rect.x, rect.y + rect.height));
    if (offset >= startOffset && offset <= endOffset)
    {
      return;
    }

    try
    {
      p = editor.modelToView(offset).getLocation();
      p.x = 0;

      viewport.setViewPosition(p);
    }
    catch (BadLocationException ex)
    {
    }
  }

  @Override
  public void doZoom(boolean direction)
  {
    JTextComponent c;
    Font font;
    float size;
    Zoom zoom;

    for (FilePanel p : filePanels)
    {
      if (p == null)
      {
        continue;
      }

      c = p.getEditor();

      zoom = (Zoom) c.getClientProperty("JMeld.zoom");
      if (zoom == null)
      {
        // Save the orginal font because that's the font which will
        //   give the derived font.
        zoom = new Zoom();
        zoom.font = c.getFont();
        c.putClientProperty("JMeld.zoom", zoom);
      }

      size = c.getFont().getSize() + (direction ? 1.0f : -1.0f);
      size = size > 100.0f ? 100.0f : size;
      size = size < 5.0f ? 5.0f : size;

      c.setFont(zoom.font.deriveFont(size));
    }
  }

  @Override
  public void doGoToSelected()
  {
    showSelectedDelta();
  }

  @Override
  public void doGoToFirst()
  {
    JMDelta d;
    List deltas;

    if (currentRevision == null)
    {
      return;
    }

    deltas = currentRevision.getDeltas();
    if (deltas.size() > 0)
    {
      setSelectedDelta(deltas.get(0));
      showSelectedDelta();
    }
  }

  @Override
  public void doGoToLast()
  {
    JMDelta d;
    List deltas;

    if (currentRevision == null)
    {
      return;
    }

    deltas = currentRevision.getDeltas();
    if (deltas.size() > 0)
    {
      setSelectedDelta(deltas.get(deltas.size() - 1));
      showSelectedDelta();
    }
  }

  @Override
  public void doGoToLine(int line)
  {
    FilePanel fp;

    fp = getSelectedPanel();
    if (fp == null)
    {
      return;
    }

    scrollSynchronizer.scrollToLine(fp, line);
    setSelectedLine(line);
  }

  class Zoom
  {
    Font font;
  }

  void setSelectedDelta(JMDelta delta)
  {
    selectedDelta = delta;
    setSelectedLine(delta == null ? 0 : delta.getOriginal().getAnchor());
  }

  private void setSelectedLine(int line)
  {
    selectedLine = line;
  }

  private void showSelectedDelta()
  {
    JMDelta delta;

    delta = getSelectedDelta();
    if (delta == null)
    {
      return;
    }

    scrollSynchronizer.showDelta(delta);
  }

  public JMDelta getSelectedDelta()
  {
    List deltas;

    if (currentRevision == null)
    {
      return null;
    }

    deltas = currentRevision.getDeltas();
    if (deltas.size() == 0)
    {
      return null;
    }

    return selectedDelta;
  }

  FilePanel getFilePanel(int index)
  {
    if (index < 0 || index > filePanels.length)
    {
      return null;
    }

    return filePanels[index];
  }

  @Override
  public String getSelectedText()
  {
    FilePanel fp;

    fp = getSelectedPanel();
    if (fp == null)
    {
      return null;
    }

    return fp.getSelectedText();
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy