You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
205 lines
8.0 KiB
205 lines
8.0 KiB
using System ;
|
|
using System.Text ;
|
|
using System.IO ;
|
|
using System.Threading ;
|
|
using System.Drawing ;
|
|
using System.Collections.Generic ;
|
|
using System.Windows.Forms ;
|
|
|
|
using Manina.Windows.Forms ;
|
|
using log4net ;
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
public partial class MainForm : Form
|
|
{
|
|
private readonly HashSet<string> mValidImageExtensions = new HashSet<string>{ ".png", ".jpg", ".gif" } ;
|
|
|
|
private Dictionary<string,ChartImage> mChartImages = new Dictionary<string,ChartImage>() ;
|
|
|
|
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 mStartupWebBrowser = new WebBrowser() ;
|
|
private Panel mChartImagePanel = new Panel() ;
|
|
private PictureBox mChartImagePictureBox = new PictureBox() ;
|
|
|
|
private string mSearchResultsKey = null ;
|
|
private Point? mMouseDragAnchor = null ;
|
|
private Tuple<int,int> mScrollDragAnchor ;
|
|
private double mUserZoom = 1.0 ;
|
|
private DateTime mLastKeyPressTimeStamp = DateTime.Now ;
|
|
|
|
public MainForm()
|
|
{
|
|
// initialize the form
|
|
InitializeComponent() ;
|
|
}
|
|
|
|
private void loadChartImages()
|
|
{
|
|
// initialize
|
|
ILog logger = LogManager.GetLogger( "startup" ) ;
|
|
|
|
// build an index of the config entries
|
|
HashSet<string> configIndex = new HashSet<string>(
|
|
Program.dataConfig.data.ToObject< Dictionary<string,object> >().Keys
|
|
) ;
|
|
|
|
// locate the chart images
|
|
string dataDir = Path.GetFullPath( Program.dataDir ) ;
|
|
IEnumerable<string> 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 )
|
|
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 ) ;
|
|
}
|
|
mStartupWebBrowser.DocumentText = buf ;
|
|
|
|
// allow searches
|
|
mSearchLabel.Enabled = true ;
|
|
mSearchQuery.Enabled = true ;
|
|
mSearchResults.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<ChartImage,float> > results = new List<Tuple<ChartImage,float>>() ;
|
|
foreach( ChartImage chartImage in mChartImages.Values ) {
|
|
float score ;
|
|
if ( searchQuery == "" )
|
|
score = 0 ;
|
|
else {
|
|
score = chartImage.getSearchScore( searchQuery ) ;
|
|
if ( score <= 0 )
|
|
continue ;
|
|
}
|
|
results.Add( new Tuple<ChartImage,float>( 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<ChartImage> results2 = new List<ChartImage>() ;
|
|
foreach ( var r in results )
|
|
results2.Add( r.Item1 ) ;
|
|
loadSearchResults( results2, searchQuery ) ;
|
|
}
|
|
|
|
private void loadSearchResults( IEnumerable<ChartImage> chartImages, string key )
|
|
{
|
|
// 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 ;
|
|
}
|
|
|
|
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 ;
|
|
}
|
|
}
|
|
|
|
}
|
|
|