| /******************************************************************************* |
| * Copyright (c) 2000, 2004 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.internal; |
| |
| import java.io.*; |
| import java.text.*; |
| import java.util.*; |
| |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.*; |
| import org.eclipse.swt.graphics.*; |
| import org.eclipse.swt.layout.*; |
| import org.eclipse.swt.widgets.*; |
| |
| import org.eclipse.jface.dialogs.*; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.viewers.Viewer; |
| |
| import org.eclipse.core.resources.*; |
| import org.eclipse.core.runtime.*; |
| |
| import org.eclipse.compare.*; |
| |
| |
| public class AddFromHistoryDialog extends ResizableDialog { |
| |
| static class HistoryInput implements ITypedElement, IEncodedStreamContentAccessor, IModificationDate { |
| IFile fFile; |
| IFileState fFileState; |
| |
| HistoryInput(IFile file, IFileState fileState) { |
| fFile= file; |
| fFileState= fileState; |
| } |
| public InputStream getContents() throws CoreException { |
| return new BufferedInputStream(fFileState.getContents()); |
| } |
| public String getCharset() { |
| String charset= null; |
| try { |
| charset= fFileState.getCharset(); |
| } catch (CoreException e) { |
| // fall through |
| } |
| if (charset == null) |
| charset= Utilities.getCharset(fFile); |
| return charset; |
| } |
| public String getName() { |
| return fFile.getName(); |
| } |
| public String getType() { |
| return fFile.getFileExtension(); |
| } |
| public Image getImage() { |
| return CompareUI.getImage(fFile); |
| } |
| public long getModificationDate() { |
| return fFileState.getModificationTime(); |
| } |
| } |
| |
| static class FileHistory { |
| private IFile fFile; |
| private IFileState[] fStates; |
| private int fSelected; |
| |
| FileHistory(IFile file) { |
| fFile= file; |
| } |
| |
| IFile getFile() { |
| return fFile; |
| } |
| |
| IFileState[] getStates() { |
| if (fStates == null) { |
| try { |
| fStates= fFile.getHistory(new NullProgressMonitor()); |
| } catch (CoreException ex) { |
| // NeedWork |
| } |
| } |
| return fStates; |
| } |
| |
| IFileState getSelectedState() { |
| return getStates()[fSelected]; |
| } |
| |
| void setSelected(IFileState state) { |
| for (int i= 0; i < fStates.length; i++) { |
| if (fStates[i] == state) { |
| fSelected= i; |
| return; |
| } |
| } |
| } |
| |
| HistoryInput getHistoryInput() { |
| return new HistoryInput(fFile, getSelectedState()); |
| } |
| |
| boolean isSelected(int index) { |
| return index == fSelected; |
| } |
| } |
| |
| private CompareConfiguration fCompareConfiguration; |
| private ArrayList fArrayList= new ArrayList(); |
| private FileHistory fCurrentFileHistory; |
| |
| // SWT controls |
| private CompareViewerSwitchingPane fContentPane; |
| private Button fCommitButton; |
| private Table fMemberTable; |
| private CompareViewerPane fMemberPane; |
| private Tree fEditionTree; |
| private CompareViewerPane fEditionPane; |
| private Image fDateImage; |
| private Image fTimeImage; |
| |
| |
| public AddFromHistoryDialog(Shell parent, ResourceBundle bundle) { |
| super(parent, bundle); |
| |
| String iconName= Utilities.getString(fBundle, "dateIcon", "obj16/day_obj.gif"); //$NON-NLS-2$ //$NON-NLS-1$ |
| ImageDescriptor id= CompareUIPlugin.getImageDescriptor(iconName); |
| if (id != null) |
| fDateImage= id.createImage(); |
| iconName= Utilities.getString(fBundle, "timeIcon", "obj16/resource_obj.gif"); //$NON-NLS-1$ //$NON-NLS-2$ |
| id= CompareUIPlugin.getImageDescriptor(iconName); |
| if (id != null) |
| fTimeImage= id.createImage(); |
| } |
| |
| public boolean select(IContainer root, IFile[] inputFiles) { |
| |
| create(); // create widgets |
| |
| String format= Utilities.getString(fBundle, "memberPaneTitle"); //$NON-NLS-1$ |
| String title= MessageFormat.format(format, new Object[] { root.getName() }); |
| fMemberPane.setImage(CompareUI.getImage(root)); |
| fMemberPane.setText(title); |
| |
| // sort input files |
| final int count= inputFiles.length; |
| final IFile[] files= new IFile[count]; |
| for (int i= 0; i < count; i++) |
| files[i]= inputFiles[i]; |
| if (count > 1) |
| internalSort(files, 0, count-1); |
| |
| |
| String prefix= root.getFullPath().toString(); |
| |
| if (fMemberTable != null && !fMemberTable.isDisposed()) { |
| for (int i= 0; i < files.length; i++) { |
| IFile file= files[i]; |
| String path= file.getFullPath().toString(); |
| if (path.startsWith(prefix)) |
| path= path.substring(prefix.length()+1); |
| TableItem ti= new TableItem(fMemberTable, SWT.NONE); |
| ti.setImage(CompareUI.getImage(file)); |
| ti.setText(path); |
| ti.setData(new FileHistory(file)); |
| } |
| } |
| |
| open(); |
| |
| return (getReturnCode() == OK) && (fArrayList.size() > 0); |
| } |
| |
| HistoryInput[] getSelected() { |
| HistoryInput[] selected= new HistoryInput[fArrayList.size()]; |
| Iterator iter= fArrayList.iterator(); |
| for (int i= 0; iter.hasNext(); i++) { |
| FileHistory h= (FileHistory) iter.next(); |
| selected[i]= h.getHistoryInput(); |
| } |
| return selected; |
| } |
| |
| protected synchronized Control createDialogArea(Composite parent2) { |
| |
| Composite parent= (Composite) super.createDialogArea(parent2); |
| |
| getShell().setText(Utilities.getString(fBundle, "title")); //$NON-NLS-1$ |
| |
| org.eclipse.compare.Splitter vsplitter= new org.eclipse.compare.Splitter(parent, SWT.VERTICAL); |
| vsplitter.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL |
| | GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL)); |
| |
| vsplitter.addDisposeListener( |
| new DisposeListener() { |
| public void widgetDisposed(DisposeEvent e) { |
| if (fDateImage != null) |
| fDateImage.dispose(); |
| if (fTimeImage != null) |
| fTimeImage.dispose(); |
| } |
| } |
| ); |
| |
| // we need two panes: the left for the elements, the right one for the editions |
| Splitter hsplitter= new Splitter(vsplitter, SWT.HORIZONTAL); |
| |
| Composite c= new Composite(hsplitter, SWT.NONE); |
| GridLayout layout= new GridLayout(); |
| layout.marginWidth= 0; |
| layout.marginHeight= 2; |
| layout.verticalSpacing= 2; |
| layout.numColumns= 1; |
| c.setLayout(layout); |
| Label l1= new Label(c, SWT.NONE); |
| l1.setText(Utilities.getString(fBundle, "memberDescription")); //$NON-NLS-1$ |
| fMemberPane= new CompareViewerPane(c, SWT.BORDER | SWT.FLAT); |
| GridData gd= new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL); |
| fMemberPane.setLayoutData(gd); |
| |
| fMemberTable= new Table(fMemberPane, SWT.CHECK | SWT.H_SCROLL | SWT.V_SCROLL); |
| fMemberTable.addSelectionListener( |
| new SelectionAdapter() { |
| public void widgetSelected(SelectionEvent e) { |
| if (e.detail == SWT.CHECK) { |
| if (e.item instanceof TableItem) { |
| TableItem ti= (TableItem) e.item; |
| if (ti.getChecked()) |
| fArrayList.add(ti.getData()); |
| else |
| fArrayList.remove(ti.getData()); |
| |
| if (fCommitButton != null) |
| fCommitButton.setEnabled(fArrayList.size() > 0); |
| } |
| } else { |
| handleMemberSelect(e.item); |
| } |
| } |
| } |
| ); |
| |
| fMemberPane.setContent(fMemberTable); |
| |
| c= new Composite(hsplitter, SWT.NONE); |
| layout= new GridLayout(); |
| layout.marginWidth= 0; |
| layout.marginHeight= 2; |
| layout.verticalSpacing= 2; |
| layout.numColumns= 1; |
| c.setLayout(layout); |
| Label l2= new Label(c, SWT.NONE); |
| l2.setText(Utilities.getString(fBundle, "editionDescription")); //$NON-NLS-1$ |
| fEditionPane= new CompareViewerPane(c, SWT.BORDER | SWT.FLAT); |
| gd= new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL); |
| fEditionPane.setLayoutData(gd); |
| |
| fEditionTree= new Tree(fEditionPane, SWT.H_SCROLL | SWT.V_SCROLL); |
| fEditionTree.addSelectionListener( |
| new SelectionAdapter() { |
| public void widgetSelected(SelectionEvent e) { |
| feedContent(e.item); |
| } |
| } |
| ); |
| fEditionPane.setContent(fEditionTree); |
| |
| applyDialogFont(parent); // to avoid applying font to compare viewer |
| fContentPane= new CompareViewerSwitchingPane(vsplitter, SWT.BORDER | SWT.FLAT) { |
| protected Viewer getViewer(Viewer oldViewer, Object input) { |
| return CompareUI.findContentViewer(oldViewer, input, this, fCompareConfiguration); |
| } |
| }; |
| vsplitter.setWeights(new int[] { 30, 70 }); |
| |
| return parent; |
| } |
| |
| /** |
| * Feeds selection from member viewer to edition viewer. |
| */ |
| private void handleMemberSelect(Widget w) { |
| Object data= null; |
| if (w != null) |
| data= w.getData(); |
| if (data instanceof FileHistory) { |
| |
| FileHistory h= (FileHistory) data; |
| fCurrentFileHistory= h; |
| |
| IFile file= h.getFile(); |
| IFileState[] states= h.getStates(); |
| |
| fEditionPane.setImage(CompareUI.getImage(file)); |
| String pattern= Utilities.getString(fBundle, "treeTitleFormat"); //$NON-NLS-1$ |
| String title= MessageFormat.format(pattern, new Object[] { file.getName() }); |
| fEditionPane.setText(title); |
| |
| if (fEditionTree != null) { |
| fEditionTree.setRedraw(false); |
| fEditionTree.removeAll(); |
| for (int i= 0; i < states.length; i++) { |
| addEdition(new HistoryInput(file, states[i]), h.isSelected(i)); |
| } |
| fEditionTree.setRedraw(true); |
| } |
| } else |
| fCurrentFileHistory= null; |
| } |
| |
| /** |
| * Adds the given Pair to the edition tree. |
| * It takes care of creating tree nodes for different dates. |
| */ |
| private void addEdition(HistoryInput input, boolean isSelected) { |
| if (fEditionTree == null || fEditionTree.isDisposed()) |
| return; |
| |
| IFileState state= input.fFileState; |
| |
| // find last day |
| TreeItem[] days= fEditionTree.getItems(); |
| TreeItem lastDay= null; |
| if (days.length > 0) |
| lastDay= days[days.length-1]; |
| |
| long ldate= state.getModificationTime(); |
| long day= dayNumber(ldate); |
| Date date= new Date(ldate); |
| if (lastDay == null || day != dayNumber(((Date)lastDay.getData()).getTime())) { |
| lastDay= new TreeItem(fEditionTree, SWT.NONE); |
| lastDay.setImage(fDateImage); |
| String df= DateFormat.getDateInstance().format(date); |
| long today= dayNumber(System.currentTimeMillis()); |
| |
| String formatKey; |
| if (day == today) |
| formatKey= "todayFormat"; //$NON-NLS-1$ |
| else if (day == today-1) |
| formatKey= "yesterdayFormat"; //$NON-NLS-1$ |
| else |
| formatKey= "dayFormat"; //$NON-NLS-1$ |
| String pattern= Utilities.getString(fBundle, formatKey); |
| if (pattern != null) |
| df= MessageFormat.format(pattern, new String[] { df }); |
| lastDay.setText(df); |
| lastDay.setData(date); |
| } |
| TreeItem ti= new TreeItem(lastDay, SWT.NONE); |
| ti.setImage(fTimeImage); |
| ti.setText(DateFormat.getTimeInstance().format(date)); |
| ti.setData(input); |
| |
| if (isSelected) { |
| lastDay.setExpanded(true); |
| fEditionTree.setSelection(new TreeItem[] { ti }); |
| feedContent(ti); |
| } |
| } |
| |
| /** |
| * Returns the number of s since Jan 1st, 1970. |
| * The given date is converted to GMT and daylight saving is taken into account too. |
| */ |
| private long dayNumber(long date) { |
| int ONE_DAY_MS= 24*60*60 * 1000; // one day in milli seconds |
| |
| Calendar calendar= Calendar.getInstance(); |
| long localTimeOffset= calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET); |
| |
| return (date + localTimeOffset) / ONE_DAY_MS; |
| } |
| |
| /** |
| * Feeds the tree viewer's selection to the contentviewer |
| */ |
| private void feedContent(Widget w) { |
| if (fContentPane != null && !fContentPane.isDisposed()) { |
| Object o= w.getData(); |
| if (o instanceof HistoryInput) { |
| HistoryInput selected= (HistoryInput) o; |
| fContentPane.setInput(selected); |
| fContentPane.setText(getEditionLabel(selected)); |
| fContentPane.setImage(fTimeImage); |
| |
| if (fCurrentFileHistory != null) |
| fCurrentFileHistory.setSelected(selected.fFileState); |
| } else { |
| fContentPane.setInput(null); |
| } |
| } |
| } |
| |
| protected String getEditionLabel(HistoryInput input) { |
| String format= Utilities.getString(fBundle, "historyEditionLabel", null); //$NON-NLS-1$ |
| if (format == null) |
| format= Utilities.getString(fBundle, "editionLabel"); //$NON-NLS-1$ |
| if (format == null) |
| format= "x{0}"; //$NON-NLS-1$ |
| |
| long modDate= input.getModificationDate(); |
| String date= DateFormat.getDateTimeInstance().format(new Date(modDate)); |
| |
| return MessageFormat.format(format, new Object[] { date }); |
| } |
| |
| /* (non-Javadoc) |
| * Method declared on Dialog. |
| */ |
| protected void createButtonsForButtonBar(Composite parent) { |
| String buttonLabel= Utilities.getString(fBundle, "buttonLabel", IDialogConstants.OK_LABEL); //$NON-NLS-1$ |
| // a 'Cancel' and a 'Add' button |
| fCommitButton= createButton(parent, IDialogConstants.OK_ID, buttonLabel, true); |
| fCommitButton.setEnabled(false); |
| createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false); |
| } |
| |
| /** |
| * Returns true if the pathname of f1 comes after f2 |
| */ |
| private static boolean greaterThan(IFile f1, IFile f2) { |
| String[] ss1= f1.getFullPath().segments(); |
| String[] ss2= f2.getFullPath().segments(); |
| int l1= ss1.length; |
| int l2= ss2.length; |
| int n= Math.max(l1, l2); |
| |
| for (int i= 0; i < n; i++) { |
| String s1= i < l1 ? ss1[i] : ""; //$NON-NLS-1$ |
| String s2= i < l2 ? ss2[i] : ""; //$NON-NLS-1$ |
| int rc= s1.compareToIgnoreCase(s2); |
| if (rc != 0) |
| return rc < 0; |
| } |
| return false; |
| } |
| |
| private static void internalSort(IFile[] keys, int left, int right) { |
| |
| int original_left= left; |
| int original_right= right; |
| |
| IFile mid= keys[(left + right) / 2]; |
| do { |
| while (greaterThan(keys[left], mid)) |
| left++; |
| |
| while (greaterThan(mid, keys[right])) |
| right--; |
| |
| if (left <= right) { |
| IFile tmp= keys[left]; |
| keys[left]= keys[right]; |
| keys[right]= tmp; |
| left++; |
| right--; |
| } |
| } while (left <= right); |
| |
| if (original_left < right) |
| internalSort(keys, original_left, right); |
| |
| if (left < original_right) |
| internalSort(keys, left, original_right); |
| } |
| } |