/**
* Copyright 2009 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.waveprotocol.wave.client.scheduler.knobs;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Style.Display;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.Label;
import org.waveprotocol.wave.client.scheduler.Scheduler.Priority;
import org.waveprotocol.wave.model.util.Preconditions;
import org.waveprotocol.wave.model.util.ReadableStringSet;
import java.util.Collection;
/**
* GWT widget implementation of a single level UI control.
* The component is implemented as a label with a click handler.
*
*/
public final class GwtKnobView extends Composite implements ClickHandler, KnobView {
private static final String UP_ARROW = "\u25B2";
private static final String DOWN_ARROW = "\u25BC";
// UI fields
private final FlowPanel knobPanel = new FlowPanel();
private final Label label = new Label();
private final FlowPanel detailsPanel = new FlowPanel() {
{
addDomHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
onDetailsClicked(event.getNativeEvent().getEventTarget().<Element>cast());
}
}, ClickEvent.getType());
}
};
private final HTML currentTasks = new HTML();
private final HTML oldTasks = new HTML();
private final Button detailsButton = new Button();
private final Button clearButton = new Button("X");
/** Level being displayed. */
private final Priority level;
/** Listener, set between {@link #init(Listener)} and {@link #reset()}. */
private Listener listener;
/**
* Creates a priority-level controller.
*
* @param level level to control
*/
GwtKnobView(Priority level) {
this.level = level;
initWidget(knobPanel);
getElement().addClassName(GwtKnobsView.CSS.knob());
knobPanel.add(label);
label.addClickHandler(this);
label.getElement().addClassName(GwtKnobsView.CSS.knobLabel());
knobPanel.add(detailsButton);
detailsButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
listener.onDetailsClicked();
}
});
knobPanel.add(clearButton);
clearButton.setTitle("Clear Details");
clearButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
listener.onClearClicked();
}
});
knobPanel.add(detailsPanel);
detailsPanel.setStyleName(GwtKnobsView.CSS.knobDetails());
detailsPanel.getElement().getStyle().setDisplay(Display.NONE);
Label currentTitle = new Label("CURRENT JOBS");
currentTitle.addStyleName(GwtKnobsView.CSS.currentTitle());
detailsPanel.add(currentTitle);
detailsPanel.add(currentTasks);
Label oldTitle = new Label("PREVIOUS JOBS");
oldTitle.addStyleName(GwtKnobsView.CSS.oldTitle());
detailsPanel.add(oldTitle);
detailsPanel.add(oldTasks);
}
private void onDetailsClicked(Element targetElement) {
listener.onToggleTask(targetElement.getInnerText());
}
@Override
public void init(Listener listener) {
this.listener = listener;
}
@Override
public void reset() {
this.listener = null;
}
@Override
public void enable() {
getElement().replaceClassName(GwtKnobsView.CSS.disabled(), GwtKnobsView.CSS.enabled());
}
@Override
public void disable() {
getElement().replaceClassName(GwtKnobsView.CSS.enabled(), GwtKnobsView.CSS.disabled());
}
@Override
public void showCount(int count) {
label.setText(level + ": " + count);
}
@Override
public void onClick(ClickEvent event) {
if (listener != null) {
listener.onClicked();
}
}
@Override
public void hideJobs() {
detailsButton.setText(DOWN_ARROW);
detailsPanel.getElement().getStyle().setDisplay(Display.NONE);
}
@Override
public void showJobs(Collection<String> currentJobs, Collection<String> oldJobs,
ReadableStringSet suppressedJobs) {
detailsButton.setText(UP_ARROW);
detailsPanel.getElement().getStyle().clearDisplay();
StringBuilder b = new StringBuilder();
for (String name : currentJobs) {
b.append(renderTask(name, suppressedJobs.contains(name)));
b.append("<br/>");
}
currentTasks.setHTML(b.toString());
b = new StringBuilder();
for (String name : oldJobs) {
b.append(renderTask(name, suppressedJobs.contains(name)));
b.append("<br/>");
}
oldTasks.setHTML(b.toString());
}
private String renderTask(String name, boolean isSuppressed) {
// NOTE(danilatos): Sanity check because we are using StringBuilder instead of
// safe html builder. 'name' is the only "external" variable. I don't expect
// this check to ever fail given that it should be a class name. This is actually
// safer than safe html because as of this writing the append text method
// has a todo for validity checking, but it is unimplemented!?
Preconditions.checkArgument(!name.contains("<"), "Task name cannot contain HTML");
return isSuppressed ? "<s>" + name + "</s>" : "<span>" + name + "</span>";
}
}