/**
* Copyright 2013 ArcBees 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 com.arcbees.gwtpwebsite.client.application.buypro.ui;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style;
import com.google.gwt.query.client.Function;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.user.client.ui.InlineHTML;
import com.google.gwt.user.client.ui.Widget;
import gwtquery.plugins.draggable.client.DraggableOptions;
import gwtquery.plugins.draggable.client.events.DragContext;
import gwtquery.plugins.draggable.client.gwt.DraggableWidget;
import java.util.List;
import static com.google.gwt.query.client.GQuery.$;
import static com.google.gwt.query.client.plugins.effects.PropertiesAnimation.Easing.SWING;
public class PinWidget extends DraggableWidget<InlineHTML> {
interface Binder extends UiBinder<InlineHTML, PinWidget> {
}
interface ExtraProjectsHandler {
void revealExtraProjectsDiv();
void hideExtraProjectsDiv();
void clearExtraProjectsInput();
void updateNumberOfProjects(int pinValue);
}
private final String nodeContainer = "#nodeContainer";
private static Binder ourUiBinder = GWT.create(Binder.class);
private ExtraProjectsHandler parent;
private List<Widget> myListSnappables;
private int listSnappableLength;
private int nodeWidth;
private int pinWidth;
private int pinBorder = 1 * 2;
private int nodesContainerLenght;
private int[] nodeDistances;
private int nodeToMoveTo;
private int pinValue;
private int duration = 250;
public PinWidget() {
InlineHTML inlineHTML = ourUiBinder.createAndBindUi(this);
initWidget(inlineHTML);
initDragging();
}
public void setSnappables(List<Widget> snappables) {
initListSnappables(snappables);
setNodesClickHandler();
}
public void movePin(int nodeTo) {
commonMoveMethods(nodeTo);
toggleExtraProjectsDiv(nodeTo);
parent.clearExtraProjectsInput();
}
public void movePinFromInput(int nodeTo) {
commonMoveMethods(nodeTo);
}
void setParent(ExtraProjectsHandler parent) {
this.parent = parent;
}
private void commonMoveMethods(int nodeTo) {
setPinValue(nodeTo);
movePinToNode(nodeTo);
}
private void setNodesClickHandler() {
Widget currentNode;
for (int i = 0; i < listSnappableLength; i++) {
final int currentPinValue = i + 1;
currentNode = myListSnappables.get(i);
$(currentNode).click(new Function() {
@Override
public void f() {
movePin(currentPinValue);
}
});
}
}
private void initDragging() {
setDraggableOptions();
getDraggableOptions().setOnDragStop(new DraggableOptions.DragFunction() {
@Override
public void f(DragContext dragContext) {
onStopDrag();
}
});
}
private void setDraggableOptions() {
setDraggingCursor(Style.Cursor.MOVE);
setDraggingOpacity((float) 0.9);
setAxis(DraggableOptions.AxisOption.X_AXIS);
setContainment("parent");
setDistance(1);
}
private void onStopDrag() {
int currentPinPosition = this.getAbsoluteLeft() + pinWidth;
getNodesDistancesFromPin(currentPinPosition);
int closestNode = getPositionOfSmallestValue(nodeDistances) + 1;
movePin(closestNode);
}
private void getNodesDistancesFromPin(int currentPinPosition) {
Widget currentNode;
int currentNodeDistance;
nodeDistances = new int[nodesContainerLenght];
for (int i = 0; i < listSnappableLength; i++) {
currentNode = myListSnappables.get(i);
currentNodeDistance = Math.abs((currentNode.getAbsoluteLeft() + nodeWidth) - currentPinPosition);
nodeDistances[i] = currentNodeDistance;
}
}
private void movePinToNode(int nodeTo) {
if (isNodeLast(nodeTo)) {
nodeToMoveTo = listSnappableLength;
} else {
nodeToMoveTo = nodeTo;
}
int distanceLeft = getDistanceLeftFromCurrentNode();
animatePin(distanceLeft);
}
private void animatePin(int distanceLeft) {
$(this).animate("left:" + distanceLeft, duration, SWING, new Function() {
@Override
public void f() {
setPinNumber();
}
});
}
private void setPinValue(int nodeToValue) {
pinValue = nodeToValue;
}
private void setPinNumber() {
$(this).children("span").children("span").text(String.valueOf(pinValue));
parent.updateNumberOfProjects(pinValue);
}
private void toggleExtraProjectsDiv(int nodeTo) {
if (isNodeLast(nodeTo)) {
parent.revealExtraProjectsDiv();
} else {
parent.hideExtraProjectsDiv();
}
}
private int getDistanceLeftFromCurrentNode() {
return myListSnappables.get(nodeToMoveTo - 1).getElement().getOffsetLeft();
}
private boolean isNodeLast(int nodeToMoveTo) {
boolean isLast;
if (nodeToMoveTo > listSnappableLength - 1) {
isLast = true;
} else {
isLast = false;
}
return isLast;
}
private void initListSnappables(List<Widget> snappables) {
myListSnappables = snappables;
listSnappableLength = myListSnappables.size();
nodeWidth = $(myListSnappables.get(0)).outerWidth() / 2;
pinWidth = $(this).width() / 2 - pinBorder;
nodesContainerLenght = $(nodeContainer).children().length();
}
private static int getPositionOfSmallestValue(int[] arr) {
int i = 0;
int min = Integer.MAX_VALUE;
int smallest = 0;
if (arr == null) {
return 0;
} else {
while (i < arr.length) {
if (arr[i] < min) {
min = arr[i];
smallest = i;
}
i++;
}
}
return smallest;
}
}