Numerous bug fixes to all platforms
Works both installed and not installed
This commit is contained in:
parent
c913f9f99a
commit
a81ea73566
68 changed files with 384 additions and 35286 deletions
|
@ -1,468 +0,0 @@
|
|||
/* MinWin Dialog class
|
||||
*
|
||||
* A general dialog class and utility helper dialogs
|
||||
*
|
||||
* Written by Ben Hinkle and released to the public domain, as
|
||||
* explained at http://creativecommons.org/licenses/publicdomain
|
||||
* Report comments and bugs at dsource: http://www.dsource.org/projects/minwin
|
||||
*/
|
||||
|
||||
module minwin.dialog;
|
||||
|
||||
private {
|
||||
import std.string;
|
||||
import std.c.string;
|
||||
import minwin.peerimpl;
|
||||
import minwin.font;
|
||||
import minwin.event;
|
||||
import minwin.component;
|
||||
import minwin.menu;
|
||||
import minwin.app;
|
||||
import minwin.logging;
|
||||
import minwin.icon;
|
||||
import minwin.image;
|
||||
}
|
||||
|
||||
version (MinWin32) {
|
||||
|
||||
private import minwin.mswindows;
|
||||
|
||||
class Dialog : AbstractWindow {
|
||||
AbstractWindow owner;
|
||||
bool modal_data;
|
||||
bool visible_data;
|
||||
|
||||
this(AbstractWindow owner, char[] title = "", bool modal = true, char[] name = "") {
|
||||
Rect r;
|
||||
owner.getBounds(r);
|
||||
DWORD style = WS_TABSTOP | WS_POPUP | DS_MODALFRAME | WS_CAPTION | WS_SYSMENU;
|
||||
peer = CreateWindowX("MinWinDialog", title,
|
||||
style & ~WS_MAXIMIZEBOX & ~WS_MINIMIZEBOX,
|
||||
r.left, r.top,
|
||||
DefaultWindowWidth, DefaultWindowHeight,
|
||||
owner.peer,
|
||||
cast(HMENU) null, gApp.hInstance, null);
|
||||
sysAssert(peer !is null, "Failed to create peer Dialog");
|
||||
setWindowPeer(this,peer,OWNS_PEER);
|
||||
backgroundPeer = CreateSolidBrush(systemBackgroundColor().native);
|
||||
this.name = name;
|
||||
this.owner = owner;
|
||||
modal_data = modal;
|
||||
WindowList[this] = this; // prevent garbage collection
|
||||
}
|
||||
|
||||
this(WindowPeer peer) {
|
||||
setWindowPeer(this,peer,FOREIGN_PEER);
|
||||
WindowList[this] = this; // prevent garbage collection
|
||||
}
|
||||
|
||||
AbstractWindow[] originalWindows;
|
||||
int[] originalEnabledState;
|
||||
|
||||
void visible(bool vis) {
|
||||
if (modal_data && !vis && visible_data) {
|
||||
// make sure original states are restored before hiding
|
||||
foreach(int n, AbstractWindow w; originalWindows) {
|
||||
if (IsWindow(w.peer)) {
|
||||
EnableWindow(w.peer,originalEnabledState[n]);
|
||||
}
|
||||
}
|
||||
originalWindows = null;
|
||||
originalEnabledState = null;
|
||||
}
|
||||
version (LOG) log.writefln("setting dialog visible %d",cast(int)vis);
|
||||
super.visible(vis);
|
||||
version (LOG) log.writefln("done with super vis");
|
||||
visible_data = vis;
|
||||
if (modal_data && vis) {
|
||||
cancelCloseDelegate ~= delegate bool(Component c) {
|
||||
Dialog dlg = cast(Dialog)c;
|
||||
if (!dlg || !dlg.owner) return false;
|
||||
EnableWindow(dlg.owner.peer,1);
|
||||
return false;
|
||||
};
|
||||
version (LOG) log.writefln("disabling windows dialog");
|
||||
originalWindows = WindowList.values;
|
||||
originalEnabledState.length = originalWindows.length;
|
||||
foreach(int n, AbstractWindow w; originalWindows) {
|
||||
originalEnabledState[n] = IsWindowEnabled(w.peer) != 0;
|
||||
version (LOG) log.writefln("disable %d %p for dialog",n,w);
|
||||
if (originalEnabledState[n] != 0 && (w !is this)) {
|
||||
EnableWindow(w.peer,0);
|
||||
}
|
||||
}
|
||||
while (visible_data && IsWindow(peer)) {
|
||||
version (LOG) log.writefln("in modal loop for %p",this);
|
||||
if (!GetMessageA(&gApp.event.native, cast(HWND) null, 0, 0)) {
|
||||
PostQuitMessage(0); // quit out of next event loop
|
||||
break;
|
||||
}
|
||||
if (!IsDialogMessageA(peer, &gApp.event.native)) {
|
||||
TranslateMessage(&gApp.event.native);
|
||||
DispatchMessageA(&gApp.event.native);
|
||||
}
|
||||
}
|
||||
visible_data = false;
|
||||
foreach(int n, AbstractWindow w; originalWindows) {
|
||||
if (IsWindow(w.peer)) {
|
||||
EnableWindow(w.peer,originalEnabledState[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int WindowProc(HWND hWnd, uint uMsg, WPARAM wParam, LPARAM lParam) {
|
||||
bool doDefault = true;
|
||||
if (uMsg <= WM_KEYLAST && uMsg >= WM_KEYFIRST) {
|
||||
keyDelegate(this,cast(KeyEvent*)&gApp.event);
|
||||
} else if (uMsg <= WM_MOUSELAST && uMsg >= WM_MOUSEFIRST) {
|
||||
mouseDelegate(this,cast(MouseEvent*)&gApp.event);
|
||||
} else {
|
||||
doDefault = super.WindowProc(hWnd,uMsg,wParam,lParam) != 0;
|
||||
}
|
||||
return doDefault;
|
||||
}
|
||||
// also handle tab key by calling GetNextDlgTabItem
|
||||
}
|
||||
static this() {
|
||||
if (DefaultWindowIcon is null) {
|
||||
IconPeer ip = LoadIconA(cast(HINSTANCE) null, IDI_APPLICATION);
|
||||
DefaultWindowIcon = DefaultWindowSmallIcon = new Icon(ip);
|
||||
}
|
||||
HINSTANCE hInst = GetModuleHandleA(null);
|
||||
WNDCLASSA wc;
|
||||
wc.lpszClassName = "MinWinDialog";
|
||||
wc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = &MinWinWindowProc;
|
||||
wc.hInstance = hInst;
|
||||
wc.hIcon = DefaultWindowIcon.peer;
|
||||
wc.hCursor = LoadCursorA(cast(HINSTANCE) null, IDC_ARROW);
|
||||
wc.hbrBackground = null;
|
||||
// wc.hbrBackground = cast(HBRUSH) (COLOR_WINDOW); // not +1 for default
|
||||
wc.lpszMenuName = null;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
RegisterClassA(&wc);
|
||||
}
|
||||
|
||||
} else version (GTK) {
|
||||
|
||||
private import minwin.gtk;
|
||||
private import minwin.gtk_peers;
|
||||
|
||||
class Dialog : AbstractWindow {
|
||||
AbstractWindow owner;
|
||||
|
||||
this(AbstractWindow owner, char[] title = "", bool modal = true, char[] name = "") {
|
||||
char* str = toStringz(title);
|
||||
this.owner = owner;
|
||||
// TODO: application name vs window name??
|
||||
peer = cast(GtkWindow*)gtk_window_new(GtkWindowType.GTK_WINDOW_TOPLEVEL);
|
||||
if (modal) {
|
||||
gtk_window_set_modal(peer,modal);
|
||||
gtk_window_set_transient_for(peer,owner.peer);
|
||||
}
|
||||
gtk_window_set_title(peer,str);
|
||||
gtk_window_set_resizable(peer,0);
|
||||
gtk_window_set_default_size(peer,DefaultWindowWidth,DefaultWindowHeight);
|
||||
this.name = name;
|
||||
g_signal_connect_data(peer,"destroy",
|
||||
cast(GCallback)&mw_destroy_callback,
|
||||
cast(gpointer)this,
|
||||
null,cast(GConnectFlags)0);
|
||||
g_signal_connect_data(peer,"delete-event",
|
||||
cast(GCallback)&mw_dialog_close_callback,
|
||||
cast(gpointer)this,
|
||||
null,cast(GConnectFlags)0);
|
||||
setWindowPeer(this,peer,OWNS_PEER);
|
||||
WindowList[this] = this; // prevent garbage collection
|
||||
|
||||
content = cast(MinWinGtkPeer*)MinWinGtkPeer_new();
|
||||
GtkWidget* wcontent = cast(GtkWidget*)content;
|
||||
gtk_widget_set_sensitive(wcontent,true);
|
||||
content.sizeRequest = >kRequest;
|
||||
content.sizeAllocate = >kAllocate;
|
||||
gtk_container_add(cast(GtkContainer*)peer,wcontent);
|
||||
g_signal_connect_data(peer,"expose-event",
|
||||
cast(GCallback)&mw_expose_callback,
|
||||
cast(gpointer)this,
|
||||
null,GConnectFlags.G_CONNECT_AFTER);
|
||||
gtk_widget_realize(wcontent);
|
||||
gtk_widget_show(wcontent);
|
||||
}
|
||||
|
||||
this(AbstractWindow owner, WindowPeer peer) {
|
||||
setWindowPeer(this,peer,FOREIGN_PEER);
|
||||
this.owner = owner;
|
||||
WindowList[this] = this; // prevent garbage collection
|
||||
}
|
||||
|
||||
GMainLoop* loop;
|
||||
|
||||
void visible(bool vis) {
|
||||
version(LOG)log.writefln("calling super.visible %d",vis);
|
||||
version(LOG)log.flush();
|
||||
super.visible(vis);
|
||||
version(LOG)log.writefln("done calling super.visible %d",vis);
|
||||
version(LOG)log.flush();
|
||||
if (!gtk_window_get_modal(peer)) return;
|
||||
if (vis) {
|
||||
version(LOG)log.writefln("making event loop");
|
||||
version(LOG)log.flush();
|
||||
loop = g_main_loop_new(null, false);
|
||||
g_main_loop_run(loop);
|
||||
g_main_loop_unref(loop);
|
||||
version(LOG)log.writefln("exiting event loop");
|
||||
version(LOG)log.flush();
|
||||
// that's it?
|
||||
} else {
|
||||
if (g_main_loop_is_running(loop))
|
||||
g_main_loop_quit(loop);
|
||||
version(LOG)log.writefln("visible off quit loop event loop");
|
||||
version(LOG)log.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
extern (C) void mw_dialog_close_callback(GtkWidget* w, GdkEvent* ev, gpointer ud) {
|
||||
AbstractWindow win = cast(AbstractWindow) ud;
|
||||
version(LOG)log.printf("mw_dialog_close_callback\n");
|
||||
if (win) {
|
||||
win.visible = false;
|
||||
// win.close(); // destroys dialog so dont do this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/***************************************
|
||||
Helper dialogs
|
||||
***************************************/
|
||||
|
||||
// used in open and save file dialogs for filtering
|
||||
struct FileFilter {
|
||||
char[] description;
|
||||
char[][] extensions;
|
||||
}
|
||||
version (GTK) {
|
||||
version = SharedFileDialogData;
|
||||
}
|
||||
|
||||
version (SharedFileDialogData) {
|
||||
struct FileDialogData {
|
||||
char[] title;
|
||||
FileFilter[] filter;
|
||||
char[] initialDir;
|
||||
char[] initialFile;
|
||||
char[] result;
|
||||
int filterIndex;
|
||||
int done;
|
||||
|
||||
// can get the requested buffer length...
|
||||
bool isBufferTooShort() {
|
||||
return false; // TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
version (MinWin32) {
|
||||
private {
|
||||
import minwin.mswindows;
|
||||
import std.c.stdlib;
|
||||
}
|
||||
|
||||
void informationDialog(AbstractWindow owner, char[] text, char[] title) {
|
||||
//version(LOG) log.writefln("info dialog peer is %x",cast(uint)owner.peer);
|
||||
MessageBoxX(owner.peer, text, title, MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
void warningDialog(AbstractWindow owner, char[] text, char[] title) {
|
||||
MessageBoxX(owner.peer, text, title, MB_OK | MB_ICONWARNING);
|
||||
}
|
||||
void errorDialog(AbstractWindow owner, char[] text, char[] title) {
|
||||
MessageBoxX(owner.peer, text, title, MB_OK | MB_ICONERROR);
|
||||
}
|
||||
void messageDialog(AbstractWindow owner, char[] text, char[] title,...) {
|
||||
MessageBoxX(owner.peer, text, title, MB_OK | MB_ICONINFORMATION);
|
||||
}
|
||||
|
||||
alias OPENFILENAMEA FileDialogNative;
|
||||
struct FileDialogData {
|
||||
FileDialogNative native;
|
||||
|
||||
void title(char[] title) {
|
||||
native.lpstrTitle = toStringz(title);
|
||||
}
|
||||
char[] title() {
|
||||
return native.lpstrTitle[0 .. strlen(native.lpstrTitle)];
|
||||
}
|
||||
|
||||
void filter(FileFilter[] filters) {
|
||||
char[] total;
|
||||
foreach (FileFilter f; filters) {
|
||||
total ~= f.description;
|
||||
total ~= 0;
|
||||
if (f.extensions.length > 0) {
|
||||
foreach (char[] ext; f.extensions) {
|
||||
total ~= "*.";
|
||||
total ~= ext;
|
||||
total ~= ";";
|
||||
}
|
||||
total.length = total.length - 1; // remove last ;
|
||||
}
|
||||
total ~= 0;
|
||||
}
|
||||
total ~= 0;
|
||||
total ~= 0;
|
||||
native.lpstrFilter = total.ptr;
|
||||
}
|
||||
|
||||
void initialDir(char[] initialDir) {
|
||||
native.lpstrInitialDir = toStringz(initialDir);
|
||||
}
|
||||
|
||||
void initialFile(char[] f) {
|
||||
if (native.lpstrFile is null) {
|
||||
native.lpstrFile = (new char[MAX_PATH]).ptr;
|
||||
native.nMaxFile = MAX_PATH;
|
||||
}
|
||||
int len = f.length < native.nMaxFile-1 ? f.length : native.nMaxFile-1;
|
||||
native.lpstrFile[0 .. len] = f[0 .. len];
|
||||
native.lpstrFile[len] = 0;
|
||||
}
|
||||
|
||||
char[] result() {
|
||||
return native.lpstrFile[0 .. strlen(native.lpstrFile)];
|
||||
}
|
||||
|
||||
// can get the requested buffer length...
|
||||
bool isBufferTooShort() {
|
||||
// bool res = CommDlgExtendedError() == FNERR_BUFFERTOOSMALL;
|
||||
return false;
|
||||
}
|
||||
|
||||
int filterIndex() {
|
||||
return native.nFilterIndex;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool openFileDialog(AbstractWindow owner, inout FileDialogData data) {
|
||||
data.native.hwndOwner = owner.peer;
|
||||
data.native.lStructSize = OPENFILENAMEA.sizeof;
|
||||
// data.flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
|
||||
data.native.Flags = OFN_PATHMUSTEXIST;
|
||||
if (data.native.lpstrFile is null) {
|
||||
data.native.lpstrFile = (new char[MAX_PATH]).ptr;
|
||||
data.native.nMaxFile = MAX_PATH;
|
||||
data.native.lpstrFile[0] = 0;
|
||||
}
|
||||
bool res = GetOpenFileNameA(&data.native) != 0;
|
||||
/* if (!res)
|
||||
check if the name was too long for the buffer
|
||||
*/
|
||||
return res;
|
||||
}
|
||||
|
||||
bool saveFileDialog(AbstractWindow owner, inout FileDialogData data) {
|
||||
data.native.hwndOwner = owner.peer;
|
||||
data.native.lStructSize = OPENFILENAMEA.sizeof;
|
||||
data.native.Flags = OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST;
|
||||
if (data.native.lpstrFile is null) {
|
||||
data.native.lpstrFile = (new char[MAX_PATH]).ptr;
|
||||
data.native.nMaxFile = MAX_PATH;
|
||||
data.native.lpstrFile[0] = 0;
|
||||
}
|
||||
bool res = GetSaveFileNameA(&data.native) != 0;
|
||||
/* if (!res)
|
||||
check if the name was too long for the buffer
|
||||
*/
|
||||
return res;
|
||||
}
|
||||
|
||||
} else version(GTK) {
|
||||
|
||||
private import minwin.gtk;
|
||||
private import minwin.window;
|
||||
|
||||
void informationDialog(AbstractWindow owner, char* text, char[]
|
||||
title) {
|
||||
msgDialog(owner,text,title,GtkMessageType.GTK_MESSAGE_INFO,
|
||||
GtkButtonsType.GTK_BUTTONS_OK);
|
||||
}
|
||||
void warningDialog(AbstractWindow owner, char* text, char[] title) {
|
||||
msgDialog(owner,text,title,GtkMessageType.GTK_MESSAGE_WARNING,
|
||||
GtkButtonsType.GTK_BUTTONS_OK);
|
||||
}
|
||||
void errorDialog(AbstractWindow owner, char* text, char[] title) {
|
||||
msgDialog(owner,text,title,GtkMessageType.GTK_MESSAGE_ERROR,
|
||||
GtkButtonsType.GTK_BUTTONS_OK);
|
||||
}
|
||||
alias informationDialog messageDialog;
|
||||
void msgDialog(AbstractWindow owner, char* text, char[] title,
|
||||
GtkMessageType type, GtkButtonsType buttons) {
|
||||
// TODO: title?
|
||||
GtkWidget* d = gtk_message_dialog_new(owner.peer,
|
||||
GtkDialogFlags.GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
type,buttons,text);
|
||||
gtk_dialog_run(cast(GtkDialog*)d);
|
||||
gtk_widget_destroy(d);
|
||||
}
|
||||
|
||||
bool openFileDialog(AbstractWindow owner, inout FileDialogData data) {
|
||||
bool result;
|
||||
GtkWidget* peer =
|
||||
gtk_file_chooser_dialog_new(toStringz(data.title),owner.peer,
|
||||
GtkFileChooserAction.GTK_FILE_CHOOSER_ACTION_OPEN,
|
||||
cast(char*)"gtk-cancel", cast(uint)GtkResponseType.GTK_RESPONSE_CANCEL,
|
||||
cast(char*)"gtk-open", cast(uint)GtkResponseType.GTK_RESPONSE_ACCEPT,
|
||||
null);
|
||||
GtkFileChooser* fc = cast(GtkFileChooser*)peer;
|
||||
return opensaveFileDialog(data,peer,fc);
|
||||
}
|
||||
|
||||
bool saveFileDialog(AbstractWindow owner, inout FileDialogData data) {
|
||||
GtkWidget* peer =
|
||||
gtk_file_chooser_dialog_new(toStringz(data.title),owner.peer,
|
||||
GtkFileChooserAction.GTK_FILE_CHOOSER_ACTION_SAVE,
|
||||
cast(char*)"gtk-cancel", cast(uint)GtkResponseType.GTK_RESPONSE_CANCEL,
|
||||
cast(char*)"gtk-save", cast(uint)GtkResponseType.GTK_RESPONSE_ACCEPT,
|
||||
null);
|
||||
GtkFileChooser* fc = cast(GtkFileChooser*)peer;
|
||||
if (data.initialFile.length > 0)
|
||||
gtk_file_chooser_set_current_name(fc,toStringz(data.initialFile));
|
||||
return opensaveFileDialog(data,peer,fc);
|
||||
}
|
||||
bool opensaveFileDialog(inout FileDialogData data,
|
||||
GtkWidget* peer,
|
||||
GtkFileChooser* fc) {
|
||||
bool result;
|
||||
if (data.initialDir.length > 0)
|
||||
gtk_file_chooser_set_current_folder(fc,toStringz(data.initialDir));
|
||||
foreach( FileFilter filt ; data.filter) {
|
||||
GtkFileFilter* f = gtk_file_filter_new();
|
||||
gtk_file_filter_set_name(f,toStringz(filt.description));
|
||||
foreach( char[] ext; filt.extensions) {
|
||||
gtk_file_filter_add_pattern(f,cast(char*)("*."~ext));
|
||||
}
|
||||
gtk_file_chooser_add_filter(fc,f);
|
||||
}
|
||||
// TODO: assertion local_full_path[0] = /
|
||||
result = gtk_dialog_run(cast(GtkDialog*)peer) == GtkResponseType.GTK_RESPONSE_ACCEPT;
|
||||
if (result) {
|
||||
char* name = gtk_file_chooser_get_filename(fc);
|
||||
int len = strlen(name);
|
||||
data.result = name[0..len].dup;
|
||||
g_free(name);
|
||||
}
|
||||
gtk_widget_destroy(peer);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// utility dialogs:
|
||||
// query dialog
|
||||
// file chooser dialog
|
||||
|
||||
// color chooser?
|
||||
// print dialog?
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue