Repository navigation
Expand file tree
/
Copy pathCompact.lua
More file actions
6588 lines (6392 loc) · 281 KB
/
Copy pathCompact.lua
File metadata and controls
6588 lines (6392 loc) · 281 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
-- Compact profession-frame view.
--
-- This file does not change the /cr window. It adds one button on Blizzard's
-- ProfessionsFrame, beside TrainerSpells, and opens its own view in the frame.
-- Plan and Recipes stay on /cr window. This view is only the current craft on the route:
-- the route down the left, the selected recipe and its reagents on the right.
--
-- Profession, goal, and route mode are the same saved settings as /cr. Changing
-- either window updates the other. Hand this file over with the one TOC line.
local _, CR = ...
-- 32 leaves a dark gap around the 26px category bar so the planks stay separate.
local ROW_H = 32
local TAB_ICON = "Interface\\AddOns\\CraftRoute\\icon" -- (icon.tga, the same image as icon.ico)
local BOOK = "Interface\\Icons\\INV_Misc_Book_09"
local REAGENT_SLOTS = 12
-- Skill bar sits under the native title. Dropdowns, then the list, follow it.
-- Positions follow Blizzard's own recipe page (measured from it), so flicking between the two
-- barely moves anything: the header band with a centred skill bar, the pickers where its search
-- box is, the list where its recipe list starts, the right panel where its schematic is.
-- Measured against Blizzard's own page (its header ends 60 px under the title bar, its skill bar's
-- centre 36 px under it), so nothing jumps when you flick between that page and this view.
local HEADER_H = 48 -- the dark band behind the skill bar
local RANK_BAR_Y = -34 -- skill bar centre
local RANK_DROP_Y = -64 -- the pickers: Blizzard's search / filter line
local LIST_TOP = -92 -- Blizzard's first recipe header
local DIVIDER_TOP = -57 -- right panel top
local LEFT_W = 357 -- Blizzard's recipe-list column
local COST_BAR_H = 22 -- the route cost bar under the list
local overlay, entry, list
local AttachPlainCover -- secure cover for Split / Combine buttons; defined with the enchant covers
local UpdateFavCart -- the shopping line under your list; defined with the right column
local PublishPlan -- rebuilds the route once, then tells the other windows; defined with the list
local qtyEd -- the one amount editor (your crafts' amount buttons open it)
local matPanel, MatSettings, ToggleMatPanel -- the materials side panel; defined with the right column
local installed = false
local installFailed = false
local openedAt = 0
local ui = {
selectedKey = nil,
openSteps = {},
openParts = {}, -- component chains opened under a step's reagents
openForks = {}, -- "Choose one" blocks of 3+ options shown in full
keepScroll = false,
openProf = nil,
pinned = nil, -- dropdown pick that the profession frame has not switched to yet
keepUntil = nil, -- a profession switch started here: stay on this view while it loads
-- Destinations view
destSelected = nil, -- recipe (spell) shown on the right
destSearch = "",
destSide = nil, -- the open recipe is in "left" (all recipes) or "right" (your list)
destQty = {}, -- [spell] = how many to make (the Craft view's quantity box)
destAnchor = nil, -- a recipe was just clicked: { spell, side, slot } - keep its row where it was
schemParts = {}, -- the Craft view's recipe panel: reagents opened to show how to make them
}
---------------------------------------------------------------------------
-- Profession frame helpers
---------------------------------------------------------------------------
-- this session: what the profession window was on when it last closed (the CraftRoute key opens it
-- there again) - its profession, and the one this tab was showing
local session = {}
local function OpenProfessionName()
local info
if ProfessionsFrame then
info = ProfessionsFrame.GetProfessionInfo and ProfessionsFrame:GetProfessionInfo() or ProfessionsFrame.professionInfo
end
if type(info) ~= "table" and C_TradeSkillUI and C_TradeSkillUI.GetBaseProfessionInfo then
info = C_TradeSkillUI.GetBaseProfessionInfo()
end
if type(info) ~= "table" then
if GetTradeSkillLine then
local name = GetTradeSkillLine()
if name == "Smelting" then return "Mining" end
return name ~= "" and name or nil
end
return nil
end
local name = info.parentProfessionName or info.professionName or info.name
if name == "Smelting" then return "Mining" end
return name
end
local function DB()
return CraftRouteCharDB
end
local function PlanProfession()
if ui.pinned then
local prof = CR.professions[ui.pinned]
if prof and prof.routes then return ui.pinned, prof end
end
local db = DB()
local name = db and db.profession
local prof = name and CR.professions[name]
if prof and prof.routes then return name, prof end
return nil, nil
end
local function ProfessionOptions()
local db = DB()
return CR.ProfessionPickerOptions(not db or db.showUnlearned, db and db.profession)
end
local function SelectProfession(name)
local db = DB()
if not db or not CR.professions[name] then return end
db.profession = name
db.profPicked = true
ui.pinned = name
ui.selectedKey = nil
wipe(ui.openSteps)
wipe(ui.openParts)
ui.keepScroll = false
-- (the window itself is opened by the secure cover on the dropdown entry - addon code can't)
ui.keepUntil = GetTime() + 2 -- and this view comes back once the new profession has loaded
PublishPlan()
end
-- Adopt the profession the frame is showing, when that profession has a route.
-- A dropdown pick of an unlearned profession stays put until the open one changes.
local function SyncOpenProfession()
local name = OpenProfessionName()
if not name or name == "" then return end
-- (the window's profession has loaded: a restored view has done its job)
if ui.restoreView and name == ui.restoreWindow then ui.restoreView, ui.restoreWindow = nil, nil end
if name == ui.openProf then return end
ui.openProf = name
local prof = name and CR.professions[name]
if not (prof and prof.routes) then return end
ui.pinned = nil
local db = DB()
if db and db.profession ~= name then
db.profession = name
db.profPicked = true
ui.selectedKey = nil
wipe(ui.openSteps)
wipe(ui.openParts)
ui.keepScroll = false
CR.InvalidatePlan()
-- This refresh is already running and will build the new plan.
CR.NotifyChanged({ keepPlan = true, keepLive = true, keepLocations = true, skipCompact = true })
end
end
local function ProfessionOf(recipe)
if not recipe then return nil end
for name, prof in pairs(CR.professions) do
if prof.recipes[recipe.spell] then return name end
end
end
-- Learned / how many you can make / actually crafting live in Core.lua and UICraft.lua
-- (CR.RecipeLearned, CR.CraftsReady, CR.CraftSpell). This view used to keep its own copies,
-- and each one called GetRecipeInfo per recipe.
---------------------------------------------------------------------------
-- Destinations: after the route - the recipes you want to keep handy or aim to make. Picked from
-- every recipe of the profession (left), kept per character and profession (right), each with its
-- cost to make, whether it's learned and where to get it, and the usual crafting panel.
---------------------------------------------------------------------------
local TRUE_MAX = 300 -- the expansion's real cap - not a phase or character-level cap
-- the profession whose recipes are listed (the combined guide's Fishing: Cooking)
local function DestProf(name)
local route = name and CR.Route(name)
return (route and route.recipeProf) or name
end
local function DestList(name)
local db = DB()
if not db or not name then return {} end
db.destinations = db.destinations or {}
db.destinations[name] = db.destinations[name] or {}
return db.destinations[name]
end
-- The Craft view's filter for this profession (kept per character): ticked mats, skillup,
-- learned, unlearned, and the quality / slot sub-lists. None ticked = everything.
local noDB = {}
local function DestFilters(name)
local db = DB()
if not name then return {} end
if not db then noDB[name] = noDB[name] or {} return noDB[name] end
db.destFilters = db.destFilters or {}
db.destFilters[name] = db.destFilters[name] or {}
return db.destFilters[name]
end
-- The recipe list's order for this profession (kept per character): "level" (learn skill, the
-- default), "profit" or "stat:<key>". Profit and stats go highest first unless destSortLow is set
-- (one toggle for both); level always goes low to high.
local function DestSort(name)
local db = DB()
local v = db and name and db.destSort and db.destSort[name]
-- 1.5.1 and older saved the direction in the value
if v == "profitDesc" or v == "profitAsc" then
db.destSortLow = db.destSortLow or {}
db.destSortLow[name] = (v == "profitAsc") or nil
v = "profit"
db.destSort[name] = v
end
return v or "level"
end
local function SetDestSort(name, v)
local db = DB()
if not db or not name then return end
db.destSort = db.destSort or {}
db.destSort[name] = (v ~= "level") and v or nil
end
function CR.DestSortLow(name)
local db = DB()
return (db and name and db.destSortLow and db.destSortLow[name]) and true or false
end
function CR.SetDestSortLow(name, low)
local db = DB()
if not db or not name then return end
db.destSortLow = db.destSortLow or {}
db.destSortLow[name] = low or nil
end
-- Your list's shopping selection: which recipes are ticked and how many of each (per profession,
-- kept per character). { [spell] = { on = true, qty = n } }
local function DestCart(name)
local db = DB()
if not db or not name then return {} end
db.destCart = db.destCart or {}
db.destCart[name] = db.destCart[name] or {}
return db.destCart[name]
end
local function CartEntry(name, spell)
local c = DestCart(name)
c[spell] = c[spell] or {}
return c[spell]
end
local function IsDest(name, spell)
for _, s in ipairs(DestList(name)) do if s == spell then return true end end
return false
end
local function ToggleDest(name, spell)
local l = DestList(name)
for i, s in ipairs(l) do
if s == spell then table.remove(l, i) return false end
end
table.insert(l, spell)
return true
end
-- The view you last picked with the Craft / Route buttons, for every profession (kept per
-- character); until you pick one: Route below the true max, Craft at it.
local function ViewFor(name)
if not name then return "route" end
local picked = CraftRouteCharDB and CraftRouteCharDB.compactView
if picked == "dest" or picked == "route" then return picked end
local cur = CR.GetSkill(DestProf(name))
return (cur or 0) >= TRUE_MAX and "dest" or "route"
end
-- The Craft view's layout (kept per character): "list" - your crafts on the right, a recipe opening
-- in place - or "recipe" - favorites at the top of the recipe list, the clicked recipe on the
-- right as on the Route view.
local function DestLayout()
return (CraftRouteCharDB and CraftRouteCharDB.craftLayout == "recipe") and "recipe" or "list"
end
-- What one craft's reagents cost to buy (vendor or AH price), and how many of them have no price
-- yet (not seen on the AH) - 0 when all are priced.
local function MakeCost(r)
local total, unpriced = 0, 0
for _, rg in ipairs(r.reagents or {}) do
local p = CR.GetUnitPrice and CR.GetUnitPrice(rg[1])
if p then total = total + p * rg[2] else unpriced = unpriced + 1 end
end
return total, unpriced
end
-- What one craft sells for: its items on the AH less the 5% AH cut, or (never seen on the AH) what
-- a vendor pays for them. nil for enchants (no item) and items with neither price.
local AH_CUT = 0.95
local function SellValue(r)
if not r.item or r.item <= 0 then return nil end
local makes = r.makes or 1
local ah = CR.GetAuctionPrice and CR.GetAuctionPrice(r.item)
if ah then return math.floor(ah * AH_CUT) * makes, "ah" end
-- GetItemInfo on a cache miss asks the client to load the item. The profit sort walks
-- every recipe; skip the ask when the client hasn't seen this item yet.
local cached = not (C_Item and C_Item.IsItemDataCachedByID) or C_Item.IsItemDataCachedByID(r.item)
local vendor = cached and GetItemInfo and select(11, GetItemInfo(r.item))
if type(vendor) == "number" and vendor > 0 then return vendor * makes, "vendor" end
return nil
end
-- Profit on one craft (sell value less the reagents' cost), how it sells, and how many reagents
-- have no price (their cost isn't in it).
local function Profit(r)
local v, how = SellValue(r)
if not v then return nil end
local cost, unpriced = MakeCost(r)
return v - cost, how, unpriced
end
-- "+1g 20s" green / "-35s" red
local function ProfitText(p)
if p >= 0 then return CR.ColorText("+", "40ff40") .. CR.FormatMoney(p) end
return CR.ColorText("-", "ff4040") .. CR.FormatMoney(-p)
end
-- Show a name in full: if it's wider than its space, the text gets a point or two smaller
-- (down to 8) rather than cut off with "...". The row's normal size comes back in ResetRow.
local function FitName(fs, text)
local font, size, flags = fs:GetFont()
if not font or type(size) ~= "number" then fs:SetText(text) return end
fs.crBaseSize = fs.crBaseSize or size
size = fs.crBaseSize
fs:SetFont(font, size, flags)
fs:SetText(text)
local room = tonumber(fs:GetWidth()) or 0
if room <= 0 then -- not laid out yet: the row's width less the icon, star and expand marker
local parent = fs:GetParent()
room = (parent and tonumber(parent:GetWidth()) or 0) - 80
end
if room <= 0 then return end
while size > 8 and (tonumber(fs:GetStringWidth()) or 0) > room do
size = size - 1
fs:SetFont(font, size, flags)
end
end
-- Filter > Slot: the gear slot a recipe makes (or, for an enchant, the slot it enchants), in
-- character-sheet order. Anything that isn't worn is "Other".
local SLOT_GROUPS = {
{ "head", "Head", { "INVTYPE_HEAD" } },
{ "neck", "Neck", { "INVTYPE_NECK" } },
{ "shoulder", "Shoulder", { "INVTYPE_SHOULDER" } },
{ "back", "Back", { "INVTYPE_CLOAK" } },
{ "chest", "Chest", { "INVTYPE_CHEST", "INVTYPE_ROBE" } },
{ "shirt", "Shirt", { "INVTYPE_BODY" } },
{ "tabard", "Tabard", { "INVTYPE_TABARD" } },
{ "wrist", "Wrist", { "INVTYPE_WRIST" } },
{ "hands", "Hands", { "INVTYPE_HAND" } },
{ "waist", "Waist", { "INVTYPE_WAIST" } },
{ "legs", "Legs", { "INVTYPE_LEGS" } },
{ "feet", "Feet", { "INVTYPE_FEET" } },
{ "finger", "Finger", { "INVTYPE_FINGER" } },
{ "trinket", "Trinket", { "INVTYPE_TRINKET" } },
{ "onehand", "One-Hand", { "INVTYPE_WEAPON", "INVTYPE_WEAPONMAINHAND", "INVTYPE_WEAPONOFFHAND" } },
{ "twohand", "Two-Hand", { "INVTYPE_2HWEAPON" } },
{ "ranged", "Ranged", { "INVTYPE_RANGED", "INVTYPE_RANGEDRIGHT", "INVTYPE_THROWN" } },
{ "shield", "Shield", { "INVTYPE_SHIELD" } },
{ "offhand", "Held In Off-hand", { "INVTYPE_HOLDABLE" } },
{ "bag", "Bag", { "INVTYPE_BAG", "INVTYPE_QUIVER" } },
{ "ammo", "Ammo", { "INVTYPE_AMMO" } },
{ "other", "Other", {} },
}
local SLOT_OF_INVTYPE = {}
for _, g in ipairs(SLOT_GROUPS) do for _, t in ipairs(g[3]) do SLOT_OF_INVTYPE[t] = g[1] end end
local SLOT_OF_ENCHANT = {
["2H Weapon"] = "twohand", ["Weapon"] = "onehand", ["Bracer"] = "wrist", ["Boots"] = "feet",
["Gloves"] = "hands", ["Chest"] = "chest", ["Cloak"] = "back", ["Shield"] = "shield",
["Off-Hand"] = "offhand", ["Necklace"] = "neck",
}
-- Filter > Type: armor and weapon type from the item's class / subclass (one- and two-handed count
-- as one type - Slot splits those). On CR, not local: the Filter popup's builder is at Lua's
-- 60-upvalue limit.
CR.TYPE_GROUPS = {
{ "cloth", "Cloth", "Armor" }, { "leather", "Leather", "Armor" }, { "mail", "Mail", "Armor" },
{ "plate", "Plate", "Armor" }, { "shield", "Shield", "Armor" },
{ "axe", "Axe", "Weapons" }, { "mace", "Mace", "Weapons" }, { "sword", "Sword", "Weapons" },
{ "dagger", "Dagger", "Weapons" }, { "fist", "Fist weapon", "Weapons" }, { "polearm", "Polearm", "Weapons" },
{ "staff", "Staff", "Weapons" }, { "bow", "Bow", "Weapons" }, { "crossbow", "Crossbow", "Weapons" },
{ "gun", "Gun", "Weapons" }, { "thrown", "Thrown", "Weapons" }, { "wand", "Wand", "Weapons" },
{ "other", "Not armor or a weapon", "Other" },
}
local TYPE_OF = {
[4] = { [1] = "cloth", [2] = "leather", [3] = "mail", [4] = "plate", [6] = "shield" },
[2] = { [0] = "axe", [1] = "axe", [2] = "bow", [3] = "gun", [4] = "mace", [5] = "mace", [6] = "polearm",
[7] = "sword", [8] = "sword", [10] = "staff", [13] = "fist", [15] = "dagger", [16] = "thrown",
[18] = "crossbow", [19] = "wand" },
}
function CR.RecipeType(r)
if r._type then return r._type end
local t = "other"
local f = (C_Item and C_Item.GetItemInfoInstant) or GetItemInfoInstant
if f and r.item and r.item > 0 and not select(2, CR.EnchantSlotFor(r)) then
local _, _, _, _, _, class, subclass = f(r.item)
t = TYPE_OF[class] and TYPE_OF[class][subclass] or "other"
end
r._type = t
return t
end
-- Filter > Type: armor and weapon type from the item's class / subclass (one- and two-handed count
-- as one type - Slot splits those). On CR, not local: the Filter popup's builder is at Lua's
-- 60-upvalue limit.
CR.TYPE_GROUPS = {
{ "cloth", "Cloth", "Armor" }, { "leather", "Leather", "Armor" }, { "mail", "Mail", "Armor" },
{ "plate", "Plate", "Armor" }, { "shield", "Shield", "Armor" },
{ "axe", "Axe", "Weapons" }, { "mace", "Mace", "Weapons" }, { "sword", "Sword", "Weapons" },
{ "dagger", "Dagger", "Weapons" }, { "fist", "Fist weapon", "Weapons" }, { "polearm", "Polearm", "Weapons" },
{ "staff", "Staff", "Weapons" }, { "bow", "Bow", "Weapons" }, { "crossbow", "Crossbow", "Weapons" },
{ "gun", "Gun", "Weapons" }, { "thrown", "Thrown", "Weapons" }, { "wand", "Wand", "Weapons" },
{ "other", "Other", "Other" },
}
local TYPE_OF = {
[4] = { [1] = "cloth", [2] = "leather", [3] = "mail", [4] = "plate", [6] = "shield" },
[2] = { [0] = "axe", [1] = "axe", [2] = "bow", [3] = "gun", [4] = "mace", [5] = "mace", [6] = "polearm",
[7] = "sword", [8] = "sword", [10] = "staff", [13] = "fist", [15] = "dagger", [16] = "thrown",
[18] = "crossbow", [19] = "wand" },
}
function CR.RecipeType(r)
if r._type then return r._type end
local t = "other"
local f = (C_Item and C_Item.GetItemInfoInstant) or GetItemInfoInstant
if f and r.item and r.item > 0 and not select(2, CR.EnchantSlotFor(r)) then
local _, _, _, _, _, class, subclass = f(r.item)
t = TYPE_OF[class] and TYPE_OF[class][subclass] or "other"
end
r._type = t
return t
end
local function RecipeSlot(r)
if r._slot then return r._slot end
local slot
local _, kind = CR.EnchantSlotFor(r)
if kind then
slot = SLOT_OF_ENCHANT[kind] or "other"
else
local f = (C_Item and C_Item.GetItemInfoInstant) or GetItemInfoInstant
if f and r.item and r.item > 0 then
local loc = select(4, f(r.item))
slot = type(loc) == "string" and SLOT_OF_INVTYPE[loc] or "other"
else
slot = "other"
end
end
r._slot = slot
return slot
end
-- Left: every recipe (searched / filtered). Right: your destinations, in the order you added them.
local function BuildDestItems(name)
local pn = DestProf(name)
local p = pn and CR.professions[pn]
if not p then return {}, {}, { profName = pn, dest = true } end
local q = (ui.destSearch or ""):lower()
local all, learnedOf = {}, {}
ui.destPresent = { quality = {}, slot = {}, type = {}, stats = {} } -- what this profession has (the sub-lists offer only these)
for _, r in pairs(p.recipes) do
ui.destPresent.quality[r.q or 1] = true
ui.destPresent.slot[RecipeSlot(r)] = true
ui.destPresent.type[CR.RecipeType(r)] = true
local st = CR.ItemStats(r)
for k in pairs(st) do ui.destPresent.stats[k] = true end
local learned = CR.RecipeLearned(pn, r) == true
learnedOf[r.spell] = learned
local F, ok = DestFilters(name), CR.RecipeMatches(r, q)
if ok and (F.learned or F.unlearned) then ok = (F.learned and learned) or (F.unlearned and not learned) end
if ok and F.mats then ok = CR.CraftsReady(pn, r) > 0 end
-- your level, class and skills can use the item (one still loading stays until it's in)
if ok and F.usable then ok = CR.ItemUsable(r.item) ~= false end
-- level range: equippable items whose required level is in it (none = 1; loading stays)
if ok and (F.lvlMin or F.lvlMax) then
local slot = RecipeSlot(r)
ok = slot ~= "other" and slot ~= "bag" and slot ~= "ammo" and not select(2, CR.EnchantSlotFor(r))
local lvl = ok and CR.ItemReqLevel(r.item)
if lvl then
lvl = math.max(1, lvl)
ok = lvl >= (F.lvlMin or 1) and lvl <= (F.lvlMax or 999)
end
end
if ok and F.skillup then
local diff = CR.Difficulty(r, CR.GetSkill(pn))
ok = diff ~= "grey" and diff ~= "red"
end
if ok and F.quality and next(F.quality) then ok = F.quality[r.q or 1] and true or false end
if ok and F.slot and next(F.slot) then ok = F.slot[RecipeSlot(r)] and true or false end
if ok and F.type and next(F.type) then ok = F.type[CR.RecipeType(r)] and true or false end
if ok and F.stats then
-- every ticked stat, at least its minimum (true = any amount)
for k, min in pairs(F.stats) do
local v = st[k]
if not v or v < (type(min) == "number" and min or 1) then ok = false break end
end
end
if ok then table.insert(all, r) end
end
local order = DestSort(name)
local statKey = order:match("^stat:(.+)")
local profitOf = {}
if order ~= "level" and not statKey then
for _, r in ipairs(all) do profitOf[r] = Profit(r) or false end
end
local low = CR.DestSortLow(name)
table.sort(all, function(a, b)
if statKey then
-- items without the stat at the end, whichever way round
local va, vb = CR.ItemStats(a)[statKey], CR.ItemStats(b)[statKey]
if (va ~= nil) ~= (vb ~= nil) then return va ~= nil end
if va and vb and va ~= vb then
if low then return va < vb end
return va > vb
end
elseif order ~= "level" then
-- recipes with no price at the end, whichever way round
local pa, pb = profitOf[a], profitOf[b]
if (pa ~= false) ~= (pb ~= false) then return pa ~= false end
if pa and pb and pa ~= pb then
if low then return pa < pb end
return pa > pb
end
end
if (a.learn or 0) ~= (b.learn or 0) then return (a.learn or 0) < (b.learn or 0) end
return a.name < b.name
end)
if ui.destSelected and not p.recipes[ui.destSelected] then ui.destSelected = nil end
local favs = {}
for _, spell in ipairs(DestList(name)) do
local r = p.recipes[spell]
if r then table.insert(favs, { kind = "destFav", recipe = r, prof = pn, list = name, fav = true, h = 44 }) end
end
local left = {}
for _, r in ipairs(all) do
table.insert(left, { kind = "dest", recipe = r, prof = pn, list = name, fav = IsDest(name, r.spell),
learned = learnedOf[r.spell], selected = ui.destSide == "left" and ui.destSelected == r.spell })
end
for _, f in ipairs(favs) do f.selected = ui.destSide == "right" and ui.destSelected == f.recipe.spell end
local sel = ui.destSelected and p.recipes[ui.destSelected]
if DestLayout() == "recipe" then
-- favorites (in the list's order, searched and filtered like the rest) moved to the top
local favRows, rest = {}, {}
for _, it in ipairs(left) do
it.pick = true
it.selected = ui.destSelected == it.recipe.spell
table.insert(it.fav and favRows or rest, it)
end
left = {}
if #favRows > 0 then
table.insert(left, { kind = "note", gold = true, text = "Favorites", h = 20 })
for _, it in ipairs(favRows) do table.insert(left, it) end
if #rest > 0 then table.insert(left, { kind = "note", gold = true, text = "Recipes", h = 20 }) end
end
for _, it in ipairs(rest) do table.insert(left, it) end
return left, favs, { profName = pn, dest = true, recipeView = true,
selected = sel and { kind = "dest", recipe = sel, spell = sel.spell,
crafts = math.max(1, ui.destQty[sel.spell] or 1) } or nil }
end
return left, favs, { profName = pn, dest = true,
selected = sel and { kind = "dest", recipe = sel, spell = sel.spell, crafts = 1 } or nil }
end
local function StepKey(st)
if st.recipe then return st.kind .. ":" .. st.recipe.spell .. ":" .. (st.from or 0) end
return (st.kind or "note") .. ":" .. (st.from or 0) .. ":" .. (st.text or "")
end
local function IsOpen(key)
local forced = ui.openSteps[key]
if forced ~= nil then return forced end
return key == ui.selectedKey
end
local function RangeLabel(st)
local range
if st.from and st.to and st.to ~= st.from then
range = st.from .. "-" .. st.to
elseif st.from and st.from > 0 then
range = tostring(st.from)
else
range = ""
end
local count = (st.estimated and "~" or "") .. (st.crafts or 0) .. "x"
local text = count
if range ~= "" then text = text .. " " .. range end
if st.skill == "Fishing" then return CR.ColorText("Fish ", "4fc3f7") .. CR.ColorText(text, "b0b0b0") end
if st.skill == "Cooking" then return CR.ColorText("Cook ", "ffb74d") .. CR.ColorText(text, "b0b0b0") end
return CR.ColorText(text, "b0b0b0")
end
-- Same rule as the Craft tab: the count is what you own (bags, plus bank / alts when bags are
-- short); green = the whole step is in your bags, yellow = at least one craft counting bank /
-- alts, red = not even one.
local function HaveNeed(itemID, per, crafts)
per = per or 1
local need = per * math.max(1, crafts or 1)
local bags, elsewhere = CR.BagsAndElsewhere(itemID)
local color = CR.ReagentColor(bags, elsewhere, need, per)
local have = bags >= need and bags or (bags + elsewhere)
return CR.ColorText(tostring(have), color) .. "/" .. need .. " " .. CR.ItemName(itemID), bags, need
end
-- The tab already rebuilt the plan. Trackers hear about it on the throttled
-- notify, and that notify leaves the plan, the recipe scan and the bag cache.
function PublishPlan()
CR.InvalidatePlan()
if overlay and overlay:IsShown() then overlay:Refresh() end
CR.NotifyChanged({ keepPlan = true, keepLive = true, keepLocations = true, skipCompact = true })
end
local function Changed()
ui.keepScroll = false
PublishPlan()
end
---------------------------------------------------------------------------
-- Left-hand route list
---------------------------------------------------------------------------
-- SetAtlas returns false when the atlas is missing, and throws on some clients.
local function TryAtlas(tex, atlas, useSize)
if not tex or not atlas or not tex.SetAtlas then return false end
local ok, result = pcall(tex.SetAtlas, tex, atlas, useSize and true or false)
return ok and result ~= false
end
-- Craft view: an opened recipe's detail rows form one panel under its row - a faint fill, the
-- selection colour down the left, a thin edge right, and a line under the last row.
local function BoxRow(row, part)
local b = row.crBox
if not b then
b = {}
b.fill = row:CreateTexture(nil, "BACKGROUND", nil, 1)
b.fill:SetPoint("TOPLEFT", 1, 0)
b.fill:SetPoint("BOTTOMRIGHT", -1, 0)
b.fill:SetColorTexture(0.35, 0.65, 1, 0.07)
b.bar = row:CreateTexture(nil, "ARTWORK", nil, 2)
b.bar:SetPoint("TOPLEFT", 1, 0)
b.bar:SetPoint("BOTTOMLEFT", 1, 0)
b.bar:SetWidth(3)
b.bar:SetColorTexture(0.35, 0.65, 1, 1)
b.right = row:CreateTexture(nil, "ARTWORK", nil, 1)
b.right:SetPoint("TOPRIGHT", -1, 0)
b.right:SetPoint("BOTTOMRIGHT", -1, 0)
b.right:SetWidth(1)
b.right:SetColorTexture(0.35, 0.65, 1, 0.75)
b.bottom = row:CreateTexture(nil, "ARTWORK", nil, 1)
b.bottom:SetPoint("BOTTOMLEFT", 1, 1)
b.bottom:SetPoint("BOTTOMRIGHT", -1, 1)
b.bottom:SetHeight(1)
b.bottom:SetColorTexture(0.35, 0.65, 1, 0.75)
row.crBox = b
end
local on = part ~= nil
b.fill:SetShown(on)
b.bar:SetShown(on)
b.right:SetShown(on)
b.bottom:SetShown(part == "last")
end
local function ResetRow(row)
row.item = nil
row.bg:SetColorTexture(0, 0, 0, 0)
row.sel:Hide()
row:ShowSelected(false)
row.optBg:Hide()
row.optCapL:Hide()
row.optCapR:Hide()
row.radio:Hide()
row.tick:Hide()
if row.name.SetShadowOffset then row.name:SetShadowOffset(1, -1) end
row.hover:Hide()
row.icon:Hide()
row.barLeft:Hide()
row.barMid:Hide()
row.barRight:Hide()
row.chevron:Hide()
row.chevron.glow:Hide()
row.name:SetText("")
row.name:SetTextColor(1, 1, 1)
row.right:SetText("")
row.act:Hide()
row.act:SetScript("OnClick", nil)
row.buy:Hide()
row.buy:SetScript("OnClick", nil)
if row.crSub then row.crSub:Hide() end
if row.crSub2 then row.crSub2:Hide() end
row.right:SetWidth(78)
row.right:ClearAllPoints()
row.right:SetPoint("RIGHT", row.chevron, "LEFT", -2, 0)
row.crEditing = qtyEd and qtyEd:IsShown() and row.crQtyBtn ~= nil and qtyEd.btn == row.crQtyBtn
for _, w in ipairs({ "crPin", "crQty", "crCreate", "crAll", "crCart", "crRoute", "crPick", "crQtyBtn", "crChoice" }) do
if row[w] then row[w]:Hide() end
end
if row.name.crBaseSize then
local font, size, flags = row.name:GetFont()
if font and size ~= row.name.crBaseSize then row.name:SetFont(font, row.name.crBaseSize, flags) end
end
if row.crStar then
row.crStar:Hide()
row.right:ClearAllPoints()
row.right:SetPoint("RIGHT", row.chevron, "LEFT", -2, 0)
end
row.actMacro = nil
row.name:ClearAllPoints()
row.name:SetPoint("LEFT", 8, 0)
row.name:SetPoint("RIGHT", row.right, "LEFT", -4, 0)
row:SetScript("OnClick", nil)
row:SetScript("OnEnter", nil)
end
-- A row's tooltip (Craft and Route views) hangs down from the row's top corner (beside it) instead
-- of standing on it - unless it would run off the bottom of the screen, then it stays as it was.
local function DropTooltip(row)
if not (GameTooltip:IsShown() and GameTooltip:GetOwner() == row) then return end
local top, h = row:GetTop(), GameTooltip:GetHeight()
if not (top and h) then return end
local below = top * row:GetEffectiveScale() - h * GameTooltip:GetEffectiveScale()
if below < 0 then return end
GameTooltip:ClearAllPoints()
GameTooltip:SetPoint("TOPLEFT", row, "TOPRIGHT")
end
local function ShowTooltip(row)
local item = row.item
if not item then return end
-- every row's tooltip opens to its right (your crafts list's too)
GameTooltip:SetOwner(row, "ANCHOR_RIGHT")
if item.kind == "reagent" then
GameTooltip:SetItemByID(item.itemID)
GameTooltip:AddLine(" ")
GameTooltip:AddLine("Where you have it:", 1, 0.82, 0)
CR.AddLocationLines(GameTooltip, item.itemID)
local info = item.info
if info then
GameTooltip:AddLine(" ")
local what = info.recipe and ((info.smelting and "Smelt it" or "Make it") .. " with " .. info.prof.name)
or (info.convert.uses > 1 and "Combine 3 lesser into 1 greater" or "Split 1 greater into 3 lesser")
GameTooltip:AddLine(what .. " - click + to see what from.", 0.2, 1, 0.2, true)
if info.missing > 0 then
GameTooltip:AddLine(string.format("%d short: %d %s cover it.", info.missing, info.toMake,
info.recipe and "crafts" or "uses"), 1, 0.4, 0.4, true)
end
if info.recipe and info.learned == false then
GameTooltip:AddLine("Not learned: " .. CR.FactionText(info.recipe.pattern or info.recipe.src or ""), 1, 0.6, 0.4, true)
elseif info.recipe and not info.skillOK then
GameTooltip:AddLine("Needs " .. CR.LearnAt(info.recipe) .. " " .. info.prof.name .. " skill.", 1, 0.4, 0.4, true)
end
end
elseif item.kind == "step" and item.step and item.step.recipe then
CR.RecipeTooltip(GameTooltip, item.step.recipe, item.step.crafts)
if item.step.note then GameTooltip:AddLine(CR.FactionText(item.step.note), 0.7, 0.7, 0.7, true) end
if item.step.auto then GameTooltip:AddLine("Filled in past the end of the guide.", 1, 0.6, 0.4, true) end
GameTooltip:AddLine(item.open and "Click to collapse." or "Click to expand the reagents.", 0.6, 0.6, 0.6)
elseif item.kind == "option" and item.step then
local st = item.step
GameTooltip:SetText(st.letter .. ": " .. st.label, 1, 1, 1, 1, true)
if CR.HasAuctionator() and ((st.cost or 0) > 0 or (st.unpricedN or 0) > 0) then
GameTooltip:AddLine("Materials to buy: " .. CR.FormatMoney(st.cost or 0)
.. ((st.unpricedN or 0) > 0 and CR.ColorText(string.format(" (+%d unpriced)", st.unpricedN), "ff9966") or ""),
1, 1, 1, true)
end
GameTooltip:AddLine(st.selected and "|TInterface\\RaidFrame\\ReadyCheck-Ready:12|t Chosen - this is the path the route follows."
or "Click to choose this path instead.", st.selected and 0.4 or 1, st.selected and 1 or 0.82, st.selected and 0.4 or 0, true)
elseif item.kind == "task" then
GameTooltip:SetText(item.task.title or "", 1, 0.82, 0, 1, true)
if item.task.text and item.task.text ~= "" then GameTooltip:AddLine(item.task.text, 1, 1, 1, true) end
GameTooltip:AddLine("Click to see what to do and where.", 0.6, 0.6, 0.6)
elseif item.kind == "dest" or item.kind == "destFav" then
CR.RecipeTooltip(GameTooltip, item.recipe, 1)
GameTooltip:AddLine(item.selected and "Click to close it." or "Click to open it - what it needs, where to get it, and crafting.",
0.4, 0.8, 1, true)
GameTooltip:AddLine(item.fav and "The star takes it off your list." or "The star adds it to your list.", 0.6, 0.6, 0.6, true)
elseif item.kind == "dProfit" then
GameTooltip:SetText("Profit", 1, 0.82, 0)
if item.how == "vendor" then
GameTooltip:AddLine("Not seen on the Auction House, so this is what a vendor pays for it, less what its materials cost.", 1, 1, 1, true)
elseif item.how == "ah" then
GameTooltip:AddLine("Its Auction House price less the 5% AH cut, less what its materials cost. Scales with the amount you craft.", 1, 1, 1, true)
else
GameTooltip:AddLine("It hasn't been seen on the Auction House and vendors don't buy it.", 1, 1, 1, true)
end
if item.unpriced > 0 then
GameTooltip:AddLine(string.format("%d of its materials have no price yet, so their cost isn't counted.", item.unpriced), 1, 0.6, 0.4, true)
end
elseif item.kind == "dInfo" then
GameTooltip:SetText(item.learned and "Learned" or "Not Learned", item.learned and 0.25 or 1, item.learned and 1 or 0.4, 0.25)
elseif item.kind == "dPin" then
GameTooltip:SetText(item.place.name)
GameTooltip:AddLine("Pin puts it on your map and tracks it on screen.", 1, 1, 1, true)
elseif item.kind == "toggle" then
GameTooltip:SetText(item.open and "Show only the chosen option" or "Show every option to choose from", 1, 1, 1)
elseif item.kind == "note" then
GameTooltip:SetText(item.tip or item.text or "", 1, 0.82, 0, 1, true) -- long guide notes wrap
else
GameTooltip:Hide()
return
end
GameTooltip:Show()
end
local function ApplyRow(row, item)
ResetRow(row)
-- this row's amount was being edited and it now shows something else: finish that edit first
if row.crEditing and not (item.kind == "destFav" and item.recipe and qtyEd.spell == item.recipe.spell
and qtyEd.list == item.list) then
qtyEd:ClearFocus()
end
row.item = item
if item.box or row.crBox then BoxRow(row, item.box) end
if overlay and overlay.enchant and overlay.enchant:GetParent() == row and item.kind ~= "dEnchant" then
overlay.enchant:Hide()
end
row:SetScript("OnEnter", function(self)
self.hover:Show()
ShowTooltip(self)
DropTooltip(self)
end)
row:SetScript("OnLeave", function(self)
self.hover:Hide()
GameTooltip_Hide()
end)
if item.kind == "note" then
row.name:SetText(item.text or "")
-- 0 is falsy, so `gold and 0 or 0.75` cannot produce a gold blue channel.
if item.gold then
row.name:SetTextColor(1, 0.82, 0)
else
row.name:SetTextColor(0.75, 0.75, 0.75)
end
if item.icon then
row.icon:SetTexture(item.icon)
row.icon:Show()
row.icon:ClearAllPoints()
row.icon:SetPoint("LEFT", 6, 0)
row.name:ClearAllPoints()
row.name:SetPoint("LEFT", row.icon, "RIGHT", 4, 0)
row.name:SetPoint("RIGHT", -8, 0)
end
return
end
if item.kind == "task" then
local t = item.task
row:ShowSelected(item.selected and true or false)
row.icon:SetTexture(t.icon or BOOK)
row.icon:Show()
row.icon:ClearAllPoints()
row.icon:SetPoint("LEFT", 6, 0)
local text
if t.waiting then
text = CR.ColorText(string.format("At %d: ", t.waiting), "66aaff") .. (t.title or "")
if item.selected then row.name:SetTextColor(1, 1, 1) else row.name:SetTextColor(0.7, 0.7, 0.7) end
elseif t.done then
text = CR.ColorText("Done: ", "40c040") .. (t.title or "")
if item.selected then row.name:SetTextColor(1, 1, 1) else row.name:SetTextColor(0.6, 0.6, 0.6) end
else
-- a step to do (not a craft): no "To do:" label - its title is orange instead, selected or
-- not (the selection box already shows which is selected)
text = t.title or ""
row.name:SetTextColor(1, 0.6, 0.2)
end
-- the title gets the whole line; the skill it's at goes just under it (as the route's steps)
local from = item.action.from
local sub = from and from > 0 and tostring(from) or nil
row.right:SetText("")
row.name:ClearAllPoints()
row.name:SetPoint("LEFT", row.icon, "RIGHT", 4, sub and 6 or 0)
row.name:SetPoint("RIGHT", row, "RIGHT", -8, sub and 6 or 0)
FitName(row.name, text) -- (a title that still doesn't fit gets a point smaller, not cut off)
if sub then
if not row.crSub then
row.crSub = row:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
row.crSub:SetJustifyH("LEFT")
end
row.crSub:ClearAllPoints()
row.crSub:SetPoint("TOPLEFT", row.name, "BOTTOMLEFT", 0, -1)
row.crSub:SetPoint("RIGHT", row, "RIGHT", -8, 0)
row.crSub:SetWordWrap(false)
row.crSub:SetText(CR.ColorText(sub, "b0b0b0"))
row.crSub:Show()
end
row:SetScript("OnClick", function()
ui.selectedKey = item.key
ui.keepScroll = true
if overlay then overlay:Refresh() end
end)
return
end
-- Craft view: an opened recipe's details, in rows under it
if item.kind == "dInfo" then
-- Add to route / Remove from route: a target the route levels you to and makes (as the main
-- window's Recipes tab ticks do). A guide followed strictly in order takes no targets.
if not row.crRoute then
row.crRoute = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
row.crRoute:SetSize(118, 20)
CR.ThemeRegisterButton(row.crRoute)
end
local rt = CR.Route and CR.Route(item.list)
local targets = CR.ProfTable("targets", item.prof)
local inRoute = targets[item.recipe.spell] ~= nil
local b = row.crRoute
b:ClearAllPoints()
b:SetPoint("RIGHT", row, "RIGHT", -8, 0)
b:SetText(inRoute and "Remove from route" or "Add to route")
b:SetEnabled(not (rt and rt.sequential))
b:SetScript("OnClick", function()
local t = CR.ProfTable("targets", item.prof)
t[item.recipe.spell] = (t[item.recipe.spell] == nil) and 1 or nil
ui.keepScroll = true
PublishPlan()
end)
b:SetScript("OnEnter", function(self)
GameTooltip:SetOwner(self, "ANCHOR_LEFT")
if rt and rt.sequential then
GameTooltip:SetText("This guide is followed in order", 1, 0.82, 0)
GameTooltip:AddLine("It doesn't take extra recipes as targets.", 1, 1, 1, true)
else
GameTooltip:SetText(inRoute and "Remove from route" or "Add to route", 1, 0.82, 0)
GameTooltip:AddLine(inRoute and "Takes it back off the route's targets."
or "Makes it a target on the Route: it's crafted at the right point (once you can), "
.. "and its materials join the route's shopping.", 1, 1, 1, true)
end
GameTooltip:Show()
end)
b:SetScript("OnLeave", GameTooltip_Hide)
b:Show()
row.name:ClearAllPoints()
row.name:SetPoint("LEFT", row, "LEFT", 32, 0)
row.name:SetPoint("RIGHT", b, "LEFT", -6, 0)
if item.learned then
row.name:SetText(CR.ColorText("Learned", "40ff40"))
else
row.name:SetText(CR.ColorText("Not Learned", "ff6060") .. CR.ColorText(" learn at " .. CR.LearnAt(item.recipe), "aaaaaa"))
end
return
end
-- where it comes from, wrapped (long "Sold by" lists go on as many of these rows as they need)
if item.kind == "dText" then
if not row.crSub then
row.crSub = row:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
row.crSub:SetJustifyH("LEFT")
if CR.NoteThemeFonts then CR.NoteThemeFonts(row.crSub) end
end
row.crSub:ClearAllPoints()
row.crSub:SetPoint("TOPLEFT", row, "TOPLEFT", 32, -2)
row.crSub:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", -8, 2)
row.crSub:SetWordWrap(true)
if row.crSub.SetJustifyV then row.crSub:SetJustifyV("MIDDLE") end
row.crSub:SetText(item.text)
row.crSub:Show()
return
end
-- the enchant target slot, moved into this row while the Craft view shows an enchant
if item.kind == "dEnchant" then
local e = overlay and overlay.enchant
if e and e.fits then
e:SetParent(row)
e:ClearAllPoints()
e:SetPoint("TOPLEFT", row, "TOPLEFT", 34, -4)
e:SetPoint("RIGHT", row, "RIGHT", -8, 0)
e:SetFrameLevel(row:GetFrameLevel() + 5)
e:Show()
end
return
end
if item.kind == "dSpacer" then return end -- (the target slot is two rows tall)
-- what it costs to make, just above the crafting row
if item.kind == "dCost" then
row.name:ClearAllPoints()
row.name:SetPoint("LEFT", row, "LEFT", 32, 0)
row.name:SetPoint("RIGHT", row, "RIGHT", -8, 0)
row.name:SetText(CR.ColorText(string.format("Cost to make %d: ", item.qty), "aaaaaa") .. item.cost)
return
end
-- what it makes you selling them (scales with the amount box, like the cost)
if item.kind == "dProfit" then
row.name:ClearAllPoints()
row.name:SetPoint("LEFT", row, "LEFT", 32, 0)
row.name:SetPoint("RIGHT", row, "RIGHT", -8, 0)
local label = CR.ColorText(string.format("Profit for %d: ", item.qty), "aaaaaa")
if item.profit then
row.name:SetText(label .. ProfitText(item.profit)
.. (item.how == "vendor" and CR.ColorText(" (vendor)", "aaaaaa") or "")
.. (item.unpriced > 0 and CR.ColorText(" (cost incomplete)", "ff9966") or ""))
else
row.name:SetText(label .. CR.ColorText("no price for it yet", "888888"))
end
return
end
if item.kind == "dPin" then
if not row.crPin then
row.crPin = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
row.crPin:SetSize(40, 20)
row.crPin:SetText("Pin")
CR.ThemeRegisterButton(row.crPin)
end
row.crPin:ClearAllPoints()
row.crPin:SetPoint("LEFT", row, "LEFT", 32, 0)
row.crPin:SetScript("OnClick", function() if CR.PinPlace then CR.PinPlace(item.place) end end)
row.crPin:Show()
row.name:ClearAllPoints()
row.name:SetPoint("LEFT", row.crPin, "RIGHT", 6, 0)
row.name:SetPoint("RIGHT", row, "RIGHT", -8, 0)