TourGuide
[WoW-TourGuide.git] / OptionHouse.lua
blob13a378fb12caef9a1b2ecf0c37a9a99ee960bafd
1 --[[-------------------------------------------------------------------------
2 Copyright (c) 2006-2007, Dongle Development Team
3 All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 * Redistributions in binary form must reproduce the above
12 copyright notice, this list of conditions and the following
13 disclaimer in the documentation and/or other materials provided
14 with the distribution.
15 * Neither the name of the Dongle Development Team nor the names of
16 its contributors may be used to endorse or promote products derived
17 from this software without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 ---------------------------------------------------------------------------]]
31 local major = "DongleStub"
32 local minor = tonumber(string.match("$Revision: 313 $", "(%d+)") or 1)
34 local g = getfenv(0)
36 if not g.DongleStub or g.DongleStub:IsNewerVersion(major, minor) then
37 local lib = setmetatable({}, {
38 __call = function(t,k)
39 if type(t.versions) == "table" and t.versions[k] then
40 return t.versions[k].instance
41 else
42 error("Cannot find a library with name '"..tostring(k).."'", 2)
43 end
44 end
47 function lib:IsNewerVersion(major, minor)
48 local versionData = self.versions and self.versions[major]
50 -- If DongleStub versions have differing major version names
51 -- such as DongleStub-Beta0 and DongleStub-1.0-RC2 then a second
52 -- instance will be loaded, with older logic. This code attempts
53 -- to compensate for that by matching the major version against
54 -- "^DongleStub", and handling the version check correctly.
56 if major:match("^DongleStub") then
57 local oldmajor,oldminor = self:GetVersion()
58 if self.versions and self.versions[oldmajor] then
59 return minor > oldminor
60 else
61 return true
62 end
63 end
65 if not versionData then return true end
66 local oldmajor,oldminor = versionData.instance:GetVersion()
67 return minor > oldminor
68 end
70 local function NilCopyTable(src, dest)
71 for k,v in pairs(dest) do dest[k] = nil end
72 for k,v in pairs(src) do dest[k] = v end
73 end
75 function lib:Register(newInstance, activate, deactivate)
76 assert(type(newInstance.GetVersion) == "function",
77 "Attempt to register a library with DongleStub that does not have a 'GetVersion' method.")
79 local major,minor = newInstance:GetVersion()
80 assert(type(major) == "string",
81 "Attempt to register a library with DongleStub that does not have a proper major version.")
82 assert(type(minor) == "number",
83 "Attempt to register a library with DongleStub that does not have a proper minor version.")
85 -- Generate a log of all library registrations
86 if not self.log then self.log = {} end
87 table.insert(self.log, string.format("Register: %s, %s", major, minor))
89 if not self:IsNewerVersion(major, minor) then return false end
90 if not self.versions then self.versions = {} end
92 local versionData = self.versions[major]
93 if not versionData then
94 -- New major version
95 versionData = {
96 ["instance"] = newInstance,
97 ["deactivate"] = deactivate,
100 self.versions[major] = versionData
101 if type(activate) == "function" then
102 table.insert(self.log, string.format("Activate: %s, %s", major, minor))
103 activate(newInstance)
105 return newInstance
108 local oldDeactivate = versionData.deactivate
109 local oldInstance = versionData.instance
111 versionData.deactivate = deactivate
113 local skipCopy
114 if type(activate) == "function" then
115 table.insert(self.log, string.format("Activate: %s, %s", major, minor))
116 skipCopy = activate(newInstance, oldInstance)
119 -- Deactivate the old libary if necessary
120 if type(oldDeactivate) == "function" then
121 local major, minor = oldInstance:GetVersion()
122 table.insert(self.log, string.format("Deactivate: %s, %s", major, minor))
123 oldDeactivate(oldInstance, newInstance)
126 -- Re-use the old table, and discard the new one
127 if not skipCopy then
128 NilCopyTable(newInstance, oldInstance)
130 return oldInstance
133 function lib:GetVersion() return major,minor end
135 local function Activate(new, old)
136 -- This code ensures that we'll move the versions table even
137 -- if the major version names are different, in the case of
138 -- DongleStub
139 if not old then old = g.DongleStub end
141 if old then
142 new.versions = old.versions
143 new.log = old.log
145 g.DongleStub = new
148 -- Actually trigger libary activation here
149 local stub = g.DongleStub or lib
150 lib = stub:Register(lib, Activate)
153 --[[-------------------------------------------------------------------------
154 Begin Library Implementation
155 ---------------------------------------------------------------------------]]
156 local major = "OptionHouse-1.0"
157 local minor = tonumber(string.match("$Revision: 581 $", "(%d+)") or 1)
159 assert(DongleStub, string.format("%s requires DongleStub.", major))
161 if( not DongleStub:IsNewerVersion(major, minor) ) then return end
163 local L = {
164 ["ERROR_NO_FRAME"] = "No frame returned for the addon \"%s\", category \"%s\", sub category \"%s\".",
165 ["NO_FUNC_PASSED"] = "You must associate a function with a category.",
166 ["IS_PRIVATEAPI"] = "You are trying to call a private api from a non-OptionHouse module.",
167 ["BAD_ARGUMENT"] = "bad argument #%d to '%s' (%s expected, got %s)",
168 ["MUST_CALL"] = "You must call '%s' from an OptionHouse addon object.",
169 ["ADDON_ALREADYREG"] = "The addon '%s' is already registered with OptionHouse.",
170 ["UNKNOWN_TAB"] = "Cannot open tab #%d, only %d tabs are registered.",
171 ["CATEGORY_ALREADYREG"] = "The category '%s' already exists in '%s'",
172 ["NO_CATEGORYEXISTS"] = "No category named '%s' in '%s' exists.",
173 ["NO_SUBCATEXISTS"] = "No sub-category '%s' exists in '%s' for the addon '%s'.",
174 ["NO_PARENTCAT"] = "No parent category named '%s' exists in %s'",
175 ["SUBCATEGORY_ALREADYREG"] = "The sub-category named '%s' already exists in the category '%s' for '%s'",
176 ["OPTION_HOUSE"] = "Option House",
177 ["ENTERED_COMBAT"] = "|cFF33FF99Option House|r: Configuration window closed due to entering combat.",
178 ["SEARCH"] = "Search...",
179 ["ADDON_OPTIONS"] = "Addons",
180 ["VERSION"] = "Version: %s",
181 ["AUTHOR"] = "Author: %s",
182 ["TOTAL_SUBCATEGORIES"] = "Sub Categories: %d",
183 ["TAB_MANAGEMENT"] = "Management",
184 ["TAB_PERFORMANCE"] = "Performance",
185 ["UNKNOWN_FRAMETYPE"] = "Unknown frame type requested '%s', only 'main', 'perf', 'addon', 'config' are supported.",
188 local function assert(level,condition,message)
189 if( not condition ) then
190 error(message,level)
194 local function argcheck(value, num, ...)
195 if( type(num) ~= "number" ) then
196 error(L["BAD_ARGUMENT"]:format(2, "argcheck", "number", type(num)), 1)
199 for i=1,select("#", ...) do
200 if( type(value) == select(i, ...) ) then return end
203 local types = string.join(", ", ...)
204 local name = string.match(debugstack(2,2,0), ": in function [`<](.-)['>]")
205 error(L["BAD_ARGUMENT"]:format(num, name, types, type(value)), 3)
208 -- OptionHouse
209 local OptionHouse = {}
210 local tabfunctions = {}
211 local methods = {"RegisterCategory", "RegisterSubCategory", "RemoveCategory", "RemoveSubCategory"}
212 local addons = {}
213 local regFrames = {}
214 local evtFrame
215 local frame
217 -- TABS
218 local function resizeTab(tab)
219 local textWidth = tab:GetFontString():GetWidth()
221 tab.middleActive:SetWidth(textWidth)
222 tab.middleInactive:SetWidth(textWidth)
224 tab:SetWidth((2 * tab.leftActive:GetWidth()) + textWidth)
225 tab.highlightTexture:SetWidth(textWidth + 20)
228 local function tabSelected(tab)
229 tab:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
230 tab.highlightTexture:Hide()
232 tab.leftActive:Show()
233 tab.middleActive:Show()
234 tab.rightActive:Show()
236 tab.leftInactive:Hide()
237 tab.middleInactive:Hide()
238 tab.rightInactive:Hide()
241 local function tabDeselected(tab)
242 tab:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
243 tab.highlightTexture:Show()
245 tab.leftInactive:Show()
246 tab.middleInactive:Show()
247 tab.rightInactive:Show()
249 tab.leftActive:Hide()
250 tab.middleActive:Hide()
251 tab.rightActive:Hide()
254 local function setTab(id)
255 if( frame.selectedTab ) then
256 tabDeselected(frame.tabs[frame.selectedTab])
259 frame.selectedTab = id
260 tabSelected(frame.tabs[id])
263 local function tabOnClick(self)
264 local id
265 if( type(self) ~= "number" ) then
266 id = self:GetID()
267 else
268 id = self
271 setTab(id)
273 for tabID, tab in pairs(tabfunctions) do
274 if( tabID == id ) then
275 if( type(tab.func) == "function" ) then
276 tab.func()
277 else
278 tab.handler[tab.func](tab.handler)
281 if( tab.type == "browse" ) then
282 frame.topLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-TopLeft")
283 frame.top:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-Top")
284 frame.topRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-TopRight")
285 frame.bottomLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-BotLeft")
286 frame.bottom:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-Bot")
287 frame.bottomRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Browse-BotRight")
288 elseif( tab.type == "bid" ) then
289 frame.topLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-TopLeft")
290 frame.top:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-Top")
291 frame.topRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-TopRight")
292 frame.bottomLeft:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-BotLeft")
293 frame.bottom:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-Bot")
294 frame.bottomRight:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-Bid-BotRight")
297 elseif( type(tab.func) == "function" ) then
298 tab.func(true)
299 else
300 tab.handler[tab.func](tab.handler, true)
305 local function createTab(text, id)
306 local tab = frame.tabs[id]
307 if( not tab ) then
308 tab = CreateFrame("Button", nil, frame)
309 tab:SetHighlightFontObject(GameFontHighlightSmall)
310 tab:SetTextFontObject(GameFontNormalSmall)
311 tab:SetHighlightTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight")
312 tab:SetText(text)
313 tab:SetWidth(115)
314 tab:SetHeight(32)
315 tab:SetID(id)
316 tab:SetScript("OnClick", tabOnClick)
317 tab:GetFontString():SetPoint("CENTER", 0, 2)
319 tab.highlightTexture = tab:GetHighlightTexture()
320 tab.highlightTexture:ClearAllPoints()
321 tab.highlightTexture:SetPoint("CENTER", tab:GetFontString(), 0, 0)
322 tab.highlightTexture:SetBlendMode("ADD")
324 -- TAB SELECTED TEXTURES
325 tab.leftActive = tab:CreateTexture(nil, "ARTWORK")
326 tab.leftActive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ActiveTab")
327 tab.leftActive:SetHeight(32)
328 tab.leftActive:SetWidth(20)
329 tab.leftActive:SetPoint("TOPLEFT", tab, "TOPLEFT")
330 tab.leftActive:SetTexCoord(0, 0.15625, 0, 1.0)
332 tab.middleActive = tab:CreateTexture(nil, "ARTWORK")
333 tab.middleActive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ActiveTab")
334 tab.middleActive:SetHeight(32)
335 tab.middleActive:SetWidth(20)
336 tab.middleActive:SetPoint("LEFT", tab.leftActive, "RIGHT")
337 tab.middleActive:SetTexCoord(0.15625, 0.84375, 0, 1.0)
339 tab.rightActive = tab:CreateTexture(nil, "ARTWORK")
340 tab.rightActive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ActiveTab")
341 tab.rightActive:SetHeight(32)
342 tab.rightActive:SetWidth(20)
343 tab.rightActive:SetPoint("LEFT", tab.middleActive, "RIGHT")
344 tab.rightActive:SetTexCoord(0.84375, 1.0, 0, 1.0)
346 -- TAB DESELECTED TEXTURES
347 tab.leftInactive = tab:CreateTexture(nil, "ARTWORK")
348 tab.leftInactive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-InActiveTab")
349 tab.leftInactive:SetHeight(32)
350 tab.leftInactive:SetWidth(20)
351 tab.leftInactive:SetPoint("TOPLEFT", tab, "TOPLEFT")
352 tab.leftInactive:SetTexCoord(0, 0.15625, 0, 1.0)
354 tab.middleInactive = tab:CreateTexture(nil, "ARTWORK")
355 tab.middleInactive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-InActiveTab")
356 tab.middleInactive:SetHeight(32)
357 tab.middleInactive:SetWidth(20)
358 tab.middleInactive:SetPoint("LEFT", tab.leftInactive, "RIGHT")
359 tab.middleInactive:SetTexCoord(0.15625, 0.84375, 0, 1.0)
361 tab.rightInactive = tab:CreateTexture(nil, "ARTWORK")
362 tab.rightInactive:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-InActiveTab")
363 tab.rightInactive:SetHeight(32)
364 tab.rightInactive:SetWidth(20)
365 tab.rightInactive:SetPoint("LEFT", tab.middleInactive, "RIGHT")
366 tab.rightInactive:SetTexCoord(0.84375, 1.0, 0, 1.0)
368 frame.totalTabs = frame.totalTabs + 1
369 frame.tabs[id] = tab
372 tab:SetText(text)
373 tab:Show()
375 tabDeselected(tab)
376 resizeTab(tab)
378 if( id == 1 ) then
379 tab:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 15, 11)
380 else
381 tab:SetPoint("TOPLEFT", frame.tabs[id - 1], "TOPRIGHT", -8, 0)
385 -- SCROLL FRAME
386 local function onVerticalScroll(self, offset)
387 offset = ceil(offset)
389 self.bar:SetValue(offset)
390 self.offset = ceil((offset / self.displayNum))
392 if( self.offset < 0 ) then
393 self.offset = 0
396 local min, max = self.bar:GetMinMaxValues()
398 if( min == offset ) then
399 self.up:Disable()
400 else
401 self.up:Enable()
404 if( max == offset ) then
405 self.down:Disable()
406 else
407 self.down:Enable()
410 self.updateFunc()
413 local function onMouseWheel(self, offset)
414 if( offset > 0 ) then
415 self.bar:SetValue(self.bar:GetValue() - (self.bar:GetHeight() / 2))
416 else
417 self.bar:SetValue(self.bar:GetValue() + (self.bar:GetHeight() / 2))
421 local function onParentMouseWheel(self, offset)
422 onMouseWheel(self.scroll, offset)
425 local function updateScroll(scroll, totalRows)
426 local max = (totalRows - scroll.displayNum) * scroll.displayNum
428 -- Macs are unhappy if max is less then the min
429 if( max < 0 ) then
430 max = 0
433 scroll.bar:SetMinMaxValues(0, max)
435 if( totalRows > scroll.displayNum ) then
436 scroll:Show()
437 scroll.bar:Show()
438 scroll.up:Show()
439 scroll.down:Show()
440 scroll.bar:GetThumbTexture():Show()
441 else
442 scroll:Hide()
443 scroll.bar:Hide()
444 scroll.up:Hide()
445 scroll.down:Hide()
446 scroll.bar:GetThumbTexture():Hide()
450 local function createScrollFrame(frame, displayNum, onScroll)
451 frame:EnableMouseWheel(true)
452 frame:SetScript("OnMouseWheel", onParentMouseWheel)
454 frame.scroll = CreateFrame("ScrollFrame", nil, frame)
455 frame.scroll:EnableMouseWheel(true)
456 frame.scroll:SetWidth(16)
457 frame.scroll:SetHeight(270)
458 frame.scroll:SetScript("OnVerticalScroll", onVerticalScroll)
459 frame.scroll:SetScript("OnMouseWheel", onMouseWheel)
461 frame.scroll.offset = 0
462 frame.scroll.displayNum = displayNum
463 frame.scroll.updateFunc = onScroll
465 -- Actual bar for scrolling
466 frame.scroll.bar = CreateFrame("Slider", nil, frame.scroll)
467 frame.scroll.bar:SetValueStep(frame.scroll.displayNum)
468 frame.scroll.bar:SetMinMaxValues(0, 0)
469 frame.scroll.bar:SetValue(0)
470 frame.scroll.bar:SetWidth(16)
471 frame.scroll.bar:SetScript("OnValueChanged", function(self, offset)
472 self:GetParent():SetVerticalScroll(offset)
473 end)
474 frame.scroll.bar:SetPoint("TOPLEFT", frame.scroll, "TOPRIGHT", 6, -16)
475 frame.scroll.bar:SetPoint("BOTTOMLEFT", frame.scroll, "BOTTOMRIGHT", 6, -16)
477 -- Up/Down buttons
478 frame.scroll.up = CreateFrame("Button", nil, frame.scroll.bar, "UIPanelScrollUpButtonTemplate")
479 frame.scroll.up:ClearAllPoints()
480 frame.scroll.up:SetPoint( "BOTTOM", frame.scroll.bar, "TOP" )
481 frame.scroll.up:SetScript("OnClick", function(self)
482 local parent = self:GetParent()
483 parent:SetValue(parent:GetValue() - (parent:GetHeight() / 2))
484 PlaySound("UChatScrollButton")
485 end)
487 frame.scroll.down = CreateFrame("Button", nil, frame.scroll.bar, "UIPanelScrollDownButtonTemplate")
488 frame.scroll.down:ClearAllPoints()
489 frame.scroll.down:SetPoint( "TOP", frame.scroll.bar, "BOTTOM" )
490 frame.scroll.down:SetScript("OnClick", function(self)
491 local parent = self:GetParent()
492 parent:SetValue(parent:GetValue() + (parent:GetHeight() / 2))
493 PlaySound("UChatScrollButton")
494 end)
496 -- That square thingy that shows where the bar is
497 frame.scroll.bar:SetThumbTexture("Interface\\Buttons\\UI-ScrollBar-Knob")
498 local thumb = frame.scroll.bar:GetThumbTexture()
500 thumb:SetHeight(16)
501 thumb:SetWidth(16)
502 thumb:SetTexCoord(0.25, 0.75, 0.25, 0.75)
504 -- Border graphic
505 frame.scroll.barUpTexture = frame.scroll:CreateTexture(nil, "BACKGROUND")
506 frame.scroll.barUpTexture:SetWidth(31)
507 frame.scroll.barUpTexture:SetHeight(256)
508 frame.scroll.barUpTexture:SetPoint("TOPLEFT", frame.scroll.up, "TOPLEFT", -7, 5)
509 frame.scroll.barUpTexture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar")
510 frame.scroll.barUpTexture:SetTexCoord(0, 0.484375, 0, 1.0)
512 frame.scroll.barDownTexture = frame.scroll:CreateTexture(nil, "BACKGROUND")
513 frame.scroll.barDownTexture:SetWidth(31)
514 frame.scroll.barDownTexture:SetHeight(106)
515 frame.scroll.barDownTexture:SetPoint("BOTTOMLEFT", frame.scroll.down, "BOTTOMLEFT", -7, -3)
516 frame.scroll.barDownTexture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-ScrollBar")
517 frame.scroll.barDownTexture:SetTexCoord(0.515625, 1.0, 0, 0.4140625)
520 -- SEARCH INPUt
521 local function focusGained(self)
522 if( self.searchText ) then
523 self.searchText = nil
524 self:SetText("")
525 self:SetTextColor(1, 1, 1, 1)
529 local function focusLost(self)
530 if( not self.searchText and string.trim(self:GetText()) == "" ) then
531 self.searchText = true
532 self:SetText(L["SEARCH"])
533 self:SetTextColor(0.90, 0.90, 0.90, 0.80)
537 local function createSearchInput(frame, onChange)
538 frame.search = CreateFrame("EditBox", nil, frame, "InputBoxTemplate")
539 frame.search:SetHeight(19)
540 frame.search:SetWidth(150)
541 frame.search:SetAutoFocus(false)
542 frame.search:ClearAllPoints()
543 frame.search:SetPoint("CENTER", frame, "BOTTOMLEFT", 100, 25)
545 frame.search.searchText = true
546 frame.search:SetText(L["SEARCH"])
547 frame.search:SetTextColor(0.90, 0.90, 0.90, 0.80)
548 frame.search:SetScript("OnTextChanged", onChange)
549 frame.search:SetScript("OnEditFocusGained", focusGained)
550 frame.search:SetScript("OnEditFocusLost", focusLost)
553 -- ADDON CONFIGURATION
554 local function showTooltip(self)
555 if( self.tooltip ) then
556 GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
557 GameTooltip:SetText(self.tooltip, nil, nil, nil, nil, 1)
561 local function hideTooltip()
562 GameTooltip:Hide()
565 local function sortCategories(a, b)
566 if( not b ) then
567 return false
570 return ( a.name < b.name )
573 -- Adds the actual row, will attempt to reuse the current row if able to
574 local function addCategoryRow(type, name, tooltip, data, parent, addon)
575 local frame = regFrames.addon
576 for i=1, #(frame.categories) do
577 -- Match type/name first
578 if( frame.categories[i].type == type and frame.categories[i].name == name ) then
579 -- Then make sure it's correct addons parent, if it's a category
580 if( (parent and frame.categories[i].parent and frame.categories[i].parent == parent) or (not parent and not frame.categories[i].parent) ) then
581 -- Now make sure it's the correct addon if it's a sub category
582 if( (addon and frame.categories[i].addon and frame.categories[i].addon == addon) or (not addon and not frame.categories[i].addon) ) then
583 frame.categories[i].tooltip = tooltip
584 frame.categories[i].data = data
585 return
591 table.insert(frame.categories, {name = name, type = type, tooltip = tooltip, data = data, parent = parent, addon = addon} )
592 frame.resortList = true
595 -- This removes the entire addon, we don't use this unless
596 -- we're removing the last category
597 local function removeAddonListing(addon)
598 local frame = regFrames.addon
599 for i=#(frame.categories), 1, -1 do
600 if( frame.categories[i].addon == addon ) then
601 table.remove(frame.categories, i)
606 -- Remove a specific category and/or sub category listing
607 -- without needing to recreate the entire list
608 local function removeCategoryListing(addon, name)
609 local frame = regFrames.addon
610 for i=#(frame.categories), 1, -1 do
611 -- Remove the category requested
612 if( frame.categories[i].type == "category" and frame.categories[i].name == name and frame.categories[i].addon == addon ) then
613 table.remove(frame.categories, i)
615 -- Remove all of it's sub categories
616 elseif( frame.categories[i].type == "subcat" and frame.categories[i].parent == name and frame.categories[i].addon == addon ) then
617 table.remove(frame.categories, i)
622 local function removeSubCategoryListing(addon, parentCat, name)
623 local frame = regFrames.addon
624 for i=#(frame.categories), 1, -1 do
625 -- Remove the specific sub category
626 if( frame.categories[i].type == "subcat" and frame.categories[i].name == name and frame.categories[i].parent == parentCat and frame.categories[i].addon == addon ) then
627 table.remove(frame.categories, i)
632 -- We have a seperate function for adding addons
633 -- so we can update a single addon out of the entire list
634 -- if it's categories/sub categories get changed, or a new ones added
635 local function addCategoryListing(name, addon)
636 local tooltip = "|cffffffff" .. (addon.title or name) .. "|r"
637 local data
639 if( addon.version ) then
640 tooltip = tooltip .. "\n" .. string.format(L["VERSION"], addon.version)
643 if( addon.author ) then
644 tooltip = tooltip .. "\n" .. string.format(L["AUTHOR"], addon.author)
647 -- One category, make clicking the addon open that category
648 if( addon.totalCats == 1 and addon.totalSubs == 0 ) then
649 for catName, cat in pairs(addon.categories) do
650 data = cat
651 data.parentCat = catName
652 break
655 -- Multiple categories, or sub categories
656 else
657 for catName, cat in pairs(addon.categories) do
658 cat.parentCat = catName
659 addCategoryRow("category", catName, cat.totalSubs > 0 and string.format(L["TOTAL_SUBCATEGORIES"], cat.totalSubs), cat, name, name)
661 for subCatName, subCat in pairs(cat.sub) do
662 subCat.parentCat = catName
663 addCategoryRow("subcat", subCatName, nil, subCat, catName, name)
668 if( not data ) then
669 data = {}
672 data.orderID = string.lower(name)
674 addCategoryRow("addon", name, (addon.version or addon.author) and tooltip, data, nil, name)
677 -- Recreates the entire listing
678 local function createCategoryListing()
679 regFrames.addon.categories = {}
681 for name, addon in pairs(addons) do
682 addCategoryListing(name, addon)
686 -- Displays the actual button
687 local function displayCategoryRow(type, text, data, tooltip, highlighted)
688 local frame = regFrames.addon
690 -- We have to let this run completely
691 -- so we know how many rows we have total
692 frame.totalRows = frame.totalRows + 1
693 if( frame.totalRows <= frame.offset or frame.rowID >= 15 ) then
694 return
697 frame.rowID = frame.rowID + 1
699 local button = frame.buttons[frame.rowID]
700 local line = frame.lines[frame.rowID]
702 if( highlighted ) then
703 button:LockHighlight()
704 else
705 button:UnlockHighlight()
708 if( type == "addon" ) then
709 button:SetText(text)
710 button:GetFontString():SetPoint("LEFT", button, "LEFT", 4, 0)
711 button:GetNormalTexture():SetAlpha(1.0)
712 line:Hide()
714 elseif( type == "category" ) then
715 button:SetText(HIGHLIGHT_FONT_COLOR_CODE..text..FONT_COLOR_CODE_CLOSE)
716 button:GetFontString():SetPoint("LEFT", button, "LEFT", 12, 0)
717 button:GetNormalTexture():SetAlpha(0.4)
718 line:Hide()
720 elseif( type == "subcat" ) then
721 button:SetText(HIGHLIGHT_FONT_COLOR_CODE..text..FONT_COLOR_CODE_CLOSE)
722 button:GetFontString():SetPoint("LEFT", button, "LEFT", 20, 0)
723 button:GetNormalTexture():SetAlpha(0.0)
724 line:SetTexCoord(0, 0.4375, 0, 0.625)
725 line:Show()
728 button.tooltip = tooltip
729 button.data = data
730 button.type = type
731 button.catText = text
732 button:Show()
735 local function updateConfigList()
736 local frame = regFrames.addon
737 frame.offset = frame.scroll.offset
738 frame.rowID = 0
739 frame.totalRows = 0
741 local lastID
742 local searchBy = string.trim(string.lower(frame.search:GetText()))
743 if( searchBy == "" or frame.search.searchText ) then
744 searchBy = nil
747 -- Make sure stuff matches our search results
748 for id, row in pairs(frame.categories) do
749 if( searchBy and not string.match(string.lower(row.name), searchBy) ) then
750 frame.categories[id].hide = true
751 else
752 frame.categories[id].hide = nil
756 -- Resort list if needed
757 if( frame.resortList ) then
758 table.sort(frame.categories, sortCategories)
759 frame.resortList = nil
762 -- Now display
763 for _, addon in pairs(frame.categories) do
764 if( not addon.hide and addon.type == "addon" ) then
765 -- Total addons
766 if( addon.name == frame.selectedAddon ) then
767 displayCategoryRow(addon.type, addon.name, addon.data, addon.tooltip, true)
769 for _, cat in pairs(frame.categories) do
770 -- Show all the categories with the addon as the parent
771 if( not cat.hide and cat.parent == addon.name and cat.type == "category" ) then
772 -- Total categories of the selected addon
773 if( cat.name == frame.selectedCategory ) then
774 displayCategoryRow(cat.type, cat.name, cat.data, cat.tooltip, true)
776 local rowID
777 for _, subCat in pairs(frame.categories) do
778 -- We don't have to check type, because it's the only one that has .addon set
779 if( not subCat.hide and subCat.parent == cat.name and subCat.addon == addon.name ) then
780 -- Total sub categories of the selected addons selected category
781 displayCategoryRow(subCat.type, subCat.name, subCat.data, subCat.tooltip, subCat.name == frame.selectedSubCat)
782 lastID = frame.rowID
786 -- Turns the line from straight down to a curve at the end
787 if( lastID ) then
788 frame.lines[lastID]:SetTexCoord(0.4375, 0.875, 0, 0.625)
790 else
791 displayCategoryRow(cat.type, cat.name, cat.data, cat.tooltip)
795 else
796 displayCategoryRow(addon.type, addon.name, addon.data, addon.tooltip)
801 updateScroll(frame.scroll, frame.totalRows)
803 for i=1, 15 do
804 if( frame.totalRows > 15 ) then
805 frame.buttons[i]:SetWidth(140)
806 else
807 frame.buttons[i]:SetWidth(156)
810 -- We have less then 15 rows used
811 -- and our index is equal or past our current
812 if( frame.rowID < 15 and i > frame.rowID ) then
813 frame.buttons[i]:Hide()
818 local function openConfigFrame(data)
819 local frame = regFrames.addon
821 -- Clicking on an addon with multiple categories or sub categories will cause no data
822 if( not data ) then
823 -- Make sure the frames hidden when only the addon button is selected
824 if( frame.shownFrame ) then
825 frame.shownFrame:Hide()
827 return
830 if( data.handler or data.func ) then
831 data.frame = nil
833 if( type(data.func) == "string" ) then
834 data.frame = data.handler[data.func](data.handler, data.parentCat or frame.selectedCategory, frame.selectedSubCat)
835 elseif( type(data.handler) == "function" ) then
836 data.frame = data.handler(data.parentCat or frame.selectedCategory, frame.selectedSubCat)
839 -- Mostly this is for authors, but it lets us clean up the logic a bit
840 if( not data.frame ) then
841 error(string.format(L["ERROR_NO_FRAME"], frame.selectedAddon, data.parentCat or frame.selectedCategory, frame.selectedSubCat), 3)
844 -- Validate location/width/height and force parent
845 if( not data.frame:GetPoint() ) then
846 data.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 190, -103)
849 if( data.frame:GetWidth() > 630 or data.frame:GetWidth() == 0 ) then
850 data.frame:SetWidth(630)
853 if( data.frame:GetHeight() > 305 or data.frame:GetHeight() == 0 ) then
854 data.frame:SetHeight(305)
857 data.frame:SetParent(frame)
858 data.frame:SetFrameStrata("HIGH")
860 if( not data.noCache ) then
861 local category
863 -- Figure out which category we're modifying
864 if( frame.selectedSubCat ~= "" ) then
865 category = addons[frame.selectedAddon].categories[frame.selectedCategory].sub[frame.selectedSubCat]
866 elseif( frame.selectedCategory ~= "" ) then
867 category = addons[frame.selectedAddon].categories[frame.selectedCategory]
868 elseif( frame.selectedAddon ~= "" ) then
869 for catName, _ in pairs(addons[frame.selectedAddon].categories) do
870 category = addons[frame.selectedAddon].categories[catName]
874 -- Remove the handler/func and save the frame for next time
875 if( category ) then
876 category.handler = nil
877 category.func = nil
878 category.frame = data.frame
883 if( frame.shownFrame ) then
884 frame.shownFrame:Hide()
887 -- Now show the current one
888 if( data.frame and frame.selectedAddon ~= "" ) then
889 data.frame:Show()
890 frame.shownFrame = data.frame
894 local function expandConfigList(self)
895 local frame = regFrames.addon
897 if( self.type == "addon" ) then
898 if( frame.selectedAddon == self.catText ) then
899 frame.selectedAddon = ""
900 else
901 frame.selectedAddon = self.catText
904 frame.selectedCategory = ""
905 frame.selectedSubCat = ""
907 elseif( self.type == "category" ) then
908 if( frame.selectedCategory == self.catText ) then
909 frame.selectedCategory = ""
910 self.data = nil
911 else
912 frame.selectedCategory = self.catText
915 frame.selectedSubCat = ""
917 elseif( self.type == "subcat" ) then
918 if( frame.selectedSubCat == self.catText ) then
919 frame.selectedSubCat = ""
921 -- Make sure the frame gets hidden when deselecting
922 self.data = addons[frame.selectedAddon].categories[frame.selectedCategory]
923 else
924 frame.selectedSubCat = self.catText
928 openConfigFrame(self.data)
929 updateConfigList()
933 local function createAddonFrame(hide)
934 local frame = regFrames.addon
936 if( frame and hide ) then
937 frame:Hide()
938 return
939 elseif( hide ) then
940 return
941 elseif( not frame ) then
942 frame = CreateFrame("Frame", nil, regFrames.main)
943 frame:SetFrameStrata("MEDIUM")
944 frame:SetAllPoints(regFrames.main)
946 regFrames.addon = frame
948 frame.buttons = {}
949 frame.lines = {}
950 for i=1, 15 do
951 local button = CreateFrame("Button", nil, frame)
952 frame.buttons[i] = button
954 button:SetHighlightFontObject(GameFontHighlightSmall)
955 button:SetTextFontObject(GameFontNormalSmall)
956 button:SetScript("OnClick", expandConfigList)
957 button:SetScript("OnEnter", showTooltip)
958 button:SetScript("OnLeave", hideTooltip)
959 button:SetWidth(140)
960 button:SetHeight(20)
962 button:SetNormalTexture("Interface\\AuctionFrame\\UI-AuctionFrame-FilterBG")
963 button:GetNormalTexture():SetTexCoord(0, 0.53125, 0, 0.625)
965 button:SetHighlightTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight")
966 button:GetHighlightTexture():SetBlendMode("ADD")
968 -- For sub categories only
969 local line = button:CreateTexture(nil, "BACKGROUND")
970 frame.lines[i] = line
972 line:SetWidth(7)
973 line:SetHeight(20)
974 line:SetPoint("LEFT", 13, 0)
975 line:SetTexture("Interface\\AuctionFrame\\UI-AuctionFrame-FilterLines")
976 line:SetTexCoord(0, 0.4375, 0, 0.625)
978 if( i > 1 ) then
979 button:SetPoint("TOPLEFT", frame.buttons[i - 1], "BOTTOMLEFT", 0, 0)
980 else
981 button:SetPoint("TOPLEFT", 23, -105)
985 createScrollFrame(frame, 15, updateConfigList)
986 frame.scroll:SetPoint("TOPRIGHT", frame, "TOPLEFT", 158, -105)
988 createSearchInput(frame, updateConfigList)
989 createCategoryListing()
992 -- Reset selection
993 frame.selectedAddon = ""
994 frame.selectedCategory = ""
995 frame.selectedSubCat = ""
997 -- Hide the open config frame
998 if( frame.shownFrame ) then
999 frame.shownFrame:Hide()
1002 updateConfigList()
1003 ShowUIPanel(frame)
1006 local function createOHFrame()
1007 if( regFrames.main ) then
1008 return
1011 frame = CreateFrame("Frame", nil, UIParent)
1012 frame:CreateTitleRegion()
1013 frame:SetClampedToScreen(true)
1014 frame:SetMovable(false)
1015 frame:SetFrameStrata("MEDIUM")
1016 frame:SetWidth(832)
1017 frame:SetHeight(447)
1018 frame:SetPoint("TOPLEFT", 0, -104)
1019 frame.totalTabs = 0
1020 frame.tabs = {}
1022 regFrames.main = frame
1024 -- If we don't hide it ourself, the panel layout becomes messed up
1025 frame:Hide()
1027 frame:SetAttribute("UIPanelLayout-defined", true)
1028 frame:SetAttribute("UIPanelLayout-enabled", true)
1029 --~ frame:SetAttribute("UIPanelLayout-area", "doublewide") -- This is broken in the Blizzy code >< Slouken's been sent a fix
1030 frame:SetAttribute("UIPanelLayout-area", "left")
1031 frame:SetAttribute("UIPanelLayout-whileDead", true)
1032 table.insert(UISpecialFrames, name)
1034 local title = frame:GetTitleRegion()
1035 title:SetWidth(757)
1036 title:SetHeight(20)
1037 title:SetPoint("TOPLEFT", 75, -15)
1039 -- Embedded version wont include the icon cause authors are more whiny then users
1040 -- Also, we want to use different methods of frame dragging
1041 if( not IsAddOnLoaded("OptionHouse") ) then
1042 local texture = frame:CreateTexture(nil, "OVERLAY")
1043 texture:SetWidth(57)
1044 texture:SetHeight(57)
1045 texture:SetPoint("TOPLEFT", 9, -7)
1046 SetPortraitTexture(texture, "player")
1047 else
1048 local texture = frame:CreateTexture(nil, "OVERLAY")
1049 texture:SetWidth(128)
1050 texture:SetHeight(128)
1051 texture:SetPoint("TOPLEFT", 9, -2)
1052 texture:SetTexture("Interface\\AddOns\\OptionHouse\\GnomePortrait")
1055 local title = frame:CreateFontString(nil, "OVERLAY")
1056 title:SetFontObject(GameFontNormal)
1057 title:SetPoint("TOP", 0, -18)
1058 title:SetText(L["OPTION_HOUSE"])
1060 frame.topLeft = frame:CreateTexture(nil, "ARTWORK")
1061 frame.topLeft:SetWidth(256)
1062 frame.topLeft:SetHeight(256)
1063 frame.topLeft:SetPoint("TOPLEFT", 0, 0)
1065 frame.top = frame:CreateTexture(nil, "ARTWORK")
1066 frame.top:SetWidth(320)
1067 frame.top:SetHeight(256)
1068 frame.top:SetPoint("TOPLEFT", 256, 0)
1070 frame.topRight = frame:CreateTexture(nil, "ARTWORK")
1071 frame.topRight:SetWidth(256)
1072 frame.topRight:SetHeight(256)
1073 frame.topRight:SetPoint("TOPLEFT", frame.top, "TOPRIGHT", 0, 0)
1075 frame.bottomLeft = frame:CreateTexture(nil, "ARTWORK")
1076 frame.bottomLeft:SetWidth(256)
1077 frame.bottomLeft:SetHeight(256)
1078 frame.bottomLeft:SetPoint("TOPLEFT", 0, -256)
1080 frame.bottom = frame:CreateTexture(nil, "ARTWORK")
1081 frame.bottom:SetWidth(320)
1082 frame.bottom:SetHeight(256)
1083 frame.bottom:SetPoint("TOPLEFT", 256, -256)
1085 frame.bottomRight = frame:CreateTexture(nil, "ARTWORK")
1086 frame.bottomRight:SetWidth(256)
1087 frame.bottomRight:SetHeight(256)
1088 frame.bottomRight:SetPoint("TOPLEFT", frame.bottom, "TOPRIGHT", 0, 0)
1090 -- Make sure the configuration tab is first
1091 local tabs = {{func = createAddonFrame, text = L["ADDON_OPTIONS"], type = "browse"}}
1092 createTab(L["ADDON_OPTIONS"], 1)
1094 for id, tab in pairs(tabfunctions) do
1095 table.insert(tabs, tab)
1096 createTab(tab.text, id + 1)
1099 tabfunctions = tabs
1101 local button = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
1102 button:SetPoint("TOPRIGHT", 3, -8)
1103 button:SetScript("OnClick", function()
1104 HideUIPanel(frame)
1105 end)
1108 -- PRIVATE API's
1109 -- These are only to be used for the standalone OptionHouse modules
1110 function OptionHouse:CreateSearchInput(frame, onChange)
1111 createSearchInput(frame, onChange)
1114 function OptionHouse:UpdateScroll(scroll, totalRows)
1115 updateScroll(scroll, totalRows)
1118 function OptionHouse:CreateScrollFrame(frame, displayNum, onScroll)
1119 createScrollFrame(frame, displayNum, onScroll)
1122 function OptionHouse.RegisterTab(self, text, func, type)
1123 -- Simple, effective you can't register a tab unless we list it here
1124 -- I highly doubt will ever need to add another one
1125 if( text ~= L["TAB_MANAGEMENT"] and text ~= L["TAB_PERFORMANCE"] ) then return end
1127 table.insert(tabfunctions, {func = func, handler = self, text = text, type = type})
1129 -- Will create all of the tabs when the frame is created if needed
1130 if( not frame ) then
1131 return
1134 createTab(text, #(tabfunctions))
1137 function OptionHouse.UnregisterTab(self, text)
1138 for i=#(tabfunctions), 1, -1 do
1139 if( tabfunctions[i].text == text ) then
1140 table.remove(tabfunctions, i)
1144 for i=1, frame.totalTabs do
1145 if( tabfunctions[i] ) then
1146 createTab(tabfunctions[i].text, i)
1147 else
1148 frame.tabs[i]:Hide()
1153 function OptionHouse.GetAddOnData(self, name)
1154 if( not addons[name] ) then
1155 return nil, nil, nil
1158 return addons[name].title, addons[name].author, addons[name].version
1161 function OptionHouse.RegisterFrame(self, type, frame)
1162 if( type ~= "addon" and type ~= "manage" and type ~= "perf" and type ~= "main" ) then return end
1163 regFrames[type] = frame
1166 -- PUBLIC API's
1167 function OptionHouse:GetFrame(type)
1168 if( type ~= "addon" and type ~= "manage" and type ~= "perf" and type ~= "main" ) then
1169 error(string.format(L["UNKNOWN_FRAMETYPE"], type), 3)
1172 return regFrames[type]
1175 function OptionHouse:Open(addonName, parentCat, childCat)
1176 argcheck(addonName, 1, "string", "nil")
1177 argcheck(parentCat, 2, "string", "nil")
1178 argcheck(childCat, 3, "string", "nil")
1180 createOHFrame()
1181 tabOnClick(1)
1183 if( not addonName ) then
1184 ShowUIPanel(frame)
1185 return
1188 -- Cleanest method for getting the func/handler/ect
1189 -- to auto open to a page
1190 for name, addon in pairs(addons) do
1191 if( name == addonName ) then
1192 regFrames.addon.selectedAddon = addonName
1193 for catName, cat in pairs(addon.categories) do
1194 if( catName == parentCat ) then
1195 regFrames.addon.selectedCategory = catName
1196 -- Searching for a sub cat
1197 if( subCat and cat.totalSubs > 0 ) then
1198 for subCatName, subCat in pairs(cat.sub) do
1199 -- Found sub cat, open it
1200 if( subCatName == childCat ) then
1201 regFrames.addon.selectedSubCat = subCatName
1202 openConfigFrame(subCat)
1203 break
1207 -- Searching for a category not a sub cat
1208 elseif( not subCat ) then
1209 openConfigFrame(cat)
1211 break
1214 break
1218 -- Now expand anything that was selected
1219 updateConfigList()
1220 ShowUIPanel(frame)
1223 function OptionHouse:OpenTab(id)
1224 argcheck(id, 1, "number")
1225 assert(3, #(tabfunctions) > id, string.format(L["UNKNOWN_TAB"], id, #(tabfunctions)))
1227 createOHFrame()
1228 tabOnClick(id)
1229 ShowUIPanel(frame)
1232 function OptionHouse:RegisterAddOn(name, title, author, version)
1233 argcheck(name, 1, "string")
1234 argcheck(title, 2, "string", "nil")
1235 argcheck(author, 3, "string", "nil")
1236 argcheck(version, 4, "string", "number", "nil")
1237 assert(3, not addons[name], string.format(L["ADDON_ALREADYREG"], name))
1239 addons[name] = {title = title, author = author, version = version, totalCats = 0, totalSubs = 0, categories = {}}
1240 addons[name].obj = {name = name}
1242 -- So we can upgrade the function pointer if a newer rev is found
1243 for id, method in pairs(methods) do
1244 addons[name].obj[method] = OptionHouse[method]
1247 if( regFrames.addon ) then
1248 addCategoryListing(name, addons[name])
1249 updateConfigList()
1252 return addons[name].obj
1255 function OptionHouse.RegisterCategory(addon, name, handler, func, noCache)
1256 argcheck(name, 2, "string")
1257 argcheck(handler, 3, "string", "function", "table")
1258 argcheck(func, 4, "string", "function", "nil")
1259 argcheck(noCache, 5, "boolean", "number", "nil")
1260 assert(3, handler or func, L["NO_FUNC_PASSED"])
1261 assert(3, addons[addon.name], string.format(L["MUST_CALL"], addon.name))
1262 assert(3, addons[addon.name].categories, string.format(L["CATEGORY_ALREADYREG"], name, addon.name))
1264 -- Category numbers are required so we know when to skip it because only one category/sub cat exists
1265 addons[addon.name].totalCats = addons[addon.name].totalCats + 1
1266 addons[addon.name].categories[name] = {func = func, handler = handler, noCache = noCache, sub = {}, orderID = addons[addon.name].totalCats, totalSubs = 0}
1268 if( regFrames.addon ) then
1269 addCategoryListing(addon.name, addons[addon.name])
1270 updateConfigList()
1274 function OptionHouse.RegisterSubCategory(addon, parentCat, name, handler, func, noCache)
1275 argcheck(parentCat, 2, "string")
1276 argcheck(name, 3, "string")
1277 argcheck(handler, 4, "string", "function", "table")
1278 argcheck(func, 5, "string", "function", "nil")
1279 argcheck(noCache, 6, "boolean", "number", "nil")
1280 assert(3, handler or func, L["NO_FUNC_PASSED"])
1281 assert(3, addons[addon.name], string.format(L["MUST_CALL"], addon.name))
1282 assert(3, addons[addon.name].categories[parentCat], string.format(L["NO_PARENTCAT"], parentCat, addon.name))
1283 assert(3, not addons[addon.name].categories[parentCat].sub[name], string.format(L["SUBCATEGORY_ALREADYREG"], name, parentCat, addon.name))
1285 addons[addon.name].totalSubs = addons[addon.name].totalSubs + 1
1286 addons[addon.name].categories[parentCat].totalSubs = addons[addon.name].categories[parentCat].totalSubs + 1
1287 addons[addon.name].categories[parentCat].sub[name] = {handler = handler, func = func, noCache = noCache, orderID = addons[addon.name].categories[parentCat].totalSubs}
1289 if( regFrames.addon ) then
1290 addCategoryListing(addon.name, addons[addon.name])
1291 updateConfigList()
1295 function OptionHouse.RemoveCategory(addon, name)
1296 argcheck(name, 2, "string")
1297 assert(3, addons[addon.name], string.format(L["MUST_CALL"], addon.name))
1298 assert(3, addons[addon.name].categories[name], string.format(L["NO_CATEGORYEXISTS"], name, addon.name))
1300 addons[addon.name].totalCats = addons[addon.name].totalCats - 1
1301 addons[addon.name].totalSubs = addons[addon.name].totalSubs - addons[addon.name].categories[name].totalSubs
1302 addons[addon.name].categories[name] = nil
1304 if( regFrames.addon ) then
1305 if( addons[addon.name].totalCats == 0 ) then
1306 removeAddonListing(addon.name)
1307 else
1308 removeCategoryListing(addon.name, name)
1311 updateConfigList()
1315 function OptionHouse.RemoveSubCategory(addon, parentCat, name)
1316 argcheck(parentCat, 2, "string")
1317 argcheck(name, 2, "string")
1318 assert(3, addons[addon.name], string.format(L["MUST_CALL"], addon.name))
1319 assert(3, addons[addon.name].categories[parentCat], string.format(L["NO_PARENTCAT"], name, addon.name))
1320 assert(3, addons[addon.name].categories[parentCat].sub[name], string.format(L["NO_SUBCATEXISTS"], name, parentCat, addon.name))
1322 addons[addon.name].totalSubs = addons[addon.name].totalSubs - 1
1323 addons[addon.name].categories[parentCat].totalSubs = addons[addon.name].categories[parentCat].totalSubs - 1
1324 addons[addon.name].categories[parentCat].sub[name] = nil
1326 if( regFrames.addon ) then
1327 -- If this means we only have no more sub categories
1328 -- and only one category we need to change how it works
1329 if( addons[addon.name].totalSubs == 0 and addons[addon.name].totalCats == 1 ) then
1330 removeAddonListing(addon.name)
1331 addCategoryListing(addon.name, addons[addon.name])
1332 else
1333 removeSubCategoryListing(addon.name, parentCat, name)
1336 updateConfigList()
1340 function OptionHouse:GetVersion() return major, minor end
1342 local function Activate(self, old)
1343 if( old ) then
1344 addons = old.addons or addons
1345 evtFrame = old.evtFrame or evtFrame
1346 tabfunctions = old.tabfunctions or tabfunctions
1347 else
1348 -- Secure headers are supported so don't want the window stuck open in combat
1349 evtFrame = CreateFrame("Frame")
1350 evtFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
1351 evtFrame:SetScript("OnEvent",function()
1352 if( frame and frame:IsShown() ) then
1353 HideUIPanel(frame)
1354 DEFAULT_CHAT_FRAME:AddMessage(L["ENTERED_COMBAT"])
1356 end)
1358 -- Make sure it hasn't been created already.
1359 -- don't have to upgrade the referance because it just uses the slash command
1360 -- which will upgrade below to use the current version anyway
1361 if( not GameMenuButtonOptionHouse ) then
1362 local menubutton = CreateFrame("Button", "GameMenuButtonOptionHouse", GameMenuFrame, "GameMenuButtonTemplate")
1363 menubutton:SetText(L["OPTION_HOUSE"])
1364 menubutton:SetScript("OnClick", function()
1365 PlaySound("igMainMenuOption")
1366 HideUIPanel(GameMenuFrame)
1367 SlashCmdList["OPTHOUSE"]()
1368 end)
1370 -- Position below "Interface Options"
1371 local a1, fr, a2, x, y = GameMenuButtonKeybindings:GetPoint()
1372 menubutton:SetPoint(a1, fr, a2, x, y)
1374 GameMenuButtonKeybindings:SetPoint(a1, menubutton, a2, x, y)
1375 GameMenuFrame:SetHeight(GameMenuFrame:GetHeight() + 25)
1379 self.addons = addons
1380 self.evtFrame = evtFrame
1381 self.tabfunctions = tabfunctions
1383 -- Upgrade functions to point towards the latest revision
1384 for name, addon in pairs(addons) do
1385 for _, method in pairs(methods) do
1386 addon.obj[method] = OptionHouse[method]
1390 SLASH_OPTHOUSE1 = "/opthouse"
1391 SLASH_OPTHOUSE2 = "/oh"
1392 SlashCmdList["OPTHOUSE"] = function(...)
1393 OptionHouse.Open(OptionHouse, ...)
1397 OptionHouse = DongleStub:Register(OptionHouse, Activate)