/******************************************************************************* * Copyright (c) 2014, 2015 Scott Clarke (scott@dawg6.com). * * This file is part of Dawg6's Demon Hunter DPS Calculator. * * Dawg6's Demon Hunter DPS Calculator is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Dawg6's Demon Hunter DPS Calculator is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. *******************************************************************************/ package com.dawg6.web.dhcalc.client; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; import java.util.Vector; import com.dawg6.gwt.client.widgets.SimpleCaptionPanel; import com.dawg6.web.dhcalc.shared.calculator.ActiveSkill; import com.dawg6.web.dhcalc.shared.calculator.Rune; import com.dawg6.web.dhcalc.shared.calculator.SkillType; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.FlexTable; import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.SimpleCheckBox; public class SkillsPanel extends Composite { private final NumberSpinner mfdUptime; private final NumberSpinner mfdAddUptime; private final NumberSpinner caltropsUptime; private final List<Anchor> skillAnchors = new Vector<Anchor>(NUM_SKILLS); private final List<ListBox> skillBoxes = new Vector<ListBox>(NUM_SKILLS); private final List<Anchor> runeAnchors = new Vector<Anchor>(NUM_SKILLS); private final List<ListBox> runeBoxes = new Vector<ListBox>(NUM_SKILLS); private final Set<SkillsChangedListener> listeners = new TreeSet<SkillsChangedListener>(); private final SimpleCheckBox syncWithCoe; private final SimpleCheckBox spenderFullHatred; public interface SkillsChangedListener { void skillsChanged(Map<ActiveSkill, Rune> skills); } public static final int NUM_SKILLS = 6; private boolean disableListeners = false; public SkillsPanel() { SimpleCaptionPanel SimpleCaptionPanel = new SimpleCaptionPanel("Active Skills"); initWidget(SimpleCaptionPanel); FlexTable flexTable = new FlexTable(); SimpleCaptionPanel.setContentWidget(flexTable); int row = 0; List<ActiveSkill> skills = new Vector<ActiveSkill>(); for (ActiveSkill s : ActiveSkill.values()) { if (s.getSkillType() != SkillType.NA) skills.add(s); } Collections.sort(skills, new Comparator<ActiveSkill>() { @Override public int compare(ActiveSkill o1, ActiveSkill o2) { return o1.getLongName().toLowerCase() .compareTo(o2.getLongName().toLowerCase()); } }); for (int i = 0; i < NUM_SKILLS; i++) { final Anchor anchor = new Anchor("Skill " + (i + 1) + ":"); anchor.setHref("javascript:void(0)"); anchor.setTarget("_blank"); anchor.setWordWrap(false); skillAnchors.add(anchor); flexTable.setWidget(row, 0, anchor); final ListBox list = new ListBox(); list.addItem("None", ""); for (ActiveSkill s : skills) { list.addItem(s.getLongName(), s.name()); } list.setSelectedIndex(0); skillBoxes.add(list); list.setWidth("100%"); flexTable.setWidget(row, 1, list); final Anchor anchor2 = new Anchor("Rune:"); anchor2.setHref("javascript:void(0)"); anchor2.setTarget("_blank"); anchor2.setWordWrap(false); runeAnchors.add(anchor2); flexTable.setWidget(row, 2, anchor2); final ListBox list2 = new ListBox(); list2.addItem("None", Rune.None.name()); list2.setSelectedIndex(0); list2.setWidth("100%"); flexTable.setWidget(row, 3, list2); runeBoxes.add(list2); list.addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { if (!disableListeners) { skillChanged(anchor, list, anchor2, list2); } } }); list2.addChangeHandler(new ChangeHandler() { @Override public void onChange(ChangeEvent event) { if (!disableListeners) { runeChanged(list, anchor2, list2); } } }); row++; } Label label_1 = new Label("Sync Cooldowns with CoE:"); label_1.setWordWrap(false); flexTable.setWidget(row, 0, label_1); flexTable.getFlexCellFormatter().setColSpan(row, 0, 2); flexTable.getFlexCellFormatter().setHorizontalAlignment(row, 0, HasHorizontalAlignment.ALIGN_RIGHT); syncWithCoe = new SimpleCheckBox(); syncWithCoe .setTitle("If set and wearing CoE, cooldowns (FoK, RoV, Companion) will only be used when proper CoE buff is up."); flexTable.setWidget(row, 1, syncWithCoe); flexTable.getFlexCellFormatter().setColSpan(row, 1, 2); row++; Label label_1a = new Label("Spender Requires Full Hatred:"); label_1a.setWordWrap(false); flexTable.setWidget(row, 0, label_1a); flexTable.getFlexCellFormatter().setColSpan(row, 0, 2); flexTable.getFlexCellFormatter().setHorizontalAlignment(row, 0, HasHorizontalAlignment.ALIGN_RIGHT); spenderFullHatred = new SimpleCheckBox(); spenderFullHatred .setTitle("Only use spender when Hatred is full (or needed for a buff)."); flexTable.setWidget(row, 1, spenderFullHatred); flexTable.getFlexCellFormatter().setColSpan(row, 1, 2); row++; Label label = new Label("Caltrops Uptime:"); label.setWordWrap(false); flexTable.setWidget(row, 0, label); flexTable.getFlexCellFormatter().setColSpan(row, 0, 2); flexTable.getFlexCellFormatter().setHorizontalAlignment(row, 0, HasHorizontalAlignment.ALIGN_RIGHT); caltropsUptime = new NumberSpinner(); caltropsUptime.setVisibleLength(3); caltropsUptime .setTitle("Percent of the time primary target will have active Caltrops applied."); flexTable.setWidget(row, 1, caltropsUptime); flexTable.getFlexCellFormatter().setColSpan(row, 1, 2); caltropsUptime.setMax(100); row++; Label lblUptime = new Label("Primary Target MfD Uptime:"); lblUptime.setWordWrap(false); flexTable.setWidget(row, 0, lblUptime); flexTable.getFlexCellFormatter().setColSpan(row, 0, 2); flexTable.getFlexCellFormatter().setHorizontalAlignment(row, 0, HasHorizontalAlignment.ALIGN_RIGHT); mfdUptime = new NumberSpinner(); mfdUptime .setTitle("Percent of the time primary target will have active MfD applied."); mfdUptime.setVisibleLength(3); flexTable.setWidget(row, 1, mfdUptime); flexTable.getFlexCellFormatter().setColSpan(row, 1, 2); row++; Label lblAdditionalTargetsUptime = new Label( "Additional Targets MfD Uptime:"); lblAdditionalTargetsUptime.setWordWrap(false); flexTable.setWidget(row, 0, lblAdditionalTargetsUptime); flexTable.getFlexCellFormatter().setColSpan(row, 0, 2); flexTable.getFlexCellFormatter().setHorizontalAlignment(row, 0, HasHorizontalAlignment.ALIGN_RIGHT); mfdAddUptime = new NumberSpinner(); mfdAddUptime.setVisibleLength(3); mfdAddUptime .setTitle("Percent of the time additional targets will have active MfD applied."); flexTable.setWidget(row, 1, mfdAddUptime); flexTable.getFlexCellFormatter().setColSpan(row, 1, 2); row++; mfdUptime.setMax(100); mfdAddUptime.setMax(100); caltropsUptime.setMax(100); } public void addSkillsChangedListener(SkillsChangedListener l) { listeners.add(l); } public void removeSkillsChangedListener(SkillsChangedListener l) { listeners.remove(l); } protected void runeChanged(ListBox list, Anchor anchor2, ListBox list2) { ActiveSkill skill = getSelectedSkill(list); Rune rune = getSelectedRune(list2); setRuneAnchor(skill, anchor2, rune); this.skillsChanged(this.getSkills()); } private void setRuneAnchor(ActiveSkill skill, Anchor anchor2, Rune rune) { if (skill == null) { anchor2.setHref("javascript:void(0)"); } else if ((rune == null) || (rune == Rune.None)) { anchor2.setHref(skill.getUrl()); } else { anchor2.setHref(skill.getUrl() + "#" + rune.getSlug() + "+"); } } private Rune getSelectedRune(ListBox list) { int i = list.getSelectedIndex(); if (i <= 0) return Rune.None; String value = list.getValue(i); return Rune.valueOf(value); } protected void skillChanged(Anchor anchor, ListBox list, Anchor anchor2, ListBox list2) { ActiveSkill skill = getSelectedSkill(list); setRunes(anchor2, list2, skill); setSkillAnchor(anchor, skill); checkDuplicate(list); this.skillsChanged(this.getSkills()); } private void checkDuplicate(ListBox lb) { int selected = lb.getSelectedIndex(); boolean prev = disableListeners; disableListeners = true; for (int i = 0; i < NUM_SKILLS; i++) { ListBox l = skillBoxes.get(i); if ((l != lb) && (l.getSelectedIndex() == selected)) { l.setSelectedIndex(0); setRunes(runeAnchors.get(i), runeBoxes.get(i), null); setSkillAnchor(skillAnchors.get(i), null); } } disableListeners = prev; } private void setSkillAnchor(Anchor anchor, ActiveSkill skill) { if (skill == null) { anchor.setHref("javascript:void(0)"); } else { anchor.setHref(skill.getUrl()); } } private void setRunes(Anchor anchor2, ListBox list2, ActiveSkill skill) { list2.clear(); if (skill != null) { for (Rune r : skill.getRunes()) { list2.addItem(r.getLongName(), r.name()); } } else { list2.addItem("None", Rune.None.name()); } list2.setSelectedIndex(0); setRuneAnchor(skill, anchor2, Rune.None); } private ActiveSkill getSelectedSkill(ListBox list) { int i = list.getSelectedIndex(); if (i <= 0) return null; String value = list.getValue(i); return ActiveSkill.valueOf(value); } public NumberSpinner getMfdUptime() { return mfdUptime; } public NumberSpinner getMfdAddUptime() { return mfdAddUptime; } public NumberSpinner getCaltropsUptime() { return caltropsUptime; } public Map<ActiveSkill, Rune> getSkills() { Map<ActiveSkill, Rune> map = new TreeMap<ActiveSkill, Rune>(); for (int i = 0; i < NUM_SKILLS; i++) { ActiveSkill s = getSelectedSkill(skillBoxes.get(i)); if (s != null) { Rune r = getSelectedRune(runeBoxes.get(i)); map.put(s, r); } } return map; } public void setSkills(Map<ActiveSkill, Rune> skills) { disableListeners = true; boolean changed = false; int i = 0; for (Map.Entry<ActiveSkill, Rune> e : skills.entrySet()) { if (i < NUM_SKILLS) { ActiveSkill skill = e.getKey(); Rune rune = e.getValue(); ListBox list = skillBoxes.get(i); ListBox list2 = runeBoxes.get(i); Anchor anchor2 = runeAnchors.get(i); ActiveSkill s1 = getSelectedSkill(list); if (skill != s1) { Anchor anchor = skillAnchors.get(i); changed = true; selectSkill(anchor, list, skill, anchor2, list2); } Rune r1 = getSelectedRune(list2); if (rune != r1) { changed = true; selectRune(skill, anchor2, list2, rune); } i++; } } while (i < NUM_SKILLS) { ListBox list = skillBoxes.get(i); ActiveSkill skill = getSelectedSkill(list); if (skill != null) { ListBox list2 = runeBoxes.get(i); Anchor anchor = skillAnchors.get(i); Anchor anchor2 = runeAnchors.get(i); changed = true; selectSkill(anchor, list, null, anchor2, list2); } i++; } disableListeners = false; if (changed) { skillsChanged(skills); } } private void skillsChanged(Map<ActiveSkill, Rune> skills) { if (!disableListeners) { for (SkillsChangedListener l : listeners) l.skillsChanged(skills); } } private void selectRune(ActiveSkill skill, Anchor anchor2, ListBox list2, Rune rune) { if (rune == null) { list2.setSelectedIndex(0); } else { int n = list2.getItemCount(); for (int i = 0; i < n; i++) { String value = list2.getValue(i); if (value.equals(rune.name())) { list2.setSelectedIndex(i); break; } } } this.setRuneAnchor(skill, anchor2, rune); } private void selectSkill(Anchor anchor, ListBox list, ActiveSkill skill, Anchor anchor2, ListBox list2) { if (skill == null) { list.setSelectedIndex(0); } else { int n = list.getItemCount(); for (int i = 0; i < n; i++) { String value = list.getValue(i); if (value.equals(skill.name())) { list.setSelectedIndex(i); break; } } } this.setSkillAnchor(anchor, skill); this.setRunes(anchor2, list2, skill); } public SimpleCheckBox getSyncWithCoe() { return this.syncWithCoe; } public SimpleCheckBox getSpenderFullHatred() { return spenderFullHatred; } }