mirror of https://github.com/odrling/Aegisub
347 lines
10 KiB
C#
347 lines
10 KiB
C#
|
using System;
|
||
|
using System.Collections;
|
||
|
using System.Windows.Forms;
|
||
|
using System.Data;
|
||
|
using System.Text;
|
||
|
using System.Runtime.InteropServices;
|
||
|
using System.Drawing;
|
||
|
|
||
|
|
||
|
|
||
|
public enum EncodingType {
|
||
|
//UTF8=0,
|
||
|
//UTF8WithPreamble,
|
||
|
//Unicode,
|
||
|
//Ansi
|
||
|
ANSI,
|
||
|
BigEndianUnicode,
|
||
|
Unicode,
|
||
|
UTF8,
|
||
|
Shift_JIS
|
||
|
};
|
||
|
|
||
|
public class SaveFileDialogWithEncoding {
|
||
|
private delegate int OFNHookProcDelegate(int hdlg, int msg, int wParam, int lParam);
|
||
|
|
||
|
private int m_LabelHandle=0;
|
||
|
private int m_ComboHandle=0;
|
||
|
|
||
|
private string m_Filter="";
|
||
|
private string m_DefaultExt="";
|
||
|
|
||
|
private string m_FileName="";
|
||
|
|
||
|
private EncodingType m_EncodingType;
|
||
|
private Screen m_ActiveScreen;
|
||
|
|
||
|
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
|
||
|
private 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)]
|
||
|
private static extern bool GetSaveFileName(ref OPENFILENAME lpofn);
|
||
|
|
||
|
[DllImport("Comdlg32.dll")]
|
||
|
private 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);
|
||
|
|
||
|
private struct RECT {
|
||
|
public int Left;
|
||
|
public int Top;
|
||
|
public int Right;
|
||
|
public int Bottom;
|
||
|
}
|
||
|
|
||
|
private struct POINT {
|
||
|
public int X;
|
||
|
public int Y;
|
||
|
}
|
||
|
|
||
|
private struct NMHDR {
|
||
|
public int HwndFrom;
|
||
|
public int IdFrom;
|
||
|
public int Code;
|
||
|
}
|
||
|
|
||
|
[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);
|
||
|
|
||
|
private const int OFN_ENABLEHOOK=0x00000020;
|
||
|
private const int OFN_EXPLORER=0x00080000;
|
||
|
private const int OFN_FILEMUSTEXIST=0x00001000;
|
||
|
private const int OFN_HIDEREADONLY=0x00000004;
|
||
|
private const int OFN_CREATEPROMPT=0x00002000;
|
||
|
private const int OFN_NOTESTFILECREATE=0x00010000;
|
||
|
private const int OFN_OVERWRITEPROMPT=0x00000002;
|
||
|
private 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 WM_NOTIFY=0x004E;
|
||
|
|
||
|
[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);
|
||
|
|
||
|
private int HookProc(int hdlg, int msg, int wParam, int lParam) {
|
||
|
switch (msg) {
|
||
|
case WM_INITDIALOG:
|
||
|
|
||
|
//we need to centre the dialog
|
||
|
Rectangle sr=m_ActiveScreen.Bounds;
|
||
|
RECT cr=new RECT();
|
||
|
int parent=GetParent(hdlg);
|
||
|
GetWindowRect(parent, ref cr);
|
||
|
|
||
|
int x=(sr.Right + sr.Left - (cr.Right-cr.Left))/2;
|
||
|
int y=(sr.Bottom + sr.Top - (cr.Bottom-cr.Top))/2;
|
||
|
|
||
|
SetWindowPos(parent, 0, x, y, cr.Right-cr.Left, cr.Bottom - cr.Top + 32, SWP_NOZORDER);
|
||
|
|
||
|
|
||
|
//we need to find the label to position our new label under
|
||
|
|
||
|
int fileTypeWindow=GetDlgItem(parent, 0x441);
|
||
|
|
||
|
RECT aboveRect=new RECT();
|
||
|
GetWindowRect(fileTypeWindow, ref aboveRect);
|
||
|
|
||
|
//now convert the label's screen co-ordinates to client co-ordinates
|
||
|
POINT point=new POINT();
|
||
|
point.X=aboveRect.Left;
|
||
|
point.Y=aboveRect.Bottom;
|
||
|
|
||
|
ScreenToClient(parent, ref point);
|
||
|
|
||
|
//create the label
|
||
|
int labelHandle=CreateWindowEx(0, "STATIC", "mylabel", WS_VISIBLE | WS_CHILD | WS_TABSTOP, point.X, point.Y + 12, 200, 100, parent, 0, 0, 0);
|
||
|
SetWindowText(labelHandle, "&Encoding:");
|
||
|
|
||
|
int fontHandle=SendMessage(fileTypeWindow, WM_GETFONT, 0, 0);
|
||
|
SendMessage(labelHandle, WM_SETFONT, fontHandle, 0);
|
||
|
|
||
|
//we now need to find the combo-box to position the new combo-box under
|
||
|
|
||
|
int fileComboWindow=GetDlgItem(parent, 0x470);
|
||
|
aboveRect=new RECT();
|
||
|
GetWindowRect(fileComboWindow, 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);
|
||
|
|
||
|
//we create the new combobox
|
||
|
|
||
|
int comboHandle=CreateWindowEx(0, "ComboBox", "mycombobox", WS_VISIBLE | WS_CHILD | CBS_HASSTRINGS | CBS_DROPDOWNLIST | WS_TABSTOP, point.X, point.Y + 8, rightPoint.X-point.X, 100, parent, 0, 0, 0);
|
||
|
SendMessage(comboHandle, WM_SETFONT, fontHandle, 0);
|
||
|
|
||
|
//and add the encodings we want to offer
|
||
|
SendMessage(comboHandle, CB_ADDSTRING, 0, "ANSI");
|
||
|
SendMessage(comboHandle, CB_ADDSTRING, 0, "BigEndianUnicode");
|
||
|
SendMessage(comboHandle, CB_ADDSTRING, 0, "Unicode");
|
||
|
SendMessage(comboHandle, CB_ADDSTRING, 0, "UTF-8");
|
||
|
SendMessage(comboHandle, CB_ADDSTRING, 0, "SJIS");
|
||
|
|
||
|
SendMessage(comboHandle, CB_SETCURSEL, (int)m_EncodingType, 0);
|
||
|
|
||
|
//remember the handles of the controls we have created so we can destroy them after
|
||
|
m_LabelHandle=labelHandle;
|
||
|
m_ComboHandle=comboHandle;
|
||
|
|
||
|
break;
|
||
|
case WM_DESTROY:
|
||
|
//destroy the handles we have created
|
||
|
if (m_ComboHandle!=0) {
|
||
|
DestroyWindow(m_ComboHandle);
|
||
|
}
|
||
|
|
||
|
if (m_LabelHandle!=0) {
|
||
|
DestroyWindow(m_LabelHandle);
|
||
|
}
|
||
|
break;
|
||
|
case WM_NOTIFY:
|
||
|
|
||
|
//we need to intercept the CDN_FILEOK message
|
||
|
//which is sent when the user selects a filename
|
||
|
|
||
|
NMHDR nmhdr=(NMHDR)Marshal.PtrToStructure(new IntPtr(lParam), typeof(NMHDR));
|
||
|
|
||
|
if (nmhdr.Code==CDN_FILEOK) {
|
||
|
//a file has been selected
|
||
|
//we need to get the encoding
|
||
|
|
||
|
m_EncodingType=(EncodingType)SendMessage(m_ComboHandle, CB_GETCURSEL, 0, 0);
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public string DefaultExt {
|
||
|
get { return m_DefaultExt; }
|
||
|
set { m_DefaultExt=value; }
|
||
|
}
|
||
|
|
||
|
public string Filter {
|
||
|
get { return m_Filter; }
|
||
|
set { m_Filter=value; }
|
||
|
}
|
||
|
|
||
|
public string FileName {
|
||
|
get { return m_FileName; }
|
||
|
set { m_FileName=value; }
|
||
|
}
|
||
|
|
||
|
public EncodingType EncodingType {
|
||
|
get { return m_EncodingType; }
|
||
|
set { m_EncodingType=value; }
|
||
|
}
|
||
|
|
||
|
public DialogResult ShowDialog(IntPtr ownerhwnd, Screen ownerscreen) {
|
||
|
return ShowDialog(ownerhwnd,ownerscreen,"Unicode");
|
||
|
}
|
||
|
|
||
|
public DialogResult ShowDialog(IntPtr ownerhwnd, Screen ownerscreen, string encoding) {
|
||
|
|
||
|
//m_EncodingType = stringToEncodingType(encoding);
|
||
|
|
||
|
//set up the struct and populate it
|
||
|
|
||
|
OPENFILENAME ofn=new OPENFILENAME();
|
||
|
|
||
|
ofn.lStructSize= Marshal.SizeOf( ofn );
|
||
|
ofn.lpstrFilter= m_Filter.Replace('|', '\0') + '\0';
|
||
|
|
||
|
ofn.lpstrFile = m_FileName + new string(' ', 512);
|
||
|
ofn.nMaxFile= ofn.lpstrFile.Length;
|
||
|
ofn.lpstrFileTitle= System.IO.Path.GetFileName(m_FileName) + new string(' ', 512);
|
||
|
ofn.nMaxFileTitle = ofn.lpstrFileTitle.Length;
|
||
|
ofn.lpstrTitle= "Save file as";
|
||
|
ofn.lpstrDefExt=m_DefaultExt;
|
||
|
|
||
|
//position the dialog above the active window
|
||
|
ofn.hwndOwner=ownerhwnd;
|
||
|
|
||
|
//we need to find out the active screen so the dialog box is
|
||
|
//centred on the correct display
|
||
|
|
||
|
m_ActiveScreen=ownerscreen;
|
||
|
|
||
|
//set up some sensible flags
|
||
|
ofn.Flags=OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_NOTESTFILECREATE | OFN_ENABLEHOOK | OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT;
|
||
|
|
||
|
//this is where the hook is set. Note that we can use a C# delegate in place of a C function pointer
|
||
|
ofn.lpfnHook=new OFNHookProcDelegate(HookProc);
|
||
|
|
||
|
//if we're running on Windows 98/ME then the struct is smaller
|
||
|
if (System.Environment.OSVersion.Platform!=PlatformID.Win32NT) {
|
||
|
ofn.lStructSize-=12;
|
||
|
}
|
||
|
|
||
|
//show the dialog
|
||
|
|
||
|
if (!GetSaveFileName(ref ofn)) {
|
||
|
int ret=CommDlgExtendedError();
|
||
|
|
||
|
if (ret!=0) {
|
||
|
throw new ApplicationException("Couldn't show file open dialog - " + ret.ToString());
|
||
|
}
|
||
|
|
||
|
return DialogResult.Cancel;
|
||
|
}
|
||
|
|
||
|
m_FileName=ofn.lpstrFile;
|
||
|
|
||
|
return DialogResult.OK;
|
||
|
}
|
||
|
|
||
|
public static System.Text.Encoding stringToEncodingType(string input) {
|
||
|
unsafe {
|
||
|
switch (input) {
|
||
|
case "ASCII" : return System.Text.Encoding.ASCII;
|
||
|
case "ANSI" : return System.Text.Encoding.GetEncoding(1252);
|
||
|
case "BigEndianUnicode" : return System.Text.Encoding.BigEndianUnicode;
|
||
|
case "SJIS" :
|
||
|
case "Shift_JIS" : return System.Text.Encoding.GetEncoding("shift_jis");
|
||
|
case "UTF8" :
|
||
|
case "UTF-8" : return System.Text.Encoding.UTF8;
|
||
|
case "Unicode" :
|
||
|
default : return System.Text.Encoding.Unicode;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|