1 /*******************************************************************************
2 * Copyright (c) 2004, 2010 TimeSys Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
9 * TimeSys Corporation - Initial implementation
10 *******************************************************************************/
11 package org.eclipse.cdt.internal.ui.preferences;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Iterator;
16 import java.util.List;
18 import org.eclipse.core.resources.IProject;
19 import org.eclipse.core.resources.ProjectScope;
20 import org.eclipse.core.runtime.CoreException;
21 import org.eclipse.core.runtime.ListenerList;
22 import org.eclipse.core.runtime.Platform;
23 import org.eclipse.core.runtime.content.IContentType;
24 import org.eclipse.core.runtime.content.IContentTypeManager;
25 import org.eclipse.core.runtime.content.IContentTypeSettings;
26 import org.eclipse.core.runtime.preferences.IScopeContext;
27 import org.eclipse.jface.dialogs.ErrorDialog;
28 import org.eclipse.jface.dialogs.MessageDialog;
29 import org.eclipse.jface.layout.PixelConverter;
30 import org.eclipse.jface.viewers.ColumnWeightData;
31 import org.eclipse.jface.viewers.ILabelProvider;
32 import org.eclipse.jface.viewers.ILabelProviderListener;
33 import org.eclipse.jface.viewers.ISelectionChangedListener;
34 import org.eclipse.jface.viewers.IStructuredContentProvider;
35 import org.eclipse.jface.viewers.IStructuredSelection;
36 import org.eclipse.jface.viewers.ITableLabelProvider;
37 import org.eclipse.jface.viewers.SelectionChangedEvent;
38 import org.eclipse.jface.viewers.TableLayout;
39 import org.eclipse.jface.viewers.TableViewer;
40 import org.eclipse.jface.viewers.Viewer;
41 import org.eclipse.jface.viewers.ViewerSorter;
42 import org.eclipse.jface.window.Window;
43 import org.eclipse.swt.SWT;
44 import org.eclipse.swt.graphics.Image;
45 import org.eclipse.swt.layout.GridData;
46 import org.eclipse.swt.layout.GridLayout;
47 import org.eclipse.swt.widgets.Button;
48 import org.eclipse.swt.widgets.Composite;
49 import org.eclipse.swt.widgets.Control;
50 import org.eclipse.swt.widgets.Event;
51 import org.eclipse.swt.widgets.Listener;
52 import org.eclipse.swt.widgets.Table;
53 import org.eclipse.swt.widgets.TableColumn;
55 import org.eclipse.cdt.core.model.CoreModel;
57 import org.eclipse.cdt.internal.ui.util.Messages;
58 import org.eclipse.cdt.internal.ui.util.SWTUtil;
61 * Preference block that encapsulates the controls used
62 * for displaying/editing CDT file type associations
64 public class CFileTypesPreferenceBlock {
66 private static final int COL_PATTERN = 0;
67 private static final int COL_DESCRIPTION = 1;
68 private static final int COL_STATUS = 2;
70 private ArrayList<CFileTypeAssociation> fAddAssoc;
71 private ArrayList<CFileTypeAssociation> fRemoveAssoc;
72 private boolean fDirty = false;
73 private IProject fInput;
74 private IContentType[] fContentTypes;
76 private TableViewer fAssocViewer;
77 private Button fBtnNew;
78 private Button fBtnRemove;
80 private class AssocSorter extends ViewerSorter {
82 public int category(Object element) {
83 if (element instanceof CFileTypeAssociation) {
84 CFileTypeAssociation assoc = (CFileTypeAssociation) element;
85 if (assoc.isExtSpec()) {
94 private class AssocContentProvider implements IStructuredContentProvider {
95 CFileTypeAssociation[] assocs;
97 public Object[] getElements(Object inputElement) {
101 public void dispose() {
105 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
106 if (newInput instanceof CFileTypeAssociation[]) {
107 assocs = (CFileTypeAssociation[]) newInput;
112 private class AssocLabelProvider implements ILabelProvider, ITableLabelProvider {
113 private ListenerList listeners = new ListenerList();
115 public Image getColumnImage(Object element, int columnIndex) {
116 if (element instanceof CFileTypeAssociation) {
117 if (COL_PATTERN == columnIndex) {
124 public String getColumnText(Object element, int columnIndex) {
125 if (element instanceof CFileTypeAssociation) {
126 CFileTypeAssociation assoc = (CFileTypeAssociation) element;
127 switch (columnIndex) {
129 return assoc.getPattern();
131 case COL_DESCRIPTION:
132 return assoc.getDescription();
135 if (assoc.isUserDefined()) {
136 return PreferencesMessages.CFileTypesPreferencePage_userDefined;
137 } else if (assoc.isPredefined()) {
138 return PreferencesMessages.CFileTypesPreferencePage_preDefined;
143 return element.toString();
146 public void addListener(ILabelProviderListener listener) {
147 listeners.add(listener);
150 public void dispose() {
155 public boolean isLabelProperty(Object element, String property) {
159 public void removeListener(ILabelProviderListener listener) {
160 listeners.remove(listener);
163 public Image getImage(Object element) {
164 return getColumnImage(element, 0);
167 public String getText(Object element) {
168 return getColumnText(element, 0);
173 public CFileTypesPreferenceBlock() {
177 public CFileTypesPreferenceBlock(IProject input) {
178 fAddAssoc = new ArrayList<CFileTypeAssociation>();
179 fRemoveAssoc = new ArrayList<CFileTypeAssociation>();
184 public Control createControl(Composite parent) {
185 Composite control = new Composite(parent, SWT.NONE);
186 GridLayout controlLayout = new GridLayout(2, false);
188 controlLayout.marginHeight = 0;
189 controlLayout.marginWidth = 0;
191 control.setLayout(controlLayout);
192 control.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.GRAB_VERTICAL));
194 // Create the table viewer for file associations
196 Composite tablePane = new Composite(control, SWT.NONE);
197 GridLayout tablePaneLayout = new GridLayout();
198 GridData gridData = new GridData(GridData.FILL_BOTH);
200 tablePaneLayout.marginHeight = 0;
201 tablePaneLayout.marginWidth = 0;
203 tablePane.setLayout(tablePaneLayout);
204 tablePane.setLayoutData(gridData);
206 Table table = new Table(tablePane, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
208 TableLayout tblLayout = new TableLayout();
209 TableColumn col = null;
211 gridData = new GridData(GridData.FILL_BOTH);
212 gridData.grabExcessHorizontalSpace = true;
213 gridData.grabExcessVerticalSpace = true;
214 gridData.heightHint = SWTUtil.getTableHeightHint(table, 25);
215 gridData.widthHint = new PixelConverter(parent).convertWidthInCharsToPixels(60);
217 tblLayout.addColumnData(new ColumnWeightData(20));
218 tblLayout.addColumnData(new ColumnWeightData(60));
219 tblLayout.addColumnData(new ColumnWeightData(20));
221 table.setLayout(tblLayout);
222 table.setLayoutData(gridData);
223 table.setHeaderVisible(true);
224 table.setLinesVisible(true);
226 col = new TableColumn(table, SWT.LEFT);
227 col.setText(PreferencesMessages.CFileTypesPreferencePage_colTitlePattern);
229 col = new TableColumn(table, SWT.LEFT);
230 col.setText(PreferencesMessages.CFileTypesPreferencePage_colTitleDescription);
232 col = new TableColumn(table, SWT.LEFT);
233 col.setText(PreferencesMessages.CFileTypesPreferencePage_colTitleStatus);
235 // Create the button pane
237 Composite buttonPane = new Composite(control, SWT.NONE);
238 GridLayout buttonPaneLayout = new GridLayout();
240 buttonPaneLayout.marginHeight = 0;
241 buttonPaneLayout.marginWidth = 0;
243 buttonPane.setLayout(buttonPaneLayout);
244 buttonPane.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
248 fBtnNew = new Button(buttonPane, SWT.PUSH);
249 fBtnNew.setText(PreferencesMessages.CFileTypesPreferenceBlock_New___);
251 gridData = new GridData(GridData.FILL_HORIZONTAL);
252 gridData.widthHint = SWTUtil.getButtonWidthHint(fBtnNew);
253 fBtnNew.setLayoutData(gridData);
255 fBtnNew.addListener(SWT.Selection, new Listener() {
256 public void handleEvent(Event e) {
263 fBtnRemove = new Button(buttonPane, SWT.PUSH);
264 fBtnRemove.setText(PreferencesMessages.CFileTypesPreferenceBlock_Remove);
266 gridData = new GridData(GridData.FILL_HORIZONTAL);
267 gridData.widthHint = SWTUtil.getButtonWidthHint(fBtnRemove);
268 fBtnRemove.setLayoutData(gridData);
270 fBtnRemove.addListener(SWT.Selection, new Listener() {
271 public void handleEvent(Event e) {
276 // Hook up the viewer
278 fAssocViewer = new TableViewer(table);
280 fAssocViewer.setSorter(new AssocSorter());
281 fAssocViewer.setContentProvider(new AssocContentProvider());
282 fAssocViewer.setLabelProvider(new AssocLabelProvider());
283 fAssocViewer.setInput(getCFileTypeAssociations());
285 fAssocViewer.addSelectionChangedListener(new ISelectionChangedListener() {
286 public void selectionChanged(SelectionChangedEvent event) {
287 handleSelectionChanged();
291 handleSelectionChanged();
296 public void setEnabled(boolean enabled) {
297 fAssocViewer.getTable().setEnabled(enabled);
298 fBtnNew.setEnabled(enabled);
299 fBtnRemove.setEnabled(enabled);
303 public void setInput(IProject input) {
305 fRemoveAssoc.clear();
307 if (null != fAssocViewer) {
308 fAssocViewer.setInput(getCFileTypeAssociations());
313 private void setDirty(boolean dirty) {
317 public boolean performOk() {
318 boolean changed = fDirty;
321 CFileTypeAssociation[] add = fAddAssoc.toArray(new CFileTypeAssociation[fAddAssoc.size()]);
322 CFileTypeAssociation[] rem = fRemoveAssoc.toArray(new CFileTypeAssociation[fRemoveAssoc.size()]);
324 changed = add.length > 0 || rem.length > 0;
325 adjustAssociations(add, rem);
328 fRemoveAssoc.clear();
335 private CFileTypeAssociation[] getCFileTypeAssociations() {
336 ArrayList<CFileTypeAssociation> list = new ArrayList<CFileTypeAssociation>();
337 if (fInput == null) {
338 fillWithUserDefinedCFileTypeAssociations(list);
339 fillWithPredefinedCFileTypeAssociations(list);
341 fillWithProjectCFileTypeAssociations(list, fInput);
343 CFileTypeAssociation[] assocs = new CFileTypeAssociation[list.size()];
344 list.toArray(assocs);
348 protected void adjustAssociations(CFileTypeAssociation[] add, CFileTypeAssociation[] rem) {
349 IScopeContext context = null;
350 if (fInput != null) {
351 context = new ProjectScope(fInput);
353 removeAssociations(rem, context);
354 addAssociations(add, context);
357 final protected void addAssociations(CFileTypeAssociation[] add, IScopeContext context) {
358 for (int i = 0; i < add.length; ++i) {
359 CFileTypeAssociation assoc = add[i];
360 String spec = assoc.getSpec();
361 IContentType contentType = assoc.getContentType();
362 int type = IContentType.FILE_NAME_SPEC;
363 if (assoc.isExtSpec()) {
364 type = IContentType.FILE_EXTENSION_SPEC;
366 addAssociation(context, contentType, spec, type);
370 protected void addAssociation(IScopeContext context, IContentType contentType, String spec, int type) {
372 IContentTypeSettings settings = contentType.getSettings(context);
373 settings.addFileSpec(spec, type);
374 } catch (CoreException e) {
379 protected void removeAssociations(CFileTypeAssociation[] rem, IScopeContext context) {
380 for (int i = 0; i < rem.length; ++i) {
381 CFileTypeAssociation assoc = rem[i];
382 IContentType contentType = assoc.getContentType();
383 String spec = assoc.getSpec();
384 int type = IContentType.FILE_NAME_SPEC;
385 if (assoc.isExtSpec()) {
386 type = IContentType.FILE_EXTENSION_SPEC;
388 removeAssociation(context, contentType, spec, type);
392 protected void removeAssociation(IScopeContext context, IContentType contentType, String spec, int type) {
394 IContentTypeSettings settings = contentType.getSettings(context);
395 settings.removeFileSpec(spec, type);
396 } catch (CoreException e) {
401 public IContentType[] getRegistedContentTypes() {
402 if (fContentTypes == null) {
403 String [] ids = CoreModel.getRegistedContentTypeIds();
404 IContentTypeManager manager = Platform.getContentTypeManager();
405 IContentType [] ctypes = new IContentType[ids.length];
406 for (int i = 0; i < ids.length; i++) {
407 ctypes[i] = manager.getContentType(ids[i]);
409 fContentTypes = ctypes;
411 return fContentTypes;
414 private void fillWithUserDefinedCFileTypeAssociations(ArrayList<CFileTypeAssociation> list) {
415 IContentType[] ctypes = getRegistedContentTypes();
416 fillWithCFileTypeAssociations(ctypes, null, IContentType.IGNORE_PRE_DEFINED | IContentType.FILE_EXTENSION_SPEC, list);
417 fillWithCFileTypeAssociations(ctypes, null, IContentType.IGNORE_PRE_DEFINED | IContentType.FILE_NAME_SPEC, list);
420 private void fillWithPredefinedCFileTypeAssociations(ArrayList<CFileTypeAssociation> list) {
421 IContentType[] ctypes = getRegistedContentTypes();
422 fillWithCFileTypeAssociations(ctypes, null, IContentType.IGNORE_USER_DEFINED | IContentType.FILE_EXTENSION_SPEC, list);
423 fillWithCFileTypeAssociations(ctypes, null, IContentType.IGNORE_USER_DEFINED | IContentType.FILE_NAME_SPEC, list);
426 private void fillWithProjectCFileTypeAssociations(ArrayList<CFileTypeAssociation> list, IProject project) {
427 IContentType[] ctypes = getRegistedContentTypes();
428 IScopeContext context = new ProjectScope(project);
429 fillWithCFileTypeAssociations(ctypes, context, IContentType.IGNORE_PRE_DEFINED | IContentType.FILE_EXTENSION_SPEC, list);
430 fillWithCFileTypeAssociations(ctypes, context, IContentType.IGNORE_PRE_DEFINED | IContentType.FILE_NAME_SPEC, list);
433 private void fillWithCFileTypeAssociations(IContentType[] ctypes, IScopeContext context, int type, ArrayList<CFileTypeAssociation> list) {
434 for (IContentType ctype : ctypes) {
436 IContentTypeSettings setting = ctype.getSettings(context);
437 String[] specs = setting.getFileSpecs(type);
438 for (String spec : specs) {
439 CFileTypeAssociation assoc = new CFileTypeAssociation(spec, type, ctype);
442 } catch (CoreException e) {
448 private CFileTypeAssociation createAssociation(String pattern, IContentType contentType) {
449 int type = IContentType.FILE_NAME_SPEC;
450 if (pattern.startsWith("*.")) { //$NON-NLS-1$
451 pattern = pattern.substring(2);
452 type = IContentType.FILE_EXTENSION_SPEC;
454 return new CFileTypeAssociation(pattern, type | IContentType.IGNORE_PRE_DEFINED, contentType);
457 protected void handleSelectionChanged() {
458 IStructuredSelection sel = getSelection();
460 fBtnRemove.setEnabled(false);
462 boolean enabled = true;
463 List<?> elements = sel.toList();
464 for (Object element : elements) {
465 CFileTypeAssociation assoc = (CFileTypeAssociation) element;
466 if (assoc.isPredefined())
469 fBtnRemove.setEnabled(enabled);
473 final protected void handleAdd() {
474 CFileTypeAssociation assoc = null;
476 CFileTypeDialog dlg = new CFileTypeDialog(fBtnNew.getParent().getShell());
478 if (Window.OK == dlg.open()) {
479 assoc = createAssociation(dlg.getPattern(), dlg.getContentType());
480 if (handleAdd(assoc)) {
481 fAssocViewer.add(assoc);
487 private boolean handleAdd(CFileTypeAssociation assoc) {
488 // assoc is marked to be added.
489 if (containsIgnoreCaseOfSpec(fAddAssoc, assoc)) {
490 reportDuplicateAssociation(assoc);
493 // assoc exists, but is marked to be removed.
494 if (containsIgnoreCaseOfSpec(fRemoveAssoc, assoc)) {
495 if (!fRemoveAssoc.remove(assoc)) {
496 fAddAssoc.add(assoc);
501 // analyze current settings
502 IContentTypeSettings settings;
503 if (fInput == null) {
504 settings= assoc.getContentType();
508 settings= assoc.getContentType().getSettings(new ProjectScope(fInput));
509 } catch (CoreException e) {
510 ErrorDialog.openError(fBtnNew.getParent().getShell(),
511 PreferencesMessages.CFileTypesPreferenceBlock_addAssociationError_title,
512 null, e.getStatus());
516 String newSpec= assoc.getSpec();
517 String[] specs= settings.getFileSpecs(assoc.getFileSpecType());
518 for (String spec : specs) {
519 if (spec.equalsIgnoreCase(newSpec)) {
520 reportDuplicateAssociation(assoc);
524 fAddAssoc.add(assoc);
528 private boolean containsIgnoreCaseOfSpec(Collection<CFileTypeAssociation> collection, CFileTypeAssociation assoc) {
529 for (CFileTypeAssociation existing : collection) {
530 if (assoc.equalsIgnoreCaseOfSpec(existing)) {
537 private void reportDuplicateAssociation(CFileTypeAssociation assoc) {
538 MessageDialog.openError(fBtnNew.getParent().getShell(),
539 PreferencesMessages.CFileTypesPreferenceBlock_addAssociationError_title,
540 Messages.format(PreferencesMessages.CFileTypesPreferenceBlock_addAssociationErrorMessage,
541 assoc.getPattern(), assoc.getContentType().getName()));
544 final protected void handleRemove() {
545 IStructuredSelection sel = getSelection();
546 if ((null != sel) && (!sel.isEmpty())) {
547 for (Iterator<?> iter = sel.iterator(); iter.hasNext();) {
548 CFileTypeAssociation assoc = (CFileTypeAssociation) iter.next();
550 fAssocViewer.remove(assoc);
556 private void handleRemove(CFileTypeAssociation assoc) {
557 if (!fAddAssoc.remove(assoc)) {
558 fRemoveAssoc.add(assoc);
562 private IStructuredSelection getSelection() {
563 return (IStructuredSelection) fAssocViewer.getSelection();