Manage your ASL charts, play aids and other documents.
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.
 
 
 
 
asl-charts/src/ChartImage.cs

178 lines
7.5 KiB

using System ;
using System.Text ;
using System.IO ;
using System.Collections.Generic ;
using System.Windows.Forms ;
using System.Drawing ;
using Manina.Windows.Forms ;
using Newtonsoft.Json.Linq ;
using log4net ;
// --------------------------------------------------------------------
public class ChartImage
{
private string mFullPath ;
private JsonConfig mJsonConfig ;
private HashSet<string> mKeywords = new HashSet<string>() ;
private Image mImage ;
private ImageListViewItem mImageListViewItem ;
public ChartImage( string key, string fullPath, dynamic config )
{
// initialize the ChartImage
mFullPath = fullPath ;
mJsonConfig = new JsonConfig( config ) ;
mImage = null ; // nb: we load this on demand
// parse the shortcut
ILog logger = LogManager.GetLogger( "shortcuts" ) ;
string val = mJsonConfig.getStringVal( new string[]{"shortcut"} ) ;
if ( val != "" ) {
Keys? keys = Shortcut.parseShortcutString( val ) ;
if ( keys == null )
Program.logStartupMsg( "bad-shortcut", $"{key}: {val}" ) ;
else {
Shortcut shortcut = Shortcut.findRegisteredShortcut( keys.Value ) ;
if ( shortcut == null ) {
shortcut = new ChartImageShortcut( keys.Value ) ;
Shortcut.registerShortcut( shortcut ) ;
}
if ( shortcut is ChartImageShortcut ) {
logger.Info( $"Registering image shortcut: {shortcut} => {caption()}" ) ;
((ChartImageShortcut)shortcut).addChartImage( this ) ;
}
else
Program.logStartupMsg( "bad-shortcut", $"Found duplicate: {shortcut}" ) ;
}
}
// prepare for search scoring
if ( config != null && config["keywords"] != null ) {
foreach( string kywd in config["keywords"] )
mKeywords.Add( kywd.ToUpper() ) ;
}
// NOTE: Thumbnails are cached by ImageListViewItem GUID, so we reuse these objects,
// instead of creating new ones every time we reload the search results.
mImageListViewItem = new ImageListViewItem( fullPath ) ;
mImageListViewItem.Tag = this ;
mImageListViewItem.Text = caption() ;
}
public double getSearchScore( string searchQuery )
{
// initialize
searchQuery = searchQuery.ToUpper() ;
List< Tuple<string,double> > scores = new List<Tuple<string,double>>() ;
ILog logger = LogManager.GetLogger( "search" ) ;
if ( logger.IsDebugEnabled ) {
if ( mKeywords.Count == 0 )
logger.Debug( $"- \"{this.caption()}\": (no keywords)" ) ;
else {
StringBuilder buf2 = new StringBuilder() ;
foreach( string kywd in mKeywords )
buf2.Append( $"{kywd} ; " ) ;
string val = buf2.ToString() ;
logger.Debug( $"- \"{this.caption()}\": [ {val.Substring(0,val.Length-3)} ]" ) ;
}
}
// initialize the search score weights
double exactKeywordMatchScore = Program.appConfig.getDoubleVal( new string[]{"search","exactKeywordMatchScore"}, 10 ) ;
double leadingPartialKeywordMatchScore = Program.appConfig.getDoubleVal( new string[]{"search","leadingPartialKeywordMatchScore"}, 2 ) ;
double internalPartialKeywordMatchScore = Program.appConfig.getDoubleVal( new string[]{"search","internalPartialKeywordMatchScore"}, 1.5 ) ;
double exactCaptionMatchScore = Program.appConfig.getDoubleVal( new string[]{"search","exactCaptionMatchScore"}, 5 ) ;
double leadingPartialCaptionMatchScore = Program.appConfig.getDoubleVal( new string[]{"search","leadingPartialCaptionMatchScore"}, 1 ) ;
double internalPartialCaptionMatchScore = Program.appConfig.getDoubleVal( new string[]{"search","internalPartialCaptionMatchScore"}, 0.5 ) ;
// look for keyword matches
foreach ( string kywd in mKeywords ) {
if ( kywd == searchQuery ) {
scores.Add( new Tuple<string,double>( "exactKeywordMatch", exactKeywordMatchScore ) ) ;
continue ;
}
int pos = kywd.IndexOf( searchQuery ) ;
if ( pos == 0 ) {
scores.Add( new Tuple<string,double>(
$"leadingPartialKeywordMatch[{kywd}]",
Math.Min( leadingPartialKeywordMatchScore * searchQuery.Length, exactKeywordMatchScore )
) ) ;
} else if ( pos > 0 ) {
scores.Add( new Tuple<string,double>(
$"internalPartialKeywordMatch[{kywd}]",
Math.Min( internalPartialKeywordMatchScore * searchQuery.Length, exactKeywordMatchScore )
) ) ;
}
}
// look for caption matches
string caption = this.caption().ToUpper() ;
if ( searchQuery == caption )
scores.Add( new Tuple<string,double>( "exactCaptionMatch", exactCaptionMatchScore ) ) ;
else {
int pos = caption.IndexOf( searchQuery ) ;
if ( pos == 0 ) {
scores.Add( new Tuple<string,double>(
$"leadingPartialCaptionMatch[{caption}]",
Math.Min( leadingPartialCaptionMatchScore * searchQuery.Length, exactCaptionMatchScore )
) ) ;
} else if ( pos > 0 ) {
scores.Add( new Tuple<string,double>(
$"internalPartialCaptionMatch[{caption}]",
Math.Min( internalPartialCaptionMatchScore * searchQuery.Length, exactCaptionMatchScore )
) ) ;
}
}
// calculate the total score
double totalScore = 0 ;
StringBuilder buf = logger.IsDebugEnabled ? new StringBuilder() : null ;
for ( int i=0 ; i < scores.Count ; ++i ) {
totalScore += scores[i].Item2 ;
if ( buf != null ) {
if ( i > 0 )
buf.Append( " ; " ) ;
buf.Append( $"{scores[i].Item1}={scores[i].Item2:F1}" ) ;
}
}
if ( totalScore > 0 ) {
logger.Debug( $" - {buf}" ) ;
logger.Debug( $" - totalScore = {totalScore:F1}" ) ;
}
return totalScore ;
}
public string caption()
{
// return the ChartImage's caption
return jsonConfig.getStringVal(
new string[]{ "caption" },
Path.GetFileNameWithoutExtension( mFullPath )
) ;
}
public Image image()
{
// return the image data
// NOTE: It would be nice to reuse the ImageListView cache, but it only seems to cache thumbnails.
if ( mImage == null ) {
try {
mImage = Image.FromFile( mFullPath ) ;
} catch( Exception ex ) {
// NOTE: This crashes on Linux, but we can live with that - ChartImage's are created by iterating
// over files in the data directory, so if we can't load the file, it'll be because the file was deleted
// after startup, or a permissions problem i.e. things we don't really need to worry about.
Program.showErrorMsg( $"Can't load image file:\n {mFullPath}\n\n{ex}" ) ;
}
}
return mImage ;
}
public JsonConfig jsonConfig { get { return mJsonConfig ; } }
public ImageListViewItem imageListViewItem { get { return mImageListViewItem ; } }
}