public SkillListView()
        {
            // forms settings
            Dock = System.Windows.Forms.DockStyle.Fill;

            // my settings
            m_ToolTip = new SkillToolTip(this);
            Options.RegisterEvent(Options.EVENT.OPTIONS_SHOWLISTGROUPS.ToString(), delegate(object x) { this.ShowGroups = Options.ShowListGroups; });
            Options.RegisterEvent(Data.EVENT.DATA_LEARNSKILL.ToString(),
                                  delegate(object x)
            {
                if (this.SelectedItem != null)
                {
                    List <Skill> list = x as List <Skill>;
                    foreach (var sk in list)
                    {
                        if (this.SelectedItem.RowObject == sk)
                        {
                            // unselect
                            this.SelectedItem = null;
                            break;     // done obv only 1 thing can be selected (by our settings on the OLV)
                        }
                    }
                }
                UpdateFilter();
            });

            m_decorRowRed.BorderPen      = UITools.Pen_Red;
            m_decorRowRed.BoundsPadding  = new Size(1, 1);
            m_decorRowRed.CornerRounding = 4.0f;
            m_decorRowRed.FillBrush      = new SolidBrush(Color.FromArgb(32, UITools.Red));
            Constant = false; // can change skills by default thru this listview

            // olv settings
            this.AutoGenerateColumns           = false;
            this.ShowGroups                    = true;
            this.SortGroupItemsByPrimaryColumn = false;
            this.ModelFilter                   = new ModelFilter(Delegate_Filter);
            this.UseFiltering                  = true;
            this.ShowGroups                    = Options.ShowListGroups;
            this.GroupingStrategy              = new SkillListGroupingStrategy(m_pSortProperties);
            this.ModelCanDrop                 += SkillListView_OnModelCanDrop;
            SimpleDropSink sink1 = (SimpleDropSink)DropSink;

            sink1.CanDropOnItem        = false;
            sink1.CanDropOnBackground  = true;
            SmallImageList             = UITools.SmallIconImageList;
            GroupImageList             = UITools.ClasstagsImageList;
            ShowFilterMenuOnRightClick = false;
            FullRowSelect = true;
            SelectColumnsOnRightClickBehaviour = ColumnSelectBehaviour.None;
            MultiSelect = false;
            // hot item selection
            RowBorderDecoration rbd = new RowBorderDecoration();

            rbd.BorderPen      = new Pen(Color.Black, 2);
            rbd.BoundsPadding  = new Size(1, 1);
            rbd.CornerRounding = 4.0f;
            rbd.FillBrush      = new SolidBrush(Color.FromArgb(64, 128, 128, 128));
            // Put the decoration onto the hot item
            HotItemStyle            = new HotItemStyle();
            HotItemStyle.Decoration = rbd;
            this.UseHotItem         = true;
            this.ClearHotItem(); // cuz it defaults to the first item for some reason without any mouse doing
            this.HideSelection     = false;
            this.MouseMove        += SkillListView_OnMouseMove;
            this.MouseLeave       += SkillListView_OnMouseLeave;
            FormatRow             += SkillListView_OnFormatRow;
            BeforeCreatingGroups  += SkillListView_OnBeforeCreatingGroups;
            AboutToCreateGroups   += SkillListView_OnAboutToCreateGroups;
            this.MouseClick       += SkillListView_OnMouseClick;
            this.MouseDoubleClick += SkillListView_OnMouseDoubleClick;

            // columns
            List <ColumnSetting> vColumns = new List <ColumnSetting>();

            vColumns.Add(new ColumnSetting("Name", "Name", 200));
            vColumns.Add(new ColumnSetting("Class", "Class", 80));
            vColumns.Add(new ColumnSetting("Spec", "Spec", 100));
            vColumns.Add(new ColumnSetting("RequiredLevel", "Req. Level", 90));
            vColumns.Add(new ColumnSetting("AECost", "AE", 50));
            vColumns.Add(new ColumnSetting("TECost", "TE", 50));

            foreach (var col in vColumns)
            {
                OLVColumn olvCol = GenerateColumn();
                //olvCol.AspectName = col.m_szAspectName;
                // aspectgetter way faster (doesnt use reflection for property names)
                switch (col.m_szAspectName)
                {
                case "Name":
                    // do our sortprops copy
                    m_pSortProperties.SortColumn = olvCol;
                    // init the real one to sync it
                    this.PrimarySortColumn   = olvCol;
                    this.PrimarySortOrder    = SortOrder.Ascending;
                    this.SecondarySortColumn = olvCol;
                    this.SecondarySortOrder  = SortOrder.Ascending;
                    olvCol.HeaderImageKey    = "letter";
                    olvCol.AspectGetter      = delegate(object sk) { return((sk as Skill).Name); };
                    olvCol.ImageGetter       = delegate(object sk) {
                        return((sk as Skill).IconAsString);
                    };
                    break;

                case "Class":
                    olvCol.HeaderImageKey = "warrior";
                    olvCol.AspectGetter   = delegate(object sk) { return((sk as Skill).Class); };
                    olvCol.ImageGetter    = delegate(object sk) { return((sk as Skill).Class); };
                    break;

                case "Spec":
                    olvCol.HeaderImageKey = "ability_marksmanship";
                    olvCol.AspectGetter   = delegate(object sk) { return(UITools.FixSpec((sk as Skill).Spec)); };
                    olvCol.ImageGetter    = delegate(object sk)
                    {
                        Skill skill = sk as Skill;
                        switch (skill.Spec)
                        {
                        case "Balance":
                            return("spell_nature_starfall");

                        case "Feral":
                            return("ability_racial_bearform");

                        case "Restoration":
                            if (skill.Class == "Druid")
                            {
                                return("spell_nature_healingtouch");
                            }
                            else         // shaman
                            {
                                return("spell_nature_magicimmunity");
                            }

                        case "BeastMastery":
                            return("ability_hunter_beasttaming");

                        case "Marksmanship":
                            return("ability_marksmanship");

                        case "Survival":
                            return("ability_hunter_swiftstrike");

                        case "Arcane":
                            return("spell_holy_magicalsentry");

                        case "Fire":
                            return("spell_fire_flamebolt");

                        case "Frost":
                            return("spell_frost_frostbolt02");

                        case "Holy":
                            return("spell_holy_holybolt");        // they both use this (pal/priest)

                        case "Protection":
                            if (skill.Class == "Paladin")
                            {
                                return("spell_holy_devotionaura");
                            }
                            else         // warrior
                            {
                                return("ability_warrior_defensivestance");
                            }

                        case "Retribution":
                            return("spell_holy_auraoflight");

                        case "Discipline":
                            return("spell_holy_wordfortitude");

                        case "Shadow":
                            return("spell_shadow_shadowwordpain");

                        case "Assassination":
                            return("ability_rogue_eviscerate");

                        case "Combat":
                            return("ability_backstab");

                        case "Subtlety":
                            return("ability_stealth");

                        case "Elemental":
                            return("spell_nature_lightning");

                        case "Enhancement":
                            return("spell_nature_lightningshield");

                        case "Affliction":
                            return("spell_shadow_deathcoil");

                        case "Demonology":
                            return("spell_shadow_metamorphosis");

                        case "Destruction":
                            return("spell_shadow_rainoffire");

                        case "Arms":
                            return("ability_rogue_eviscerate");

                        case "Fury":
                            return("ability_warrior_innerrage");

                        case "General":
                            return("ability_dualwield");

                        default:
                            return("");
                        }
                    };
                    break;

                case "RequiredLevel":
                    olvCol.HeaderImageKey = "level";
                    olvCol.AspectGetter   = delegate(object sk) { return((sk as Skill).RequiredLevel); };
                    break;

                case "AECost":
                    olvCol.HeaderImageKey = "ae";
                    olvCol.AspectGetter   = delegate(object sk) { return((sk as Skill).AECost); };
                    break;

                case "TECost":
                    olvCol.HeaderImageKey = "te";
                    olvCol.AspectGetter   = delegate(object sk) { return((sk as Skill).TECost); };
                    break;
                }
                olvCol.Text  = col.m_szText;
                olvCol.Width = col.m_nWidth;
            }

            SetObjects(new List <Skill>(Data.AvailableList));
        }
        override protected bool My_OnPaint(Graphics gfx)
        {
            // do default
            if (!base.My_OnPaint(gfx)) // checks for classtag validity for us
            {
                return(false);         // halt
            }
            // prepare to draw, sir
            Point ptLoc = new Point();

            string[] szLines = new string[3];

            // draw all our abilites for this classpec
            List <SkillUI> vGridData = GetGridData(Data.SelectedClasstag);

            int nIconSize = IconSize;

            foreach (var sUI in vGridData)
            {
                Point ptIcon = GetScrolledGridLocation(sUI.XGrid, sUI.YGrid);

                // prepare the text(s) to be shown
                string szSkillName = sUI.Skill.Name;

                // truncate too long names
                if (szSkillName.Length > NAME_TRUNC_LIMIT + 3)
                {
                    szSkillName  = szSkillName.Remove(NAME_TRUNC_LIMIT);
                    szSkillName += "...";
                }

                szLines[0] = szSkillName;
                szLines[1] = "Level " + sUI.Skill.RequiredLevel;
                szLines[2] = sUI.Skill.AECost + " AE";

                ptLoc.Y = ptIcon.Y + nIconSize - 8;
                // position each line (by row)
                foreach (var szLine in szLines)
                {
                    SizeF sizeString = gfx.MeasureString(szLine, m_ftText);
                    ptLoc.X  = ptIcon.X + (nIconSize >> 1) - ((int)(sizeString.Width) >> 1);
                    ptLoc.Y += (int)sizeString.Height - 1;

                    // draw text (WITH SHADOW!) (based on learned color)
                    Brush brush = Brushes.White;
                    if (Data.IsSkillLearned(sUI.Skill))
                    {
                        brush = UITools.Brush_Green;
                    }
                    else if (!Data.CanSkillBeLearned(sUI.Skill))
                    {
                        brush = UITools.Brush_Red;
                    }

                    UITools.DrawShadowString(gfx, m_ftText, brush, szLine, ptLoc.X, ptLoc.Y);
                }
            }

            // done
            return(true);
        }