private void search_button_Click(object sender, EventArgs e)
        {
            ProgressDialog progress;
            DataTable results = null;

            progress = new ProgressDialog();
            progress.AutoAdvance = true;
            progress.AddTask((p) =>
            {
                p.Update("Querying compendium...");

                // create the data table
                results = new DataTable("Monsters");
                results.Columns.Add("ID", typeof(int));
                results.Columns.Add("Name", typeof(string));
                results.Columns.Add("Level", typeof(int));
                results.Columns.Add("GroupRole", typeof(string));
                results.Columns.Add("CombatRole", typeof(string));
                results.Columns.Add("SourceBook", typeof(string));
                results.BeginLoadData();

                // query the web service, create the document, and select nodes
                using (Stream s = m_helper.SearchMonsters(name_box.Text))
                {
                    XPathDocument doc = new XPathDocument(s);
                    XPathNavigator nav = doc.CreateNavigator();
                    XPathNodeIterator iter = nav.Select("/Data/Results/Monster");

                    // load the rows
                    while (iter.MoveNext())
                    {
                        results.LoadDataRow(new object[] {
                        iter.Current.SelectSingleNode("ID").ValueAsInt,
                        iter.Current.SelectSingleNode("Name").Value,
                        iter.Current.SelectSingleNode("Level").ValueAsInt,
                        iter.Current.SelectSingleNode("GroupRole").Value,
                        iter.Current.SelectSingleNode("CombatRole").Value,
                        iter.Current.SelectSingleNode("SourceBook").Value,
                    }, false);
                    }
                }

                // complete load
                results.AcceptChanges();
                results.DefaultView.Sort = "Level ASC, Name ASC";
                results.EndLoadData();
            });

            try
            {
                progress.Show();

                grid.DataSource = results;
            }
            catch (Exception ex)
            {
                MessageBox.Show(this, ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        private void RunQuery()
        {
            ProgressDialog dialog;
            EventHandler<ProgressEventArgs> handler = null;

            // update our internal representation of the search boxes
            m_clauses = GetWhereClauses(m_searchControls);

            // run the query on the asset database again
            try
            {
                // create dialog
                dialog = new ProgressDialog();

                // create the handler for price query updates
                handler = delegate(object sender, ProgressEventArgs e)
                {
                    if (e.Max < 1) return;
                    dialog.Update("Updating market prices...", e.Progress, e.Max);
                };
                Program.PriceProvider.UpdateProgress += handler;

                // initialize the dialog and display it
                dialog.Update(0, 1);
                dialog.AddTask(UpdateAssetTable);
                dialog.Show();

                // display the fresh asset data in the grid
                grid.DataSource = m_assets.DefaultView;
                grid.AutoResizeColumns();
                UpdateAssetCount();
            }
            catch (ProgressException ex)
            {
                ShowException(ex);
            }
            finally
            {
                if (handler != null)
                    Program.PriceProvider.UpdateProgress -= handler;
            }
        }
        private void GenerateReport(string defaultTitle, string fileFilter, string defaultExt, GenerateReportDelegate reportMethod)
        {
            List<WhereClause> clauses;
            DataTable assets;
            ProgressDialog dialog;
            ReportOptionsDialog options;
            ReportOptionsDialog.AssetSourceType sourceType;
            EventHandler<ProgressEventArgs> handler = null;
            int savedSearchID = -1;

            // ask the user some stuff
            options = new ReportOptionsDialog(fileFilter, defaultExt);
            options.ReportTitle = defaultTitle;
            if (options.ShowDialog(this) == DialogResult.Cancel) return;
            sourceType = options.AssetSource;
            if (sourceType == ReportOptionsDialog.AssetSourceType.SavedSearch)
                savedSearchID = options.SavedSearchID;

            // create clauses
            clauses = new List<WhereClause>();

            try
            {
                // create dialog
                dialog = new ProgressDialog();

                // create the handler for price query updates
                handler = delegate(object sender, ProgressEventArgs e)
                {
                    if (e.Max < 1) return;
                    dialog.Update("Updating market prices...", e.Progress, e.Max);
                };
                Program.PriceProvider.UpdateProgress += handler;

                // add the progress task
                dialog.AddTask(delegate(IProgressDialog p)
                {
                    // create our clauses and get assets
                    p.Update("Querying assets...");
                    switch (sourceType)
                    {
                        case ReportOptionsDialog.AssetSourceType.All:
                            assets = AssetCache.GetAssetTable(clauses);
                            break;
                        case ReportOptionsDialog.AssetSourceType.Current:
                            if (m_assets == null) throw new ApplicationException("There are no current search results to use.");
                            assets = m_assets;
                            break;
                        case ReportOptionsDialog.AssetSourceType.SavedSearch:
                            clauses.AddRange(GetWhereClausesForSavedSearch(savedSearchID));
                            assets = AssetCache.GetAssetTable(clauses);
                            break;
                        default:
                            throw new ApplicationException("Don't know how to get assets for source type " + sourceType.ToString());
                    }

                    // generate report
                    p.Update("Generating report...");
                    reportMethod(assets, options.ReportTitle, options.ReportPath);
                });

                // run it!
                dialog.Show();
            }
            catch (Exception ex)
            {
                // let the user know we failed
                ShowException("Failed to generate report:", ex);

                // try to get rid of the partially-written file
                try { if (File.Exists(options.ReportPath)) File.Delete(options.ReportPath); }
                catch { /* pass */ }

                // get outta here, she's gonna blow!
                return;
            }
            finally
            {
                if (handler != null)
                    Program.PriceProvider.UpdateProgress -= handler;
            }

            // open the report
            if (Program.OptionsDialog["Reports.OpenReport"].ValueAsBoolean)
                System.Diagnostics.Process.Start(options.ReportPath);
        }
        private void menu_options_refresh_Click(object sender, EventArgs e)
        {
            ProgressDialog dialog;

            // refresh our asset data from EVE
            try
            {
                dialog = new ProgressDialog();
                dialog.AddTask(RefreshAssets);
                dialog.Show();
            }
            catch (ProgressException ex)
            {
                ShowException(ex);
                return;
            }
            finally
            {
                // give the user feedback on the new state of the cache, successful or not
                BeginCheckAssets();
            }

            // re-run the query
            RunQuery();
        }
        private void refresh_button_Click(object sender, EventArgs e)
        {
            ProgressDialog dialog;

            try
            {
                // grids freak out if we modify their data sources in another thread
                grid_keys.DataSource = null;
                grid_characters.DataSource = null;

                // refresh using a user-friendly progress dialog
                dialog = new ProgressDialog();
                dialog.AddTask(Program.RefreshCharacters);
                dialog.Show();

                // re-attach grids
                BindGrids();
            }
            catch (Exception ex)
            {
                MainForm.ShowException(this, "Failed to refresh characters", ex);
            }
        }
        private void go_button_Click(object sender, EventArgs e)
        {
            ProgressDialog d = new ProgressDialog();
            string character_path = character_box.Text;
            string macro_path = macro_box.Text;
            List<PowerInfo> powers = new List<PowerInfo>();
            Dictionary<string, int> skills = new Dictionary<string, int>();
            System.Text.RegularExpressions.Regex img_remover = new System.Text.RegularExpressions.Regex(@"<img.*?>", System.Text.RegularExpressions.RegexOptions.IgnoreCase);

            // save the value of the checkbox
            m_use_compendium = compendium_check.Checked;

            d.AddTask((progress) =>
            {
                progress.Update("Scanning character sheet...");

                using (FileStream fs = File.OpenRead(character_path))
                {
                    XPathDocument doc = new XPathDocument(fs);
                    XPathNavigator nav = doc.CreateNavigator();
                    XPathNodeIterator power_iter, weapon_iter;

                    // select all power nodes
                    power_iter = nav.Select("/D20Character/CharacterSheet/PowerStats/Power");

                    // update the progress steps
                    progress.Update(0, power_iter.Count);

                    // iterate them
                    while (power_iter.MoveNext())
                    {
                        string name;
                        string action_type;
                        List<string> compendium_info = new List<string>();
                        PowerUsage usage;
                        XPathNavigator power_url_nav = null;

                        try
                        {
                            // read the basic stuff
                            name = power_iter.Current.SelectSingleNode("@name").Value;
                            usage = Util.EnumParse<PowerUsage>(power_iter.Current.SelectSingleNode("specific[@name = 'Power Usage']").Value.Replace("-", ""));
                            action_type = power_iter.Current.SelectSingleNode("specific[@name = 'Action Type']").Value.Trim();

                            // get the url for the power in the compendium
                            if (m_use_compendium)
                                power_url_nav = nav.SelectSingleNode("//RulesElementTally/RulesElement[@name = \"" + name + "\"]/@url");

                            // ...and if we did that, pull down the power description
                            if (power_url_nav != null)
                            {
                                HtmlAgilityPack.HtmlDocument scraper_doc = new HtmlAgilityPack.HtmlDocument();
                                HtmlAgilityPack.HtmlNodeNavigator scraper_result;
                                XPathNodeIterator content_iter;
                                XPathNavigator scraper_nav = null;

                                // slurp
                                try
                                {
                                    using (Stream s = m_compendium.GetEntryByUrl(power_url_nav.Value))
                                    {
                                        scraper_doc.Load(s, new UTF8Encoding());
                                        scraper_nav = scraper_doc.CreateNavigator();
                                    }
                                }
                                catch (Exception ex)
                                {
                                    // if the user clicked cancel, stop attempting compendium stuff
                                    if (ex is ApplicationException && ex.Message.ToLowerInvariant().Contains("user refused"))
                                        m_use_compendium = false;
                                    else
                                        MessageBox.Show(ex.ToString(), "Compendium Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                                }

                                // if the web request succeeded...
                                if (scraper_nav != null)
                                {
                                    // select all the detail p tags
                                    content_iter = scraper_nav.Select("//div[@id = 'detail']/p");

                                    // loop through them
                                    while (content_iter.MoveNext())
                                    {
                                        string line;

                                        // cast to HtmlNodeNavigator
                                        scraper_result = content_iter.Current as HtmlAgilityPack.HtmlNodeNavigator;

                                        // skip publishing stuff
                                        if (scraper_result == null || scraper_result.CurrentNode.InnerHtml.ToLowerInvariant().Contains("published in"))
                                            continue;

                                        // grab the line, strip images, and remove [] stuff
                                        line = scraper_result.CurrentNode.InnerHtml;
                                        line = img_remover.Replace(line, "-");
                                        line = line.Replace("[W]", "<i>W</i>");

                                        // add the line
                                        compendium_info.Add(line);
                                    }
                                }
                            }

                            // select weapons
                            weapon_iter = power_iter.Current.Select("Weapon");

                            // some powers aren't attacks or don't use weapons
                            if (weapon_iter.Count < 1)
                            {
                                powers.Add(new PowerInfo(name, compendium_info, WEAPON_NONE, usage, action_type));
                            }
                            else
                            {
                                while (weapon_iter.MoveNext())
                                {
                                    PowerInfo power = new PowerInfo(name, compendium_info, weapon_iter.Current.SelectSingleNode("@name").Value, usage, action_type);

                                    // when there is more than one weapon, skip the Unarmed weapon
                                    if (power.Weapon == WEAPON_UNARMED && weapon_iter.Count > 1)
                                        continue;

                                    // read the attack info
                                    power.AttackBonus = weapon_iter.Current.SelectSingleNode("AttackBonus").ValueAsInt;
                                    power.AttackStat = weapon_iter.Current.SelectSingleNode("AttackStat").Value.Trim();
                                    power.DamageExpression = weapon_iter.Current.SelectSingleNode("Damage").Value.Trim();
                                    power.Defense = weapon_iter.Current.SelectSingleNode("Defense").Value.Trim();

                                    // all powers list an Unarmed weapon, even if they don't include an attack, set these to WEAPON_NONE
                                    if (power.Defense == "")
                                        power.Weapon = WEAPON_NONE;

                                    // add it to the list
                                    powers.Add(power);
                                }
                            }


                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }

                        // wait briefly to avoid spamming compendium
                        if (m_use_compendium)
                            System.Threading.Thread.Sleep(200);

                        // advance progress
                        progress.Advance();
                    }

                    // detect skills from the RulesElementTally
                    power_iter = nav.Select("//RulesElementTally/RulesElement[@type = 'Skill']/@name");

                    // read the value for each one
                    while (power_iter.MoveNext())
                    {
                        string name = power_iter.Current.Value;
                        int value;
                        XPathNavigator value_node = nav.SelectSingleNode("/D20Character/CharacterSheet/StatBlock/Stat[@name = '" + name + "']/@value");

                        if (value_node != null && int.TryParse(value_node.Value, out value))
                            skills[name] = value;
                    }
                }
            });

            d.AddTask((progress) =>
            {
                progress.Update("Writing macros...");

                using (FileStream fs = File.Open(macro_path, FileMode.Create, FileAccess.Write))
                {
                    XmlWriterSettings settings;

                    settings = new XmlWriterSettings();
                    settings.Encoding = new System.Text.ASCIIEncoding();
                    settings.Indent = true;
                    settings.IndentChars = "  ";
                    settings.OmitXmlDeclaration = false;

                    using (XmlWriter w = XmlWriter.Create(fs, settings))
                    {
                        w.WriteStartDocument();
                        w.WriteStartElement("list");

                        foreach (PowerInfo power in powers)
                        {
                            StringBuilder command = new StringBuilder();
                            string text, background;

                            // skip basic attacks with no weapons
                            if (IsPowerBasicAttack(power.Name) && power.Weapon == WEAPON_NONE)
                                continue;

                            // begin macro definition
                            w.WriteStartElement(MACRO_ELEMENT_NAME);
                            w.WriteElementString("saveLocation", "Token");
                            w.WriteElementString("label", power.Name);
                            w.WriteElementString("autoExecute", "true");
                            w.WriteElementString("sortby", GetSortPriority(power));

                            // set colors based on usage limits
                            if (GetColors(power.Usage, out text, out background))
                            {
                                w.WriteElementString("fontColorKey", text);
                                w.WriteElementString("colorKey", background);
                            }

                            // start with the table and header
                            command.AppendLine("<table border=\"0\">");
                            command.AppendFormat("<tr bgcolor=\"{0}\" style=\"color: white;\">", background);
                            command.AppendLine();
                            command.AppendFormat("<th align=\"left\" style=\"font-size: 1.1em; padding: 2px 4px;\">{0}</th>", power.Name);
                            command.AppendLine();
                            command.AppendFormat("<td align=\"right\" valign=\"middle\" style=\"padding: 2px 4px;\"><i>{0}</i></td></tr>", power.Weapon == WEAPON_NONE ? "" : power.Weapon);
                            command.AppendLine();
                            command.AppendLine("</tr>");

                            // write compendium info
                            foreach (string line in power.CompendiumInfo)
                                command.AppendLine("<tr><td colspan=\"2\">" + line + "</td></tr>");

                            // when we have a weapon, put the macro in the weapon's group and include the rolls
                            if (power.Weapon != WEAPON_NONE)
                            {
                                w.WriteElementString("group", "Attacks - " + power.Weapon);

                                command.AppendFormat("<tr><td nowrap><b>[1d20+{0}]</b></td><td><b>vs. {1}</b></td></tr>", power.AttackBonus, power.Defense);
                                command.AppendLine();
                                command.AppendFormat("<tr><td nowrap>[{0}]</td><td>damage</td></tr>", power.DamageExpression);
                                command.AppendLine();
                            }

                            // finish up the command
                            command.AppendLine("</table>");

                            // write the command and end element
                            w.WriteElementString("command", command.ToString());
                            w.WriteEndElement(); // MACRO_ELEMENT_NAME
                        }

                        // write skill check macros
                        foreach (KeyValuePair<string, int> skill in skills)
                        {
                            // begin macro definition
                            w.WriteStartElement(MACRO_ELEMENT_NAME);
                            w.WriteElementString("saveLocation", "Token");
                            w.WriteElementString("label", skill.Key);
                            w.WriteElementString("autoExecute", "true");
                            w.WriteElementString("group", "Skills");
                            w.WriteElementString("command", string.Format("<p><b>{0} Check</b> [1d20+{1}]</p>", skill.Key, skill.Value));
                            w.WriteEndElement(); // MACRO_ELEMENT_NAME
                        }

                        w.WriteEndDocument();
                    }
                }
            });

            try
            {
                d.Show();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }