Skip to main content
summaryrefslogtreecommitdiffstats
blob: 946dc31eccb13d42ad8369c08e530328601ecba3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
/*******************************************************************************
 * Copyright (c) 2004, 2010 BitMethods Inc and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     BitMethods Inc - initial API and implementation
 *     Sascha Radike <sradike@ejectlag.com> - Support for workspace browsing and small improvements
 *     James Blackburn (Broadcom Corp.)
 *******************************************************************************/
package org.eclipse.cdt.utils.ui.controls;

import java.util.ArrayList;
import java.util.Arrays;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.AbstractOperation;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.core.commands.operations.IUndoableOperation;
import org.eclipse.core.commands.operations.ObjectUndoContext;
import org.eclipse.core.commands.operations.OperationHistoryFactory;
import org.eclipse.core.filesystem.URIUtil;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.variables.IStringVariableManager;
import org.eclipse.core.variables.VariablesPlugin;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.IDialogSettings;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;
import org.eclipse.ui.swt.IFocusService;
import org.eclipse.ui.views.navigator.ResourceComparator;

import org.eclipse.cdt.core.cdtvariables.CdtVariableException;
import org.eclipse.cdt.core.cdtvariables.ICdtVariable;
import org.eclipse.cdt.ui.CDTSharedImages;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.newui.CDTStatusInfo;
import org.eclipse.cdt.ui.newui.TypedCDTViewerFilter;
import org.eclipse.cdt.utils.cdtvariables.CdtVariableResolver;
import org.eclipse.cdt.utils.cdtvariables.IVariableContextInfo;
import org.eclipse.cdt.utils.cdtvariables.IVariableSubstitutor;
import org.eclipse.cdt.utils.cdtvariables.SupplierBasedCdtVariableManager;
import org.eclipse.cdt.utils.cdtvariables.SupplierBasedCdtVariableSubstitutor;

import org.eclipse.cdt.internal.core.resources.ResourceLookup;

import org.eclipse.cdt.internal.ui.newui.Messages;

/**
 * Instances of this class allow the user to add, remove, delete, moveup and movedown
 * the items in the list control.
 *
 * @noextend This class is not intended to be subclassed by clients.
 */
public class FileListControl {

	/**
	 * Constant copied from ManagedBuild IOption indicating that the entries in this
	 * FileListControl are neither files nor directories -- they're treated as a plain
	 * String list.
	 *
	 * #see org.eclipse.cdt.managedbuilder.core.IOption#BROWSE_NONE
	 * @since 5.2
	 */
	public static final int BROWSE_NONE = 0;
	/**
	 * Constant copied from ManagedBuild IOption indicating that the entries in this
	 * FileListControl are Files.
	 *
	 * #see org.eclipse.cdt.managedbuilder.core.IOption#BROWSE_FILE
	 * @since 5.2
	 */
	public static final int BROWSE_FILE = 1;
	/**
	 * Constant copied from ManagedBuild IOption indicating that the entries in this
	 * FileListControl are Directories.
	 *
	 * #see org.eclipse.cdt.managedbuilder.core.IOption#BROWSE_DIR
	 * @since 5.2
	 */
	public static final int BROWSE_DIR = 2;

	/**
	 * Multi-purpose dialog to prompt the user for a value, path, or file.
	 *
	 * @since 2.0
	 */
	class SelectPathInputDialog extends InputDialog {

		private String[] values = new String[0];

		private int type;
		/* True if user successfully set the text value by a browse dialog */
		private boolean fSetByBrowseDialog = false;

		/**
		 * @param parentShell
		 * @param dialogTitle
		 * @param dialogMessage
		 * @param initialValue
		 * @param validator
		 * @param browseType
		 */
		public SelectPathInputDialog(Shell parentShell, String dialogTitle, String dialogMessage, String initialValue, IInputValidator validator, int browseType) {
			super(parentShell, dialogTitle, dialogMessage, initialValue, validator);
			this.type = browseType;
		}

		private String getInitialValue(String initialValue) {
			IDialogSettings dialogSettings = CUIPlugin.getDefault().getDialogSettings();
			// restore file
			String sfile = dialogSettings.get(FileListControl.class.getName());
			if (sfile != null && (initialValue == null || initialValue.isEmpty())) {
				return sfile;
			}
			return initialValue;
		}

		protected void saveWidgetValues() {
			IDialogSettings dialogSettings = CUIPlugin.getDefault().getDialogSettings();
			// save file name
			dialogSettings.put(FileListControl.class.getName(), getValue());
		}

		@Override
		public boolean close() {
			saveWidgetValues();
			return super.close();
		}

