package com.mobilesorcery.sdk.ui; import java.text.FieldPosition; import java.text.MessageFormat; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IDoubleClickListener; import org.eclipse.jface.viewers.IOpenListener; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.ISelectionChangedListener; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.OpenEvent; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StyledCellLabelProvider; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerCell; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Font; 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.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.ui.ISharedImages; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.forms.widgets.FormToolkit; import com.mobilesorcery.sdk.core.MoSyncProject; import com.mobilesorcery.sdk.core.MoSyncTool; import com.mobilesorcery.sdk.core.Pair; import com.mobilesorcery.sdk.core.Util; import com.mobilesorcery.sdk.profiles.IProfile; import com.mobilesorcery.sdk.profiles.IVendor; import com.mobilesorcery.sdk.profiles.Profile; import com.mobilesorcery.sdk.profiles.filter.DeviceCapabilitiesFilter; import com.mobilesorcery.sdk.ui.internal.DefaultProfileFilterDialog; public class PlatformSelectionComposite implements Listener, ISelectionChangedListener, IOpenListener, IDoubleClickListener { public class ProfileTextFilter extends ViewerFilter { private final String pattern; private final HashMap<Object, Boolean> match = new HashMap<Object, Boolean>(); public ProfileTextFilter(String pattern) { this.pattern = pattern; } @Override public boolean select(Viewer viewer, Object parentElement, Object element) { return doesMatch(element); } private boolean doesMatch(Object element) { Boolean match = this.match.get(element); if (match != null) { return match; } match = innerMatch(element); this.match.put(element, match); return match; } private boolean innerMatch(Object element) { if (element instanceof IProfile) { IProfile profile = (IProfile) element; String name = MoSyncTool.toString(profile); String[] subPatterns = pattern.split("\\s+"); // AND or OR? Let's go for AND. for (String subPattern : subPatterns) { if (!name.toLowerCase().contains(subPattern.toLowerCase())) { return false; } } return true; } else if (element instanceof IVendor) { IVendor vendor = (IVendor) element; for (IProfile profile : vendor.getProfiles()) { if (doesMatch(profile)) { return true; } } return false; } return true; } } public class RichProfileLabelProvider extends StyledCellLabelProvider { private Font originalFont = null; @Override public void update(ViewerCell cell) { Object obj = cell.getElement(); boolean isTargetProfile = false; Image image = null; String mainText = ""; String subText = ""; Point imageSize = new Point(16, 16); if (originalFont == null) { originalFont = cell.getFont(); } Font boldFont = MosyncUIPlugin.getDefault().getFont( MosyncUIPlugin.FONT_DEFAULT_BOLD); Font italicFont = MosyncUIPlugin.getDefault().getFont( MosyncUIPlugin.FONT_DEFAULT_ITALIC); if (obj instanceof IProfile) { IProfile profile = (IProfile) obj; mainText = profile.getName(); isTargetProfile = isTargetProfile(profile); subText = getProfileDescription(project, profile); if (subText.equals(mainText)) subText = ""; image = isTargetProfile ? MosyncUIPlugin.getDefault() .getImageRegistry() .get(MosyncUIPlugin.TARGET_PHONE_IMAGE) : MosyncUIPlugin.getDefault().getImageRegistry() .get(MosyncUIPlugin.PHONE_IMAGE); } else if (obj instanceof IVendor) { IVendor platform = (IVendor) obj; image = MosyncUIPlugin.getDefault().getPlatformImage(platform, imageSize); mainText = platform.getName(); } if (image != null) { cell.setImage(image); } int mainTextStyle = SWT.NORMAL; if (obj instanceof IVendor) { mainTextStyle = SWT.ITALIC; cell.setFont(italicFont); } else if (isTargetProfile) { mainTextStyle = SWT.BOLD; cell.setFont(boldFont); } else { cell.setFont(originalFont); } boolean hasSubText = Util.isEmpty(subText); String space = hasSubText ? "" : " "; String fullText = mainText + space + subText; cell.setText(fullText); StyleRange[] styleRanges = new StyleRange[2]; styleRanges[0] = new StyleRange(0, mainText.length(), null, null, mainTextStyle); Color gray = cell.getControl().getDisplay() .getSystemColor(SWT.COLOR_DARK_GRAY); styleRanges[1] = new StyleRange(mainText.length(), fullText.length() - mainText.length(), gray, null, mainTextStyle); cell.setStyleRanges(styleRanges); } private boolean isTargetProfile(IProfile profile) { return profile != null && getProfileToApply() != null && profile.equals(currentProfile); } } private static final String FILTER = "filter"; private static final String LOOKUP = "lookup"; private final Control control; private MoSyncProject project; private Text filterBox; private boolean filterShown; private TreeViewer profileTable; private int mode = SWT.NONE; private RichProfileLabelProvider profileLabelProvider; private IProfile currentProfile; private final int style; private Button applyButton; private ProfileContentProvider content; private Button cancelButton; private Button lookupButton; private Button filterButton; private Composite extrasPanel; private Text filterText; private Button convertButton; private Text lookupText; private static Shell CURRENT_SHELL; public PlatformSelectionComposite(Control control, int style) { this.control = control; this.style = style; } public void setProject(MoSyncProject project) { this.project = project; } private boolean hasSearchBox() { return (style & SWT.SEARCH) != 0; } private boolean alwaysShowSearchBox() { return mode == SWT.SEARCH || (style & SWT.BACKGROUND) == 0; } protected Composite createContentArea(Composite parent) { Shell main = CURRENT_SHELL; // main.setLayout(UIUtils.newPrefsLayout(1)); // inner = new Composite(main, SWT.NONE); // inner.setLayoutData(new GridData(GridData.FILL_BOTH)); FormToolkit toolkit = new FormToolkit(parent.getDisplay()); Composite profilesArea = toolkit.createComposite(main, SWT.BORDER); profilesArea.setLayoutData(new GridData(GridData.FILL_BOTH)); GridLayout layout = new GridLayout(1, false); layout.verticalSpacing = 0; profilesArea.setLayout(layout); filterBox = new Text(profilesArea, SWT.SEARCH); filterBox.addListener(SWT.KeyUp, this); filterBox.addListener(SWT.KeyDown, this); profileTable = new TreeViewer(profilesArea, SWT.NONE); profileLabelProvider = new RichProfileLabelProvider(); profileTable.setLabelProvider(profileLabelProvider); content = new ProfileContentProvider(project); profileTable.setContentProvider(content); GridData profileTableData = new GridData(SWT.FILL, SWT.FILL, true, true); // profileTableData.grabExcessVerticalSpace = true; // profileTableData.verticalAlignment = SWT.TOP; profileTable.getControl().setLayoutData(profileTableData); profileTable.getControl().setFocus(); profileTable.getControl().addListener(SWT.KeyDown, this); profileTable.getControl().addListener(SWT.Selection, this); profileTable.addDoubleClickListener(this); profileTable.addSelectionChangedListener(this); profileTable.addOpenListener(this); extrasPanel = toolkit.createComposite(main); extrasPanel .setLayout(new GridLayout( project.getProfileManagerType() == MoSyncTool.DEFAULT_PROFILE_TYPE ? 3 : 1, false)); createExtrasPanel(toolkit); /* * extrasPanel = new DescriptiveButtonBar(main, SWT.NONE); * extrasPanel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); * * extrasPanel.add(FILTER, * MosyncUIPlugin.getDefault().getImageRegistry() * .get(MosyncUIPlugin.IMG_FILTER), "Select Capabilities", "!"); * extrasPanel.add(LOOKUP, * MosyncUIPlugin.getDefault().getImageRegistry() * .get(MosyncUIPlugin.IMG_LOOKUP), "Find Profile for Device", "!"); * * toolkit.adapt(extrasPanel); */ Composite buttonBar = toolkit.createComposite(main); buttonBar.setLayout(new GridLayout(2, true)); buttonBar.setLayoutData(new GridData(SWT.RIGHT, SWT.DEFAULT, false, false)); cancelButton = new Button(buttonBar, SWT.PUSH); cancelButton.setText("&Cancel"); cancelButton.setLayoutData(new GridData(SWT.RIGHT, SWT.DEFAULT, false, false)); cancelButton.addListener(SWT.Selection, this); applyButton = new Button(buttonBar, SWT.PUSH); applyButton.setText("&Apply"); applyButton.setLayoutData(new GridData(SWT.RIGHT, SWT.DEFAULT, false, false)); applyButton.addListener(SWT.Selection, this); main.setBackground(profileTable.getControl().getBackground()); setMode(mode, true); showFilterBox(hasSearchBox() && alwaysShowSearchBox()); currentProfile = project.getTargetProfile(); if (currentProfile != null) { profileTable.refresh(currentProfile); profileTable.reveal(currentProfile); profileTable.setSelection(new StructuredSelection(currentProfile)); } toolkit.adapt(profileTable.getControl(), true, true); return main; } private void createExtrasPanel(FormToolkit toolkit) { if (project.getProfileManagerType() == MoSyncTool.LEGACY_PROFILE_TYPE) { convertButton = new Button(extrasPanel, SWT.PUSH); convertButton.setImage(MosyncUIPlugin.getDefault() .getImageRegistry().get(MosyncUIPlugin.IMG_FILTER)); convertButton.setText("Make Platform Based"); convertButton.addListener(SWT.Selection, this); Text convertText = new Text(extrasPanel, SWT.WRAP | SWT.READ_ONLY); convertText.setFont(MosyncUIPlugin.getDefault().getFont( MosyncUIPlugin.FONT_INFO_TEXT)); GridData convertTextData = new GridData( 2 * UIUtils.getDefaultFieldSize(), SWT.DEFAULT); convertTextData.verticalAlignment = SWT.TOP; convertText.setLayoutData(convertTextData); convertText .setText("Click the button above to convert this project into a platform based one (instead of the current device based)."); toolkit.adapt(convertText, true, true); } else { filterButton = new Button(extrasPanel, SWT.PUSH); filterButton.setText("Select Capabilities"); filterButton.setImage(MosyncUIPlugin.getDefault() .getImageRegistry().get(MosyncUIPlugin.IMG_FILTER)); filterButton.setLayoutData(new GridData(SWT.DEFAULT, SWT.DEFAULT)); filterButton.addListener(SWT.Selection, this); Label separator = new Label(extrasPanel, SWT.SEPARATOR | SWT.VERTICAL); separator.setLayoutData(new GridData(SWT.DEFAULT, SWT.FILL, true, false, 1, 2)); lookupButton = new Button(extrasPanel, SWT.PUSH); lookupButton.setLayoutData(new GridData(SWT.LEFT, SWT.DEFAULT, true, false)); initLookupButton(); Point lookupButtonSize = lookupButton.computeSize(SWT.DEFAULT, SWT.DEFAULT); filterButton.setLayoutData(new GridData(lookupButtonSize.x, lookupButtonSize.y)); lookupButton.addListener(SWT.Selection, this); filterText = new Text(extrasPanel, SWT.WRAP | SWT.READ_ONLY); filterText.setFont(MosyncUIPlugin.getDefault().getFont( MosyncUIPlugin.FONT_INFO_TEXT)); GridData filterTextData = new GridData( UIUtils.getDefaultFieldSize(), SWT.DEFAULT); filterTextData.verticalAlignment = SWT.TOP; filterText.setLayoutData(filterTextData); toolkit.adapt(filterText, true, true); lookupText = new Text(extrasPanel, SWT.WRAP | SWT.READ_ONLY); lookupText.setFont(MosyncUIPlugin.getDefault().getFont( MosyncUIPlugin.FONT_INFO_TEXT)); GridData lookupTextData = new GridData( UIUtils.getDefaultFieldSize(), SWT.DEFAULT); lookupTextData.verticalAlignment = SWT.TOP; lookupText.setLayoutData(lookupTextData); toolkit.adapt(lookupText, true, true); } CURRENT_SHELL.layout(); } private void showFilterBox(boolean show) { filterShown = show; GridData data = show ? new GridData(SWT.FILL, SWT.NONE, true, false) : new GridData(0, 0); filterBox.setLayoutData(data); filterBox.getParent().layout(); filterBox.getParent().redraw(); filterBox.forceFocus(); filterBox.setVisible(show); if (!show) { filterBox.setText(""); updateFilter(); } } public void show(int style) { boolean asDropdown = (style & SWT.DROP_DOWN) != 0; close(false); int shellStyle = (asDropdown ? SWT.ON_TOP | SWT.TOOL : SWT.RESIZE | SWT.TITLE | SWT.CLOSE); final Shell shell = new Shell(control.getShell(), shellStyle); shell.setText("Select active profile"); CURRENT_SHELL = shell; shell.setLayout(new GridLayout(1, false)); createContentArea(shell); int preferredWidth = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT).x; shell.setSize(preferredWidth, 3 * UIUtils.getDefaultListHeight() / 2); if (asDropdown) { Point controlLocation = control.toDisplay(asDropdown ? control .getLocation() : new Point(0, 0)); shell.setLocation(controlLocation.x, controlLocation.y + control.getSize().y); } else { UIUtils.centerShell(shell); } attachListeners(shell, asDropdown); shell.open(); } private void close(boolean saveTargetProfile) { if (CURRENT_SHELL != null && !CURRENT_SHELL.isDisposed()) { CURRENT_SHELL.close(); } CURRENT_SHELL = null; IProfile newProfile = getProfileToApply(); if (saveTargetProfile && newProfile != null) { project.setTargetProfile(newProfile); } } private IProfile getProfileToApply() { IProfile result = currentProfile; if (mode == SWT.SEARCH && result != null) { result = matchLegacyProfile(project, result); } return result; } private void attachListeners(Shell shell, boolean asDropdown) { if (asDropdown) { shell.addListener(SWT.Deactivate, this); } else { shell.addListener(SWT.Resize, this); } } @Override public void handleEvent(Event event) { if (event.type == SWT.KeyDown && shouldCharBeSentToSearchBox(event.character)) { if (!filterShown) { showFilterBox(hasSearchBox()); } if (event.widget != filterBox && event.character == SWT.BS) { filterBox.setText(filterBox.getText(0, Math.max(0, filterBox.getText().length() - 2))); updateFilter(); } else if (event.widget != filterBox) { filterBox.append("" + event.character); updateFilter(); } filterBox.setSelection(filterBox.getText().length()); filterBox.forceFocus(); } else if (event.widget == filterBox && event.type == SWT.KeyDown) { if (event.keyCode == SWT.ARROW_DOWN || event.keyCode == SWT.ARROW_UP) { profileTable.getControl().forceFocus(); if (profileTable.getSelection().isEmpty() && profileTable.getExpandedElements().length > 0) { profileTable.getTree().select( (TreeItem) profileTable.getExpandedElements()[0]); } } } else if (event.widget == filterBox && event.type == SWT.KeyUp && hasSearchBox()) { String filterText = filterBox.getText(); updateFilter(); if (filterText.length() == 0) { showFilterBox(alwaysShowSearchBox()); profileTable.getControl().forceFocus(); } } else if (event.widget == CURRENT_SHELL && event.type == SWT.Deactivate) { close(false); } else if (event.widget == CURRENT_SHELL && event.type == SWT.Resize) { CURRENT_SHELL.layout(); resize(CURRENT_SHELL.getBounds().width, CURRENT_SHELL.getBounds().height); } else if (event.keyCode == SWT.ESC && (event.type == SWT.KeyDown || event.type == SWT.KeyUp)) { close(false); } else if (event.widget == filterButton) { DefaultProfileFilterDialog dialog = new DefaultProfileFilterDialog( CURRENT_SHELL); dialog.setProject(project); dialog.open(); setMode(SWT.NONE, true); } else if (event.widget == lookupButton) { if (mode == SWT.SEARCH) { showFilterBox(false); } setMode(mode == SWT.SEARCH ? SWT.NONE : SWT.SEARCH, true); } else if (event.widget == convertButton) { project.setProfileManagerType(MoSyncTool.DEFAULT_PROFILE_TYPE); // Just close + open. close(false); show(SWT.NONE); } else if (event.widget == cancelButton) { close(false); } else if (event.widget == applyButton) { setAndClose(profileTable.getSelection()); } } private void updateFilter() { profileTable.setFilters(new ViewerFilter[] { new ProfileTextFilter( filterBox.getText()) }); } private void resize(int width, int height) { /* * GridLayout innerData = UIUtils.newPrefsLayout(1); * innerData.verticalSpacing = 0; inner.setLayout(innerData); * inner.setLayoutData(new GridData(width, height)); * inner.setSize(width, height); inner.getParent().layout(true, true); */ } private boolean shouldCharBeSentToSearchBox(char ch) { return Character.isLetterOrDigit(ch) || (ch == SWT.BS && !filterBox.getText().isEmpty()); } private void setMode(int mode, boolean force) { this.mode = mode; if (force || (this.mode != mode && profileTable != null)) { if (mode == SWT.SEARCH) { // The content provider uses the mosync tool to get // 'legacy' devices. content.setProject(null); profileTable.setInput(MoSyncTool.getDefault()); showFilterBox(true); } else { profileTable.setInput(project); content.setProject(project); } updateFilterMessage(); updateLookupMessage(); } } private void updateFilterMessage() { if (filterText != null) { int total = project.getProfileManager().getVendors().length; int filteredOut = total - project.getFilteredVendors().length; filterText .setText(MessageFormat .format("{0} of {1} platforms are filtered out, click the button above to filter platforms based on available capabilites.", filteredOut, total)); } } private void initLookupButton() { lookupButton.setText("Find Profile for Device"); lookupButton.setImage(MosyncUIPlugin.getDefault().getImageRegistry() .get(MosyncUIPlugin.IMG_LOOKUP)); if (lookupText != null) { lookupText .setText("Click the button above to find out which platform a specific device has."); } } private void updateLookupMessage() { if (lookupButton != null) { if (mode == SWT.SEARCH) { Image back = PlatformUI.getWorkbench().getSharedImages() .getImage(ISharedImages.IMG_TOOL_BACK); lookupButton.setImage(back); lookupButton.setText("Back"); if (lookupText != null) { lookupText.setText(""); } } else { initLookupButton(); } } } @Override public void selectionChanged(SelectionChangedEvent event) { updateFromSelection(event.getSelection()); } private void updateFromSelection(ISelection selection) { IStructuredSelection sSelection = (IStructuredSelection) selection; Object element = sSelection.getFirstElement(); IProfile oldProfile = currentProfile; currentProfile = null; if (element instanceof IProfile) { IProfile profile = (IProfile) element; currentProfile = profile; } if (oldProfile != null) { profileTable.refresh(); } if (currentProfile != null) { profileTable.refresh(currentProfile); } updateUI(); } private void updateUI() { applyButton.setEnabled(getProfileToApply() != null); } private IProfile matchLegacyProfile(MoSyncProject project, IProfile legacyProfile) { if (project.getProfileManagerType() == MoSyncTool.LEGACY_PROFILE_TYPE) { return legacyProfile; } else { return DeviceCapabilitiesFilter.matchLegacyProfile(project, legacyProfile); } } public void setAndClose(ISelection selection) { updateFromSelection(selection); if (getProfileToApply() != null) { close(true); } } @Override public void open(OpenEvent event) { setAndClose(event.getSelection()); } @Override public void doubleClick(DoubleClickEvent event) { IStructuredSelection selection = (IStructuredSelection) event .getSelection(); if (selection.getFirstElement() instanceof IVendor) { profileTable .setExpandedState(selection.getFirstElement(), !profileTable.getExpandedState(selection .getFirstElement())); } setAndClose(selection); } private String getProfileDescription(MoSyncProject project, IProfile profile) { if (mode == SWT.SEARCH) { IProfile platformProfile = matchLegacyProfile(project, profile); if (platformProfile != null) { return "\u2192 " + MoSyncTool.toString(platformProfile); } else { List<IProfile> filteredOutProfile = MoSyncTool.getDefault() .getProfileManager(MoSyncTool.DEFAULT_PROFILE_TYPE) .getProfilesForRuntime(profile.getRuntime()); if (filteredOutProfile != null && filteredOutProfile.size() > 0) { return MessageFormat.format("(Filtered out - {0})", MoSyncTool.toString(filteredOutProfile.get(0))); } } return MessageFormat.format("({0}: Unknown runtime", Profile.getAbbreviatedPlatform(profile)); } else { return getProfileShortDescription(project, profile); } } private String getProfileShortDescription(MoSyncProject project, IProfile profile) { return profile.getPackager().getShortDescription(project, profile); } }