time tracking: add clock panel
This commit is contained in:
@@ -32,6 +32,7 @@ import net.runelite.api.ItemID;
|
||||
@Getter
|
||||
public enum Tab
|
||||
{
|
||||
CLOCK("Timers & Stopwatches", ItemID.WATCH),
|
||||
BIRD_HOUSE("Bird Houses", ItemID.OAK_BIRD_HOUSE),
|
||||
ALLOTMENT("Allotment Patches", ItemID.CABBAGE),
|
||||
FLOWER("Flower Patches", ItemID.RED_FLOWERS),
|
||||
|
||||
@@ -26,15 +26,21 @@ package net.runelite.client.plugins.timetracking;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import javax.swing.JPanel;
|
||||
import net.runelite.client.ui.PluginPanel;
|
||||
|
||||
public abstract class TabContentPanel extends JPanel
|
||||
{
|
||||
/**
|
||||
* Gets the update interval of this panel, in units of 200 milliseconds
|
||||
* (the plugin panel checks if its contents should be updated every 200 ms;
|
||||
* this can be considered its "tick rate").
|
||||
*/
|
||||
public abstract int getUpdateInterval();
|
||||
|
||||
public abstract void update();
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize()
|
||||
{
|
||||
return new Dimension(PluginPanel.PANEL_WIDTH, super.getPreferredSize().height);
|
||||
return super.getPreferredSize();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,24 +31,39 @@ import net.runelite.client.config.ConfigItem;
|
||||
@ConfigGroup("timetracking")
|
||||
public interface TimeTrackingConfig extends Config
|
||||
{
|
||||
String KEY_NAME = "timetracking";
|
||||
String CONFIG_GROUP = "timetracking";
|
||||
String AUTOWEED = "autoweed";
|
||||
String BIRD_HOUSE = "birdhouse";
|
||||
String TIMERS = "timers";
|
||||
String STOPWATCHES = "stopwatches";
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "estimateRelative",
|
||||
name = "Show relative time",
|
||||
description = "Show amount of time remaining instead of completion time"
|
||||
description = "Show amount of time remaining instead of completion time",
|
||||
position = 1
|
||||
)
|
||||
default boolean estimateRelative()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "timerNotification",
|
||||
name = "Timer notification",
|
||||
description = "Notify you whenever a timer has finished counting down",
|
||||
position = 2
|
||||
)
|
||||
default boolean timerNotification()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "birdHouseNotification",
|
||||
name = "Bird house notification",
|
||||
description = "Notify you when all bird houses are full"
|
||||
description = "Notify you when all bird houses are full",
|
||||
position = 3
|
||||
)
|
||||
default boolean birdHouseNotification()
|
||||
{
|
||||
@@ -63,7 +78,7 @@ public interface TimeTrackingConfig extends Config
|
||||
)
|
||||
default Tab activeTab()
|
||||
{
|
||||
return Tab.ALLOTMENT;
|
||||
return Tab.CLOCK;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
|
||||
@@ -34,10 +34,12 @@ import javax.annotation.Nullable;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.client.game.AsyncBufferedImage;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.plugins.timetracking.clocks.ClockManager;
|
||||
import net.runelite.client.plugins.timetracking.farming.FarmingTracker;
|
||||
import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
@@ -61,7 +63,7 @@ class TimeTrackingPanel extends PluginPanel
|
||||
private TabContentPanel activeTabPanel = null;
|
||||
|
||||
TimeTrackingPanel(ItemManager itemManager, TimeTrackingConfig config,
|
||||
FarmingTracker farmingTracker, BirdHouseTracker birdHouseTracker)
|
||||
FarmingTracker farmingTracker, BirdHouseTracker birdHouseTracker, ClockManager clockManager)
|
||||
{
|
||||
super(false);
|
||||
|
||||
@@ -79,6 +81,7 @@ class TimeTrackingPanel extends PluginPanel
|
||||
add(tabGroup, BorderLayout.NORTH);
|
||||
add(display, BorderLayout.CENTER);
|
||||
|
||||
addTab(Tab.CLOCK, clockManager.getClockTabPanel());
|
||||
addTab(Tab.BIRD_HOUSE, birdHouseTracker.createBirdHouseTabPanel());
|
||||
|
||||
for (Tab tab : Tab.FARMING_TABS)
|
||||
@@ -130,6 +133,17 @@ class TimeTrackingPanel extends PluginPanel
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the update interval of the active tab panel, in units of 200 milliseconds.
|
||||
*/
|
||||
int getUpdateInterval()
|
||||
{
|
||||
return activeTabPanel == null ? Integer.MAX_VALUE : activeTabPanel.getUpdateInterval();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the active tab panel, if this plugin panel is displayed.
|
||||
*/
|
||||
void update()
|
||||
{
|
||||
if (!active || activeTabPanel == null)
|
||||
@@ -137,7 +151,7 @@ class TimeTrackingPanel extends PluginPanel
|
||||
return;
|
||||
}
|
||||
|
||||
activeTabPanel.update();
|
||||
SwingUtilities.invokeLater(activeTabPanel::update);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,13 +28,17 @@ package net.runelite.client.plugins.timetracking;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.SwingUtilities;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.UsernameChanged;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
@@ -43,6 +47,10 @@ import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.CONFIG_GROUP;
|
||||
import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.STOPWATCHES;
|
||||
import static net.runelite.client.plugins.timetracking.TimeTrackingConfig.TIMERS;
|
||||
import net.runelite.client.plugins.timetracking.clocks.ClockManager;
|
||||
import net.runelite.client.plugins.timetracking.farming.FarmingTracker;
|
||||
import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker;
|
||||
import net.runelite.client.task.Schedule;
|
||||
@@ -52,8 +60,8 @@ import net.runelite.client.util.ImageUtil;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Time Tracking",
|
||||
description = "Enable the Time Tracking panel, which contains farming and bird house trackers",
|
||||
tags = {"birdhouse", "farming", "hunter", "notifications", "skilling", "panel"}
|
||||
description = "Enable the Time Tracking panel, which contains timers, stopwatches, and farming and bird house trackers",
|
||||
tags = {"birdhouse", "farming", "hunter", "notifications", "skilling", "stopwatches", "timers", "panel"}
|
||||
)
|
||||
@Slf4j
|
||||
public class TimeTrackingPlugin extends Plugin
|
||||
@@ -70,12 +78,20 @@ public class TimeTrackingPlugin extends Plugin
|
||||
@Inject
|
||||
private BirdHouseTracker birdHouseTracker;
|
||||
|
||||
@Inject
|
||||
private ClockManager clockManager;
|
||||
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
|
||||
@Inject
|
||||
private TimeTrackingConfig config;
|
||||
|
||||
@Inject
|
||||
private ScheduledExecutorService executorService;
|
||||
|
||||
private ScheduledFuture panelUpdateFuture;
|
||||
|
||||
private TimeTrackingPanel panel;
|
||||
|
||||
private NavigationButton navButton;
|
||||
@@ -92,11 +108,13 @@ public class TimeTrackingPlugin extends Plugin
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
clockManager.loadTimers();
|
||||
clockManager.loadStopwatches();
|
||||
birdHouseTracker.loadFromConfig();
|
||||
|
||||
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "watch.png");
|
||||
|
||||
panel = new TimeTrackingPanel(itemManager, config, farmingTracker, birdHouseTracker);
|
||||
panel = new TimeTrackingPanel(itemManager, config, farmingTracker, birdHouseTracker, clockManager);
|
||||
|
||||
navButton = NavigationButton.builder()
|
||||
.tooltip("Time Tracking")
|
||||
@@ -107,7 +125,7 @@ public class TimeTrackingPlugin extends Plugin
|
||||
|
||||
clientToolbar.addNavigation(navButton);
|
||||
|
||||
updatePanel();
|
||||
panelUpdateFuture = executorService.scheduleAtFixedRate(this::updatePanel, 200, 200, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -115,9 +133,34 @@ public class TimeTrackingPlugin extends Plugin
|
||||
{
|
||||
lastTickLocation = null;
|
||||
lastTickPostLogin = false;
|
||||
|
||||
if (panelUpdateFuture != null)
|
||||
{
|
||||
panelUpdateFuture.cancel(true);
|
||||
panelUpdateFuture = null;
|
||||
}
|
||||
|
||||
clientToolbar.removeNavigation(navButton);
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onConfigChanged(ConfigChanged e)
|
||||
{
|
||||
if (!e.getGroup().equals(CONFIG_GROUP))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (clockManager.getTimers().isEmpty() && e.getKey().equals(TIMERS))
|
||||
{
|
||||
clockManager.loadTimers();
|
||||
}
|
||||
else if (clockManager.getStopwatches().isEmpty() && e.getKey().equals(STOPWATCHES))
|
||||
{
|
||||
clockManager.loadStopwatches();
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onGameTick(GameTick t)
|
||||
{
|
||||
@@ -154,7 +197,7 @@ public class TimeTrackingPlugin extends Plugin
|
||||
|
||||
if (birdHouseDataChanged || farmingDataChanged)
|
||||
{
|
||||
updatePanel();
|
||||
panel.update();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +206,7 @@ public class TimeTrackingPlugin extends Plugin
|
||||
{
|
||||
farmingTracker.migrateConfiguration();
|
||||
birdHouseTracker.loadFromConfig();
|
||||
updatePanel();
|
||||
panel.update();
|
||||
}
|
||||
|
||||
@Schedule(period = 10, unit = ChronoUnit.SECONDS)
|
||||
@@ -173,13 +216,24 @@ public class TimeTrackingPlugin extends Plugin
|
||||
|
||||
if (birdHouseDataChanged)
|
||||
{
|
||||
updatePanel();
|
||||
panel.update();
|
||||
}
|
||||
}
|
||||
|
||||
@Schedule(period = 10, unit = ChronoUnit.SECONDS)
|
||||
public void updatePanel()
|
||||
private void updatePanel()
|
||||
{
|
||||
SwingUtilities.invokeLater(panel::update);
|
||||
long unitTime = Instant.now().toEpochMilli() / 200;
|
||||
|
||||
boolean clockDataChanged = false;
|
||||
|
||||
if (unitTime % 5 == 0)
|
||||
{
|
||||
clockDataChanged = clockManager.checkCompletion();
|
||||
}
|
||||
|
||||
if (unitTime % panel.getUpdateInterval() == 0 || clockDataChanged)
|
||||
{
|
||||
panel.update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.timetracking.clocks;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
abstract class Clock
|
||||
{
|
||||
protected String name;
|
||||
|
||||
// last updated time (recorded as seconds since epoch)
|
||||
protected long lastUpdate;
|
||||
|
||||
// whether the clock is currently running
|
||||
protected boolean active;
|
||||
|
||||
Clock(String name)
|
||||
{
|
||||
this.name = name;
|
||||
this.lastUpdate = Instant.now().getEpochSecond();
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
abstract long getDisplayTime();
|
||||
|
||||
abstract void setDuration(long duration);
|
||||
|
||||
abstract boolean start();
|
||||
|
||||
abstract boolean pause();
|
||||
|
||||
abstract void reset();
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.timetracking.clocks;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.inject.Singleton;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import javax.inject.Inject;
|
||||
import javax.swing.SwingUtilities;
|
||||
import joptsimple.internal.Strings;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.Notifier;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.plugins.timetracking.TimeTrackingConfig;
|
||||
|
||||
@Singleton
|
||||
public class ClockManager
|
||||
{
|
||||
private static final long DEFAULT_TIMER_DURATION = 60 * 5; // 5 minutes
|
||||
|
||||
@Inject
|
||||
private ConfigManager configManager;
|
||||
|
||||
@Inject
|
||||
private TimeTrackingConfig config;
|
||||
|
||||
@Inject
|
||||
private Notifier notifier;
|
||||
|
||||
@Getter
|
||||
private final List<Timer> timers = new CopyOnWriteArrayList<>();
|
||||
|
||||
@Getter
|
||||
private final List<Stopwatch> stopwatches = new ArrayList<>();
|
||||
|
||||
@Getter
|
||||
private ClockTabPanel clockTabPanel = new ClockTabPanel(this);
|
||||
|
||||
void addTimer()
|
||||
{
|
||||
timers.add(new Timer("Timer " + (timers.size() + 1), DEFAULT_TIMER_DURATION));
|
||||
saveTimers();
|
||||
|
||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||
}
|
||||
|
||||
void addStopwatch()
|
||||
{
|
||||
stopwatches.add(new Stopwatch("Stopwatch " + (stopwatches.size() + 1)));
|
||||
saveStopwatches();
|
||||
|
||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||
}
|
||||
|
||||
void removeTimer(Timer timer)
|
||||
{
|
||||
timers.remove(timer);
|
||||
saveTimers();
|
||||
|
||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||
}
|
||||
|
||||
void removeStopwatch(Stopwatch stopwatch)
|
||||
{
|
||||
stopwatches.remove(stopwatch);
|
||||
saveStopwatches();
|
||||
|
||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if any timers have completed, and send notifications if required.
|
||||
*/
|
||||
public boolean checkCompletion()
|
||||
{
|
||||
boolean changed = false;
|
||||
|
||||
for (Timer timer : timers)
|
||||
{
|
||||
if (timer.isActive() && timer.getDisplayTime() == 0)
|
||||
{
|
||||
timer.pause();
|
||||
changed = true;
|
||||
|
||||
if (config.timerNotification())
|
||||
{
|
||||
notifier.notify("[" + timer.getName() + "] has finished counting down.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
saveTimers();
|
||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
public void loadTimers()
|
||||
{
|
||||
final String timersJson = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.TIMERS);
|
||||
|
||||
if (!Strings.isNullOrEmpty(timersJson))
|
||||
{
|
||||
final Gson gson = new Gson();
|
||||
final List<Timer> timers = gson.fromJson(timersJson, new TypeToken<ArrayList<Timer>>()
|
||||
{
|
||||
}.getType());
|
||||
|
||||
this.timers.clear();
|
||||
this.timers.addAll(timers);
|
||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||
}
|
||||
}
|
||||
|
||||
public void loadStopwatches()
|
||||
{
|
||||
final String stopwatchesJson = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.STOPWATCHES);
|
||||
|
||||
if (!Strings.isNullOrEmpty(stopwatchesJson))
|
||||
{
|
||||
final Gson gson = new Gson();
|
||||
final List<Stopwatch> stopwatches = gson.fromJson(stopwatchesJson, new TypeToken<ArrayList<Stopwatch>>()
|
||||
{
|
||||
}.getType());
|
||||
|
||||
this.stopwatches.clear();
|
||||
this.stopwatches.addAll(stopwatches);
|
||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||
}
|
||||
}
|
||||
|
||||
public void clear()
|
||||
{
|
||||
timers.clear();
|
||||
stopwatches.clear();
|
||||
|
||||
SwingUtilities.invokeLater(clockTabPanel::rebuild);
|
||||
}
|
||||
|
||||
void saveToConfig()
|
||||
{
|
||||
saveTimers();
|
||||
saveStopwatches();
|
||||
}
|
||||
|
||||
void saveTimers()
|
||||
{
|
||||
final Gson gson = new Gson();
|
||||
final String json = gson.toJson(timers);
|
||||
configManager.setConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.TIMERS, json);
|
||||
}
|
||||
|
||||
void saveStopwatches()
|
||||
{
|
||||
final Gson gson = new Gson();
|
||||
final String json = gson.toJson(stopwatches);
|
||||
configManager.setConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.STOPWATCHES, json);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,269 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.timetracking.clocks;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.event.FocusEvent;
|
||||
import java.awt.event.FocusListener;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import javax.swing.BorderFactory;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.CompoundBorder;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.components.FlatTextField;
|
||||
import net.runelite.client.ui.components.IconButton;
|
||||
|
||||
abstract class ClockPanel extends JPanel
|
||||
{
|
||||
private static final Border NAME_BOTTOM_BORDER = new CompoundBorder(
|
||||
BorderFactory.createMatteBorder(0, 0, 1, 0, ColorScheme.DARK_GRAY_COLOR),
|
||||
BorderFactory.createLineBorder(ColorScheme.DARKER_GRAY_COLOR));
|
||||
|
||||
private static final Color ACTIVE_CLOCK_COLOR = ColorScheme.LIGHT_GRAY_COLOR.brighter();
|
||||
private static final Color INACTIVE_CLOCK_COLOR = ColorScheme.LIGHT_GRAY_COLOR.darker();
|
||||
|
||||
// additional content or buttons should be added to these panels in the subclasses
|
||||
final JPanel contentContainer;
|
||||
final JPanel leftActions;
|
||||
final JPanel rightActions;
|
||||
|
||||
private final FlatTextField nameInput;
|
||||
private final IconButton startPauseButton;
|
||||
private final FlatTextField displayInput;
|
||||
|
||||
@Getter
|
||||
private final Clock clock;
|
||||
|
||||
private final String clockType;
|
||||
private final boolean editable;
|
||||
|
||||
ClockPanel(ClockManager clockManager, Clock clock, String clockType, boolean editable)
|
||||
{
|
||||
this.clock = clock;
|
||||
this.clockType = clockType;
|
||||
this.editable = editable;
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
setBorder(new EmptyBorder(3, 0, 0, 0));
|
||||
|
||||
JPanel nameWrapper = new JPanel(new BorderLayout());
|
||||
nameWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
nameWrapper.setBorder(NAME_BOTTOM_BORDER);
|
||||
|
||||
nameInput = new FlatTextField();
|
||||
nameInput.setText(clock.getName());
|
||||
nameInput.setBorder(null);
|
||||
nameInput.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
nameInput.setPreferredSize(new Dimension(0, 24));
|
||||
nameInput.getTextField().setBorder(new EmptyBorder(0, 8, 0, 0));
|
||||
nameInput.addActionListener(e -> getParent().requestFocusInWindow());
|
||||
|
||||
nameInput.getTextField().addFocusListener(new FocusListener()
|
||||
{
|
||||
@Override
|
||||
public void focusGained(FocusEvent e)
|
||||
{
|
||||
nameInput.getTextField().selectAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent e)
|
||||
{
|
||||
clock.setName(nameInput.getText());
|
||||
clockManager.saveToConfig();
|
||||
}
|
||||
});
|
||||
|
||||
nameWrapper.add(nameInput, BorderLayout.CENTER);
|
||||
|
||||
JPanel mainContainer = new JPanel(new BorderLayout());
|
||||
mainContainer.setBorder(new EmptyBorder(5, 0, 0, 0));
|
||||
mainContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
contentContainer = new JPanel(new BorderLayout());
|
||||
contentContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
displayInput = new FlatTextField();
|
||||
displayInput.setEditable(editable);
|
||||
displayInput.setBorder(null);
|
||||
displayInput.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
displayInput.setPreferredSize(new Dimension(0, 24));
|
||||
displayInput.getTextField().setHorizontalAlignment(SwingConstants.CENTER);
|
||||
displayInput.addActionListener(e -> getParent().requestFocusInWindow());
|
||||
|
||||
displayInput.getTextField().addFocusListener(new FocusListener()
|
||||
{
|
||||
@Override
|
||||
public void focusGained(FocusEvent e)
|
||||
{
|
||||
displayInput.getTextField().setForeground(INACTIVE_CLOCK_COLOR);
|
||||
displayInput.getTextField().selectAll();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void focusLost(FocusEvent e)
|
||||
{
|
||||
String[] parts = displayInput.getText().split(":");
|
||||
long duration = 0;
|
||||
|
||||
// parse from back to front, so as to accept hour:min:sec, min:sec, and sec formats
|
||||
for (int i = parts.length - 1, multiplier = 1; i >= 0 && multiplier <= 3600; i--, multiplier *= 60)
|
||||
{
|
||||
try
|
||||
{
|
||||
duration += Integer.parseInt(parts[i].trim()) * multiplier;
|
||||
}
|
||||
catch (NumberFormatException nfe)
|
||||
{
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
clock.setDuration(Math.max(0, duration));
|
||||
clock.reset();
|
||||
updateDisplayInput();
|
||||
updateActivityStatus();
|
||||
clockManager.saveTimers();
|
||||
}
|
||||
});
|
||||
|
||||
updateDisplayInput();
|
||||
|
||||
contentContainer.add(displayInput, BorderLayout.NORTH);
|
||||
|
||||
JPanel actionsBar = new JPanel(new BorderLayout());
|
||||
actionsBar.setBorder(new EmptyBorder(4, 0, 4, 0));
|
||||
actionsBar.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
leftActions = new JPanel(new FlowLayout(FlowLayout.LEFT, 6, 0));
|
||||
leftActions.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
startPauseButton = new IconButton(ClockTabPanel.START_ICON);
|
||||
startPauseButton.setPreferredSize(new Dimension(16, 14));
|
||||
updateActivityStatus();
|
||||
|
||||
startPauseButton.addMouseListener(new MouseAdapter()
|
||||
{
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e)
|
||||
{
|
||||
startPauseButton.setIcon(clock.isActive() ? ClockTabPanel.PAUSE_ICON_HOVER : ClockTabPanel.START_ICON_HOVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e)
|
||||
{
|
||||
startPauseButton.setIcon(clock.isActive() ? ClockTabPanel.PAUSE_ICON : ClockTabPanel.START_ICON);
|
||||
}
|
||||
});
|
||||
|
||||
startPauseButton.addActionListener(e ->
|
||||
{
|
||||
if (clock.isActive())
|
||||
{
|
||||
clock.pause();
|
||||
}
|
||||
else if (!clock.start())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
updateActivityStatus();
|
||||
clockManager.saveToConfig();
|
||||
});
|
||||
|
||||
IconButton resetButton = new IconButton(ClockTabPanel.RESET_ICON, ClockTabPanel.RESET_ICON_HOVER);
|
||||
resetButton.setPreferredSize(new Dimension(16, 14));
|
||||
resetButton.setToolTipText("Reset " + clockType);
|
||||
|
||||
resetButton.addActionListener(e ->
|
||||
{
|
||||
clock.reset();
|
||||
reset();
|
||||
clockManager.saveToConfig();
|
||||
});
|
||||
|
||||
leftActions.add(startPauseButton);
|
||||
leftActions.add(resetButton);
|
||||
|
||||
rightActions = new JPanel(new FlowLayout(FlowLayout.RIGHT, 6, 0));
|
||||
rightActions.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
|
||||
actionsBar.add(leftActions, BorderLayout.WEST);
|
||||
actionsBar.add(rightActions, BorderLayout.EAST);
|
||||
|
||||
mainContainer.add(contentContainer, BorderLayout.CENTER);
|
||||
mainContainer.add(actionsBar, BorderLayout.SOUTH);
|
||||
|
||||
add(nameWrapper, BorderLayout.NORTH);
|
||||
add(mainContainer, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
void reset()
|
||||
{
|
||||
updateDisplayInput();
|
||||
updateActivityStatus();
|
||||
}
|
||||
|
||||
void updateDisplayInput()
|
||||
{
|
||||
if (!displayInput.getTextField().hasFocus())
|
||||
{
|
||||
displayInput.setText(getFormattedDuration(clock.getDisplayTime()));
|
||||
}
|
||||
}
|
||||
|
||||
void updateActivityStatus()
|
||||
{
|
||||
boolean isActive = clock.isActive();
|
||||
|
||||
displayInput.setEditable(editable && !isActive);
|
||||
displayInput.getTextField().setForeground(isActive ? ACTIVE_CLOCK_COLOR : INACTIVE_CLOCK_COLOR);
|
||||
startPauseButton.setToolTipText(isActive ? "Pause " + clockType : "Start " + clockType);
|
||||
startPauseButton.setIcon(isActive ? ClockTabPanel.PAUSE_ICON : ClockTabPanel.START_ICON);
|
||||
|
||||
if (editable && clock.getDisplayTime() == 0 && !isActive)
|
||||
{
|
||||
displayInput.getTextField().setForeground(ColorScheme.PROGRESS_ERROR_COLOR.darker());
|
||||
}
|
||||
}
|
||||
|
||||
static String getFormattedDuration(long duration)
|
||||
{
|
||||
long hours = duration / (60 * 60);
|
||||
long mins = (duration / 60) % 60;
|
||||
long seconds = duration % 60;
|
||||
|
||||
return String.format("%02d:%02d:%02d", hours, mins, seconds);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.timetracking.clocks;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import net.runelite.client.plugins.timetracking.TabContentPanel;
|
||||
import net.runelite.client.plugins.timetracking.TimeTrackingPlugin;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.DynamicGridLayout;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.ui.components.IconButton;
|
||||
import net.runelite.client.ui.components.shadowlabel.JShadowedLabel;
|
||||
import net.runelite.client.util.ImageUtil;
|
||||
|
||||
public class ClockTabPanel extends TabContentPanel
|
||||
{
|
||||
static final ImageIcon DELETE_ICON;
|
||||
static final ImageIcon DELETE_ICON_HOVER;
|
||||
static final ImageIcon LAP_ICON;
|
||||
static final ImageIcon LAP_ICON_HOVER;
|
||||
static final ImageIcon PAUSE_ICON;
|
||||
static final ImageIcon PAUSE_ICON_HOVER;
|
||||
static final ImageIcon RESET_ICON;
|
||||
static final ImageIcon RESET_ICON_HOVER;
|
||||
static final ImageIcon START_ICON;
|
||||
static final ImageIcon START_ICON_HOVER;
|
||||
|
||||
private static final ImageIcon ADD_ICON;
|
||||
private static final ImageIcon ADD_ICON_HOVER;
|
||||
|
||||
private final ClockManager clockManager;
|
||||
|
||||
private final List<ClockPanel> clockPanels = new ArrayList<>();
|
||||
|
||||
static
|
||||
{
|
||||
BufferedImage deleteIcon = ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "delete_icon.png");
|
||||
BufferedImage lapIcon = ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "lap_icon.png");
|
||||
BufferedImage pauseIcon = ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "pause_icon.png");
|
||||
BufferedImage resetIcon = ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "reset_icon.png");
|
||||
BufferedImage startIcon = ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "start_icon.png");
|
||||
BufferedImage addIcon = ImageUtil.getResourceStreamFromClass(TimeTrackingPlugin.class, "add_icon.png");
|
||||
|
||||
DELETE_ICON = new ImageIcon(deleteIcon);
|
||||
DELETE_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(deleteIcon, -80));
|
||||
LAP_ICON = new ImageIcon(lapIcon);
|
||||
LAP_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(lapIcon, -80));
|
||||
PAUSE_ICON = new ImageIcon(pauseIcon);
|
||||
PAUSE_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(pauseIcon, -80));
|
||||
RESET_ICON = new ImageIcon(resetIcon);
|
||||
RESET_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(resetIcon, -80));
|
||||
START_ICON = new ImageIcon(startIcon);
|
||||
START_ICON_HOVER = new ImageIcon(ImageUtil.grayscaleOffset(startIcon, -80));
|
||||
ADD_ICON = new ImageIcon(addIcon);
|
||||
ADD_ICON_HOVER = new ImageIcon(ImageUtil.alphaOffset(addIcon, 0.53f));
|
||||
}
|
||||
|
||||
ClockTabPanel(ClockManager clockManager)
|
||||
{
|
||||
this.clockManager = clockManager;
|
||||
|
||||
setLayout(new DynamicGridLayout(0, 1, 0, 4));
|
||||
setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears and recreates the components of this panel.
|
||||
* This should be done whenever a clock is added or removed.
|
||||
*/
|
||||
void rebuild()
|
||||
{
|
||||
removeAll();
|
||||
clockPanels.clear();
|
||||
|
||||
add(createHeaderPanel("Timers", "timer", false, e -> clockManager.addTimer()));
|
||||
|
||||
for (Timer timer : clockManager.getTimers())
|
||||
{
|
||||
TimerPanel panel = new TimerPanel(clockManager, timer);
|
||||
|
||||
clockPanels.add(panel);
|
||||
add(panel);
|
||||
}
|
||||
|
||||
if (clockManager.getTimers().isEmpty())
|
||||
{
|
||||
add(createInfoPanel("Click the + button to add a timer."));
|
||||
}
|
||||
|
||||
add(createHeaderPanel("Stopwatches", "stopwatch", true, e -> clockManager.addStopwatch()));
|
||||
|
||||
for (Stopwatch stopwatch : clockManager.getStopwatches())
|
||||
{
|
||||
StopwatchPanel panel = new StopwatchPanel(clockManager, stopwatch);
|
||||
|
||||
clockPanels.add(panel);
|
||||
add(panel);
|
||||
}
|
||||
|
||||
if (clockManager.getStopwatches().isEmpty())
|
||||
{
|
||||
add(createInfoPanel("Click the + button to add a stopwatch."));
|
||||
}
|
||||
|
||||
revalidate();
|
||||
}
|
||||
|
||||
private JPanel createHeaderPanel(String title, String type, boolean largePadding, ActionListener actionListener)
|
||||
{
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(new EmptyBorder(largePadding ? 11 : 0, 0, 0, 0));
|
||||
panel.setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
JLabel headerLabel = new JLabel(title);
|
||||
headerLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||
panel.add(headerLabel, BorderLayout.CENTER);
|
||||
|
||||
IconButton addButton = new IconButton(ADD_ICON, ADD_ICON_HOVER);
|
||||
addButton.setPreferredSize(new Dimension(14, 14));
|
||||
addButton.setToolTipText("Add a " + type);
|
||||
addButton.addActionListener(actionListener);
|
||||
panel.add(addButton, BorderLayout.EAST);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
private JPanel createInfoPanel(String text)
|
||||
{
|
||||
JPanel panel = new JPanel(new BorderLayout());
|
||||
panel.setBorder(new EmptyBorder(7, 8, 6, 8));
|
||||
panel.setBackground(ColorScheme.DARK_GRAY_COLOR);
|
||||
|
||||
JLabel infoLabel = new JShadowedLabel(text);
|
||||
infoLabel.setForeground(ColorScheme.LIGHT_GRAY_COLOR.darker());
|
||||
infoLabel.setFont(FontManager.getRunescapeSmallFont());
|
||||
panel.add(infoLabel);
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUpdateInterval()
|
||||
{
|
||||
return 1; // 200 milliseconds
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update()
|
||||
{
|
||||
for (ClockPanel panel : clockPanels)
|
||||
{
|
||||
if (panel.getClock().isActive())
|
||||
{
|
||||
panel.updateDisplayInput();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.timetracking.clocks;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
class Stopwatch extends Clock
|
||||
{
|
||||
// the number of seconds elapsed, as of last updated time
|
||||
private long elapsed = 0;
|
||||
|
||||
// a list of lap times (recorded as seconds since epoch)
|
||||
private List<Long> laps = new ArrayList<>();
|
||||
|
||||
Stopwatch(String name)
|
||||
{
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
long getDisplayTime()
|
||||
{
|
||||
if (!active)
|
||||
{
|
||||
return elapsed;
|
||||
}
|
||||
|
||||
return Math.max(0, elapsed + (Instant.now().getEpochSecond() - lastUpdate));
|
||||
}
|
||||
|
||||
@Override
|
||||
void setDuration(long duration)
|
||||
{
|
||||
elapsed = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean start()
|
||||
{
|
||||
if (!active)
|
||||
{
|
||||
lastUpdate = Instant.now().getEpochSecond();
|
||||
active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean pause()
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
active = false;
|
||||
elapsed = Math.max(0, elapsed + (Instant.now().getEpochSecond() - lastUpdate));
|
||||
lastUpdate = Instant.now().getEpochSecond();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void lap()
|
||||
{
|
||||
laps.add(getDisplayTime());
|
||||
}
|
||||
|
||||
@Override
|
||||
void reset()
|
||||
{
|
||||
active = false;
|
||||
elapsed = 0;
|
||||
laps.clear();
|
||||
lastUpdate = Instant.now().getEpochSecond();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.timetracking.clocks;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.GridBagConstraints;
|
||||
import java.awt.GridBagLayout;
|
||||
import java.awt.Insets;
|
||||
import java.util.List;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import net.runelite.client.ui.ColorScheme;
|
||||
import net.runelite.client.ui.FontManager;
|
||||
import net.runelite.client.ui.components.IconButton;
|
||||
|
||||
class StopwatchPanel extends ClockPanel
|
||||
{
|
||||
private static final Color LAP_DATA_COLOR = ColorScheme.LIGHT_GRAY_COLOR.darker();
|
||||
|
||||
private final JPanel lapsContainer;
|
||||
private final Stopwatch stopwatch;
|
||||
|
||||
StopwatchPanel(ClockManager clockManager, Stopwatch stopwatch)
|
||||
{
|
||||
super(clockManager, stopwatch, "stopwatch", false);
|
||||
|
||||
this.stopwatch = stopwatch;
|
||||
|
||||
lapsContainer = new JPanel(new GridBagLayout());
|
||||
lapsContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR);
|
||||
rebuildLapList();
|
||||
|
||||
contentContainer.add(lapsContainer);
|
||||
|
||||
IconButton lapButton = new IconButton(ClockTabPanel.LAP_ICON, ClockTabPanel.LAP_ICON_HOVER);
|
||||
lapButton.setPreferredSize(new Dimension(16, 14));
|
||||
lapButton.setToolTipText("Add lap time");
|
||||
|
||||
lapButton.addActionListener(e ->
|
||||
{
|
||||
stopwatch.lap();
|
||||
rebuildLapList();
|
||||
clockManager.saveStopwatches();
|
||||
});
|
||||
|
||||
leftActions.add(lapButton);
|
||||
|
||||
IconButton deleteButton = new IconButton(ClockTabPanel.DELETE_ICON, ClockTabPanel.DELETE_ICON_HOVER);
|
||||
deleteButton.setPreferredSize(new Dimension(16, 14));
|
||||
deleteButton.setToolTipText("Delete stopwatch");
|
||||
deleteButton.addActionListener(e -> clockManager.removeStopwatch(stopwatch));
|
||||
rightActions.add(deleteButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
void reset()
|
||||
{
|
||||
super.reset();
|
||||
rebuildLapList();
|
||||
}
|
||||
|
||||
private void rebuildLapList()
|
||||
{
|
||||
lapsContainer.removeAll();
|
||||
|
||||
List<Long> laps = stopwatch.getLaps();
|
||||
|
||||
if (laps.isEmpty())
|
||||
{
|
||||
lapsContainer.setBorder(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
lapsContainer.setBorder(new EmptyBorder(5, 0, 0, 0));
|
||||
|
||||
GridBagConstraints c = new GridBagConstraints();
|
||||
c.insets = new Insets(4, 5, 3, 5);
|
||||
c.fill = GridBagConstraints.HORIZONTAL;
|
||||
c.weightx = 1;
|
||||
c.gridx = 0;
|
||||
c.gridy = 0;
|
||||
|
||||
long previousLap = 0;
|
||||
for (long lap : stopwatch.getLaps())
|
||||
{
|
||||
c.gridx = 0;
|
||||
lapsContainer.add(createSmallLabel("" + (c.gridy + 1)), c);
|
||||
|
||||
c.gridx = 1;
|
||||
lapsContainer.add(createSmallLabel(getFormattedDuration(lap - previousLap)), c);
|
||||
|
||||
c.gridx = 2;
|
||||
lapsContainer.add(createSmallLabel(getFormattedDuration(lap)), c);
|
||||
|
||||
previousLap = lap;
|
||||
c.gridy++;
|
||||
}
|
||||
}
|
||||
|
||||
lapsContainer.revalidate();
|
||||
lapsContainer.repaint();
|
||||
}
|
||||
|
||||
private JLabel createSmallLabel(String text)
|
||||
{
|
||||
JLabel label = new JLabel(text, SwingConstants.CENTER);
|
||||
label.setFont(FontManager.getRunescapeSmallFont());
|
||||
label.setForeground(LAP_DATA_COLOR);
|
||||
|
||||
return label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.timetracking.clocks;
|
||||
|
||||
import java.time.Instant;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@AllArgsConstructor
|
||||
class Timer extends Clock
|
||||
{
|
||||
// the total number of seconds that the timer should run for
|
||||
private long duration;
|
||||
|
||||
// the number of seconds remaining on the timer, as of last updated time
|
||||
private long remaining;
|
||||
|
||||
Timer(String name, long duration)
|
||||
{
|
||||
super(name);
|
||||
this.duration = duration;
|
||||
this.remaining = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
long getDisplayTime()
|
||||
{
|
||||
if (!active)
|
||||
{
|
||||
return remaining;
|
||||
}
|
||||
|
||||
return Math.max(0, remaining - (Instant.now().getEpochSecond() - lastUpdate));
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean start()
|
||||
{
|
||||
if (!active && duration > 0)
|
||||
{
|
||||
if (remaining <= 0)
|
||||
{
|
||||
remaining = duration;
|
||||
}
|
||||
lastUpdate = Instant.now().getEpochSecond();
|
||||
active = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean pause()
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
active = false;
|
||||
remaining = Math.max(0, remaining - (Instant.now().getEpochSecond() - lastUpdate));
|
||||
lastUpdate = Instant.now().getEpochSecond();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
void reset()
|
||||
{
|
||||
active = false;
|
||||
remaining = duration;
|
||||
lastUpdate = Instant.now().getEpochSecond();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Daniel Teo <https://github.com/takuyakanbr>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package net.runelite.client.plugins.timetracking.clocks;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import net.runelite.client.ui.components.IconButton;
|
||||
|
||||
class TimerPanel extends ClockPanel
|
||||
{
|
||||
TimerPanel(ClockManager clockManager, Timer timer)
|
||||
{
|
||||
super(clockManager, timer, "timer", true);
|
||||
|
||||
IconButton deleteButton = new IconButton(ClockTabPanel.DELETE_ICON, ClockTabPanel.DELETE_ICON_HOVER);
|
||||
deleteButton.setPreferredSize(new Dimension(16, 14));
|
||||
deleteButton.setToolTipText("Delete timer");
|
||||
deleteButton.addActionListener(e -> clockManager.removeTimer(timer));
|
||||
rightActions.add(deleteButton);
|
||||
}
|
||||
}
|
||||
@@ -120,6 +120,12 @@ public class FarmingTabPanel extends TabContentPanel
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUpdateInterval()
|
||||
{
|
||||
return 50; // 10 seconds
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update()
|
||||
{
|
||||
@@ -128,7 +134,7 @@ public class FarmingTabPanel extends TabContentPanel
|
||||
|
||||
boolean autoweed;
|
||||
{
|
||||
String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername();
|
||||
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername();
|
||||
autoweed = Integer.toString(Autoweed.ON.ordinal())
|
||||
.equals(configManager.getConfiguration(group, TimeTrackingConfig.AUTOWEED));
|
||||
}
|
||||
@@ -136,7 +142,7 @@ public class FarmingTabPanel extends TabContentPanel
|
||||
for (TimeablePanel<FarmingPatch> panel : patchPanels)
|
||||
{
|
||||
FarmingPatch patch = panel.getTimeable();
|
||||
String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername() + "." + patch.getRegion().getRegionID();
|
||||
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + patch.getRegion().getRegionID();
|
||||
String key = Integer.toString(patch.getVarbit().getId());
|
||||
String storedValue = configManager.getConfiguration(group, key);
|
||||
long unixTime = 0;
|
||||
|
||||
@@ -72,7 +72,7 @@ public class FarmingTracker
|
||||
boolean changed = false;
|
||||
|
||||
{
|
||||
String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername();
|
||||
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername();
|
||||
String autoweed = Integer.toString(client.getVar(Varbits.AUTOWEED));
|
||||
if (!autoweed.equals(configManager.getConfiguration(group, TimeTrackingConfig.AUTOWEED)))
|
||||
{
|
||||
@@ -86,7 +86,7 @@ public class FarmingTracker
|
||||
{
|
||||
// Write config with new varbits
|
||||
// timetracking.<login-username>.<regionID>.<VarbitID>=<varbitValue>:<unix time>
|
||||
String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername() + "." + region.getRegionID();
|
||||
String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + region.getRegionID();
|
||||
long unixNow = Instant.now().getEpochSecond();
|
||||
for (Varbits varbit : region.getVarbits())
|
||||
{
|
||||
@@ -137,7 +137,7 @@ public class FarmingTracker
|
||||
// migrate autoweed config
|
||||
{
|
||||
String oldGroup = OLD_KEY_NAME + "." + username;
|
||||
String newGroup = TimeTrackingConfig.KEY_NAME + "." + username;
|
||||
String newGroup = TimeTrackingConfig.CONFIG_GROUP + "." + username;
|
||||
String storedValue = configManager.getConfiguration(oldGroup, TimeTrackingConfig.AUTOWEED);
|
||||
|
||||
if (storedValue != null)
|
||||
@@ -151,7 +151,7 @@ public class FarmingTracker
|
||||
for (FarmingRegion region : farmingWorld.getRegions().values())
|
||||
{
|
||||
String oldGroup = OLD_KEY_NAME + "." + username + "." + region.getRegionID();
|
||||
String newGroup = TimeTrackingConfig.KEY_NAME + "." + username + "." + region.getRegionID();
|
||||
String newGroup = TimeTrackingConfig.CONFIG_GROUP + "." + username + "." + region.getRegionID();
|
||||
|
||||
for (Varbits varbit : region.getVarbits())
|
||||
{
|
||||
|
||||
@@ -78,6 +78,12 @@ public class BirdHouseTabPanel extends TabContentPanel
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getUpdateInterval()
|
||||
{
|
||||
return 50; // 10 seconds
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update()
|
||||
{
|
||||
|
||||
@@ -89,7 +89,7 @@ public class BirdHouseTracker
|
||||
{
|
||||
birdHouseData.clear();
|
||||
|
||||
final String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername() + "." + TimeTrackingConfig.BIRD_HOUSE;
|
||||
final String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + TimeTrackingConfig.BIRD_HOUSE;
|
||||
|
||||
for (BirdHouseSpace space : BirdHouseSpace.values())
|
||||
{
|
||||
@@ -216,7 +216,7 @@ public class BirdHouseTracker
|
||||
|
||||
private void saveToConfig(Map<BirdHouseSpace, BirdHouseData> updatedData)
|
||||
{
|
||||
final String group = TimeTrackingConfig.KEY_NAME + "." + client.getUsername() + "." + TimeTrackingConfig.BIRD_HOUSE;
|
||||
final String group = TimeTrackingConfig.CONFIG_GROUP + "." + client.getUsername() + "." + TimeTrackingConfig.BIRD_HOUSE;
|
||||
|
||||
for (BirdHouseData data : updatedData.values())
|
||||
{
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 500 B |
Binary file not shown.
|
After Width: | Height: | Size: 647 B |
Binary file not shown.
|
After Width: | Height: | Size: 905 B |
Binary file not shown.
|
After Width: | Height: | Size: 563 B |
Binary file not shown.
|
After Width: | Height: | Size: 801 B |
Binary file not shown.
|
After Width: | Height: | Size: 654 B |
Reference in New Issue
Block a user