// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. package org.chromium.debug.ui.propertypages; import org.chromium.debug.ui.DialogUtils.Updater; import org.chromium.debug.ui.DialogUtils.ValueConsumer; import org.chromium.debug.ui.DialogUtils.ValueSource; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.StyleRange; import org.eclipse.swt.custom.StyledText; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; 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.Label; /** * UI control element that shows several last segments of the file path and allow to show more * or less. The segments is used in the system to match file in remote VM, thus showing more * segments makes matching more accurate. Accurateness value is the number of visible segments. */ class AccuratenessControl { private final Logic logic; AccuratenessControl(Composite composite, String[] pathSegments, int initialAccuratenessValue) { Composite inner; { inner = new Composite(composite, SWT.NONE); GridLayout layout = new GridLayout(1, false); layout.marginHeight = 0; inner.setLayout(layout); inner.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); } Composite topLineBlock; { topLineBlock = new Composite(inner, SWT.NONE); GridLayout layout = new GridLayout(2, false); layout.marginHeight = 0; layout.marginWidth = 0; topLineBlock.setLayout(layout); topLineBlock.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); } Label label = new Label(topLineBlock, SWT.NONE); label.setText(Messages.AccuratenessControl_LABEL_USE); final Button less; final Button more; { Composite buttonsGroup = new Composite(topLineBlock, SWT.NONE); GridLayout layout = new GridLayout(2, false); layout.marginHeight = 0; buttonsGroup.setLayout(layout); less = new Button(buttonsGroup, SWT.NONE); less.setText(Messages.AccuratenessControl_LESS_BUTTON); more = new Button(buttonsGroup, SWT.NONE); more.setText(Messages.AccuratenessControl_MORE_BUTTON); } final PathLabel pathLabel = new PathLabel(inner, pathSegments); pathLabel.getControl().setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); pathLabel.setVisiblePart(initialAccuratenessValue); // Padding new Label(inner, SWT.NONE); Elements elements = new Elements() { @Override public AccuratenessControl.PathLabel getPathLabel() { return pathLabel; } @Override public Button getMoreButton() { return more; } @Override public Button getLessButton() { return less; } }; logic = createLogic(elements); logic.updateAll(); } int getAccuratenessValue() { return logic.getAccuratenessValue(); } private static Logic createLogic(final Elements elements) { // The logic is Updater-based. // Presented path of several visible segments influences whether 'more' or 'less' // buttons enabled. final Updater updater = new Updater(); final AccuratenessControl.PathLabel pathElement = elements.getPathLabel(); final ValueSource<Integer> visiblePartSource = new ValueSource<Integer>() { @Override public Integer getValue() { return pathElement.getVisiblePartSize(); } }; updater.addSource(updater.rootScope(), visiblePartSource); // Wraps button as ValueConsumer. abstract class ButtonController implements ValueConsumer { private final Button button; ButtonController(Button button) { this.button = button; button.addSelectionListener(new SelectionListener() { @Override public void widgetSelected(SelectionEvent e) { int size = pathElement.getVisiblePartSize(); if (!isEnabled(size)) { return; } int newSize = getNewValue(size); pathElement.setVisiblePart(newSize); updater.reportChanged(visiblePartSource); updater.update(); } @Override public void widgetDefaultSelected(SelectionEvent e) { } }); } @Override public void update(Updater updater) { int size = pathElement.getVisiblePartSize(); boolean enabled = isEnabled(size); button.setEnabled(enabled); } // Defines button logic. protected abstract boolean isEnabled(int visiblePartSize); // Defines button logic. protected abstract int getNewValue(int currentValue); } ValueConsumer lessController = new ButtonController(elements.getLessButton()) { @Override protected boolean isEnabled(int visiblePartSize) { return visiblePartSize > 1; } @Override protected int getNewValue(int currentValue) { return currentValue - 1; } }; updater.addConsumer(updater.rootScope(), lessController); updater.addDependency(lessController, visiblePartSource); final int size = elements.getPathLabel().size(); ValueConsumer moreController = new ButtonController(elements.getMoreButton()) { @Override protected boolean isEnabled(int visiblePartSize) { return visiblePartSize < size; } @Override protected int getNewValue(int currentValue) { return currentValue + 1; } }; updater.addConsumer(updater.rootScope(), moreController); updater.addDependency(moreController, visiblePartSource); return new Logic() { @Override public int getAccuratenessValue() { return elements.getPathLabel().getVisiblePartSize(); } @Override public void updateAll() { updater.updateAll(); } }; } private interface Elements { AccuratenessControl.PathLabel getPathLabel(); Button getLessButton(); Button getMoreButton(); } private interface Logic { int getAccuratenessValue(); void updateAll(); } /** * UI elements that shows several last segments of a particular file path * (called visible part size). * Last segment is always visible and is presented in bold font. */ private static class PathLabel { private final String[] components; private final StyledText styledText; private int visiblePartSize = 1; /** * @param components input file path components */ PathLabel(Composite composite, String[] components) { this.components = components; styledText = new StyledText(composite, SWT.NONE); styledText.setEditable(false); styledText.setCaret(null); styledText.setBackground(composite.getBackground()); } public Control getControl() { return styledText; } int size() { return components.length; } int getVisiblePartSize() { return visiblePartSize; } void setVisiblePart(int visiblePartSize) { this.visiblePartSize = visiblePartSize; int size = components.length; StringBuilder builder = new StringBuilder(); for (int i = size - visiblePartSize; i < size - 1; i++) { builder.append(components[i]); builder.append("/"); //$NON-NLS-1$ } int lastComponentStart = builder.length(); builder.append(components[size - 1]); int lastComponentEnd = builder.length(); styledText.setText(builder.toString()); StyleRange boldRange = new StyleRange(lastComponentStart, lastComponentEnd - lastComponentStart, null, null, SWT.BOLD); StyleRange[] ranges = { boldRange }; styledText.setStyleRanges(ranges); styledText.setCaretOffset(lastComponentStart); } } }