Add files via upload

This commit is contained in:
Dusk-92
2026-06-09 11:23:40 +02:00
committed by GitHub
commit 11cb9ce00a
90 changed files with 57994 additions and 0 deletions
@@ -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