1 /// 2 module dfl.toolbar; 3 4 private import dfl.base, dfl.control, dfl.drawing, dfl.application, 5 dfl.event, dfl.collections; 6 private import dfl.internal.winapi, dfl.internal.dlib; 7 8 version(DFL_NO_IMAGELIST) 9 { 10 } 11 else 12 { 13 private import dfl.imagelist; 14 } 15 16 version(DFL_NO_MENUS) 17 version = DFL_TOOLBAR_NO_MENU; 18 19 version(DFL_TOOLBAR_NO_MENU) 20 { 21 } 22 else 23 { 24 private import dfl.menu; 25 } 26 27 28 /// 29 enum ToolBarButtonStyle: ubyte 30 { 31 PUSH_BUTTON = TBSTYLE_BUTTON, /// 32 TOGGLE_BUTTON = TBSTYLE_CHECK, /// ditto 33 SEPARATOR = TBSTYLE_SEP, /// ditto 34 //DROP_DOWN_BUTTON = TBSTYLE_DROPDOWN, /// ditto 35 DROP_DOWN_BUTTON = TBSTYLE_DROPDOWN | BTNS_WHOLEDROPDOWN, /// ditto 36 } 37 38 39 /// 40 class ToolBarButton 41 { 42 /// 43 this() 44 { 45 Application.ppin(cast(void*)this); 46 } 47 48 /// 49 this(Dstring text) 50 { 51 this(); 52 53 this.text = text; 54 } 55 56 57 version(DFL_NO_IMAGELIST) 58 { 59 } 60 else 61 { 62 /// 63 final @property void imageIndex(int index) // setter 64 { 65 this._imgidx = index; 66 67 //if(tbar && tbar.created) 68 // tbar.updateItem(this); 69 } 70 71 /// ditto 72 final @property int imageIndex() // getter 73 { 74 return _imgidx; 75 } 76 } 77 78 79 /// 80 @property void text(Dstring newText) // setter 81 { 82 _text = newText; 83 84 //if(tbar && tbar.created) 85 // 86 } 87 88 /// ditto 89 @property Dstring text() // getter 90 { 91 return _text; 92 } 93 94 95 /// 96 final @property void style(ToolBarButtonStyle st) // setter 97 { 98 this._style = st; 99 100 //if(tbar && tbar.created) 101 // 102 } 103 104 /// ditto 105 final @property ToolBarButtonStyle style() // getter 106 { 107 return _style; 108 } 109 110 111 override Dstring toString() 112 { 113 return text; 114 } 115 116 117 override Dequ opEquals(Object o) 118 { 119 return text == getObjectString(o); 120 } 121 122 123 Dequ opEquals(Dstring val) 124 { 125 return text == val; 126 } 127 128 129 override int opCmp(Object o) 130 { 131 return stringICmp(text, getObjectString(o)); 132 } 133 134 135 int opCmp(Dstring val) 136 { 137 return stringICmp(text, val); 138 } 139 140 141 /// 142 final @property void tag(Object o) // setter 143 { 144 _tag = o; 145 } 146 147 /// ditto 148 final @property Object tag() // getter 149 { 150 return _tag; 151 } 152 153 154 version(DFL_TOOLBAR_NO_MENU) 155 { 156 } 157 else 158 { 159 /// 160 final @property void dropDownMenu(ContextMenu cmenu) // setter 161 { 162 _cmenu = cmenu; 163 } 164 165 /// ditto 166 final @property ContextMenu dropDownMenu() // getter 167 { 168 return _cmenu; 169 } 170 } 171 172 173 /// 174 final @property ToolBar parent() // getter 175 { 176 return tbar; 177 } 178 179 180 /// 181 final @property Rect rectangle() // getter 182 { 183 //if(!tbar || !tbar.created) 184 if(!visible) 185 return Rect(0, 0, 0, 0); // ? 186 assert(tbar !is null); 187 RECT rect; 188 //assert(-1 != tbar.buttons.indexOf(this)); 189 tbar.prevwproc(TB_GETITEMRECT, tbar.buttons.indexOf(this), cast(LPARAM)&rect); // Fails if item is hidden. 190 return Rect(&rect); // Should return all 0`s if TB_GETITEMRECT failed. 191 } 192 193 194 /// 195 final @property void visible(bool byes) // setter 196 { 197 if(byes) 198 _state &= ~TBSTATE_HIDDEN; 199 else 200 _state |= TBSTATE_HIDDEN; 201 202 if(tbar && tbar.created) 203 tbar.prevwproc(TB_SETSTATE, _id, MAKELPARAM(_state, 0)); 204 } 205 206 /// ditto 207 final @property bool visible() // getter 208 { 209 if(!tbar || !tbar.created) 210 return false; 211 return true; // To-do: get actual hidden state. 212 } 213 214 215 /// 216 final @property void enabled(bool byes) // setter 217 { 218 if(byes) 219 _state |= TBSTATE_ENABLED; 220 else 221 _state &= ~TBSTATE_ENABLED; 222 223 if(tbar && tbar.created) 224 tbar.prevwproc(TB_SETSTATE, _id, MAKELPARAM(_state, 0)); 225 } 226 227 /// ditto 228 final @property bool enabled() // getter 229 { 230 if(_state & TBSTATE_ENABLED) 231 return true; 232 return false; 233 } 234 235 236 /// 237 final @property void pushed(bool byes) // setter 238 { 239 if(byes) 240 _state = (_state & ~TBSTATE_INDETERMINATE) | TBSTATE_CHECKED; 241 else 242 _state &= ~TBSTATE_CHECKED; 243 244 if(tbar && tbar.created) 245 tbar.prevwproc(TB_SETSTATE, _id, MAKELPARAM(_state, 0)); 246 } 247 248 /// ditto 249 final @property bool pushed() // getter 250 { 251 if(TBSTATE_CHECKED == (_state & TBSTATE_CHECKED)) 252 return true; 253 return false; 254 } 255 256 257 /// 258 final @property void partialPush(bool byes) // setter 259 { 260 if(byes) 261 _state = (_state & ~TBSTATE_CHECKED) | TBSTATE_INDETERMINATE; 262 else 263 _state &= ~TBSTATE_INDETERMINATE; 264 265 if(tbar && tbar.created) 266 tbar.prevwproc(TB_SETSTATE, _id, MAKELPARAM(_state, 0)); 267 } 268 269 /// ditto 270 final @property bool partialPush() // getter 271 { 272 if(TBSTATE_INDETERMINATE == (_state & TBSTATE_INDETERMINATE)) 273 return true; 274 return false; 275 } 276 277 278 private: 279 ToolBar tbar; 280 int _id = 0; 281 Dstring _text; 282 Object _tag; 283 ToolBarButtonStyle _style = ToolBarButtonStyle.PUSH_BUTTON; 284 BYTE _state = TBSTATE_ENABLED; 285 version(DFL_TOOLBAR_NO_MENU) 286 { 287 } 288 else 289 { 290 ContextMenu _cmenu; 291 } 292 version(DFL_NO_IMAGELIST) 293 { 294 } 295 else 296 { 297 int _imgidx = -1; 298 } 299 } 300 301 302 /// 303 class ToolBarButtonClickEventArgs: EventArgs 304 { 305 this(ToolBarButton tbbtn) 306 { 307 _btn = tbbtn; 308 } 309 310 311 /// 312 final @property ToolBarButton button() // getter 313 { 314 return _btn; 315 } 316 317 318 private: 319 320 ToolBarButton _btn; 321 } 322 323 324 /// 325 class ToolBar: ControlSuperClass // docmain 326 { 327 class ToolBarButtonCollection 328 { 329 protected this() 330 { 331 } 332 333 334 private: 335 336 ToolBarButton[] _buttons; 337 338 339 void _adding(size_t idx, ToolBarButton val) 340 { 341 if(val.tbar) 342 throw new DflException("ToolBarButton already belongs to a ToolBar"); 343 } 344 345 346 void _added(size_t idx, ToolBarButton val) 347 { 348 val.tbar = tbar; 349 val._id = tbar._allocTbbID(); 350 351 if(created) 352 { 353 _ins(idx, val); 354 } 355 } 356 357 358 void _removed(size_t idx, ToolBarButton val) 359 { 360 if(size_t.max == idx) // Clear all. 361 { 362 } 363 else 364 { 365 if(created) 366 { 367 prevwproc(TB_DELETEBUTTON, idx, 0); 368 } 369 val.tbar = null; 370 } 371 } 372 373 374 public: 375 376 mixin ListWrapArray!(ToolBarButton, _buttons, 377 _adding, _added, 378 _blankListCallback!(ToolBarButton), _removed, 379 true, false, false, 380 true); // CLEAR_EACH 381 } 382 383 384 private @property ToolBar tbar() 385 { 386 return this; 387 } 388 389 390 this() 391 { 392 _initToolbar(); 393 394 _tbuttons = new ToolBarButtonCollection(); 395 396 dock = DockStyle.TOP; 397 398 //wexstyle |= WS_EX_CLIENTEDGE; 399 wclassStyle = toolbarClassStyle; 400 } 401 402 403 /// 404 final @property ToolBarButtonCollection buttons() // getter 405 { 406 return _tbuttons; 407 } 408 409 410 // buttonSize... 411 412 413 /// 414 final @property Size imageSize() // getter 415 { 416 version(DFL_NO_IMAGELIST) 417 { 418 } 419 else 420 { 421 if(_imglist) 422 return _imglist.imageSize; 423 } 424 return Size(16, 16); // ? 425 } 426 427 428 version(DFL_NO_IMAGELIST) 429 { 430 } 431 else 432 { 433 /// 434 final @property void imageList(ImageList imglist) // setter 435 { 436 if(isHandleCreated) 437 { 438 prevwproc(TB_SETIMAGELIST, 0, cast(WPARAM)imglist.handle); 439 } 440 441 _imglist = imglist; 442 } 443 444 /// ditto 445 final @property ImageList imageList() // getter 446 { 447 return _imglist; 448 } 449 } 450 451 452 /// 453 Event!(ToolBar, ToolBarButtonClickEventArgs) buttonClick; 454 455 456 /// 457 protected void onButtonClick(ToolBarButtonClickEventArgs ea) 458 { 459 buttonClick(this, ea); 460 } 461 462 463 protected override void onReflectedMessage(ref Message m) 464 { 465 switch(m.msg) 466 { 467 case WM_NOTIFY: 468 { 469 auto nmh = cast(LPNMHDR)m.lParam; 470 switch(nmh.code) 471 { 472 case NM_CLICK: 473 { 474 auto nmm = cast(LPNMMOUSE)nmh; 475 if(nmm.dwItemData) 476 { 477 auto tbb = cast(ToolBarButton)cast(void*)nmm.dwItemData; 478 scope ToolBarButtonClickEventArgs bcea = new ToolBarButtonClickEventArgs(tbb); 479 onButtonClick(bcea); 480 } 481 } 482 break; 483 484 case TBN_DROPDOWN: 485 version(DFL_TOOLBAR_NO_MENU) // This condition might be removed later. 486 { 487 } 488 else // Ditto. 489 { 490 auto nmtb = cast(LPNMTOOLBARA)nmh; // NMTOOLBARA/NMTOOLBARW doesn't matter here; string fields not used. 491 auto tbb = buttomFromID(nmtb.iItem); 492 if(tbb) 493 { 494 version(DFL_TOOLBAR_NO_MENU) // Keep this here in case the other condition is removed. 495 { 496 } 497 else // Ditto. 498 { 499 if(tbb._cmenu) 500 { 501 auto brect = tbb.rectangle; 502 tbb._cmenu.show(this, pointToScreen(Point(brect.x, brect.bottom))); 503 // Note: showing a menu also triggers a click! 504 } 505 } 506 } 507 } 508 m.result = TBDDRET_DEFAULT; 509 return; 510 511 default: 512 } 513 } 514 break; 515 516 default: 517 super.onReflectedMessage(m); 518 } 519 } 520 521 522 protected override @property Size defaultSize() // getter 523 { 524 return Size(100, 16); 525 } 526 527 528 protected override void createParams(ref CreateParams cp) 529 { 530 super.createParams(cp); 531 532 cp.className = TOOLBAR_CLASSNAME; 533 } 534 535 536 537 // Used internally 538 /+package+/ final ToolBarButton buttomFromID(int id) // package 539 { 540 foreach(tbb; _tbuttons._buttons) 541 { 542 if(id == tbb._id) 543 return tbb; 544 } 545 return null; 546 } 547 548 549 package int _lastTbbID = 0; 550 551 package final int _allocTbbID() 552 { 553 for(int j = 0; j != 250; j++) 554 { 555 _lastTbbID++; 556 if(_lastTbbID >= short.max) 557 _lastTbbID = 1; 558 559 if(!buttomFromID(_lastTbbID)) 560 return _lastTbbID; 561 } 562 return 0; 563 } 564 565 566 567 protected override void onHandleCreated(EventArgs ea) 568 { 569 super.onHandleCreated(ea); 570 571 static assert(TBBUTTON.sizeof == 20); 572 prevwproc(TB_BUTTONSTRUCTSIZE, TBBUTTON.sizeof, 0); 573 574 //prevwproc(TB_SETPADDING, 0, MAKELPARAM(0, 0)); 575 576 version(DFL_NO_IMAGELIST) 577 { 578 } 579 else 580 { 581 if(_imglist) 582 prevwproc(TB_SETIMAGELIST, 0, cast(WPARAM)_imglist.handle); 583 } 584 585 foreach(idx, tbb; _tbuttons._buttons) 586 { 587 _ins(idx, tbb); 588 } 589 590 //prevwproc(TB_AUTOSIZE, 0, 0); 591 } 592 593 594 protected override void prevWndProc(ref Message msg) 595 { 596 //msg.result = CallWindowProcA(toolbarPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 597 msg.result = dfl.internal.utf.callWindowProc(toolbarPrevWndProc, msg.hWnd, msg.msg, msg.wParam, msg.lParam); 598 } 599 600 601 private: 602 603 ToolBarButtonCollection _tbuttons; 604 605 version(DFL_NO_IMAGELIST) 606 { 607 } 608 else 609 { 610 ImageList _imglist; 611 } 612 613 614 void _ins(size_t idx, ToolBarButton tbb) 615 { 616 // To change: TB_SETBUTTONINFO 617 618 TBBUTTON xtb; 619 version(DFL_NO_IMAGELIST) 620 { 621 xtb.iBitmap = -1; 622 } 623 else 624 { 625 xtb.iBitmap = tbb._imgidx; 626 } 627 xtb.idCommand = tbb._id; 628 xtb.dwData = cast(DWORD)cast(void*)tbb; 629 xtb.fsState = tbb._state; 630 xtb.fsStyle = TBSTYLE_AUTOSIZE | tbb._style; // TBSTYLE_AUTOSIZE factors in the text's width instead of default button size. 631 LRESULT lresult; 632 // MSDN says iString can be either an int offset or pointer to a string buffer. 633 if(dfl.internal.utf.useUnicode) 634 { 635 if(tbb._text.length) 636 xtb.iString = cast(typeof(xtb.iString))dfl.internal.utf.toUnicodez(tbb._text); 637 //prevwproc(TB_ADDBUTTONSW, 1, cast(LPARAM)&xtb); 638 lresult = prevwproc(TB_INSERTBUTTONW, idx, cast(LPARAM)&xtb); 639 } 640 else 641 { 642 if(tbb._text.length) 643 xtb.iString = cast(typeof(xtb.iString))dfl.internal.utf.toAnsiz(tbb._text); 644 //prevwproc(TB_ADDBUTTONSA, 1, cast(LPARAM)&xtb); 645 lresult = prevwproc(TB_INSERTBUTTONA, idx, cast(LPARAM)&xtb); 646 } 647 //if(!lresult) 648 // throw new DflException("Unable to add ToolBarButton"); 649 } 650 651 652 package: 653 final: 654 LRESULT prevwproc(UINT msg, WPARAM wparam, LPARAM lparam) 655 { 656 //return CallWindowProcA(toolbarPrevWndProc, hwnd, msg, wparam, lparam); 657 return dfl.internal.utf.callWindowProc(toolbarPrevWndProc, hwnd, msg, wparam, lparam); 658 } 659 } 660 661 662 private 663 { 664 enum TOOLBAR_CLASSNAME = "DFL_ToolBar"; 665 666 WNDPROC toolbarPrevWndProc; 667 668 LONG toolbarClassStyle; 669 670 void _initToolbar() 671 { 672 if(!toolbarPrevWndProc) 673 { 674 _initCommonControls(ICC_BAR_CLASSES); 675 676 dfl.internal.utf.WndClass info; 677 toolbarPrevWndProc = superClass(HINSTANCE.init, "ToolbarWindow32", TOOLBAR_CLASSNAME, info); 678 if(!toolbarPrevWndProc) 679 _unableToInit(TOOLBAR_CLASSNAME); 680 toolbarClassStyle = info.wc.style; 681 } 682 } 683 } 684