/*============================================================================= Project: SharpImage Module: siImageDialog.cs Language: C# Author: Dan Mueller Date: $Date: 2007-08-25 10:46:54 +1000 (Sat, 25 Aug 2007) $ Revision: $Revision: 16 $ Copyright (c) Queensland University of Technology (QUT) 2007. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notices for more information. =============================================================================*/ using System; using System.IO; using System.Data; using System.Text; using System.Drawing; using System.Diagnostics; using System.ComponentModel; using System.Windows.Forms; using System.Collections.Generic; using System.Runtime.InteropServices; namespace SharpImage.Main { public abstract class siImageDialog { #region Instance Members //===================================================================== protected delegate int OFNHookProcDelegate(int hdlg, int msg, int wParam, int lParam); private int m_hLabelPixelType = 0; private int m_hComboPixelType = 0; private int m_hLabelPixelArray = 0; private int m_hComboPixelArray = 0; private int m_hLabelDimensions = 0; private int m_hComboDimensions = 0; private int m_hLabelUseCompression = 0; private int m_hComboUseCompression = 0; protected Form m_Parent = null; //===================================================================== #endregion #region Construction and Disposal //===================================================================== //===================================================================== #endregion #region Properties //===================================================================== #region Title //===================================================================== protected string m_Title = string.Empty; /// /// Get/set the title for the dialog. /// The default is empty. /// public string Title { get { return this.m_Title; } set { this.m_Title = value; } } //===================================================================== #endregion #region DefaultExt //===================================================================== private string m_DefaultExt = string.Empty; /// /// Get/set the default extension for the dialog. /// public string DefaultExt { get { return this.m_DefaultExt; } set { this.m_DefaultExt = value; } } //===================================================================== #endregion #region Filter //===================================================================== private string m_Filter = string.Empty; /// /// Get/set the filter string for the dialog. /// Eg. "FileType1 (*.ty1)|*.ty1|FileType2 (*.ty2)|*.ty2" /// public string Filter { get { return this.m_Filter; } set { this.m_Filter = value; } } //===================================================================== #endregion #region FilterIndex //===================================================================== private int m_FilterIndex; /// /// Gets/sets the default filter index. /// public int FilterIndex { get { return this.m_FilterIndex; } set { this.m_FilterIndex = value; } } //===================================================================== #endregion #region FileName //===================================================================== private string m_FileName = string.Empty; /// /// Get/set the selected filename. /// public string FileName { get { return this.m_FileName; } set { this.m_FileName = value; } } //===================================================================== #endregion #region PixelType //===================================================================== private itk.itkPixelTypeEnum m_PixelType; /// /// Gets/sets the pixel type selected by the user. /// public itk.itkPixelTypeEnum PixelType { get { return this.m_PixelType; } set { this.m_PixelType = value; } } //===================================================================== #endregion #region ShowPixelArray //===================================================================== private bool m_ShowPixelArray = true; /// /// Gets/sets if the pixel array label and combobox are displayed. /// public bool ShowPixelArray { get { return this.m_ShowPixelArray; } set { this.m_ShowPixelArray = value; } } //===================================================================== #endregion #region PixelArray //===================================================================== private itk.itkPixelArrayEnum m_PixelArray; /// /// Gets/sets the pixel array selected by the user. /// public itk.itkPixelArrayEnum PixelArray { get { return this.m_PixelArray; } set { this.m_PixelArray = value; } } //===================================================================== #endregion #region NumberOfComponentsPerPixel //===================================================================== /// /// Gets the number of components in the pixel array. /// Returns 0 if the number of components is unknown. /// public uint NumberOfComponentsPerPixel { get { switch (this.m_PixelArray) { case itk.itkPixelArrayEnum.Scalar: return 1U; case itk.itkPixelArrayEnum.ArrayRGB: return 3U; case itk.itkPixelArrayEnum.ArrayRGBA: return 4U; case itk.itkPixelArrayEnum.ArrayVector: // Fall through case itk.itkPixelArrayEnum.ArrayCovariantVector: return this.m_Dimensions; default: return 0U; } } } //===================================================================== #endregion #region ShowDimensions //===================================================================== private bool m_ShowDimensions = true; /// /// Gets/sets if the dimensions label and combobox are displayed. /// public bool ShowDimensions { get { return this.m_ShowDimensions; } set { this.m_ShowDimensions = value; } } //===================================================================== #endregion #region Dimensions //===================================================================== private uint m_Dimensions = 2; /// /// Gets/sets the number of image dimensions selected by the user. /// public uint Dimensions { get { return this.m_Dimensions; } set { this.m_Dimensions = value; } } //===================================================================== #endregion #region ShowUseCompression //===================================================================== private bool m_ShowUseCompression = false; /// /// Gets/sets if the use compression label and checkbox are displayed. /// public bool ShowUseCompression { get { return this.m_ShowUseCompression; } set { this.m_ShowUseCompression = value; } } //===================================================================== #endregion #region UseCompression //===================================================================== private bool m_UseCompression = false; /// /// Gets/sets the if the image IO object should use compression. /// public bool UseCompression { get { return this.m_UseCompression; } set { this.m_UseCompression = value; } } //===================================================================== #endregion //===================================================================== #endregion #region Public Methods //===================================================================== /// /// Show the dialog. /// /// The parent form. /// public abstract DialogResult ShowDialog(Form parent); //===================================================================== #endregion #region Protected and Private Methods //===================================================================== protected int HookProc(int hdlg, int msg, int wParam, int lParam) { int parent = GetParent(hdlg); switch (msg) { case WM_INITDIALOG: // Resize and center the dialog // NOTE: The resize is important to ensure the new controls are shown Rectangle sr = Screen.FromControl(this.m_Parent).Bounds; RECT cr = new RECT(); GetWindowRect(parent, ref cr); int x = (sr.Right + sr.Left - (cr.Right - cr.Left)) / 2; int y = 0; int y1 = 64; int y2 = 32; if (this.m_ShowDimensions) { y1 += 64; y2 += 32; } if (this.m_ShowPixelArray) { y1 += 64; y2 += 32; } if (this.m_ShowUseCompression) { y1 += 64; y2 += 32; } y = (sr.Bottom + sr.Top - (cr.Bottom - cr.Top + y1)) / 2; SetWindowPos(parent, 0, x, y, cr.Right - cr.Left, cr.Bottom - cr.Top + y2, SWP_NOZORDER); // Find the label to position the PixelType label int windowFileTypeLabel = GetDlgItem(parent, 0x441); RECT aboveRect = new RECT(); GetWindowRect(windowFileTypeLabel, ref aboveRect); // Convert label screen coords to client coords POINT point = new POINT(); point.X = aboveRect.Left; point.Y = aboveRect.Bottom; ScreenToClient(parent, ref point); // Create PixelType label this.m_hLabelPixelType = CreateWindowEx(0, "STATIC", "labelpt", WS_VISIBLE | WS_CHILD | WS_TABSTOP, point.X, point.Y + 12, 200, 100, parent, 0, 0, 0); SetWindowText(this.m_hLabelPixelType, "&Pixel Type:"); int hFont = SendMessage(windowFileTypeLabel, WM_GETFONT, 0, 0); SendMessage(this.m_hLabelPixelType, WM_SETFONT, hFont, 0); // Create Pixel Array label int labelY = 40; if (this.m_ShowPixelArray) { this.m_hLabelPixelArray = CreateWindowEx(0, "STATIC", "labelarray", WS_VISIBLE | WS_CHILD | WS_TABSTOP, point.X, point.Y + labelY, 200, 100, parent, 0, 0, 0); SetWindowText(this.m_hLabelPixelArray, "&Pixel Array:"); SendMessage(this.m_hLabelPixelArray, WM_SETFONT, hFont, 0); labelY += 28; } // Create Dimensions label if (this.m_ShowDimensions) { this.m_hLabelDimensions = CreateWindowEx(0, "STATIC", "labeldims", WS_VISIBLE | WS_CHILD | WS_TABSTOP, point.X, point.Y + labelY, 200, 100, parent, 0, 0, 0); SetWindowText(this.m_hLabelDimensions, "&Dimensions:"); SendMessage(this.m_hLabelDimensions, WM_SETFONT, hFont, 0); labelY += 28; } // Create Dimensions label if (this.m_ShowUseCompression) { this.m_hLabelUseCompression = CreateWindowEx(0, "STATIC", "labelcompress", WS_VISIBLE | WS_CHILD | WS_TABSTOP, point.X, point.Y + labelY, 200, 100, parent, 0, 0, 0); SetWindowText(this.m_hLabelUseCompression, "&Compression:"); SendMessage(this.m_hLabelUseCompression, WM_SETFONT, hFont, 0); labelY += 28; } // Find the combo-box to position the PixelType combo-box under int windowFileTypeCombo = GetDlgItem(parent, 0x470); aboveRect = new RECT(); GetWindowRect(windowFileTypeCombo, ref aboveRect); point = new POINT(); point.X = aboveRect.Left; point.Y = aboveRect.Bottom; ScreenToClient(parent, ref point); POINT rightPoint = new POINT(); rightPoint.X = aboveRect.Right; rightPoint.Y = aboveRect.Top; ScreenToClient(parent, ref rightPoint); // Create the PixelType combobox this.m_hComboPixelType = CreateWindowEx(0, "ComboBox", "comboboxpt", WS_VISIBLE | WS_CHILD | CBS_HASSTRINGS | CBS_DROPDOWNLIST | WS_TABSTOP, point.X, point.Y + 6, rightPoint.X - point.X, 100, parent, 0, 0, 0); SendMessage(this.m_hComboPixelType, WM_SETFONT, hFont, 0); // Add the PixelType enumerated types and select first foreach (string pixelTypeName in Enum.GetNames(typeof(itk.itkPixelTypeEnum))) SendMessage(this.m_hComboPixelType, CB_ADDSTRING, 0, this.InsertSpaceIntoCamelCase(pixelTypeName)); SendMessage(this.m_hComboPixelType, CB_SETCURSEL, (int)this.m_PixelType, 0); int comboY = 34; if (this.m_ShowPixelArray) { // Create the PixelArray combobox this.m_hComboPixelArray = CreateWindowEx(0, "ComboBox", "comboboxarray", WS_VISIBLE | WS_CHILD | CBS_HASSTRINGS | CBS_DROPDOWNLIST | WS_TABSTOP, point.X, point.Y + comboY, rightPoint.X - point.X, 100, parent, 0, 0, 0); SendMessage(this.m_hComboPixelArray, WM_SETFONT, hFont, 0); // Add the PixelArray enumerated types and select first foreach (itk.itkPixelArrayEnum enumArray in Enum.GetValues(typeof(itk.itkPixelArrayEnum))) SendMessage(this.m_hComboPixelArray, CB_ADDSTRING, 0, this.ConvertPixelArrayEnumToString(enumArray)); SendMessage(this.m_hComboPixelArray, CB_SETCURSEL, (int)this.m_PixelArray, 0); comboY += 28; } if (this.m_ShowDimensions) { // Create the Dimensions combobox this.m_hComboDimensions = CreateWindowEx(0, "ComboBox", "comboboxdims", WS_VISIBLE | WS_CHILD | CBS_HASSTRINGS | CBS_DROPDOWNLIST | WS_TABSTOP, point.X, point.Y + comboY, rightPoint.X - point.X, 100, parent, 0, 0, 0); SendMessage(this.m_hComboDimensions, WM_SETFONT, hFont, 0); // Add the Dimensions items and select first SendMessage(this.m_hComboDimensions, CB_ADDSTRING, 0, "2"); SendMessage(this.m_hComboDimensions, CB_ADDSTRING, 0, "3"); SendMessage(this.m_hComboDimensions, CB_SETCURSEL, (int)(this.m_Dimensions - 2), 0); } if (this.m_ShowUseCompression) { // Create the UseCompression checkbox this.m_hComboUseCompression = CreateWindowEx(0, "ComboBox", "comboboxcompress", WS_VISIBLE | WS_CHILD | CBS_HASSTRINGS | CBS_DROPDOWNLIST | WS_TABSTOP, point.X, point.Y + comboY, rightPoint.X - point.X, 100, parent, 0, 0, 0); SendMessage(this.m_hComboUseCompression, WM_SETFONT, hFont, 0); // Add the compression options SendMessage(this.m_hComboUseCompression, CB_ADDSTRING, 0, "No Compression"); SendMessage(this.m_hComboUseCompression, CB_ADDSTRING, 0, "Use Compression"); SendMessage(this.m_hComboUseCompression, CB_SETCURSEL, Convert.ToInt32(this.UseCompression), 0); } break; case WM_DESTROY: // Destroy the created handles if (this.m_hLabelPixelType != 0) { DestroyWindow(m_hLabelPixelType); this.m_hLabelPixelType = 0; } if (this.m_hComboPixelType != 0) { DestroyWindow(m_hComboPixelType); this.m_hComboPixelType = 0; } if (this.m_hLabelPixelArray != 0) { DestroyWindow(m_hLabelPixelArray); this.m_hLabelPixelArray = 0; } if (this.m_hComboPixelArray != 0) { DestroyWindow(m_hComboPixelArray); this.m_hComboPixelArray = 0; } if (this.m_hLabelDimensions != 0) { DestroyWindow(m_hLabelDimensions); this.m_hLabelDimensions = 0; } if (this.m_hComboDimensions != 0) { DestroyWindow(m_hComboDimensions); this.m_hComboDimensions = 0; } if (this.m_hLabelUseCompression != 0) { DestroyWindow(m_hLabelUseCompression); this.m_hLabelUseCompression = 0; } if (this.m_hComboUseCompression != 0) { DestroyWindow(m_hComboUseCompression); this.m_hComboUseCompression = 0; } break; case WM_NOTIFY: // Intercept the CDN_FILEOK msg which is sent when a filename is selected NMHDR nmhdr = (NMHDR)Marshal.PtrToStructure(new IntPtr(lParam), typeof(NMHDR)); if (nmhdr.Code == CDN_FILEOK) { this.m_PixelType = (itk.itkPixelTypeEnum)SendMessage(this.m_hComboPixelType, CB_GETCURSEL, 0, 0); if (this.m_ShowPixelArray) this.m_PixelArray = (itk.itkPixelArrayEnum)SendMessage(this.m_hComboPixelArray, CB_GETCURSEL, 0, 0); if (this.m_ShowDimensions) this.m_Dimensions = (uint)SendMessage(this.m_hComboDimensions, CB_GETCURSEL, 0, 0) + 2; if (this.m_ShowUseCompression) this.m_UseCompression = Convert.ToBoolean(SendMessage(this.m_hComboUseCompression, CB_GETCURSEL, 0, 0)); } else if (nmhdr.Code == CDN_SELCHANGE) { try { // Get the currently selected filename String filepath = new String('\0', MAX_PATH + 2); int size = siImageDialog.SendMessage(parent, CDM_GETFILEPATH, MAX_PATH, filepath); filepath = filepath.Trim('\0'); if (size > 0 && filepath != null & filepath.Length > 0) { // Check that the file exists if (File.Exists(filepath)) { itk.itkImageInformation info = itk.itkImage.ReadInformation(filepath); SendMessage(this.m_hComboDimensions, CB_SETCURSEL, (int)info.Dimension - 2, 0); SendMessage(this.m_hComboPixelType, CB_SETCURSEL, (int)info.PixelType.TypeAsEnum, 0); SendMessage(this.m_hComboPixelArray, CB_SETCURSEL, (int)info.PixelType.ArrayAsEnum, 0); } } } catch (Exception ex) { const String caption = "Could not read image information"; String text = caption + ".\r\n" + ex.ToString(); MessageBox.Show(text, caption, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); } } break; }// end switch return 0; } /// /// Insert a space between camel case words. /// Eg. "ThisWord" = "This Word" /// /// /// private string InsertSpaceIntoCamelCase(string camelcase) { string result = string.Empty; foreach (char c in camelcase.ToCharArray()) { if (Char.IsUpper(c)) result += " "; result += c; } return result.Trim(' '); } private string ConvertPixelArrayEnumToString(itk.itkPixelArrayEnum enumArray) { switch (enumArray) { case itk.itkPixelArrayEnum.Scalar: return "Scalar Pixel"; case itk.itkPixelArrayEnum.ArrayVector: return "Vector Pixel"; case itk.itkPixelArrayEnum.ArrayCovariantVector: return "Covariant Vector Pixel"; case itk.itkPixelArrayEnum.ArrayVariableLengthVector: return "Variable Length Vector Pixel"; case itk.itkPixelArrayEnum.ArrayVectorImage: return "Vector Image"; case itk.itkPixelArrayEnum.ArrayRGB: return "RGB Pixel"; case itk.itkPixelArrayEnum.ArrayRGBA: return "RGBA Pixel"; default: return this.InsertSpaceIntoCamelCase(enumArray.ToString()); } } //===================================================================== #endregion #region Win32 API constants, structs, and methods //===================================================================== protected const int OFN_ENABLEHOOK = 0x00000020; protected const int OFN_EXPLORER = 0x00080000; protected const int OFN_FILEMUSTEXIST = 0x00001000; protected const int OFN_HIDEREADONLY = 0x00000004; protected const int OFN_CREATEPROMPT = 0x00002000; protected const int OFN_NOTESTFILECREATE = 0x00010000; protected const int OFN_OVERWRITEPROMPT = 0x00000002; protected const int OFN_PATHMUSTEXIST = 0x00000800; private const int SWP_NOSIZE = 0x0001; private const int SWP_NOMOVE = 0x0002; private const int SWP_NOZORDER = 0x0004; private const int WM_INITDIALOG = 0x110; private const int WM_DESTROY = 0x2; private const int WM_SETFONT = 0x0030; private const int WM_GETFONT = 0x0031; private const int CBS_DROPDOWNLIST = 0x0003; private const int CBS_HASSTRINGS = 0x0200; private const int CB_ADDSTRING = 0x0143; private const int CB_SETCURSEL = 0x014E; private const int CB_GETCURSEL = 0x0147; private const uint WS_VISIBLE = 0x10000000; private const uint WS_CHILD = 0x40000000; private const uint WS_TABSTOP = 0x00010000; private const int CDN_FILEOK = -606; private const int CDN_SELCHANGE = -602; private const int WM_USER = 1024; private const int CDM_FIRST = WM_USER + 100; private const int CDM_GETSPEC = CDM_FIRST + 0x000; private const int CDM_GETFILEPATH = CDM_FIRST + 0x001; private const int CDM_GETFOLDERPATH = CDM_FIRST + 0x002; private const int WM_NOTIFY = 0x004E; private const int MAX_PATH = 1024; private struct RECT { public int Left; public int Top; public int Right; public int Bottom; } private struct POINT { public int X; public int Y; } [StructLayout(LayoutKind.Sequential)] private struct NMHDR { public IntPtr HwndFrom; public IntPtr IdFrom; public int Code; // This constructor is needed to satisfy the compiler and supress warning CS0649 public NMHDR(Int32 none) { this.HwndFrom = IntPtr.Zero; this.IdFrom = IntPtr.Zero; this.Code = 0; } } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] protected struct OPENFILENAME { public int lStructSize; public IntPtr hwndOwner; public int hInstance; [MarshalAs(UnmanagedType.LPTStr)] public string lpstrFilter; [MarshalAs(UnmanagedType.LPTStr)] public string lpstrCustomFilter; public int nMaxCustFilter; public int nFilterIndex; [MarshalAs(UnmanagedType.LPTStr)] public string lpstrFile; public int nMaxFile; [MarshalAs(UnmanagedType.LPTStr)] public string lpstrFileTitle; public int nMaxFileTitle; [MarshalAs(UnmanagedType.LPTStr)] public string lpstrInitialDir; [MarshalAs(UnmanagedType.LPTStr)] public string lpstrTitle; public int Flags; public short nFileOffset; public short nFileExtension; [MarshalAs(UnmanagedType.LPTStr)] public string lpstrDefExt; public int lCustData; public OFNHookProcDelegate lpfnHook; [MarshalAs(UnmanagedType.LPTStr)] public string lpTemplateName; //only if on nt 5.0 or higher public int pvReserved; public int dwReserved; public int FlagsEx; } [DllImport("Comdlg32.dll", CharSet = CharSet.Auto, SetLastError = true)] protected static extern bool GetSaveFileName(ref OPENFILENAME lpofn); [DllImport("Comdlg32.dll", CharSet = CharSet.Auto, SetLastError = true)] protected static extern bool GetOpenFileName(ref OPENFILENAME lpofn); [DllImport("Comdlg32.dll")] protected static extern int CommDlgExtendedError(); [DllImport("user32.dll")] private static extern bool SetWindowPos(int hWnd, int hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); [DllImport("user32.dll")] private static extern bool GetWindowRect(int hWnd, ref RECT lpRect); [DllImport("user32.dll")] private static extern int GetParent(int hWnd); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern bool SetWindowText(int hWnd, string lpString); [DllImport("user32.dll")] private static extern int SendMessage(int hWnd, int Msg, int wParam, int lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int SendMessage(int hWnd, int Msg, int wParam, string lParam); [DllImport("user32.dll")] private static extern bool DestroyWindow(int hwnd); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int GetDlgItem(int hDlg, int nIDDlgItem); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern int CreateWindowEx(int dwExStyle, string lpClassName, string lpWindowName, uint dwStyle, int x, int y, int nWidth, int nHeight, int hWndParent, int hMenu, int hInstance, int lpParam); [DllImport("user32.dll")] private static extern bool ScreenToClient(int hWnd, ref POINT lpPoint); //===================================================================== #endregion } }