upload tizen1.0 source
[sdk/ide/product.git] / org.eclipse.cdt.ui / src / org / eclipse / cdt / internal / ui / preferences / CFileTypesPreferenceBlock.java
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
7  *
8  * Contributors:
9  *     TimeSys Corporation - Initial implementation
10  *******************************************************************************/
11 package org.eclipse.cdt.internal.ui.preferences;
12
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Iterator;
16 import java.util.List;
17
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;
54
55 import org.eclipse.cdt.core.model.CoreModel;
56
57 import org.eclipse.cdt.internal.ui.util.Messages;
58 import org.eclipse.cdt.internal.ui.util.SWTUtil;
59
60 /*
61  * Preference block that encapsulates the controls used
62  * for displaying/editing CDT file type associations
63  */
64 public class CFileTypesPreferenceBlock {
65
66         private static final int        COL_PATTERN             = 0;
67         private static final int        COL_DESCRIPTION = 1;
68         private static final int        COL_STATUS      = 2;
69         
70         private ArrayList<CFileTypeAssociation> fAddAssoc;
71         private ArrayList<CFileTypeAssociation> fRemoveAssoc;
72         private boolean         fDirty = false;
73         private IProject        fInput;
74         private IContentType[] fContentTypes;
75
76         private TableViewer             fAssocViewer;
77         private Button                          fBtnNew;
78         private Button                          fBtnRemove;
79
80         private class AssocSorter extends ViewerSorter {
81                 @Override
82                 public int category(Object element) {
83                         if (element instanceof CFileTypeAssociation) {
84                                 CFileTypeAssociation assoc = (CFileTypeAssociation) element;
85                                 if (assoc.isExtSpec()) {
86                                         return 10;
87                                 }
88                                 return 20;
89                         }
90                         return 30;
91                 }
92         }
93         
94         private class AssocContentProvider implements IStructuredContentProvider {
95                 CFileTypeAssociation[] assocs;
96                 
97                 public Object[] getElements(Object inputElement) {
98                         return assocs;
99                 }
100
101                 public void dispose() {
102                         assocs = null;
103                 }
104
105                 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
106                         if (newInput instanceof CFileTypeAssociation[]) {       
107                                 assocs = (CFileTypeAssociation[]) newInput;
108                         }
109                 }
110         }
111         
112         private class AssocLabelProvider implements ILabelProvider, ITableLabelProvider {
113                 private ListenerList listeners = new ListenerList();
114                 
115                 public Image getColumnImage(Object element, int columnIndex) {
116                         if (element instanceof CFileTypeAssociation) {
117                                 if (COL_PATTERN == columnIndex) {
118                                         return null;
119                                 }
120                         }
121                         return null;
122                 }
123
124                 public String getColumnText(Object element, int columnIndex) {
125                         if (element instanceof CFileTypeAssociation) {
126                                 CFileTypeAssociation assoc = (CFileTypeAssociation) element;
127                                 switch (columnIndex) {
128                                         case COL_PATTERN:
129                                                 return assoc.getPattern();
130                                         
131                                         case COL_DESCRIPTION:
132                                                 return assoc.getDescription();
133                                                 
134                                         case COL_STATUS:
135                                                 if (assoc.isUserDefined()) {
136                                                         return PreferencesMessages.CFileTypesPreferencePage_userDefined; 
137                                                 } else if (assoc.isPredefined()) {
138                                                         return PreferencesMessages.CFileTypesPreferencePage_preDefined; 
139                                                 }
140                                                 return new String();
141                                 }
142                         }
143                         return element.toString();
144                 }
145
146                 public void addListener(ILabelProviderListener listener) {
147                         listeners.add(listener);
148                 }
149
150                 public void dispose() {
151                         listeners.clear();
152                         listeners = null;
153                 }
154
155                 public boolean isLabelProperty(Object element, String property) {
156                         return false;
157                 }
158
159                 public void removeListener(ILabelProviderListener listener) {
160                         listeners.remove(listener);
161                 }
162
163                 public Image getImage(Object element) {
164                         return getColumnImage(element, 0);
165                 }
166
167                 public String getText(Object element) {
168                         return getColumnText(element, 0);
169                 }
170
171         }
172
173         public CFileTypesPreferenceBlock() {
174                 this(null);
175         }
176
177         public CFileTypesPreferenceBlock(IProject input) {
178                 fAddAssoc = new ArrayList<CFileTypeAssociation>();
179                 fRemoveAssoc = new ArrayList<CFileTypeAssociation>();
180                 fInput = input;
181                 setDirty(false);
182         }
183
184         public Control createControl(Composite parent) {
185                 Composite       control                 = new Composite(parent, SWT.NONE);
186                 GridLayout      controlLayout   = new GridLayout(2, false);
187
188                 controlLayout.marginHeight = 0;
189                 controlLayout.marginWidth  = 0;
190
191                 control.setLayout(controlLayout);
192                 control.setLayoutData(new GridData(GridData.FILL_BOTH | GridData.GRAB_VERTICAL));
193                 
194                 // Create the table viewer for file associations
195                 
196                 Composite       tablePane               = new Composite(control, SWT.NONE);
197                 GridLayout      tablePaneLayout = new GridLayout();
198                 GridData        gridData                = new GridData(GridData.FILL_BOTH);
199                 
200                 tablePaneLayout.marginHeight = 0;
201                 tablePaneLayout.marginWidth  = 0;
202                 
203                 tablePane.setLayout(tablePaneLayout);
204                 tablePane.setLayoutData(gridData);
205
206                 Table table = new Table(tablePane, SWT.MULTI | SWT.V_SCROLL | SWT.BORDER | SWT.FULL_SELECTION);
207                 
208                 TableLayout     tblLayout       = new TableLayout();
209                 TableColumn     col                     = null;
210                 
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);
216                 
217                 tblLayout.addColumnData(new ColumnWeightData(20));
218                 tblLayout.addColumnData(new ColumnWeightData(60));
219                 tblLayout.addColumnData(new ColumnWeightData(20));
220                 
221                 table.setLayout(tblLayout);
222                 table.setLayoutData(gridData);
223                 table.setHeaderVisible(true);
224                 table.setLinesVisible(true);            
225                 
226                 col = new TableColumn(table, SWT.LEFT);
227                 col.setText(PreferencesMessages.CFileTypesPreferencePage_colTitlePattern); 
228                 
229                 col = new TableColumn(table, SWT.LEFT);
230                 col.setText(PreferencesMessages.CFileTypesPreferencePage_colTitleDescription); 
231                 
232                 col = new TableColumn(table, SWT.LEFT);
233                 col.setText(PreferencesMessages.CFileTypesPreferencePage_colTitleStatus); 
234
235                 // Create the button pane
236
237                 Composite       buttonPane                      = new Composite(control, SWT.NONE);
238                 GridLayout      buttonPaneLayout        = new GridLayout();
239                 
240                 buttonPaneLayout.marginHeight = 0;
241                 buttonPaneLayout.marginWidth  = 0;
242                 
243                 buttonPane.setLayout(buttonPaneLayout);
244                 buttonPane.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_BEGINNING));
245
246                 // New button
247                 
248                 fBtnNew         = new Button(buttonPane, SWT.PUSH);
249                 fBtnNew.setText(PreferencesMessages.CFileTypesPreferenceBlock_New___);  
250                 
251                 gridData        = new GridData(GridData.FILL_HORIZONTAL);
252                 gridData.widthHint      = SWTUtil.getButtonWidthHint(fBtnNew);
253                 fBtnNew.setLayoutData(gridData);
254                 
255                 fBtnNew.addListener(SWT.Selection, new Listener() {
256                         public void handleEvent(Event e) {
257                                 handleAdd();
258                         }
259                 });
260
261                 // Remove button
262                 
263                 fBtnRemove      = new Button(buttonPane, SWT.PUSH);
264                 fBtnRemove.setText(PreferencesMessages.CFileTypesPreferenceBlock_Remove);  
265                 
266                 gridData        = new GridData(GridData.FILL_HORIZONTAL);
267                 gridData.widthHint      = SWTUtil.getButtonWidthHint(fBtnRemove);
268                 fBtnRemove.setLayoutData(gridData);
269                 
270                 fBtnRemove.addListener(SWT.Selection, new Listener() {
271                         public void handleEvent(Event e) {
272                                 handleRemove();
273                         }
274                 });
275
276                 // Hook up the viewer
277                 
278                 fAssocViewer = new TableViewer(table);
279                 
280                 fAssocViewer.setSorter(new AssocSorter());
281                 fAssocViewer.setContentProvider(new AssocContentProvider());
282                 fAssocViewer.setLabelProvider(new AssocLabelProvider());
283                 fAssocViewer.setInput(getCFileTypeAssociations());
284
285                 fAssocViewer.addSelectionChangedListener(new ISelectionChangedListener() {
286                         public void selectionChanged(SelectionChangedEvent event) {
287                                 handleSelectionChanged();
288                         }
289                 });
290                 
291                 handleSelectionChanged();
292                 
293                 return control;
294         }
295
296         public void setEnabled(boolean enabled) {
297                 fAssocViewer.getTable().setEnabled(enabled);
298                 fBtnNew.setEnabled(enabled);
299                 fBtnRemove.setEnabled(enabled);
300                 setDirty(enabled);
301         }
302         
303         public void setInput(IProject input) {
304                 fAddAssoc.clear();
305                 fRemoveAssoc.clear();
306                 fInput = input;
307                 if (null != fAssocViewer) {
308                         fAssocViewer.setInput(getCFileTypeAssociations());
309                 }
310                 setDirty(true);
311         }
312
313         private void setDirty(boolean dirty) {
314                 fDirty = dirty;
315         }
316         
317         public boolean performOk() {
318                 boolean changed = fDirty;
319                 
320                 if (fDirty) {
321                         CFileTypeAssociation[] add = fAddAssoc.toArray(new CFileTypeAssociation[fAddAssoc.size()]);
322                         CFileTypeAssociation[] rem = fRemoveAssoc.toArray(new CFileTypeAssociation[fRemoveAssoc.size()]);
323                         
324                         changed = add.length > 0 || rem.length > 0;
325                         adjustAssociations(add, rem);
326         
327                         fAddAssoc.clear();
328                         fRemoveAssoc.clear();
329
330                         setDirty(false);
331                 }               
332                 return changed;
333         }
334         
335         private CFileTypeAssociation[] getCFileTypeAssociations() {
336                 ArrayList<CFileTypeAssociation> list = new ArrayList<CFileTypeAssociation>();
337                 if (fInput == null) {
338                         fillWithUserDefinedCFileTypeAssociations(list);
339                         fillWithPredefinedCFileTypeAssociations(list);
340                 } else {
341                         fillWithProjectCFileTypeAssociations(list, fInput);
342                 }
343                 CFileTypeAssociation[] assocs = new CFileTypeAssociation[list.size()];
344                 list.toArray(assocs);
345                 return assocs;
346         }
347
348         protected void adjustAssociations(CFileTypeAssociation[] add, CFileTypeAssociation[] rem) {
349                 IScopeContext context = null;
350                 if (fInput != null) {
351                         context = new ProjectScope(fInput);
352                 }
353                 removeAssociations(rem, context);
354                 addAssociations(add, context);
355         }
356
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;
365                         }                       
366                         addAssociation(context, contentType, spec, type);
367                 }
368         }
369
370         protected void addAssociation(IScopeContext context, IContentType contentType, String spec, int type) {
371                 try {
372                         IContentTypeSettings settings = contentType.getSettings(context);
373                         settings.addFileSpec(spec, type);
374                 } catch (CoreException e) {
375                         // ignore ??
376                 }
377         }
378
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;
387                         }
388                         removeAssociation(context, contentType, spec, type);
389                 }
390         }
391
392         protected void removeAssociation(IScopeContext context, IContentType contentType, String spec, int type) {
393                 try {
394                         IContentTypeSettings settings = contentType.getSettings(context);
395                         settings.removeFileSpec(spec, type);
396                 } catch (CoreException e) {
397                         // ignore ??
398                 }
399         }
400
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]);
408                         }
409                         fContentTypes = ctypes;
410                 }
411                 return fContentTypes;
412         }
413
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);
418         }
419
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);
424         }
425
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);
431         }
432
433         private void fillWithCFileTypeAssociations(IContentType[] ctypes, IScopeContext context, int type, ArrayList<CFileTypeAssociation> list) {
434                 for (IContentType ctype : ctypes) {
435                         try {
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);
440                                         list.add(assoc);
441                                 }
442                         } catch (CoreException e) {
443                                 // skip over it.
444                         }
445                 }
446         }
447
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;
453                 }
454                 return new CFileTypeAssociation(pattern, type | IContentType.IGNORE_PRE_DEFINED, contentType);
455         }
456
457         protected void handleSelectionChanged() {
458                 IStructuredSelection sel = getSelection();
459                 if (sel.isEmpty()) {
460                         fBtnRemove.setEnabled(false);
461                 } else {
462                         boolean enabled = true;
463                         List<?> elements = sel.toList();
464                         for (Object element : elements) {
465                                 CFileTypeAssociation assoc = (CFileTypeAssociation) element;
466                                 if (assoc.isPredefined())
467                                         enabled = false;
468                         }
469                         fBtnRemove.setEnabled(enabled);
470                 }
471         }
472
473         final protected void handleAdd() {
474                 CFileTypeAssociation assoc = null;
475
476                 CFileTypeDialog dlg = new CFileTypeDialog(fBtnNew.getParent().getShell());
477                 
478                 if (Window.OK == dlg.open()) {
479                         assoc = createAssociation(dlg.getPattern(), dlg.getContentType());
480                         if (handleAdd(assoc)) {
481                                 fAssocViewer.add(assoc);
482                                 setDirty(true);
483                         }
484                 }
485         }
486         
487         private boolean handleAdd(CFileTypeAssociation assoc) {
488                 // assoc is marked to be added.
489                 if (containsIgnoreCaseOfSpec(fAddAssoc, assoc)) {
490                         reportDuplicateAssociation(assoc);
491                         return false;
492                 }                       
493                 // assoc exists, but is marked to be removed.
494                 if (containsIgnoreCaseOfSpec(fRemoveAssoc, assoc)) {
495                         if (!fRemoveAssoc.remove(assoc)) {
496                                 fAddAssoc.add(assoc);
497                         }
498                         return true;
499                 }
500
501                 // analyze current settings
502                 IContentTypeSettings settings;
503                 if (fInput == null) {
504                         settings= assoc.getContentType();
505                 }
506                 else {
507                         try {
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());
513                                 return false;
514                         }
515                 }
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);
521                                 return false;
522                         }                               
523                 }
524                 fAddAssoc.add(assoc);
525                 return true;
526         }       
527         
528         private boolean containsIgnoreCaseOfSpec(Collection<CFileTypeAssociation> collection, CFileTypeAssociation assoc) {
529                 for (CFileTypeAssociation existing : collection) {
530                         if (assoc.equalsIgnoreCaseOfSpec(existing)) {
531                                 return true;
532                         }
533                 }
534                 return false;
535         }
536
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()));
542         }
543
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();
549                                 handleRemove(assoc);
550                                 fAssocViewer.remove(assoc);
551                                 setDirty(true);
552                         }
553                 }
554         }
555
556         private void handleRemove(CFileTypeAssociation assoc) {
557                 if (!fAddAssoc.remove(assoc)) {
558                         fRemoveAssoc.add(assoc);
559                 }
560         }
561
562         private IStructuredSelection getSelection() {
563                 return (IStructuredSelection) fAssocViewer.getSelection();
564         }
565 }