blob: e6e83c4db11311759d6dcb06ba953374919fe744 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.compare.contentmergeviewer;
import java.util.ResourceBundle;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.ui.IKeyBindingService;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.util.*;
import org.eclipse.jface.action.*;
import org.eclipse.jface.dialogs.*;
import org.eclipse.jface.viewers.ContentViewer;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.compare.*;
import org.eclipse.compare.structuremergeviewer.*;
import org.eclipse.compare.internal.*;
/**
* An abstract compare and merge viewer with two side-by-side content areas
* and an optional content area for the ancestor. The implementation makes no
* assumptions about the content type.
* <p>
* <code>ContentMergeViewer</code>
* <ul>
* <li>implements the overall layout and defines hooks so that subclasses
* can easily provide an implementation for a specific content type,
* <li>implements the UI for making the areas resizable,
* <li>has an action for controlling whether the ancestor area is visible or not,
* <li>has actions for copying one side of the input to the other side,
* <li>tracks the dirty state of the left and right sides and send out notification
* on state changes.
* </ul>
* A <code>ContentMergeViewer</code> accesses its
* model by means of a content provider which must implement the
* <code>IMergeViewerContentProvider</code> interface.
* </p>
* <p>
* Clients may wish to use the standard concrete subclass <code>TextMergeViewer</code>,
* or define their own subclass.
*
* @see IMergeViewerContentProvider
* @see TextMergeViewer
*/
public abstract class ContentMergeViewer extends ContentViewer
implements IPropertyChangeNotifier, ISavable {
class SaveAction extends MergeViewerAction {
SaveAction(boolean left) {
super(true, false, false);
Utilities.initAction(this, getResourceBundle(), "action.save."); //$NON-NLS-1$
}
public void run() {
saveContent(getInput());
}
};
/**
* Property names.
*/
private static final String ANCESTOR_ENABLED= ComparePreferencePage.INITIALLY_SHOW_ANCESTOR_PANE;
/* package */ static final int HORIZONTAL= 1;
/* package */ static final int VERTICAL= 2;
static final double HSPLIT= 0.5;
static final double VSPLIT= 0.3;
private class ContentMergeViewerLayout extends Layout {
public Point computeSize(Composite c, int w, int h, boolean force) {
return new Point(100, 100);
}
public void layout(Composite composite, boolean force) {
// determine some derived sizes
int headerHeight= fLeftLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
Rectangle r= composite.getClientArea();
int centerWidth= getCenterWidth();
int width1= (int)((r.width-centerWidth)*fHSplit);
int width2= r.width-width1-centerWidth;
int height1= 0;
int height2= 0;
if (fAncestorEnabled && fShowAncestor) {
height1= (int)((r.height-(2*headerHeight))*fVSplit);
height2= r.height-(2*headerHeight)-height1;
} else {
height1= 0;
height2= r.height-headerHeight;
}
int y= 0;
if (fAncestorEnabled && fShowAncestor) {
fAncestorLabel.setBounds(0, y, r.width, headerHeight);
fAncestorLabel.setVisible(true);
y+= headerHeight;
handleResizeAncestor(0, y, r.width, height1);
y+= height1;
} else {
fAncestorLabel.setVisible(false);
handleResizeAncestor(0, 0, 0, 0);
}
fLeftLabel.getSize(); // without this resizing would not always work
if (centerWidth > 3) {
fLeftLabel.setBounds(0, y, width1+1, headerHeight);
fDirectionLabel.setVisible(true);
fDirectionLabel.setBounds(width1+1, y, centerWidth-1, headerHeight);
fRightLabel.setBounds(width1+centerWidth, y, width2, headerHeight);
} else {
fLeftLabel.setBounds(0, y, width1, headerHeight);
fDirectionLabel.setVisible(false);
fRightLabel.setBounds(width1, y, r.width-width1, headerHeight);
}
y+= headerHeight;
if (fCenter != null && !fCenter.isDisposed())
fCenter.setBounds(width1, y, centerWidth, height2);
handleResizeLeftRight(0, y, width1, centerWidth, width2, height2);
}
}
class Resizer extends MouseAdapter implements MouseMoveListener {
Control fControl;
int fX, fY;
int fWidth1, fWidth2;
int fHeight1, fHeight2;
int fDirection;
boolean fLiveResize;
boolean fIsDown;
public Resizer(Control c, int dir) {
fDirection= dir;
fControl= c;
fLiveResize= !(fControl instanceof Sash);
fControl.addMouseListener(this);
fControl.addMouseMoveListener(this);
fControl.addDisposeListener(
new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
fControl= null;
}
}
);
}
public void mouseDoubleClick(MouseEvent e) {
if ((fDirection & HORIZONTAL) != 0)
fHSplit= HSPLIT;
if ((fDirection & VERTICAL) != 0)
fVSplit= VSPLIT;
fComposite.layout(true);
}
public void mouseDown(MouseEvent e) {
Composite parent= fControl.getParent();
Point s= parent.getSize();
Point as= fAncestorLabel.getSize();
Point ys= fLeftLabel.getSize();
Point ms= fRightLabel.getSize();
fWidth1= ys.x;
fWidth2= ms.x;
fHeight1= fLeftLabel.getLocation().y-as.y;
fHeight2= s.y-(fLeftLabel.getLocation().y+ys.y);
fX= e.x;
fY= e.y;
fIsDown= true;
}
public void mouseUp(MouseEvent e) {
fIsDown= false;
if (!fLiveResize)
resize(e);
}
public void mouseMove(MouseEvent e) {
if (fIsDown && fLiveResize)
resize(e);
}
private void resize(MouseEvent e) {
int dx= e.x-fX;
int dy= e.y-fY;
int centerWidth= fCenter.getSize().x;
if (fWidth1 + dx > centerWidth && fWidth2 - dx > centerWidth) {
fWidth1+= dx;
fWidth2-= dx;
if ((fDirection & HORIZONTAL) != 0)
fHSplit= (double)fWidth1/(double)(fWidth1+fWidth2);
}
if (fHeight1 + dy > centerWidth && fHeight2 - dy > centerWidth) {
fHeight1+= dy;
fHeight2-= dy;
if ((fDirection & VERTICAL) != 0)
fVSplit= (double)fHeight1/(double)(fHeight1+fHeight2);
}
fComposite.layout(true);
fControl.getDisplay().update();
}
};
/** Style bits for top level composite */
private int fStyles;
private ResourceBundle fBundle;
private CompareConfiguration fCompareConfiguration;
private IPropertyChangeListener fPropertyChangeListener;
private ICompareInputChangeListener fCompareInputChangeListener;
private ListenerList fListenerList;
boolean fConfirmSave= true;
private double fHSplit= HSPLIT; // width ratio of left and right panes
private double fVSplit= VSPLIT; // height ratio of ancestor and bottom panes
private boolean fAncestorEnabled= true; // show ancestor in case of conflicts
/* package */ boolean fShowAncestor= false; // if current input has conflicts
private boolean fIsThreeWay= false;
private ActionContributionItem fAncestorItem;
private Action fCopyLeftToRightAction; // copy from left to right
private Action fCopyRightToLeftAction; // copy from right to left
MergeViewerAction fLeftSaveAction;
MergeViewerAction fRightSaveAction;
private IKeyBindingService fKeyBindingService;
// SWT widgets
/* package */ Composite fComposite;
private CLabel fAncestorLabel;
private CLabel fLeftLabel;
private CLabel fRightLabel;
/* package */ CLabel fDirectionLabel;
/* package */ Control fCenter;
//---- SWT resources to be disposed
private Image fRightArrow;
private Image fLeftArrow;
private Image fBothArrow;
//---- end
/**
* Creates a new content merge viewer and initializes with a resource bundle and a
* configuration.
*
* @param bundle the resource bundle
* @param cc the configuration object
*/
protected ContentMergeViewer(int style, ResourceBundle bundle, CompareConfiguration cc) {
fStyles= style;
fBundle= bundle;
fAncestorEnabled= Utilities.getBoolean(cc, ANCESTOR_ENABLED, fAncestorEnabled);
fConfirmSave= Utilities.getBoolean(cc, CompareEditor.CONFIRM_SAVE_PROPERTY, fConfirmSave);
setContentProvider(new MergeViewerContentProvider(cc));
fCompareInputChangeListener= new ICompareInputChangeListener() {
public void compareInputChanged(ICompareInput input) {
ContentMergeViewer.this.internalRefresh(input);
}
};
fCompareConfiguration= cc;
if (fCompareConfiguration != null) {
fPropertyChangeListener= new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
ContentMergeViewer.this.propertyChange(event);
}
};
fCompareConfiguration.addPropertyChangeListener(fPropertyChangeListener);
}
fLeftSaveAction= new SaveAction(true);
fLeftSaveAction.setEnabled(false);
fRightSaveAction= new SaveAction(false);
fRightSaveAction.setEnabled(false);
}
//---- hooks ---------------------
/**
* Returns the viewer's name.
*
* @return the viewer's name
*/
public String getTitle() {
return Utilities.getString(getResourceBundle(), "title"); //$NON-NLS-1$
}
/**
* Creates the SWT controls for the ancestor, left, and right
* content areas of this compare viewer.
* Implementations typically hold onto the controls
* so that they can be initialized with the input objects in method
* <code>updateContent</code>.
*
* @param composite the container for the three areas
*/
abstract protected void createControls(Composite composite);
/**
* Lays out the ancestor area of the compare viewer.
* It is called whenever the viewer is resized or when the sashes between
* the areas are moved to adjust the size of the areas.
*
* @param x the horizontal position of the ancestor area within its container
* @param y the vertical position of the ancestor area within its container
* @param width the width of the ancestor area
* @param height the height of the ancestor area
*/
abstract protected void handleResizeAncestor(int x, int y, int width, int height);
/**
* Lays out the left and right areas of the compare viewer.
* It is called whenever the viewer is resized or when the sashes between
* the areas are moved to adjust the size of the areas.
*
* @param x the horizontal position of the left area within its container
* @param y the vertical position of the left and right area within its container
* @param leftWidth the width of the left area
* @param centerWidth the width of the gap between the left and right areas
* @param rightWidth the width of the right area
* @param height the height of the left and right areas
*/
abstract protected void handleResizeLeftRight(int x, int y, int leftWidth, int centerWidth,
int rightWidth, int height);
/**
* Contributes items to the given <code>ToolBarManager</code>.
* It is called when this viewer is installed in its container and if the container
* has a <code>ToolBarManager</code>.
* The <code>ContentMergeViewer</code> implementation of this method does nothing.
* Subclasses may reimplement.
*
* @param toolBarManager the toolbar manager to contribute to
*/
protected void createToolItems(ToolBarManager toolBarManager) {
}
/**
* Initializes the controls of the three content areas with the given input objects.
*
* @param ancestor the input for the ancestor area
* @param left the input for the left area
* @param right the input for the right area
*/
abstract protected void updateContent(Object ancestor, Object left, Object right);
/**
* Copies the content of one side to the other side.
* Called from the (internal) actions for copying the sides of the viewer's input object.
*
* @param leftToRight if <code>true</code>, the left side is copied to the right side;
* if <code>false</code>, the right side is copied to the left side
*/
abstract protected void copy(boolean leftToRight);
/**
* Returns the byte contents of the left or right side. If the viewer
* has no editable content <code>null</code> can be returned.
*
* @param left if <code>true</code>, the byte contents of the left area is returned;
* if <code>false</code>, the byte contents of the right area
* @return the content as an array of bytes, or <code>null</code>
*/
abstract protected byte[] getContents(boolean left);
//----------------------------
/**
* Returns the resource bundle of this viewer.
*
* @return the resource bundle
*/
protected ResourceBundle getResourceBundle() {
return fBundle;
}
/**
* Returns the compare configuration of this viewer,
* or <code>null</code> if this viewer does not yet have a configuration.
*
* @return the compare configuration, or <code>null</code> if none
*/
protected CompareConfiguration getCompareConfiguration() {
return fCompareConfiguration;
}
/**
* The <code>ContentMergeViewer</code> implementation of this
* <code>ContentViewer</code> method
* checks to ensure that the content provider is an <code>IMergeViewerContentProvider</code>.
*/
public void setContentProvider(IContentProvider contentProvider) {
Assert.isTrue(contentProvider instanceof IMergeViewerContentProvider);
super.setContentProvider(contentProvider);
}
/* package */ IMergeViewerContentProvider getMergeContentProvider() {
return (IMergeViewerContentProvider) getContentProvider();
}
/**
* The <code>ContentMergeViewer</code> implementation of this
* <code>Viewer</code> method returns the empty selection. Subclasses may override.
*/
public ISelection getSelection() {
return new ISelection() {
public boolean isEmpty() {
return true;
}
};
}
/**
* The <code>ContentMergeViewer</code> implementation of this
* <code>Viewer</code> method does nothing. Subclasses may reimplement.
*/
public void setSelection(ISelection s, boolean reveal) {
}
/* package */ void propertyChange(PropertyChangeEvent event) {
String key= event.getProperty();
if (key.equals(ANCESTOR_ENABLED)) {
fAncestorEnabled= Utilities.getBoolean(getCompareConfiguration(), ANCESTOR_ENABLED, fAncestorEnabled);
fComposite.layout(true);
return;
}
}
void setAncestorVisibility(boolean visible, boolean enabled) {
if (fAncestorItem != null) {
Action action= (Action) fAncestorItem.getAction();
if (action != null) {
action.setChecked(visible);
action.setEnabled(enabled);
}
}
if (fCompareConfiguration != null)
fCompareConfiguration.setProperty(ANCESTOR_ENABLED, new Boolean(visible));
}
//---- input
/* package */ boolean isThreeWay() {
return fIsThreeWay;
}
/**
* Internal hook method called when the input to this viewer is
* initially set or subsequently changed.
* <p>
* The <code>ContentMergeViewer</code> implementation of this <code>Viewer</code>
* method tries to save the old input by calling <code>doSave(...)</code> and
* then calls <code>internalRefresh(...)</code>.
*
* @param input the new input of this viewer, or <code>null</code> if there is no new input
* @param oldInput the old input element, or <code>null</code> if there was previously no input
*/
protected final void inputChanged(Object input, Object oldInput) {
if (input != oldInput)
if (oldInput instanceof ICompareInput)
((ICompareInput)oldInput).removeCompareInputChangeListener(fCompareInputChangeListener);
boolean success= doSave(input, oldInput);
if (input != oldInput)
if (input instanceof ICompareInput)
((ICompareInput)input).addCompareInputChangeListener(fCompareInputChangeListener);
if (success) {
setLeftDirty(false);
setRightDirty(false);
}
if (input != oldInput)
internalRefresh(input);
}
/**
* This method is called from the <code>Viewer</code> method <code>inputChanged</code>
* to save any unsaved changes of the old input.
* <p>
* The <code>ContentMergeViewer</code> implementation of this
* method calls <code>saveContent(...)</code>. If confirmation has been turned on
* with <code>setConfirmSave(true)</code>, a confirmation alert is posted before saving.
* </p>
* Clients can override this method and are free to decide whether
* they want to call the inherited method.
* @param newInput the new input of this viewer, or <code>null</code> if there is no new input
* @param oldInput the old input element, or <code>null</code> if there was previously no input
* @return <code>true</code> if saving was successful, or if the user didn't want to save (by pressing 'NO' in the confirmation dialog).
* @since 2.0
*/
protected boolean doSave(Object newInput, Object oldInput) {
// before setting the new input we have to save the old
if (fLeftSaveAction.isEnabled() || fRightSaveAction.isEnabled()) {
// post alert
if (fConfirmSave) {
Shell shell= fComposite.getShell();
MessageDialog dialog= new MessageDialog(shell,
Utilities.getString(getResourceBundle(), "saveDialog.title"), //$NON-NLS-1$
null, // accept the default window icon
Utilities.getString(getResourceBundle(), "saveDialog.message"), //$NON-NLS-1$
MessageDialog.QUESTION,
new String[] {
IDialogConstants.YES_LABEL,
IDialogConstants.NO_LABEL,
},
0); // default button index
switch (dialog.open()) { // open returns index of pressed button
case 0:
saveContent(oldInput);
break;
case 1:
setLeftDirty(false);
setRightDirty(false);
break;
case 2:
throw new ViewerSwitchingCancelled();
}
} else
saveContent(oldInput);
return true;
}
return false;
}
/**
* Controls whether <code>doSave(Object, Object)</code> asks for confirmation before saving
* the old input with <code>saveContent(Object)</code>.
* @param enable a value of <code>true</code> enables confirmation
* @since 2.0
*/
public void setConfirmSave(boolean enable) {
fConfirmSave= enable;
}
/* (non Javadoc)
* see Viewer.refresh
*/
public void refresh() {
internalRefresh(getInput());
}
private void internalRefresh(Object input) {
IMergeViewerContentProvider content= getMergeContentProvider();
if (content != null) {
Object ancestor= content.getAncestorContent(input);
if (input instanceof ICompareInput)
fIsThreeWay= (((ICompareInput)input).getKind() & Differencer.DIRECTION_MASK) != 0;
else
fIsThreeWay= ancestor != null;
if (fAncestorItem != null)
fAncestorItem.setVisible(fIsThreeWay);
boolean oldFlag= fShowAncestor;
fShowAncestor= fIsThreeWay && content.showAncestor(input);
if (fAncestorEnabled && oldFlag != fShowAncestor)
fComposite.layout(true);
ToolBarManager tbm= CompareViewerSwitchingPane.getToolBarManager(fComposite.getParent());
if (tbm != null) {
updateToolItems();
tbm.update(true);
tbm.getControl().getParent().layout(true);
}
updateHeader();
Object left= content.getLeftContent(input);
Object right= content.getRightContent(input);
updateContent(ancestor, left, right);
}
}
//---- layout & SWT control creation
/**
* Builds the SWT controls for the three areas of a compare/merge viewer.
* <p>
* Calls the hooks <code>createControls</code> and <code>createToolItems</code>
* to let subclasses build the specific content areas and to add items to
* an enclosing toolbar.
* <p>
* This method must only be called in the constructor of subclasses.
*
* @param parent the parent control
* @return the new control
*/
protected final Control buildControl(Composite parent) {
fComposite= new Composite(parent, fStyles) {
public boolean setFocus() {
return internalSetFocus();
}
};
fComposite.setData(CompareUI.COMPARE_VIEWER_TITLE, getTitle());
hookControl(fComposite); // hook help & dispose listener
fComposite.setLayout(new ContentMergeViewerLayout());
int style= SWT.SHADOW_OUT;
fAncestorLabel= new CLabel(fComposite, style);
fLeftLabel= new CLabel(fComposite, style);
new Resizer(fLeftLabel, VERTICAL);
fDirectionLabel= new CLabel(fComposite, style);
fDirectionLabel.setAlignment(SWT.CENTER);
new Resizer(fDirectionLabel, HORIZONTAL | VERTICAL);
fRightLabel= new CLabel(fComposite, style);
new Resizer(fRightLabel, VERTICAL);
if (fCenter == null || fCenter.isDisposed())
fCenter= createCenter(fComposite);
createControls(fComposite);
IWorkbenchPartSite ps= Utilities.findSite(fComposite);
fKeyBindingService= ps != null ? ps.getKeyBindingService() : null;
ToolBarManager tbm= CompareViewerSwitchingPane.getToolBarManager(parent);
if (tbm != null) {
tbm.removeAll();
// define groups
tbm.add(new Separator("modes")); //$NON-NLS-1$
tbm.add(new Separator("merge")); //$NON-NLS-1$
tbm.add(new Separator("navigation")); //$NON-NLS-1$
CompareConfiguration cc= getCompareConfiguration();
if (cc.isRightEditable()) {
fCopyLeftToRightAction=
new Action() {
public void run() {
copy(true);
}
};
Utilities.initAction(fCopyLeftToRightAction, getResourceBundle(), "action.CopyLeftToRight."); //$NON-NLS-1$
tbm.appendToGroup("merge", fCopyLeftToRightAction); //$NON-NLS-1$
Utilities.registerAction(fKeyBindingService, fCopyLeftToRightAction, "org.eclipse.compare.copyAllLeftToRight"); //$NON-NLS-1$
}
if (cc.isLeftEditable()) {
fCopyRightToLeftAction=
new Action() {
public void run() {
copy(false);
}
};
Utilities.initAction(fCopyRightToLeftAction, getResourceBundle(), "action.CopyRightToLeft."); //$NON-NLS-1$
tbm.appendToGroup("merge", fCopyRightToLeftAction); //$NON-NLS-1$
Utilities.registerAction(fKeyBindingService, fCopyRightToLeftAction, "org.eclipse.compare.copyAllRightToLeft"); //$NON-NLS-1$
}
Action a= new ChangePropertyAction(fBundle, fCompareConfiguration, "action.EnableAncestor.", ANCESTOR_ENABLED); //$NON-NLS-1$
a.setChecked(fAncestorEnabled);
fAncestorItem= new ActionContributionItem(a);
fAncestorItem.setVisible(false);
tbm.appendToGroup("modes", fAncestorItem); //$NON-NLS-1$
createToolItems(tbm);
updateToolItems();
tbm.update(true);
}
return fComposite;
}
/* package */ boolean internalSetFocus() {
return false;
}
/* package */ int getCenterWidth() {
return 3;
}
/* package */ boolean getAncestorEnabled() {
return fAncestorEnabled;
}
/* package */ Control createCenter(Composite parent) {
Sash sash= new Sash(parent, SWT.VERTICAL);
new Resizer(sash, HORIZONTAL);
return sash;
}
/* package */ Control getCenter() {
return fCenter;
}
/*
* @see Viewer.getControl()
*/
public Control getControl() {
return fComposite;
}
/**
* Called on the viewer disposal.
* Unregisters from the compare configuration.
* Clients may extend if they have to do additional cleanup.
*/
protected void handleDispose(DisposeEvent event) {
if (fKeyBindingService != null) {
if (fCopyLeftToRightAction != null)
fKeyBindingService.unregisterAction(fCopyLeftToRightAction);
if (fCopyRightToLeftAction != null)
fKeyBindingService.unregisterAction(fCopyRightToLeftAction);
fKeyBindingService= null;
}
Object input= getInput();
if (input instanceof ICompareInput)
((ICompareInput)input).removeCompareInputChangeListener(fCompareInputChangeListener);
if (fCompareConfiguration != null && fPropertyChangeListener != null) {
fCompareConfiguration.removePropertyChangeListener(fPropertyChangeListener);
fPropertyChangeListener= null;
}
fAncestorLabel= null;
fLeftLabel= null;
fDirectionLabel= null;
fRightLabel= null;
fCenter= null;
if (fRightArrow != null) {
fRightArrow.dispose();
fRightArrow= null;
}
if (fLeftArrow != null) {
fLeftArrow.dispose();
fLeftArrow= null;
}
if (fBothArrow != null) {
fBothArrow.dispose();
fBothArrow= null;
}
super.handleDispose(event);
}
/**
* Updates the enabled state of the toolbar items.
* <p>
* This method is called whenever the state of the items needs updating.
* <p>
* Subclasses may extend this method, although this is generally not required.
*/
protected void updateToolItems() {
IMergeViewerContentProvider content= getMergeContentProvider();
Object input= getInput();
if (fCopyLeftToRightAction != null) {
boolean enable= content.isRightEditable(input);
// if (enable && input instanceof ICompareInput) {
// ITypedElement e= ((ICompareInput) input).getLeft();
// if (e == null)
// enable= false;
// }
fCopyLeftToRightAction.setEnabled(enable);
}
if (fCopyRightToLeftAction != null) {
boolean enable= content.isLeftEditable(input);
// if (enable && input instanceof ICompareInput) {
// ITypedElement e= ((ICompareInput) input).getRight();
// if (e == null)
// enable= false;
// }
fCopyRightToLeftAction.setEnabled(enable);
}
}
/**
* Updates the headers of the three areas
* by querying the content provider for a name and image for
* the three sides of the input object.
* <p>
* This method is called whenever the header must be updated.
* <p>
* Subclasses may extend this method, although this is generally not required.
*/
protected void updateHeader() {
IMergeViewerContentProvider content= getMergeContentProvider();
Object input= getInput();
if (fAncestorLabel != null) {
fAncestorLabel.setImage(content.getAncestorImage(input));
fAncestorLabel.setText(content.getAncestorLabel(input));
}
if (fLeftLabel != null) {
fLeftLabel.setImage(content.getLeftImage(input));
fLeftLabel.setText(content.getLeftLabel(input));
}
if (fRightLabel != null) {
fRightLabel.setImage(content.getRightImage(input));
fRightLabel.setText(content.getRightLabel(input));
}
}
// private Image loadImage(String name) {
// ImageDescriptor id= ImageDescriptor.createFromFile(ContentMergeViewer.class, name);
// if (id != null)
// return id.createImage();
// return null;
// }
/**
* Calculates the height of the header.
*/
/* package */ int getHeaderHeight() {
int headerHeight= fLeftLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
headerHeight= Math.max(headerHeight, fDirectionLabel.computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y);
return headerHeight;
}
//---- merge direction
/**
* Returns true if both sides are editable.
*/
/* package */ boolean canToggleMergeDirection() {
IMergeViewerContentProvider content= getMergeContentProvider();
Object input= getInput();
return content.isLeftEditable(input) && content.isRightEditable(input);
}
//---- dirty state & saving state
/* (non Javadoc)
* see IPropertyChangeNotifier.addPropertyChangeListener
*/
public void addPropertyChangeListener(IPropertyChangeListener listener) {
if (fListenerList == null)
fListenerList= new ListenerList();
fListenerList.add(listener);
}
/* (non Javadoc)
* see IPropertyChangeNotifier.removePropertyChangeListener
*/
public void removePropertyChangeListener(IPropertyChangeListener listener) {
if (fListenerList != null) {
fListenerList.remove(listener);
if (fListenerList.isEmpty())
fListenerList= null;
}
}
/* package */ void fireDirtyState(boolean state) {
Utilities.firePropertyChange(fListenerList, this, CompareEditorInput.DIRTY_STATE, null, new Boolean(state));
}
/**
* Sets the dirty state of the left side of this viewer.
* If the new value differs from the old
* all registered listener are notified with
* a <code>PropertyChangeEvent</code> with the
* property name <code>CompareEditorInput.DIRTY_STATE</code>.
*
* @param dirty the state of the left side dirty flag
*/
protected void setLeftDirty(boolean dirty) {
if (fLeftSaveAction.isEnabled() != dirty) {
fLeftSaveAction.setEnabled(dirty);
fireDirtyState(dirty);
}
}
/**
* Sets the dirty state of the right side of this viewer.
* If the new value differs from the old
* all registered listener are notified with
* a <code>PropertyChangeEvent</code> with the
* property name <code>CompareEditorInput.DIRTY_STATE</code>.
*
* @param dirty the state of the right side dirty flag
*/
protected void setRightDirty(boolean dirty) {
if (fRightSaveAction.isEnabled() != dirty) {
fRightSaveAction.setEnabled(dirty);
fireDirtyState(dirty);
}
}
/**
* Save the viewers's content.
* Note: this method is for internal use only. Clients should not call this method.
* @since 2.0
*/
public void save(IProgressMonitor pm) throws CoreException {
saveContent(getInput());
}
/**
* Save modified content back to input elements via the content provider.
*/
/* package */ void saveContent(Object oldInput) {
// write back modified contents
IMergeViewerContentProvider content= (IMergeViewerContentProvider) getContentProvider();
boolean leftEmpty= content.getLeftContent(oldInput) == null;
boolean rightEmpty= content.getRightContent(oldInput) == null;
if (fCompareConfiguration.isLeftEditable() && fLeftSaveAction.isEnabled()) {
byte[] bytes= getContents(true);
if (leftEmpty && bytes != null && bytes.length == 0)
bytes= null;
setLeftDirty(false);
content.saveLeftContent(oldInput, bytes);
}
if (fCompareConfiguration.isRightEditable() && fRightSaveAction.isEnabled()) {
byte[] bytes= getContents(false);
if (rightEmpty && bytes != null && bytes.length == 0)
bytes= null;
setRightDirty(false);
content.saveRightContent(oldInput, bytes);
}
}
}