diff --git a/src/MainForm.cs b/src/MainForm.cs index 907fb8e..9a9e037 100644 --- a/src/MainForm.cs +++ b/src/MainForm.cs @@ -1,5 +1,6 @@ using System ; using System.IO ; +using System.Drawing ; using System.Collections.Generic ; using System.Windows.Forms ; @@ -19,7 +20,11 @@ public partial class MainForm : Form private Label mSearchLabel = new Label() ; private TextBox mSearchQuery = new TextBox() ; private ImageListView mSearchResults = new ImageListView() ; - private PictureBox mImagePictureBox = new PictureBox() ; + private Panel mChartImagePanel = new Panel() ; + private PictureBox mChartImagePictureBox = new PictureBox() ; + + private Point? mMouseDragAnchor = null ; + private Tuple mScrollDragAnchor ; public MainForm() { diff --git a/src/MainForm.ui.cs b/src/MainForm.ui.cs index 6a9ba68..6d7d0be 100644 --- a/src/MainForm.ui.cs +++ b/src/MainForm.ui.cs @@ -52,7 +52,7 @@ public partial class MainForm : Form this.mSearchResults.SetRenderer( new AppImageListView.AppImageListViewRenderer() ) ; this.mSearchResults.CacheMode = CacheMode.Continuous ; this.mSearchResults.Location = new Point( mSearchUserControl.Location.X + mSearchUserControl.Width, margin ) ; - Size searchResultsSize = new Size( mSplitter.Width - (mSearchQuery.Location.X + mSearchQuery.Width) - margin, mSplitter.Panel1MinSize ) ; + Size searchResultsSize = new Size( mSplitter.Width - (mSearchQuery.Location.X + mSearchQuery.Width) - margin - 7, mSplitter.Panel1MinSize ) ; this.mSearchResults.Size = searchResultsSize ; this.mSearchResults.BorderStyle = BorderStyle.None ; this.mSearchResults.Anchor = AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Bottom ; @@ -61,25 +61,186 @@ public partial class MainForm : Form // initialize the bottom pane (image viewer) // FIXME! We will probably need something better to render the images, but this will do, for now... - this.mImagePictureBox.Dock = DockStyle.Fill ; - this.mImagePictureBox.SizeMode = PictureBoxSizeMode.Zoom ; - this.mSplitter.Panel2.Controls.Add( mImagePictureBox ) ; + this.mChartImagePictureBox.SizeMode = PictureBoxSizeMode.Zoom ; + // FUDGE! We need to put the PictureBox inside a Panel in order to get the h-scrollbar to appear :-/ + mChartImagePanel.Anchor = AnchorStyles.Top | AnchorStyles.Left ; + mChartImagePanel.AutoScroll = true ; + mChartImagePanel.Controls.Add( mChartImagePictureBox ) ; + mSplitter.Panel2.Controls.Add( mChartImagePanel ) ; // initialize handlers this.Load += new EventHandler( this.MainForm_Load ) ; + this.Resize += new EventHandler( this.MainForm_Resize ) ; this.mSearchQuery.TextChanged += new EventHandler( this.SearchQuery_TextChanged ) ; this.mSearchQuery.KeyDown += new KeyEventHandler( this.SearchQuery_KeyDown ) ; this.mSearchResults.KeyPress += new KeyPressEventHandler( this.SearchResults_KeyPress ) ; this.mSearchResults.SelectionChanged += new EventHandler( this.SearchResults_SelectionChanged ) ; + + mChartImagePictureBox.MouseDown += new MouseEventHandler( this.ChartImagePictureBox_MouseDown ) ; + mChartImagePictureBox.MouseMove += new MouseEventHandler( this.ChartImagePictureBox_MouseMove ) ; + mChartImagePictureBox.MouseUp += new MouseEventHandler( this.ChartImagePictureBox_MouseUp ) ; + // NOTE: We also register mouse event handlers for the panel, so that dragging will work if the user clicks + // inside the image panel, but not on the image itself (i.e. when the image is smaller than the panel). + mChartImagePanel.MouseDown += new MouseEventHandler( this.ChartImagePictureBox_MouseDown ) ; + mChartImagePanel.MouseMove += new MouseEventHandler( this.ChartImagePictureBox_MouseMove ) ; + mChartImagePanel.MouseUp += new MouseEventHandler( this.ChartImagePictureBox_MouseUp ) ; + } + + public bool preFilterMessage( ref Message msg ) + { + // FUDGE! On Windows, when we handle Ctrl/Shift-ScrollWheel, Windows still wants to scroll the ChartImage. + // To stop this, we disable mouse wheel messages and handle everything ourself :-/ + if ( msg.Msg == 0x020A ) { // nb: WM_MOUSEWHEEL + ulong wParam = (ulong)msg.WParam & 0xFFFFFFFF ; + int nLines = (int)( wParam >> 16 ) ; + if ( (nLines & 0x8000) != 0 ) + nLines = nLines - 0x10000 ; // nb: negate the 32-bit value + nLines = nLines * SystemInformation.MouseWheelScrollLines / 120 ; + if ( (wParam & 0xFFFF) == 0 ) + nLines = - nLines ; // nb: no virtual keys are down + this.scrollMainForm( nLines ) ; + return true ; + } + return false ; } private void MainForm_Load( object sender, EventArgs e ) { + // initialize + MainForm_Resize( this, null ) ; + // load the chart images loadChartImages() ; loadSearchResults( mChartImages.Values ) ; } + private void MainForm_Resize( object sender, EventArgs e ) + { + // check if we are showing a ChartImage + Image img = mChartImagePictureBox.Image ; + if ( img == null ) { + mChartImagePanel.Visible = false ; + return ; + } + mChartImagePanel.Visible = true ; + + // resize the ChartImage panel to fill the available space + mChartImagePanel.Size = new Size( + mSplitter.Panel2.Width - (Program.isMono?8:17), + mSplitter.Panel2.Height - (Program.isMono?28:40) + ) ; + + // try to size the image to fill the available width + int margin = 5 ; + float aspectRatio = (float)img.Width / (float)img.Height ; + int availableWidth = mChartImagePanel.Width - 2*margin ; + int width = availableWidth ; + int height = (int) (availableWidth / aspectRatio) ; + + // check that we're not zooming in/out too much + float zoom = (float)width / (float)img.Width ; + float minZoom = Program.appConfig.getFloatVal( new string[]{"MinZoom"}, 0.5f ) ; + float maxZoom = Program.appConfig.getFloatVal( new string[]{"MaxZoom"}, 2f ) ; + if ( minZoom > 0 && zoom < minZoom ) { + width = (int) (img.Width * minZoom) ; + height = (int) (img.Height * minZoom) ; + } else if ( maxZoom > 0 && zoom > maxZoom ) { + width = (int) (img.Width * maxZoom) ; + height = (int) (img.Height * maxZoom) ; + } + + // check if the v-scrollbar is showing + if ( height >= mChartImagePanel.Height ) { + // yup - adjust the width + int sbWidth = SystemInformation.VerticalScrollBarWidth ; + availableWidth -= sbWidth ; + width -= sbWidth ; + } + + // resize and position the ChartImage + // TODO: It would be nice to maintain the scroll position of the current image, + // but how .NET sets the scroll range/position is weird :-/ + mChartImagePictureBox.Size = new Size( width, height ) ; + if ( width < availableWidth ) + mChartImagePictureBox.Location = new Point( margin + (availableWidth - width)/2, margin ) ; + else + mChartImagePictureBox.Location = new Point( margin, margin ) ; + mChartImagePanel.HorizontalScroll.Value = 0 ; + mChartImagePanel.VerticalScroll.Value = 0 ; + } + + private void ChartImagePictureBox_MouseDown( object sender, MouseEventArgs e ) + { + // flag that the user has started to drag the ChartImage + // FUDGE! The mouse position as reported by the MouseEventArgs bounces around weirdly during mouse move, + // so we read the mouse position directly :shrug: + mMouseDragAnchor = Cursor.Position ; + mScrollDragAnchor = new Tuple( mChartImagePanel.HorizontalScroll.Value, mChartImagePanel.VerticalScroll.Value ) ; + Cursor.Current = Cursors.Hand ; + } + + private void ChartImagePictureBox_MouseMove( object sender, MouseEventArgs e ) + { + // check if a drag is in progress + if ( mMouseDragAnchor == null ) + return ; + Point pos = Cursor.Position ; + int deltaX = pos.X - mMouseDragAnchor.Value.X ; + int deltaY = pos.Y - mMouseDragAnchor.Value.Y ; + int scrollX = mScrollDragAnchor.Item1 - deltaX ; + int scrollY = mScrollDragAnchor.Item2 - deltaY ; + scrollChartImagePanel( scrollX, scrollY ) ; + } + + private void ChartImagePictureBox_MouseUp( object sender, MouseEventArgs e ) + { + // end the drag + mMouseDragAnchor = null ; + mScrollDragAnchor = null ; + Cursor.Current = Cursors.Default ; + } + + private void scrollMainForm( int nLines ) + { + // check if the mouse is over the ChartImage Panel + Point pos = Cursor.Position ; + pos = mChartImagePanel.PointToClient( pos ) ; + if ( pos.X < 0 || pos.X >= mChartImagePanel.Width || pos.Y < 0 || pos.Y >= mChartImagePanel.Height ) + return ; + if ( (Control.ModifierKeys & Keys.Shift) != 0 ) { + // scroll the ChartImage left/right + HScrollProperties hscroll = mChartImagePanel.HorizontalScroll ; + int scrollRange = hscroll.Maximum - hscroll.Minimum ; + int scrollX = hscroll.Value + (int)( scrollRange * nLines/100 ) ; + scrollChartImagePanel( scrollX, null ) ; + } else { + // scroll the ChartImage up/down + VScrollProperties vscroll = mChartImagePanel.VerticalScroll ; + int scrollRange = vscroll.Maximum - vscroll.Minimum ; + int scrollY = vscroll.Value + (int)( scrollRange * nLines/100 ) ; + scrollChartImagePanel( null, scrollY ) ; + } + } + + private void scrollChartImagePanel( int? scrollX, int? scrollY ) + { + // update the scroll position + if ( scrollX != null ) { + HScrollProperties hscroll = mChartImagePanel.HorizontalScroll ; + int maxScrollX = hscroll.Maximum - mChartImagePanel.Width + 17 ; + int scrollPos = Math.Max( 0, Math.Min( maxScrollX, scrollX.Value ) ) ; + hscroll.Value = scrollPos ; + hscroll.Value = scrollPos ; // nb: for Windows :shrug: + } + if ( scrollY != null ) { + VScrollProperties vscroll = mChartImagePanel.VerticalScroll ; + int maxScrollY = vscroll.Maximum - mChartImagePanel.Height + 17 ; + int scrollPos = Math.Max( 0, Math.Min( maxScrollY, scrollY.Value ) ) ; + vscroll.Value = scrollPos ; + vscroll.Value = scrollPos ; // nb: for Windows :shrug: + } + } + private void SearchResults_SelectionChanged( object sender, EventArgs e ) { // show the selected chart image @@ -87,9 +248,12 @@ public partial class MainForm : Form ChartImage chartImage ; if ( mSearchResults.SelectedItems.Count > 0 ) { chartImage = (ChartImage) mSearchResults.SelectedItems[0].Tag ; - mImagePictureBox.Image = chartImage.image ; + mChartImagePictureBox.Image = chartImage.image ; } else - mImagePictureBox.Image = null ; + mChartImagePictureBox.Image = null ; + mChartImagePanel.HorizontalScroll.Value = 0 ; + mChartImagePanel.VerticalScroll.Value = 0 ; + MainForm_Resize( this, null ) ; } private void SearchQuery_TextChanged( object sender, EventArgs e ) diff --git a/src/Program.cs b/src/Program.cs index 983576b..821899b 100644 --- a/src/Program.cs +++ b/src/Program.cs @@ -9,7 +9,12 @@ using log4net.Config ; public static class Program { + private class MyMessageFilter : IMessageFilter { + public bool PreFilterMessage( ref Message msg ) { return Program.preFilterMessage( ref msg ) ; } + } + public const string APP_NAME = "ASL Charts" ; + private static bool mIsMono ; private static string mBaseDir ; private static string mDataDir ; @@ -23,6 +28,7 @@ public static class Program { // initialize mBaseDir = Application.StartupPath ; + mIsMono = Type.GetType( "Mono.Runtime" ) != null ; // configure logging string fname = Path.Combine( mBaseDir, "log4net.xml" ) ; @@ -62,6 +68,9 @@ public static class Program return ; } + // install our message filter + Application.AddMessageFilter( new MyMessageFilter() ) ; + // run the application Application.EnableVisualStyles() ; Application.SetCompatibleTextRenderingDefault( false ) ; @@ -69,10 +78,21 @@ public static class Program Application.Run( mMainForm ) ; } + public static bool preFilterMessage( ref Message msg ) + { + // check if we should filter this message + if ( Program.mainForm.preFilterMessage( ref msg ) ) + return true ; + return false ; + } + public static void showInfoMsg( string msg ) { MessageBox.Show(msg,APP_NAME,MessageBoxButtons.OK,MessageBoxIcon.Information) ; } public static void showWarningMsg( string msg ) { MessageBox.Show(msg,APP_NAME,MessageBoxButtons.OK,MessageBoxIcon.Warning) ; } public static void showErrorMsg( string msg ) { MessageBox.Show(msg,APP_NAME,MessageBoxButtons.OK,MessageBoxIcon.Error) ; } + public static bool isMono { get { return mIsMono ; } } + + public static MainForm mainForm { get { return mMainForm ; } } public static JsonConfig appConfig { get { return mAppConfig ; } } public static JsonConfig dataConfig { get { return mDataConfig ; } } public static JsonConfig debugConfig { get { return mDebugConfig ; } }