		@Override
		protected Control createContents(Composite parent) {
			Control area = super.createContents(parent);
			getText().setText(getInitialValue(getValue()));
			return area;
		}
		/**
		 * Returns true if the value has been set by a browse dialog.
		 */
		public boolean isValueSetByBrowse() {
			return fSetByBrowseDialog;
		}

		/**
		 * Allow this dialog to return multiple entires
		 * @return String[] represeting the collected values
		 */
		public String[] getValues() {
			// If values only has one entry or fewer, then return getValue() to catch more recent changes to edit field
			if (values.length <= 1)
				return new String[] { getValue() };
			return values;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.jface.dialogs.Dialog#createButtonsForButtonBar(org.eclipse.swt.widgets.Composite)
		 */
		@Override
		protected void createButtonsForButtonBar(Composite parent) {
			super.createButtonsForButtonBar(parent);

			if (((type == BROWSE_DIR) || (type == BROWSE_FILE)
					) && (fWorkspaceSupport)) {

				/* Browse button for workspace folders/files */
				final Button workspaceButton = createButton(parent, 3, WORKSPACEBUTTON_NAME, false);
				workspaceButton.addSelectionListener(new SelectionAdapter() {
					@Override
					public void widgetSelected(SelectionEvent ev) {
						/* Before opening the browse dialog we try to convert the current
						 * path text to a valid workspace resource, so we can set it
						 * as initial selection in the dialog.
						 *
						 * First we remove all double-quotes. Then the build macro provider
						 * will resolve all macros/variables (like workspace_loc, ...).
						 *
						 * If the workspace location path is a prefix of our resolved path,
						 * we will remove that part and finally get a full path relative to the
						 * workspace. We can use that path to set the initially selected resource.
						 */

						String currentPathText = getText().getText();

						/* Remove double quotes */
						currentPathText = currentPathText.replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$

						/* Resolve variables */
						IStringVariableManager variableManager = VariablesPlugin.getDefault().getStringVariableManager();

						/* See if we can discover the project from the context *
						 * and check whether the path must be resolved... */
						IProject project = null;

						if(contextInfo != null) {
							try {
								// Try to find the project
								ICdtVariable var = SupplierBasedCdtVariableManager.getVariable(PROJECTNAME_VAR, contextInfo, true);
								if (var != null && var.getValueType() == ICdtVariable.VALUE_TEXT)
									project = ResourcesPlugin.getWorkspace().getRoot().getProject(var.getStringValue());

								// Try to resolve the currentPathText
								IVariableSubstitutor varSubs = new SupplierBasedCdtVariableSubstitutor(contextInfo, "", "");  //$NON-NLS-1$//$NON-NLS-2$
								currentPathText = CdtVariableResolver.resolveToString(currentPathText,
										varSubs);

							} catch (CdtVariableException e) {
								// It's OK not to find the project... carry on as before
							}
						} else {
							try {
								currentPathText = variableManager.performStringSubstitution(currentPathText,
										false);
							} catch (CoreException e) {
								// ignore
							}
						}
						IResource resource = null;
						if (!currentPathText.isEmpty()) {
							IResource rs[] = null;
							try {
								rs = ResourcesPlugin.getWorkspace().getRoot()
										.findContainersForLocationURI(URIUtil.toURI(currentPathText));
							} catch (Exception e) {
								// rs will be null here, exception is throw is path is not absolute
							}
							if (rs == null || rs.length == 0)
								resource = ResourceLookup.selectFileForLocation(new Path(currentPathText),
										null);
							else
								resource = rs[0];
						}

						/* Create workspace folder/file selection dialog and
						 * set initial selection */
						ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(getShell(),
								new WorkbenchLabelProvider(), new WorkbenchContentProvider());

		                dialog.setInput(ResourcesPlugin.getWorkspace().getRoot());
		                dialog.setComparator(new ResourceComparator(ResourceComparator.NAME));

						if (type == BROWSE_DIR)	{
							dialog.setInitialSelection(resource);
							Class<?>[] filteredResources = {IContainer.class, IProject.class};
							dialog.addFilter(new TypedCDTViewerFilter(filteredResources));
							dialog.setTitle(WORKSPACE_DIR_DIALOG_TITLE);
			                dialog.setMessage(WORKSPACE_DIR_DIALOG_MSG);
						} else {
							dialog.setInitialSelection(resource);
							dialog.setValidator(new ISelectionStatusValidator() {
							    @Override
								public IStatus validate(Object[] selection) {
							    	if (selection != null)
						    			for (Object sel : selection)
							    			if (!(sel instanceof IFile))
							    				return new CDTStatusInfo(IStatus.ERROR, WORKSPACE_FILE_DIALOG_ERR);
							    	return new CDTStatusInfo();
							    }
							});
							dialog.setTitle(WORKSPACE_FILE_DIALOG_TITLE);
			                dialog.setMessage(WORKSPACE_FILE_DIALOG_MSG);
						}

						/* Open dialog and process result.
						 * If a resource has been selected we create a workspace relative path for it.
						 * Use ${ProjName} if the full path is relative to the context's location */
						if (dialog.open() == Window.OK) {
							fSetByBrowseDialog = true;

							Object[] rs = dialog.getResult();

							if (rs != null) {
								int i = 0;
								values = new String[rs.length];
								for (Object o : rs) {
									resource = (IResource) o;
									if (resource.getProject().equals(project))
										values[i++] = variableManager.generateVariableExpression(WORKSPACELOC_VAR,
												PROJECTNAME_PATH.append(resource.getProjectRelativePath()).makeAbsolute().toString());
									else
										values[i++] = variableManager.generateVariableExpression(WORKSPACELOC_VAR,
												resource.getFullPath().toString());
								}
								// If only one entry, update the text field
								if (values.length == 1)
									getText().setText(values[0]);
								else
									// More then one item selected and OK pressed. Exit this edit dialog
									buttonPressed(IDialogConstants.OK_ID);
							}
						}
					}
				});
			}

			if (type != BROWSE_NONE) {
				/* Browse button for external directories/files */
				final Button externalButton = createButton(parent, 4, FILESYSTEMBUTTON_NAME, false);
				externalButton.addSelectionListener(new SelectionAdapter() {
					@Override
					public void widgetSelected(SelectionEvent ev) {
						String currentName;
						String result;
						switch (type) {
							case BROWSE_DIR :
								DirectoryDialog dialog = new DirectoryDialog(getParentShell(),
										SWT.OPEN|SWT.APPLICATION_MODAL);
								currentName = getText().getText();
								if(currentName != null && currentName.trim().length() != 0) {
									dialog.setFilterPath(currentName);
								} else if(FileListControl.this.filterPath != null) {
									dialog.setFilterPath(FileListControl.this.filterPath);
								}
								dialog.setMessage(FILESYSTEM_DIR_DIALOG_MSG);
								result = dialog.open();
								if(result != null) {
									fSetByBrowseDialog = true;
									getText().setText(result);
								}
								break;
							case BROWSE_FILE:
								FileDialog browseDialog = new FileDialog(getParentShell());
								currentName = getText().getText();
								if (currentName != null && currentName.trim().length() != 0) {
									browseDialog.setFilterPath(currentName);
								} else if (FileListControl.this.filterPath != null) {
									browseDialog.setFilterPath(FileListControl.this.filterPath);
								}
								if (FileListControl.this.filterExtensions != null) {
									browseDialog.setFilterExtensions(FileListControl.this.filterExtensions);
								}
								result = browseDialog.open();
								if (result != null) {
									fSetByBrowseDialog = true;
									getText().setText(result);
								}
								break;
						}
					}
				});
			}
		}

	}

