void RenderInvestmentGraph(ToolStripStatusLabel lbl)
        {
            Image         imgOld    = lbl.BackgroundImage;
            List <string> vFinalTag = new List <string>();

            // get all investments from data
            Dictionary <string, Data.Investment> investments = Data.Investments;

            // determine total variable by sender (ae/te/both)
            int nTotal;

            if (lbl.Name.Contains("AE"))
            {
                nTotal = Data.GetTotalInvestments(Data.INVESTMENT.AE);
            }
            else if (lbl.Name.Contains("TE"))
            {
                nTotal = Data.GetTotalInvestments(Data.INVESTMENT.TE);
            }
            else
            {
                nTotal = Data.GetTotalInvestments(Data.INVESTMENT.AE) + Data.GetTotalInvestments(Data.INVESTMENT.TE);
            }

            Rectangle  rectCurrent     = new Rectangle(0, 0, lbl.Width, lbl.Height);
            Bitmap     bmpFinal        = new Bitmap(lbl.Width, lbl.Height);
            Graphics   gfx             = Graphics.FromImage(bmpFinal);
            SolidBrush brClasstagColor = new SolidBrush(Color.White);

            if (nTotal > 0)
            {
                foreach (KeyValuePair <string, Data.Investment> entry in investments)
                {
                    int nClasstagValue;

                    if (lbl.Name.Contains("AE"))
                    {
                        nClasstagValue = entry.Value.AE;
                    }
                    else if (lbl.Name.Contains("TE"))
                    {
                        nClasstagValue = entry.Value.TE;
                    }
                    else
                    {
                        nClasstagValue = entry.Value.AE + entry.Value.TE;
                    }

                    if (nClasstagValue == 0)
                    {
                        continue;                                                                         // dont change the color
                    }
                    brClasstagColor.Color = UITools.GetClassColor(entry.Key.Split("_".ToCharArray())[0]); // get color of this classtag

                    // find the % of this investment
                    float fPercent = nClasstagValue / (float)nTotal;

                    // adjust the draw rect by that %
                    rectCurrent.Width = (int)(lbl.Width * fPercent);

                    // draw it
                    gfx.FillRectangle(brClasstagColor, rectCurrent);

                    // now move over that much
                    rectCurrent.X += rectCurrent.Width;

                    // adjust tooltip based on this classtag's info
                    // string.Format("{0,-8} {1,-20} {2}", stuff)
                    //szFinalTag += UITools.FixClasstag(entry.Key) + ": \t" + ((int)(fPercent * 100)) + "%\n";
                    string szTabAmount = "\t";
                    if (entry.Key.Contains("General"))
                    {
                        szTabAmount += "\t";
                    }
                    string szPercent = string.Format("{0:#.0}", fPercent * 100) + "%";
                    vFinalTag.Add(string.Format("{0,-25}{1}{2,-6}\n", UITools.FixClasstag(entry.Key), szTabAmount, szPercent));
                }

                if (rectCurrent.X < lbl.Width) // if theres a gap left on the end, fill it
                {
                    rectCurrent.Width = lbl.Width;
                    gfx.FillRectangle(brClasstagColor, rectCurrent); // color will be the last successful classtag
                }
            }

            // render border
            gfx.DrawRectangle(Pens.Black, new Rectangle(0, 0, lbl.Width - 1, lbl.Height - 1)); // -1 cuz we wanna see it, not be ON the control border

            // gdi cleanup
            brClasstagColor.Dispose();
            gfx.Dispose();

            // set bg image
            lbl.BackgroundImage = bmpFinal;

            // get rid of old image
            if (imgOld != null)
            {
                imgOld.Dispose();
            }

            // save the "tooltip" data
            // sort the text by % first (beginning of string X.X%)
            string szFinalTag = "";

            //customerList = customerList.OrderBy(c => int.Parse(c.Code)).ToList();
            vFinalTag.Sort(new InvestmentGraphSorter());
            foreach (var szLine in vFinalTag)
            {
                szFinalTag += szLine;
            }
            lbl.Tag = szFinalTag;
        }
        public static bool LoadFromString(string szData, bool bAskToContinue)
        {
            // prepare the storage of shit to load (because might not do anything depending on file validity)
            List <LoadData> vLoadData      = new List <LoadData>();
            int             nNewLevelLimit = 1;

            try
            {
                /*
                 * Ascension Calculator
                 *
                 *  Required Level:  59
                 *  Level Limit:     60
                 *
                 *  AE Spent:        57
                 *  TE Spent:        50
                 *
                 *  AE Remaining:     3
                 *  TE Remaining:     1
                 *
                 *  Druid - Balance (0 AE, 1 TE)
                 *          Owlkin Frenzy (1/3)
                 *
                 *  Hunter - Beast Mastery (4 AE, 2 TE)
                 *          Animal Handler (1/2)
                 *          Invigoration (1/2)
                 *          Scare Beast
                 *          Tame Beast
                 *
                 *  General (2 AE, 0 TE)
                 *          Parry
                 */
                // get each line
                string[] szLines           = szData.Split("\r\n".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                string   szCurrentClasstag = "";

                foreach (var line in szLines)
                {
                    if (line.Contains("Level Limit:"))
                    {
                        int nFinalSpaceIndex = line.LastIndexOf(' ');
                        nNewLevelLimit = Int32.Parse(line.Substring(nFinalSpaceIndex + 1)); // + 1 to not include the space
                    }
                    else if (line[0] == '\t')                                               // skill line
                    {
                        string szSkillName;
                        int    nRanks = 1; // assume 1 because abilities wont change this at all

                        // no skill name contains a '/' (thank f**k)
                        if (line.Contains('/')) // talent
                        {
                            int nSlashIndex = line.IndexOf('/');
                            nRanks      = Int32.Parse(line[nSlashIndex - 1].ToString());
                            szSkillName = line.Substring(1, nSlashIndex - 3 - 1);
                        }
                        else
                        {
                            szSkillName = line.TrimStart("\t".ToCharArray());
                        }

                        // construct this pretend skill for later additage
                        vLoadData.Add(new LoadData(szCurrentClasstag, szSkillName, nRanks));
                    }
                    else if (line.Contains('(')) // classtag line
                    {
                        // rip the classtag
                        int nIndex = line.IndexOf(" (");
                        szCurrentClasstag = UITools.FixClasstag(line.Substring(0, nIndex), true);
                    }
                }
            }
            catch (Exception)
            {
                MessageBox.Show("This file is corrupt. This can happen from trying to load ascension calculator files before version 2.0 or a manually modified file.",
                                "!!!ERROR!!!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return(false); // didnt work :(
            }

            // now, ask if they want to continue
            if (bAskToContinue)
            {
                if (MessageBox.Show("All skills will be reset to perform this operation. Proceed?", "!!!WARNING!!!", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.No)
                {
                    return(false);
                }
            }

            // attempt to learn every skill in the list now that the file i/o was successful (and they wanted to)
            // (remember skills can change names in patches. make it so it "fails" but keeps going silently)

            // kill em all, first
            ResetSkills();

            // set new level limit, without warning
            LevelLimitCheck = false;
            LevelLimit      = nNewLevelLimit;
            LevelLimitCheck = true;

            SuspendLearnEvent(true);
            // check for missing skills and buil the collection of learned skills
            List <Skill> vLearning = new List <Skill>();
            bool         bGood     = true;

            foreach (var ld in vLoadData)
            {
                Skill sk = Data.LearnSkillByName(ld.Classtag, ld.SkillName, ld.TalentRanks);
                if (sk == null)
                {
                    bGood = false;
                }
                else
                {
                    vLearning.Add(sk);
                }
            }

            SuspendLearnEvent(false, vLearning);

            if (!bGood) // warn on skill failure
            {
                MessageBox.Show("This file contains skills that could not be found or learned properly. " +
                                "The remainder have been loaded. " +
                                "Try downloading the newest version of the calculator and manually remaking the build.",
                                "!!!WARNING!!!", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

            return(true); // sall good man
        }