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 dguihub.toolbar;
10 
11 import dguihub.core.controls.subclassedcontrol;
12 import dguihub.core.utils;
13 public import dguihub.imagelist;
14 
15 enum ToolButtonStyle : ubyte {
16    button = TBSTYLE_BUTTON,
17    separator = TBSTYLE_SEP,
18    dropdown = TBSTYLE_DROPDOWN,
19 }
20 
21 class ToolButton {
22    public Event!(ToolButton, EventArgs) click;
23 
24    private ToolBar _owner;
25    private ContextMenu _ctxMenu;
26    private ToolButtonStyle _tbs;
27    private int _imgIndex;
28    private bool _enabled;
29 
30    package this(ToolBar tb, ToolButtonStyle tbs, int imgIndex, bool enabled) {
31       this._owner = tb;
32       this._tbs = tbs;
33       this._imgIndex = imgIndex;
34       this._enabled = enabled;
35    }
36 
37    @property public final int index() {
38       if (this._owner && this._owner.created && this._owner.buttons) {
39          int i = 0;
40 
41          foreach (ToolButton tbtn; this._owner.buttons) {
42             if (tbtn is this) {
43                return i;
44             }
45 
46             i++;
47          }
48       }
49 
50       return -1;
51    }
52 
53    @property public final ToolButtonStyle style() {
54       return this._tbs;
55    }
56 
57    @property public final void style(ToolButtonStyle tbs) {
58       this._tbs = tbs;
59 
60       if (this._owner && this._owner.created) {
61          TBBUTTONINFOW tbinfo = void;
62 
63          tbinfo.cbSize = TBBUTTONINFOW.sizeof;
64          tbinfo.dwMask = TBIF_BYINDEX | TBIF_STYLE;
65          tbinfo.fsStyle = tbs;
66 
67          this._owner.sendMessage(TB_SETBUTTONINFOW, this.index, cast(LPARAM)&tbinfo);
68       }
69    }
70 
71    @property public final int imageIndex() {
72       return this._imgIndex;
73    }
74 
75    @property public final void imageIndex(int idx) {
76       this._imgIndex = idx;
77 
78       if (this._owner && this._owner.created) {
79          TBBUTTONINFOW tbinfo = void;
80 
81          tbinfo.cbSize = TBBUTTONINFOW.sizeof;
82          tbinfo.dwMask = TBIF_BYINDEX | TBIF_IMAGE;
83          tbinfo.iImage = idx;
84 
85          this._owner.sendMessage(TB_SETBUTTONINFOW, this.index, cast(LPARAM)&tbinfo);
86       }
87    }
88 
89    @property public final bool enabled() {
90       return this._enabled;
91    }
92 
93    @property public final void enabled(bool b) {
94       this._enabled = b;
95 
96       if (this._owner && this._owner.created) {
97          TBBUTTONINFOW tbinfo = void;
98 
99          tbinfo.cbSize = TBBUTTONINFOW.sizeof;
100          tbinfo.dwMask = TBIF_BYINDEX | TBIF_STATE;
101          this._owner.sendMessage(TB_GETBUTTONINFOW, this.index, cast(LPARAM)&tbinfo); //Ricavo i dati completi.
102 
103          b ? (tbinfo.fsState |= TBSTATE_ENABLED) : (tbinfo.fsState &= ~TBSTATE_ENABLED);
104          this._owner.sendMessage(TB_SETBUTTONINFOW, this.index, cast(LPARAM)&tbinfo);
105       }
106    }
107 
108    @property public ContextMenu contextMenu() {
109       return this._ctxMenu;
110    }
111 
112    @property public void contextMenu(ContextMenu cm) {
113       this._ctxMenu = cm;
114    }
115 
116    @property public final ToolBar toolBar() {
117       return this._owner;
118    }
119 
120    package void onToolBarButtonClick(EventArgs e) {
121       this.click(this, e);
122    }
123 }
124 
125 class ToolBar : SubclassedControl {
126    private Collection!(ToolButton) _buttons;
127    private ImageList _imgList;
128 
129    @property public final ImageList imageList() {
130       return this._imgList;
131    }
132 
133    @property public final void imageList(ImageList imgList) {
134       this._imgList = imgList;
135 
136       if (this.created) {
137          this.sendMessage(TB_SETIMAGELIST, 0, cast(LPARAM)this._imgList.handle);
138       }
139    }
140 
141    public final ToolButton addDropdownButton(int imgIndex, ContextMenu ctxMenu, bool en = true) {
142       if (!this._buttons) {
143          this._buttons = new Collection!(ToolButton)();
144       }
145 
146       ToolButton tb = new ToolButton(this, ToolButtonStyle.dropdown, imgIndex, en);
147       tb.contextMenu = ctxMenu;
148       this._buttons.add(tb);
149 
150       if (this.created) {
151          ToolBar.addItem(tb);
152       }
153 
154       return tb;
155    }
156 
157    public final ToolButton addButton(int imgIndex, bool en = true) {
158       if (!this._buttons) {
159          this._buttons = new Collection!(ToolButton)();
160       }
161 
162       ToolButton tb = new ToolButton(this, ToolButtonStyle.button, imgIndex, en);
163       this._buttons.add(tb);
164 
165       if (this.created) {
166          ToolBar.addItem(tb);
167       }
168 
169       return tb;
170    }
171 
172    public final void addSeparator() {
173       if (!this._buttons) {
174          this._buttons = new Collection!(ToolButton)();
175       }
176 
177       ToolButton tb = new ToolButton(this, ToolButtonStyle.separator, -1, true);
178       this._buttons.add(tb);
179 
180       if (this.created) {
181          ToolBar.addItem(tb);
182       }
183    }
184 
185    public final void removeButton(int idx) {
186       this._buttons.removeAt(idx);
187 
188       if (this.created) {
189          this.sendMessage(TB_DELETEBUTTON, idx, 0);
190       }
191    }
192 
193    @property public final ToolButton[] buttons() {
194       if (this._buttons) {
195          return this._buttons.get();
196       }
197 
198       return null;
199    }
200 
201    private void forceToolbarSize() {
202       uint sz = this.sendMessage(TB_GETBUTTONSIZE, 0, 0);
203 
204       this.size = Size(LOWORD(sz), HIWORD(sz));
205    }
206 
207    private static void addItem(ToolButton tb) {
208       TBBUTTON tbtn;
209 
210       switch (tb.style) {
211       case ToolButtonStyle.button, ToolButtonStyle.dropdown:
212          tbtn.iBitmap = tb.imageIndex;
213          tbtn.fsStyle = cast(ubyte)tb.style;
214          tbtn.fsState = cast(ubyte)(tb.enabled ? TBSTATE_ENABLED : 0);
215          tbtn.dwData = winCast!(uint)(tb);
216          break;
217 
218       case ToolButtonStyle.separator:
219          tbtn.fsStyle = cast(ubyte)tb.style;
220          break;
221 
222       default:
223          assert(false, "Unknown ToolButton Style");
224       }
225 
226       if (tb.toolBar._dock is DockStyle.left || tb.toolBar._dock is DockStyle.right) {
227          tbtn.fsState |= TBSTATE_WRAP;
228       }
229 
230       tb.toolBar.sendMessage(TB_INSERTBUTTONW, tb.index, cast(LPARAM)&tbtn);
231    }
232 
233    protected override void createControlParams(ref CreateControlParams ccp) {
234       ccp.superclassName = WC_TOOLBAR;
235       ccp.className = WC_DTOOLBAR;
236       this.setStyle(TBSTYLE_FLAT | CCS_NODIVIDER | CCS_NOPARENTALIGN, true);
237 
238       if (this._dock is DockStyle.none) {
239          this._dock = DockStyle.top;
240       }
241 
242       if (this._dock is DockStyle.left || this._dock is DockStyle.right) {
243          this.setStyle(CCS_VERT, true);
244       }
245 
246       super.createControlParams(ccp);
247    }
248 
249    protected override void onHandleCreated(EventArgs e) {
250       this.sendMessage(TB_BUTTONSTRUCTSIZE, TBBUTTON.sizeof, 0);
251       int exStyle = this.sendMessage(TB_GETEXTENDEDSTYLE, 0, 0);
252       this.sendMessage(TB_SETEXTENDEDSTYLE, 0, exStyle | TBSTYLE_EX_DRAWDDARROWS);
253       this.forceToolbarSize(); // HACK: Forza il ridimensionamento della barra strumenti.
254 
255       if (this._imgList) {
256          this.sendMessage(TB_SETIMAGELIST, 0, cast(LPARAM)this._imgList.handle);
257       }
258 
259       if (this._buttons) {
260          foreach (ToolButton tb; this._buttons) {
261             ToolBar.addItem(tb);
262          }
263       }
264 
265       super.onHandleCreated(e);
266    }
267 
268    protected override void onReflectedMessage(ref Message m) {
269       switch (m.msg) {
270       case WM_NOTIFY: {
271             NMHDR* pNmhdr = cast(NMHDR*)m.lParam;
272 
273             switch (pNmhdr.code) {
274             case NM_CLICK: {
275                   NMMOUSE* pNMouse = cast(NMMOUSE*)m.lParam;
276                   ToolButton tBtn = winCast!(ToolButton)(pNMouse.dwItemData);
277 
278                   if (tBtn) {
279                      tBtn.onToolBarButtonClick(EventArgs.empty); //FIXME!
280                   }
281                }
282                break;
283 
284             case TBN_DROPDOWN: {
285                   NMTOOLBARW* pNmToolbar = cast(NMTOOLBARW*)m.lParam;
286 
287                   Point pt = Cursor.position;
288                   convertPoint(pt, null, this);
289                   int idx = this.sendMessage(TB_HITTEST, 0, cast(LPARAM)&pt.point);
290 
291                   if (idx != -1) {
292                      ToolButton tbtn = this._buttons[idx];
293 
294                      if (tbtn && tbtn.contextMenu) {
295                         tbtn.contextMenu.popupMenu(this._handle, Cursor.position);
296                      }
297                   }
298                }
299                break;
300 
301             default:
302                break;
303             }
304          }
305          break;
306 
307       default:
308          break;
309       }
310 
311       super.onReflectedMessage(m);
312    }
313 
314    protected override void wndProc(ref Message m) {
315       if (m.msg == WM_WINDOWPOSCHANGING) {
316          /*
317 			 * HACK: Forza il ridimensionamento della barra strumenti.
318 			 */
319 
320          WINDOWPOS* pWindowPos = cast(WINDOWPOS*)m.lParam;
321          uint sz = this.sendMessage(TB_GETBUTTONSIZE, 0, 0);
322 
323          switch (this._dock) {
324          case DockStyle.top, DockStyle.bottom:
325             pWindowPos.cy = HIWORD(sz);
326             break;
327 
328          case DockStyle.left, DockStyle.right:
329             pWindowPos.cx = LOWORD(sz);
330             break;
331 
332          default:
333             break;
334          }
335       }
336 
337       super.wndProc(m);
338    }
339 }