	/**
	 * An extended List control with support for cut / copy / paste & undo
	 * Needs to be public for the copy method to be called by the platform via reflection
	 * @since 5.2
	 * @noinstantiate This class is not intended to be instantiated by clients.
	 */
	public final class ClipboardList extends List {
		private Clipboard clipboard;

		public ClipboardList(Composite parent, int style) {
			super (parent, style);
		}
		private String[] getClipboardContents() {
			Clipboard cp = getClipboard();
			String contents = (String)cp.getContents(TextTransfer.getInstance());
			if (contents != null) {
				String[] arr = contents.split("\n"); //$NON-NLS-1$
				return arr;
			}
			return new String[0];
		}
		public void copy() {
			String[] toCopy = getSelection();
			if (toCopy != null && toCopy.length > 0) {
				StringBuilder sb = new StringBuilder();
				for (String item : toCopy)
					sb.append(item.trim()).append("\n"); //$NON-NLS-1$
				Clipboard cp = getClipboard();
				cp.setContents(new Object[]{sb.toString().trim()}, new Transfer[] {TextTransfer.getInstance()});
			}
		}
		public void cut() {
			copy();
			// Only remove from the list box if the cut was successful
			if (Arrays.equals(getClipboardContents(), getSelection()))
				removePressed();
		}
		public void paste() {
			String[] pasteBuffer = getClipboardContents();
			int i = getSelectionIndex();
			// insert items at the correct location
			for (String item : pasteBuffer)
				if (!item.trim().equals("")) //$NON-NLS-1$
					add(item.trim(), ++i);
			checkNotificationNeeded();
		}
		public void undo() {
			try {
				operationHistory.undo(undoContext, null, null);
			} catch (ExecutionException e) {
				CUIPlugin.log(e);
			}
		}
		public void redo() {
			try {
				operationHistory.redo(undoContext, null, null);
			} catch (ExecutionException e) {
				CUIPlugin.log(e);
			}
		}
		private Clipboard getClipboard() {
			if (clipboard == null)
				clipboard = new Clipboard(Display.getDefault());
			return clipboard;
		}
		@Override
		public void dispose() {
			super.dispose();
			if (clipboard != null)
				clipboard.dispose();
		}
		/**
		 * Handle backspace / delete key
		 */
		public void delete() {
			removePressed();
		}
		@Override
		protected void checkSubclass() {
			// We're adding action handlers, override...
		}
	}


