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 }