1 /*******************************************************************************
2  * Copyright (c) 2000, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.team.internal.ui.preferences;
15 
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Set;
21 
22 import org.eclipse.jface.dialogs.Dialog;
23 import org.eclipse.jface.dialogs.IInputValidator;
24 import org.eclipse.jface.dialogs.InputDialog;
25 import org.eclipse.jface.dialogs.MessageDialog;
26 import org.eclipse.jface.preference.PreferencePage;
27 import org.eclipse.jface.util.PropertyChangeEvent;
28 import org.eclipse.jface.viewers.ISelection;
29 import org.eclipse.jface.viewers.IStructuredSelection;
30 import org.eclipse.jface.window.Window;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.layout.GridData;
33 import org.eclipse.swt.widgets.Button;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Control;
36 import org.eclipse.team.core.IFileContentManager;
37 import org.eclipse.team.core.IStringMapping;
38 import org.eclipse.team.core.Team;
39 import org.eclipse.team.internal.ui.IHelpContextIds;
40 import org.eclipse.team.internal.ui.PixelConverter;
41 import org.eclipse.team.internal.ui.SWTUtils;
42 import org.eclipse.team.internal.ui.TeamUIMessages;
43 import org.eclipse.team.internal.ui.TeamUIPlugin;
44 import org.eclipse.team.internal.ui.preferences.FileTypeTable.Item;
45 import org.eclipse.team.ui.TeamUI;
46 import org.eclipse.ui.IWorkbench;
47 import org.eclipse.ui.IWorkbenchPreferencePage;
48 import org.eclipse.ui.PlatformUI;
49 /**
50  * This preference page displays all patterns which determine whether a resource
51  * is to be treated as a text file or not. The page allows the user to add or
52  * remove entries from this table, and change their values from Text to Binary.
53  */
54 public class TextPreferencePage extends PreferencePage implements IWorkbenchPreferencePage {
55 
56 	private static final class ExtensionValidator implements IInputValidator {
57 		@Override
isValid(String newText)58 		public String isValid(String newText) {
59 			if (newText.trim().length() == 0)
60 				return ""; //$NON-NLS-1$
61 			if (!isComplete(newText)){
62 				return TeamUIMessages.TextPreferencePage_ExtensionNotCompleted;
63 			}
64 			newText = strip(newText);
65 			if (newText.indexOf('*') >= 0)
66 				return TeamUIMessages.TextPreferencePage_2;
67 			if (newText.indexOf('.') >= 0)
68 				return TeamUIMessages.TextPreferencePage_3;
69 			return null;
70 		}
71 
72 		/**
73 		 * @param newText
74 		 * @return the text
75 		 */
strip(String newText)76 		public String strip(String newText) {
77 			newText= newText.trim();
78 			if (newText.startsWith("*")) //$NON-NLS-1$
79 				newText= newText.substring(1);
80 			if (newText.startsWith(".")) //$NON-NLS-1$
81 				newText= newText.substring(1);
82 			return newText;
83 		}
84 
isComplete(String text)85 		public boolean isComplete(String text){
86 			//Allowed formats of extension are:
87 			// extension
88 			// .extension
89 			// *.extension
90 			if (text.equals("*") || text.equals("*.") || text.equals(".")){  //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
91 				return false;
92 			}
93 			return true;
94 		}
95 	}
96 
97 	private static final class FilenameValidator implements IInputValidator {
98 		@Override
isValid(String newText)99 		public String isValid(String newText) {
100 			if (newText.trim().length() == 0)
101 				return ""; //$NON-NLS-1$
102 			if (newText.indexOf('*') >= 0)
103 				return TeamUIMessages.TextPreferencePage_5;
104 			return null;
105 		}
106 	}
107 
108 	// The input for the table viewer
109 	private final List<Item> fItems;
110 
111 	// Widgets
112 	private Button fRemoveButton;
113 	private Button fChangeButton;
114 
115 	protected FileTypeTable fTable;
116 
TextPreferencePage()117 	public TextPreferencePage() {
118 		fItems= new ArrayList<>();
119 		initializeItems();
120 	}
121 
initializeItems()122 	private void initializeItems() {
123 
124 		fItems.clear();
125 
126 		final IFileContentManager manager= Team.getFileContentManager();
127 
128 		final IStringMapping [] extensionInfoArray= manager.getExtensionMappings();
129 		final IStringMapping [] nameInfoArray= manager.getNameMappings();
130 
131 		Set fPluginNames= makeSetOfStrings(manager.getDefaultNameMappings());
132 		Set fPluginExtensions= makeSetOfStrings(manager.getDefaultExtensionMappings());
133 
134 		for (IStringMapping info : extensionInfoArray) {
135 			final FileTypeTable.Extension extension= new FileTypeTable.Extension(info.getString(), fPluginExtensions.contains(info.getString()));
136 			extension.mode= info.getType();
137 			fItems.add(extension);
138 		}
139 		for (IStringMapping info : nameInfoArray) {
140 			final FileTypeTable.Name name= new FileTypeTable.Name(info.getString(), fPluginNames.contains(info.getString()));
141 			name.mode= info.getType();
142 			fItems.add(name);
143 		}
144 
145 	}
146 
makeSetOfStrings(IStringMapping [] mappings)147 	private static Set<String> makeSetOfStrings(IStringMapping [] mappings) {
148 		final Set<String> set= new HashSet<>(mappings.length);
149 		for (IStringMapping mapping : mappings) {
150 			set.add(mapping.getString());
151 		}
152 		return set;
153 	}
154 
155 	@Override
init(IWorkbench workbench)156 	public void init(IWorkbench workbench) {
157 	}
158 
159 	@Override
createContents(Composite parent)160 	protected Control createContents(Composite parent) {
161 
162 		initializeDialogUnits(parent);
163 
164 		final PixelConverter converter= SWTUtils.createDialogPixelConverter(parent);
165 
166 		final Composite composite= SWTUtils.createHVFillComposite(parent, SWTUtils.MARGINS_NONE, 2);
167 
168 		fTable= new FileTypeTable(composite, fItems, false);
169 
170 		fTable.getViewer().getControl().addListener(SWT.Selection, e -> handleSelection());
171 
172 		fTable.getViewer().addDoubleClickListener(event -> {
173 			final ISelection selection = event.getSelection();
174 			if (selection == null || !(selection instanceof IStructuredSelection)) {
175 				return;
176 			}
177 			fTable.getViewer().editElement(((IStructuredSelection)selection).getFirstElement(), 1);
178 		});
179 
180 		final Composite buttonsComposite = new Composite(composite, SWT.NONE);
181 		buttonsComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false));
182 		buttonsComposite.setLayout(SWTUtils.createGridLayout(1, converter, SWTUtils.MARGINS_NONE));
183 
184 		final Button addExtensionButton = new Button(buttonsComposite, SWT.PUSH);
185 		addExtensionButton.setText(TeamUIMessages.TextPreferencePage_add);
186 		final Button addNameButton = new Button(buttonsComposite, SWT.PUSH);
187 		addNameButton.setText(TeamUIMessages.TextPreferencePage_0);
188 		fChangeButton = new Button(buttonsComposite, SWT.PUSH);
189 		fChangeButton.setText(TeamUIMessages.TextPreferencePage_change);
190 		fRemoveButton= new Button(buttonsComposite, SWT.PUSH);
191 		fRemoveButton.setText(TeamUIMessages.TextPreferencePage_remove);
192 
193 		SWTUtils.createLabel(composite, TeamUIMessages.TextPreferencePage_1, 2);
194 
195 		/**
196 		 * Calculate and set the button size
197 		 */
198 		applyDialogFont(composite);
199 		final int buttonWidth= SWTUtils.calculateControlSize(converter, new Button [] { addExtensionButton, addNameButton, fChangeButton, fRemoveButton });
200 		addExtensionButton.setLayoutData(SWTUtils.createGridData(buttonWidth, SWT.DEFAULT, SWT.FILL, SWT.FILL, false, false));
201 		addNameButton.setLayoutData(SWTUtils.createGridData(buttonWidth, SWT.DEFAULT, SWT.FILL, SWT.FILL, false, false));
202 		fChangeButton.setLayoutData(SWTUtils.createGridData(buttonWidth, SWT.DEFAULT, SWT.FILL, SWT.FILL, false, false));
203 		fRemoveButton.setLayoutData(SWTUtils.createGridData(buttonWidth, SWT.DEFAULT, SWT.FILL, SWT.FILL, false, false));
204 
205 		addExtensionButton.addListener(SWT.Selection, e -> addExtension());
206 		addNameButton.addListener(SWT.Selection, e -> addName());
207 
208 		fChangeButton.setEnabled(false);
209 		fChangeButton.addListener(SWT.Selection, e -> changePattern());
210 
211 		fRemoveButton.setEnabled(false);
212 		fRemoveButton.addListener(SWT.Selection, e -> removePattern());
213 
214 		Dialog.applyDialogFont(parent);
215 
216 		// set F1 help
217 		PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), IHelpContextIds.FILE_TYPE_PREFERENCE_PAGE);
218 
219 		return composite;
220 	}
221 
222 	@Override
performDefaults()223 	protected void performDefaults() {
224 		super.performDefaults();
225 		initializeItems();
226 		if (fTable != null)
227 			fTable.getViewer().refresh();
228 	}
229 
230 	/**
231 	 * Do anything necessary because the OK button has been pressed.
232 	 *
233 	 * @return whether it is okay to close the preference page
234 	 */
235 	@Override
performOk()236 	public boolean performOk() {
237 		final ArrayList<String> extensionsList= new ArrayList<>();
238 		final ArrayList<Integer> extensionsModesList= new ArrayList<>();
239 
240 		final ArrayList<String> namesList= new ArrayList<>();
241 		final ArrayList<Integer> namesModesList= new ArrayList<>();
242 
243 		for (Object element : fItems) {
244 			final FileTypeTable.Item item= (FileTypeTable.Item) element;
245 
246 			if (item instanceof FileTypeTable.Extension) {
247 				extensionsList.add(item.name);
248 				extensionsModesList.add(Integer.valueOf(item.mode));
249 			} else if (item instanceof FileTypeTable.Name) {
250 				namesList.add(item.name);
251 				namesModesList.add(Integer.valueOf(item.mode));
252 			}
253 		}
254 
255 		final String [] extensions= extensionsList.toArray(new String [extensionsList.size()]);
256 		final String [] names= namesList.toArray(new String [namesList.size()]);
257 
258 		final int [] extensionsModes= integerListToIntArray(extensionsModesList);
259 		final int [] namesModes= integerListToIntArray(namesModesList);
260 
261 		Team.getFileContentManager().setExtensionMappings(extensions, extensionsModes);
262 		Team.getFileContentManager().setNameMappings(names, namesModes);
263 
264 		TeamUIPlugin.broadcastPropertyChange(new PropertyChangeEvent(this, TeamUI.GLOBAL_FILE_TYPES_CHANGED, null, null));
265 
266 		return true;
267 	}
268 
integerListToIntArray(List integers)269 	private static int [] integerListToIntArray(List integers) {
270 		final int [] array= new int [integers.size()];
271 		int index= 0;
272 		for (Iterator iter = integers.iterator(); iter.hasNext();)
273 			array[index++]= ((Integer)iter.next()).intValue();
274 		return array;
275 	}
276 
277 	/**
278 	 * Add a new item to the table with the default type of Text.
279 	 */
addExtension()280 	void addExtension() {
281 		final ExtensionValidator validator = new ExtensionValidator();
282 		final InputDialog dialog = new InputDialog(getShell(), TeamUIMessages.TextPreferencePage_enterExtensionShort, TeamUIMessages.TextPreferencePage_enterExtensionLong, null, validator); //
283 		if (dialog.open() != Window.OK)
284 			return;
285 
286 		final String extension = validator.strip(dialog.getValue());
287 
288 		// Check if the item already exists
289 		final Iterator it = fItems.iterator();
290 		while (it.hasNext()) {
291 			final FileTypeTable.Item item= (FileTypeTable.Item)it.next();
292 			if (item instanceof FileTypeTable.Extension && item.name.equals(extension)) {
293 				MessageDialog.openWarning(getShell(), TeamUIMessages.TextPreferencePage_extensionExistsShort, TeamUIMessages.TextPreferencePage_extensionExistsLong); //
294 				return;
295 			}
296 		}
297 		final FileTypeTable.Item item= new FileTypeTable.Extension(extension, false);
298 		fItems.add(item);
299 		fTable.getViewer().refresh();
300 	}
301 
302 	/**
303 	 * Add a new item to the table with the default type of Text.
304 	 */
addName()305 	void addName() {
306 		final InputDialog dialog = new InputDialog(getShell(), TeamUIMessages.TextPreferencePage_6, TeamUIMessages.TextPreferencePage_7, null, new FilenameValidator()); //
307 		if (dialog.open() != Window.OK)
308 			return;
309 
310 		final String name = dialog.getValue();
311 
312 		// Check if the item already exists
313 		final Iterator it = fItems.iterator();
314 		while (it.hasNext()) {
315 			final FileTypeTable.Item item= (FileTypeTable.Item)it.next();
316 			if (item instanceof FileTypeTable.Name && item.name.equals(name)) {
317 				MessageDialog.openWarning(getShell(), TeamUIMessages.TextPreferencePage_extensionExistsShort, TeamUIMessages.TextPreferencePage_extensionExistsLong); //
318 				return;
319 			}
320 		}
321 		final FileTypeTable.Item item= new FileTypeTable.Name(name, false);
322 		fItems.add(item);
323 		fTable.getViewer().refresh();
324 	}
325 
326 	/**
327 	 * Remove the selected items from the table
328 	 */
removePattern()329 	void removePattern() {
330 		final IStructuredSelection selection = fTable.getSelection();
331 		if (selection == null)
332 			return;
333 
334 		for (final Iterator it = selection.iterator(); it.hasNext(); ) {
335 			final FileTypeTable.Item item= (FileTypeTable.Item)it.next();
336 			if (item.contributed)
337 				continue;
338 			fItems.remove(item);
339 		}
340 		fTable.getViewer().refresh();
341 		handleSelection();
342 	}
343 	/**
344 	 * Toggle the selected items' content types
345 	 */
changePattern()346 	void changePattern() {
347 		final IStructuredSelection selection = fTable.getSelection();
348 		if (selection == null)
349 			return;
350 
351 		for (final Iterator it = selection.iterator(); it.hasNext(); ) {
352 			final FileTypeTable.Item item= (FileTypeTable.Item)it.next();
353 			item.mode= item.mode == Team.TEXT ? Team.BINARY : Team.TEXT;
354 			fTable.getViewer().refresh(item);
355 		}
356 	}
357 
358 	/**
359 	 * The table viewer selection has changed. Update the remove and change button enablement.
360 	 */
handleSelection()361 	void handleSelection() {
362 		final boolean empty = fTable.getSelection().isEmpty();
363 		FileTypeTable.Item selectedItem = (Item) fTable.getSelection().getFirstElement();
364 
365 		fRemoveButton.setEnabled(!empty && !selectedItem.contributed);
366 		fChangeButton.setEnabled(!empty);
367 	}
368 }
369