	/* Variable names */
	/* See CdtMacroSupplier: used for making absolute paths relative if desired */
	private static final String WORKSPACELOC_VAR = "workspace_loc"; //$NON-NLS-1$
	private static final String PROJECTNAME_VAR = "ProjName"; //$NON-NLS-1$
	private static final IPath PROJECTNAME_PATH = new Path(VariablesPlugin.getDefault().getStringVariableManager().generateVariableExpression(PROJECTNAME_VAR, null));

	/* Names, messages and titles */
	private static final String WORKSPACEBUTTON_NAME = Messages.FileListControl_button_workspace;
	private static final String FILESYSTEMBUTTON_NAME = Messages.FileListControl_button_fs;

	private static final String ADD_STR = Messages.FileListControl_add;
	private static final String DEL_STR = Messages.FileListControl_delete;
	private static final String EDIT_STR = Messages.FileListControl_edit;
	private static final String MOVEUP_STR = Messages.FileListControl_moveup;
	private static final String MOVEDOWN_STR = Messages.FileListControl_movedown;
	private static final String FILE_TITLE_ADD = Messages.BrowseEntryDialog_file_title_add;
	private static final String DIR_TITLE_ADD = Messages.BrowseEntryDialog_dir_title_add;
	private static final String FILE_TITLE_EDIT = Messages.BrowseEntryDialog_file_title_edit;
	private static final String DIR_TITLE_EDIT = Messages.BrowseEntryDialog_dir_title_edit;
	private static final String WORKSPACE_DIR_DIALOG_TITLE = Messages.BrowseEntryDialog_wsp_dir_dlg_title;
	private static final String WORKSPACE_FILE_DIALOG_TITLE = Messages.BrowseEntryDialog_wsp_file_dlg_title;
	private static final String WORKSPACE_DIR_DIALOG_MSG = Messages.FileListControl_BrowseEntryDialog_wsp_dir_dlg_msg;
	private static final String WORKSPACE_FILE_DIALOG_MSG = Messages.FileListControl_BrowseEntryDialog_wsp_file_dlg_msg;
	private static final String WORKSPACE_FILE_DIALOG_ERR = Messages.FileListControl_BrowseEntryDialog_wsp_file_dlg_err;
	private static final String FILESYSTEM_DIR_DIALOG_MSG = Messages.BrowseEntryDialog_fs_dir_dlg_msg;
	private static final String FILE_MSG = Messages.BrowseEntryDialog_message_file;
	private static final String DIR_MSG = Messages.BrowseEntryDialog_message_directory;
	private static final String TITLE = Messages.BuildPropertyCommon_label_title;

	/** flag which prevents us from resetting the prompt for delete flag */
	private boolean neverPromptForDelete;
	/** Flag indicating whether the user should be prompted for delete */
	private boolean promptForDelete;

	//toolbar
	private ToolBar toolBar;
	// toolbar items
	private ToolItem addItem, deleteItem, editItem, moveUpItem, moveDownItem;
	// title label
	private Label title;
	// list control
	private ClipboardList list;
	private String compTitle;
	private SelectionListener selectionListener;
	private GridData tgdata, grid3, grid4, grid2;

	// The type of browse support that is required
	private int browseType;
	private String filterPath;
	private String[] filterExtensions;
	/** The base path that should be used when adding new resources */
	private IPath path = new Path(""); //$NON-NLS-1$

	/* Workspace support */
	private boolean fWorkspaceSupport = false;
	private IVariableContextInfo contextInfo;
	/** Undo support */
	IUndoContext undoContext;
	IOperationHistory operationHistory = OperationHistoryFactory.getOperationHistory();

	private java.util.List<IFileListChangeListener> listeners = new ArrayList<IFileListChangeListener>();
	private String[] oldValue;

	//images
	private final Image IMG_ADD = CDTSharedImages.getImage(CDTSharedImages.IMG_FILELIST_ADD);
	private final Image IMG_DEL = CDTSharedImages.getImage(CDTSharedImages.IMG_FILELIST_DEL);
	private final Image IMG_EDIT = CDTSharedImages.getImage(CDTSharedImages.IMG_FILELIST_EDIT);
	private final Image IMG_MOVEUP = CDTSharedImages.getImage(CDTSharedImages.IMG_FILELIST_MOVEUP);
	private final Image IMG_MOVEDOWN = CDTSharedImages.getImage(CDTSharedImages.IMG_FILELIST_MOVEDOWN);

