1 /** DGui project file.
2 
3 Copyright: Trogu Antonio Davide 2011-2013
4 
5 License: $(HTTP boost.org/LICENSE_1_0.txt, Boost License 1.0).
6 
7 Authors: Trogu Antonio Davide
8 */
9 module dgui.tooltip;
10 
11 import dgui.core.controls.subclassedcontrol;
12 
13 enum ToolTipIcons
14 {
15 	none	= TTI_NONE,
16 	info 	= TTI_INFO,
17 	warning = TTI_WARNING,
18 	error   = TTI_ERROR,
19 }
20 
21 class ToolTip: SubclassedControl
22 {
23 	private ToolTipIcons _ttIcon = ToolTipIcons.none;
24 	private bool _creating = false;
25 	private Control _ctrl;
26 	private string _title;
27 
28 	public override void dispose()
29 	{
30 		if(this._ctrl)
31 		{
32 			this.removeTool();
33 		}
34 
35 		super.dispose();
36 	}
37 
38 	private void addTool(Control c)
39 	{
40 		this._ctrl = c;
41 
42 		TOOLINFOW ti;
43 
44 		ti.cbSize = TOOLINFOW.sizeof;
45 		ti.uFlags = TTF_SUBCLASS | TTF_IDISHWND;
46 		ti.lpszText = cast(wchar*)LPSTR_TEXTCALLBACKW;
47 		ti.hwnd = c.parent ? c.parent.handle : c.handle;
48 		ti.uId = cast(uint)c.handle;
49 
50 		this.sendMessage(TTM_ADDTOOLW, 0, cast(LPARAM)&ti);
51 	}
52 
53 	private void removeTool()
54 	{
55 		TOOLINFOW ti;
56 
57 		ti.cbSize = TOOLINFOW.sizeof;
58 		ti.hwnd = this._ctrl.parent ? this._ctrl.parent.handle : this._ctrl.handle;
59 		ti.uId = cast(uint)this._ctrl.handle;
60 
61 		this.sendMessage(TTM_DELTOOLW, 0, cast(LPARAM)&ti);
62 		this._ctrl = null;
63 	}
64 
65 	@property public void icon(ToolTipIcons tti)
66 	{
67 		this._ttIcon = tti;
68 
69 		if(this.created)
70 		{
71 			this.sendMessage(TTM_SETTITLEW, this._ttIcon, cast(LPARAM)toUTFz!(wchar*)(this._title));
72 		}
73 	}
74 
75 	@property public string title()
76 	{
77 		return this._title;
78 	}
79 
80 	@property public void title(string s)
81 	{
82 		this._title = s;
83 
84 		if(this.created)
85 		{
86 			this.sendMessage(TTM_SETTITLEW, this._ttIcon, cast(LPARAM)toUTFz!(wchar*)(this._title));
87 		}
88 	}
89 
90 	@property public void baloonTip(bool b)
91 	{
92 		this.setStyle(TTS_BALLOON, b);
93 	}
94 
95 	@property public void closeButton(bool b)
96 	{
97 		this.setStyle(TTS_CLOSE, b);
98 	}
99 
100 	@property public void alwaysTip(bool b)
101 	{
102 		this.setStyle(TTS_ALWAYSTIP, b);
103 	}
104 
105 	@property public override void parent(Control c)
106 	{
107 		if(this._creating)
108 		{
109 			super.parent = c;
110 		}
111 		else
112 		{
113 			throwException!(DGuiException)("A ToolTip cannot have a parent");
114 		}
115 	}
116 
117 	public final void activate(Control c)
118 	{
119 		if(!this.created)
120 		{
121 			this._creating = true;
122 			this.parent = c.parent ? c.parent : c;
123 			this.show();
124 			this._creating = false;
125 		}
126 
127 		if(this._ctrl !is c)
128 		{
129 			if(this._ctrl)
130 			{
131 				this.removeTool();
132 			}
133 
134 			this.addTool(c);
135 		}
136 
137 		this.sendMessage(TTM_ACTIVATE, true, 0);
138 	}
139 
140 	public override void show()
141 	{
142 		if(this._creating)
143 		{
144 			super.show();
145 		}
146 		else
147 		{
148 			throwException!(DGuiException)("Cannot create a ToolTip directly");
149 		}
150 	}
151 
152 	protected override void createControlParams(ref CreateControlParams ccp)
153 	{
154 		ccp.superclassName = WC_TOOLTIP;
155 		ccp.className = WC_DTOOLTIP;
156 		ccp.defaultBackColor = SystemColors.colorInfo;
157 		ccp.defaultForeColor = SystemColors.colorInfoText;
158 
159 		this.setStyle(WS_POPUP | TTS_NOPREFIX, true);
160 		this.setExStyle(WS_EX_TOPMOST | WS_EX_TOOLWINDOW, true);
161 
162 		/* According To MSDN:
163 		    The window procedure for the tooltip control automatically sets the size, position, and visibility of the control.
164 		    The height of the tooltip window is based on the height of the font currently selected into the device context
165 		    for the tooltip control.
166 		    The width varies based on the length of the string currently in the tooltip window. */
167 		this.bounds = Rect(CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT);
168 
169 		ToolTip.setBit(this._cBits, ControlBits.cannotAddChild | ControlBits.useCachedText, true);
170 		super.createControlParams(ccp);
171 	}
172 
173 	protected override void onReflectedMessage(ref Message m)
174 	{
175 		if(m.msg == WM_NOTIFY)
176 		{
177 			NMHDR* pNotify = cast(NMHDR*)m.lParam;
178 
179 			if(pNotify.code == TTN_GETDISPINFOW)
180 			{
181 				NMTTDISPINFOW* pDispInfo = cast(NMTTDISPINFOW*)pNotify;
182 				pDispInfo.lpszText = toUTFz!(wchar*)(this.text);
183 			}
184 		}
185 
186 		super.onReflectedMessage(m);
187 	}
188 
189 	protected override void onHandleCreated(EventArgs e)
190 	{
191 		if(this._ttIcon !is ToolTipIcons.none || this._title.length)
192 		{
193 			this.sendMessage(TTM_SETTITLEW, this._ttIcon, cast(LPARAM)toUTFz!(wchar*)(this._title));
194 		}
195 
196 		SetWindowPos(this._handle, cast(HWND)HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
197 		super.onHandleCreated(e);
198 	}
199 }