/*
* SearchWidget.java
*
* Copyright (C) 2009-12 by RStudio, Inc.
*
* Unless you have received this program directly from RStudio pursuant
* to the terms of a commercial license agreement with RStudio, then
* this program is licensed to you under the terms of version 3 of the
* GNU Affero General Public License. This program is distributed WITHOUT
* ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
* AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
*
*/
package org.rstudio.core.client.widget;
import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.*;
import com.google.gwt.user.client.ui.SuggestBox.DefaultSuggestionDisplay;
import com.google.gwt.user.client.ui.SuggestBox.SuggestionDisplay;
import org.rstudio.core.client.events.SelectionCommitEvent;
import org.rstudio.core.client.events.SelectionCommitHandler;
import org.rstudio.core.client.theme.res.ThemeResources;
import org.rstudio.core.client.theme.res.ThemeStyles;
public class SearchWidget extends Composite implements SearchDisplay
{
interface MyUiBinder extends UiBinder<Widget, SearchWidget> {}
private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);
class FocusSuggestBox extends SuggestBox implements HasAllFocusHandlers
{
FocusSuggestBox(SuggestOracle oracle)
{
super(oracle);
}
FocusSuggestBox(SuggestOracle oracle, TextBoxBase textBox)
{
super(oracle, textBox);
}
FocusSuggestBox(SuggestOracle oracle,
TextBoxBase textBox,
SuggestionDisplay suggestDisplay)
{
super(oracle, textBox, suggestDisplay);
}
public HandlerRegistration addBlurHandler(BlurHandler handler)
{
return addDomHandler(handler, BlurEvent.getType());
}
public HandlerRegistration addFocusHandler(FocusHandler handler)
{
return addDomHandler(handler, FocusEvent.getType());
}
}
public SearchWidget(SuggestOracle oracle)
{
this(oracle, null);
}
public SearchWidget(SuggestOracle oracle,
SuggestionDisplay suggestDisplay)
{
this(oracle, new TextBox(), suggestDisplay);
}
public SearchWidget(SuggestOracle oracle,
TextBoxBase textBox,
SuggestionDisplay suggestDisplay)
{
this(oracle, textBox, suggestDisplay, true);
}
public SearchWidget(SuggestOracle oracle,
TextBoxBase textBox,
SuggestionDisplay suggestDisplay,
boolean continuousSearch)
{
textBox.getElement().setAttribute("spellcheck", "false");
if (suggestDisplay != null)
suggestBox_ = new FocusSuggestBox(oracle, textBox, suggestDisplay);
else
suggestBox_ = new FocusSuggestBox(oracle, textBox);
initWidget(uiBinder.createAndBindUi(this));
close_.setVisible(false);
ThemeStyles styles = ThemeResources.INSTANCE.themeStyles();
suggestBox_.setStylePrimaryName(styles.searchBox());
suggestBox_.setAutoSelectEnabled(false) ;
addKeyDownHandler(new KeyDownHandler() {
public void onKeyDown(KeyDownEvent event)
{
switch (event.getNativeKeyCode())
{
case KeyCodes.KEY_ENTER:
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
public void execute()
{
SelectionCommitEvent.fire(SearchWidget.this,
suggestBox_.getText()) ;
}
}) ;
break ;
case KeyCodes.KEY_ESCAPE:
event.preventDefault();
event.stopPropagation();
// defer the handling of ESC so that it doesn't end up
// inside other UI (the editor) if/when the parent search
// ui is dismissed
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute()
{
if (getSuggestionDisplay().isSuggestionListShowing())
{
getSuggestionDisplay().hideSuggestions();
setText("", true);
}
else
{
CloseEvent.fire(SearchWidget.this, SearchWidget.this);
}
}
});
break ;
}
}
}) ;
if (continuousSearch)
{
// Unlike SuggestBox's ValueChangeEvent impl, we want the
// event to fire as soon as the value changes
suggestBox_.addKeyUpHandler(new KeyUpHandler() {
public void onKeyUp(KeyUpEvent event)
{
String value = suggestBox_.getText();
if (!value.equals(lastValueSent_))
{
updateLastValue(value);
ValueChangeEvent.fire(SearchWidget.this, value);
}
}
});
}
suggestBox_.addValueChangeHandler(new ValueChangeHandler<String>()
{
public void onValueChange(ValueChangeEvent<String> evt)
{
if (!evt.getValue().equals(lastValueSent_))
{
updateLastValue(evt.getValue());
delegateEvent(SearchWidget.this, evt);
}
}
});
close_.addMouseDownHandler(new MouseDownHandler()
{
public void onMouseDown(MouseDownEvent event)
{
event.preventDefault();
suggestBox_.setText("");
ValueChangeEvent.fire(suggestBox_, "");
}
});
focusTracker_ = new FocusTracker(suggestBox_);
}
public HandlerRegistration addFocusHandler(FocusHandler handler)
{
return suggestBox_.addFocusHandler(handler);
}
public HandlerRegistration addBlurHandler(BlurHandler handler)
{
return suggestBox_.addBlurHandler(handler);
}
public HandlerRegistration addValueChangeHandler(
ValueChangeHandler<String> handler)
{
return addHandler(handler, ValueChangeEvent.getType());
}
public HandlerRegistration addSelectionCommitHandler(
SelectionCommitHandler<String> handler)
{
return addHandler(handler, SelectionCommitEvent.getType()) ;
}
public HandlerRegistration addKeyDownHandler(final KeyDownHandler handler)
{
return suggestBox_.addKeyDownHandler(new KeyDownHandler()
{
public void onKeyDown(KeyDownEvent event)
{
handler.onKeyDown(event);
}
});
}
public HandlerRegistration addSelectionHandler(
SelectionHandler<SuggestOracle.Suggestion> handler)
{
return suggestBox_.addSelectionHandler(handler);
}
public HandlerRegistration addCloseHandler(
CloseHandler<SearchDisplay> handler)
{
return addHandler(handler, CloseEvent.getType());
}
@Override
public void setAutoSelectEnabled(boolean selectsFirstItem)
{
suggestBox_.setAutoSelectEnabled(selectsFirstItem);
}
public String getText()
{
return suggestBox_.getText() ;
}
public void setText(String text)
{
suggestBox_.setText(text) ;
}
public void setText(String text, boolean fireEvents)
{
suggestBox_.setValue(text, fireEvents);
}
@Override
public String getValue()
{
return suggestBox_.getValue();
}
@Override
public void setValue(String value)
{
suggestBox_.setValue(value);
}
@Override
public void setValue(String value, boolean fireEvents)
{
suggestBox_.setValue(value, fireEvents);
}
public void setIcon(ImageResource image)
{
icon_.setResource(image);
}
public void focus()
{
suggestBox_.setFocus(true);
}
public void clear()
{
setText("", true);
close_.setVisible(false);
}
// NOTE: only works if you are using the default display!
public DefaultSuggestionDisplay getSuggestionDisplay()
{
return (DefaultSuggestionDisplay) suggestBox_.getSuggestionDisplay();
}
protected ValueBoxBase<String> getTextBox()
{
return suggestBox_.getValueBox();
}
private void updateLastValue(String value)
{
lastValueSent_ = value;
close_.setVisible(lastValueSent_.length() > 0);
}
public String getLastValue()
{
return lastValueSent_;
}
public void setPlaceholderText(String value)
{
suggestBox_.getElement().setAttribute("placeholder", value);
}
public boolean isFocused()
{
return focusTracker_.isFocused();
}
@UiField(provided=true)
FocusSuggestBox suggestBox_;
@UiField
Image close_;
@UiField
Image icon_;
private String lastValueSent_ = null;
private final FocusTracker focusTracker_;
}