	/**
	 * Constructor
	 *
	 * @param parent
	 * @param compTitle
	 * @param type
	 * @param promptForDelete indicates whether the user should be prompted on delete
	 * @see #FileListControl(Composite, String, int)
	 * @since 5.2
	 */
	public FileListControl(Composite parent, String compTitle, int type, boolean promptForDelete) {
		this(parent, compTitle, type);
		this.promptForDelete = promptForDelete;
		this.neverPromptForDelete = !promptForDelete;
	}

	/**
	 * Constructor
	 *
	 * This FileListControl only prompts the user on Delete for BROWSE_FILE and BROWSE_DIR
	 * @param parent
	 * @param compTitle
	 * @param type one of the IOption BROWSE types
	 * @see #BROWSE_NONE
	 * @see #BROWSE_FILE
	 * @see #BROWSE_DIR
	 */
	public FileListControl(Composite parent, String compTitle, int type) {
		promptForDelete = type == BROWSE_FILE || type == BROWSE_DIR;

		// Default to no browsing
		browseType = type;

		//file panel
		Composite filePanel = new Composite(parent, SWT.NONE);
		GridLayout form1 = new GridLayout();
		form1.numColumns = 1;
		form1.horizontalSpacing = 0;
		form1.verticalSpacing = 0;
		form1.marginHeight = 0;
		form1.marginWidth = 0;
		filePanel.setLayout(form1);
		filePanel.setLayoutData(new GridData(GridData.FILL_BOTH));

		// title panel
		Composite titlePanel = new Composite(filePanel, SWT.BORDER);
		GridLayout titleform = new GridLayout(2, false);
		titleform.horizontalSpacing = 0;
		titleform.verticalSpacing = 0;
		titleform.marginHeight = 0;
		titleform.marginWidth = 0;
		titlePanel.setLayout(titleform);
		tgdata = new GridData(GridData.FILL_HORIZONTAL);
		tgdata.heightHint = IDialogConstants.BUTTON_BAR_HEIGHT;
		titlePanel.setLayoutData(tgdata);
		title = new Label(titlePanel, SWT.NONE | SWT.BOLD);
		this.compTitle = "  " + compTitle; //$NON-NLS-1$
		title.setText(this.compTitle);
		grid2 = new GridData(GridData.FILL_HORIZONTAL);
		title.setLayoutData(grid2);
		//button panel
		Composite buttonPanel = new Composite(titlePanel, SWT.NONE);
		GridLayout form2 = new GridLayout();
		form2.numColumns = 5;
		form2.horizontalSpacing = 0;
		form2.verticalSpacing = 0;
		form2.marginWidth = 0;
		form2.marginHeight = 0;
		buttonPanel.setLayout(form2);
		// toolbar
		toolBar = new ToolBar(buttonPanel, SWT.HORIZONTAL | SWT.RIGHT
				| SWT.FLAT);
		// add toolbar item
		addItem = new ToolItem(toolBar, SWT.PUSH);
		addItem.setImage(IMG_ADD);
		addItem.setToolTipText(ADD_STR);
		addItem.addSelectionListener(getSelectionListener());
		// delete toolbar item
		deleteItem = new ToolItem(toolBar, SWT.PUSH);
		deleteItem.setImage(IMG_DEL);
		deleteItem.setToolTipText(DEL_STR);
		deleteItem.addSelectionListener(getSelectionListener());
		// edit toolbar item
		editItem = new ToolItem(toolBar, SWT.PUSH);
		editItem.setImage(IMG_EDIT);
		editItem.setToolTipText(EDIT_STR);
		editItem.addSelectionListener(getSelectionListener());
		// moveup toolbar item
		moveUpItem = new ToolItem(toolBar, SWT.PUSH);
		moveUpItem.setImage(IMG_MOVEUP);
		moveUpItem.setToolTipText(MOVEUP_STR);
		moveUpItem.addSelectionListener(getSelectionListener());
		// movedown toolbar item
		moveDownItem = new ToolItem(toolBar, SWT.PUSH);
		moveDownItem.setImage(IMG_MOVEDOWN);
		moveDownItem.setToolTipText(MOVEDOWN_STR);
		moveDownItem.addSelectionListener(getSelectionListener());
		grid3 = new GridData(GridData.FILL_HORIZONTAL | GridData.HORIZONTAL_ALIGN_END);
		buttonPanel.setLayoutData(grid3);
		// list control
		list = new ClipboardList(filePanel, SWT.V_SCROLL | SWT.H_SCROLL | SWT.BORDER | SWT.MULTI);
		grid4 = new GridData(GridData.FILL_BOTH);
		// force the list to be no wider than the title bar
		Point preferredSize = titlePanel.computeSize(SWT.DEFAULT, SWT.DEFAULT);
		grid4.widthHint = preferredSize.x;
		grid4.heightHint = preferredSize.y * 3;
		grid4.horizontalSpan = 2;
		list.setLayoutData(grid4);
		list.addSelectionListener(getSelectionListener());
		//Add a double-click event handler
		list.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseDoubleClick(MouseEvent e) {
				// Popup the editor on the selected item from the list
				editSelection();
			}
		});
		// Add a delete key listener
		list.addKeyListener(new KeyAdapter() {
			/* (non-Javadoc)
			 * @see org.eclipse.swt.events.KeyAdapter#keyPressed(org.eclipse.swt.events.KeyEvent)
			 */
			@Override
			public void keyPressed(KeyEvent e) {
				switch (e.keyCode) {
				case SWT.BS:
				case SWT.DEL:
					if (e.stateMask == SWT.NONE)
						removePressed();
					break;
				}
			}
		});

