using System ; using System.Text ; using System.IO ; using System.Threading ; using System.Drawing ; using System.Security ; using System.Collections.Generic ; using System.Windows.Forms ; using Manina.Windows.Forms ; using log4net ; // -------------------------------------------------------------------- public partial class MainForm : Form { private readonly HashSet mValidImageExtensions = new HashSet{ ".png", ".jpg", ".gif" } ; private Dictionary mChartImages = new Dictionary() ; private SplitContainer mSplitter = new SplitContainer() ; private UserControl mSearchUserControl = new UserControl() ; private Label mSearchLabel = new Label() ; private TextBox mSearchQuery = new TextBox() ; private ImageListView mSearchResults = new ImageListView() ; private WebBrowser mWebBrowser = new WebBrowser() ; private Panel mChartImagePanel = new Panel() ; private PictureBox mChartImagePictureBox = new PictureBox() ; private string mSearchResultsKey = null ; private Point? mMouseDragAnchor = null ; private Tuple mScrollDragAnchor ; private double mCurrZoom, mMinZoom, mMaxZoom ; private DateTime mLastKeyPressTimeStamp = DateTime.Now ; private bool mDisableProcessCmdKey = false ; private bool mSearchQueryTextChangedDisabled = false ; public MainForm() { // initialize the form InitializeComponent() ; } private void loadChartImages() { // initialize ILog logger = LogManager.GetLogger( "startup" ) ; // build an index of the config entries HashSet configIndex = new HashSet( Program.dataConfig.data.ToObject< Dictionary >().Keys ) ; // locate the chart images string dataDir = Path.GetFullPath( Program.dataDir ) ; IEnumerable files = Directory.EnumerateFiles( dataDir, "*.*", SearchOption.AllDirectories ) ; foreach( string fname in files ) { // load the next image string extn = Path.GetExtension( fname ).ToLower() ; if ( ! mValidImageExtensions.Contains( extn ) ) continue ; string key ; string fullPath = Path.GetFullPath( fname ) ; if ( fullPath.StartsWith( dataDir ) ) { key = fname.Substring( dataDir.Length ) ; key = key.Replace( "\\", "/" ) ; if ( key.StartsWith( "/" ) ) key = key.Substring( 1 ) ; } else { // NOTE: I don't think we should ever get here :-/ If we do, the user will have to manage // their configuration using full paths for the image files, but at least we will still run... key = fullPath ; } logger.Debug( $"Loading image: {key}" ) ; dynamic config = Program.dataConfig.data[ key ] ; if ( config == null ) Program.logStartupMsg( "unconfigured-image", fname.Substring(dataDir.Length+1) ) ; else configIndex.Remove( key ) ; ChartImage chartImage = new ChartImage( key, fullPath, config ) ; mChartImages[ key ] = chartImage ; // add the image to the list ImageListViewItem item = chartImage.imageListViewItem ; mSearchResults.Invoke( (MethodInvoker) ( () => mSearchResults.Items.Add( item ) ) ) ; } // log any config entries that were unused foreach( string key in configIndex ) { if ( key[0] == '_' ) continue ; Program.logStartupMsg( "unused-config", key ) ; } // everything has been loaded, update the UI this.Invoke( (MethodInvoker) onStartupCompleted ) ; } private void onStartupCompleted() { // show any startup messages string unusedConfigs = Program.getStartupMsgs( "unused-config", "WARNING: Found unused configuration entries:" ) ; string unconfiguredImages = Program.getStartupMsgs( "unconfigured-image", "WARNING: Found image files with no configuration:" ) ; string badShortcuts = Program.getStartupMsgs( "bad-shortcut", "WARNING: Couldn't parse shortcuts:" ) ; string otherMsgs = Program.getStartupMsgs( "", "" ) ; string buf = "" ; string fname = Path.Combine( Program.resourcesDir, "startup-msgs.html" ) ; if ( File.Exists( fname ) ) { buf = File.ReadAllText( fname ) ; buf = buf.Replace( "{{UNUSED-CONFIGS}}", unusedConfigs ) ; buf = buf.Replace( "{{UNCONFIGURED-IMAGES}}", unconfiguredImages ) ; buf = buf.Replace( "{{BAD-SHORTCUTS}}", badShortcuts ) ; buf = buf.Replace( "{{OTHER-MESSAGES}}", otherMsgs ) ; } mWebBrowser.DocumentText = buf ; // allow searches mSearchLabel.Enabled = true ; mSearchQuery.Enabled = true ; mSearchQuery.Focus() ; } private void updateSearchResults( string searchQuery ) { // initialize searchQuery = searchQuery.Trim() ; ILog logger = LogManager.GetLogger( "search" ) ; logger.Info( $"Updating search results: query=\"{searchQuery}\"" ) ; // search for matching chart images List< Tuple > results = new List>() ; foreach( ChartImage chartImage in mChartImages.Values ) { double score ; if ( searchQuery == "" ) score = 0 ; else { score = chartImage.getSearchScore( searchQuery ) ; if ( score <= 0 ) continue ; } results.Add( new Tuple( chartImage, score ) ) ; } // sort the search results results.Sort( (lhs, rhs) => { if ( lhs.Item2 < rhs.Item2 ) return +1 ; else if ( lhs.Item2 > rhs.Item2 ) return -1 ; // NOTE: We also sort by caption/filename to handle things like "foo-1" vs. "foo-2". return String.Compare( lhs.Item1.caption(), rhs.Item1.caption() ) ; } ) ; if ( searchQuery != "" && logger.IsInfoEnabled ) { if ( results.Count > 0 ) { logger.Info( "- Sorted results:" ) ; foreach ( var val in results ) logger.Info( $" - \"{val.Item1.caption()}\" = {val.Item2:F1}" ) ; } else logger.Info( "- No results." ) ; } else logger.Info( "- No search query, showing all chart images." ) ; // show the search results List results2 = new List() ; foreach ( var r in results ) results2.Add( r.Item1 ) ; loadSearchResults( results2, searchQuery, false ) ; } public void loadSearchResults( IEnumerable chartImages, string key, bool clearSearchQuery ) { // check if we need to reload the search results if ( key == mSearchResultsKey ) return ; // clear the search results mSearchResults.SuspendLayout() ; mSearchResults.Items.Clear() ; mSearchResults.ResumeLayout( true ) ; // load the search results mSearchResults.SuspendLayout() ; int nItems = 0 ; foreach( ChartImage chartImage in chartImages ) { ImageListViewItem item = chartImage.imageListViewItem ; mSearchResults.Items.Add( item ) ; if ( nItems++ == 0 && key != "" ) item.Selected = true ; } mSearchResults.ResumeLayout( true ) ; mSearchResultsKey = key ; // clear the search query if ( clearSearchQuery ) { mSearchQueryTextChangedDisabled = true ; mSearchQuery.Text = "" ; mSearchQueryTextChangedDisabled = false ; } } public void showShortcuts() { // load the report template string fname = Path.Combine( Program.resourcesDir, "shortcuts.html" ) ; if ( ! File.Exists( fname ) ) { Program.showErrorMsg( "Can't find the shortcuts report template." ) ; return ; } string htmlBuf = File.ReadAllText( fname ) ; // FUDGE! We also log the shortcuts, in case WebBrowser is not working e.g. on Mono :-/ ILog logger = LogManager.GetLogger( "shortcuts" ) ; // generate HTML fragments for each type of shortcut Dictionary> registeredShortcuts = Shortcut.getRegisteredShortcuts() ; string makeShortcutsHtml( string caption, Type shortcutType ) { logger.Info( $"SHORTCUTS: {caption}" ) ; List shortcuts ; if ( ! registeredShortcuts.TryGetValue( shortcutType, out shortcuts ) ) return "" ; shortcuts.Sort( (lhs, rhs) => { int rc = String.Compare( lhs.getKeyCode().ToString().ToLower(), rhs.getKeyCode().ToString().ToLower() ) ; if ( rc != 0 ) return rc ; int nLhsModifiers = lhs.countModifiers() ; int nRhsModifiers = rhs.countModifiers() ; if ( nLhsModifiers < nRhsModifiers ) return -1 ; else if ( nLhsModifiers > nRhsModifiers ) return +1 ; else return 0 ; } ) ; StringBuilder buf = new StringBuilder() ; buf.Append( $"
{caption}
" ) ; buf.Append( "" ) ; foreach( Shortcut shortcut in shortcuts ) { logger.Info( $"- {shortcut} => {shortcut.textDescription()}" ) ; buf.Append( "" ) ; buf.Append( "
" + SecurityElement.Escape(shortcut.ToString()) ) ; buf.Append( "" + shortcut.htmlDescription() ) ; } buf.Append( "
" ) ; return buf.ToString() ; } string htmlBuf2a = makeShortcutsHtml( "Search queries", typeof(SearchQueryShortcut) ) ; string htmlBuf2b = makeShortcutsHtml( "Images", typeof(ChartImageShortcut) ) ; // generate the shortcuts report if ( htmlBuf2a != "" && htmlBuf2b != "" ) { mWebBrowser.DocumentText = htmlBuf.Replace( "{{SHORTCUTS}}", "
" + htmlBuf2a + "" + htmlBuf2b + "
" ) ; } else if ( htmlBuf2a != "" ) mWebBrowser.DocumentText = htmlBuf.Replace( "{{SHORTCUTS}}", htmlBuf2a ) ; else if ( htmlBuf2b != "" ) mWebBrowser.DocumentText = htmlBuf.Replace( "{{SHORTCUTS}}", htmlBuf2b ) ; else mWebBrowser.DocumentText = htmlBuf.Replace( "{{SHORTCUTS}}", "There are no shortcuts defined." ) ; mWebBrowser.BringToFront() ; } public void setSearchQuery( string s ) { mSearchQuery.Text = s ; } private void setChartImagePanelScrollPos( int? hscrollPos, int? vscrollPos ) { // FUDGE! These need to be set twice to take effect on Windows :-/ if ( hscrollPos != null ) { mChartImagePanel.HorizontalScroll.Value = hscrollPos.Value ; mChartImagePanel.HorizontalScroll.Value = hscrollPos.Value ; } if ( vscrollPos != null ) { mChartImagePanel.VerticalScroll.Value = vscrollPos.Value ; mChartImagePanel.VerticalScroll.Value = vscrollPos.Value ; } } }