/* MinWin Button classes * * Various button classes like PushButtons * * 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.button; public import minwin.component; private { import minwin.icon; import minwin.app; import minwin.window; import minwin.peerimpl; import minwin.logging; import std.string; import minwin.peer; } abstract class AbstractButton : WindowChild { int cmd; Icon icon(); void icon(Icon c); char[] text(); void text(char[] c); MultiDelegate!(Component) actionDelegate; void doCommand(int cmd) { // only command is action command version(LOG) log.writefln("calling action delegate"); if (cmd == ButtonClickedCommand) { actionDelegate(cast(Component)this); if (this.cmd != 0) { super.doCommand(this.cmd); } } } } abstract class AbstractToggleButton : AbstractButton { bool selected(); void selected(bool x); } version (MinWin32) { version = SimpleToggleGroup; } version (SimpleToggleGroup) { private import minwin.dialog; // manages selected state between several ToggleButtons class ToggleGroup { AbstractToggleButton[] buttons; void addButton(AbstractToggleButton[] btns ...) { buttons ~= btns; foreach(b; btns) { b.actionDelegate ~= &select; } } void select(Component c) { version (LOG) log.writefln("selecting component ", c); foreach(AbstractToggleButton b; buttons) { b.selected = (b is c); } } void select(int index) { foreach(int n, AbstractToggleButton b; buttons) { b.selected = n == index; } } int selected() { foreach(int n, AbstractToggleButton b; buttons) { version(LOG)log.printf("testing button %d %x\n",n,b); if (b && b.selected) return n; } version(LOG)log.printf(" no button selected??\n"); return 0; } } } version (MinWin32) { private import minwin.mswindows; const int ButtonClickedCommand = BN_CLICKED; template PreferredSizeImpl() { const int paddingX; const int paddingY; Point preferredSize() { int width,height; HDC dc = GetDC(peer); sysAssert(dc !is null, "Failed to get button DC in preferredSize"); SIZE s; try { Font f = standardFont(StandardFont.Gui); HFONT oldfont = SelectObject(dc,f.peer); sysAssert(oldfont !is null, "Failed to get Button font in preferredSize"); BOOL status = GetTextExtentPoint32X(dc,text_data,text_data.length,&s); SelectObject(dc,oldfont); sysAssert(status != false, "Failed to get font extents in preferredSize"); } finally { ReleaseDC(peer,dc); } width = s.cx > 75-paddingX ? s.cx+paddingX*2 : 75; height = s.cy > 23-paddingY ? s.cy+paddingY*2 : 23; if (userPreferredWidth_data > 0) width = userPreferredWidth_data; if (userPreferredHeight_data > 0) height = userPreferredHeight_data; return XY(width,height); // Requires WinXP // SIZE sz; // SendMessageA(peer,BCM_GETIDEALSIZE,0,cast(int)&sz); // width = sz.cx; // height = sz.cy; } } template SelectedImpl() { bool selected() { version(LOG)log.printf("sending message..."); bool res = SendMessageA(peer,BM_GETCHECK,0,0) == BST_CHECKED; version(LOG)log.printf(" got %d\n",res); return res; } void selected(bool x) { SendMessageA(peer,BM_SETCHECK,x,0); } } template IconTextImpl() { Icon icon_data; Icon icon() { return icon_data; } void icon(Icon c) { icon_data = c; SendMessageA(peer,BM_SETIMAGE,cast(WPARAM)c.peer,0); } char[] text_data; char[] text() { return text_data; } void text(char[] c) { text_data = c; SendMessageX(peer,WM_SETTEXT,0,c); } } class Button : AbstractButton { this(Component parent, char[] intext, char[] name = "") { paddingX = GetSystemMetrics(SM_CXFIXEDFRAME)*2; paddingY = GetSystemMetrics(SM_CXFIXEDFRAME)*2; PeerForAdd parentp = parent.getPeerForAdd(); text_data = intext; peer = CreateWindowX("BUTTON",intext, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 0,0,10,10,parentp, cast(HMENU)0,gApp.hInstance,null); sysAssert(peer !is null, "Failed to create peer Button"); finish_button_setup(this,parent,name,peer); } mixin WindowChildImpl!(); mixin IconTextImpl!(); mixin PreferredSizeImpl!(); } class CheckBox : AbstractToggleButton { this(Component parent, char[] intext, char[] name = "") { paddingX = GetSystemMetrics(SM_CXMENUCHECK); paddingY = GetSystemMetrics(SM_CYFIXEDFRAME)*2; PeerForAdd parentp = parent.getPeerForAdd(); text_data = intext; peer = CreateWindowX("BUTTON",intext, BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 0,0,10,10,parentp, cast(HMENU)0,gApp.hInstance,null); sysAssert(peer !is null, "Failed to create peer CheckBox"); finish_button_setup(this,parent,name,peer); } mixin WindowChildImpl!(); mixin SelectedImpl!(); mixin IconTextImpl!(); mixin PreferredSizeImpl!(); } class RadioButton : AbstractToggleButton { this(Component parent, char[] intext, char[] name = "") { paddingX = GetSystemMetrics(SM_CXMENUCHECK); paddingY = GetSystemMetrics(SM_CYFIXEDFRAME)*2; PeerForAdd parentp = parent.getPeerForAdd(); text_data = intext; peer = CreateWindowX("BUTTON",intext, BS_RADIOBUTTON | WS_CHILD | WS_VISIBLE, 0,0,10,10,parentp, cast(HMENU)0,gApp.hInstance,null); sysAssert(peer !is null, "Failed to create peer RadioButton"); finish_button_setup(this,parent,name,peer); } mixin WindowChildImpl!(); mixin SelectedImpl!(); mixin IconTextImpl!(); mixin PreferredSizeImpl!(); } class ToggleButton : AbstractToggleButton { this(Component parent, char[] intext, char[] name = "") { paddingX = GetSystemMetrics(SM_CXFIXEDFRAME)*2; paddingY = GetSystemMetrics(SM_CYFIXEDFRAME)*2; PeerForAdd parentp = parent.getPeerForAdd(); text_data = intext; peer = CreateWindowX("BUTTON",intext, BS_PUSHLIKE | BS_AUTOCHECKBOX | WS_CHILD | WS_VISIBLE, 0,0,10,10,parentp, cast(HMENU)0,gApp.hInstance,null); sysAssert(peer !is null, "Failed to create peer ToggleButton"); finish_button_setup(this,parent,name,peer); } mixin WindowChildImpl!(); mixin SelectedImpl!(); mixin IconTextImpl!(); mixin PreferredSizeImpl!(); } private void finish_button_setup(AbstractButton b,Component parent, char[] name, WindowChildPeer peer) { setWindowChildPeer(b,peer,OWNS_PEER); b.name = name; Font f = standardFont(StandardFont.Gui); SendMessageA(peer,WM_SETFONT,cast(WPARAM)f.peer,0); parent.addChild(b); } } else version (GTK) { private import minwin.gtk; private import std.c.string; const int ButtonClickedCommand = 1; abstract class HeavyAbstractToggleButton : AbstractToggleButton { mixin WindowChildImpl!(); } // manages selected state between several ToggleButtons class ToggleGroup { HeavyAbstractToggleButton[] buttons; void addButton(HeavyAbstractToggleButton[] btns ...) { buttons ~= btns; foreach(b; btns) { RadioButton rb = cast(RadioButton)b; if (rb is null) { b.actionDelegate ~= &select; } else { // set the RadioButton group to its sibling, if any if (buttons.length != 0) { GtkRadioButton* rbpeer1 = cast(GtkRadioButton*)buttons[0].peer; GtkRadioButton* rbpeer = cast(GtkRadioButton*)rb.peer; GSList* group = gtk_radio_button_get_group(rbpeer1); gtk_radio_button_set_group(rbpeer,group); } } } } void select(Component c) { version (LOG) log.writefln("selecting component ", c); foreach(HeavyAbstractToggleButton b; buttons) { b.selected = (b is c); } } void select(int index) { foreach(int n, HeavyAbstractToggleButton b; buttons) { b.selected = n == index; } } int selected() { foreach(int n, HeavyAbstractToggleButton b; buttons) { if (b.selected) return n; } } } template SelectedImpl() { bool selected() { return gtk_toggle_button_get_active(cast(GtkToggleButton*)peer) != 0; } void selected(bool x) { gtk_toggle_button_set_active(cast(GtkToggleButton*)peer,x); } } template IconTextImpl() { Icon icon_data; Icon icon() { return icon_data; } void icon(Icon c) { icon_data = c; // SendMessageA(peer,BM_SETIMAGE,cast(WPARAM)c.peer,0); } char[] text() { char* str = gtk_button_get_label(cast(GtkButton*)peer); if (str is null) return ""; else return str[0..strlen(str)].dup; } void text(char[] c) { gtk_button_set_label(cast(GtkButton*)peer,toStringz(c)); } } class Button : AbstractButton { this(Component parent, char[] text, char[] name = "") { PeerForAdd parentp = parent.getPeerForAdd(); this.name = name; char* str = toStringz(text); peer = gtk_button_new_with_label(str); finish_button_setup(this,peer,parentp,parent); } mixin WindowChildImpl!(); mixin IconTextImpl!(); } class CheckBox : HeavyAbstractToggleButton { this(Component parent, char[] text, char[] name = "") { PeerForAdd parentp = parent.getPeerForAdd(); this.name = name; char* str = toStringz(text); peer = gtk_check_button_new_with_label(str); finish_button_setup(this,peer,parentp,parent); } mixin SelectedImpl!(); mixin IconTextImpl!(); } class RadioButton : HeavyAbstractToggleButton { this(Component parent, char[] text, char[] name = "") { PeerForAdd parentp = parent.getPeerForAdd(); this.name = name; char* str = toStringz(text); peer = gtk_radio_button_new_with_label(null,str); finish_button_setup(this,peer,parentp,parent); } mixin SelectedImpl!(); mixin IconTextImpl!(); } class ToggleButton : HeavyAbstractToggleButton { this(Component parent, char[] text, char[] name = "") { PeerForAdd parentp = parent.getPeerForAdd(); this.name = name; char* str = toStringz(text); peer = gtk_toggle_button_new_with_label(str); finish_button_setup(this,peer,parentp,parent); } mixin SelectedImpl!(); mixin IconTextImpl!(); } private void finish_button_setup(AbstractButton b, WindowChildPeer peer, PeerForAdd parentp, Component parent) { gtk_widget_set_sensitive(peer,true); gtk_container_add(cast(GtkContainer*)parentp,peer); g_signal_connect_data(peer,"clicked", cast(GCallback)&mw_buttonclick_callback, cast(gpointer)b, null,cast(GConnectFlags)0); setWindowChildPeer(b,peer,OWNS_PEER); parent.addChild(b); gtk_widget_realize(peer); b.visible = true; } extern (C) void mw_buttonclick_callback(GtkButton *but, gpointer ud) { Component c = cast(Component) ud; if (c) { c.doCommand(ButtonClickedCommand); } } }