From 353820ede2116b3bf4e1e96be584962d8d657839 Mon Sep 17 00:00:00 2001 From: Manuel Simon Hirsig Date: Sun, 31 Jul 2016 19:21:46 +0200 Subject: [PATCH 1/9] working on new filter tab --- gui.lua | 139 ++++++++----------------- tables/auction_listing.lua | 6 +- tabs/post/frames.lua | 2 +- tabs/search/core.lua | 60 ++++++++++- tabs/search/frames.lua | 204 ++++++------------------------------- util/core.lua | 24 ++++- util/scan_util.lua | 10 +- 7 files changed, 159 insertions(+), 286 deletions(-) diff --git a/gui.lua b/gui.lua index 4680ef7..fa78a19 100644 --- a/gui.lua +++ b/gui.lua @@ -14,15 +14,6 @@ public.config = { off_color = {0.7, 0.3, 0.3}, } -do - local x = 0 - - function private.id() - x = x + 1 - return 'Aux_frame'..x - end -end - function public.inline_color(color) local r, g, b, a = unpack(color) return format('|c%02X%02X%02X%02X', a, r, g, b) @@ -329,7 +320,7 @@ function public.item(parent) local item = CreateFrame('Button', nil, parent) item:SetWidth(193) item:SetHeight(40) - local icon = CreateFrame('CheckButton', m.id(), item, 'ActionButtonTemplate') + local icon = CreateFrame('CheckButton', 'aux_frame'..Aux.unique(), item, 'ActionButtonTemplate') icon:SetPoint('LEFT', 2, 0.5) icon:SetHighlightTexture(nil) icon:RegisterForClicks() @@ -379,99 +370,53 @@ function public.vertical_line(parent, x_offset, top_offset, bottom_offset, inver return texture end -do - local id = 0 - function public.dropdown(parent) - id = id + 1 - local dropdown = CreateFrame('Frame', 'aux_dropdown'..id, parent, 'UIDropDownMenuTemplate') +function public.dropdown(parent) + local dropdown = CreateFrame('Frame', 'aux_frame'..Aux.unique(), parent, 'UIDropDownMenuTemplate') - dropdown:SetBackdrop{bgFile=[[Interface\Buttons\WHITE8X8]], edgeFile=[[Interface\Buttons\WHITE8X8]], edgeSize=m.config.edge_size, insets={top=5,bottom=5}} - dropdown:SetBackdropColor(unpack(m.config.content_color.backdrop)) - dropdown:SetBackdropBorderColor(unpack(m.config.content_color.border)) - local left = getglobal(dropdown:GetName()..'Left'):Hide() - local middle = getglobal(dropdown:GetName()..'Middle'):Hide() - local right = getglobal(dropdown:GetName()..'Right'):Hide() + dropdown:SetBackdrop{bgFile=[[Interface\Buttons\WHITE8X8]], edgeFile=[[Interface\Buttons\WHITE8X8]], edgeSize=m.config.edge_size, insets={top=5,bottom=5}} + dropdown:SetBackdropColor(unpack(m.config.content_color.backdrop)) + dropdown:SetBackdropBorderColor(unpack(m.config.content_color.border)) + local left = getglobal(dropdown:GetName()..'Left'):Hide() + local middle = getglobal(dropdown:GetName()..'Middle'):Hide() + local right = getglobal(dropdown:GetName()..'Right'):Hide() - local button = getglobal(dropdown:GetName()..'Button') - button:ClearAllPoints() - button:SetPoint('RIGHT', dropdown, 0, 0) + local button = getglobal(dropdown:GetName()..'Button') + button:ClearAllPoints() + button:SetPoint('RIGHT', dropdown, 0, 0) - local text = getglobal(dropdown:GetName()..'Text') - text:ClearAllPoints() - text:SetPoint('RIGHT', button, 'LEFT', -2, 0) - text:SetPoint('LEFT', dropdown, 'LEFT', 8, 0) - text:SetFont(m.config.content_font, 13) - text:SetShadowColor(0, 0, 0, 0) + local text = getglobal(dropdown:GetName()..'Text') + text:ClearAllPoints() + text:SetPoint('RIGHT', button, 'LEFT', -2, 0) + text:SetPoint('LEFT', dropdown, 'LEFT', 8, 0) + text:SetFont(m.config.content_font, 13) + text:SetShadowColor(0, 0, 0, 0) - -- - -- dropdown:ClearAllPoints() - -- dropdown:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -7, 0) - -- dropdown:SetScript("OnHide", nil) - -- dropdown:SetScript("OnEnter", Control_OnEnter) - -- dropdown:SetScript("OnLeave", Control_OnLeave) - -- dropdown:SetScript("OnMouseUp", function(self, button) Dropdown_TogglePullout(self.obj.button, button) end) - -- TSMAPI.Design:SetContentColor(dropdown) - -- - -- local left = _G[dropdown:GetName().."Left"] - -- local middle = _G[dropdown:GetName().."Middle"] - -- local right = _G[dropdown:GetName().."Right"] - -- - -- middle:ClearAllPoints() - -- right:ClearAllPoints() - -- - -- middle:SetPoint("LEFT", left, "RIGHT", 0, 0) - -- middle:SetPoint("RIGHT", right, "LEFT", 0, 0) - -- right:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", 0, 17) - -- - -- local button = _G[dropdown:GetName().."Button"] - -- button:RegisterForClicks("AnyUp") - -- button:SetScript("OnEnter", Control_OnEnter) - -- button:SetScript("OnLeave", Control_OnLeave) - -- button:SetScript("OnClick", Dropdown_TogglePullout) - -- button:ClearAllPoints() - -- button:SetPoint("RIGHT", dropdown, 0, 0) - -- - -- local text = _G[dropdown:GetName().."Text"] - -- text:ClearAllPoints() - -- text:SetPoint("RIGHT", button, "LEFT", -2, 0) - -- text:SetPoint("LEFT", dropdown, "LEFT", 8, 0) - -- text:SetFont(TSMAPI.Design:GetContentFont("normal")) - -- text:SetShadowColor(0, 0, 0, 0) - -- - -- local label = frame:CreateFontString(nil, "OVERLAY") - -- label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0) - -- label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, 0) - -- label:SetJustifyH("LEFT") - -- label:SetHeight(18) - -- label:SetFont(TSMAPI.Design:GetContentFont("small")) - -- label:SetShadowColor(0, 0, 0, 0) - -- label:Hide() - -- - -- left:Hide() - -- middle:Hide() - -- right:Hide() - -- - -- local widget = { - -- frame = frame, - -- label = label, - -- dropdown = dropdown, - -- text = text, - -- button = button, - -- count = count, - -- alignoffset = 30, - -- type = Type, - -- } - -- for method, func in pairs(methods) do - -- widget[method] = func - -- end - -- frame.obj = widget - -- dropdown.obj = widget - -- text.obj = widget - -- button.obj = widget +-- +-- dropdown:ClearAllPoints() +-- dropdown:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -7, 0) +-- dropdown:SetScript("OnHide", nil) +-- dropdown:SetScript("OnEnter", Control_OnEnter) +-- dropdown:SetScript("OnLeave", Control_OnLeave) +-- dropdown:SetScript("OnMouseUp", function(self, button) Dropdown_TogglePullout(self.obj.button, button) end) +-- TSMAPI.Design:SetContentColor(dropdown) +-- +-- middle:ClearAllPoints() +-- right:ClearAllPoints() +-- +-- middle:SetPoint("LEFT", left, "RIGHT", 0, 0) +-- middle:SetPoint("RIGHT", right, "LEFT", 0, 0) +-- right:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", 0, 17) +-- +-- local button = _G[dropdown:GetName().."Button"] +-- button:RegisterForClicks("AnyUp") +-- button:SetScript("OnEnter", Control_OnEnter) +-- button:SetScript("OnLeave", Control_OnLeave) +-- button:SetScript("OnClick", Dropdown_TogglePullout) +-- button:ClearAllPoints() +-- button:SetPoint("RIGHT", dropdown, 0, 0) - return dropdown - end + return dropdown end function public.slider(frame) diff --git a/tables/auction_listing.lua b/tables/auction_listing.lua index 7756cc0..0bfaf61 100644 --- a/tables/auction_listing.lua +++ b/tables/auction_listing.lua @@ -900,7 +900,7 @@ local methods = { end, RemoveAuctionRecord = function(self, record) - local index = Aux.util.index_of(record, self.records) + local index = Aux.util.key(record, self.records) if index then tremove(self.records, index) end @@ -910,7 +910,7 @@ local methods = { RemoveSelectedRecord = function(self, count) count = count or 1 for i=1, count do - local index = Aux.util.index_of(self.selected, self.records) + local index = Aux.util.key(self.selected, self.records) if index then tremove(self.records, index) end @@ -927,7 +927,7 @@ local methods = { end, ContainsRecord = function(rt, record) - if Aux.util.index_of(record, rt.records) then + if Aux.util.key(record, rt.records) then return true end end, diff --git a/tabs/post/frames.lua b/tabs/post/frames.lua index 93f24e5..72cafb8 100644 --- a/tabs/post/frames.lua +++ b/tabs/post/frames.lua @@ -75,7 +75,7 @@ Aux.post_tab.FRAMES(function(m, public, private) m.auction_listing:EnableSorting(false) m.auction_listing:DisableSelection(true) m.auction_listing:SetHandler('OnClick', function(table, row_data, column, button) - local column_index = Aux.util.index_of(column, column.row.cols) + local column_index = Aux.util.key(column, column.row.cols) local unit_start_price, unit_buyout_price = m.undercut(row_data.record, m.stack_size_slider:GetValue(), button == 'RightButton') if column_index == 3 then m.stack_size_slider:SetValue(row_data.record.stack_size) diff --git a/tabs/search/core.lua b/tabs/search/core.lua index 4e5b67b..2a75c3f 100644 --- a/tabs/search/core.lua +++ b/tabs/search/core.lua @@ -229,7 +229,7 @@ function private.get_form_filter() end end - local quality = UIDropDownMenu_GetSelectedValue(m.quality_dropdown) ~= 0 and UIDropDownMenu_GetSelectedValue(m.quality_dropdown) + local quality = UIDropDownMenu_GetSelectedValue(m.quality_dropdown) >= 0 and UIDropDownMenu_GetSelectedValue(m.quality_dropdown) if quality then add(strlower(getglobal('ITEM_QUALITY'..quality..'_DESC'))) end @@ -765,7 +765,7 @@ function private.initialize_quality_dropdown() UIDropDownMenu_AddButton{ text = ALL, - value = 0, + value = -1, func = on_click, } for i=0,4 do @@ -777,3 +777,59 @@ function private.initialize_quality_dropdown() end end +function private.initialize_filter_dropdown() + local function on_click() + UIDropDownMenu_SetSelectedValue(m.filter_dropdown, this.value) + m.filter_button:SetText(this.value) + if Aux.safe(Aux).scan_util.filters[this.value].arity/0 == 0 then + m.filter_input:Hide() + else + m.filter_input:Show() + end + end + + for _, filter in {'and', 'or', 'not', 'min-unit-buy', 'max-unit-bid', 'max-unit-bid', 'max-unit-buy', 'bid-profit', 'buy-profit', 'bid-vend-profit', 'buy-vend-profit', 'bid-dis-profit', 'buy-dis-profit', 'bid-pct', 'buy-pct', 'item', 'tooltip', 'min-lvl', 'max-lvl', 'rarity', 'left', 'utilizable', 'discard'} do + UIDropDownMenu_AddButton{ + text = filter, + value = filter, + func = on_click, + } + end +end + +do + local stack = {} + + function private.add_post_filter() + local name = UIDropDownMenu_GetSelectedValue(m.filter_dropdown) or '' + local input = m.filter_input:GetText() + if Aux.safe(Aux).scan_util.filters[name].test(input)/true then + local filter = name + if Aux.safe(Aux).scan_util.filters[name].arity/0 > 0 then + filter = filter..'/'..input + end + + local filters = m.filter_display:GetText() or '' + if filters ~= '' then + filters = filters..'|n' + end + for _=1,getn(stack) do + filters = filters..' ' + end + m.filter_display:SetText(filters..filter) + + m.filter_input:SetText('') + m.filter_input:ClearFocus() + + if Aux.util.set('and', 'or')[filter] then + tinsert(stack, 2) + else + local top = tremove(stack) + if top and top > 1 then + tinsert(stack, top - 1) + end + end + end + end +end + diff --git a/tabs/search/frames.lua b/tabs/search/frames.lua index 051a523..21e64b2 100644 --- a/tabs/search/frames.lua +++ b/tabs/search/frames.lua @@ -517,186 +517,46 @@ Aux.search_tab.FRAMES(function(m, public, private) private.quality_dropdown = dropdown end Aux.gui.vertical_line(m.frame.filter, 332) - local function add_modifier(...) - local current_filter_string = m.search_box:GetText() - for i=1,arg.n do - if current_filter_string ~= '' and strsub(current_filter_string, -1) ~= '/' then - current_filter_string = current_filter_string..'/' - end - current_filter_string = current_filter_string..arg[i] - end - m.search_box:SetText(current_filter_string) - end do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPRIGHT', -362, -10) - btn:SetWidth(50) - btn:SetHeight(19) - btn:SetText('and') - btn:SetScript('OnClick', function() - add_modifier('and') + local dropdown = Aux.gui.dropdown(m.frame.filter) + dropdown:SetPoint('TOPRIGHT', -200, -10) + dropdown:SetWidth(170) + dropdown:SetHeight(10) + UIDropDownMenu_Initialize(dropdown, m.initialize_filter_dropdown) + dropdown:SetScript('OnShow', function() + UIDropDownMenu_Initialize(this, m.initialize_filter_dropdown) end) - private.and_operator_button = btn + getglobal(dropdown:GetName()..'Text'):Hide() + private.filter_dropdown = dropdown end do local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('LEFT', m.and_operator_button, 'RIGHT', 10, 0) - btn:SetWidth(50) - btn:SetHeight(19) - btn:SetText('or') - btn:SetScript('OnClick', function() - add_modifier('or') + btn:SetWidth(170) + btn:SetHeight(25) + btn:SetPoint('CENTER', m.filter_dropdown, 'CENTER', 0, 0) + btn:SetScript('OnClick', m.add_post_filter) + private.filter_button = btn + end + do + local input = Aux.gui.editbox(m.frame.filter) + input:SetPoint('LEFT', m.filter_dropdown, 'RIGHT', 10, 0) + input:SetWidth(150) + input:SetHeight(25) + -- editbox:SetNumeric(true) + -- editbox:SetMaxLetters(2) + input:SetScript('OnChar', function() + local filter = Aux.safe(Aux).scan_util.filters[UIDropDownMenu_GetSelectedValue(m.filter_dropdown)] + Aux.completion.complete(this, function() return ({filter.test()/nil})[2] end) end) - private.or_operator_button = btn + input:SetScript('OnEnterPressed', m.add_post_filter) + private.filter_input = input end do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('LEFT', m.or_operator_button, 'RIGHT', 10, 0) - btn:SetWidth(50) - btn:SetHeight(19) - btn:SetText('not') - btn:SetScript('OnClick', function() - add_modifier('not') - end) - private.not_operator_button = btn - end - private.modifier_buttons = {} - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.and_operator_button, 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['min-unit-bid'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['min-unit-bid'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['min-unit-buy'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['min-unit-buy'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['max-unit-bid'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['max-unit-bid'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['max-unit-buy'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['max-unit-buy'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['bid-profit'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['bid-profit'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['buy-profit'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['buy-profit'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['bid-vend-profit'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['bid-vend-profit'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['buy-vend-profit'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['buy-vend-profit'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['bid-dis-profit'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['bid-dis-profit'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['buy-dis-profit'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.and_operator_button, 'BOTTOMLEFT', 205, -10) - m.modifier_buttons['bid-pct'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['bid-pct'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['buy-pct'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['buy-pct'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['item'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['item'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['tooltip'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['tooltip'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['min-lvl'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['min-lvl'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['max-lvl'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['max-lvl'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['rarity'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['rarity'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['left'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['left'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['utilizable'] = btn - end - do - local btn = Aux.gui.button(m.frame.filter, 16) - btn:SetPoint('TOPLEFT', m.modifier_buttons['utilizable'], 'BOTTOMLEFT', 0, -10) - m.modifier_buttons['discard'] = btn - end - for modifier_name, btn in m.modifier_buttons do - local modifier_name = modifier_name - local btn = btn - - local filter = Aux.scan_util.filters[modifier_name] - - btn:SetWidth(100) - btn:SetHeight(19) - btn:SetText(modifier_name) - btn:SetScript('OnClick', function() - local args = Aux.util.map(btn.inputs, function(input) return input:GetText() end) - if filter.test(unpack(args)) then - add_modifier(modifier_name, unpack(args)) - for _, input in btn.inputs do - input:SetText('') - input:ClearFocus() - end - end - end) - btn.inputs = {} - if filter.arity > 0 then - local editbox = Aux.gui.editbox(m.frame.filter) - editbox.complete = Aux.completion.complete(function() return ({filter.test()})[2] end) - editbox:SetPoint('LEFT', btn, 'RIGHT', 10, 0) - editbox:SetWidth(80) - -- editbox:SetNumeric(true) - -- editbox:SetMaxLetters(2) - editbox:SetScript('OnChar', function() - this:complete() - end) - local on_click = btn:GetScript('OnClick') - editbox:SetScript('OnEnterPressed', function() - on_click() - end) - tinsert(btn.inputs, editbox) - end + local label = Aux.gui.label(m.frame.filter, 13) + label:SetPoint('TOPLEFT', 340, -50) + label:SetWidth(200) + label:SetJustifyH('LEFT') + private.filter_display = label end private.status_bars = {} diff --git a/util/core.lua b/util/core.lua index 1158ebe..ddfbd90 100644 --- a/util/core.lua +++ b/util/core.lua @@ -3,6 +3,22 @@ local m, public, private = Aux.module'util' function public.pass() end +function public.sum(...) + local x = 0 + for i=1,arg.n do + x = x + arg[i] + end + return x +end + +function public.product(...) + local x = 0 + for i=1,arg.n do + x = x * arg[i] + end + return x +end + function public.id(value) return value end @@ -179,10 +195,10 @@ function public.take(n, xs) return ys end -function public.index_of(value, array) - for i, item in ipairs(array) do - if item == value then - return i +function public.key(value, t) + for k, v in t do + if v == value then + return k end end end diff --git a/util/scan_util.lua b/util/scan_util.lua index bdef823..030f588 100644 --- a/util/scan_util.lua +++ b/util/scan_util.lua @@ -79,13 +79,9 @@ public.filters = { return false, {'poor', 'common', 'uncommon', 'rare', 'epic'}, 'Erroneous rarity modifier' end - local code = ({ - ['poor'] = 0, - ['common'] = 1, - ['uncommon'] = 2, - ['rare'] = 3, - ['epic'] = 4, - })[rarity or ''] + local code = Aux.util.key(rarity, {'poor', 'common', 'uncommon', 'rare', 'epic'}) + code = code and code - 1 + if not code then return false, {}, 'Erroneous rarity modifier' end From 1098be024c1ee9135059bf4a5fd46815223d851e Mon Sep 17 00:00:00 2001 From: Manuel Simon Hirsig Date: Mon, 1 Aug 2016 21:07:51 +0200 Subject: [PATCH 2/9] big filter refactoring --- Aux-AddOn.toc | 1 + components/scan.lua | 10 +- core.lua | 96 ++---- tabs/search/core.lua | 12 +- tabs/search/frames.lua | 6 +- util/completion.lua | 2 +- util/core.lua | 2 +- util/info.lua | 33 +++ util/scan_util.lua | 659 +---------------------------------------- 9 files changed, 68 insertions(+), 753 deletions(-) diff --git a/Aux-AddOn.toc b/Aux-AddOn.toc index c247ab5..97929e1 100644 --- a/Aux-AddOn.toc +++ b/Aux-AddOn.toc @@ -15,6 +15,7 @@ util\persistence.lua util\sort.lua util\money.lua util\info.lua +util\filter.lua util\scan_util.lua diff --git a/components/scan.lua b/components/scan.lua index eb3ef52..e1d5f22 100644 --- a/components/scan.lua +++ b/components/scan.lua @@ -23,7 +23,7 @@ function private.current_thread() end function public.start(params) - Aux.safe(m).abort(Aux.safe(m).threads[params.type].id) + m.abort(Aux.safe(m.threads[params.type]).id/0) local thread_id = Aux.control.new_thread(Aux.f(m.wait_for_callback, params.on_scan_start, m.scan)) @@ -189,11 +189,11 @@ function private.scan_auctions_helper(i, k) Aux.history.process_auction(auction_info) - if Aux.safe(m).current_thread().params.auto_buy_validator(auction_info)/false then + if Aux.safe(m.current_thread().params.auto_buy_validator)(auction_info)/false then local c = Aux.control.wait_for(recurse) Aux.place_bid(auction_info.query_type, auction_info.index, auction_info.buyout_price, Aux.f(c, true)) Aux.control.new_thread(Aux.control.sleep, 10, Aux.f(c, false)) - elseif Aux.safe(m).current_query().validator(auction_info)/true then + elseif Aux.safe(m.current_query().validator)(auction_info)/true then return m.wait_for_callback(m.current_thread().params.on_auction, auction_info, function(removed) if removed then return recurse(true) @@ -210,14 +210,14 @@ end function private.submit_query(k) Aux.control.when(function() return m.current_thread().params.type ~= 'list' or CanSendAuctionQuery() end, function() - Aux.safe(m).current_thread().params.on_submit_query() + Aux.safe(m.current_thread().params.on_submit_query)() if m.current_thread().params.type == 'bidder' then GetBidderAuctionItems(m.current_thread().page) elseif m.current_thread().params.type == 'owner' then GetOwnerAuctionItems(m.current_thread().page) else - local blizzard_query = Aux.safe(m).current_query().blizzard_query/{} + local blizzard_query = Aux.safe(m.current_query().blizzard_query)/{} QueryAuctionItems( blizzard_query.name, blizzard_query.min_level, diff --git a/core.lua b/core.lua index c202c37..a3a3227 100644 --- a/core.lua +++ b/core.lua @@ -56,62 +56,33 @@ function public.f(func, ...) end end -do - local data = {} - local eq = function() return true end - local proto = setmetatable({}, { __eq = eq }) - local safe_nil - - local function unsafe(object) - if object == proto then - return -object - else - return object - end - end - - local safe_mt = { +function public.safe(value) + return setmetatable({},{ __index = function(self, key) - key = unsafe(key) - if data[self] == nil or key == nil then - return safe_nil + if value ~= nil and key ~= nil then + return m.safe(value[key]) else - return m.safe(data[self][key]) + return m.safe(nil) end end, __call = function(self, ...) - if data[self] == nil then - return safe_nil - end - for i=1,arg.n do - if arg[i] == proto and -arg[i] == nil then - return safe_nil - else - arg[i] = unsafe(arg[i]) + if value ~= nil then + if type(value) ~= 'function' then + error('', 2) end - end - return m.safe(data[self](unpack(arg))) - end, - __div = function(self, default) - if data[self] == nil then - return default + return m.safe(value(unpack(arg))) else - return data[self] + return m.safe(nil) end end, - __unm = function(self) - return data[self] + __div = function(self, alt) + if value ~= nil then + return value + else + return alt + end end, - __eq = eq, - } - - function public.safe(object) - local self = {} - data[self] = unsafe(object) - return setmetatable(self, safe_mt) - end - - safe_nil = m.safe() + }) end do @@ -476,39 +447,6 @@ function private.UseContainerItem(...) return m.orig.UseContainerItem(unpack(arg)) end -function public.item_class_index(item_class) - for i, class in ipairs({ GetAuctionItemClasses() }) do - if strupper(class) == strupper(item_class) then - return i - end - end -end - -function public.item_subclass_index(class_index, item_subclass) - for i, subclass in ipairs({ GetAuctionItemSubClasses(class_index) }) do - if strupper(subclass) == strupper(item_subclass) then - return i - end - end -end - -function public.item_slot_index(class_index, subclass_index, slot_name) - for i, slot in ipairs({ GetAuctionInvTypes(class_index, subclass_index) }) do - if strupper(getglobal(slot)) == strupper(slot_name) then - return i - end - end -end - -function public.item_quality_index(item_quality) - for i=0,4 do - local quality = getglobal('ITEM_QUALITY'..i..'_DESC') - if strupper(item_quality) == strupper(quality) then - return i - end - end -end - function public.is_player(name, current) local realm = GetCVar('realmName') return (not current and m.safe(aux_characters)[realm][name]/false) or UnitName('player') == name diff --git a/tabs/search/core.lua b/tabs/search/core.lua index 2a75c3f..c1c406d 100644 --- a/tabs/search/core.lua +++ b/tabs/search/core.lua @@ -152,7 +152,7 @@ end function private.update_auto_buy_filter() if aux_auto_buy_filter ~= '' then - local queries = Aux.scan_util.parse_filter_string(aux_auto_buy_filter) + local queries = Aux.filter.queries(aux_auto_buy_filter) if queries then if getn(queries) > 1 then Aux.log('Error: The automatic buyout filter may contain only one query') @@ -431,7 +431,7 @@ function public.execute(resume, real_time) end local filter_string = m.search_box:GetText() - local queries = Aux.scan_util.parse_filter_string(filter_string) + local queries = Aux.filter.queries(filter_string) if not queries then return elseif real_time then @@ -460,7 +460,7 @@ function public.execute(resume, real_time) end m.current_search().real_time = m.real_time_button:GetChecked() m.current_search().auto_buy = m.auto_buy_button:GetChecked() - m.current_search().auto_buy_validator = m.auto_buy_filter_button:GetChecked() and m.auto_buy_validator -- some redundancy to be save + m.current_search().auto_buy_validator = m.auto_buy_validator end local continuation = resume and m.current_search().continuation @@ -781,7 +781,7 @@ function private.initialize_filter_dropdown() local function on_click() UIDropDownMenu_SetSelectedValue(m.filter_dropdown, this.value) m.filter_button:SetText(this.value) - if Aux.safe(Aux).scan_util.filters[this.value].arity/0 == 0 then + if Aux.safe(Aux.filter.filters[this.value]).input_type/'' == '' then m.filter_input:Hide() else m.filter_input:Show() @@ -803,9 +803,9 @@ do function private.add_post_filter() local name = UIDropDownMenu_GetSelectedValue(m.filter_dropdown) or '' local input = m.filter_input:GetText() - if Aux.safe(Aux).scan_util.filters[name].test(input)/true then + if Aux.safe(Aux.filter.filters[name]).validator(input)/true then local filter = name - if Aux.safe(Aux).scan_util.filters[name].arity/0 > 0 then + if Aux.safe(Aux.filter.filters)[name].input_type ~= '' then filter = filter..'/'..input end diff --git a/tabs/search/frames.lua b/tabs/search/frames.lua index 21e64b2..01f854b 100644 --- a/tabs/search/frames.lua +++ b/tabs/search/frames.lua @@ -327,7 +327,7 @@ Aux.search_tab.FRAMES(function(m, public, private) btn:SetHeight(24) btn:SetText('Favorite') btn:SetScript('OnClick', function() - local filters = Aux.scan_util.parse_filter_string(m.search_box:GetText()) + local filters = Aux.filter.queries(m.search_box:GetText()) if filters then tinsert(aux_favorite_searches, 1, { filter_string = m.search_box:GetText(), @@ -545,8 +545,8 @@ Aux.search_tab.FRAMES(function(m, public, private) -- editbox:SetNumeric(true) -- editbox:SetMaxLetters(2) input:SetScript('OnChar', function() - local filter = Aux.safe(Aux).scan_util.filters[UIDropDownMenu_GetSelectedValue(m.filter_dropdown)] - Aux.completion.complete(this, function() return ({filter.test()/nil})[2] end) +-- local filter = Aux.filter.filters[UIDropDownMenu_GetSelectedValue(m.filter_dropdown) or ''] -- TODO +-- Aux.completion.complete(this, function() return filter and ({filter.validator()})[3] or {} end) end) input:SetScript('OnEnterPressed', m.add_post_filter) private.filter_input = input diff --git a/util/completion.lua b/util/completion.lua index 6681fbf..c40b3d3 100644 --- a/util/completion.lua +++ b/util/completion.lua @@ -8,7 +8,7 @@ function public:complete_filter() local filter_string = this:GetText() local completed_filter_string = ({strfind(filter_string, '([^;]*)/[^/;]*$')})[3] - local _, suggestions = Aux.scan_util.query(completed_filter_string or '') + local _, suggestions = Aux.filter.query(completed_filter_string or '') local start_index, _, current_modifier = strfind(filter_string, '([^/;]*)$') current_modifier = current_modifier or '' diff --git a/util/core.lua b/util/core.lua index ddfbd90..b0a25db 100644 --- a/util/core.lua +++ b/util/core.lua @@ -114,7 +114,7 @@ function public.bag_type(bag) if link then local item_id = Aux.info.parse_hyperlink(GetInventoryItemLink('player', ContainerIDToInventoryID(bag))) local item_info = Aux.info.item(item_id) - return Aux.item_subclass_index(3, item_info.subclass) + return Aux.info.item_subclass_index(3, item_info.subclass) end end diff --git a/util/info.lua b/util/info.lua index f14f6c0..04383de 100644 --- a/util/info.lua +++ b/util/info.lua @@ -384,4 +384,37 @@ function public.item(item_id, suffix_id) max_stack = max_stack, texture = texture, } or Aux.cache.item_info(item_id) +end + +function public.item_class_index(item_class) + for i, class in ipairs({ GetAuctionItemClasses() }) do + if strupper(class) == strupper(item_class) then + return i + end + end +end + +function public.item_subclass_index(class_index, item_subclass) + for i, subclass in ipairs({ GetAuctionItemSubClasses(class_index) }) do + if strupper(subclass) == strupper(item_subclass) then + return i + end + end +end + +function public.item_slot_index(class_index, subclass_index, slot_name) + for i, slot in ipairs({ GetAuctionInvTypes(class_index, subclass_index) }) do + if strupper(getglobal(slot)) == strupper(slot_name) then + return i + end + end +end + +function public.item_quality_index(item_quality) + for i=0,4 do + local quality = getglobal('ITEM_QUALITY'..i..'_DESC') + if strupper(item_quality) == strupper(quality) then + return i + end + end end \ No newline at end of file diff --git a/util/scan_util.lua b/util/scan_util.lua index 030f588..b728156 100644 --- a/util/scan_util.lua +++ b/util/scan_util.lua @@ -1,311 +1,5 @@ local m, public, private = Aux.module'scan_util' -function public.default_filter(str) - return { - arity = 0, - test = function() - return function(auction_record) - return Aux.util.any(auction_record.tooltip, function(entry) - return strfind(strupper(entry.left_text or ''), strupper(str or ''), 1, true) or strfind(strupper(entry.right_text or ''), strupper(str or ''), 1, true) - end) - end - end, - } -end - -public.filters = { - - ['utilizable'] = { - arity = 0, - test = function() - return function(auction_record) - return auction_record.usable and not Aux.info.tooltip_match(ITEM_SPELL_KNOWN, auction_record.tooltip) - end - end, - }, - - ['tooltip'] = { - arity = 1, - test = function(str) - if str then - return m.default_filter(str).test() - else - return false, {}, 'Erroneous tooltip modifier' - end - end, - }, - - ['item'] = { - arity = 1, - test = function(name) - if not name then - return false, aux_auctionable_items, 'Erroneous item modifier' - end - - return function(auction_record) - return strlower(Aux.info.item(auction_record.item_id).name) == name - end - end - }, - - ['left'] = { - arity = 1, - test = function(duration) - if not duration then - return false, {'30m', '2h', '8h', '24h'}, 'Erroneous time left modifier' - end - - local code = ({ - ['30m'] = 1, - ['2h'] = 2, - ['8h'] = 3, - ['24h'] = 4, - })[duration or ''] - - if not code then - return false, {}, 'Erroneous time left modifier' - end - - return function(auction_record) - return auction_record.duration == code - end - end - }, - - ['rarity'] = { - arity = 1, - test = function(rarity) - if not rarity then - return false, {'poor', 'common', 'uncommon', 'rare', 'epic'}, 'Erroneous rarity modifier' - end - - local code = Aux.util.key(rarity, {'poor', 'common', 'uncommon', 'rare', 'epic'}) - code = code and code - 1 - - if not code then - return false, {}, 'Erroneous rarity modifier' - end - - return function(auction_record) - return auction_record.quality == code - end - end - }, - - ['min-lvl'] = { - arity = 1, - test = function(level) - level = tonumber(level or '') - if level then - return function(auction_record) - return auction_record.level >= level - end - else - return false, {}, 'Erroneous min level modifier' - end - end - }, - - ['max-lvl'] = { - arity = 1, - test = function(level) - level = tonumber(level or '') - if level then - return function(auction_record) - return auction_record.level <= level - end - else - return false, {}, 'Erroneous max level modifier' - end - end - }, - - ['min-unit-bid'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - return auction_record.unit_bid_price >= amount - end - else - return false, {}, 'Erroneous min bid modifier' - end - end - }, - - ['min-unit-buy'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - return auction_record.unit_buyout_price >= amount - end - else - return false, {}, 'Erroneous min buyout modifier' - end - end - }, - - ['max-unit-bid'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - return auction_record.unit_bid_price <= amount - end - else - return false, {}, 'Erroneous max bid modifier' - end - end - }, - - ['max-unit-buy'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - return auction_record.buyout_price > 0 and auction_record.unit_buyout_price <= amount - end - else - return false, {}, 'Erroneous max buyout modifier' - end - end - }, - - ['bid-pct'] = { - arity = 1, - test = function(pct) - pct = tonumber(pct) - if pct then - return function(auction_record) - return auction_record.unit_buyout_price > 0 - and Aux.history.value(auction_record.item_key) - and auction_record.unit_buyout_price / Aux.history.value(auction_record.item_key) * 100 <= pct - end - else - return false, {}, 'Erroneous bid percentage modifier' - end - end - }, - - ['buy-pct'] = { - arity = 1, - test = function(pct) - pct = tonumber(pct) - if pct then - return function(auction_record) - return auction_record.unit_buyout_price > 0 - and Aux.history.value(auction_record.item_key) - and auction_record.unit_buyout_price / Aux.history.value(auction_record.item_key) * 100 <= pct - end - else - return false, {}, 'Erroneous buyout percentage modifier' - end - end - }, - - ['bid-profit'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - return Aux.history.value(auction_record.item_key) and Aux.history.value(auction_record.item_key) * auction_record.aux_quantity - auction_record.bid_price >= amount - end - else - return false, {}, 'Erroneous bid profit modifier' - end - end - }, - - ['buy-profit'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - return auction_record.buyout_price > 0 and Aux.history.value(auction_record.item_key) and Aux.history.value(auction_record.item_key) * auction_record.aux_quantity - auction_record.buyout_price >= amount - end - else - return false, {}, 'Erroneous buyout profit modifier' - end - end - }, - - ['bid-dis-profit'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - local disenchant_value = Aux.disenchant.value(auction_record.slot, auction_record.quality, auction_record.level) - return disenchant_value and disenchant_value - auction_record.bid_price >= amount - end - else - return false, {}, 'Erroneous bid disenchant profit modifier' - end - end - }, - - ['buy-dis-profit'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - local disenchant_value = Aux.disenchant.value(auction_record.slot, auction_record.quality, auction_record.level) - return auction_record.buyout_price > 0 and disenchant_value and disenchant_value - auction_record.buyout_price >= amount - end - else - return false, {}, 'Erroneous buyout disenchant profit modifier' - end - end - }, - - ['bid-vend-profit'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - local vendor_price = Aux.cache.merchant_info(auction_record.item_id) - return vendor_price and vendor_price * auction_record.aux_quantity - auction_record.bid_price >= amount - end - else - return false, {}, 'Erroneous bid vendor profit modifier' - end - end - }, - - ['buy-vend-profit'] = { - arity = 1, - test = function(amount) - amount = Aux.money.from_string(amount or '') or 0 - if amount > 0 then - return function(auction_record) - local vendor_price = Aux.cache.merchant_info(auction_record.item_id) - return auction_record.buyout_price > 0 and vendor_price and vendor_price * auction_record.aux_quantity - auction_record.buyout_price >= amount - end - else - return false, {}, 'Erroneous buyout vendor profit modifier' - end - end - }, - - ['discard'] = { - arity = 0, - test = function() - return function() - return false - end - end - }, -} - function public.find(auction_record, status_bar, on_abort, on_failure, on_success) local function test(index) @@ -376,29 +70,12 @@ function public.find(auction_record, status_bar, on_abort, on_failure, on_succes } end -function public.filter_builder() - local filter = '' - return { - append = function(self, modifier) - modifier = modifier - filter = filter == '' and modifier or filter..'/'..modifier - end, - prepend = function(self, modifier) - modifier = modifier - filter = filter == '' and modifier or modifier..'/'..filter - end, - get = function(self) - return filter - end - } -end - function public.item_query(item_id, first_page, last_page) local item_info = Aux.info.item(item_id) if item_info then - local filter = m.query(item_info.name..'/exact') + local filter = Aux.filter.query(item_info.name..'/exact') filter.blizzard_query.first_page = first_page filter.blizzard_query.last_page = last_page return { @@ -406,338 +83,4 @@ function public.item_query(item_id, first_page, last_page) blizzard_query = filter.blizzard_query, } end -end - -function public.parse_filter_string(filter_string) - local parts = Aux.util.split(filter_string, ';') - - local filters = {} - for _, str in ipairs(parts) do - str = Aux.util.trim(str) - - local filter, _, error = m.query(str) - - if not filter then - Aux.log('Invalid filter: '..error) - return - elseif filter.name and strlen(filter.name) > 63 then - Aux.log('Invalid filter: The name may not longer than 63 characters') - return - else - tinsert(filters, filter) - end - end - - return filters -end - -function public.query(filter_term) - local parts = Aux.util.map(Aux.util.split(filter_term, '/'), function(part) return strlower(Aux.util.trim(part)) end) - - local blizzard_filters = {} - local post_filters = {} - local prettified = m.filter_builder() - local polish_notation_counter = 0 - local i = 1 - - local function post_filter(str) - local filter = m.filters[str] - if filter then - prettified:append('|cffffff00'..str..'|r') - else - filter = filter or m.default_filter(str) - prettified:append('|cffff9218'..str..'|r') - end - - local args = {} - for j=1, filter.arity do - local arg = parts[i - 1 + j] - if arg then - tinsert(args, arg) - prettified:append('|cffff9218'..arg..'|r') - end - end - i = i + filter.arity - - local test, suggestions, error = filter.test(unpack(args)) - if test then - tinsert(post_filters, test) - else - return error, i > getn(parts) and suggestions or {} - end - end - - while i <= getn(parts) do - local str = parts[i] - i = i + 1 - - if str == 'and*' or str == 'or*' then - if polish_notation_counter > 1 then - break - end - polish_notation_counter = 0 - tinsert(post_filters, str) - prettified:append('|cffffff00'..str..'|r') - elseif polish_notation_counter > 0 - or (function(_, _, m) return m == '' or m and tonumber(m) > 2 end)(strfind(str, '^and(%d*)$')) - or (function(_, _, m) return m == '' or m and tonumber(m) > 2 end)(strfind(str, '^or(%d*)$')) - or str == 'not' - then - polish_notation_counter = polish_notation_counter == 0 and polish_notation_counter + 1 or polish_notation_counter - if strfind(str, '^and%d*$') or strfind(str, '^or%d*$') then - local and_match, or_match = {strfind(str, '^(and)(%d*)$')}, {strfind(str, '^(or)(%d*)$') } - local op = and_match[3] or or_match[3] - local count = (tonumber(and_match[4]) or tonumber(or_match[4]) or 2) - 1 - polish_notation_counter = polish_notation_counter + count - for _=1,count do - tinsert(post_filters, op) - end - prettified:append('|cffffff00'..str..'|r') - elseif str == 'not' then - tinsert(post_filters, str) - prettified:append('|cffffff00'..str..'|r') - elseif str ~= '' then - polish_notation_counter = polish_notation_counter - 1 - local error, suggestions = post_filter(str) - if error then - return false, suggestions, error - end - end - elseif tonumber(str) then - if tonumber(str) < 1 or tonumber(str) > 60 then - return false, {}, 'Erroneous level range modifier' - end - if not blizzard_filters.min_level then - blizzard_filters.min_level = tonumber(str) - prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r') - elseif not blizzard_filters.max_level and tonumber(str) >= blizzard_filters.min_level then - blizzard_filters.max_level = tonumber(str) - prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r') - else - return false, {}, 'Erroneous level range modifier' - end - elseif Aux.item_class_index(str) and not (blizzard_filters.class and not blizzard_filters.subclass and str == strlower(({ GetAuctionItemClasses() })[10])) then - if not blizzard_filters.class then - blizzard_filters.class = Aux.item_class_index(str) - prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r') - else - return false, {}, 'Erroneous item class modifier' - end - elseif blizzard_filters.class and Aux.item_subclass_index(blizzard_filters.class, str) then - if not blizzard_filters.subclass then - blizzard_filters.subclass = Aux.item_subclass_index(blizzard_filters.class, str) - prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r') - else - return false, {}, 'Erroneous item subclass modifier' - end - elseif blizzard_filters.subclass and Aux.item_slot_index(blizzard_filters.class, blizzard_filters.subclass, str) then - if not blizzard_filters.slot then - blizzard_filters.slot = Aux.item_slot_index(blizzard_filters.class, blizzard_filters.subclass, str) - prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r') - else - return false, {}, 'Erroneous item slot modifier' - end - elseif Aux.item_quality_index(str) then - if not blizzard_filters.quality then - blizzard_filters.quality = Aux.item_quality_index(str) - prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r') - else - return false, {}, 'Erroneous rarity modifier' - end - elseif str == 'usable' then - if not blizzard_filters.usable then - blizzard_filters.usable = true - prettified:append(Aux.gui.inline_color({216, 225, 211, 1})..str..'|r') - else - return false, {}, 'Erroneous usable only modifier' - end - elseif str == 'exact' then - if not blizzard_filters.exact then - blizzard_filters.exact = true - else - return false, {}, 'Erroneous exact only modifier' - end - elseif i == 2 and not m.filters[str] then - blizzard_filters.name = str - elseif str ~= '' then - local error, suggestions = post_filter(str) - if error then - return false, suggestions, error - end - else - return false, {}, 'Empty modifier' - end - end - - if polish_notation_counter > 0 then - local suggestions = {} - for filter, _ in m.filters do - tinsert(suggestions, strlower(filter)) - end - tinsert(suggestions, 'and') - tinsert(suggestions, 'or') - tinsert(suggestions, 'not') - return false, i > getn(parts) and suggestions, 'Malformed expression' - end - - if blizzard_filters.exact then - if blizzard_filters.min_level - or blizzard_filters.max_level - or blizzard_filters.class - or blizzard_filters.subclass - or blizzard_filters.slot - or blizzard_filters.quality - or blizzard_filters.usable - or not blizzard_filters.name - then - return false, {}, 'Erroneous exact only modifier' - else - prettified:prepend(Aux.info.display_name(Aux.cache.item_id(blizzard_filters.name)) or Aux.gui.inline_color({216, 225, 211, 1})..'['..blizzard_filters.name..']|r') - end - elseif blizzard_filters.name then - if blizzard_filters.name == '' then - prettified:prepend('|cffff0000'..'No Filter'..'|r') - else - prettified:prepend(Aux.gui.inline_color({216, 225, 211, 1})..blizzard_filters.name..'|r') - end - end - - return { - blizzard_query = m.blizzard_query(blizzard_filters), - validator = m.validator(blizzard_filters, post_filters), - prettified = prettified:get(), - }, m.suggestions(blizzard_filters, getn(parts)) -end - -function public.suggestions(blizzard_filter, num_parts) - - local suggestions = {} - - if blizzard_filter.name - and not blizzard_filter.min_level - and not blizzard_filter.max_level - and not blizzard_filter.class - and not blizzard_filter.subclass - and not blizzard_filter.slot - and not blizzard_filter.quality - and not blizzard_filter.usable - then - tinsert(suggestions, 'exact') - end - - tinsert(suggestions, 'and') - tinsert(suggestions, 'or') - tinsert(suggestions, 'not') - tinsert(suggestions, 'tt') - - for filter, _ in pairs(m.filters) do - tinsert(suggestions, strlower(filter)) - end - - -- classes - if not blizzard_filter.class then - for _, class in ipairs({ GetAuctionItemClasses() }) do - tinsert(suggestions, class) - end - end - - -- subclasses - if blizzard_filter.class and not blizzard_filter.subclass then - for _, subclass in ipairs({ GetAuctionItemSubClasses(blizzard_filter.class) }) do - tinsert(suggestions, subclass) - end - end - - -- slots - if blizzard_filter.class and blizzard_filter.subclass and not blizzard_filter.slot then - for _, invtype in ipairs({ GetAuctionInvTypes(blizzard_filter.class, blizzard_filter.subclass) }) do - tinsert(suggestions, getglobal(invtype)) - end - end - - -- usable - if not blizzard_filter.usable then - tinsert(suggestions, 'usable') - end - - -- rarities - if not blizzard_filter.quality then - for i=0,4 do - tinsert(suggestions, getglobal('ITEM_QUALITY'..i..'_DESC')) - end - end - - -- item names - if num_parts == 1 and blizzard_filter.name == '' then - for _, name in aux_auctionable_items do - tinsert(suggestions, name..'/exact') - end - end - - return suggestions -end - -function public.blizzard_query(filter) - local query = { name=filter.name } - - local item_info, class_index, subclass_index, slot_index - if filter.exact then - local item_id = Aux.cache.item_id(filter.name) - item_info = Aux.info.item(item_id) - class_index = item_info and Aux.item_class_index(item_info.class) - subclass_index = class_index and item_info.subclass and Aux.item_subclass_index(class_index, item_info.subclass) - slot_index = subclass_index and item_info.slot and Aux.item_slot_index(class_index, subclass_index, item_info.slot) - end - - if item_info then - query.min_level = item_info.level - query.max_level = item_info.level - query.class = class_index - query.subclass = subclass_index - query.slot = item_info.class - query.usable = item_info.usable - query.quality = item_info.quality - else - query.min_level = filter.min_level - query.max_level = filter.max_level - query.class = filter.class - query.subclass = filter.subclass - query.slot = filter.slot - query.usable = filter.usable and 1 - query.quality = filter.quality - end - - return query -end - -function public.validator(blizzard_filter, post_filter) - - return function(record) - if blizzard_filter.exact and strlower(Aux.info.item(record.item_id).name) ~= blizzard_filter.name then - return false - end - if getn(post_filter) > 0 then - local stack = {} - for i=getn(post_filter),1,-1 do - local op = post_filter[i] - if op == 'and' then - local a, b = tremove(stack), tremove(stack) - tinsert(stack, a and b) - elseif op == 'or' then - local a, b = tremove(stack), tremove(stack) - tinsert(stack, a or b) - elseif op == 'not' then - tinsert(stack, not tremove(stack)) - elseif op == 'and*' then - stack = {Aux.util.all(stack, Aux.util.id)} - elseif op == 'or*' then - stack = {Aux.util.any(stack, Aux.util.id)} - else - tinsert(stack, op(record) and true or false) - end - end - return Aux.util.all(stack, Aux.util.id) - end - return true - end end \ No newline at end of file From a15da87db7efa533cdf9d1e53eadc0d690278fd3 Mon Sep 17 00:00:00 2001 From: Manuel Simon Hirsig Date: Mon, 1 Aug 2016 21:10:10 +0200 Subject: [PATCH 3/9] big filter refactoring --- util/filter.lua | 611 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 611 insertions(+) create mode 100644 util/filter.lua diff --git a/util/filter.lua b/util/filter.lua new file mode 100644 index 0000000..cbc252d --- /dev/null +++ b/util/filter.lua @@ -0,0 +1,611 @@ +local m, public, private = Aux.module'filter' + +function private.default_filter(str) + return { + input_type = '', + validator = function() + return function(auction_record) + return Aux.util.any(auction_record.tooltip, function(entry) + return strfind(strupper(entry.left_text or ''), strupper(str or ''), 1, true) or strfind(strupper(entry.right_text or ''), strupper(str or ''), 1, true) + end) + end + end, + } +end + +public.filters = { + + ['utilizable'] = { + input_type = '', + validator = function() + return function(auction_record) + return auction_record.usable and not Aux.info.tooltip_match(ITEM_SPELL_KNOWN, auction_record.tooltip) + end + end, + }, + + ['tooltip'] = { + input_type = 'string', + validator = function(str) + return m.default_filter(str).validator() + end, + }, + + ['item'] = { + input_type = 'string', + validator = function(name) + return function(auction_record) + return strlower(Aux.info.item(auction_record.item_id).name) == name + end + end + }, + + ['left'] = { + input_type = {'30m', '2h', '8h', '24h'}, + validator = function(duration) + local code = Aux.util.key(duration, {'30m', '2h', '8h', '24h'}) + return function(auction_record) + return auction_record.duration == code + end + end + }, + + ['rarity'] = { + input_type = {'poor', 'common', 'uncommon', 'rare', 'epic'}, + validator = function(rarity) + local code = Aux.util.key(rarity, {'poor', 'common', 'uncommon', 'rare', 'epic'}) - 1 + return function(auction_record) + return auction_record.quality == code + end + end + }, + + ['min-lvl'] = { + input_type = 'number', + validator = function(level) + return function(auction_record) + return auction_record.level >= level + end + end + }, + + ['max-lvl'] = { + input_type = 'number', + validator = function(level) + return function(auction_record) + return auction_record.level <= level + end + end + }, + + ['min-unit-bid'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + return auction_record.unit_bid_price >= amount + end + end + }, + + ['min-unit-buy'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + return auction_record.unit_buyout_price >= amount + end + end + }, + + ['max-unit-bid'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + return auction_record.unit_bid_price <= amount + end + end + }, + + ['max-unit-buy'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + return auction_record.buyout_price > 0 and auction_record.unit_buyout_price <= amount + end + end + }, + + ['bid-pct'] = { + input_type = 'number', + validator = function(pct) + return function(auction_record) + return auction_record.unit_buyout_price > 0 + and Aux.history.value(auction_record.item_key) + and auction_record.unit_buyout_price / Aux.history.value(auction_record.item_key) * 100 <= pct + end + end + }, + + ['buy-pct'] = { + input_type = 'number', + validator = function(pct) + return function(auction_record) + return auction_record.unit_buyout_price > 0 + and Aux.history.value(auction_record.item_key) + and auction_record.unit_buyout_price / Aux.history.value(auction_record.item_key) * 100 <= pct + end + end + }, + + ['bid-profit'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + return Aux.history.value(auction_record.item_key) and Aux.history.value(auction_record.item_key) * auction_record.aux_quantity - auction_record.bid_price >= amount + end + end + }, + + ['buy-profit'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + return auction_record.buyout_price > 0 and Aux.history.value(auction_record.item_key) and Aux.history.value(auction_record.item_key) * auction_record.aux_quantity - auction_record.buyout_price >= amount + end + end + }, + + ['bid-dis-profit'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + local disenchant_value = Aux.disenchant.value(auction_record.slot, auction_record.quality, auction_record.level) + return disenchant_value and disenchant_value - auction_record.bid_price >= amount + end + end + }, + + ['buy-dis-profit'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + local disenchant_value = Aux.disenchant.value(auction_record.slot, auction_record.quality, auction_record.level) + return auction_record.buyout_price > 0 and disenchant_value and disenchant_value - auction_record.buyout_price >= amount + end + end + }, + + ['bid-vend-profit'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + local vendor_price = Aux.cache.merchant_info(auction_record.item_id) + return vendor_price and vendor_price * auction_record.aux_quantity - auction_record.bid_price >= amount + end + end + }, + + ['buy-vend-profit'] = { + input_type = 'money', + validator = function(amount) + return function(auction_record) + local vendor_price = Aux.cache.merchant_info(auction_record.item_id) + return auction_record.buyout_price > 0 and vendor_price and vendor_price * auction_record.aux_quantity - auction_record.buyout_price >= amount + end + end + }, + + ['discard'] = { + input_type = '', + validator = function() + return function() + return false + end + end + }, +} + +function private.operator(str) + if str == 'not' then + return 'not', 1 + end + + local _, _, and_op, and_arity = strfind(str, '^(and)(%d*)$') + local _, _, or_op, or_arity = strfind(str, '^(or)(%d*)$') + + local op = and_op or or_op + local arity = and_arity or or_arity + + if op then + if arity == '' then + return op, 2 + elseif tonumber(arity) > 2 then + return op, tonumber(arity) + elseif arity == '*' then + return op + end + end +end + +function private.blizzard_filter_parser() + local filters = {} + return function(str, first) + local filter + if tonumber(str) then + if tonumber(str) < 1 or tonumber(str) > 60 then + return nil, 'Erroneous level range modifier' + end + if not filters.min_level then + filter = {'min_level', str} + elseif not filters.max_level and tonumber(str) >= filters.min_level then + filter = {'max_level', str} + else + return nil, 'Erroneous level range modifier' + end + elseif Aux.info.item_class_index(str) and not (filters.class and not filters.subclass and str == strlower(({ GetAuctionItemClasses() })[10])) then + if not filters.class then + filter = {'class', str} + else + return nil, 'Erroneous item class modifier' + end + elseif filters.class and Aux.info.item_subclass_index(filters.class, str) then + if not filters.subclass then + filter = {'subclass', str} + else + return nil, 'Erroneous item subclass modifier' + end + elseif filters.subclass and Aux.info.item_slot_index(filters.class, filters.subclass, str) then + if not filters.slot then + filter = {'slot', str} + else + return nil, 'Erroneous item slot modifier' + end + elseif Aux.info.item_quality_index(str) then + if not filters.quality then + filter = {'quality', str} + else + return nil, 'Erroneous rarity modifier' + end + elseif str == 'usable' then + if not filters.usable then + filter = {'usable'} + else + return nil, 'Erroneous usable only modifier' + end + elseif str == 'exact' then + if filters.name and not filters.exact then + filter = {'exact'} + else + return nil, 'Erroneous exact only modifier' + end + elseif first then + if strlen(str) <= 63 then + filter = {'name', str } + else + return nil, 'The name must not be longer than 63 characters' + end + end + + if filter then + filters[filter[1]] = filter[2] or true + end + + if filters.exact and + ( + filters.min_level + or filters.max_level + or filters.class + or filters.subclass + or filters.slot + or filters.quality + or filters.usable + ) + then + return false, 'Erroneous exact only modifier' + end + + return filter + end +end + +function private.parse_query_string(str) + local components = { blizzard = {}, post = {} } + local blizzard_filter_parser = m.blizzard_filter_parser() + local parts = Aux.util.map(Aux.util.split(str, '/'), function(part) return strlower(Aux.util.trim(part)) end) + + local polish_notation_counter = 0 + local i = 1 + while parts[i] do + local op, arity = m.operator(parts[i]) + if op then + tinsert(components.post, {'operator', op, arity}) + polish_notation_counter = polish_notation_counter + (arity or 1) - 1 + elseif m.filters[parts[i]] then + local component = {'filter', parts[i] } + if m.filters[parts[i]].input_type == 'money' then + tinsert(component, Aux.safe(Aux.money.from_string(parts[i + 1] or ''))/0) + if component[3] <= 0 then + return nil, 'Invalid money input for '..parts[i] + end + i = i + 1 + elseif m.filters[parts[i]].input_type == 'number' then + tinsert(component, tonumber(parts[i + 1])) + if not component[3] then + return nil, 'Invalid number input for '..parts[i] + end + i = i + 1 + elseif m.filters[parts[i]].input_type == 'string' then + tinsert(component, parts[i + 1]) + if component[3] == '' then + return nil, 'Invalid string input for '..parts[i] + end + i = i + 1 + elseif type(m.filters[parts[i]].input_type) == 'table' then + tinsert(component, parts[i + 1]) + if not Aux.util.key(component[3], m.filters[parts[i]].input_type) then + return nil, 'Invalid input choice for '..parts[i], m.filters[parts[i]].input_type + end + i = i + 1 + end + tinsert(components.post, component) + polish_notation_counter = polish_notation_counter - 1 + else + local component, error = blizzard_filter_parser(parts[i], i == 1) + if component then + tinsert(components.blizzard, component) + elseif error then + return nil, error + elseif parts[i] ~= '' then + tinsert(components.post, {'filter', 'tooltip', parts[i]}) + polish_notation_counter = polish_notation_counter - 1 + else + return nil, 'Empty modifier' + end + end + i = i + 1 + end + + if polish_notation_counter > 0 then + local suggestions = {} + for filter, _ in m.filters do + tinsert(suggestions, strlower(filter)) + end + tinsert(suggestions, 'and') + tinsert(suggestions, 'or') + tinsert(suggestions, 'not') + return nil, 'Malformed expression', i > getn(parts) and suggestions + end + + return components +end + +function public.query(query_string) + local components, error, suggestions = m.parse_query_string(query_string) + + if not components then + return nil, error, suggestions + end + + return { + blizzard_query = m.blizzard_query(components), + validator = m.validator(components), + prettified = m.prettified(components), + }, m.suggestions(components) +end + +function public.queries(query_string) + local parts = Aux.util.split(query_string, ';') + + local queries = {} + for _, str in ipairs(parts) do + str = Aux.util.trim(str) + + local query, error = m.query(str) + + if not query then + Aux.log('Invalid filter:', error) + return + else + tinsert(queries, query) + end + end + + return queries +end + +function private.suggestions(components) + + local blizzard_filters = {} + for _, filter in components.blizzard do + blizzard_filters[filter[1]] = filter[2] or true + end + + local suggestions = {} + + if blizzard_filters.name + and not blizzard_filters.min_level + and not blizzard_filters.max_level + and not blizzard_filters.class + and not blizzard_filters.subclass + and not blizzard_filters.slot + and not blizzard_filters.quality + and not blizzard_filters.usable + then + tinsert(suggestions, 'exact') + end + + tinsert(suggestions, 'and') + tinsert(suggestions, 'or') + tinsert(suggestions, 'not') + tinsert(suggestions, 'tt') + + for filter, _ in pairs(m.filters) do + tinsert(suggestions, strlower(filter)) + end + + -- classes + if not blizzard_filters.class then + for _, class in ipairs({ GetAuctionItemClasses() }) do + tinsert(suggestions, class) + end + end + + -- subclasses + if blizzard_filters.class and not blizzard_filters.subclass then + for _, subclass in ipairs({ GetAuctionItemSubClasses(blizzard_filters.class) }) do + tinsert(suggestions, subclass) + end + end + + -- slots + if blizzard_filters.class and blizzard_filters.subclass and not blizzard_filters.slot then + for _, invtype in ipairs({ GetAuctionInvTypes(blizzard_filters.class, blizzard_filters.subclass) }) do + tinsert(suggestions, getglobal(invtype)) + end + end + + -- usable + if not blizzard_filters.usable then + tinsert(suggestions, 'usable') + end + + -- rarities + if not blizzard_filters.quality then + for i=0,4 do + tinsert(suggestions, getglobal('ITEM_QUALITY'..i..'_DESC')) + end + end + + -- item names + if getn(components.blizzard) + getn(components.post) == 1 and blizzard_filters.name == '' then + for _, name in aux_auctionable_items do + tinsert(suggestions, name..'/exact') + end + end + + return suggestions +end + +function private.prettified(components) + local prettified = m.query_builder() + + local blizzard_filters = {} + for _, filter in components.blizzard do + blizzard_filters[filter[1]] = filter[2] or true + if filter[1] == 'exact' then + prettified.prepend(Aux.info.display_name(Aux.cache.item_id(blizzard_filters.name)) or Aux.gui.inline_color({216, 225, 211, 1})..'['..blizzard_filters.name..']|r') + elseif filter[1] ~= 'name' then + prettified.append(Aux.gui.inline_color({216, 225, 211, 1})..(filter[2] or filter[1])..'|r') + end + end + + if not blizzard_filters.exact then + if blizzard_filters.name == '' then + prettified.prepend('|cffff0000'..'No Filter'..'|r') + elseif blizzard_filters.name then + prettified.prepend(Aux.gui.inline_color({216, 225, 211, 1})..blizzard_filters.name..'|r') + end + end + + for _, component in components.post do + if component[1] == 'operator' then + prettified.append('|cffffff00'..component[2]..(component[3] or '*')..'|r') + elseif component[1] == 'filter' then + prettified.append('|cffffff00'..component[2]..'|r') + if component[3] then + prettified.append('|cffff9218'..component[3]..'|r') + end + end + end + + return prettified.get() +end + +function private.blizzard_query(components) + local filters = {} + for _, filter in components.blizzard do + filters[filter[1]] = filter[2] or true + end + + local query = { name=filters.name } + + local item_info, class_index, subclass_index, slot_index + if filters.exact then + local item_id = Aux.cache.item_id(filters.name) + item_info = Aux.info.item(item_id) + class_index = item_info and Aux.info.item_class_index(item_info.class) + subclass_index = class_index and item_info.subclass and Aux.info.item_subclass_index(class_index, item_info.subclass) + slot_index = subclass_index and item_info.slot and Aux.info.item_slot_index(class_index, subclass_index, item_info.slot) + end + + if item_info then + query.min_level = item_info.level + query.max_level = item_info.level + query.class = class_index + query.subclass = subclass_index + query.slot = item_info.class + query.usable = item_info.usable + query.quality = item_info.quality + else + query.min_level = tonumber(filters.min_level) + query.max_level = tonumber(filters.max_level) + query.class = filters.class and Aux.info.item_class_index(filters.class) + query.subclass = query.class and Aux.info.item_subclass_index(query.class, filters.subclass) + query.slot = query.subclass and Aux.info.item_slot_index(query.class, query.subclass, filters.slot) + query.usable = filters.usable and 1 + query.quality = filters.quality and Aux.info.item_quality_index(filters.quality) + end + + return query +end + +function private.validator(components) + + local validators = {} + for _, filter in Aux.util.filter(components.post, function(component) return component[1] == 'filter' end) do + validators[filter[2]] = m.filters[filter[2]].validator(filter[3]) + end + + return function(record) + for _, filter in components.blizzard do + if filter[1] == 'exact' and strlower(Aux.info.item(record.item_id).name) ~= components.blizzard[1][2] then + return false + end + end + local stack = {} + for i=getn(components.post),1,-1 do + local type, name, param = unpack(components.post[i]) + if type == 'operator' then + local args = {} + while (not param or param > 0) and getn(stack) > 0 do + tinsert(args, tremove(stack)) + param = param and param - 1 + end + if name == 'not' then + tinsert(stack, not args[1]) + elseif name == 'and' then + tinsert(stack, Aux.util.all(args, Aux.util.id)) + elseif name == 'or' then + tinsert(stack, Aux.util.any(args, Aux.util.id)) + end + elseif type == 'filter' then + tinsert(stack, validators[name](record) and true or false) + end + end + return Aux.util.all(stack, Aux.util.id) + end +end + +function private.query_builder() + local filter = '' + return { + append = function(part) + filter = filter == '' and part or filter..'/'..part + end, + prepend = function(part) + filter = filter == '' and part or part..'/'..filter + end, + get = function() + return filter + end + } +end \ No newline at end of file From 58f8c8450b0126a707bd7e4d0aec78683e10db0b Mon Sep 17 00:00:00 2001 From: Manuel Simon Hirsig Date: Mon, 1 Aug 2016 21:21:35 +0200 Subject: [PATCH 4/9] big filter refactoring --- util/filter.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/util/filter.lua b/util/filter.lua index cbc252d..61c9772 100644 --- a/util/filter.lua +++ b/util/filter.lua @@ -508,9 +508,17 @@ function private.prettified(components) for _, component in components.post do if component[1] == 'operator' then - prettified.append('|cffffff00'..component[2]..(component[3] or '*')..'|r') + local suffix = '' + if not component[3] then + suffix = '*' + elseif component[3] > 2 then + suffix = component[3] + end + prettified.append('|cffffff00'..component[2]..suffix..'|r') elseif component[1] == 'filter' then - prettified.append('|cffffff00'..component[2]..'|r') + if component[2] ~= 'tooltip' then + prettified.append('|cffffff00'..component[2]..'|r') + end if component[3] then prettified.append('|cffff9218'..component[3]..'|r') end From 67a0179a2f74ef251c187a7895adb10081088a82 Mon Sep 17 00:00:00 2001 From: Manuel Simon Hirsig Date: Mon, 1 Aug 2016 22:56:52 +0200 Subject: [PATCH 5/9] bugfix --- util/filter.lua | 80 ++++++++++++++++++++++++++-------------------- util/scan_util.lua | 10 +++--- 2 files changed, 50 insertions(+), 40 deletions(-) diff --git a/util/filter.lua b/util/filter.lua index 61c9772..d538c6b 100644 --- a/util/filter.lua +++ b/util/filter.lua @@ -236,7 +236,7 @@ function private.blizzard_filter_parser() end if not filters.min_level then filter = {'min_level', str} - elseif not filters.max_level and tonumber(str) >= filters.min_level then + elseif not filters.max_level and tonumber(str) >= tonumber(filters.min_level) then filter = {'max_level', str} else return nil, 'Erroneous level range modifier' @@ -247,13 +247,13 @@ function private.blizzard_filter_parser() else return nil, 'Erroneous item class modifier' end - elseif filters.class and Aux.info.item_subclass_index(filters.class, str) then + elseif filters.class and Aux.info.item_subclass_index(Aux.info.item_class_index(filters.class), str) then if not filters.subclass then filter = {'subclass', str} else return nil, 'Erroneous item subclass modifier' end - elseif filters.subclass and Aux.info.item_slot_index(filters.class, filters.subclass, str) then + elseif filters.subclass and Aux.info.item_slot_index(Aux.info.item_class_index(filters.class), Aux.info.item_subclass_index(Aux.info.item_class_index(filters.class), filters.subclass), str) then if not filters.slot then filter = {'slot', str} else @@ -307,6 +307,29 @@ function private.blizzard_filter_parser() end end +function private.parse_parameter(input_type, str) + if input_type == 'money' then + local money = Aux.money.from_string(str) + if money and money > 0 then + return money + end + elseif input_type == 'number' then + local number = tonumber(str) + if number then + return number + end + elseif input_type == 'string' then + if str ~= '' then + return str + end + elseif type(input_type) == 'table' then + local choice = Aux.util.key(str, input_type) + if choice then + return choice + end + end +end + function private.parse_query_string(str) local components = { blizzard = {}, post = {} } local blizzard_filter_parser = m.blizzard_filter_parser() @@ -320,33 +343,20 @@ function private.parse_query_string(str) tinsert(components.post, {'operator', op, arity}) polish_notation_counter = polish_notation_counter + (arity or 1) - 1 elseif m.filters[parts[i]] then - local component = {'filter', parts[i] } - if m.filters[parts[i]].input_type == 'money' then - tinsert(component, Aux.safe(Aux.money.from_string(parts[i + 1] or ''))/0) - if component[3] <= 0 then - return nil, 'Invalid money input for '..parts[i] - end - i = i + 1 - elseif m.filters[parts[i]].input_type == 'number' then - tinsert(component, tonumber(parts[i + 1])) - if not component[3] then - return nil, 'Invalid number input for '..parts[i] - end - i = i + 1 - elseif m.filters[parts[i]].input_type == 'string' then - tinsert(component, parts[i + 1]) - if component[3] == '' then - return nil, 'Invalid string input for '..parts[i] - end - i = i + 1 - elseif type(m.filters[parts[i]].input_type) == 'table' then - tinsert(component, parts[i + 1]) - if not Aux.util.key(component[3], m.filters[parts[i]].input_type) then - return nil, 'Invalid input choice for '..parts[i], m.filters[parts[i]].input_type + local input_type = m.filters[parts[i]].input_type + if input_type ~= '' then + if not parts[i + 1] or not m.parse_parameter(input_type, parts[i + 1]) then + if type(input_type) == 'table' then + return nil, 'Invalid input of type choice for '..parts[i], input_type + else + return nil, 'Invalid input of type '..input_type..' for '..parts[i] + end end + tinsert(components.post, {'filter', parts[i], parts[i + 1]}) i = i + 1 + else + tinsert(components.post, {'filter', parts[i]}) end - tinsert(components.post, component) polish_notation_counter = polish_notation_counter - 1 else local component, error = blizzard_filter_parser(parts[i], i == 1) @@ -382,7 +392,7 @@ function public.query(query_string) local components, error, suggestions = m.parse_query_string(query_string) if not components then - return nil, error, suggestions + return nil, suggestions or {}, error end return { @@ -399,7 +409,7 @@ function public.queries(query_string) for _, str in ipairs(parts) do str = Aux.util.trim(str) - local query, error = m.query(str) + local query, _, error = m.query(str) if not query then Aux.log('Invalid filter:', error) @@ -451,14 +461,14 @@ function private.suggestions(components) -- subclasses if blizzard_filters.class and not blizzard_filters.subclass then - for _, subclass in ipairs({ GetAuctionItemSubClasses(blizzard_filters.class) }) do + for _, subclass in ipairs({ GetAuctionItemSubClasses(Aux.info.item_class_index(blizzard_filters.class)) }) do tinsert(suggestions, subclass) end end -- slots if blizzard_filters.class and blizzard_filters.subclass and not blizzard_filters.slot then - for _, invtype in ipairs({ GetAuctionInvTypes(blizzard_filters.class, blizzard_filters.subclass) }) do + for _, invtype in ipairs({ GetAuctionInvTypes(Aux.info.item_class_index(blizzard_filters.class), Aux.info.item_subclass_index(Aux.info.item_class_index(blizzard_filters.class), blizzard_filters.subclass)) }) do tinsert(suggestions, getglobal(invtype)) end end @@ -556,9 +566,9 @@ function private.blizzard_query(components) else query.min_level = tonumber(filters.min_level) query.max_level = tonumber(filters.max_level) - query.class = filters.class and Aux.info.item_class_index(filters.class) - query.subclass = query.class and Aux.info.item_subclass_index(query.class, filters.subclass) - query.slot = query.subclass and Aux.info.item_slot_index(query.class, query.subclass, filters.slot) + query.class = filters.class and filters.class and Aux.info.item_class_index(filters.class) + query.subclass = query.class and filters.subclass and Aux.info.item_subclass_index(query.class, filters.subclass) + query.slot = query.subclass and filters.slot and Aux.info.item_slot_index(query.class, query.subclass, filters.slot) query.usable = filters.usable and 1 query.quality = filters.quality and Aux.info.item_quality_index(filters.quality) end @@ -570,7 +580,7 @@ function private.validator(components) local validators = {} for _, filter in Aux.util.filter(components.post, function(component) return component[1] == 'filter' end) do - validators[filter[2]] = m.filters[filter[2]].validator(filter[3]) + validators[filter[2]] = m.filters[filter[2]].validator(m.parse_parameter(m.filters[filter[2]].input_type, filter[3])) end return function(record) diff --git a/util/scan_util.lua b/util/scan_util.lua index b728156..b47b378 100644 --- a/util/scan_util.lua +++ b/util/scan_util.lua @@ -75,12 +75,12 @@ function public.item_query(item_id, first_page, last_page) local item_info = Aux.info.item(item_id) if item_info then - local filter = Aux.filter.query(item_info.name..'/exact') - filter.blizzard_query.first_page = first_page - filter.blizzard_query.last_page = last_page + local query = Aux.filter.query(item_info.name..'/exact') + query.blizzard_query.first_page = first_page + query.blizzard_query.last_page = last_page return { - validator = filter.validator, - blizzard_query = filter.blizzard_query, + validator = query.validator, + blizzard_query = query.blizzard_query, } end end \ No newline at end of file From a11a4e891cc0aaa5deb77a027271be15a007c73c Mon Sep 17 00:00:00 2001 From: Manuel Simon Hirsig Date: Tue, 2 Aug 2016 00:25:13 +0200 Subject: [PATCH 6/9] big filter refactoring --- core.lua | 2 +- tabs/search/core.lua | 48 ++++++++++------------ util/filter.lua | 97 +++++++++++++++++++++++++++++++++----------- 3 files changed, 96 insertions(+), 51 deletions(-) diff --git a/core.lua b/core.lua index a3a3227..da1a243 100644 --- a/core.lua +++ b/core.lua @@ -95,7 +95,7 @@ do end function public.on_load() - public.version = '3.5.3' + public.version = '3.6.0' public.bids_loaded = false public.current_owner_page = nil public.last_owner_page_requested = nil diff --git a/tabs/search/core.lua b/tabs/search/core.lua index c1c406d..368f6cc 100644 --- a/tabs/search/core.lua +++ b/tabs/search/core.lua @@ -785,6 +785,7 @@ function private.initialize_filter_dropdown() m.filter_input:Hide() else m.filter_input:Show() + m.filter_input:SetFocus() end end @@ -797,38 +798,31 @@ function private.initialize_filter_dropdown() end end -do - local stack = {} +function private.import_query_string() - function private.add_post_filter() - local name = UIDropDownMenu_GetSelectedValue(m.filter_dropdown) or '' - local input = m.filter_input:GetText() - if Aux.safe(Aux.filter.filters[name]).validator(input)/true then - local filter = name - if Aux.safe(Aux.filter.filters)[name].input_type ~= '' then - filter = filter..'/'..input - end +end - local filters = m.filter_display:GetText() or '' - if filters ~= '' then - filters = filters..'|n' - end - for _=1,getn(stack) do - filters = filters..' ' - end - m.filter_display:SetText(filters..filter) +function private.export_query_string() +end + +private.post_query = {} + +function private.add_post_filter() + local name = UIDropDownMenu_GetSelectedValue(m.filter_dropdown) + if name then + local filter = name + if Aux.filter.filters[name] and Aux.filter.filters[name].input_type ~= '' then + filter = filter..'/'..m.filter_input:GetText() + end + + local components, error, suggestions = Aux.filter.parse_query_string(filter) + + if components then + tinsert(m.post_query, components[1]) + m.filter_display:SetText(Aux.filter.indented_post_query_string(m.post_query)) m.filter_input:SetText('') m.filter_input:ClearFocus() - - if Aux.util.set('and', 'or')[filter] then - tinsert(stack, 2) - else - local top = tremove(stack) - if top and top > 1 then - tinsert(stack, top - 1) - end - end end end end diff --git a/util/filter.lua b/util/filter.lua index d538c6b..275e216 100644 --- a/util/filter.lua +++ b/util/filter.lua @@ -330,18 +330,16 @@ function private.parse_parameter(input_type, str) end end -function private.parse_query_string(str) +function public.parse_query_string(str) local components = { blizzard = {}, post = {} } local blizzard_filter_parser = m.blizzard_filter_parser() local parts = Aux.util.map(Aux.util.split(str, '/'), function(part) return strlower(Aux.util.trim(part)) end) - local polish_notation_counter = 0 local i = 1 while parts[i] do local op, arity = m.operator(parts[i]) if op then tinsert(components.post, {'operator', op, arity}) - polish_notation_counter = polish_notation_counter + (arity or 1) - 1 elseif m.filters[parts[i]] then local input_type = m.filters[parts[i]].input_type if input_type ~= '' then @@ -357,7 +355,6 @@ function private.parse_query_string(str) else tinsert(components.post, {'filter', parts[i]}) end - polish_notation_counter = polish_notation_counter - 1 else local component, error = blizzard_filter_parser(parts[i], i == 1) if component then @@ -366,7 +363,6 @@ function private.parse_query_string(str) return nil, error elseif parts[i] ~= '' then tinsert(components.post, {'filter', 'tooltip', parts[i]}) - polish_notation_counter = polish_notation_counter - 1 else return nil, 'Empty modifier' end @@ -374,17 +370,6 @@ function private.parse_query_string(str) i = i + 1 end - if polish_notation_counter > 0 then - local suggestions = {} - for filter, _ in m.filters do - tinsert(suggestions, strlower(filter)) - end - tinsert(suggestions, 'and') - tinsert(suggestions, 'or') - tinsert(suggestions, 'not') - return nil, 'Malformed expression', i > getn(parts) and suggestions - end - return components end @@ -395,10 +380,31 @@ function public.query(query_string) return nil, suggestions or {}, error end + local polish_notation_counter = 0 + for _, component in components.post do + if component[1] == 'operator' then + polish_notation_counter = max(polish_notation_counter, 1) + polish_notation_counter = polish_notation_counter + (component[2] or 1) - 1 + elseif component[1] == 'filter' then + polish_notation_counter = polish_notation_counter - 1 + end + end + + if polish_notation_counter > 0 then + local suggestions = {} + for filter, _ in m.filters do + tinsert(suggestions, strlower(filter)) + end + tinsert(suggestions, 'and') + tinsert(suggestions, 'or') + tinsert(suggestions, 'not') + return nil, {}, 'Malformed expression' --, i > getn(parts) and suggestions + end + return { blizzard_query = m.blizzard_query(components), validator = m.validator(components), - prettified = m.prettified(components), + prettified = m.prettified_query_string(components), }, m.suggestions(components) end @@ -495,7 +501,44 @@ function private.suggestions(components) return suggestions end -function private.prettified(components) +function public.indented_post_query_string(components) + local stack = {} + local str = '' + + for _, component in components.post do + + if str ~= '' then + str = str..'|n' + end + for _=1,getn(stack) do + str = str..' ' + end + + if component[1] == 'operator' and component[2] then + local suffix = '' + if not component[3] then + suffix = '*' + elseif component[3] > 2 then + suffix = component[3] + end + str = str..'|cffffff00'..component[2]..suffix..'|r' + tinsert(stack, component[3]) + elseif component[1] == 'filter' then + str = str..'|cffffff00'..component[2]..'|r' + if component[3] then + str = str..': '..'|cffff9218'..component[3]..'|r' + end + local top = tremove(stack) + if top and top > 1 then + tinsert(stack, top - 1) + end + end + end + + return str +end + +function private.prettified_query_string(components) local prettified = m.query_builder() local blizzard_filters = {} @@ -579,8 +622,10 @@ end function private.validator(components) local validators = {} - for _, filter in Aux.util.filter(components.post, function(component) return component[1] == 'filter' end) do - validators[filter[2]] = m.filters[filter[2]].validator(m.parse_parameter(m.filters[filter[2]].input_type, filter[3])) + for i, component in components.post do + if component[1] == 'filter' then + validators[i] = m.filters[component[2]].validator(m.parse_parameter(m.filters[component[2]].input_type, component[3])) + end end return function(record) @@ -606,16 +651,22 @@ function private.validator(components) tinsert(stack, Aux.util.any(args, Aux.util.id)) end elseif type == 'filter' then - tinsert(stack, validators[name](record) and true or false) + tinsert(stack, validators[i](record) and true or false) end end return Aux.util.all(stack, Aux.util.id) end end -function private.query_builder() - local filter = '' +function public.query_builder(str) + local filter = str or '' return { + appended = function(part) + return m.query_builder(filter == '' and part or filter..'/'..part) + end, + prepended = function(part) + return m.query_builder(filter == '' and part or part..'/'..filter) + end, append = function(part) filter = filter == '' and part or filter..'/'..part end, From 440c6b5ab0759b76efbb19cf8dea4089bc7d66f9 Mon Sep 17 00:00:00 2001 From: Manuel Simon Hirsig Date: Tue, 2 Aug 2016 02:06:09 +0200 Subject: [PATCH 7/9] big filter refactoring, filter tab changes --- tabs/search/core.lua | 180 +++++++++++++++++++++++++---------------- tabs/search/frames.lua | 24 ++---- util/filter.lua | 51 ++++++++++-- 3 files changed, 163 insertions(+), 92 deletions(-) diff --git a/tabs/search/core.lua b/tabs/search/core.lua index 368f6cc..deefeb6 100644 --- a/tabs/search/core.lua +++ b/tabs/search/core.lua @@ -133,21 +133,15 @@ function public.set_filter(filter_string) m.search_box:SetText(filter_string) end -function private.add_filter(filter_string, replace) - filter_string = filter_string or m.get_form_filter() +function public.add_filter(filter_string) + local old_filter_string = m.search_box:GetText() + old_filter_string = Aux.util.trim(old_filter_string) - - local old_filter_string - if not replace then - old_filter_string = m.search_box:GetText() - old_filter_string = Aux.util.trim(old_filter_string) - - if strlen(old_filter_string) > 0 then - old_filter_string = old_filter_string..';' - end + if old_filter_string ~= '' then + old_filter_string = old_filter_string..';' end - m.search_box:SetText((old_filter_string or '')..filter_string) + m.search_box:SetText(old_filter_string..filter_string) end function private.update_auto_buy_filter() @@ -187,56 +181,6 @@ function private.clear_form() UIDropDownMenu_ClearAll(m.quality_dropdown) end -function private.get_form_filter() - local filter_term = '' - - local function add(part) - filter_term = filter_term == '' and part or filter_term..'/'..part - end - - add(m.name_input:GetText()) - - if m.exact_checkbox:GetChecked() then - add('exact') - end - - local min_level = m.blizzard_level(m.min_level_input:GetText()) - if min_level then - add(min_level) - end - - local max_level = m.blizzard_level(m.min_level_input:GetText()) - if max_level then - add(max_level) - end - - if m.usable_checkbox:GetChecked() then - add('usable') - end - - local class = UIDropDownMenu_GetSelectedValue(m.class_dropdown) ~= 0 and UIDropDownMenu_GetSelectedValue(m.class_dropdown) - if class then - local classes = { GetAuctionItemClasses() } - add(strlower(classes[class])) - local subclass = UIDropDownMenu_GetSelectedValue(m.subclass_dropdown) ~= 0 and UIDropDownMenu_GetSelectedValue(m.subclass_dropdown) - if subclass then - local subclasses = {GetAuctionItemSubClasses(class)} - add(strlower(subclasses[subclass])) - local slot = UIDropDownMenu_GetSelectedValue(m.slot_dropdown) ~= 0 and UIDropDownMenu_GetSelectedValue(m.slot_dropdown) - if slot then - add(strlower(getglobal(slot))) - end - end - end - - local quality = UIDropDownMenu_GetSelectedValue(m.quality_dropdown) >= 0 and UIDropDownMenu_GetSelectedValue(m.quality_dropdown) - if quality then - add(strlower(getglobal('ITEM_QUALITY'..quality..'_DESC'))) - end - - return filter_term -end - function private.discard_continuation() Aux.scan.abort(m.search_scan_id) m.current_search().continuation = nil @@ -798,19 +742,117 @@ function private.initialize_filter_dropdown() end end -function private.import_query_string() +function private.get_form() + local query_string = '' + local function add(part) + query_string = query_string == '' and part or query_string..'/'..part + end + + add(m.name_input:GetText()) + + if m.exact_checkbox:GetChecked() then + add('exact') + end + + local min_level = m.blizzard_level(m.min_level_input:GetText()) + if min_level then + add(min_level) + end + + local max_level = m.blizzard_level(m.min_level_input:GetText()) + if max_level then + add(max_level) + end + + if m.usable_checkbox:GetChecked() then + add('usable') + end + + local class = UIDropDownMenu_GetSelectedValue(m.class_dropdown) ~= 0 and UIDropDownMenu_GetSelectedValue(m.class_dropdown) + if class then + local classes = { GetAuctionItemClasses() } + add(strlower(classes[class])) + local subclass = UIDropDownMenu_GetSelectedValue(m.subclass_dropdown) ~= 0 and UIDropDownMenu_GetSelectedValue(m.subclass_dropdown) + if subclass then + local subclasses = {GetAuctionItemSubClasses(class)} + add(strlower(subclasses[subclass])) + local slot = UIDropDownMenu_GetSelectedValue(m.slot_dropdown) ~= 0 and UIDropDownMenu_GetSelectedValue(m.slot_dropdown) + if slot then + add(strlower(getglobal(slot))) + end + end + end + + local quality = UIDropDownMenu_GetSelectedValue(m.quality_dropdown) + if quality and quality >= 0 then + add(strlower(getglobal('ITEM_QUALITY'..quality..'_DESC'))) + end + + return query_string +end + +function private.set_form(filters) + + local class_index, subclass_index + + for _, filter in filters do + if filter[1] == 'name' then + m.name_input:SetText(filter[2]) + elseif filter[1] == 'exact' then + m.exact_checkbox:SetChecked(true) + elseif filter[1] == 'min_level' then + m.min_level_input:SetText(tonumber(filter[2])) + elseif filter[1] == 'max_level' then + m.max_level_input:SetText(tonumber(filter[2])) + elseif filter[1] == 'usable' then + m.usable_checkbox:SetChecked(true) + elseif filter[1] == 'class' then + class_index = Aux.info.item_class_index(filter[2]) + UIDropDownMenu_Initialize(m.class_dropdown, m.initialize_class_dropdown) -- TODO, wtf, why is this needed + UIDropDownMenu_SetSelectedValue(m.class_dropdown, class_index) + elseif filter[1] == 'subclass' then + subclass_index = Aux.info.item_subclass_index(class_index, filter[2]) + UIDropDownMenu_Initialize(m.subclass_dropdown, m.initialize_subclass_dropdown) -- TODO, wtf, why is this needed + UIDropDownMenu_SetSelectedValue(m.subclass_dropdown, subclass_index) + elseif filter[1] == 'slot' then + UIDropDownMenu_Initialize(m.slot_dropdown, m.initialize_slot_dropdown) -- TODO, wtf, why is this needed + UIDropDownMenu_SetSelectedValue(m.slot_dropdown, Aux.info.item_slot_index(class_index, subclass_index, filter[2])) + elseif filter[1] == 'quality' then + UIDropDownMenu_Initialize(m.quality_dropdown, m.initialize_quality_dropdown) -- TODO, wtf, why is this needed + UIDropDownMenu_SetSelectedValue(m.quality_dropdown, Aux.info.item_quality_index(filter[2])) + end + end +end + +function private.import_query_string() + local components, error = Aux.filter.parse_query_string(m.search_box:GetText()) + if components then + m.set_form(components.blizzard) + m.post_components = components.post + m.filter_display:SetText(Aux.filter.indented_post_query_string(m.post_components)) + else + Aux.log(error) + end end function private.export_query_string() - + local components, error = Aux.filter.parse_query_string(m.get_form()) + if components then + m.search_box:SetText(Aux.filter.query_string({blizzard=components.blizzard, post=m.post_components})) + m.clear_form() + m.post_components = {} + m.filter_display:SetText('') + else + Aux.log(error) + end end -private.post_query = {} +private.post_components = {} function private.add_post_filter() local name = UIDropDownMenu_GetSelectedValue(m.filter_dropdown) - if name then + if name and not strfind(Aux.filter.indented_post_query_string(m.post_components), '|n', 20) then local filter = name if Aux.filter.filters[name] and Aux.filter.filters[name].input_type ~= '' then filter = filter..'/'..m.filter_input:GetText() @@ -819,10 +861,12 @@ function private.add_post_filter() local components, error, suggestions = Aux.filter.parse_query_string(filter) if components then - tinsert(m.post_query, components[1]) - m.filter_display:SetText(Aux.filter.indented_post_query_string(m.post_query)) + tinsert(m.post_components, components.post[1]) + m.filter_display:SetText(Aux.filter.indented_post_query_string(m.post_components)) m.filter_input:SetText('') m.filter_input:ClearFocus() + else + Aux.log(error) end end end diff --git a/tabs/search/frames.lua b/tabs/search/frames.lua index 01f854b..c599fb4 100644 --- a/tabs/search/frames.lua +++ b/tabs/search/frames.lua @@ -344,9 +344,7 @@ Aux.search_tab.FRAMES(function(m, public, private) btn1:SetHeight(24) btn1:SetText('Search') btn1:SetScript('OnClick', function() - m.search_box:SetText('') - m.add_filter() - m.clear_form() + m.export_query_string() m.execute() end) @@ -354,21 +352,15 @@ Aux.search_tab.FRAMES(function(m, public, private) btn2:SetPoint('LEFT', btn1, 'RIGHT', 5, 0) btn2:SetWidth(80) btn2:SetHeight(24) - btn2:SetText('Add') - btn2:SetScript('OnClick', function() - m.add_filter() - m.clear_form() - end) + btn2:SetText('Export') + btn2:SetScript('OnClick', m.export_query_string) local btn3 = Aux.gui.button(m.frame.filter, 16) btn3:SetPoint('LEFT', btn2, 'RIGHT', 5, 0) btn3:SetWidth(80) btn3:SetHeight(24) - btn3:SetText('Replace') - btn3:SetScript('OnClick', function() - m.add_filter(nil, true) - m.clear_form() - end) + btn3:SetText('Import') + btn3:SetScript('OnClick', m.import_query_string) end do local editbox = Aux.gui.editbox(m.frame.filter) @@ -519,7 +511,7 @@ Aux.search_tab.FRAMES(function(m, public, private) Aux.gui.vertical_line(m.frame.filter, 332) do local dropdown = Aux.gui.dropdown(m.frame.filter) - dropdown:SetPoint('TOPRIGHT', -200, -10) + dropdown:SetPoint('TOPRIGHT', -210, -10) dropdown:SetWidth(170) dropdown:SetHeight(10) UIDropDownMenu_Initialize(dropdown, m.initialize_filter_dropdown) @@ -553,8 +545,8 @@ Aux.search_tab.FRAMES(function(m, public, private) end do local label = Aux.gui.label(m.frame.filter, 13) - label:SetPoint('TOPLEFT', 340, -50) - label:SetWidth(200) + label:SetPoint('TOPLEFT', 350, -53) + label:SetWidth(300) label:SetJustifyH('LEFT') private.filter_display = label end diff --git a/util/filter.lua b/util/filter.lua index 275e216..e882907 100644 --- a/util/filter.lua +++ b/util/filter.lua @@ -227,6 +227,7 @@ function private.operator(str) end function private.blizzard_filter_parser() + local class_index, subclass_index local filters = {} return function(str, first) local filter @@ -242,18 +243,20 @@ function private.blizzard_filter_parser() return nil, 'Erroneous level range modifier' end elseif Aux.info.item_class_index(str) and not (filters.class and not filters.subclass and str == strlower(({ GetAuctionItemClasses() })[10])) then + class_index = Aux.info.item_class_index(str) if not filters.class then filter = {'class', str} else return nil, 'Erroneous item class modifier' end - elseif filters.class and Aux.info.item_subclass_index(Aux.info.item_class_index(filters.class), str) then + elseif class_index and Aux.info.item_subclass_index(class_index, str) then + subclass_index = Aux.info.item_subclass_index(class_index, str) if not filters.subclass then filter = {'subclass', str} else return nil, 'Erroneous item subclass modifier' end - elseif filters.subclass and Aux.info.item_slot_index(Aux.info.item_class_index(filters.class), Aux.info.item_subclass_index(Aux.info.item_class_index(filters.class), filters.subclass), str) then + elseif subclass_index and Aux.info.item_slot_index(class_index, subclass_index, str) then if not filters.slot then filter = {'slot', str} else @@ -466,15 +469,17 @@ function private.suggestions(components) end -- subclasses - if blizzard_filters.class and not blizzard_filters.subclass then - for _, subclass in ipairs({ GetAuctionItemSubClasses(Aux.info.item_class_index(blizzard_filters.class)) }) do + local class_index = Aux.info.item_class_index(blizzard_filters.class or '') + if class_index and not blizzard_filters.subclass then + for _, subclass in ipairs({ GetAuctionItemSubClasses(class_index) }) do tinsert(suggestions, subclass) end end -- slots - if blizzard_filters.class and blizzard_filters.subclass and not blizzard_filters.slot then - for _, invtype in ipairs({ GetAuctionInvTypes(Aux.info.item_class_index(blizzard_filters.class), Aux.info.item_subclass_index(Aux.info.item_class_index(blizzard_filters.class), blizzard_filters.subclass)) }) do + local subclass_index = Aux.info.item_subclass_index(class_index, blizzard_filters.subclass or '') + if subclass_index and not blizzard_filters.slot then + for _, invtype in ipairs({ GetAuctionInvTypes(class_index, Aux.info.item_subclass_index(Aux.info.item_class_index(blizzard_filters.class), blizzard_filters.subclass)) }) do tinsert(suggestions, getglobal(invtype)) end end @@ -501,20 +506,50 @@ function private.suggestions(components) return suggestions end +function public.query_string(components) + local prettified = m.query_builder() + + local blizzard_filters = {} + for _, filter in components.blizzard do + prettified.append((filter[2] or filter[1])) + end + + for _, component in components.post do + if component[1] == 'operator' then + local suffix = '' + if not component[3] then + suffix = '*' + elseif component[3] > 2 then + suffix = component[3] + end + prettified.append(component[2]..suffix) + elseif component[1] == 'filter' then + prettified.append(component[2]) + if component[3] then + prettified.append(component[3]) + end + end + end + + return prettified.get() +end + function public.indented_post_query_string(components) + local no_line_break local stack = {} local str = '' - for _, component in components.post do + for _, component in components do if str ~= '' then - str = str..'|n' + str = str..(no_line_break and ' ' or '|n') end for _=1,getn(stack) do str = str..' ' end if component[1] == 'operator' and component[2] then + no_line_break = component[2] == 'not' local suffix = '' if not component[3] then suffix = '*' From 40c144ef51c99ee598971985dcf98a65263b99b8 Mon Sep 17 00:00:00 2001 From: Manuel Simon Hirsig Date: Tue, 2 Aug 2016 02:19:31 +0200 Subject: [PATCH 8/9] big filter refactoring, filter tab changes --- tabs/search/core.lua | 6 +++++- util/filter.lua | 8 ++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/tabs/search/core.lua b/tabs/search/core.lua index deefeb6..9cd21b3 100644 --- a/tabs/search/core.lua +++ b/tabs/search/core.lua @@ -171,9 +171,12 @@ end function private.clear_form() m.name_input:SetText('') + m.name_input:ClearFocus() m.exact_checkbox:SetChecked(nil) m.min_level_input:SetText('') + m.min_level_input:ClearFocus() m.max_level_input:SetText('') + m.max_level_input:ClearFocus() m.usable_checkbox:SetChecked(nil) UIDropDownMenu_ClearAll(m.class_dropdown) UIDropDownMenu_ClearAll(m.subclass_dropdown) @@ -841,6 +844,7 @@ function private.export_query_string() if components then m.search_box:SetText(Aux.filter.query_string({blizzard=components.blizzard, post=m.post_components})) m.clear_form() + m.filter_input:ClearFocus() m.post_components = {} m.filter_display:SetText('') else @@ -852,7 +856,7 @@ private.post_components = {} function private.add_post_filter() local name = UIDropDownMenu_GetSelectedValue(m.filter_dropdown) - if name and not strfind(Aux.filter.indented_post_query_string(m.post_components), '|n', 20) then + if name and getn(Aux.util.filter(m.post_components, function(component) return component[1] == 'operator' end)) < 20 then local filter = name if Aux.filter.filters[name] and Aux.filter.filters[name].input_type ~= '' then filter = filter..'/'..m.filter_input:GetText() diff --git a/util/filter.lua b/util/filter.lua index e882907..6153341 100644 --- a/util/filter.lua +++ b/util/filter.lua @@ -387,7 +387,7 @@ function public.query(query_string) for _, component in components.post do if component[1] == 'operator' then polish_notation_counter = max(polish_notation_counter, 1) - polish_notation_counter = polish_notation_counter + (component[2] or 1) - 1 + polish_notation_counter = polish_notation_counter + (tonumber(component[2]) or 1) - 1 elseif component[1] == 'filter' then polish_notation_counter = polish_notation_counter - 1 end @@ -469,7 +469,7 @@ function private.suggestions(components) end -- subclasses - local class_index = Aux.info.item_class_index(blizzard_filters.class or '') + local class_index = blizzard_filters.class and Aux.info.item_class_index(blizzard_filters.class) if class_index and not blizzard_filters.subclass then for _, subclass in ipairs({ GetAuctionItemSubClasses(class_index) }) do tinsert(suggestions, subclass) @@ -477,9 +477,9 @@ function private.suggestions(components) end -- slots - local subclass_index = Aux.info.item_subclass_index(class_index, blizzard_filters.subclass or '') + local subclass_index = class_index and blizzard_filters.subclass and Aux.info.item_subclass_index(class_index, blizzard_filters.subclass) if subclass_index and not blizzard_filters.slot then - for _, invtype in ipairs({ GetAuctionInvTypes(class_index, Aux.info.item_subclass_index(Aux.info.item_class_index(blizzard_filters.class), blizzard_filters.subclass)) }) do + for _, invtype in ipairs({ GetAuctionInvTypes(class_index, Aux.info.item_subclass_index(class_index, blizzard_filters.subclass)) }) do tinsert(suggestions, getglobal(invtype)) end end From 75b2e8b5201c669044ab5c66a03db50678289410 Mon Sep 17 00:00:00 2001 From: Manuel Simon Hirsig Date: Tue, 2 Aug 2016 02:26:32 +0200 Subject: [PATCH 9/9] big filter refactoring, filter tab changes --- tabs/search/core.lua | 5 +++++ tabs/search/frames.lua | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/tabs/search/core.lua b/tabs/search/core.lua index 9cd21b3..697c7ff 100644 --- a/tabs/search/core.lua +++ b/tabs/search/core.lua @@ -875,3 +875,8 @@ function private.add_post_filter() end end +function private.remove_post_filter() + tremove(m.post_components) + m.filter_display:SetText(Aux.filter.indented_post_query_string(m.post_components)) +end + diff --git a/tabs/search/frames.lua b/tabs/search/frames.lua index c599fb4..01dd62d 100644 --- a/tabs/search/frames.lua +++ b/tabs/search/frames.lua @@ -526,7 +526,14 @@ Aux.search_tab.FRAMES(function(m, public, private) btn:SetWidth(170) btn:SetHeight(25) btn:SetPoint('CENTER', m.filter_dropdown, 'CENTER', 0, 0) - btn:SetScript('OnClick', m.add_post_filter) + btn:RegisterForClicks('LeftButtonUp', 'RightButtonUp') + btn:SetScript('OnClick', function() + if arg1 == 'LeftButton' then + m.add_post_filter() + elseif arg1 == 'RightButton' then + m.remove_post_filter() + end + end) private.filter_button = btn end do @@ -541,6 +548,7 @@ Aux.search_tab.FRAMES(function(m, public, private) -- Aux.completion.complete(this, function() return filter and ({filter.validator()})[3] or {} end) end) input:SetScript('OnEnterPressed', m.add_post_filter) + input:Hide() private.filter_input = input end do