		// Set-up Undo history
		undoContext = new ObjectUndoContext(this);
		operationHistory.setLimit(undoContext, 50);

		// Add command handlers for undo to the control
		try {
			IFocusService fs = (IFocusService)PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
								.getActivePart().getSite().getService(IFocusService.class);
			fs.addFocusTracker(list, "org.eclipse.cdt.ui.FileListControl"); //$NON-NLS-1$
		} catch (Exception e) {
			// Any of the get* methods may return null. As this is in the UI constructor for this control
			// it shouldn't happen. Log and carry on.
			CUIPlugin.log(e);
		}

		selectionChanged();
	}

	/**
	 * Set list values
	 *
	 * @param listVal
	 */
	public void setList(String[] listVal) {
		if (list != null) {
			list.removeAll();
		}
		for (String element : listVal) {
			list.add(element);
		}
		checkNotificationNeeded();
	}

	public void addChangeListener(IFileListChangeListener listener){
		listeners.add(listener);
	}

	public void removeChangeListener(IFileListChangeListener listener){
		listeners.remove(listener);
	}

	/**
	 * Checks whether a notification is needed, and notifies listeners
	 *
	 * Persist any changes in the undo history.
	 *
	 * This method must be called after every change to the contents of the list box
	 *
	 * At end of method oldValue.equals(list.getItems())
	 */
	public void checkNotificationNeeded(){
		final String items[] = getItems();
		if(oldValue != null) {
			if (Arrays.equals(oldValue, items))
				return;

			// Add some context to the undo history
			IUndoableOperation op = new AbstractOperation("") { //$NON-NLS-1$
				final String[] previousValue = oldValue;
				final String[] newValue = items;
				@Override
				public IStatus undo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
					list.setItems(previousValue);
					notifyListeners(newValue, previousValue);
					oldValue = previousValue;
					return Status.OK_STATUS;
				}
				@Override
				public IStatus redo(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
					list.setItems(newValue);
					notifyListeners(previousValue, newValue);
					oldValue = newValue;
					return Status.OK_STATUS;
				}
				@Override
				public IStatus execute(IProgressMonitor monitor, IAdaptable info) throws ExecutionException {
					return Status.CANCEL_STATUS;
				}
			};
			op.addContext(undoContext);
			operationHistory.add(op);
			System.arraycopy(items, 0, oldValue = new String[items.length], 0, items.length);
			notifyListeners(oldValue, items);
			list.setFocus(); // Ensure this control retains focus
		} else
			System.arraycopy(items, 0, oldValue = new String[items.length], 0, items.length);
	}

	public void notifyListeners(String oldVal[], String newVal[]){
		for (IFileListChangeListener listener: listeners) {
			listener.fileListChanged(this,oldVal,newVal);
		}
	}

	/**
	 * Set selection
	 *
	 * @param sel
	 */
	public void setSelection(int sel) {
		if (list.getItemCount() > 0)
			list.setSelection(sel);
		selectionChanged();
	}
	/**
	 * Set default selection
	 */
	public void setSelection() {
		if (list.getItemCount() > 0)
			list.setSelection(0);
	}
	/**
	 * removes all items from list control
	 */
	public void removeAll() {
		if (list != null){
			list.removeAll();
			checkNotificationNeeded();
		}
	}
	/**
	 * get list items
	 */
	public String[] getItems() {
		return list.getItems();
	}
	/**
	 * Create selection listener for buttons
	 */
	private void createSelectionListener() {
		selectionListener = new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent event) {
				Widget widget = event.widget;
				if (widget == addItem) {
					addPressed();
				} else if (widget == deleteItem) {
					removePressed();
				} else if (widget == moveUpItem) {
					upPressed();
				} else if (widget == moveDownItem) {
					downPressed();
				} else if (widget == list) {
					selectionChanged();
				} else if (widget == editItem) {
					editSelection();
				}
			}
		};
	}
	/**
	 * Returns selection listener
	 *
	 * @return
	 */
	private SelectionListener getSelectionListener() {
		if (selectionListener == null)
			createSelectionListener();
		return selectionListener;
	}

	/**
	 * This method will be called when the add button is pressed
	 */
	private void addPressed() {
		// Prompt user for a new item
		String[] input = getNewInputObject();

		// Add it to the list
		if (input.length > 0) {
			int index = list.getSelectionIndex();
			int i = 0;
			for (String s : input)
				list.add(s, index + ++i);
			list.setSelection(index + 1);
			checkNotificationNeeded();
		}

		selectionChanged();
	}

	/**
	 * This method will be called when the remove button is pressed
	 */
	private void removePressed() {
		if (list.getSelectionCount() == 0 || list.getSelectionIndex() == -1)
			return;
		boolean delDir = true;
		if (promptForDelete) {
			String quest = Messages.FileListControl_deletedialog_message;
			String title = Messages.FileListControl_deletedialog_title;
			delDir = MessageDialog.openQuestion(list.getShell(), title, quest);
		}
		if (delDir){
			int i;
			while ((i = list.getSelectionIndex()) != -1)
				list.remove(i);
			checkNotificationNeeded();
		}
		selectionChanged();
	}
	/**
	 * This method will be called when the move up button is pressed
	 */
	private void upPressed() {
		int index = list.getSelectionIndex();
		String curSelList = list.getItem(index);
		String preList = list.getItem(index - 1);
		list.setItem(index - 1, curSelList);
		list.setItem(index, preList);
		list.setSelection(index - 1);
		checkNotificationNeeded();
		selectionChanged();
	}
	/**
	 * This method will be called when the move down button is pressed
	 */
	private void downPressed() {
		int index = list.getSelectionIndex();
		String curSelList = list.getItem(index);
		String nextList = list.getItem(index + 1);
		list.setItem(index + 1, curSelList);
		list.setItem(index, nextList);
		list.setSelection(index + 1);
		checkNotificationNeeded();
		selectionChanged();
	}
	/**
	 * This method will be called when the edit button is pressed
	 */
	private void editSelection() {
		final int index = list.getSelectionIndex();
		if (index != -1) {
			String selItem = list.getItem(index);
			if (selItem != null) {
				/* Use SelectPathInputDialog for IOption.BROWSE_DIR and
				 * IOption.BROWSE_FILE. Use simple input dialog otherwise.
				 */
				InputDialog dialog;
				if ((browseType == BROWSE_DIR) ||
						(browseType == BROWSE_FILE)) {

					String title;
					String message;
					if (browseType == BROWSE_DIR) {
						title = DIR_TITLE_EDIT;
						message = DIR_MSG;
					} else {
						title = FILE_TITLE_EDIT;
						message = FILE_MSG;
					}
					dialog =  new SelectPathInputDialog(getListControl().getShell(), title, message, selItem, null, browseType);
				} else {
					String title = Messages.FileListControl_editdialog_title;
					dialog = new InputDialog(null, title, compTitle, selItem, null);
				}

				if (dialog.open() == Window.OK) {
					String[] newItems;

					/* If newItem is a directory or file path we need to
					 * double-quote it if required. We only do this if the user
					 * selected a new path using a browse button. If he/she simply
					 * edited the text, we skip this so the user can remove quotes if he/she
					 * wants to.
					 */
					if (dialog instanceof SelectPathInputDialog) {
						SelectPathInputDialog selDialog = (SelectPathInputDialog)dialog;
						newItems = selDialog.getValues();
						if (selDialog.isValueSetByBrowse())
							for (int i = 0 ; i < newItems.length ; i++)
								newItems[i] = doubleQuotePath(newItems[i]);
					} else
						newItems = new String[] { dialog.getValue() };

					// If no change, return
					if (newItems.length == 1 && newItems[0].equals(selItem))
						return;

					// Replace the changed item & insert new items
					list.setItem(index, newItems[0]);
					for (int i = 1 ; i < newItems.length ; i++)
						list.add(newItems[i], index + i);
					checkNotificationNeeded();
					selectionChanged();
				}
			}
		}
	}

	/**
	 * This method will be called when the list selection changed
	 */
	public void selectionChanged() {
		int index = list.getSelectionIndex();
		int size = list.getItemCount();
		int selectionCount = list.getSelectionCount();
		deleteItem.setEnabled(size > 0);
		moveUpItem.setEnabled(size > 1 && index > 0 && selectionCount == 1);
		moveDownItem.setEnabled(size > 1 && index >= 0 && index < size - 1 && selectionCount == 1);
		editItem.setEnabled(selectionCount == 1);
	}
	/**
	 * Returns List control
	 */
	public List getListControl() {
		return list;
	}

	/**
	 * Sets the IPath of the project the field editor was
	 * created for.
	 *
	 * @param path The path to the
	 */
	public void setPath(IPath path) {
		this.path = path;
	}

	/**
	 * Set browseType
	 * @deprecated This class should be constructed with the correct type
	 */
	@Deprecated
	public void setType(int type) {
		browseType = type;
		if (!neverPromptForDelete)
			promptForDelete = type == BROWSE_FILE || type == BROWSE_DIR;
	}

	/**
	 * Sets the default filter-path for the underlying Browse dialog. Only applies when browseType is 'file' or 'dir'.
	 * @param filterPath
	 *
	 * @since 5.2
	 */
	public void setFilterPath(String filterPath) {
		this.filterPath = filterPath;
	}

	/**
	 * Sets the filter-extensions for the underlying Browse dialog. Only applies when browseType is 'file'.
	 * @param filterExtensions
	 *
	 * @since 5.2
	 */
	public void setFilterExtensions(String[] filterExtensions) {
		this.filterExtensions = filterExtensions;
	}

	/**
	 * Enable/Disable workspace support. If enabled, the workspace browse button
	 * will be visible in the SelectPathInputDialog.
	 * @param enable
	 */
	public void setWorkspaceSupport(boolean enable)	{
		fWorkspaceSupport = enable;
	}

	/**
	 * Set the field editor context.
	 */
	public void setContext(IVariableContextInfo info) {
		contextInfo = info;
		for(;info != null;info = info.getNext()){
			/*
			if(info.getContextType() == IBuildMacroProvider.CONTEXT_PROJECT){
				IManagedProject mngProj = (IManagedProject)info.getContextData();
				this.rc = mngProj.getOwner();
				break;
			}
			*/
		}
	}

	/**
	 * Returns the input dialog string
	 */
	private String[] getNewInputObject() {
		// Create a dialog to prompt for a new list item
		String[] input = new String[0];
		String title = ""; //$NON-NLS-1$
		String message = ""; //$NON-NLS-1$
		String initVal = ""; //$NON-NLS-1$

		if (browseType == BROWSE_DIR) {
			title = DIR_TITLE_ADD;
			message = DIR_MSG;
			initVal = path.toString();
		} else if (browseType == BROWSE_FILE) {
			title = FILE_TITLE_ADD;
			message = FILE_MSG;
			initVal = path.toString();
		} else {
			title = TITLE;
			message = compTitle;
		}

		// Prompt for value
		SelectPathInputDialog dialog = new SelectPathInputDialog(getListControl().getShell(), title, message, initVal, null, browseType);
		if (dialog.open() == Window.OK) {
			input = dialog.getValues();

			/* Double-quote (if required) the text if it is a directory or file */
			if (input.length > 0) {
				if (browseType == BROWSE_DIR || browseType == BROWSE_FILE)
					for (int i = 0 ; i < input.length ; i++)
						input[i] = doubleQuotePath(input[i]);
			}
		}

		return input;
	}

	public Label getLabelControl(){
		return title;
	}

	public void setEnabled(boolean enabled){
		title.setEnabled(enabled);
		toolBar.setEnabled(enabled);
		list.setEnabled(enabled);
	}

	/**
	 * Double-quotes a path name if it contains white spaces, backslahes
	 * or a macro/variable (We don't know if a macro will contain spaces, so we
	 * have to be on the safe side).
	 * @param pathName The path name to double-quote.
	 * @return
	 */
	private String doubleQuotePath(String pathName)	{
		/* Trim */
		pathName = pathName.trim();

		/* Check if path is already double-quoted */
		boolean bStartsWithQuote = pathName.startsWith("\""); //$NON-NLS-1$
		boolean bEndsWithQuote = pathName.endsWith("\""); //$NON-NLS-1$

		/* Check for spaces, backslashes or macros */
		int i = pathName.indexOf(" ") + pathName.indexOf("\\") //$NON-NLS-1$ //$NON-NLS-2$
			+ pathName.indexOf("${"); //$NON-NLS-1$

		/* If indexof didn't fail all three times, double-quote path */
		if (i != -3) {
			if (!bStartsWithQuote)
				pathName = "\"" + pathName; //$NON-NLS-1$
			if (!bEndsWithQuote)
				pathName = pathName + "\""; //$NON-NLS-1$
		}

		return pathName;
	}
}

Back to the top