Add files via upload
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
<Bindings>
|
||||
|
||||
<Binding name="UNITXPSP3NEARESTENEMY" header="UNITXPSP3TARGETING">
|
||||
UnitXP("target", "nearestEnemy");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3TARGETMOSTHP" header="UNITXPSP3TARGETING">
|
||||
UnitXP("target", "mostHP");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3WORLDBOSS">
|
||||
UnitXP("target", "worldBoss");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3NEXTMARKER">
|
||||
UnitXP("target", "nextMarkedEnemyInCycle");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3PREVIOUSMARKER">
|
||||
UnitXP("target", "previousMarkedEnemyInCycle");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3NEXTENEMY">
|
||||
UnitXP("target", "nextEnemyInCycle");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3PREVIOUSENEMY">
|
||||
UnitXP("target", "previousEnemyInCycle");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3NEXTMELEE">
|
||||
UnitXP("target", "nextEnemyConsideringDistance");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3PREVIOUSMELEE">
|
||||
UnitXP("target", "previousEnemyConsideringDistance");
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3RAISECAMERA" header="UNITXPSP3UTILITIES">
|
||||
UnitXP_SP3_raiseCameraHeight();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3LOWERCAMERA">
|
||||
UnitXP_SP3_lowerCameraHeight();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3LEFTCAMERA">
|
||||
UnitXP_SP3_rightPlayer();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3RIGHTCAMERA">
|
||||
UnitXP_SP3_leftPlayer();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3RESETCAMERA">
|
||||
UnitXP_SP3_resetCamera();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3TOGGLEMODERNNAMEPLATEDISTANCE">
|
||||
UnitXP_SP3_toggleModernNameplateDistance();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3TOGGLEPRIORITIZETARGETNAMEPLATE">
|
||||
UnitXP_SP3_togglePrioritizeTargetNameplate();
|
||||
</Binding>
|
||||
|
||||
<Binding name="UNITXPSP3TOGGLEPRIORITIZEMARKEDNAMEPLATE">
|
||||
UnitXP_SP3_togglePrioritizeMarkedNameplate();
|
||||
</Binding>
|
||||
|
||||
</Bindings>
|
||||
@@ -0,0 +1,2 @@
|
||||
# UnitXP_SP3_Addon
|
||||
Vanilla 1.12 Lua Addon for supporting [UnitXP SP3 mod](https://codeberg.org/konaka/UnitXP_SP3)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,14 @@
|
||||
## Interface: 11200
|
||||
## Title: UnitXP SP3 Lua Addon
|
||||
## Notes: Menu to control UnitXP SP3 mod
|
||||
## Author: allfox and thankful community
|
||||
## DefaultState: enabled
|
||||
## OptionalDeps: none
|
||||
## SavedVariables: UnitXP_SP3_Addon, UnitXP_SP3_Icon
|
||||
libs\LibStub.lua
|
||||
libs\AceCore-3.0.lua
|
||||
libs\AceHook-3.0.lua
|
||||
libs\CallbackHandler-1.0.lua
|
||||
libs\LibDataBroker-1.1.lua
|
||||
libs\LibDBIcon-1.0.lua
|
||||
UnitXP_SP3_Addon.xml
|
||||
@@ -0,0 +1,856 @@
|
||||
<Ui
|
||||
xmlns="http://www.blizzard.com/wow/ui/"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<!-- $Id$ -->
|
||||
<!-- Script -->
|
||||
<Script file="UnitXP_SP3_Addon.lua"/>
|
||||
<!-- Frames -->
|
||||
<Frame name="xpsp3Frame" toplevel="true" movable="true" enableMouse="true" enableKeyboard="true" hidden="true">
|
||||
<TitleRegion setAllPoints="true"/>
|
||||
<Size>
|
||||
<AbsDimension x="550" y="430"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="CENTER" relativeTo="UIParent" />
|
||||
</Anchors>
|
||||
<Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background" edgeFile="Interface\DialogFrame\UI-DialogBox-Border" tile="true">
|
||||
<BackgroundInsets>
|
||||
<AbsInset left="11" right="12" top="12" bottom="11"/>
|
||||
</BackgroundInsets>
|
||||
<TileSize>
|
||||
<AbsValue val="32"/>
|
||||
</TileSize>
|
||||
<EdgeSize>
|
||||
<AbsValue val="32"/>
|
||||
</EdgeSize>
|
||||
</Backdrop>
|
||||
<Layers>
|
||||
<Layer level="ARTWORK">
|
||||
<Texture name="xpsp3_title_texture" file="Interface\DialogFrame\UI-DialogBox-Header">
|
||||
<Size>
|
||||
<AbsDimension x="320" y="64"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOP">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="12"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</Texture>
|
||||
<FontString inherits="GameFontNormal" text="UnitXP Service Pack 3">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="xpsp3_title_texture">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="-14"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString inherits="GameFontNormal" text="FPS Cap:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-40"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString inherits="GameFontNormal" text="Background:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="399" y="-40"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="xpsp3_fontString_combatTextSP3_fontSize" inherits="GameFontNormal" text="Size:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="295" y="-300"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="xpsp3_fontString_combatTextSP3_nameplateHeight" inherits="GameFontNormal" text="Elevation:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="413" y="-300"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString name="xpsp3_fontString_combatTextSP3_fontName" inherits="GameFontNormal" text="Font:" justifyH="LEFT" justifyV="TOP">
|
||||
<Size x="200" y="440" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="295" y="-335"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
<FontString inherits="GameFontNormal" text="Targeting functions could be accessed via Key Bindings Menu" justifyH="CENTER" justifyV="TOP">
|
||||
<Size x="550" y="430" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="xpsp3Frame">
|
||||
<Offset>
|
||||
<AbsDimension x="0" y="-370"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<Button name="xpsp3_buttonCancel_close" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Close">
|
||||
<Size>
|
||||
<AbsDimension x="100" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMRIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="-15" y="15"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_buttonCancel_resetCamera" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Reset Camera">
|
||||
<Size>
|
||||
<AbsDimension x="100" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="BOTTOMRIGHT">
|
||||
<Offset>
|
||||
<AbsDimension x="-115" y="15"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<CheckButton name="xpsp3_checkButton_minimapButton" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-30"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Show Minimap Button");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Toggle visibility of the minimap button");
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_modernNameplate" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-70"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Proper Nameplates Occlusion");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Nameplates would also check camera's line of sight in addition to distance");
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_prioritizeTargetNameplate" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-100"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Prioritize Target Nameplate");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Other nameplates would disappear when a target is selected");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_prioritizeMarkedNameplate" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-130"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Prioritize Marked Nameplate");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Other nameplates would disappear when some nameplates are raid-marked");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_nameplateCombatFilter" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-160"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Hide Healthy Nameplates");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Aside from prioritized nameplates, only those in-combat/damaged/PvP-flagged nameplates would be shown");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_hideCritterNameplate" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-190"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Hide Critter Nameplates");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Aside from prioritized nameplates, only those in-combat critters would have nameplates");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_showInCombatNameplatesNearPlayer" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-220"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Show In-combat Nameplates Nearby");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("In-combat nameplates in small range would be shown regardless of occlusion");
|
||||
xpsp3tooltip:AddLine("Need Proper Nameplates Occlusion to be also enabled",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_notify_flashTaskbarIcon" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-260"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Taskbar Notification");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Flash operating system's taskbar icon when the game requires attention");
|
||||
xpsp3tooltip:AddLine("When the game is in background/minimized",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_notify_playSystemDefaultSound" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-290"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("System Sound Notification");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Play operating system's default sound when the game requires attention");
|
||||
xpsp3tooltip:AddLine("When the game is in background/minimized",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_perfectScreenshot" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="24" y="-320"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Perfect Screenshot");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Generate PNG screenshot");
|
||||
xpsp3tooltip:AddLine("In-game screenshots would be in larger PNG files instead of JPEG files",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_cameraPinHeight" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-160"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Pin Camera Height");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Camera would keep its height during shapeshifting");
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_weatherAlwaysClear" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-190"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Disable Rain");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("This would fix FPS-drop at the cost of losing raining particles");
|
||||
xpsp3tooltip:AddLine("Toggling the switch would influence the next weather event but not the current one",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_combatTextSP3" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-260"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Anti-aliased Combat Text");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Alternative style for floating combat text");
|
||||
xpsp3tooltip:AddLine("It requires d3dx9_43.dll which is from DirectX End-User Runtimes",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<CheckButton name="xpsp3_checkButton_hideEXPtext" inherits="UICheckButtonTemplate">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-220"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
getglobal(this:GetName().."Text"):SetText("Hide EXP Text");
|
||||
</OnLoad>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
<OnEnter>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_RIGHT");
|
||||
xpsp3tooltip:SetText("Don't show that purple message when gaining experience point");
|
||||
xpsp3tooltip:AddLine("Some people don't like it...",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEnter>
|
||||
<OnLeave>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnLeave>
|
||||
</Scripts>
|
||||
</CheckButton>
|
||||
<EditBox name="xpsp3_editBox_combatTextSP3_fontSize" letters="2" autoFocus="false" numeric="true">
|
||||
<Size x="50" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="343" y="-290"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-17" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.12" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="-7" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.88" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Adjust combat text font size");
|
||||
xpsp3tooltip:AddLine("Range from 10 to 99. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<EditBox name="xpsp3_editBox_combatTextSP3_nameplateHeight" letters="3" autoFocus="false" numeric="true">
|
||||
<Size x="50" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="490" y="-290"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-17" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.12" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="-7" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.88" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Raise or lower combat text on enemies");
|
||||
xpsp3tooltip:AddLine("Range from 0 to 256. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<EditBox name="xpsp3_editBox_combatTextSP3_fontName" autoFocus="false" numeric="false">
|
||||
<Size x="150" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="336" y="-325"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="100" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-10" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.4" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="100" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="10" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.6" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Change the font of combat text");
|
||||
xpsp3tooltip:AddLine("Input a font name in operating system. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<EditBox name="xpsp3_editBox_FPScap" letters="3" autoFocus="false" numeric="true">
|
||||
<Size x="50" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="343" y="-30"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-17" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.12" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="-7" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.88" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Limit frames-per-second to the specific value");
|
||||
xpsp3tooltip:AddLine("Zero means no limit. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<EditBox name="xpsp3_editBox_backgroundFPScap" letters="3" autoFocus="false" numeric="true">
|
||||
<Size x="50" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="490" y="-30"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Left">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="LEFT">
|
||||
<Offset x="-17" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0" right="0.12" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
<Texture file="Interface\ChatFrame\UI-ChatInputBorder-Right">
|
||||
<Size x="30" y="32" />
|
||||
<Anchors>
|
||||
<Anchor point="RIGHT">
|
||||
<Offset x="-7" y="0" />
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<TexCoords left="0.88" right="1.0" top="0" bottom="1.0"/>
|
||||
</Texture>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Scripts>
|
||||
<OnEditFocusGained>
|
||||
xpsp3tooltip:SetOwner(this, "ANCHOR_LEFT");
|
||||
xpsp3tooltip:SetText("Limit background-frames-per-second to the specific value");
|
||||
xpsp3tooltip:AddLine("Zero means no limit. Press Enter to confirm",1,1,1);
|
||||
xpsp3tooltip:Show();
|
||||
</OnEditFocusGained>
|
||||
<OnEditFocusLost>
|
||||
xpsp3tooltip:Hide();
|
||||
</OnEditFocusLost>
|
||||
<OnEnterPressed>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnEnterPressed>
|
||||
</Scripts>
|
||||
<FontString inherits="ChatFontNormal" />
|
||||
</EditBox>
|
||||
<Button name="xpsp3_button_cameraHeight_raise" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Raise Camera">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-75"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraHeight_lower" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Lower Camera">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="390" y="-75"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraPitch_up" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="CamPitch Up">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-105"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraPitch_down" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="CamPitch Down">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="390" y="-105"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraHorizontalDisplacement_leftPlayer" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Left Player">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="274" y="-135"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
<Button name="xpsp3_button_cameraHorizontalDisplacement_rightPlayer" parent="xpsp3Frame" inherits="GameMenuButtonTemplate" text="Right Player">
|
||||
<Size>
|
||||
<AbsDimension x="110" y="24"/>
|
||||
</Size>
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT">
|
||||
<Offset>
|
||||
<AbsDimension x="390" y="-135"/>
|
||||
</Offset>
|
||||
</Anchor>
|
||||
</Anchors>
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
UnitXP_SP3_UI_OnClick(this);
|
||||
</OnClick>
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>
|
||||
UnitXP_SP3_OnLoad();
|
||||
</OnLoad>
|
||||
<OnEvent>
|
||||
UnitXP_SP3_OnEvent(event);
|
||||
</OnEvent>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
<GameTooltip name="xpsp3tooltip" frameStrata="TOOLTIP" hidden="true" parent="xpsp3Frame" inherits="GameTooltipTemplate" />
|
||||
</Ui>
|
||||
@@ -0,0 +1,209 @@
|
||||
local ACECORE_MAJOR, ACECORE_MINOR = "AceCore-3.0", 2
|
||||
local AceCore, oldminor = LibStub:NewLibrary(ACECORE_MAJOR, ACECORE_MINOR)
|
||||
|
||||
if not AceCore then return end -- No upgrade needed
|
||||
|
||||
AceCore._G = AceCore._G or getfenv()
|
||||
local _G = AceCore._G
|
||||
local strsub, strgsub, strfind = string.sub, string.gsub, string.find
|
||||
local tremove, tconcat = table.remove, table.concat
|
||||
local tgetn, tsetn = table.getn, table.setn
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = "k"})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if not t then
|
||||
return {}
|
||||
end
|
||||
list[t] = nil
|
||||
return t
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
tsetn(t,0)
|
||||
list[t] = true
|
||||
end
|
||||
|
||||
print = print or function(text)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(text)
|
||||
end
|
||||
|
||||
-- debug
|
||||
function AceCore.listcount()
|
||||
local count = 0
|
||||
for k in list do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
end -- AceCore.new, AceCore.del
|
||||
AceCore.new, AceCore.del = new, del
|
||||
|
||||
local function errorhandler(err)
|
||||
return geterrorhandler()(err)
|
||||
end
|
||||
AceCore.errorhandler = errorhandler
|
||||
|
||||
local function CreateSafeDispatcher(argCount)
|
||||
local code = [[
|
||||
local errorhandler = LibStub("AceCore-3.0").errorhandler
|
||||
local method, UP_ARGS
|
||||
local function call()
|
||||
local func, ARGS = method, UP_ARGS
|
||||
method, UP_ARGS = nil, NILS
|
||||
return func(ARGS)
|
||||
end
|
||||
return function(func, ARGS)
|
||||
method, UP_ARGS = func, ARGS
|
||||
return xpcall(call, errorhandler)
|
||||
end
|
||||
]]
|
||||
local c = 4*argCount-1
|
||||
local s = "b01,b02,b03,b04,b05,b06,b07,b08,b09,b10,b11,b12,b13,b14,b15,b16,b17,b18,b19,b20"
|
||||
code = strgsub(code, "UP_ARGS", string.sub(s,1,c))
|
||||
s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20"
|
||||
code = strgsub(code, "ARGS", string.sub(s,1,c))
|
||||
s = "nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil"
|
||||
code = strgsub(code, "NILS", string.sub(s,1,c))
|
||||
return assert(loadstring(code, "safecall SafeDispatcher["..tostring(argCount).."]"))()
|
||||
end
|
||||
|
||||
local SafeDispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||
local dispatcher
|
||||
if not tonumber(argCount) then dbg(debugstack()) end
|
||||
if argCount > 0 then
|
||||
dispatcher = CreateSafeDispatcher(argCount)
|
||||
else
|
||||
dispatcher = function(func) return xpcall(func,errorhandler) end
|
||||
end
|
||||
rawset(self, argCount, dispatcher)
|
||||
return dispatcher
|
||||
end})
|
||||
|
||||
local function safecall(func,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
-- we check to see if the func is passed is actually a function here and don't error when it isn't
|
||||
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
|
||||
-- present execution should continue without hinderance
|
||||
if type(func) == "function" then
|
||||
return SafeDispatchers[argc](func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
AceCore.safecall = safecall
|
||||
|
||||
local function CreateDispatcher(argCount)
|
||||
local code = [[
|
||||
return function(func,ARGS)
|
||||
return func(ARGS)
|
||||
end
|
||||
]]
|
||||
local s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20"
|
||||
code = strgsub(code, "ARGS", string.sub(s,1,4*argCount-1))
|
||||
return assert(loadstring(code, "call Dispatcher["..tostring(argCount).."]"))()
|
||||
end
|
||||
|
||||
AceCore.Dispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||
local dispatcher
|
||||
if argCount > 0 then
|
||||
dispatcher = CreateDispatcher(argCount)
|
||||
else
|
||||
dispatcher = function(func) return func() end
|
||||
end
|
||||
rawset(self, argCount, dispatcher)
|
||||
return dispatcher
|
||||
end})
|
||||
|
||||
-- some string functions
|
||||
-- vanilla available string operations:
|
||||
-- sub, gfind, rep, gsub, char, dump, find, upper, len, format, byte, lower
|
||||
-- we will just replace every string.match with string.find in the code
|
||||
function AceCore.strtrim(s)
|
||||
return strgsub(s, "^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
|
||||
local function strsplit(delim, s, n)
|
||||
if n and n < 2 then return s end
|
||||
beg = beg or 1
|
||||
local i,j = string.find(s,delim,beg)
|
||||
if not i then
|
||||
return s, nil
|
||||
end
|
||||
return string.sub(s,1,j-1), strsplit(delim, string.sub(s,j+1), n and n-1 or nil)
|
||||
end
|
||||
AceCore.strsplit = strsplit
|
||||
|
||||
-- Ace3v: fonctions copied from AceHook-2.1
|
||||
local protFuncs = {
|
||||
CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true,
|
||||
TurnOrActionStart = true, TurnOrActionStop = true,
|
||||
PitchUpStart = true, PitchUpStop = true,
|
||||
PitchDownStart = true, PitchDownStop = true,
|
||||
MoveBackwardStart = true, MoveBackwardStop = true,
|
||||
MoveForwardStart = true, MoveForwardStop = true,
|
||||
Jump = true, StrafeLeftStart = true,
|
||||
StrafeLeftStop = true, StrafeRightStart = true,
|
||||
StrafeRightStop = true, ToggleMouseMove = true,
|
||||
ToggleRun = true, TurnLeftStart = true,
|
||||
TurnLeftStop = true, TurnRightStart = true,
|
||||
TurnRightStop = true,
|
||||
}
|
||||
|
||||
local function issecurevariable(x)
|
||||
return protFuncs[x] and 1 or nil
|
||||
end
|
||||
AceCore.issecurevariable = issecurevariable
|
||||
|
||||
local function hooksecurefunc(arg1, arg2, arg3)
|
||||
if type(arg1) == "string" then
|
||||
arg1, arg2, arg3 = _G, arg1, arg2
|
||||
end
|
||||
local orig = arg1[arg2]
|
||||
if type(orig) ~= "function" then
|
||||
error("The function "..arg2.." does not exist", 2)
|
||||
end
|
||||
arg1[arg2] = function(...)
|
||||
local tmp = {orig(unpack(arg))}
|
||||
arg3(unpack(arg))
|
||||
return unpack(tmp)
|
||||
end
|
||||
end
|
||||
AceCore.hooksecurefunc = hooksecurefunc
|
||||
|
||||
-- pickfirstset() - picks the first non-nil value and returns it
|
||||
local function pickfirstset(argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if (argc <= 1) or (a1 ~= nil) then
|
||||
return a1
|
||||
else
|
||||
return pickfirstset(argc-1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
end
|
||||
AceCore.pickfirstset = pickfirstset
|
||||
|
||||
local function countargs(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if (a1 == nil) then return 0 end
|
||||
return 1 + countargs(a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
AceCore.countargs = countargs
|
||||
|
||||
-- wipe preserves metatable
|
||||
function AceCore.wipe(t)
|
||||
for k,v in pairs(t) do t[k] = nil end
|
||||
tsetn(t,0)
|
||||
return t
|
||||
end
|
||||
|
||||
function AceCore.truncate(t,e)
|
||||
e = e or tgetn(t)
|
||||
for i=1,e do
|
||||
if t[i] == nil then
|
||||
tsetn(t,i-1)
|
||||
return
|
||||
end
|
||||
end
|
||||
tsetn(t,e)
|
||||
end
|
||||
@@ -0,0 +1,540 @@
|
||||
--- **AceHook-3.0** offers safe Hooking/Unhooking of functions, methods and frame scripts.
|
||||
-- Using AceHook-3.0 is recommended when you need to unhook your hooks again, so the hook chain isn't broken
|
||||
-- when you manually restore the original function.
|
||||
--
|
||||
-- **AceHook-3.0** can be embeded into your addon, either explicitly by calling AceHook:Embed(MyAddon) or by
|
||||
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
|
||||
-- and can be accessed directly, without having to explicitly call AceHook itself.\\
|
||||
-- It is recommended to embed AceHook, otherwise you'll have to specify a custom `self` on all calls you
|
||||
-- make into AceHook.
|
||||
-- @class file
|
||||
-- @name AceHook-3.0
|
||||
-- @release $Id: AceHook-3.0.lua 1118 2014-10-12 08:21:54Z nevcairiel $
|
||||
local ACEHOOK_MAJOR, ACEHOOK_MINOR = "AceHook-3.0", 8
|
||||
local AceHook, oldminor = LibStub:NewLibrary(ACEHOOK_MAJOR, ACEHOOK_MINOR)
|
||||
|
||||
if not AceHook then return end -- No upgrade needed
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local new, del = AceCore.new, AceCore.del
|
||||
|
||||
AceHook.embeded = AceHook.embeded or {}
|
||||
AceHook.registry = AceHook.registry or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end })
|
||||
AceHook.handlers = AceHook.handlers or {}
|
||||
AceHook.actives = AceHook.actives or {}
|
||||
AceHook.scripts = AceHook.scripts or {}
|
||||
AceHook.onceSecure = AceHook.onceSecure or {}
|
||||
AceHook.hooks = AceHook.hooks or {}
|
||||
|
||||
-- local upvalues
|
||||
local registry = AceHook.registry
|
||||
local handlers = AceHook.handlers
|
||||
local actives = AceHook.actives
|
||||
local scripts = AceHook.scripts
|
||||
local onceSecure = AceHook.onceSecure
|
||||
|
||||
-- Lua APIs
|
||||
local pairs, next, type = pairs, next, type
|
||||
local format = string.format
|
||||
local assert, error = assert, error
|
||||
|
||||
-- WoW APIs
|
||||
local hooksecurefunc, issecurevariable = AceCore.hooksecurefunc, AceCore.issecurevariable
|
||||
|
||||
local _G = AceCore._G
|
||||
|
||||
local protectedScripts = {
|
||||
OnClick = true,
|
||||
}
|
||||
|
||||
-- upgrading of embeded is done at the bottom of the file
|
||||
|
||||
local mixins = {
|
||||
"Hook", "SecureHook",
|
||||
"HookScript", "SecureHookScript",
|
||||
"Unhook", "UnhookAll",
|
||||
"IsHooked",
|
||||
"RawHook", "RawHookScript"
|
||||
}
|
||||
|
||||
-- AceHook:Embed( target )
|
||||
-- target (object) - target object to embed AceHook in
|
||||
--
|
||||
-- Embeds AceEevent into the target object making the functions from the mixins list available on target:..
|
||||
function AceHook:Embed( target )
|
||||
for k, v in pairs( mixins ) do
|
||||
target[v] = self[v]
|
||||
end
|
||||
self.embeded[target] = true
|
||||
-- inject the hooks table safely
|
||||
target.hooks = target.hooks or {}
|
||||
return target
|
||||
end
|
||||
|
||||
-- AceHook:OnEmbedDisable( target )
|
||||
-- target (object) - target object that is being disabled
|
||||
--
|
||||
-- Unhooks all hooks when the target disables.
|
||||
-- this method should be called by the target manually or by an addon framework
|
||||
function AceHook:OnEmbedDisable( target )
|
||||
target:UnhookAll()
|
||||
end
|
||||
|
||||
-- failsafe means even the hooking method fails, the original method will
|
||||
-- always be called
|
||||
local function createHook(self, handler, orig, secure, failsafe)
|
||||
local uid
|
||||
local method = type(handler) == "string"
|
||||
-- Ace3v: when this function called in the "hook" function, we have
|
||||
-- failsafe = not (raw or secure), so the first check condition
|
||||
-- is same as "if failsafe" or "if not raw and not secure"
|
||||
if failsafe and not secure then
|
||||
-- failsafe hook creation
|
||||
uid = function(...)
|
||||
if actives[uid] then
|
||||
if method then
|
||||
self[handler](self,unpack(arg))
|
||||
else
|
||||
handler(unpack(arg))
|
||||
end
|
||||
end
|
||||
return orig(unpack(arg))
|
||||
end
|
||||
-- /failsafe hook
|
||||
else
|
||||
-- all other hooks
|
||||
uid = function(...)
|
||||
if actives[uid] then
|
||||
if method then
|
||||
return self[handler](self,unpack(arg))
|
||||
else
|
||||
return handler(unpack(arg))
|
||||
end
|
||||
elseif not secure then -- backup on non secure
|
||||
return orig(unpack(arg))
|
||||
end
|
||||
end
|
||||
-- /hook
|
||||
end
|
||||
return uid
|
||||
end
|
||||
|
||||
local function donothing() end
|
||||
|
||||
-- @param self
|
||||
-- @param obj nil or a frame object, use with script = true
|
||||
-- @param method string, the name of a global function or the name of an object method
|
||||
-- @param handler function, or string when it's a method of 'self', if nil then will use the same value as method
|
||||
-- @param script boolean, must be true, if the hooked object is a frame
|
||||
-- @param secure boolean, if hooking a secure script, if true the original script will be called first, else later
|
||||
-- @param raw boolean, if raw hooking (replacing the original method)
|
||||
-- @param forceSecure boolean, if forcing to hook a secure method
|
||||
-- @param usage
|
||||
local function hook(self, obj, method, handler, script, secure, raw, forceSecure, usage)
|
||||
if not handler then handler = method end
|
||||
|
||||
-- These asserts make sure AceHooks's devs play by the rules.
|
||||
assert(not script or type(script) == "boolean")
|
||||
assert(not secure or type(secure) == "boolean")
|
||||
assert(not raw or type(raw) == "boolean")
|
||||
assert(not forceSecure or type(forceSecure) == "boolean")
|
||||
assert(usage)
|
||||
|
||||
-- Error checking Battery!
|
||||
if obj and type(obj) ~= "table" then
|
||||
error(format("%s: 'object' - nil or table expected got %s", usage, type(obj)), 3)
|
||||
end
|
||||
if type(method) ~= "string" then
|
||||
error(format("%s: 'method' - string expected got %s", usage, type(method)), 3)
|
||||
end
|
||||
if type(handler) ~= "string" and type(handler) ~= "function" then
|
||||
error(format("%s: 'handler' - nil, string, or function expected got %s", usage, type(handler)), 3)
|
||||
end
|
||||
if type(handler) == "string" and type(self[handler]) ~= "function" then
|
||||
error(format("%s: 'handler' - Handler specified does not exist at self[handler]", usage), 3)
|
||||
end
|
||||
if script then
|
||||
if not obj or not obj.GetScript or not obj:HasScript(method) then
|
||||
error(format("%s: You can only hook a script on a frame object", usage), 3)
|
||||
end
|
||||
if not secure and obj.IsProtected and obj:IsProtected() and protectedScripts[method] then
|
||||
error(format("Cannot hook secure script %q; Use SecureHookScript(obj, method, [handler]) instead.", method), 3)
|
||||
end
|
||||
else
|
||||
local issecure
|
||||
if obj then
|
||||
issecure = onceSecure[obj] and onceSecure[obj][method] or issecurevariable(obj, method)
|
||||
else
|
||||
issecure = onceSecure[method] or issecurevariable(method)
|
||||
end
|
||||
if issecure then
|
||||
if forceSecure then
|
||||
if obj then
|
||||
onceSecure[obj] = onceSecure[obj] or {}
|
||||
onceSecure[obj][method] = true
|
||||
else
|
||||
onceSecure[method] = true
|
||||
end
|
||||
elseif not secure then
|
||||
error(format("%s: Attempt to hook secure function %s. Use `SecureHook' or add `true' to the argument list to override.", usage, method), 3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local uid
|
||||
if obj then
|
||||
uid = registry[self][obj] and registry[self][obj][method]
|
||||
else
|
||||
uid = registry[self][method]
|
||||
end
|
||||
|
||||
if uid then
|
||||
if actives[uid] then
|
||||
-- Only two sane choices exist here. We either a) error 100% of the time or b) always unhook and then hook
|
||||
-- choice b would likely lead to odd debuging conditions or other mysteries so we're going with a.
|
||||
error(format("Attempting to rehook already active hook %s.", method))
|
||||
end
|
||||
|
||||
if handlers[uid] == handler then -- turn on a decative hook, note enclosures break this ability, small memory leak
|
||||
actives[uid] = true
|
||||
return
|
||||
elseif obj then -- is there any reason not to call unhook instead of doing the following several lines?
|
||||
if self.hooks and self.hooks[obj] then
|
||||
self.hooks[obj][method] = nil
|
||||
end
|
||||
registry[self][obj][method] = nil
|
||||
else
|
||||
if self.hooks then
|
||||
self.hooks[method] = nil
|
||||
end
|
||||
registry[self][method] = nil
|
||||
end
|
||||
handlers[uid], actives[uid], scripts[uid] = nil, nil, nil
|
||||
uid = nil
|
||||
end
|
||||
|
||||
local orig
|
||||
if script then
|
||||
-- Sometimes there is not a original function for a script.
|
||||
orig = obj:GetScript(method) or donothing
|
||||
elseif obj then
|
||||
orig = obj[method]
|
||||
else
|
||||
orig = _G[method]
|
||||
end
|
||||
|
||||
if not orig then
|
||||
error(format("%s: Attempting to hook a non existing target", usage), 3)
|
||||
end
|
||||
|
||||
uid = createHook(self, handler, orig, secure, not (raw or secure))
|
||||
|
||||
if obj then
|
||||
registry[self][obj] = registry[self][obj] or new()
|
||||
registry[self][obj][method] = uid
|
||||
|
||||
if not secure then
|
||||
self.hooks[obj] = self.hooks[obj] or new()
|
||||
self.hooks[obj][method] = orig
|
||||
end
|
||||
|
||||
if script then
|
||||
if not secure then
|
||||
obj:SetScript(method, uid)
|
||||
else
|
||||
obj:SetScript(method, function(...)
|
||||
local tmp = {orig(unpack(arg))}
|
||||
uid(unpack(arg))
|
||||
return unpack(tmp)
|
||||
end)
|
||||
--obj:HookScript(method, uid) -- Ace3v: vanilla frame has no HookScript
|
||||
end
|
||||
else
|
||||
if not secure then
|
||||
obj[method] = uid
|
||||
else
|
||||
hooksecurefunc(obj, method, uid)
|
||||
end
|
||||
end
|
||||
else
|
||||
registry[self][method] = uid
|
||||
|
||||
if not secure then
|
||||
_G[method] = uid
|
||||
self.hooks[method] = orig
|
||||
else
|
||||
hooksecurefunc(method, uid)
|
||||
end
|
||||
end
|
||||
|
||||
actives[uid], handlers[uid], scripts[uid] = true, handler, script and true or nil
|
||||
end
|
||||
|
||||
--- Hook a function or a method on an object.
|
||||
-- The hook created will be a "safe hook", that means that your handler will be called
|
||||
-- before the hooked function ("Pre-Hook"), and you don't have to call the original function yourself,
|
||||
-- however you cannot stop the execution of the function, or modify any of the arguments/return values.\\
|
||||
-- This type of hook is typically used if you need to know if some function got called, and don't want to modify it.
|
||||
-- @paramsig [object], method, [handler], [hookSecure]
|
||||
-- @param object The object to hook a method from
|
||||
-- @param method If object was specified, the name of the method, or the name of the function to hook.
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked function)
|
||||
-- @param hookSecure If true, AceHook will allow hooking of secure functions.
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook ActionButton_UpdateHotkeys, overwriting the secure status
|
||||
-- self:Hook("ActionButton_UpdateHotkeys", true)
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:ActionButton_UpdateHotkeys(button, type)
|
||||
-- print(button:GetName() .. " is updating its HotKey")
|
||||
-- end
|
||||
function AceHook:Hook(object, method, handler, hookSecure)
|
||||
if type(object) == "string" then
|
||||
method, handler, hookSecure, object = object, method, handler, nil
|
||||
end
|
||||
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
|
||||
hook(self, object, method, handler, false, false, false, hookSecure or false, "Usage: Hook([object], method, [handler], [hookSecure])")
|
||||
end
|
||||
|
||||
--- RawHook a function or a method on an object.
|
||||
-- The hook created will be a "raw hook", that means that your handler will completly replace
|
||||
-- the original function, and your handler has to call the original function (or not, depending on your intentions).\\
|
||||
-- The original function will be stored in `self.hooks[object][method]` or `self.hooks[functionName]` respectively.\\
|
||||
-- This type of hook can be used for all purposes, and is usually the most common case when you need to modify arguments
|
||||
-- or want to control execution of the original function.
|
||||
-- @paramsig [object], method, [handler], [hookSecure]
|
||||
-- @param object The object to hook a method from
|
||||
-- @param method If object was specified, the name of the method, or the name of the function to hook.
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked function)
|
||||
-- @param hookSecure If true, AceHook will allow hooking of secure functions.
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook ActionButton_UpdateHotkeys, overwriting the secure status
|
||||
-- self:RawHook("ActionButton_UpdateHotkeys", true)
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:ActionButton_UpdateHotkeys(button, type)
|
||||
-- if button:GetName() == "MyButton" then
|
||||
-- -- do stuff here
|
||||
-- else
|
||||
-- self.hooks.ActionButton_UpdateHotkeys(button, type)
|
||||
-- end
|
||||
-- end
|
||||
function AceHook:RawHook(object, method, handler, hookSecure)
|
||||
if type(object) == "string" then
|
||||
method, handler, hookSecure, object = object, method, handler, nil
|
||||
end
|
||||
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
|
||||
hook(self, object, method, handler, false, false, true, hookSecure or false, "Usage: RawHook([object], method, [handler], [hookSecure])")
|
||||
end
|
||||
|
||||
--- SecureHook a function or a method on an object.
|
||||
-- This function is a wrapper around the `hooksecurefunc` function in the WoW API. Using AceHook
|
||||
-- extends the functionality of secure hooks, and adds the ability to unhook once the hook isn't
|
||||
-- required anymore, or the addon is being disabled.\\
|
||||
-- Secure Hooks should be used if the secure-status of the function is vital to its function,
|
||||
-- and taint would block execution. Secure Hooks are always called after the original function was called
|
||||
-- ("Post Hook"), and you cannot modify the arguments, return values or control the execution.
|
||||
-- @paramsig [object], method, [handler]
|
||||
-- @param object The object to hook a method from
|
||||
-- @param method If object was specified, the name of the method, or the name of the function to hook.
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked function)
|
||||
function AceHook:SecureHook(object, method, handler)
|
||||
if type(object) == "string" then
|
||||
method, handler, object = object, method, nil
|
||||
end
|
||||
|
||||
hook(self, object, method, handler, false, true, false, false, "Usage: SecureHook([object], method, [handler])")
|
||||
end
|
||||
|
||||
--- Hook a script handler on a frame.
|
||||
-- The hook created will be a "safe hook", that means that your handler will be called
|
||||
-- before the hooked script ("Pre-Hook"), and you don't have to call the original function yourself,
|
||||
-- however you cannot stop the execution of the function, or modify any of the arguments/return values.\\
|
||||
-- This is the frame script equivalent of the :Hook safe-hook. It would typically be used to be notified
|
||||
-- when a certain event happens to a frame.
|
||||
-- @paramsig frame, script, [handler]
|
||||
-- @param frame The Frame to hook the script on
|
||||
-- @param script The script to hook
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked script)
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook the OnShow of FriendsFrame
|
||||
-- self:HookScript(FriendsFrame, "OnShow", "FriendsFrameOnShow")
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:FriendsFrameOnShow(frame)
|
||||
-- print("The FriendsFrame was shown!")
|
||||
-- end
|
||||
function AceHook:HookScript(frame, script, handler)
|
||||
hook(self, frame, script, handler, true, false, false, false, "Usage: HookScript(object, method, [handler])")
|
||||
end
|
||||
|
||||
--- RawHook a script handler on a frame.
|
||||
-- The hook created will be a "raw hook", that means that your handler will completly replace
|
||||
-- the original script, and your handler has to call the original script (or not, depending on your intentions).\\
|
||||
-- The original script will be stored in `self.hooks[frame][script]`.\\
|
||||
-- This type of hook can be used for all purposes, and is usually the most common case when you need to modify arguments
|
||||
-- or want to control execution of the original script.
|
||||
-- @paramsig frame, script, [handler]
|
||||
-- @param frame The Frame to hook the script on
|
||||
-- @param script The script to hook
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked script)
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook the OnShow of FriendsFrame
|
||||
-- self:RawHookScript(FriendsFrame, "OnShow", "FriendsFrameOnShow")
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:FriendsFrameOnShow(frame)
|
||||
-- -- Call the original function
|
||||
-- self.hooks[frame].OnShow(frame)
|
||||
-- -- Do our processing
|
||||
-- -- .. stuff
|
||||
-- end
|
||||
function AceHook:RawHookScript(frame, script, handler)
|
||||
hook(self, frame, script, handler, true, false, true, false, "Usage: RawHookScript(object, method, [handler])")
|
||||
end
|
||||
|
||||
--- SecureHook a script handler on a frame.
|
||||
-- This function is a wrapper around the `frame:HookScript` function in the WoW API. Using AceHook
|
||||
-- extends the functionality of secure hooks, and adds the ability to unhook once the hook isn't
|
||||
-- required anymore, or the addon is being disabled.\\
|
||||
-- Secure Hooks should be used if the secure-status of the function is vital to its function,
|
||||
-- and taint would block execution. Secure Hooks are always called after the original function was called
|
||||
-- ("Post Hook"), and you cannot modify the arguments, return values or control the execution.
|
||||
-- @paramsig frame, script, [handler]
|
||||
-- @param frame The Frame to hook the script on
|
||||
-- @param script The script to hook
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked script)
|
||||
function AceHook:SecureHookScript(frame, script, handler)
|
||||
hook(self, frame, script, handler, true, true, false, false, "Usage: SecureHookScript(object, method, [handler])")
|
||||
end
|
||||
|
||||
--- Unhook from the specified function, method or script.
|
||||
-- @paramsig [obj], method
|
||||
-- @param obj The object or frame to unhook from
|
||||
-- @param method The name of the method, function or script to unhook from.
|
||||
function AceHook:Unhook(obj, method)
|
||||
local usage = "Usage: Unhook([obj], method)"
|
||||
if type(obj) == "string" then
|
||||
method, obj = obj, nil
|
||||
end
|
||||
|
||||
if obj and type(obj) ~= "table" then
|
||||
error(format("%s: 'obj' - expecting nil or table got %s", usage, type(obj)), 2)
|
||||
end
|
||||
if type(method) ~= "string" then
|
||||
error(format("%s: 'method' - expeting string got %s", usage, type(method)), 2)
|
||||
end
|
||||
|
||||
local uid
|
||||
if obj then
|
||||
uid = registry[self][obj] and registry[self][obj][method]
|
||||
else
|
||||
uid = registry[self][method]
|
||||
end
|
||||
|
||||
if not uid or not actives[uid] then
|
||||
-- Declining to error on an unneeded unhook since the end effect is the same and this would just be annoying.
|
||||
return false
|
||||
end
|
||||
|
||||
actives[uid], handlers[uid] = nil, nil
|
||||
|
||||
if obj then
|
||||
local tmp = registry[self][obj]
|
||||
tmp[method] = nil
|
||||
if not next(tmp) then
|
||||
del(tmp)
|
||||
registry[self][obj] = nil
|
||||
end
|
||||
|
||||
-- if the hook reference doesnt exist, then its a secure hook, just bail out and dont do any unhooking
|
||||
if not self.hooks[obj] or not self.hooks[obj][method] then return true end
|
||||
|
||||
if scripts[uid] and obj:GetScript(method) == uid then -- unhooks scripts
|
||||
obj:SetScript(method, self.hooks[obj][method] ~= donothing and self.hooks[obj][method] or nil)
|
||||
scripts[uid] = nil
|
||||
elseif obj and self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then -- unhooks methods
|
||||
obj[method] = self.hooks[obj][method]
|
||||
end
|
||||
|
||||
tmp = self.hooks[obj]
|
||||
tmp[method] = nil
|
||||
if not next(tmp) then
|
||||
del(tmp)
|
||||
self.hooks[obj] = nil
|
||||
end
|
||||
else
|
||||
registry[self][method] = nil
|
||||
|
||||
-- if self.hooks[method] doesn't exist, then this is a SecureHook, just bail out
|
||||
if not self.hooks[method] then return true end
|
||||
|
||||
if self.hooks[method] and _G[method] == uid then -- unhooks functions
|
||||
_G[method] = self.hooks[method]
|
||||
end
|
||||
|
||||
self.hooks[method] = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Unhook all existing hooks for this addon.
|
||||
function AceHook:UnhookAll()
|
||||
for key, value in pairs(registry[self]) do
|
||||
if type(key) == "table" then
|
||||
for method in pairs(value) do
|
||||
self:Unhook(key, method)
|
||||
end
|
||||
else
|
||||
self:Unhook(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if the specific function, method or script is already hooked.
|
||||
-- @paramsig [obj], method
|
||||
-- @param obj The object or frame to unhook from
|
||||
-- @param method The name of the method, function or script to unhook from.
|
||||
function AceHook:IsHooked(obj, method)
|
||||
-- we don't check if registry[self] exists, this is done by evil magicks in the metatable
|
||||
if type(obj) == "string" then
|
||||
if registry[self][obj] and actives[registry[self][obj]] then
|
||||
return true, handlers[registry[self][obj]]
|
||||
end
|
||||
else
|
||||
if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then
|
||||
return true, handlers[registry[self][obj][method]]
|
||||
end
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
--- Upgrade our old embeded
|
||||
for target, v in pairs( AceHook.embeded ) do
|
||||
AceHook:Embed( target )
|
||||
end
|
||||
@@ -0,0 +1,280 @@
|
||||
--[[ $Id: CallbackHandler-1.0.lua 1131 2015-06-04 07:29:24Z nevcairiel $ ]]
|
||||
local MAJOR, MINOR = "CallbackHandler-1.0", 6
|
||||
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
|
||||
|
||||
if not CallbackHandler then return end -- No upgrade needed
|
||||
|
||||
-- Lua APIs
|
||||
local tconcat, tinsert, tgetn, tsetn = table.concat, table.insert, table.getn, table.setn
|
||||
local assert, error, loadstring = assert, error, loadstring
|
||||
local setmetatable, rawset, rawget = setmetatable, rawset, rawget
|
||||
local next, pairs, type, tostring = next, pairs, type, tostring
|
||||
local strgsub = string.gsub
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = "k"})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if not t then
|
||||
return {}
|
||||
end
|
||||
list[t] = nil
|
||||
return t
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
tsetn(t,0)
|
||||
list[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local meta = {__index = function(tbl, key) rawset(tbl, key, new()) return tbl[key] end}
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: geterrorhandler
|
||||
|
||||
local function errorhandler(err)
|
||||
return geterrorhandler()(err)
|
||||
end
|
||||
CallbackHandler.errorhandler = errorhandler
|
||||
|
||||
local function CreateDispatcher(argCount)
|
||||
local code = [[
|
||||
local xpcall, errorhandler = xpcall, LibStub("CallbackHandler-1.0").errorhandler
|
||||
local method, UP_ARGS
|
||||
local function call()
|
||||
local func, ARGS = method, UP_ARGS
|
||||
method, UP_ARGS = nil, NILS
|
||||
return func(ARGS)
|
||||
end
|
||||
return function(handlers, ARGS)
|
||||
local index
|
||||
index, method = next(handlers)
|
||||
if not method then return end
|
||||
repeat
|
||||
UP_ARGS = ARGS
|
||||
xpcall(call, errorhandler)
|
||||
index, method = next(handlers, index)
|
||||
until not method
|
||||
end
|
||||
]]
|
||||
local c = 4*argCount-1
|
||||
local s = "b01,b02,b03,b04,b05,b06,b07,b08,b09,b10"
|
||||
code = strgsub(code, "UP_ARGS", string.sub(s,1,c))
|
||||
s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10"
|
||||
code = strgsub(code, "ARGS", string.sub(s,1,c))
|
||||
s = "nil,nil,nil,nil,nil,nil,nil,nil,nil,nil"
|
||||
code = strgsub(code, "NILS", string.sub(s,1,c))
|
||||
return assert(loadstring(code, "safecall Dispatcher["..tostring(argCount).."]"))()
|
||||
end
|
||||
|
||||
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||
local dispatcher = CreateDispatcher(argCount)
|
||||
rawset(self, argCount, dispatcher)
|
||||
return dispatcher
|
||||
end})
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
-- CallbackHandler:New
|
||||
--
|
||||
-- target - target object to embed public APIs in
|
||||
-- RegisterName - name of the callback registration API, default "RegisterCallback"
|
||||
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
|
||||
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
|
||||
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName)
|
||||
|
||||
RegisterName = RegisterName or "RegisterCallback"
|
||||
UnregisterName = UnregisterName or "UnregisterCallback"
|
||||
if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
|
||||
UnregisterAllName = "UnregisterAllCallbacks"
|
||||
end
|
||||
|
||||
-- we declare all objects and exported APIs inside this closure to quickly gain access
|
||||
-- to e.g. function names, the "target" parameter, etc
|
||||
|
||||
|
||||
-- Create the registry object
|
||||
local events = setmetatable({}, meta)
|
||||
local registry = { recurse=0, events=events }
|
||||
|
||||
-- registry:Fire() - fires the given event/message into the registry
|
||||
function registry:Fire(eventname, argc, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
|
||||
if not rawget(events, eventname) or not next(events[eventname]) then return end
|
||||
local oldrecurse = registry.recurse
|
||||
registry.recurse = oldrecurse + 1
|
||||
|
||||
argc = argc or 0
|
||||
Dispatchers[argc+1](events[eventname], eventname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
|
||||
|
||||
registry.recurse = oldrecurse
|
||||
|
||||
if registry.insertQueue and oldrecurse==0 then
|
||||
-- Something in one of our callbacks wanted to register more callbacks; they got queued
|
||||
for eventname,callbacks in pairs(registry.insertQueue) do
|
||||
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
|
||||
for self,func in pairs(callbacks) do
|
||||
events[eventname][self] = func
|
||||
-- fire OnUsed callback?
|
||||
if first and registry.OnUsed then
|
||||
registry.OnUsed(registry, target, eventname)
|
||||
first = nil
|
||||
end
|
||||
end
|
||||
del(callbacks)
|
||||
end
|
||||
del(registry.insertQueue)
|
||||
registry.insertQueue = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Registration of a callback, handles:
|
||||
-- self["method"], leads to self["method"](self, ...)
|
||||
-- self with function ref, leads to functionref(...)
|
||||
-- "addonId" (instead of self) with function ref, leads to functionref(...)
|
||||
-- all with an optional arg, which, if present, gets passed as first argument (after self if present)
|
||||
target[RegisterName] = function(self, eventname, method, ...)
|
||||
if type(eventname) ~= "string" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
|
||||
end
|
||||
|
||||
method = method or eventname
|
||||
|
||||
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
|
||||
|
||||
if type(method) ~= "string" and type(method) ~= "function" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'method' - string or function expected.", 2)
|
||||
end
|
||||
|
||||
local regfunc
|
||||
local a1 = arg[1]
|
||||
|
||||
if type(method) == "string" then
|
||||
-- self["method"] calling style
|
||||
if type(self) ~= "table" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): self was not a table?", 2)
|
||||
elseif self==target then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): do not use Library:"..RegisterName.."(), use your own 'self'.", 2)
|
||||
elseif type(self[method]) ~= "function" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'method' - method '"..tostring(method).."' not found on 'self'.", 2)
|
||||
end
|
||||
|
||||
if tgetn(arg) >= 1 then
|
||||
regfunc = function (...) return self[method](self,a1,unpack(arg)) end
|
||||
else
|
||||
regfunc = function (...) return self[method](self,unpack(arg)) end
|
||||
end
|
||||
else
|
||||
-- function ref with self=object or self="addonId"
|
||||
if type(self)~="table" and type(self)~="string" then
|
||||
error("Usage: "..RegisterName.."(self or addonId, eventname, method[, arg]): 'self or addonId': table or string expected.", 2)
|
||||
end
|
||||
|
||||
if tgetn(arg) >= 1 then
|
||||
regfunc = function (...) return method(a1, unpack(arg)) end
|
||||
else
|
||||
regfunc = method
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if events[eventname][self] or registry.recurse<1 then
|
||||
-- if registry.recurse<1 then
|
||||
-- we're overwriting an existing entry, or not currently recursing. just set it.
|
||||
events[eventname][self] = regfunc
|
||||
-- fire OnUsed callback?
|
||||
if registry.OnUsed and first then
|
||||
registry.OnUsed(registry, target, eventname)
|
||||
end
|
||||
else
|
||||
-- we're currently processing a callback in this registry, so delay the registration of this new entry!
|
||||
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
|
||||
registry.insertQueue = registry.insertQueue or setmetatable(new(),meta)
|
||||
registry.insertQueue[eventname][self] = regfunc
|
||||
end
|
||||
end
|
||||
|
||||
-- Unregister a callback
|
||||
target[UnregisterName] = function(self, eventname)
|
||||
if not self or self==target then
|
||||
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
|
||||
end
|
||||
if type(eventname) ~= "string" then
|
||||
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
|
||||
end
|
||||
if rawget(events, eventname) and events[eventname][self] then
|
||||
events[eventname][self] = nil
|
||||
|
||||
-- Fire OnUnused callback?
|
||||
if registry.OnUnused and not next(events[eventname]) then
|
||||
registry.OnUnused(registry, target, eventname)
|
||||
end
|
||||
|
||||
if rawget(events, eventname) and not next(events[eventname]) then
|
||||
del(events[eventname])
|
||||
events[eventname] = nil
|
||||
end
|
||||
end
|
||||
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
|
||||
registry.insertQueue[eventname][self] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds
|
||||
if UnregisterAllName then
|
||||
target[UnregisterAllName] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if not a1 then
|
||||
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or 'addonId' to unregister events for.", 2)
|
||||
end
|
||||
if a1 == target then
|
||||
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or 'addonId'", 2)
|
||||
end
|
||||
|
||||
-- use our registry table as argument table
|
||||
registry[1] = a1
|
||||
registry[2] = a2
|
||||
registry[3] = a3
|
||||
registry[4] = a4
|
||||
registry[5] = a5
|
||||
registry[6] = a6
|
||||
registry[7] = a7
|
||||
registry[8] = a8
|
||||
registry[9] = a9
|
||||
registry[10] = a10
|
||||
for i=1,10 do
|
||||
local self = registry[i]
|
||||
registry[i] = nil
|
||||
if self then
|
||||
if registry.insertQueue then
|
||||
for eventname, callbacks in pairs(registry.insertQueue) do
|
||||
if callbacks[self] then
|
||||
callbacks[self] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
for eventname, callbacks in pairs(events) do
|
||||
if callbacks[self] then
|
||||
callbacks[self] = nil
|
||||
-- Fire OnUnused callback?
|
||||
if registry.OnUnused and not next(callbacks) then
|
||||
registry.OnUnused(registry, target, eventname)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return registry
|
||||
end
|
||||
|
||||
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
|
||||
-- try to upgrade old implicit embeds since the system is selfcontained and
|
||||
-- relies on closures to work.
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- LibDBIcon-1.0
|
||||
--
|
||||
-- Allows addons to easily create a lightweight minimap icon as an alternative to heavier LDB displays.
|
||||
--
|
||||
|
||||
local DBICON10 = "LibDBIcon-1.0"
|
||||
local DBICON10_MINOR = 44 -- Bump on changes
|
||||
if not LibStub then error(DBICON10 .. " requires LibStub.") end
|
||||
local ldb = LibStub("LibDataBroker-1.1", true)
|
||||
if not ldb then error(DBICON10 .. " requires LibDataBroker-1.1.") end
|
||||
local lib = LibStub:NewLibrary(DBICON10, DBICON10_MINOR)
|
||||
if not lib then return end
|
||||
|
||||
LibStub("AceHook-3.0"):Embed(lib)
|
||||
lib.objects = lib.objects or {}
|
||||
lib.callbackRegistered = lib.callbackRegistered or nil
|
||||
lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
|
||||
lib.notCreated = lib.notCreated or {}
|
||||
lib.radius = lib.radius or 5
|
||||
lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", "LibDBIconTooltip", UIParent, "GameTooltipTemplate")
|
||||
local next, Minimap = next, Minimap
|
||||
local tgetn = table.getn
|
||||
local isDraggingButton = false
|
||||
|
||||
function lib:IconCallback(event, name, key, value)
|
||||
if lib.objects[name] then
|
||||
if key == "icon" then
|
||||
lib.objects[name].icon:SetTexture(value)
|
||||
elseif key == "iconCoords" then
|
||||
lib.objects[name].icon:UpdateCoord()
|
||||
elseif key == "iconR" then
|
||||
local _, g, b = lib.objects[name].icon:GetVertexColor()
|
||||
lib.objects[name].icon:SetVertexColor(value, g, b)
|
||||
elseif key == "iconG" then
|
||||
local r, _, b = lib.objects[name].icon:GetVertexColor()
|
||||
lib.objects[name].icon:SetVertexColor(r, value, b)
|
||||
elseif key == "iconB" then
|
||||
local r, g = lib.objects[name].icon:GetVertexColor()
|
||||
lib.objects[name].icon:SetVertexColor(r, g, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not lib.callbackRegistered then
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__icon", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconCoords", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconR", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconG", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconB", "IconCallback")
|
||||
lib.callbackRegistered = true
|
||||
end
|
||||
|
||||
local function getAnchors(frame)
|
||||
local x, y = frame:GetCenter()
|
||||
if not x or not y then return "CENTER" end
|
||||
local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or ""
|
||||
local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM"
|
||||
return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf
|
||||
end
|
||||
|
||||
local function onEnter()
|
||||
if isDraggingButton then return end
|
||||
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
|
||||
local obj = this.dataObject
|
||||
if obj.OnTooltipShow then
|
||||
lib.tooltip:SetOwner(this, "ANCHOR_NONE")
|
||||
lib.tooltip:SetPoint(getAnchors(this))
|
||||
obj.OnTooltipShow(lib.tooltip)
|
||||
lib.tooltip:Show()
|
||||
elseif obj.OnEnter then
|
||||
obj.OnEnter()
|
||||
end
|
||||
end
|
||||
|
||||
local function onLeave()
|
||||
lib.tooltip:Hide()
|
||||
|
||||
if not isDraggingButton then
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Play()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local obj = this.dataObject
|
||||
if obj.OnLeave then
|
||||
obj.OnLeave()
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local onDragStart, updatePosition
|
||||
|
||||
do
|
||||
local minimapShapes = {
|
||||
["ROUND"] = {true, true, true, true},
|
||||
["SQUARE"] = {false, false, false, false},
|
||||
["CORNER-TOPLEFT"] = {false, false, false, true},
|
||||
["CORNER-TOPRIGHT"] = {false, false, true, false},
|
||||
["CORNER-BOTTOMLEFT"] = {false, true, false, false},
|
||||
["CORNER-BOTTOMRIGHT"] = {true, false, false, false},
|
||||
["SIDE-LEFT"] = {false, true, false, true},
|
||||
["SIDE-RIGHT"] = {true, false, true, false},
|
||||
["SIDE-TOP"] = {false, false, true, true},
|
||||
["SIDE-BOTTOM"] = {true, true, false, false},
|
||||
["TRICORNER-TOPLEFT"] = {false, true, true, true},
|
||||
["TRICORNER-TOPRIGHT"] = {true, false, true, true},
|
||||
["TRICORNER-BOTTOMLEFT"] = {true, true, false, true},
|
||||
["TRICORNER-BOTTOMRIGHT"] = {true, true, true, false},
|
||||
}
|
||||
|
||||
local rad, cos, sin, sqrt, max, min = math.rad, math.cos, math.sin, math.sqrt, math.max, math.min
|
||||
function updatePosition(button, position)
|
||||
local angle = rad(position or 225)
|
||||
local x, y, q = cos(angle), sin(angle), 1
|
||||
if x < 0 then q = q + 1 end
|
||||
if y > 0 then q = q + 2 end
|
||||
local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
|
||||
local quadTable = minimapShapes[minimapShape]
|
||||
local w = (Minimap:GetWidth() / 2) + lib.radius
|
||||
local h = (Minimap:GetHeight() / 2) + lib.radius
|
||||
if quadTable[q] then
|
||||
x, y = x*w, y*h
|
||||
else
|
||||
local diagRadiusW = sqrt(2*(w)^2)-10
|
||||
local diagRadiusH = sqrt(2*(h)^2)-10
|
||||
x = max(-w, min(x*diagRadiusW, w))
|
||||
y = max(-h, min(y*diagRadiusH, h))
|
||||
end
|
||||
button:SetPoint("CENTER", Minimap, "CENTER", x, y)
|
||||
end
|
||||
end
|
||||
|
||||
local function onClick( )
|
||||
if this.dataObject.OnClick then
|
||||
this.dataObject.OnClick( this, arg1)
|
||||
end
|
||||
end
|
||||
|
||||
local function onMouseDown()
|
||||
this.isMouseDown = true
|
||||
this.icon:UpdateCoord()
|
||||
end
|
||||
|
||||
local function onMouseUp()
|
||||
this.isMouseDown = false
|
||||
this.icon:UpdateCoord()
|
||||
end
|
||||
|
||||
local fmod = function(x, y)
|
||||
return x - math.floor(x / y) * y
|
||||
end
|
||||
|
||||
do
|
||||
local deg, atan2 = math.deg, math.atan2
|
||||
local function onUpdate()
|
||||
local mx, my = Minimap:GetCenter()
|
||||
local px, py = GetCursorPosition()
|
||||
local scale = Minimap:GetEffectiveScale()
|
||||
px, py = px / scale, py / scale
|
||||
local pos = 225
|
||||
if this.db then
|
||||
pos = fmod(deg(atan2(py - my, px - mx)) , 360)
|
||||
this.db.minimapPos = pos
|
||||
else
|
||||
pos = fmod(deg(atan2(py - my, px - mx)) , 360)
|
||||
this.minimapPos = pos
|
||||
end
|
||||
updatePosition(this, pos)
|
||||
end
|
||||
|
||||
function onDragStart()
|
||||
this:LockHighlight()
|
||||
this.isMouseDown = true
|
||||
this.icon:UpdateCoord()
|
||||
this:SetScript("OnUpdate", onUpdate)
|
||||
isDraggingButton = true
|
||||
lib.tooltip:Hide()
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function onDragStop()
|
||||
this:SetScript("OnUpdate", nil)
|
||||
this.isMouseDown = false
|
||||
this.icon:UpdateCoord()
|
||||
this:UnlockHighlight()
|
||||
isDraggingButton = false
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Play()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local defaultCoords = {0, 1, 0, 1}
|
||||
local function updateCoord(self)
|
||||
local coords = self:GetParent().dataObject.iconCoords or defaultCoords
|
||||
local deltaX, deltaY = 0, 0
|
||||
if not self:GetParent().isMouseDown then
|
||||
deltaX = (coords[2] - coords[1]) * 0.05
|
||||
deltaY = (coords[4] - coords[3]) * 0.05
|
||||
end
|
||||
self:SetTexCoord(coords[1] + deltaX, coords[2] - deltaX, coords[3] + deltaY, coords[4] - deltaY)
|
||||
end
|
||||
|
||||
local function createButton(name, object, db)
|
||||
local button = CreateFrame("Button", "LibDBIcon10_"..name, Minimap)
|
||||
button.dataObject = object
|
||||
button.db = db
|
||||
button:SetFrameStrata("MEDIUM")
|
||||
button:SetWidth(31)
|
||||
button:SetHeight(31)
|
||||
button:SetFrameLevel(8)
|
||||
button:SetFrameStrata("HIGH")
|
||||
button:SetFrameLevel(7)
|
||||
button:EnableMouse(true)
|
||||
--button:EnableMouseWheel(true)
|
||||
button:SetMovable(true)
|
||||
button:RegisterForClicks("LeftButtonUp","RightButtonUp")
|
||||
button:RegisterForDrag("LeftButton")
|
||||
button:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
|
||||
local overlay = button:CreateTexture(nil, "OVERLAY")
|
||||
overlay:SetWidth(53)
|
||||
overlay:SetHeight(53)
|
||||
overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
|
||||
overlay:SetPoint("TOPLEFT",0,0)
|
||||
local background = button:CreateTexture(nil, "BACKGROUND")
|
||||
background:SetWidth(20)
|
||||
background:SetHeight(20)
|
||||
background:SetTexture("Interface\\Minimap\\UI-Minimap-Background")
|
||||
background:SetPoint("TOPLEFT", 7, -5)
|
||||
local icon = button:CreateTexture(nil, "ARTWORK")
|
||||
icon:SetWidth(17)
|
||||
icon:SetHeight(17)
|
||||
icon:SetTexture(object.icon)
|
||||
icon:SetPoint("TOPLEFT", 7, -6)
|
||||
button.icon = icon
|
||||
icon.parent = button
|
||||
button.isMouseDown = false
|
||||
|
||||
local r, g, b = icon:GetVertexColor()
|
||||
icon:SetVertexColor(object.iconR or r, object.iconG or g, object.iconB or b)
|
||||
|
||||
icon.UpdateCoord = updateCoord
|
||||
icon:UpdateCoord()
|
||||
|
||||
button:SetScript("OnEnter", onEnter)
|
||||
button:SetScript("OnLeave", onLeave)
|
||||
button:SetScript("OnClick", onClick)
|
||||
if not db or not db.lock then
|
||||
button:SetScript("OnDragStart", onDragStart)
|
||||
button:SetScript("OnDragStop", onDragStop)
|
||||
end
|
||||
button:SetScript("OnMouseDown", onMouseDown)
|
||||
button:SetScript("OnMouseUp", onMouseUp)
|
||||
--[[
|
||||
button.fadeOut = button:CreateAnimationGroup()
|
||||
local animOut = button.fadeOut:CreateAnimation("Alpha")
|
||||
animOut:SetOrder(1)
|
||||
animOut:SetDuration(0.2)
|
||||
animOut:SetFromAlpha(1)
|
||||
animOut:SetToAlpha(0)
|
||||
animOut:SetStartDelay(1)
|
||||
button.fadeOut:SetToFinalAlpha(true)
|
||||
]]
|
||||
lib.objects[name] = button
|
||||
|
||||
if lib.loggedIn then
|
||||
updatePosition(button, db and db.minimapPos)
|
||||
if not db or not db.hide then
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
end
|
||||
lib.callbacks:Fire("LibDBIcon_IconCreated", button, name) -- Fire 'Icon Created' callback
|
||||
end
|
||||
|
||||
-- We could use a metatable.__index on lib.objects, but then we'd create
|
||||
-- the icons when checking things like :IsRegistered, which is not necessary.
|
||||
local function check(name)
|
||||
if lib.notCreated[name] then
|
||||
createButton(name, lib.notCreated[name][1], lib.notCreated[name][2])
|
||||
lib.notCreated[name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Wait a bit with the initial positioning to let any GetMinimapShape addons
|
||||
-- load up.
|
||||
if not lib.loggedIn then
|
||||
local f = CreateFrame("Frame")
|
||||
f:SetScript("OnEvent", function()
|
||||
for _, button in next, lib.objects do
|
||||
updatePosition(button, button.db and button.db.minimapPos)
|
||||
if not button.db or not button.db.hide then
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
end
|
||||
lib.loggedIn = true
|
||||
this:SetScript("OnEvent", nil)
|
||||
end)
|
||||
f:RegisterEvent("PLAYER_LOGIN")
|
||||
end
|
||||
|
||||
local function getDatabase(name)
|
||||
return lib.notCreated[name] and lib.notCreated[name][2] or lib.objects[name].db
|
||||
end
|
||||
|
||||
function lib:Register(name, object, db)
|
||||
if not object.icon then error("Can't register LDB objects without icons set!") end
|
||||
if lib.objects[name] or lib.notCreated[name] then error(DBICON10.. ": Object '".. name .."' is already registered.") end
|
||||
if not db or not db.hide then
|
||||
createButton(name, object, db)
|
||||
else
|
||||
lib.notCreated[name] = {object, db}
|
||||
end
|
||||
end
|
||||
|
||||
function lib:Lock(name)
|
||||
if not lib:IsRegistered(name) then return end
|
||||
if lib.objects[name] then
|
||||
lib.objects[name]:SetScript("OnDragStart", nil)
|
||||
lib.objects[name]:SetScript("OnDragStop", nil)
|
||||
end
|
||||
local db = getDatabase(name)
|
||||
if db then
|
||||
db.lock = true
|
||||
end
|
||||
end
|
||||
|
||||
function lib:Unlock(name)
|
||||
if not lib:IsRegistered(name) then return end
|
||||
if lib.objects[name] then
|
||||
lib.objects[name]:SetScript("OnDragStart", onDragStart)
|
||||
lib.objects[name]:SetScript("OnDragStop", onDragStop)
|
||||
end
|
||||
local db = getDatabase(name)
|
||||
if db then
|
||||
db.lock = nil
|
||||
end
|
||||
end
|
||||
|
||||
function lib:Hide(name)
|
||||
if not lib.objects[name] then return end
|
||||
lib.objects[name]:Hide()
|
||||
end
|
||||
|
||||
function lib:Show(name)
|
||||
check(name)
|
||||
local button = lib.objects[name]
|
||||
if button then
|
||||
button:Show()
|
||||
updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
|
||||
end
|
||||
end
|
||||
|
||||
function lib:IsRegistered(name)
|
||||
return (lib.objects[name] or lib.notCreated[name]) and true or false
|
||||
end
|
||||
|
||||
function lib:Refresh(name, db)
|
||||
check(name)
|
||||
local button = lib.objects[name]
|
||||
if db then
|
||||
button.db = db
|
||||
end
|
||||
updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
|
||||
if not button.db or not button.db.hide then
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
if not button.db or not button.db.lock then
|
||||
button:SetScript("OnDragStart", onDragStart)
|
||||
button:SetScript("OnDragStop", onDragStop)
|
||||
else
|
||||
button:SetScript("OnDragStart", nil)
|
||||
button:SetScript("OnDragStop", nil)
|
||||
end
|
||||
end
|
||||
|
||||
function lib:GetMinimapButton(name)
|
||||
return lib.objects[name]
|
||||
end
|
||||
|
||||
do
|
||||
local function OnMinimapEnter()
|
||||
if isDraggingButton then return end
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
-- button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
local function OnMinimapLeave()
|
||||
if isDraggingButton then return end
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
-- button.fadeOut:Play()
|
||||
end
|
||||
end
|
||||
end
|
||||
lib:HookScript(Minimap, "OnEnter", OnMinimapEnter)
|
||||
lib:HookScript(Minimap, "OnLeave", OnMinimapLeave)
|
||||
|
||||
function lib:ShowOnEnter(name, value)
|
||||
local button = lib.objects[name]
|
||||
if button then
|
||||
if value then
|
||||
button.showOnMouseover = true
|
||||
-- button.fadeOut:Stop()
|
||||
button:SetAlpha(0)
|
||||
else
|
||||
button.showOnMouseover = false
|
||||
-- button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lib:GetButtonList()
|
||||
local t = {}
|
||||
for name in next, lib.objects do
|
||||
tinsert(t, name)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
function lib:SetButtonRadius(radius)
|
||||
if type(radius) == "number" then
|
||||
lib.radius = radius
|
||||
for _, button in next, lib.objects do
|
||||
updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lib:SetButtonToPosition(button, position)
|
||||
updatePosition(lib.objects[button] or button, position)
|
||||
end
|
||||
|
||||
-- Upgrade!
|
||||
for name, button in next, lib.objects do
|
||||
local db = getDatabase(name)
|
||||
if not db or not db.lock then
|
||||
button:SetScript("OnDragStart", onDragStart)
|
||||
button:SetScript("OnDragStop", onDragStop)
|
||||
end
|
||||
button:SetScript("OnEnter", onEnter)
|
||||
button:SetScript("OnLeave", onLeave)
|
||||
button:SetScript("OnClick", onClick)
|
||||
button:SetScript("OnMouseDown", onMouseDown)
|
||||
button:SetScript("OnMouseUp", onMouseUp)
|
||||
--[[
|
||||
if not button.fadeOut then -- Upgrade to 39
|
||||
button.fadeOut = button:CreateAnimationGroup()
|
||||
local animOut = button.fadeOut:CreateAnimation("Alpha")
|
||||
animOut:SetOrder(1)
|
||||
animOut:SetDuration(0.2)
|
||||
animOut:SetFromAlpha(1)
|
||||
animOut:SetToAlpha(0)
|
||||
animOut:SetStartDelay(1)
|
||||
button.fadeOut:SetToFinalAlpha(true)
|
||||
end
|
||||
]]
|
||||
end
|
||||
lib:SetButtonRadius(lib.radius) -- Upgrade to 40
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
assert(LibStub, "LibDataBroker-1.1 requires LibStub")
|
||||
assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
|
||||
|
||||
local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4)
|
||||
if not lib then return end
|
||||
oldminor = oldminor or 0
|
||||
|
||||
|
||||
lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
|
||||
lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
|
||||
local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
|
||||
|
||||
if oldminor < 2 then
|
||||
lib.domt = {
|
||||
__metatable = "access denied",
|
||||
__index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
|
||||
}
|
||||
end
|
||||
|
||||
if oldminor < 3 then
|
||||
lib.domt.__newindex = function(self, key, value)
|
||||
if not attributestorage[self] then attributestorage[self] = {} end
|
||||
if attributestorage[self][key] == value then return end
|
||||
attributestorage[self][key] = value
|
||||
local name = namestorage[self]
|
||||
if not name then return end
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
|
||||
end
|
||||
end
|
||||
|
||||
if oldminor < 2 then
|
||||
function lib:NewDataObject(name, dataobj)
|
||||
if self.proxystorage[name] then return end
|
||||
|
||||
if dataobj then
|
||||
assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
|
||||
self.attributestorage[dataobj] = {}
|
||||
for i,v in pairs(dataobj) do
|
||||
self.attributestorage[dataobj][i] = v
|
||||
dataobj[i] = nil
|
||||
end
|
||||
end
|
||||
dataobj = setmetatable(dataobj or {}, self.domt)
|
||||
self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
|
||||
self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
|
||||
return dataobj
|
||||
end
|
||||
end
|
||||
|
||||
if oldminor < 1 then
|
||||
function lib:DataObjectIterator()
|
||||
return pairs(self.proxystorage)
|
||||
end
|
||||
|
||||
function lib:GetDataObjectByName(dataobjectname)
|
||||
return self.proxystorage[dataobjectname]
|
||||
end
|
||||
|
||||
function lib:GetNameByDataObject(dataobject)
|
||||
return self.namestorage[dataobject]
|
||||
end
|
||||
end
|
||||
|
||||
if oldminor < 4 then
|
||||
local next = pairs(attributestorage)
|
||||
function lib:pairs(dataobject_or_name)
|
||||
local t = type(dataobject_or_name)
|
||||
assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")
|
||||
|
||||
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
|
||||
assert(attributestorage[dataobj], "Data object not found")
|
||||
|
||||
return next, attributestorage[dataobj], nil
|
||||
end
|
||||
|
||||
local ipairs_iter = ipairs(attributestorage)
|
||||
function lib:ipairs(dataobject_or_name)
|
||||
local t = type(dataobject_or_name)
|
||||
assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")
|
||||
|
||||
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
|
||||
assert(attributestorage[dataobj], "Data object not found")
|
||||
|
||||
return ipairs_iter, attributestorage[dataobj], 0
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
|
||||
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
|
||||
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
|
||||
local _G = getfenv()
|
||||
local strfind, strfmt = string.find, string.format
|
||||
local LibStub = _G[LIBSTUB_MAJOR]
|
||||
|
||||
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
|
||||
LibStub = LibStub or { libs = {}, minors = {} }
|
||||
_G[LIBSTUB_MAJOR] = LibStub
|
||||
LibStub.minor = LIBSTUB_MINOR
|
||||
|
||||
function LibStub:NewLibrary(major, minor)
|
||||
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
|
||||
local _,_,num = strfind(minor, "(%d+)")
|
||||
minor = assert(tonumber(num), "Minor version must either be a number or contain a number.")
|
||||
|
||||
local oldminor = self.minors[major]
|
||||
if oldminor and oldminor >= minor then return nil end
|
||||
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
|
||||
return self.libs[major], oldminor
|
||||
end
|
||||
|
||||
function LibStub:GetLibrary(major, silent)
|
||||
if not self.libs[major] and not silent then
|
||||
error(strfmt("Cannot find a library instance of %q.", tostring(major)), 2)
|
||||
end
|
||||
return self.libs[major], self.minors[major]
|
||||
end
|
||||
|
||||
function LibStub:IterateLibraries() return pairs(self.libs) end
|
||||
setmetatable(LibStub, { __call = LibStub.GetLibrary })
|
||||
end
|
||||
Reference in New Issue
Block a user