/*******************************************************************************
* Copyright 2011 Antti Havanko
*
* This file is part of Motiver.fi.
* Motiver.fi is licensed under one open source license and one commercial license.
*
* Commercial license: This is the appropriate option if you want to use Motiver.fi in
* commercial purposes. Contact license@motiver.fi for licensing options.
*
* Open source license: This is the appropriate option if you are creating an open source
* application with a license compatible with the GNU GPL license v3. Although the GPLv3 has
* many terms, the most important is that you must provide the source code of your application
* to your users so they can be free to modify your application for their own needs.
******************************************************************************/
package com.delect.motiver.client.view.training;
import java.util.Date;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Widget;
import com.delect.motiver.client.AppController;
import com.delect.motiver.client.Motiver;
import com.delect.motiver.client.StringConstants;
import com.delect.motiver.client.presenter.training.ExercisePresenter.ExerciseDisplay;
import com.delect.motiver.client.presenter.training.WorkoutPresenter;
import com.delect.motiver.client.presenter.training.WorkoutPresenter.WorkoutHandler;
import com.delect.motiver.client.res.MyResources;
import com.delect.motiver.client.view.RatingPanel;
import com.delect.motiver.client.view.RatingPanel.RatingPanelHandler;
import com.delect.motiver.client.view.SmallNotePanel;
import com.delect.motiver.client.view.SmallNotePanelDisplay;
import com.delect.motiver.client.view.TimeSelectFieldView;
import com.delect.motiver.client.view.TimeSelectFieldView.TimeSelectFieldHandler;
import com.delect.motiver.client.view.widget.NameInputWidget;
import com.delect.motiver.client.view.widget.ImageButton;
import com.delect.motiver.client.view.widget.MyButton;
import com.delect.motiver.client.view.widget.NameInputWidget.EnterNamePanelHandler;
import com.delect.motiver.shared.util.CommonUtils;
import com.delect.motiver.shared.util.CommonUtils.MessageBoxHandler;
import com.delect.motiver.shared.WorkoutModel;
import com.extjs.gxt.ui.client.Style.HorizontalAlignment;
import com.extjs.gxt.ui.client.dnd.DropTarget;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.DNDEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.util.KeyNav;
import com.extjs.gxt.ui.client.util.Margins;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.MessageBox;
import com.extjs.gxt.ui.client.widget.Popup;
import com.extjs.gxt.ui.client.widget.Text;
import com.extjs.gxt.ui.client.widget.button.Button;
import com.extjs.gxt.ui.client.widget.form.DateField;
import com.extjs.gxt.ui.client.widget.form.DateTimePropertyEditor;
import com.extjs.gxt.ui.client.widget.form.FormButtonBinding;
import com.extjs.gxt.ui.client.widget.form.FormPanel;
import com.extjs.gxt.ui.client.widget.layout.FormData;
import com.extjs.gxt.ui.client.widget.layout.HBoxLayout;
import com.extjs.gxt.ui.client.widget.layout.HBoxLayout.HBoxLayoutAlign;
import com.extjs.gxt.ui.client.widget.layout.HBoxLayoutData;
import com.extjs.gxt.ui.client.widget.layout.RowData;
import com.extjs.gxt.ui.client.widget.layout.RowLayout;
public class WorkoutView extends WorkoutPresenter.WorkoutDisplay {
private MessageBox box = null;
//widgets
private MyButton btnAdd = new MyButton();
private ImageButton btnMoveWorkout;
private WorkoutHandler handler;
private Image imgDone = new Image(MyResources.INSTANCE.done());
private Image imgDoneNot = new Image(MyResources.INSTANCE.notDone());
//panels
private SmallNotePanelDisplay panelBase = (SmallNotePanelDisplay)GWT.create(SmallNotePanel.class);
private LayoutContainer panelComments = new LayoutContainer();
private LayoutContainer panelExercises = new LayoutContainer();
private RatingPanel panelRating;
private LayoutContainer panelUser = new LayoutContainer();
private LayoutContainer panelWorkoutInfo = new LayoutContainer();
private Text textDuration = new Text();
private WorkoutModel workout = null;
/**
* Workout view
* @param showOnlyTitle : show title (TRUE, open workout when clicked) or show just workout (FALSE)
*/
public WorkoutView() {
try {
panelBase.setStylePrefix("panel-workout");
panelBase.setCollapsible(false);
this.add(panelBase);
//listener for shift + key
panelBase.addListener(Events.OnMouseOver, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
panelBase.setTabIndex(0);
}
});
new KeyNav<ComponentEvent>(panelBase) {
@Override
public void onKeyPress(ComponentEvent ce) {
//if valid key comco
if(CommonUtils.isValidKeyCombo(ce)) {
switch(ce.getKeyCode()) {
//shift + E
case 69:
handler.newExercise();
ce.cancelBubble();
break;
}
}
}
};
} catch (Exception e) {
Motiver.showException(e);
}
}
@Override
public Widget asWidget() {
if(workout.getRoutineId() == 0 || workout.getDate() != null) {
panelBase.setStyleName("panel-workout");
}
else {
panelBase.setStyleName("panel-workout-inroutine");
}
panelBase.getPanelData().removeAll();
panelBase.getPanelButtons().removeAll();
try {
//if no model -> ask for name
if(workout.getId() == 0) {
//add panel where user can type name
NameInputWidget panelNameInput = new NameInputWidget(new EnterNamePanelHandler() {
@Override
public void newName(String name) {
//if cancelled
if(name == null) {
handler.saveData(null);
}
else {
workout.setName(name);
handler.saveData(workout);
}
}
});
panelBase.getPanelData().add(panelNameInput);
}
//model set
else {
//userview
panelUser.setStyleAttribute("float", "right");
panelUser.setStyleAttribute("margin", "20px 20px 0 20px");
panelUser.setVisible(false);
this.insert(panelUser, 0);
initTitlePanel();
//workout info
panelBase.getPanelData().add(panelWorkoutInfo, new RowData(-1, -1, new Margins(0, 0, 5, 0)));
//exercises
panelExercises.setStyleAttribute("min-height", "125px");
panelBase.getPanelData().add(panelExercises, new RowData(-1, -1, new Margins(0, 0, 5, 0)));
panelBase.getPanelData().add(panelComments, new RowData(-1, -1, new Margins(10)));
panelExercises.setLayout(new RowLayout());
}
} catch (Exception e) {
Motiver.showException(e);
}
panelBase.getPanelData().layout();
return this;
}
@Override
public LayoutContainer getBodyContainer() {
return panelExercises;
}
@Override
public LayoutContainer getCommentsContainer() {
return panelComments;
}
@Override
public LayoutContainer getUserContainer() {
panelUser.setVisible(true);
return panelUser;
}
@Override
public void onStop() {
if(box != null && box.isVisible()) {
box.close();
}
}
@Override
public void setAddButtonVisible(boolean visible) {
if(btnAdd != null) {
btnAdd.setVisible(visible);
}
}
@Override
public void setDropTarget(String target) {
if(target != null) {
DropTarget dt = new DropTarget(panelBase.getPanelData()) {
@Override
protected void onDragCancelled(DNDEvent event) {
setRowStyles(event, false);
super.onDragCancelled(event);
}
@Override
protected void onDragDrop(DNDEvent event) {
super.onDragDrop(event);
try {
int thisY = event.getXY().y - (panelExercises.getAbsoluteTop() - Window.getScrollTop());
int childs = panelExercises.getItemCount();
ExerciseDisplay draggedE = ((ExerciseDisplay)event.getData());
long draggedId = draggedE.getExercise().getId();
//check where this is dragged
if(childs > 0) {
int newPos = 1;
int totalY = 0;
for(newPos=1; newPos <= childs; newPos++) {
LayoutContainer firstChild = (LayoutContainer)panelExercises.getItem(newPos - 1);
final int childHeight = firstChild.getHeight();
//before current
if(thisY < totalY + childHeight / 2) {
break;
}
//after current
else if(thisY < totalY + childHeight) {
newPos++;
break;
}
totalY += childHeight;
}
//if new pos after old position -> remove on position
if(draggedE.getExercise().getOrder() < newPos) {
newPos--;
}
handler.dragged(draggedId, newPos);
//reset styles
for(int i=1; i <= childs; i++) {
final LayoutContainer firstChild = (LayoutContainer)panelExercises.getItem(i - 1);
//remove old styles
firstChild.removeStyleName("row-drag-after");
firstChild.removeStyleName("row-drag-before");
}
}
} catch (Exception e) {
Motiver.showException(e);
}
}
@Override
protected void onDragFail(DNDEvent event) {
setRowStyles(event, false);
super.onDragFail(event);
}
@Override
protected void onDragLeave(DNDEvent event) {
setRowStyles(event, false);
super.onDragLeave(event);
}
@Override
protected void onDragMove(DNDEvent event) {
setRowStyles(event, true);
super.onDragMove(event);
}
};
dt.setGroup(target);
dt.setOverStyle("drag-ok");
}
}
@Override
public void setHandler(WorkoutHandler handler) {
this.handler = handler;
}
@Override
public void setModel(WorkoutModel workout) {
this.workout = workout;
}
/**
* Inits panel which contains the title
*/
private void initTitlePanel() {
try {
String name = "- " + AppController.Lang.NoName() + " -";
if(workout.getName().length() > 0) {
name = workout.getName();
}
panelBase.setTitleText(name);
//buttons
if(workout.getId() != 0) {
if(workout.getUser().equals(AppController.User)) {
//add exercise
panelBase.addHeaderButton(AppController.Lang.AddTarget(AppController.Lang.Exercise().toLowerCase()),
new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
handler.newExercise();
}
});
//move workout (only if in calendar)
if(workout.getDate() != null) {
btnMoveWorkout = panelBase.addHeaderImageButton(AppController.Lang.Move(), MyResources.INSTANCE.iconBtnCalendar(),
new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
//show popup containing date field and button
final Popup popupMove = new Popup() {
//disable autohide when "autohide" variable is false
@Override
protected boolean onAutoHide(Event event) {
return Boolean.parseBoolean(this.getData("autohide").toString());
}
};
popupMove.setData("autohide", true);
FormData formData = new FormData("0");
FormPanel simple = new FormPanel();
simple.setFrame(true);
simple.setWidth(250);
simple.setHeading(AppController.Lang.MoveTarget(AppController.Lang.ThisWorkout().toLowerCase()));
final DateField tfDate = new DateField();
final DateTimeFormat fmt = DateTimeFormat.getFormat(StringConstants.DATEFORMATS[AppController.User.getDateFormat()]);
DateTimePropertyEditor pr = new DateTimePropertyEditor(fmt.getPattern());
tfDate.setPropertyEditor(pr);
tfDate.setAllowBlank(false);
if(workout.getDate() != null) {
tfDate.setValue(workout.getDate());
}
tfDate.setFieldLabel(AppController.Lang.Date());
//disable autohide so popup won't close when text field is clicked
tfDate.addListener(Events.OnClick, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
popupMove.setData("autohide", false);
}
});
tfDate.addListener(Events.Valid, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
popupMove.setData("autohide", true);
}
});
simple.add(tfDate, formData);
//move
final Button b = new Button(AppController.Lang.Move());
simple.addButton(b);
simple.setButtonAlign(HorizontalAlignment.LEFT);
FormButtonBinding binding = new FormButtonBinding(simple);
binding.addButton(b);
b.addListener(Events.OnClick, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
//if valid date
if(tfDate.isValid()) {
//hide popup
if(popupMove != null) {
popupMove.hide();
}
Date date = tfDate.getValue();
handler.workoutMoved(date);
}
}
});
popupMove.add(simple);
popupMove.show(btnMoveWorkout.getElement(), "", new int[] {-4, -4} );
}
});
}
//open in new window
panelBase.addHeaderImageButton(AppController.Lang.OpenInNewWindow(), MyResources.INSTANCE.iconBtnNewWindow(),
new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
if(handler != null)
handler.openNewWindow();
}
});
//rename workout
panelBase.addHeaderImageButton(AppController.Lang.Rename(), MyResources.INSTANCE.iconBtnRename(),
new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
if(box != null && box.isVisible()) {
box.close();
}
//ask for confirm
box = CommonUtils.getMessageBoxPrompt(workout.getName(), new MessageBoxHandler() {
@Override
public void okPressed(String text) {
if(!workout.getName().equals( text )) {
workout.setName(text);
panelBase.setTitleText(workout.getName());
handler.saveData(workout);
}
}
});
box.setTitle(AppController.Lang.Name());
box.setMessage(AppController.Lang.EnterName() + ":");
box.show();
}
});
//remove workout
panelBase.addHeaderImageButton(AppController.Lang.RemoveTarget(AppController.Lang.Workout().toLowerCase()), MyResources.INSTANCE.iconRemove(),
new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
//ask for confirm
box = CommonUtils.getMessageBoxConfirm(AppController.Lang.RemoveConfirm(AppController.Lang.ThisWorkout().toLowerCase()), new MessageBoxHandler() {
@Override
public void okPressed(String text) {
handler.workoutRemoved();
}
});
box.show();
}
});
}
//init times, rating, etc...
if(workout.getDate() != null) {
if(panelWorkoutInfo.getItemCount() == 0) {
HBoxLayout layout3 = new HBoxLayout();
layout3.setHBoxLayoutAlign(HBoxLayoutAlign.MIDDLE);
panelWorkoutInfo.setLayout(layout3);
panelWorkoutInfo.setHeight(45);
panelWorkoutInfo.setStyleName("panel-workout-info");
//times
panelWorkoutInfo.add(new Text(AppController.Lang.Time() + ": "), new HBoxLayoutData(new Margins(0, 5, 0, 0)));
if(workout.getUser().equals(AppController.User)) {
TimeSelectFieldView tfStart = new TimeSelectFieldView((int) workout.getTimeStart(), new TimeSelectFieldHandler() {
@Override
public void timeChanged(int time) {
workout.setTimeStart(time);
setDuration();
handler.saveData(workout);
}
});
tfStart.setAllowBlank(true);
panelWorkoutInfo.add(tfStart, new HBoxLayoutData(new Margins(0, 5, 0, 0)));
panelWorkoutInfo.add(new Text("-"), new HBoxLayoutData(new Margins(0, 5, 0, 0)));
TimeSelectFieldView tfEnd = new TimeSelectFieldView((int) workout.getTimeEnd(), new TimeSelectFieldHandler() {
@Override
public void timeChanged(int time) {
workout.setTimeEnd(time);
setDuration();
handler.saveData(workout);
}
});
tfEnd.setAllowBlank(true);
panelWorkoutInfo.add(tfEnd, new HBoxLayoutData(new Margins(0, 5, 0, 0)));
}
//show just text
else {
panelWorkoutInfo.add(new Text(CommonUtils.getTimeToString((int) workout.getTimeStart())), new HBoxLayoutData(new Margins(0, 5, 0, 0)));
panelWorkoutInfo.add(new Text("-"), new HBoxLayoutData(new Margins(0, 5, 0, 0)));
panelWorkoutInfo.add(new Text(CommonUtils.getTimeToString((int) workout.getTimeEnd())), new HBoxLayoutData(new Margins(0, 5, 0, 0)));
}
//duration
textDuration.setStyleName("label-duration");
panelWorkoutInfo.add(textDuration, new HBoxLayoutData(new Margins(0, 5, 0, 5)));
setDuration();
//spacer
HBoxLayoutData flex2 = new HBoxLayoutData(new Margins(0, 5, 0, 0));
flex2.setFlex(1);
panelWorkoutInfo.add(new Text(), flex2);
//rating
panelRating = new RatingPanel(workout.getRating(), new RatingPanelHandler() {
@Override
public void ratingChanged(int rating) {
workout.setRating(rating);
handler.saveData(workout);
}
});
panelRating.setEnabled(workout.getUser().equals(AppController.User));
panelWorkoutInfo.add(panelRating, new HBoxLayoutData(new Margins(0, 10, 0, 0)));
//done
imgDone.setVisible(workout.getDone());
imgDoneNot.setVisible(!workout.getDone());
LayoutContainer lcDone = new LayoutContainer();
if(workout.getUser().equals(AppController.User)) {
//tooltip and click listener
imgDoneNot.setTitle(AppController.Lang.MarkAsDone());
final ClickHandler handlerClick = new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
workout.setDone( !workout.getDone() );
//change images
imgDone.setVisible(workout.getDone());
imgDoneNot.setVisible(!workout.getDone());
handler.saveData(workout);
}
};
imgDone.addClickHandler(handlerClick);
imgDoneNot.addClickHandler(handlerClick);
lcDone.setStyleAttribute("cursor", "pointer");
}
lcDone.add(imgDone);
lcDone.add(imgDoneNot);
panelWorkoutInfo.add(lcDone, new HBoxLayoutData(new Margins(0, 5, 0, 0)));
}
panelWorkoutInfo.layout();
}
else {
panelWorkoutInfo.setVisible(false);
}
}
} catch (Exception e) {
Motiver.showException(e);
}
}
/**
* Set duration text based on workout times
*/
protected void setDuration() {
try {
//set duration
if(workout.getTimeStart() > 0 && workout.getTimeEnd() > 0 && workout.getTimeEnd() > workout.getTimeStart()) {
textDuration.setText( CommonUtils.getDurationString(workout.getTimeEnd() - workout.getTimeStart()) );
}
else {
textDuration.setText("");
}
} catch (Exception e) {
textDuration.setText("");
}
}
/**
* Sets correct styles for each exercise (if not mousemove -> clears all styles)
* @param event
* @param onMouseMove : called from mousemove event
*/
protected void setRowStyles(DNDEvent event, boolean onMouseMove) {
try {
int thisY = event.getXY().y - (panelExercises.getAbsoluteTop() - Window.getScrollTop());
int childs = panelExercises.getItemCount();
//check where this is dragged
if(childs > 0) {
boolean found = false;
int totalY = 0;
for(int i=1; i <= childs; i++) {
final LayoutContainer firstChild = (LayoutContainer)panelExercises.getItem(i - 1);
final int childHeight = firstChild.getHeight();
//remove old styles
firstChild.removeStyleName("row-drag-after");
firstChild.removeStyleName("row-drag-before");
if(thisY < totalY + childHeight / 2 && !found && onMouseMove) {
found = true;
firstChild.addStyleName("row-drag-before");
}
else if(thisY < totalY + childHeight && !found && onMouseMove) {
found = true;
firstChild.addStyleName("row-drag-after");
}
totalY += childHeight;
}
}
} catch (Exception e) {
Motiver.showException(e);
}
}
}