1 // Written by Christopher E. Miller
2 // See the included license.txt for copyright and license details.
3 
4 module dfl.statusbar;
5 import core.sys.windows.windows;
6 import core.sys.windows.commctrl;
7 
8 import dfl.application;
9 import dfl.base;
10 import dfl.collections;
11 import dfl.control;
12 import dfl.event;
13 import dfl.exception;
14 import dfl.internal.dlib;
15 import dfl.internal.dlib;
16 import dfl.internal.utf;
17 // FIX: import dfl.internal.winapi;
18 
19 private extern (Windows) void _initStatusbar();
20 
21 /+
22 enum StatusBarPanelAutoSize: ubyte {
23    NONE,
24    CONTENTS,
25    SPRING,
26 }
27 +/
28 
29 enum StatusBarPanelBorderStyle : ubyte {
30    NONE,
31    SUNKEN,
32    RAISED
33 }
34 
35 class StatusBarPanel : DObject {
36 
37    this(Dstring text) {
38       this._txt = text;
39    }
40 
41    this(Dstring text, int width) {
42       this._txt = text;
43       this._width = width;
44    }
45 
46    this() {}
47 
48    override Dstring toString() {
49       return _txt;
50    }
51 
52    override Dequ opEquals(Object o) {
53       return _txt == getObjectString(o); // ?
54    }
55 
56    Dequ opEquals(StatusBarPanel pnl) {
57       return _txt == pnl._txt;
58    }
59 
60    Dequ opEquals(Dstring val) {
61       return _txt == val;
62    }
63 
64    override int opCmp(Object o) {
65       return stringICmp(_txt, getObjectString(o)); // ?
66    }
67 
68    int opCmp(StatusBarPanel pnl) {
69       return stringICmp(_txt, pnl._txt);
70    }
71 
72    int opCmp(Dstring val) {
73       return stringICmp(_txt, val);
74    }
75 
76    /+
77 
78    final @property void alignment(HorizontalAlignment ha) {
79 
80    }
81 
82 
83    final @property HorizontalAlignment alignment() {
84       //LEFT
85    }
86    +/
87 
88    /+
89 
90    final @property void autoSize(StatusBarPanelAutoSize asize) {
91 
92    }
93 
94 
95    final @property StatusBarPanelAutoSize autoSize() {
96       //NONE
97    }
98    +/
99 
100    final @property void borderStyle(StatusBarPanelBorderStyle bs) {
101       switch (bs) {
102       case StatusBarPanelBorderStyle.NONE:
103          _utype = (_utype & ~SBT_POPOUT) | SBT_NOBORDERS;
104          break;
105 
106       case StatusBarPanelBorderStyle.RAISED:
107          _utype = (_utype & ~SBT_NOBORDERS) | SBT_POPOUT;
108          break;
109 
110       case StatusBarPanelBorderStyle.SUNKEN:
111          _utype &= ~(SBT_NOBORDERS | SBT_POPOUT);
112          break;
113 
114       default:
115          assert(0);
116       }
117 
118       if (_parent && _parent.isHandleCreated) {
119          _parent.panels._fixtexts(); // Also fixes styles.
120       }
121    }
122 
123    final @property StatusBarPanelBorderStyle borderStyle() {
124       if (_utype & SBT_POPOUT) {
125          return StatusBarPanelBorderStyle.RAISED;
126       }
127       if (_utype & SBT_NOBORDERS) {
128          return StatusBarPanelBorderStyle.NONE;
129       }
130       return StatusBarPanelBorderStyle.RAISED;
131    }
132 
133    // icon
134 
135    /+
136 
137    final @property void minWidth(int mw)
138    in {
139       assert(mw >= 0);
140    }
141    body {
142 
143    }
144 
145 
146    final @property int minWidth() {
147       //10
148    }
149    +/
150 
151    final @property StatusBar parent() {
152       return _parent;
153    }
154 
155    // style
156    final @property void text(Dstring txt) {
157       if (_parent && _parent.isHandleCreated) {
158          int idx = _parent.panels.indexOf(this);
159          assert(-1 != idx);
160          _parent._sendidxtext(idx, _utype, txt);
161       }
162 
163       this._txt = txt;
164    }
165 
166    final @property Dstring text() {
167       return _txt;
168    }
169 
170    /+
171 
172    final @property void toolTipText(Dstring txt) {
173 
174    }
175 
176 
177    final @property Dstring toolTipText() {
178       //null
179    }
180    +/
181 
182    final @property void width(int w) {
183       _width = w;
184 
185       if (_parent && _parent.isHandleCreated) {
186          _parent.panels._fixwidths();
187       }
188    }
189 
190    final @property int width() {
191       return _width;
192    }
193 
194 private:
195 
196    Dstring _txt = null;
197    int _width = 100;
198    StatusBar _parent = null;
199    WPARAM _utype = 0; // StatusBarPanelBorderStyle.SUNKEN.
200 }
201 
202 /+
203 
204 class StatusBarPanelClickEventArgs: MouseEventArgs {
205 
206    this(StatusBarPanel sbpanel, MouseButtons btn, int clicks, int x, int y) {
207       this._sbpanel = sbpanel;
208       super(btn, clicks, x, y, 0);
209    }
210 
211 
212  private:
213    StatusBarPanel _sbpanel;
214 }
215 +/
216 
217 class StatusBar : ControlSuperClass {
218 
219    class StatusBarPanelCollection {
220       protected this(StatusBar sb)
221       in {
222          assert(sb.lpanels is null);
223       }
224       body {
225          this.sb = sb;
226       }
227 
228    private:
229 
230       StatusBar sb;
231       package StatusBarPanel[] _panels;
232 
233       package void _fixwidths() {
234          assert(isHandleCreated);
235 
236          UINT[20] _pws = void;
237          UINT[] pws = _pws;
238          if (_panels.length > _pws.length) {
239             pws = new UINT[_panels.length];
240          }
241          UINT right = 0;
242          foreach (idx, pnl; _panels) {
243             if (-1 == pnl.width) {
244                pws[idx] = -1;
245             } else {
246                right += pnl.width;
247                pws[idx] = right;
248             }
249          }
250          sb.prevwproc(SB_SETPARTS, cast(WPARAM) _panels.length, cast(LPARAM) pws.ptr);
251       }
252 
253       void _fixtexts() {
254          assert(isHandleCreated);
255 
256          if (dfl.internal.utf.useUnicode) {
257             foreach (idx, pnl; _panels) {
258                sb.prevwproc(SB_SETTEXTW, cast(WPARAM) idx | pnl._utype,
259                   cast(LPARAM) dfl.internal.utf.toUnicodez(pnl._txt));
260             }
261          } else {
262             foreach (idx, pnl; _panels) {
263                sb.prevwproc(SB_SETTEXTA, cast(WPARAM) idx | pnl._utype,
264                   cast(LPARAM) dfl.internal.utf.toAnsiz(pnl._txt));
265             }
266          }
267       }
268 
269       void _setcurparts() {
270          assert(isHandleCreated);
271 
272          _fixwidths();
273 
274          _fixtexts();
275       }
276 
277       void _removed(size_t idx, Object val) {
278          if (size_t.max == idx) { // Clear all.
279             if (sb.isHandleCreated) {
280                sb.prevwproc(SB_SETPARTS, 0, 0); // 0 parts.
281             }
282          } else {
283             if (sb.isHandleCreated) {
284                _setcurparts();
285             }
286          }
287       }
288 
289       void _added(size_t idx, StatusBarPanel val) {
290          if (val._parent) {
291             throw new DflException("StatusBarPanel already belongs to a StatusBar");
292          }
293 
294          val._parent = sb;
295 
296          if (sb.isHandleCreated) {
297             _setcurparts();
298          }
299       }
300 
301       void _adding(size_t idx, StatusBarPanel val) {
302          if (_panels.length >= 254) { // Since SB_SETTEXT with 255 has special meaning.
303             throw new DflException("Too many status bar panels");
304          }
305       }
306 
307    public:
308 
309       mixin ListWrapArray!(StatusBarPanel, _panels, _adding, _added,
310          _blankListCallback!(StatusBarPanel), _removed, true, /+true+/ false, false) _wraparray;
311    }
312 
313    this() {
314       _initStatusbar();
315 
316       _issimple = true;
317       wstyle |= SBARS_SIZEGRIP;
318       wclassStyle = statusbarClassStyle;
319       //height = ?;
320       dock = DockStyle.BOTTOM;
321 
322       lpanels = new StatusBarPanelCollection(this);
323    }
324 
325    // backColor / font / foreColor ...
326 
327    override @property void dock(DockStyle ds) {
328       switch (ds) {
329       case DockStyle.BOTTOM:
330       case DockStyle.TOP:
331          super.dock = ds;
332          break;
333 
334       default:
335          throw new DflException("Invalid status bar dock");
336       }
337    }
338 
339    alias dock = Control.dock; // Overload.
340 
341    final @property StatusBarPanelCollection panels() {
342       return lpanels;
343    }
344 
345    final @property void showPanels(bool byes) {
346       if (!byes == _issimple) {
347          return;
348       }
349 
350       if (isHandleCreated) {
351          prevwproc(SB_SIMPLE, cast(WPARAM) !byes, 0);
352 
353          /+ // It's kept in sync even if simple.
354          if(byes) {
355             panels._setcurparts();
356          }
357          +/
358 
359          if (!byes) {
360             _sendidxtext(255, 0, _simpletext);
361          }
362       }
363 
364       _issimple = !byes;
365    }
366 
367    final @property bool showPanels() {
368       return !_issimple;
369    }
370 
371    final @property void sizingGrip(bool byes) {
372       if (byes == sizingGrip) {
373          return;
374       }
375 
376       if (byes) {
377          _style(_style() | SBARS_SIZEGRIP);
378       } else {
379          _style(_style() & ~SBARS_SIZEGRIP);
380       }
381    }
382 
383    final @property bool sizingGrip() {
384       if (wstyle & SBARS_SIZEGRIP) {
385          return true;
386       }
387       return false;
388    }
389 
390    override @property void text(Dstring txt) {
391       if (isHandleCreated && !showPanels) {
392          _sendidxtext(255, 0, txt);
393       }
394 
395       this._simpletext = txt;
396 
397       onTextChanged(EventArgs.empty);
398    }
399 
400    override @property Dstring text() {
401       return this._simpletext;
402    }
403 
404    protected override void onHandleCreated(EventArgs ea) {
405       super.onHandleCreated(ea);
406 
407       if (_issimple) {
408          prevwproc(SB_SIMPLE, cast(WPARAM) true, 0);
409          panels._setcurparts();
410          if (_simpletext.length) {
411             _sendidxtext(255, 0, _simpletext);
412          }
413       } else {
414          panels._setcurparts();
415          prevwproc(SB_SIMPLE, cast(WPARAM) false, 0);
416       }
417    }
418 
419    protected override void createParams(ref CreateParams cp) {
420       super.createParams(cp);
421 
422       cp.className = STATUSBAR_CLASSNAME;
423    }
424 
425    protected override void prevWndProc(ref Message msg) {
426       //msg.result = CallWindowProcA(statusbarPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam);
427       msg.result = dfl.internal.utf.callWindowProc(statusbarPrevWndProc,
428          msg.hWnd, msg.msg, msg.wParam, msg.lParam);
429    }
430 
431    /+
432    protected override void createHandle() {
433       //CreateStatusWindow
434    }
435    +/
436 
437    //StatusBarPanelClickEventHandler panelClick;
438    //Event!(StatusBar, StatusBarPanelClickEventArgs) panelClick;
439 
440 protected:
441 
442    // onDrawItem ...
443 
444    /+
445 
446    void onPanelClick(StatusBarPanelClickEventArgs ea) {
447       panelClick(this, ea);
448    }
449    +/
450 
451 private:
452 
453    StatusBarPanelCollection lpanels;
454    Dstring _simpletext = null;
455    bool _issimple = true;
456 
457 package:
458 final:
459 
460    LRESULT prevwproc(UINT msg, WPARAM wparam, LPARAM lparam) {
461       //return CallWindowProcA(statusbarPrevWndProc, hwnd, msg, wparam, lparam);
462       return dfl.internal.utf.callWindowProc(statusbarPrevWndProc, hwnd, msg, wparam,
463          lparam);
464    }
465 
466    void _sendidxtext(int idx, WPARAM utype, Dstring txt) {
467       assert(isHandleCreated);
468 
469       if (dfl.internal.utf.useUnicode) {
470          prevwproc(SB_SETTEXTW, cast(WPARAM) idx | utype,
471             cast(LPARAM) dfl.internal.utf.toUnicodez(txt));
472       } else {
473          prevwproc(SB_SETTEXTA, cast(WPARAM) idx | utype, cast(LPARAM) dfl.internal.utf.toAnsiz(
474             txt));
475       }
476    }
477 }