view dbg/ui/CrashWindow.d @ 0:10317f0c89a5

Initial commit
author korDen
date Sat, 24 Oct 2009 08:42:06 +0400
parents
children 2cc604139636
line wrap: on
line source

/**
 A window used to display the data from a CrashInfo struct generated by the
 runtime's crash handler.

 TODO:
	* Send report - need SMTP implementation
	* Save report

 Authors:
	Jeremie Pelletier

 License:
	Public Domain
*/
module dbg.ui.CrashWindow;

import std.c.string;
import dlib.CrashHandler;
import dbg.Debug : SystemException;

import std.stdio;

private enum {
	ReportWindowWidth		= 640,
	ReportWindowHeight		= 480,
	ReportWindowMinWidth	= 320,
	ReportWindowMinHeight	= 240,
	ReportWindowTitle		= "Unhandled exception!"
}

// ----------------------------------------------------------------------------
// W i n 3 2
// ----------------------------------------------------------------------------

version(Windows) {

import win32.windows;

private enum {
	ID_LABEL			= 100,
	ID_SAVE_BTN			= 101,
	ID_SEND_BTN			= 102,
	ID_CLOSE_BTN		= 103,
	ID_REPORT			= 104
}

enum CLEARTYPE_QUALITY = 5;
enum LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x20;

/**
 Create the crash window for the given crash information, the routine will
 return when the window is closed.
*/
void ShowCrashWindow(CrashInfo* crashInfo)
in {
	assert(crashInfo);
}
body {
	try {

	HINSTANCE inst = GetModuleHandle(null);

	WNDCLASSEXA wc;
	wc.cbSize = WNDCLASSEX.sizeof;
	wc.lpfnWndProc = &ReportWndProc;
	wc.hInstance = inst;
	wc.hIcon = cast(HICON)LoadImage(HINSTANCE.init, MAKEINTRESOURCE(OIC_ERROR), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
	wc.hCursor = cast(HCURSOR)LoadImage(HINSTANCE.init, MAKEINTRESOURCE(OCR_NORMAL), IMAGE_CURSOR, 0, 0, LR_DEFAULTSIZE);
	wc.hbrBackground = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
	wc.lpszClassName = "CrashWindowClass";

	if(!RegisterClassExA(&wc)) SystemException();
	scope(exit) if(!UnregisterClassA("CrashWindowClass", inst)) SystemException();

	RECT rc = void;
	GetClientRect(GetDesktopWindow(), &rc);
	
	writeln(crashInfo.toString);

	HWND wnd = CreateWindowExA(
		WS_EX_WINDOWEDGE,
		"CrashWindowClass", ReportWindowTitle,
		WS_OVERLAPPEDWINDOW | WS_VISIBLE,
		(rc.right >> 1) - (ReportWindowWidth >> 1), (rc.bottom >> 1) - (ReportWindowHeight >> 1),
		ReportWindowWidth, ReportWindowHeight,
		HWND.init, HMENU.init, inst, cast(void*)crashInfo.toString.ptr
	);
	if(!wnd) SystemException();

	MSG msg = void;
	while(GetMessage(&msg, HWND.init, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	} // try
	catch(Throwable e) {
		MessageBoxA(HWND.init, (e.toString ~ '\0').ptr, "Crash Window Error!", MB_ICONERROR | MB_OK);
	}
}

private:

__gshared HWND saveButton, sendButton, closeButton, reportField;

extern(Windows)
LRESULT ReportWndProc(HWND wnd, uint msg, WPARAM w, LPARAM l) {
	try {

	switch(msg) {
	case WM_CREATE:
		HINSTANCE inst = cast(HINSTANCE).GetModuleHandle(null);
		CREATESTRUCT* cs = cast(CREATESTRUCT*)l;

		LOGFONTA lf;
		lf.lfHeight = 15;
		lf.lfWeight = FW_REGULAR;
		lf.lfCharSet = DEFAULT_CHARSET;
		lf.lfOutPrecision = OUT_DEFAULT_PRECIS;
		lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
		lf.lfQuality = CLEARTYPE_QUALITY;
		lf.lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;

		HFONT font = .CreateFontIndirectA(&lf);
		if(!font) SystemException();

		HINSTANCE iconMod = LoadLibraryExA("shell32.dll", null,
			DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);

		HWND CreateButton(string caption, uint id, ushort iconId) {
			HWND ret = CreateWindowExA(
				0, "BUTTON", caption.ptr, WS_CHILD | WS_VISIBLE,
				0, 0, 0, 0, wnd, cast(HMENU)id, inst, null
			);
			if(!ret) SystemException();

			SendMessageA(ret, WM_SETFONT, cast(WPARAM)font, 0);

			if(iconMod) {
				HANDLE icon = LoadImage(iconMod, MAKEINTRESOURCE(iconId), IMAGE_ICON, 24, 24, 0);
				if(icon) SendMessageA(ret, BM_SETIMAGE, IMAGE_ICON, cast(uint)icon);
			}

			return ret;
		}

		saveButton = CreateButton("Save Report", ID_SAVE_BTN, 7);
		sendButton = CreateButton("Send Report", ID_SEND_BTN, 27);
		closeButton = CreateButton("Close", ID_CLOSE_BTN, 28);

		if(iconMod) FreeLibrary(cast(HMODULE)iconMod);

		enum ReportFont = "Courier New\0";
		lf.lfHeight = 14;
		lf.lfFaceName[0 .. ReportFont.length] = ReportFont;

		font = CreateFontIndirectA(&lf);
		if(!font) SystemException();

		reportField = CreateWindowExA(
			WS_EX_CLIENTEDGE, "EDIT", null,
			WS_CHILD | WS_VSCROLL | WS_HSCROLL | WS_VISIBLE |
				ES_READONLY | ES_MULTILINE | ES_AUTOVSCROLL,
			0, 0, 0, 0, wnd, cast(HMENU)ID_REPORT, inst, null
		);
		if(!reportField) SystemException();

		SendMessageA(reportField, EM_SETMARGINS, EC_LEFTMARGIN | EC_RIGHTMARGIN, MAKELONG(5, 5));
		SendMessageA(reportField, WM_SETFONT, cast(WPARAM)font, 0);
		SendMessageA(reportField, WM_SETTEXT, 0, cast(LPARAM)cs.lpCreateParams);

		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	case WM_GETMINMAXINFO:
		MINMAXINFO* mm = cast(MINMAXINFO*)l;
		mm.ptMinTrackSize.x = ReportWindowMinWidth;
		mm.ptMinTrackSize.y = ReportWindowMinHeight;
		break;

	case WM_SIZE:
		int width = LOWORD(l), halfWidth = width >> 1;
		int height = HIWORD(l);

		enum {
			BtnWidth = 125,
			BtnHeight = 35,
			NumBtns = 3,
			Pad = 10,
			ReportHPad = Pad * 2,
			ReportVPad = BtnHeight + Pad * 3,
			BtnVPad = BtnHeight + Pad,
			BtnHalfWidth = (BtnWidth * NumBtns + (Pad * (NumBtns - 1))) >> 1
		}

		if(!MoveWindow(reportField, Pad, Pad, width - ReportHPad, height - ReportVPad, true))
			SystemException();

		void Move(HWND wnd, int i) {
			if(!MoveWindow(wnd, halfWidth - BtnHalfWidth + BtnWidth * i + Pad * i,
				height - BtnVPad, BtnWidth, BtnHeight, true))
					SystemException();
		}

		Move(saveButton, 0);
		Move(sendButton, 1);
		Move(closeButton, 2);

		break;

	case WM_COMMAND:
		if(HIWORD(w) != BN_CLICKED) break;

		int id = LOWORD(w);

		// GetSaveFileName fails on win7.. no idea why
		if(id == ID_SAVE_BTN) {
			/*char[256] path = void;
			path[0 .. 11] = "Report.txt\0";

			OPENFILENAMEA ofn;
			ofn.lStructSize = OPENFILENAME.sizeof;
			ofn.hwndOwner = wnd;
			ofn.lpstrFilter = "Text File\0*.txt\0\0";
			ofn.lpstrFile = path.ptr;
			ofn.nMaxFile = path.length;
			ofn.Flags = OFN_OVERWRITEPROMPT;

			try {
				if(!GetSaveFileNameA(&ofn)) SystemException();

				uint len = strlen(path.ptr);
				if(path[len-4 .. len] != ".txt") path[len .. len + 5] = ".txt\0";

				HANDLE file = CreateFileA(path.ptr, GENERIC_WRITE, 0,
					null, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, null);

				if(file == INVALID_HANDLE_VALUE) SystemException();
				scope(exit) if(!CloseHandle(file)) SystemException();

				char* text;
				SendMessageA(reportField, WM_GETTEXT, 0, cast(LPARAM)text);
				len = strlen(text);

				uint written = void;
				if(!WriteFile(file, text, len, &written, null))
					SystemException();

				if(written != len) throw new SystemException("Couldn't write entire data.");

				id = ID_CLOSE_BTN;
			}
			catch(SystemException e) {
				MessageBoxA(wnd, (e.toString ~ '\0').ptr, "Error!", MB_OK | MB_ICONERROR);
			}*/

			MessageBoxA(wnd, "TODO", "Error!", MB_OK | MB_ICONERROR);
		}

		if(id == ID_SEND_BTN)
			MessageBoxA(wnd, "TODO", "Error!", MB_OK | MB_ICONERROR);

		if(id == ID_CLOSE_BTN)
			SendMessageA(wnd, WM_CLOSE, 0, 0);

		break;

	default:
		return DefWindowProcA(wnd, msg, w, l);
	}

	} // try
	catch(Exception e) {
		MessageBoxA(HWND.init, (e.toString ~ '\0').ptr, "Crash Window Handler Error!", MB_ICONERROR | MB_OK);
		PostQuitMessage(0);
	}

	return 0;
}

} // version(Windows)

else version(Gnome) {

import std.c.stdio;	

import ext.Gnome.gtk;
import ext.Gnome.gobject;

void ErrorGUI(in ErrorReport* report) {
	int argc;
	if(!gtk_init_check(&argc, null)) {
		printf("gtk failed!\n");
		Pause;
	}

	void SetSignal(A, B)(A* widget, string signal, B callback) {
		g_signal_connect(cast(void*)widget, signal.ptr, cast(GCallback)callback, null);
	}

	// Create the report window
	GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title(cast(GtkWindow*)window, ReportWindowTitle);
	gtk_window_set_default_size(cast(GtkWindow*)window, ReportWindowWidth, ReportWindowHeight);
	gtk_window_set_icon_name(cast(GtkWindow*)window, GTK_STOCK_DIALOG_ERROR);

	SetSignal(window, "destroy", &gtk_main_quit);

	// Create the root box
	GtkWidget* vbox = gtk_vbox_new(false, 0);
	gtk_container_add(cast(GtkContainer*)window, vbox);

	// Create the report edit
	GtkWidget* view = gtk_text_view_new();
	gtk_text_view_set_editable(cast(GtkTextView*)view, false);
	gtk_text_view_set_cursor_visible(cast(GtkTextView*)view, false);

	GtkTextBuffer* buffer = gtk_text_view_get_buffer(cast(GtkTextView*)view);
	gtk_text_buffer_set_text(buffer, report.dumpText.ptr, -1);

	gtk_box_pack_start(cast(GtkBox*)vbox, view, true, true, 0);

	// Create the buttons box
	GtkWidget* hbox = gtk_hbutton_box_new();
	gtk_box_set_spacing(cast(GtkBox*)hbox, 10);
	gtk_button_box_set_layout(cast(GtkButtonBox*)hbox, GTK_BUTTONBOX_CENTER);
	gtk_box_pack_start(cast(GtkBox*)vbox, hbox, false, true, 10);

	// Create the buttons
	GtkWidget* CreateButton(B)(string label, string stockId, B callback) {
		GtkWidget* button = gtk_button_new_with_label(label.ptr);

		GtkWidget* image = gtk_image_new_from_stock(stockId.ptr, GTK_ICON_SIZE_BUTTON);
		gtk_button_set_image(cast(GtkButton*)button, image);

		gtk_container_add(cast(GtkContainer*)hbox, button);

		if(callback) SetSignal(button, "clicked", callback);

		return button;
	}

	CreateButton("Save Report", GTK_STOCK_SAVE_AS, &OnClickSave);
	CreateButton("Send Report", GTK_STOCK_CONNECT, &OnClickSend);
	GtkWidget* close = CreateButton("Close", GTK_STOCK_CLOSE, null);

	g_signal_connect_swapped(cast(void*)close, "clicked",
		cast(GCallback)(&gtk_widget_destroy), cast(void*)(window));

	// Display the window and run the main loop
	gtk_widget_show_all(window);
	gtk_main();
}

extern(C):

void OnClickSave(GtkButton* button, gpointer user_data) {
	// TODO
}

void OnClickSend(GtkButton* button, gpointer user_data) {
	// TODO
}

} // version(Gnome)
else static assert(0);