• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.eclipse.org/org/documents/epl-v10.php
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.ide.eclipse.adt.internal.properties;
18 
19 import com.android.ide.eclipse.adt.AdtPlugin;
20 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper;
21 import com.android.ide.eclipse.adt.internal.project.ProjectChooserHelper.IProjectChooserFilter;
22 import com.android.ide.eclipse.adt.internal.sdk.ProjectState;
23 import com.android.ide.eclipse.adt.internal.sdk.Sdk;
24 import com.android.ide.eclipse.adt.internal.sdk.ProjectState.LibraryState;
25 import com.android.sdklib.internal.project.ProjectProperties;
26 import com.android.sdklib.internal.project.ProjectPropertiesWorkingCopy;
27 
28 import org.eclipse.core.resources.IProject;
29 import org.eclipse.core.runtime.IPath;
30 import org.eclipse.jdt.core.IJavaProject;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.events.ControlAdapter;
33 import org.eclipse.swt.events.ControlEvent;
34 import org.eclipse.swt.events.DisposeEvent;
35 import org.eclipse.swt.events.DisposeListener;
36 import org.eclipse.swt.events.SelectionAdapter;
37 import org.eclipse.swt.events.SelectionEvent;
38 import org.eclipse.swt.graphics.Image;
39 import org.eclipse.swt.graphics.Rectangle;
40 import org.eclipse.swt.layout.GridData;
41 import org.eclipse.swt.layout.GridLayout;
42 import org.eclipse.swt.widgets.Button;
43 import org.eclipse.swt.widgets.Composite;
44 import org.eclipse.swt.widgets.Label;
45 import org.eclipse.swt.widgets.Table;
46 import org.eclipse.swt.widgets.TableColumn;
47 import org.eclipse.swt.widgets.TableItem;
48 
49 import java.util.ArrayList;
50 import java.util.List;
51 import java.util.Set;
52 
53 
54 /**
55  * Self-contained UI to edit the library dependencies of a Project.
56  */
57 final class LibraryProperties {
58 
59     private Composite mTop;
60     private Table mTable;
61     private Image mMatchIcon;
62     private Image mErrorIcon;
63     private Button mAddButton;
64     private Button mRemoveButton;
65     private Button mUpButton;
66     private Button mDownButton;
67     private ProjectChooserHelper mProjectChooser;
68 
69     /**
70      * Original ProjectState being edited. This is read-only.
71      * @see #mPropertiesWorkingCopy
72      */
73     private ProjectState mState;
74     /**
75      * read-write copy of the properties being edited.
76      */
77     private ProjectPropertiesWorkingCopy mPropertiesWorkingCopy;
78 
79     private final List<ItemData> mItemDataList = new ArrayList<ItemData>();
80     private boolean mMustSave = false;
81 
82     /**
83      * Internal struct to store library info in the table item.
84      */
85     private final static class ItemData {
86         String relativePath;
87         IProject project;
88     }
89 
90     /**
91      * {@link IProjectChooserFilter} implementation that dynamically ignores libraries
92      * that are already dependencies.
93      */
94     IProjectChooserFilter mFilter = new IProjectChooserFilter() {
95         public boolean accept(IProject project) {
96             // first check if it's a library
97             ProjectState state = Sdk.getProjectState(project);
98             if (state != null) {
99                 if (state.isLibrary() == false || project == mState.getProject()) {
100                     return false;
101                 }
102 
103                 // then check if the library is not already part of the dependencies.
104                 for (ItemData data : mItemDataList) {
105                     if (data.project == project) {
106                         return false;
107                     }
108                 }
109 
110                 return true;
111             }
112 
113             return false;
114         }
115 
116         public boolean useCache() {
117             return false;
118         }
119     };
120 
LibraryProperties(Composite parent)121     LibraryProperties(Composite parent) {
122 
123         mMatchIcon = AdtPlugin.getImageDescriptor("/icons/match.png").createImage(); //$NON-NLS-1$
124         mErrorIcon = AdtPlugin.getImageDescriptor("/icons/error.png").createImage(); //$NON-NLS-1$
125 
126         // Layout has 2 column
127         mTop = new Composite(parent, SWT.NONE);
128         mTop.setLayout(new GridLayout(2, false));
129         mTop.setLayoutData(new GridData(GridData.FILL_BOTH));
130         mTop.setFont(parent.getFont());
131         mTop.addDisposeListener(new DisposeListener() {
132             public void widgetDisposed(DisposeEvent e) {
133                 mMatchIcon.dispose();
134                 mErrorIcon.dispose();
135             }
136         });
137 
138         mTable = new Table(mTop, SWT.BORDER | SWT.FULL_SELECTION | SWT.SINGLE);
139         mTable.setLayoutData(new GridData(GridData.FILL_BOTH));
140         mTable.setHeaderVisible(true);
141         mTable.setLinesVisible(false);
142         mTable.addSelectionListener(new SelectionAdapter() {
143             @Override
144             public void widgetSelected(SelectionEvent e) {
145                 resetEnabled();
146             }
147         });
148 
149         final TableColumn column0 = new TableColumn(mTable, SWT.NONE);
150         column0.setText("Reference");
151         final TableColumn column1 = new TableColumn(mTable, SWT.NONE);
152         column1.setText("Project");
153 
154         Composite buttons = new Composite(mTop, SWT.NONE);
155         buttons.setLayout(new GridLayout());
156         buttons.setLayoutData(new GridData(GridData.FILL_VERTICAL));
157 
158         mProjectChooser = new ProjectChooserHelper(parent.getShell(), mFilter);
159 
160         mAddButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
161         mAddButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
162         mAddButton.setText("Add...");
163         mAddButton.addSelectionListener(new SelectionAdapter() {
164             @Override
165             public void widgetSelected(SelectionEvent e) {
166                 IJavaProject javaProject = mProjectChooser.chooseJavaProject(null /*projectName*/,
167                         "Please select a library project");
168                 if (javaProject != null) {
169                     IProject iProject = javaProject.getProject();
170                     IPath relativePath = Sdk.makeRelativeTo(
171                             iProject.getLocation(), mState.getProject().getLocation());
172 
173                     addItem(relativePath.toString(), iProject, -1);
174                     resetEnabled();
175                     mMustSave = true;
176                 }
177             }
178         });
179 
180         mRemoveButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
181         mRemoveButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
182         mRemoveButton.setText("Remove");
183         mRemoveButton.addSelectionListener(new SelectionAdapter() {
184             @Override
185             public void widgetSelected(SelectionEvent e) {
186                 // selection is ensured and in single mode.
187                 TableItem selection = mTable.getSelection()[0];
188                 ItemData data = (ItemData) selection.getData();
189                 mItemDataList.remove(data);
190                 mTable.remove(mTable.getSelectionIndex());
191                 resetEnabled();
192                 mMustSave = true;
193             }
194         });
195 
196         Label l = new Label(buttons, SWT.SEPARATOR | SWT.HORIZONTAL);
197         l.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
198 
199         mUpButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
200         mUpButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
201         mUpButton.setText("Up");
202         mUpButton.addSelectionListener(new SelectionAdapter() {
203             @Override
204             public void widgetSelected(SelectionEvent e) {
205                 int index = mTable.getSelectionIndex();
206                 ItemData data = mItemDataList.remove(index);
207                 mTable.remove(index);
208 
209                 // add at a lower index.
210                 addItem(data.relativePath, data.project, index - 1);
211 
212                 // reset the selection
213                 mTable.select(index - 1);
214                 resetEnabled();
215                 mMustSave = true;
216             }
217         });
218 
219         mDownButton = new Button(buttons, SWT.PUSH | SWT.FLAT);
220         mDownButton.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
221         mDownButton.setText("Down");
222         mDownButton.addSelectionListener(new SelectionAdapter() {
223             @Override
224             public void widgetSelected(SelectionEvent e) {
225                 int index = mTable.getSelectionIndex();
226                 ItemData data = mItemDataList.remove(index);
227                 mTable.remove(index);
228 
229                 // add at a higher index.
230                 addItem(data.relativePath, data.project, index + 1);
231 
232                 // reset the selection
233                 mTable.select(index + 1);
234                 resetEnabled();
235                 mMustSave = true;
236             }
237         });
238 
239         adjustColumnsWidth(mTable, column0, column1);
240     }
241 
242     /**
243      * Sets or reset the content.
244      * @param state the {@link ProjectState} to display. This is read-only.
245      * @param propertiesWorkingCopy the working copy of {@link ProjectProperties} to modify.
246      */
setContent(ProjectState state, ProjectPropertiesWorkingCopy propertiesWorkingCopy)247     void setContent(ProjectState state, ProjectPropertiesWorkingCopy propertiesWorkingCopy) {
248         mState = state;
249         mPropertiesWorkingCopy = propertiesWorkingCopy;
250 
251         // reset content
252         mTable.removeAll();
253         mItemDataList.clear();
254 
255         // get the libraries and make a copy of the data we need.
256         List<LibraryState> libs = state.getLibraries();
257 
258         for (LibraryState lib : libs) {
259             ProjectState libState = lib.getProjectState();
260             addItem(lib.getRelativePath(), libState != null ? libState.getProject() : null, -1);
261         }
262 
263         mMustSave = false;
264 
265         resetEnabled();
266     }
267 
268     /**
269      * Saves the state of the UI into the {@link ProjectProperties} object that was returned by
270      * {@link #setContent(ProjectState)}.
271      * <p/>This does not update the {@link ProjectState} object that was provided, nor does it save
272      * the new properties on disk. Saving the properties on disk, via
273      * {@link ProjectProperties#save()}, and updating the {@link ProjectState} instance, via
274      * {@link ProjectState#reloadProperties()} must be done by the caller.
275      * @return <code>true</code> if there was actually new data saved in the project state, false
276      * otherwise.
277      */
save()278     boolean save() {
279         boolean mustSave = mMustSave;
280         if (mMustSave) {
281             // remove all previous library dependencies.
282             Set<String> keys = mPropertiesWorkingCopy.keySet();
283             for (String key : keys) {
284                 if (key.startsWith(ProjectProperties.PROPERTY_LIB_REF)) {
285                     mPropertiesWorkingCopy.removeProperty(key);
286                 }
287             }
288 
289             // now add the new libraries.
290             int index = 1;
291             for (ItemData data : mItemDataList) {
292                 mPropertiesWorkingCopy.setProperty(ProjectProperties.PROPERTY_LIB_REF + index++,
293                         data.relativePath);
294             }
295         }
296 
297         mMustSave = false;
298         return mustSave;
299     }
300 
301     /**
302      * Enables or disables the whole widget.
303      * @param enabled whether the widget must be enabled or not.
304      */
setEnabled(boolean enabled)305     void setEnabled(boolean enabled) {
306         if (enabled == false) {
307             mTable.setEnabled(false);
308             mAddButton.setEnabled(false);
309             mRemoveButton.setEnabled(false);
310             mUpButton.setEnabled(false);
311             mDownButton.setEnabled(false);
312         } else {
313             mTable.setEnabled(true);
314             mAddButton.setEnabled(true);
315             resetEnabled();
316         }
317     }
318 
resetEnabled()319     private void resetEnabled() {
320         int index = mTable.getSelectionIndex();
321         mRemoveButton.setEnabled(index != -1);
322         mUpButton.setEnabled(index > 0);
323         mDownButton.setEnabled(index != -1 && index < mTable.getItemCount() - 1);
324     }
325 
326     /**
327      * Adds a new item and stores a {@link Stuff} into {@link #mStuff}.
328      *
329      * @param relativePath the relative path of the library entry
330      * @param project the associated IProject
331      * @param index if different than -1, the index at which to insert the item.
332      */
addItem(String relativePath, IProject project, int index)333     private void addItem(String relativePath, IProject project, int index) {
334         ItemData data = new ItemData();
335         data.relativePath = relativePath;
336         data.project = project;
337         TableItem item;
338         if (index == -1) {
339             mItemDataList.add(data);
340             item = new TableItem(mTable, SWT.NONE);
341         } else {
342             mItemDataList.add(index, data);
343             item = new TableItem(mTable, SWT.NONE, index);
344         }
345         item.setData(data);
346         item.setText(0, data.relativePath);
347         item.setImage( data.project != null ? mMatchIcon : mErrorIcon);
348         item.setText(1, data.project != null ? data.project.getName() : "?");
349     }
350 
351     /**
352      * Adds a listener to adjust the columns width when the parent is resized.
353      * <p/>
354      * If we need something more fancy, we might want to use this:
355      * http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet77.java?view=co
356      */
adjustColumnsWidth(final Table table, final TableColumn column0, final TableColumn column1)357     private void adjustColumnsWidth(final Table table,
358             final TableColumn column0,
359             final TableColumn column1) {
360         // Add a listener to resize the column to the full width of the table
361         table.addControlListener(new ControlAdapter() {
362             @Override
363             public void controlResized(ControlEvent e) {
364                 Rectangle r = table.getClientArea();
365                 column0.setWidth(r.width * 50 / 100); // 50%
366                 column1.setWidth(r.width * 50 / 100); // 50%
367             }
368         });
369     }
370 }
371 
372