package com.giftoftheembalmer.gotefarm.client;
import com.allen_sauer.gwt.dnd.client.DragContext;
import com.allen_sauer.gwt.dnd.client.DragEndEvent;
import com.allen_sauer.gwt.dnd.client.DragHandler;
import com.allen_sauer.gwt.dnd.client.DragStartEvent;
import com.allen_sauer.gwt.dnd.client.PickupDragController;
import com.allen_sauer.gwt.dnd.client.VetoDragException;
import com.allen_sauer.gwt.dnd.client.drop.AbstractDropController;
import com.allen_sauer.gwt.dnd.client.drop.DropController;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class Events
extends Composite
implements ValueChangeHandler<List<JSCharacter>> {
VerticalPanel vpanel = new VerticalPanel();
VerticalPanel vmsgpanel = new VerticalPanel();
VerticalPanel veventpanel = new VerticalPanel();
final DateTimeFormat time_formatter = DateTimeFormat.getFormat("EEE MMM dd, yyyy hh:mm aaa");
private List<JSCharacter> characters = new ArrayList<JSCharacter>();
private String account_key;
private JSGuild current_guild;
PickupDragController dragController;
public ValueChangeHandler<JSGuild> guildChange
= new ValueChangeHandler<JSGuild>() {
public void onValueChange(ValueChangeEvent<JSGuild> event) {
current_guild = event.getValue();
refresh();
}
};
public ValueChangeHandler<AdminChange> adminChange
= new ValueChangeHandler<AdminChange>() {
public void onValueChange(
ValueChangeEvent<AdminChange> event) {
AdminChange value = event.getValue();
if (value.events_changed) {
refresh();
}
}
};
private final CharacterCache chr_cache = new CharacterCache();
public class Event
extends Composite
implements DragHandler {
static final String SPACE_AVAILABLE = "available";
JSEvent event = null;
JSEventSignups signups = null;
VerticalPanel vpanel = new VerticalPanel();
HorizontalPanel hchrpanel = new HorizontalPanel();
HorizontalPanel hchractions = new HorizontalPanel();
FlexTable flex = new FlexTable();
boolean have_character_signed_up = false;
Label signup_error = new Label();
private Set<DropController> registered_drop_controllers = new HashSet<DropController>();
Map<String, RoleSignup> role_signups = new HashMap<String, RoleSignup>();
private DropController removeSignupDC;
public Event(JSEvent event) {
this.event = event;
signup_error.addStyleName("error");
signup_error.addStyleName("gwt-Button-bottom");
signup_error.addStyleName("gwt-Button-right");
vpanel.setWidth("100%");
flex.setWidth("100%");
vpanel.add(new Label(event.name));
vpanel.add(new Label(time_formatter.format(event.start_time)));
vpanel.add(flex);
hchractions.setWidth("100%");
vpanel.add(hchractions);
hchrpanel.setSpacing(10);
hchractions.add(hchrpanel);
hchractions.add(signup_error);
setSignups(null);
initWidget(vpanel);
setStyleName("Event");
}
void setEvent(JSEvent event) {
JSEvent old_event = this.event;
this.event = event;
if (event == null || !event.equals(old_event)) {
showSignups(false);
}
}
void remakeFlex(FlexTable newFlex) {
// recreate the flex table; clear() doesn't remove columns
int index = vpanel.getWidgetIndex(flex);
vpanel.remove(flex);
flex = newFlex;
flex.setWidth("100%");
vpanel.insert(flex, index);
resizeRows();
}
void styleColumn(FlexCellFormatter formatter, int column) {
formatter.addStyleName(1, column, "coming");
formatter.setVerticalAlignment(1, column, HasVerticalAlignment.ALIGN_TOP);
formatter.addStyleName(2, column, "standby");
formatter.setVerticalAlignment(2, column, HasVerticalAlignment.ALIGN_TOP);
formatter.addStyleName(3, column, "maybe");
formatter.setVerticalAlignment(3, column, HasVerticalAlignment.ALIGN_TOP);
formatter.addStyleName(4, column, "notcoming");
formatter.setVerticalAlignment(4, column, HasVerticalAlignment.ALIGN_TOP);
}
class Signup extends Label {
String eventKey;
JSCharacter chr;
JSEventSignup sup;
public Signup(String eventKey, JSCharacter chr, JSEventSignup sup) {
this(eventKey, chr);
this.sup = sup;
}
public Signup(String eventKey, JSCharacter chr) {
super(chr.name);
this.eventKey = eventKey;
this.chr = chr;
dragController.makeDraggable(this);
if (chr.account_key.equals(account_key)) {
addStyleName("draggable-character");
}
else {
addStyleName("non-draggable-character");
}
addStyleName(chr.clazz.replace(' ', '-'));
}
public JSCharacter getCharacter() {
return chr;
}
}
void setSignups(JSEventSignups signups) {
this.signups = signups;
showSignups(true);
}
AsyncCallback<JSEventSignups> signupCallback = new AsyncCallback<JSEventSignups>() {
public void onSuccess(JSEventSignups signups) {
if (signups == null) {
// unchanged
return;
}
setSignups(signups);
}
public void onFailure(Throwable caught) {
// re-setSignups to rebuild interface, otherwise a failed
// drop will leave a signup missing
setSignups(signups);
signup_error.setText(caught.getMessage());
}
};
class RoleDropController extends AbstractDropController {
String eventKey;
JSEventRole role;
Widget dropTarget;
int signupType;
String highlightStyle;
RoleDropController(String eventKey, JSEventRole role,
Widget widget, int signupType,
String highlightStyle) {
super(widget);
this.eventKey = eventKey;
this.role = role;
this.dropTarget = widget;
this.signupType = signupType;
this.highlightStyle = highlightStyle;
}
boolean allowDrop(Signup signup) {
JSCharacter chr = signup.getCharacter();
if (role == null) {
return true;
}
// character must have this role
if (!chr.hasRole(role.role_key)) {
signup_error.setText(chr.name + " is missing the role '"
+ role.name + ".'");
return false;
}
// signups must have started
long now = System.currentTimeMillis();
long signups_start = Event.this.event.signups_start.getTime();
if (now < signups_start) {
// character needs to qualify for an early signup for this
// role
boolean found = false;
boolean found_early = false;
long earliest_signup = signups_start;
for (JSEventBadge badge : Event.this.event.badges) {
if (!chr.hasBadge(badge.badge_key)) {
continue;
}
// if this badge applies to a role, it must apply to
// the drop target role to count
if ( badge.applyToRole != null
&& !badge.applyToRole.equals(role.name)) {
continue;
}
// character qualifies, but has it started?
long badge_signups_start = signups_start - badge.earlySignup * 3600000L;
if (now < badge_signups_start) {
found_early = true;
earliest_signup = Math.min(earliest_signup, badge_signups_start);
continue;
}
found = true;
break;
}
if (!found) {
if (found_early) {
signup_error.setText(chr.name + " can't sign up"
+ " for this role yet, but"
+ " early signups start at "
+ time_formatter.format(
new Date(earliest_signup))
+ ".");
}
else {
signup_error.setText(chr.name + " can't sign up"
+ " for this role yet,"
+ " signups start at "
+ time_formatter.format(
new Date(earliest_signup))
+ ".");
}
return false;
}
}
signup_error.setText("");
return true;
}
@Override
public void onEnter(DragContext context) {
super.onEnter(context);
Signup sup = (Signup)context.selectedWidgets.get(0);
// ignore drag events for other raid events
if (!sup.eventKey.equals(eventKey)) {
return;
}
if (allowDrop(sup)) {
dropTarget.addStyleName(highlightStyle);
}
else {
dropTarget.addStyleName("bad-drop-target");
}
}
@Override
public void onLeave(DragContext context) {
dropTarget.removeStyleName(highlightStyle);
dropTarget.removeStyleName("bad-drop-target");
super.onLeave(context);
}
@Override
public void onPreviewDrop(DragContext context)
throws VetoDragException {
super.onPreviewDrop(context);
Signup sup = (Signup)context.selectedWidgets.get(0);
// reject drag events for other raid events
if (!sup.eventKey.equals(eventKey)) {
throw new VetoDragException();
}
// is this signup allowed to happen?
if (!allowDrop(sup)) {
throw new VetoDragException();
}
// is this no-op drop?
if ( sup.sup != null
&& role != null
&& sup.sup.role_key.equals(role.role_key)
&& sup.sup.signup_type == signupType) {
throw new VetoDragException();
}
}
@Override
public void onDrop(DragContext context) {
super.onDrop(context);
Signup sup = (Signup)context.selectedWidgets.get(0);
sup.removeFromParent();
signup_error.setText("");
if (sup.sup == null) {
// new signup
GoteFarm.goteService.signupForEvent(event.key,
sup.chr.key, role.role_key,
signupType,
signupCallback);
}
else if (signupType < 0) {
// deleting signup
GoteFarm.goteService.removeEventSignup(sup.sup.key,
signupCallback);
}
else
{
// changing signup
GoteFarm.goteService.changeEventSignup(sup.sup.key,
role.role_key,
signupType,
signupCallback);
}
}
}
class RoleSignup {
JSEvent event;
JSEventRole role;
int column = -1;
int spaces_left;
Set<JSEventBadge> badges_required = new HashSet<JSEventBadge>();
Map<JSEventBadge, Integer> badges_needed = new HashMap<JSEventBadge, Integer>();
List<JSEventSignup> coming = new ArrayList<JSEventSignup>();
List<JSEventSignup> standby = new ArrayList<JSEventSignup>();
List<JSEventSignup> maybe = new ArrayList<JSEventSignup>();
List<JSEventSignup> not_coming = new ArrayList<JSEventSignup>();
DropController coming_dc;
DropController maybe_dc;
DropController not_coming_dc;
RoleSignup(JSEvent event, JSEventRole role) {
this.event = event;
this.role = role;
}
}
void showSignups(final boolean call_characters_changed) {
try {
unregisterDropControllers();
remakeFlex(makeNewSignupTable());
if (call_characters_changed) {
charactersChanged();
}
}
catch (CharacterNotCachedException e) {
// Fetch the missing character and try again
// TODO: indicate progress in the UI somehow
final String key = e.getCharacterKey();
chr_cache.getCharacter(key, new AsyncCallback<JSCharacter>() {
public void onSuccess(JSCharacter chr) {
chr_cache.put(key, chr);
// recurse (sort of)
showSignups(call_characters_changed);
}
public void onFailure(Throwable caught) {
// TODO: what to do here?
}
});
}
}
FlexTable makeNewSignupTable() throws CharacterNotCachedException {
FlexTable flex = new FlexTable();
have_character_signed_up = false;
List<JSEventSignup> limbo = new ArrayList<JSEventSignup>();
role_signups.clear();
// For each role, figure out the badge requirements
for (JSEventRole role : event.roles) {
RoleSignup rsup = new RoleSignup(event, role);
rsup.spaces_left = role.max;
role_signups.put(role.role_key, rsup);
for (JSEventBadge badge : event.badges) {
if ( badge.requireForSignup
&& ( badge.applyToRole == null
|| badge.applyToRole.equals(role.name))) {
if (badge.applyToRole == null) {
rsup.badges_required.add(badge);
}
else {
rsup.badges_needed.put(badge, badge.numSlots);
}
}
}
}
int spots_left = event.size;
if (signups != null) {
// Categorize the current signups
for (JSEventSignup es : signups.signups) {
JSCharacter chr = chr_cache.get(es.chr_key);
if (chr.account_key.equals(account_key)) {
have_character_signed_up = true;
}
RoleSignup rsup = role_signups.get(es.role_key);
// role does not exist: limbo
if (rsup == null) {
limbo.add(es);
continue;
}
// user not coming
if (es.signup_type == JSEventSignup.SIGNUP_TYPE_MAYBE) {
rsup.maybe.add(es);
continue;
}
else if (es.signup_type == JSEventSignup.SIGNUP_TYPE_NOT_COMING) {
rsup.not_coming.add(es);
continue;
}
// event full?
if (spots_left == 0) {
rsup.standby.add(es);
continue;
}
// role full?
if (rsup.spaces_left == 0) {
rsup.standby.add(es);
continue;
}
// character missing required badges?
boolean standby = false;
for (JSEventBadge req_badge : rsup.badges_required) {
if (!chr.hasBadge(req_badge.badge_key)) {
rsup.standby.add(es);
standby = true;
break;
}
}
// check for reserved spots
int role_spots_left = Math.min(spots_left, rsup.spaces_left);
for (Map.Entry<JSEventBadge, Integer> needed : rsup.badges_needed.entrySet()) {
if (needed.getValue() >= role_spots_left) {
// character must have this badge to sign up
if (!chr.hasBadge(needed.getKey().badge_key)) {
rsup.standby.add(es);
standby = true;
break;
}
}
}
if (standby) {
continue;
}
// this character is allowed in!
--spots_left;
--rsup.spaces_left;
// subtract out any badges requirements this character fulfills
List<JSEventBadge> to_delete = new ArrayList<JSEventBadge>();
for (Map.Entry<JSEventBadge, Integer> needed : rsup.badges_needed.entrySet()) {
int num = needed.getValue();
if (chr.hasBadge(needed.getKey().badge_key)) {
if (num == 1) {
to_delete.add(needed.getKey());
}
else {
needed.setValue(num - 1);
}
}
}
for (JSEventBadge badge : to_delete) {
rsup.badges_needed.remove(badge);
}
rsup.coming.add(es);
}
}
FlexCellFormatter formatter = flex.getFlexCellFormatter();
flex.setText(1, 0, JSEventSignup.SIGNUP_LABEL_COMING);
flex.setText(2, 0, "Standby");
flex.setText(3, 0, JSEventSignup.SIGNUP_LABEL_MAYBE);
flex.setText(4, 0, JSEventSignup.SIGNUP_LABEL_NOT_COMING);
styleColumn(formatter, 0);
SimplePanel sp;
int column = 1;
for (JSEventRole role : event.roles) {
RoleSignup rsup = role_signups.get(role.role_key);
rsup.column = column;
flex.setText(0, column, role.name);
styleColumn(formatter, column);
sp = new SimplePanel();
VerticalPanel vsign = new VerticalPanel();
// show signups
for (JSEventSignup sup : rsup.coming) {
JSCharacter chr = chr_cache.get(sup.chr_key);
vsign.add(new Signup(event.key, chr, sup));
}
// show remaining slots
int eff_spots_left = Math.min(rsup.spaces_left, spots_left);
for (int i = 0; i < eff_spots_left; ++i) {
HorizontalPanel hpanel = new HorizontalPanel();
{
Label l = new Label(SPACE_AVAILABLE);
l.addStyleName("available-character");
hpanel.add(l);
}
for (JSEventBadge badge : rsup.badges_required) {
Label l = new Label("!");
l.setTitle("Badge required: " + badge.name);
hpanel.add(l);
}
for (Map.Entry<JSEventBadge, Integer> needed : rsup.badges_needed.entrySet()) {
int num = needed.getValue();
if (num >= eff_spots_left) {
Label l = new Label("!");
l.setTitle("Badge required: " + needed.getKey().name);
hpanel.add(l);
}
// mark the last spots as reserved, they will switch to
// required as more people sign up who are missing this
// badge
else if (num >= eff_spots_left - i) {
Label l = new Label("*");
l.setTitle("Reserved for: " + needed.getKey().name);
hpanel.add(l);
}
}
vsign.add(hpanel);
}
sp.add(vsign);
flex.setWidget(1, column, sp);
rsup.coming_dc = new RoleDropController(event.key, role, sp,
JSEventSignup.SIGNUP_TYPE_COMING,
"coming-hi");
dragController.registerDropController(rsup.coming_dc);
registered_drop_controllers.add(rsup.coming_dc);
// show standby
VerticalPanel vstand = new VerticalPanel();
for (JSEventSignup sup : rsup.standby) {
JSCharacter chr = chr_cache.get(sup.chr_key);
vstand.add(new Signup(event.key, chr, sup));
}
flex.setWidget(2, column, vstand);
// show maybe
sp = new SimplePanel();
VerticalPanel vmaybe = new VerticalPanel();
for (JSEventSignup sup : rsup.maybe) {
JSCharacter chr = chr_cache.get(sup.chr_key);
vmaybe.add(new Signup(event.key, chr, sup));
}
if (rsup.maybe.isEmpty()) {
vmaybe.add(new HTML(" "));
}
sp.add(vmaybe);
flex.setWidget(3, column, sp);
rsup.maybe_dc = new RoleDropController(event.key, role, sp,
JSEventSignup.SIGNUP_TYPE_MAYBE,
"maybe-hi");
dragController.registerDropController(rsup.maybe_dc);
registered_drop_controllers.add(rsup.maybe_dc);
// show not coming
sp = new SimplePanel();
VerticalPanel vnotcoming = new VerticalPanel();
for (JSEventSignup sup : rsup.not_coming) {
JSCharacter chr = chr_cache.get(sup.chr_key);
vnotcoming.add(new Signup(event.key, chr, sup));
}
if (rsup.not_coming.isEmpty()) {
vnotcoming.add(new HTML(" "));
}
sp.add(vnotcoming);
flex.setWidget(4, column, sp);
rsup.not_coming_dc = new RoleDropController(event.key, role, sp,
JSEventSignup.SIGNUP_TYPE_NOT_COMING,
"notcoming-hi");
dragController.registerDropController(rsup.not_coming_dc);
registered_drop_controllers.add(rsup.not_coming_dc);
++column;
}
if (!limbo.isEmpty()) {
styleColumn(formatter, column);
// create limbo column
flex.setText(0, column, "Limbo");
VerticalPanel vstand = new VerticalPanel();
for (JSEventSignup sup : limbo) {
JSCharacter chr = chr_cache.get(sup.chr_key);
vstand.add(new Signup(event.key, chr, sup));
}
flex.setWidget(2, column, vstand);
++column;
}
return flex;
}
void resizeRows() {
if (!flex.isAttached()) {
// Can't figure out the size if not attached to DOM.
return;
}
int column = flex.getCellCount(1);
// Each role <td> must have the same number of items in it to size
// the drop area so it fills the <td>. This hack should be removed
// if a way to get the VerticalPanel to fill its space vertically
// is found.
int most_coming = 0;
int most_maybe = 0;
int most_not_coming = 0;
SimplePanel sp;
VerticalPanel vp;
UIObject uio;
for (int c = 1; c < column; ++c) {
sp = (SimplePanel)flex.getWidget(1, c);
if (sp != null) {
vp = (VerticalPanel)sp.getWidget();
most_coming = Math.max(most_coming, vp.getOffsetHeight());
}
sp = (SimplePanel)flex.getWidget(3, c);
if (sp != null) {
vp = (VerticalPanel)sp.getWidget();
most_maybe = Math.max(most_maybe, vp.getOffsetHeight());
}
sp = (SimplePanel)flex.getWidget(4, c);
if (sp != null) {
vp = (VerticalPanel)sp.getWidget();
most_not_coming = Math.max(most_not_coming, vp.getOffsetHeight());
}
}
for (int c = 1; c < column; ++c) {
uio = (UIObject)flex.getWidget(1, c);
if (uio != null) {
uio.setHeight("" + most_coming + "px");
}
uio = (UIObject)flex.getWidget(3, c);
if (uio != null) {
uio.setHeight("" + most_maybe + "px");
}
uio = (UIObject)flex.getWidget(4, c);
if (uio != null) {
uio.setHeight("" + most_not_coming + "px");
}
}
}
public void charactersChanged() {
hchrpanel.clear();
if (removeSignupDC != null) {
dragController.unregisterDropController(removeSignupDC);
removeSignupDC = null;
}
if (have_character_signed_up) {
Label l = new Label("You are signed up for this event. Drag to here to delete your signup.");
hchrpanel.add(l);
removeSignupDC = new RoleDropController(event.key, null, l, -1, "coming");
dragController.registerDropController(removeSignupDC);
return;
}
if (characters.isEmpty()) {
hchrpanel.add(new Label("You have no characters enrolled."));
hchrpanel.add(new Hyperlink("Manage characters", "characters"));
return;
}
// FIXME: the user's account_key is already known from calling
// getAccount, replace this hack
account_key = characters.get(0).account_key;
for (JSCharacter c : characters) {
hchrpanel.add(new Signup(event.key, c));
}
}
void populateRole(JSEventRole role, int column) {
flex.setText(0, column, role.name + " (" + role.min + "/" + role.max + ")");
HashMap<String, Integer> role_badges = new HashMap<String, Integer>();
// find the badge requirements
for (JSEventBadge badge : event.badges) {
if ( badge.requireForSignup
&& ( badge.applyToRole == null
|| badge.applyToRole.equals(role.name))) {
int num_slots;
if (badge.applyToRole == null) {
num_slots = role.max;
}
else {
num_slots = badge.numSlots;
}
role_badges.put(badge.name, num_slots);
}
}
int spaces_left = role.max;
for (int row = 1; row <= role.max; ++row) {
StringBuilder sb = new StringBuilder(64).append(SPACE_AVAILABLE);
for (Map.Entry<String, Integer> en : role_badges.entrySet()) {
int val = en.getValue();
if (val > 0) {
if (val >= spaces_left) {
sb.append(" - requires ")
.append(en.getKey());
}
else {
sb.append(" - reserved for ")
.append(en.getKey());
}
en.setValue(val - 1);
}
}
flex.setText(row, column, sb.toString());
--spaces_left;
}
}
void fetchSignups() {
Date asof;
if (signups != null) {
asof = signups.asof;
}
else {
asof = new Date(0L);
}
GoteFarm.goteService.getEventSignups(event.key, asof,
signupCallback);
}
public void onDragEnd(DragEndEvent event) {
DragContext c = event.getContext();
Signup sup = (Signup)c.selectedWidgets.get(0);
// ignore drag events for other raid events
if (!sup.eventKey.equals(Event.this.event.key)) {
return;
}
// Reset drop targets
FlexCellFormatter formatter = flex.getFlexCellFormatter();
for (RoleSignup rsup : role_signups.values()) {
formatter.removeStyleName(0, rsup.column, "bad-drop-target");
}
}
public void onDragStart(DragStartEvent event) {
DragContext c = event.getContext();
Signup sup = (Signup)c.selectedWidgets.get(0);
// ignore drag events for other raid events
if (!sup.eventKey.equals(Event.this.event.key)) {
return;
}
JSCharacter chr = sup.getCharacter();
FlexCellFormatter formatter = flex.getFlexCellFormatter();
// show which drop targets are viable
for (Map.Entry<String, RoleSignup> rsup : role_signups.entrySet()) {
if (!chr.hasRole(rsup.getKey())) {
formatter.addStyleName(0, rsup.getValue().column, "bad-drop-target");
}
}
}
public void onPreviewDragEnd(DragEndEvent event)
throws VetoDragException {
DragContext c = event.getContext();
Signup sup = (Signup)c.selectedWidgets.get(0);
// ignore drag events for other raid events
if (!sup.eventKey.equals(Event.this.event.key)) {
return;
}
// reject a drop outside the drop zones for a character that
// isn't signed up
if ( c.vetoException == null
&& !(c.finalDropController instanceof RoleDropController)) {
if (sup.sup == null) {
throw new VetoDragException();
}
}
}
public void onPreviewDragStart(DragStartEvent event)
throws VetoDragException {
DragContext c = event.getContext();
Signup sup = (Signup)c.selectedWidgets.get(0);
// ignore drag events for other raid events
if (!sup.eventKey.equals(Event.this.event.key)) {
return;
}
long now = System.currentTimeMillis();
// are signups over?
if (now >= Event.this.event.signups_end.getTime()) {
signup_error.setText("Signups for this event are closed.");
throw new VetoDragException();
}
JSCharacter chr = sup.getCharacter();
// must be at least this high to ride
if (chr.level < Event.this.event.minimumLevel) {
signup_error.setText(chr.name + " is level " + chr.level
+ ", this event requires level "
+ Event.this.event.minimumLevel + ".");
throw new VetoDragException();
}
// character must have at least one role
boolean role_found = false;
for (JSEventRole role : Event.this.event.roles) {
for (JSChrRole chrrole : chr.roles) {
if (role.role_key.equals(chrrole.key)) {
role_found = true;
break;
}
}
}
if (!role_found) {
signup_error.setText(chr.name + " does not perform any of the"
+ " needed roles to sign up for this"
+ " event.");
throw new VetoDragException();
}
// check if chr is able to signup yet
long signups_start = Event.this.event.signups_start.getTime();
if (now < signups_start) {
// character needs to qualify for an early signup
boolean found = false;
boolean found_early = false;
long earliest_signup = signups_start;
for (JSEventBadge badge : Event.this.event.badges) {
if (!chr.hasBadge(badge.badge_key)) {
continue;
}
if (badge.applyToRole != null && !chr.hasRole(badge.applyToRole)) {
continue;
}
// character qualifies, but has it started?
long badge_signups_start = signups_start - badge.earlySignup * 3600000L;
if (now < badge_signups_start) {
found_early = true;
earliest_signup = Math.min(earliest_signup, badge_signups_start);
continue;
}
found = true;
break;
}
if (!found) {
if (found_early) {
signup_error.setText(chr.name + " can't sign up yet,"
+ " but early signups start at "
+ time_formatter.format(
new Date(earliest_signup))
+ ".");
}
else {
signup_error.setText(chr.name + " can't sign up yet,"
+ " signups start at "
+ time_formatter.format(
new Date(earliest_signup))
+ ".");
}
throw new VetoDragException();
}
}
signup_error.setText("");
}
public void unregisterDropControllers() {
for (DropController dc : registered_drop_controllers) {
dragController.unregisterDropController(dc);
}
registered_drop_controllers.clear();
}
}
public Events() {
vpanel.setWidth("100%");
vmsgpanel.setWidth("100%");
veventpanel.setWidth("100%");
vpanel.add(vmsgpanel);
vpanel.add(veventpanel);
veventpanel.setSpacing(20);
vmsgpanel.add(new Label("You are not signed in."));
dragController = new PickupDragController(RootPanel.get(), true);
dragController.setBehaviorBoundaryPanelDrop(false);
dragController.setBehaviorConstrainedToBoundaryPanel(true);
dragController.setBehaviorDragStartSensitivity(1);
initWidget(vpanel);
// update the events list once a minute
Timer refreshTimer = new Timer() {
public void run() {
refresh();
}
};
refreshTimer.scheduleRepeating(60000);
setStyleName("Characters");
}
private ArrayList<Event> events = new ArrayList<Event>();
public void refresh() {
vmsgpanel.clear();
if (current_guild == null) {
vmsgpanel.add(new Label("Loading..."));
return;
}
else if (current_guild.key == null) {
vmsgpanel.add(new Label("Select a guild to see events."));
return;
}
// update the list of events, and also refresh the signup lists of
// each event
GoteFarm.goteService.getEvents(current_guild.key,
new AsyncCallback<List<JSEvent>>() {
public void onSuccess(List<JSEvent> result) {
vmsgpanel.clear();
events.ensureCapacity(result.size());
// merge new events into existing event list, to reuse widgets
Date now = new Date();
int old_index = 0;
int new_index = 0;
for (JSEvent e : result) {
// is this event already in the events list?
Event old_event = null;
if (old_index < events.size()) {
old_event = events.get(old_index);
}
if ( old_event != null
&& e.key.equals(old_event.event.key)) {
// give existing event widget new JSEvent
old_event.setEvent(e);
}
else {
// new event, insert
Event event = new Event(e);
dragController.addDragHandler(event);
events.add(old_index, event);
veventpanel.insert(event, old_index);
// should we be seeing this event?
if (e.display_start.compareTo(now) > 0) {
event.setVisible(false);
}
else {
// need to resize after attaching to DOM
event.resizeRows();
}
}
++old_index;
++new_index;
}
// remove remaining old events
while (old_index < events.size()) {
Event event = events.remove(old_index);
event.unregisterDropControllers();
dragController.removeDragHandler(event);
veventpanel.remove(event);
}
boolean any_visible = false;
for (Event e : events) {
if (!e.isVisible()) {
if (e.event.display_start.compareTo(now) <= 0) {
any_visible = true;
e.setVisible(true);
e.resizeRows();
}
}
else {
any_visible = true;
}
e.fetchSignups();
}
if (!any_visible) {
vmsgpanel.add(new Label("No events"));
}
}
public void onFailure(Throwable caught) {
vmsgpanel.add(new Label(caught.getMessage()));
}
});
}
public void onValueChange(ValueChangeEvent<List<JSCharacter>> event) {
characters = event.getValue();
for (Event e : events) {
e.charactersChanged();
}
}
public void resizeRows() {
for (Event e : events) {
e.resizeRows();
}
}
}