1 /* 2 Dynamic construction of dialogs for FAR Manager 3.0 build 4444 3 4 Original code: http://farmanager.googlecode.com/svn/trunk/plugins/common/unicode/DlgBuilder.hpp 5 License: derived from original one without extra restriction 6 */ 7 8 module dlgbuilder; 9 10 import farcolor; 11 import farplugin; 12 13 import std.stdint; 14 import core.sys.windows.windows; 15 import core.stdc.string; 16 import core.stdc.stddef; 17 18 // Элемент выпадающего списка в диалоге. 19 struct DialogBuilderListItem 20 { 21 // Строчка из LNG-файла, которая будет показана в диалоге. 22 int MessageId; 23 24 // Значение, которое будет записано в поле Value при выборе этой строчки. 25 int ItemValue; 26 } 27 28 class DialogItemBinding(T) 29 { 30 int BeforeLabelID = -1; 31 int AfterLabelID = -1; 32 33 this() 34 { 35 } 36 37 void SaveValue(T *Item, int RadioGroupIndex) 38 { 39 } 40 } 41 42 class CheckBoxBinding(T): DialogItemBinding!T 43 { 44 private: 45 BOOL* Value; 46 int Mask; 47 48 public: 49 this(int* aValue, int aMask) 50 { 51 Value = aValue; 52 Mask = aMask; 53 } 54 55 override void SaveValue(T* Item, int RadioGroupIndex) 56 { 57 if (!Mask) 58 { 59 *Value = Item.Selected; 60 } 61 else 62 { 63 if (Item.Selected) 64 *Value |= Mask; 65 else 66 *Value &= ~Mask; 67 } 68 } 69 } 70 71 class RadioButtonBinding(T): DialogItemBinding!T 72 { 73 private: 74 int* Value; 75 76 public: 77 this(int* aValue) 78 { 79 Value = aValue; 80 } 81 82 override void SaveValue(T* Item, int RadioGroupIndex) 83 { 84 if (Item.Selected) 85 *Value = RadioGroupIndex; 86 } 87 } 88 89 /* 90 Класс для динамического построения диалогов. Автоматически вычисляет положение и размер 91 для добавляемых контролов, а также размер самого диалога. Автоматически записывает выбранные 92 значения в указанное место после закрытия диалога по OK. 93 94 По умолчанию каждый контрол размещается в новой строке диалога. Ширина для текстовых строк, 95 checkbox и radio button вычисляется автоматически, для других элементов передаётся явно. 96 Есть также возможность добавить статический текст слева или справа от контрола, при помощи 97 методов AddTextBefore и AddTextAfter. 98 99 Поддерживается также возможность расположения контролов в две колонки. Используется следующим 100 образом: 101 - StartColumns() 102 - добавляются контролы для первой колонки 103 - ColumnBreak() 104 - добавляются контролы для второй колонки 105 - EndColumns() 106 107 Поддерживается также возможность расположения контролов внутри бокса. Используется следующим 108 образом: 109 - StartSingleBox() 110 - добавляются контролы 111 - EndSingleBox() 112 113 Базовая версия класса используется как внутри кода Far, так и в плагинах. 114 */ 115 116 class DialogBuilderBase(T) 117 { 118 protected: 119 T* m_DialogItems; 120 DialogItemBinding!T[] m_Bindings; 121 int m_DialogItemsCount; 122 int m_DialogItemsAllocated; 123 int m_NextY; 124 int m_Indent; 125 int m_SingleBoxIndex; 126 int m_FirstButtonID; 127 int m_CancelButtonID; 128 int m_ColumnStartIndex; 129 int m_ColumnBreakIndex; 130 int m_ColumnStartY; 131 int m_ColumnEndY; 132 intptr_t m_ColumnMinWidth; 133 intptr_t m_ButtonsWidth; 134 135 static const int SECOND_COLUMN = -2; 136 137 void ReallocDialogItems() 138 { 139 // реаллокация инвалидирует указатели на DialogItemEx, возвращённые из 140 // AddDialogItem и аналогичных методов, поэтому размер массива подбираем такой, 141 // чтобы все нормальные диалоги помещались без реаллокации 142 // TODO хорошо бы, чтобы они вообще не инвалидировались 143 m_DialogItemsAllocated += 64; 144 if (!m_DialogItems) 145 { 146 m_DialogItems = new T[m_DialogItemsAllocated].ptr; 147 m_Bindings = new DialogItemBinding!T[m_DialogItemsAllocated]; 148 } 149 else 150 { 151 T* NewDialogItems = new T[m_DialogItemsAllocated].ptr; 152 auto NewBindings = new DialogItemBinding!T[m_DialogItemsAllocated]; 153 for(int i=0; i<m_DialogItemsCount; i++) 154 { 155 NewDialogItems [i] = m_DialogItems [i]; 156 NewBindings [i] = m_Bindings [i]; 157 } 158 m_DialogItems.destroy(); 159 m_Bindings.destroy(); 160 m_DialogItems = NewDialogItems; 161 m_Bindings = NewBindings; 162 } 163 } 164 165 T* AddDialogItem(FARDIALOGITEMTYPES Type, in wchar* Text) 166 { 167 if (m_DialogItemsCount == m_DialogItemsAllocated) 168 { 169 ReallocDialogItems(); 170 } 171 int Index = m_DialogItemsCount++; 172 T* Item = &m_DialogItems [Index]; 173 InitDialogItem(Item, Text); 174 Item.Type = Type; 175 m_Bindings [Index] = new DialogItemBinding!T; 176 return Item; 177 } 178 179 void SetNextY(T* Item) 180 { 181 Item.X1 = 5 + m_Indent; 182 Item.Y1 = Item.Y2 = m_NextY++; 183 } 184 185 intptr_t ItemWidth(ref const T Item) 186 { 187 with (FARDIALOGITEMTYPES) switch(Item.Type) 188 { 189 case DI_TEXT: 190 return TextWidth(Item); 191 192 case DI_CHECKBOX: 193 case DI_RADIOBUTTON: 194 case DI_BUTTON: 195 return TextWidth(Item) + 4; 196 197 case DI_EDIT: 198 case DI_FIXEDIT: 199 case DI_COMBOBOX: 200 case DI_LISTBOX: 201 case DI_PSWEDIT: 202 intptr_t Width = Item.X2 - Item.X1 + 1; 203 // стрелка history занимает дополнительное место, но раньше она рисовалась поверх рамки??? 204 if (Item.Flags & DIF_HISTORY) 205 Width++; 206 return Width; 207 208 default: 209 break; 210 } 211 return 0; 212 } 213 214 void AddBorder(in wchar* TitleText) 215 { 216 T* Title = AddDialogItem(FARDIALOGITEMTYPES.DI_DOUBLEBOX, TitleText); 217 Title.X1 = 3; 218 Title.Y1 = 1; 219 } 220 221 void UpdateBorderSize() 222 { 223 T* Title = &m_DialogItems[0]; 224 intptr_t MaxWidth = MaxTextWidth(); 225 intptr_t MaxHeight = 0; 226 Title.X2 = Title.X1 + MaxWidth - 1 + 4; 227 228 for (int i=1; i<m_DialogItemsCount; i++) 229 { 230 if (m_DialogItems[i].Type == FARDIALOGITEMTYPES.DI_SINGLEBOX) 231 { 232 m_Indent = 2; 233 m_DialogItems[i].X2 = Title.X2; 234 } 235 else if (m_DialogItems[i].Type == FARDIALOGITEMTYPES.DI_TEXT && (m_DialogItems[i].Flags & DIF_CENTERTEXT)) 236 {//BUGBUG: two columns items are not supported 237 m_DialogItems[i].X2 = m_DialogItems[i].X1 + MaxWidth - 1; 238 } 239 240 if (m_DialogItems[i].Y2 > MaxHeight) 241 { 242 MaxHeight = m_DialogItems[i].Y2; 243 } 244 } 245 246 Title.X2 += m_Indent; 247 Title.Y2 = MaxHeight + 1; 248 m_Indent = 0; 249 } 250 251 intptr_t MaxTextWidth() 252 { 253 intptr_t MaxWidth = 0; 254 for(int i=1; i<m_DialogItemsCount; i++) 255 { 256 if (m_DialogItems [i].X1 == SECOND_COLUMN) continue; 257 DialogItemBinding!T Binding = FindBinding(m_DialogItems+i); 258 intptr_t Width = ItemWidth(m_DialogItems [i]); 259 if (Binding && Binding.BeforeLabelID != -1) 260 Width += ItemWidth(m_DialogItems [Binding.BeforeLabelID]) + 1; 261 if (Binding && Binding.AfterLabelID != -1) 262 Width += 1 + ItemWidth(m_DialogItems [Binding.AfterLabelID]); 263 264 if (MaxWidth < Width) 265 MaxWidth = Width; 266 } 267 intptr_t ColumnsWidth = 2*m_ColumnMinWidth+1; 268 if (MaxWidth < ColumnsWidth) 269 MaxWidth = ColumnsWidth; 270 if (MaxWidth < m_ButtonsWidth) 271 MaxWidth = m_ButtonsWidth; 272 return MaxWidth; 273 } 274 275 void UpdateSecondColumnPosition() 276 { 277 intptr_t SecondColumnX1 = 4 + (m_DialogItems [0].X2 - m_DialogItems [0].X1 + 1)/2; 278 for(int i=0; i<m_DialogItemsCount; i++) 279 { 280 if (m_DialogItems [i].X1 == SECOND_COLUMN) 281 { 282 DialogItemBinding!T Binding = FindBinding(m_DialogItems+i); 283 284 int before = Binding.BeforeLabelID; 285 long BeforeWidth = 0; 286 if (Binding && Binding.BeforeLabelID != -1) 287 BeforeWidth = m_DialogItems [before].X2 - m_DialogItems [before].X1 + 1 + 1; 288 289 intptr_t Width = m_DialogItems [i].X2 - m_DialogItems [i].X1 + 1; 290 m_DialogItems [i].X1 = SecondColumnX1 + BeforeWidth; 291 m_DialogItems [i].X2 = m_DialogItems [i].X1 + Width - 1; 292 293 if (Binding && Binding.AfterLabelID != -1) 294 { 295 int after = Binding.AfterLabelID; 296 long AfterWidth = m_DialogItems [after].X2 - m_DialogItems [after].X1 + 1; 297 m_DialogItems [after].X1 = m_DialogItems [i].X2 + 1 + 1; 298 m_DialogItems [after].X2 = m_DialogItems [after].X1 + AfterWidth - 1; 299 } 300 } 301 } 302 } 303 304 void InitDialogItem(T* NewDialogItem, in wchar* Text) 305 { 306 } 307 308 int TextWidth(ref const T Item) 309 { 310 return 0; 311 } 312 313 void SetLastItemBinding(DialogItemBinding!T Binding) 314 { 315 if (m_Bindings [m_DialogItemsCount-1]) 316 m_Bindings [m_DialogItemsCount-1].destroy(); 317 m_Bindings [m_DialogItemsCount-1] = Binding; 318 } 319 320 int GetItemID(T* Item) const 321 { 322 int Index = cast(int)(Item - m_DialogItems); 323 if (Index >= 0 && Index < m_DialogItemsCount) 324 return Index; 325 return -1; 326 } 327 328 DialogItemBinding!T FindBinding(T* Item) 329 { 330 int Index = cast(int)(Item - m_DialogItems); 331 if (Index >= 0 && Index < m_DialogItemsCount) 332 return m_Bindings [Index]; 333 return null; 334 } 335 336 void SaveValues() 337 { 338 int RadioGroupIndex = 0; 339 for(int i=0; i<m_DialogItemsCount; i++) 340 { 341 if (m_DialogItems [i].Flags & DIF_GROUP) 342 RadioGroupIndex = 0; 343 else 344 RadioGroupIndex++; 345 346 if (m_Bindings [i]) 347 m_Bindings [i].SaveValue(&m_DialogItems [i], RadioGroupIndex); 348 } 349 } 350 351 const(wchar)* GetLangString(int MessageID) 352 { 353 return null; 354 } 355 356 intptr_t DoShowDialog() 357 { 358 return -1; 359 } 360 361 DialogItemBinding!T CreateCheckBoxBinding(int* Value, int Mask) 362 { 363 return null; 364 } 365 366 DialogItemBinding!T CreateRadioButtonBinding(int* Value) 367 { 368 return null; 369 } 370 371 this() 372 { 373 m_NextY = 2; 374 m_SingleBoxIndex = -1; 375 m_FirstButtonID = -1; 376 m_CancelButtonID = -1; 377 m_ColumnStartIndex = -1; 378 m_ColumnBreakIndex = -1; 379 m_ColumnStartY = -1; 380 m_ColumnEndY = -1; 381 } 382 383 ~this() 384 { 385 for (int i=0; i<m_DialogItemsCount; i++) 386 { 387 if (m_Bindings [i]) 388 m_Bindings [i].destroy(); 389 } 390 m_DialogItems.destroy(); 391 m_Bindings.destroy(); 392 } 393 394 public: 395 396 int GetLastID() const 397 { 398 return m_DialogItemsCount-1; 399 } 400 401 // Добавляет статический текст, расположенный на отдельной строке в диалоге. 402 T* AddText(int LabelId) 403 { 404 return AddText(LabelId == -1 ? "" : GetLangString(LabelId)); 405 } 406 407 // Добавляет статический текст, расположенный на отдельной строке в диалоге. 408 T* AddText(in wchar* Label) 409 { 410 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_TEXT, Label); 411 SetNextY(Item); 412 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 413 return Item; 414 } 415 416 // Добавляет чекбокс. 417 T* AddCheckbox(int TextMessageId, int* Value, int Mask=0, bool ThreeState=false) 418 { 419 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_CHECKBOX, GetLangString(TextMessageId)); 420 if (ThreeState && !Mask) 421 Item.Flags |= DIF_3STATE; 422 SetNextY(Item); 423 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 424 if (!Mask) 425 Item.Selected = *Value; 426 else 427 Item.Selected = (*Value & Mask) != 0; 428 SetLastItemBinding(CreateCheckBoxBinding(Value, Mask)); 429 return Item; 430 } 431 432 // Добавляет группу радиокнопок. 433 void AddRadioButtons(int* Value, int OptionCount, const int[] MessageIDs, bool FocusOnSelected=false) 434 { 435 for(int i=0; i<OptionCount; i++) 436 { 437 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_RADIOBUTTON, GetLangString(MessageIDs[i])); 438 SetNextY(Item); 439 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 440 if (!i) 441 Item.Flags |= DIF_GROUP; 442 if (*Value == i) 443 { 444 Item.Selected = TRUE; 445 if (FocusOnSelected) 446 Item.Flags |= DIF_FOCUS; 447 } 448 SetLastItemBinding(CreateRadioButtonBinding(Value)); 449 } 450 } 451 452 void AddRadioButtons(int* Value, const int[] MessageIDs, bool FocusOnSelected=false) 453 { 454 AddRadioButtons(Value, cast(int) MessageIDs.length, MessageIDs, FocusOnSelected); 455 } 456 457 // Добавляет поле типа FARDIALOGITEMTYPES.DI_FIXEDIT для редактирования указанного числового значения. 458 T* AddIntEditField(int* Value, int Width) 459 { 460 return null; 461 } 462 463 T* AddUIntEditField(uint* Value, int Width) 464 { 465 return null; 466 } 467 468 // Добавляет указанную текстовую строку слева от элемента RelativeTo. 469 T* AddTextBefore(T* RelativeTo, in wchar* Label) 470 { 471 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_TEXT, Label); 472 Item.Y1 = Item.Y2 = RelativeTo.Y1; 473 Item.X1 = 5 + m_Indent; 474 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 475 476 intptr_t RelativeToWidth = RelativeTo.X2 - RelativeTo.X1 + 1; 477 RelativeTo.X1 = Item.X2 + 1 + 1; 478 RelativeTo.X2 = RelativeTo.X1 + RelativeToWidth - 1; 479 480 DialogItemBinding!T Binding = FindBinding(RelativeTo); 481 if (Binding) 482 Binding.BeforeLabelID = GetItemID(Item); 483 484 return Item; 485 } 486 487 T* AddTextBefore(T* RelativeTo, int LabelId) 488 { 489 return AddTextBefore(RelativeTo, GetLangString(LabelId)); 490 } 491 492 // Добавляет указанную текстовую строку справа от элемента RelativeTo. 493 T* AddTextAfter(T* RelativeTo, in wchar* Label) 494 { 495 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_TEXT, Label); 496 Item.Y1 = Item.Y2 = RelativeTo.Y1; 497 Item.X1 = RelativeTo.X2 + 1 + 1; 498 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 499 500 DialogItemBinding!T Binding = FindBinding(RelativeTo); 501 if (Binding) 502 Binding.AfterLabelID = GetItemID(Item); 503 504 return Item; 505 } 506 507 T* AddTextAfter(T* RelativeTo, int LabelId) 508 { 509 return AddTextAfter(RelativeTo, GetLangString(LabelId)); 510 } 511 512 // Добавляет кнопку справа от элемента RelativeTo. 513 T* AddButtonAfter(T* RelativeTo, in wchar* Label) 514 { 515 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_BUTTON, Label); 516 Item.Y1 = Item.Y2 = RelativeTo.Y1; 517 Item.X1 = RelativeTo.X2 + 1 + 1; 518 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 519 520 DialogItemBinding!T Binding = FindBinding(RelativeTo); 521 if (Binding) 522 Binding.AfterLabelID = GetItemID(Item); 523 524 return Item; 525 } 526 527 T* AddButtonAfter(T* RelativeTo, int LabelId) 528 { 529 return AddButtonAfter(RelativeTo, GetLangString(LabelId)); 530 } 531 532 // Начинает располагать поля диалога в две колонки. 533 void StartColumns() 534 { 535 m_ColumnStartIndex = m_DialogItemsCount; 536 m_ColumnStartY = m_NextY; 537 } 538 539 // Завершает колонку полей в диалоге и переходит к следующей колонке. 540 void ColumnBreak() 541 { 542 m_ColumnBreakIndex = m_DialogItemsCount; 543 m_ColumnEndY = m_NextY; 544 m_NextY = m_ColumnStartY; 545 } 546 547 // Завершает расположение полей диалога в две колонки. 548 void EndColumns() 549 { 550 for(int i=m_ColumnStartIndex; i<m_DialogItemsCount; i++) 551 { 552 intptr_t Width = ItemWidth(m_DialogItems [i]); 553 DialogItemBinding!T Binding = FindBinding(m_DialogItems + i); 554 long BindingAdd = 0; 555 if (Binding) { 556 if (Binding.BeforeLabelID != -1) 557 BindingAdd += ItemWidth(m_DialogItems [Binding.BeforeLabelID]) + 1; 558 if (Binding.AfterLabelID != -1) 559 BindingAdd += 1 + ItemWidth(m_DialogItems [Binding.AfterLabelID]); 560 } 561 562 if (Width + BindingAdd > m_ColumnMinWidth) 563 m_ColumnMinWidth = Width + BindingAdd; 564 565 if (i >= m_ColumnBreakIndex) 566 { 567 m_DialogItems [i].X1 = SECOND_COLUMN; 568 m_DialogItems [i].X2 = SECOND_COLUMN + Width - 1; 569 } 570 } 571 572 m_ColumnStartIndex = -1; 573 m_ColumnBreakIndex = -1; 574 575 if (m_NextY < m_ColumnEndY) 576 m_NextY = m_ColumnEndY; 577 } 578 579 // Начинает располагать поля диалога внутри single box 580 void StartSingleBox(int MessageId=-1, bool LeftAlign=false) 581 { 582 T* SingleBox = AddDialogItem(FARDIALOGITEMTYPES.DI_SINGLEBOX, MessageId == -1 ? "" : GetLangString(MessageId)); 583 SingleBox.Flags = LeftAlign ? DIF_LEFTTEXT : DIF_NONE; 584 SingleBox.X1 = 5; 585 SingleBox.Y1 = m_NextY++; 586 m_Indent = 2; 587 m_SingleBoxIndex = m_DialogItemsCount - 1; 588 } 589 590 // Завершает расположение полей диалога внутри single box 591 void EndSingleBox() 592 { 593 if (m_SingleBoxIndex != -1) 594 { 595 m_DialogItems[m_SingleBoxIndex].Y2 = m_NextY++; 596 m_Indent = 0; 597 m_SingleBoxIndex = -1; 598 } 599 } 600 601 // Добавляет пустую строку. 602 void AddEmptyLine() 603 { 604 m_NextY++; 605 } 606 607 // Добавляет сепаратор. 608 void AddSeparator(int MessageId=-1) 609 { 610 return AddSeparator(MessageId == -1 ? "" : GetLangString(MessageId)); 611 } 612 613 void AddSeparator(in wchar* Text) 614 { 615 T* Separator = AddDialogItem(FARDIALOGITEMTYPES.DI_TEXT, Text); 616 Separator.Flags = DIF_SEPARATOR; 617 Separator.X1 = -1; 618 Separator.Y1 = Separator.Y2 = m_NextY++; 619 } 620 621 // Добавляет сепаратор, кнопки OK и Cancel. 622 void AddOKCancel(int OKMessageId, int CancelMessageId, int ExtraMessageId = -1, bool Separator=true) 623 { 624 if (Separator) 625 AddSeparator(); 626 627 int[3] MsgIDs = [ OKMessageId, CancelMessageId, ExtraMessageId ]; 628 int NumButtons = (ExtraMessageId != -1) ? 3 : (CancelMessageId != -1? 2 : 1); 629 630 AddButtons(NumButtons, MsgIDs, 0, 1); 631 } 632 633 // Добавляет линейку кнопок. 634 void AddButtons(int ButtonCount, const int[] MessageIDs, int defaultButtonIndex = 0, int cancelButtonIndex = -1) 635 { 636 int LineY = m_NextY++; 637 T* PrevButton = null; 638 639 for (int i = 0; i < ButtonCount; i++) 640 { 641 T* NewButton = AddDialogItem(FARDIALOGITEMTYPES.DI_BUTTON, GetLangString(MessageIDs[i])); 642 NewButton.Flags = DIF_CENTERGROUP; 643 NewButton.Y1 = NewButton.Y2 = LineY; 644 if (PrevButton) 645 { 646 NewButton.X1 = PrevButton.X2 + 1; 647 } 648 else 649 { 650 NewButton.X1 = 2 + m_Indent; 651 m_FirstButtonID = m_DialogItemsCount - 1; 652 } 653 NewButton.X2 = NewButton.X1 + ItemWidth(*NewButton) - 1 + 1; 654 655 if (defaultButtonIndex == i) 656 { 657 NewButton.Flags |= DIF_DEFAULTBUTTON; 658 } 659 if (cancelButtonIndex == i) 660 m_CancelButtonID = m_DialogItemsCount - 1; 661 662 PrevButton = NewButton; 663 } 664 auto Width = PrevButton.X2 - 1 - m_DialogItems [m_FirstButtonID].X1 + 1; 665 if (m_ButtonsWidth < Width) 666 m_ButtonsWidth = Width; 667 } 668 669 void AddButtons(const int[] MessageIDs, int defaultButtonIndex = 0, int cancelButtonIndex = -1) 670 { 671 AddButtons(cast(int) MessageIDs.length, MessageIDs, defaultButtonIndex, cancelButtonIndex); 672 } 673 674 intptr_t ShowDialogEx() 675 { 676 UpdateBorderSize(); 677 UpdateSecondColumnPosition(); 678 intptr_t Result = DoShowDialog(); 679 if (Result >= 0 && Result != m_CancelButtonID) 680 { 681 SaveValues(); 682 } 683 684 if (m_FirstButtonID >= 0 && Result >= m_FirstButtonID) 685 { 686 Result -= m_FirstButtonID; 687 } 688 return Result; 689 } 690 691 bool ShowDialog() 692 { 693 intptr_t Result = ShowDialogEx(); 694 return Result >= 0 && (m_CancelButtonID < 0 || Result + m_FirstButtonID != m_CancelButtonID); 695 } 696 697 } 698 699 class DialogAPIBinding: DialogItemBinding!FarDialogItem 700 { 701 protected: 702 const(PluginStartupInfo)* Info; 703 HANDLE* DialogHandle; 704 int ID; 705 706 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID) 707 { 708 Info = aInfo; 709 DialogHandle = aHandle; 710 ID = aID; 711 } 712 } 713 714 class PluginCheckBoxBinding: DialogAPIBinding 715 { 716 int *Value; 717 int Mask; 718 719 public: 720 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue, int aMask) 721 { 722 super(aInfo, aHandle, aID); 723 Value = aValue; 724 Mask = aMask; 725 } 726 727 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 728 { 729 int Selected = cast(int)(Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCHECK, ID, null)); 730 if (!Mask) 731 { 732 *Value = Selected; 733 } 734 else 735 { 736 if (Selected) 737 *Value |= Mask; 738 else 739 *Value &= ~Mask; 740 } 741 } 742 } 743 744 class PluginRadioButtonBinding: DialogAPIBinding 745 { 746 private: 747 int *Value; 748 749 public: 750 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue) 751 { 752 super(aInfo, aHandle, aID); 753 Value = aValue; 754 } 755 756 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 757 { 758 if (Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCHECK, ID, null)) 759 *Value = RadioGroupIndex; 760 } 761 } 762 763 class PluginEditFieldBinding: DialogAPIBinding 764 { 765 private: 766 wchar* Value; 767 int MaxSize; 768 769 public: 770 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, wchar* aValue, int aMaxSize) 771 { 772 super(aInfo, aHandle, aID); 773 Value = aValue; 774 MaxSize = aMaxSize; 775 } 776 777 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 778 { 779 const(wchar)* DataPtr = cast(const(wchar)*) Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCONSTTEXTPTR, ID, null); 780 lstrcpynW(Value, DataPtr, MaxSize); 781 } 782 } 783 784 class PluginIntEditFieldBinding: DialogAPIBinding 785 { 786 private: 787 int* Value; 788 wchar[32] Buffer; 789 wchar[32] Mask; 790 791 public: 792 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue, int Width) 793 { 794 super(aInfo, aHandle, aID); 795 Value = aValue; 796 aInfo.FSF.sprintf(Buffer.ptr, "%d"w.ptr, *aValue); 797 int MaskWidth = Width < 31 ? Width : 31; 798 for(int i=1; i<MaskWidth; i++) 799 Mask[i] = '9'; 800 Mask[0] = '#'; 801 Mask[MaskWidth] = '\0'; 802 } 803 804 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 805 { 806 wchar* DataPtr = cast(wchar*) Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCONSTTEXTPTR, ID, null); 807 *Value = Info.FSF.atoi(DataPtr); 808 } 809 810 wchar* GetBuffer() 811 { 812 return Buffer.ptr; 813 } 814 815 const(wchar)* GetMask() const 816 { 817 return Mask.ptr; 818 } 819 } 820 821 class PluginUIntEditFieldBinding: DialogAPIBinding 822 { 823 private: 824 uint* Value; 825 wchar[32] Buffer; 826 wchar[32] Mask; 827 828 public: 829 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, uint* aValue, int Width) 830 { 831 super(aInfo, aHandle, aID); 832 Value = aValue; 833 aInfo.FSF.sprintf(Buffer.ptr, "%u"w.ptr, *aValue); 834 int MaskWidth = Width < 31 ? Width : 31; 835 for(int i=1; i<MaskWidth; i++) 836 Mask[i] = '9'; 837 Mask[0] = '#'; 838 Mask[MaskWidth] = '\0'; 839 } 840 841 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 842 { 843 wchar* DataPtr = cast(wchar*) Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCONSTTEXTPTR, ID, null); 844 *Value = cast(uint)Info.FSF.atoi64(DataPtr); 845 } 846 847 wchar* GetBuffer() 848 { 849 return Buffer.ptr; 850 } 851 852 const(wchar)* GetMask() const 853 { 854 return Mask.ptr; 855 } 856 } 857 858 class PluginListControlBinding : DialogAPIBinding 859 { 860 private: 861 int* SelectedIndex; 862 wchar* TextBuf; 863 FarList* List; 864 865 public: 866 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue, wchar* aText, FarList* aList) 867 { 868 super(aInfo, aHandle, aID); 869 SelectedIndex = aValue; 870 TextBuf = aText; 871 List = aList; 872 } 873 874 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue, FarList* aList) 875 { 876 super(aInfo, aHandle, aID); 877 SelectedIndex = aValue; 878 List = aList; 879 } 880 881 ~this() 882 { 883 if (List) 884 { 885 List.Items.destroy(); 886 } 887 List.destroy(); 888 } 889 890 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 891 { 892 if (SelectedIndex) 893 { 894 *SelectedIndex = cast(int)(Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_LISTGETCURPOS, ID, null)); 895 } 896 if (TextBuf) 897 { 898 FarDialogItemData fdid = {FarDialogItemData.sizeof, 0, TextBuf}; 899 Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETTEXT, ID, &fdid); 900 } 901 } 902 } 903 904 /* 905 Версия класса для динамического построения диалогов, используемая в плагинах к Far. 906 */ 907 class PluginDialogBuilder: DialogBuilderBase!FarDialogItem 908 { 909 protected: 910 const(PluginStartupInfo)* Info; 911 HANDLE DialogHandle; 912 const(wchar)* HelpTopic; 913 GUID PluginId; 914 GUID Id; 915 FARWINDOWPROC DlgProc; 916 void* UserParam; 917 FARDIALOGFLAGS Flags; 918 919 override void InitDialogItem(FarDialogItem* Item, in wchar* Text) 920 { 921 memset(Item, 0, FarDialogItem.sizeof); 922 Item.Data = Text; 923 } 924 925 override int TextWidth(ref const FarDialogItem Item) 926 { 927 return lstrlenW(Item.Data); 928 } 929 930 override const(wchar)* GetLangString(int MessageID) 931 { 932 return Info.GetMsg(&PluginId, MessageID); 933 } 934 935 override intptr_t DoShowDialog() 936 { 937 intptr_t Width = m_DialogItems[0].X2+4; 938 intptr_t Height = m_DialogItems[0].Y2+2; 939 DialogHandle = Info.DialogInit(&PluginId, &Id, -1, -1, Width, Height, HelpTopic, m_DialogItems, m_DialogItemsCount, 0, Flags, DlgProc, UserParam); 940 return Info.DialogRun(DialogHandle); 941 } 942 943 override DialogItemBinding!FarDialogItem CreateCheckBoxBinding(int* Value, int Mask) 944 { 945 return new PluginCheckBoxBinding(Info, &DialogHandle, m_DialogItemsCount-1, Value, Mask); 946 } 947 948 override DialogItemBinding!FarDialogItem CreateRadioButtonBinding(BOOL* Value) 949 { 950 return new PluginRadioButtonBinding(Info, &DialogHandle, m_DialogItemsCount-1, Value); 951 } 952 953 FarDialogItem* AddListControl(FARDIALOGITEMTYPES Type, int* SelectedItem, wchar* Text, int Width, int Height, in wchar*[] ItemsText, size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 954 { 955 FarDialogItem* Item = AddDialogItem(Type, Text ? Text : ""); 956 SetNextY(Item); 957 Item.X2 = Item.X1 + Width; 958 Item.Y2 = Item.Y2 + Height; 959 Item.Flags |= ItemFlags; 960 961 m_NextY += Height; 962 963 FarListItem* ListItems; 964 if (ItemsText) 965 { 966 ListItems = new FarListItem[ItemCount].ptr; 967 for(size_t i=0; i<ItemCount; i++) 968 { 969 ListItems[i].Text = ItemsText[i]; 970 ListItems[i].Flags = SelectedItem && (*SelectedItem == cast(int)i) ? LIF_SELECTED : 0; 971 } 972 } 973 FarList* List = new FarList; 974 List.StructSize = FarList.sizeof; 975 List.Items = ListItems; 976 List.ItemsNumber = ListItems ? ItemCount : 0; 977 Item.ListItems = List; 978 979 SetLastItemBinding(new PluginListControlBinding(Info, &DialogHandle, m_DialogItemsCount - 1, SelectedItem, Text, List)); 980 return Item; 981 } 982 983 FarDialogItem* AddListControl(FARDIALOGITEMTYPES Type, int* SelectedItem, wchar* Text, int Width, int Height, const int[] MessageIDs, size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 984 { 985 const(wchar)*[] ItemsText; 986 if (MessageIDs) 987 { 988 ItemsText = new const(wchar)*[ItemCount]; 989 for (size_t i = 0; i < ItemCount; i++) 990 { 991 ItemsText[i] = GetLangString(MessageIDs[i]); 992 } 993 } 994 995 FarDialogItem* result = AddListControl(Type, SelectedItem, Text, Width, Height, ItemsText, ItemCount, ItemFlags); 996 997 ItemsText.destroy(); 998 999 return result; 1000 } 1001 1002 public: 1003 this(in PluginStartupInfo* aInfo, ref const GUID aPluginId, ref const GUID aId, int TitleMessageID, in wchar* aHelpTopic, FARWINDOWPROC aDlgProc=null, void* aUserParam=null, FARDIALOGFLAGS aFlags = FDLG_NONE) 1004 { 1005 Info = aInfo; 1006 HelpTopic = aHelpTopic; 1007 PluginId = aPluginId; 1008 Id = aId; 1009 DlgProc = aDlgProc; 1010 UserParam = aUserParam; 1011 Flags = aFlags; 1012 AddBorder(GetLangString(TitleMessageID)); 1013 } 1014 1015 this(in PluginStartupInfo* aInfo, ref const GUID aPluginId, ref const GUID aId, in wchar* TitleMessage, in wchar* aHelpTopic, FARWINDOWPROC aDlgProc=null, void* aUserParam=null, FARDIALOGFLAGS aFlags = FDLG_NONE) 1016 { 1017 Info = aInfo; 1018 HelpTopic = aHelpTopic; 1019 PluginId = aPluginId; 1020 Id = aId; 1021 DlgProc = aDlgProc; 1022 UserParam = aUserParam; 1023 Flags = aFlags; 1024 AddBorder(TitleMessage); 1025 } 1026 1027 ~this() 1028 { 1029 Info.DialogFree(DialogHandle); 1030 } 1031 1032 override FarDialogItem* AddIntEditField(int* Value, int Width) 1033 { 1034 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_FIXEDIT, ""); 1035 Item.Flags |= DIF_MASKEDIT; 1036 auto Binding = new PluginIntEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount-1, Value, Width); 1037 Item.Data = Binding.GetBuffer(); 1038 Item.Mask = Binding.GetMask(); 1039 SetNextY(Item); 1040 Item.X2 = Item.X1 + Width - 1; 1041 SetLastItemBinding(Binding); 1042 return Item; 1043 } 1044 1045 override FarDialogItem* AddUIntEditField(uint* Value, int Width) 1046 { 1047 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_FIXEDIT, ""); 1048 Item.Flags |= DIF_MASKEDIT; 1049 auto Binding = new PluginUIntEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount-1, Value, Width); 1050 Item.Data = Binding.GetBuffer(); 1051 Item.Mask = Binding.GetMask(); 1052 SetNextY(Item); 1053 Item.X2 = Item.X1 + Width - 1; 1054 SetLastItemBinding(Binding); 1055 return Item; 1056 } 1057 1058 FarDialogItem* AddEditField(wchar* Value, int MaxSize, int Width, in wchar* HistoryID = null, bool UseLastHistory = false) 1059 { 1060 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_EDIT, Value); 1061 SetNextY(Item); 1062 Item.X2 = Item.X1 + Width - 1; 1063 if (HistoryID) 1064 { 1065 Item.History = HistoryID; 1066 Item.Flags |= DIF_HISTORY; 1067 if (UseLastHistory) 1068 Item.Flags |= DIF_USELASTHISTORY; 1069 } 1070 1071 SetLastItemBinding(new PluginEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount-1, Value, MaxSize)); 1072 return Item; 1073 } 1074 1075 FarDialogItem* AddPasswordField(wchar* Value, int MaxSize, int Width) 1076 { 1077 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_PSWEDIT, Value); 1078 SetNextY(Item); 1079 Item.X2 = Item.X1 + Width - 1; 1080 1081 SetLastItemBinding(new PluginEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount-1, Value, MaxSize)); 1082 return Item; 1083 } 1084 1085 FarDialogItem* AddFixEditField(wchar* Value, int MaxSize, int Width, in wchar* Mask = null) 1086 { 1087 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_FIXEDIT, Value); 1088 SetNextY(Item); 1089 Item.X2 = Item.X1 + Width - 1; 1090 if (Mask) 1091 { 1092 Item.Mask = Mask; 1093 Item.Flags |= DIF_MASKEDIT; 1094 } 1095 1096 SetLastItemBinding(new PluginEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount-1, Value, MaxSize)); 1097 return Item; 1098 } 1099 1100 FarDialogItem* AddComboBox(int* SelectedItem, wchar* Text, int Width, in wchar*[] ItemsText, size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 1101 { 1102 return AddListControl(FARDIALOGITEMTYPES.DI_COMBOBOX, SelectedItem, Text, Width, 0, ItemsText, ItemCount, ItemFlags); 1103 } 1104 FarDialogItem* AddComboBox(int* SelectedItem, wchar* Text, int Width, in wchar*[] ItemsText, FARDIALOGITEMFLAGS ItemFlags) 1105 { 1106 return AddComboBox(SelectedItem, Text, Width, ItemsText, ItemsText.length, ItemFlags); 1107 } 1108 1109 FarDialogItem* AddComboBox(int* SelectedItem, wchar* Text, int Width, const int[] MessageIDs, size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 1110 { 1111 return AddListControl(FARDIALOGITEMTYPES.DI_COMBOBOX, SelectedItem, Text, Width, 0, MessageIDs, ItemCount, ItemFlags); 1112 } 1113 FarDialogItem* AddComboBox(int* SelectedItem, wchar* Text, int Width, const int[] MessageIDs, FARDIALOGITEMFLAGS ItemFlags) 1114 { 1115 return AddComboBox(SelectedItem, Text, Width, MessageIDs, MessageIDs.length, ItemFlags); 1116 } 1117 1118 FarDialogItem* AddListBox(int* SelectedItem, int Width, int Height, in wchar*[] ItemsText, size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 1119 { 1120 return AddListControl(FARDIALOGITEMTYPES.DI_LISTBOX, SelectedItem, null, Width, Height, ItemsText, ItemCount, ItemFlags); 1121 } 1122 FarDialogItem* AddListBox(int* SelectedItem, int Width, int Height, in wchar*[] ItemsText, FARDIALOGITEMFLAGS ItemFlags) 1123 { 1124 return AddListBox(SelectedItem, Width, Height, ItemsText, ItemsText.length, ItemFlags); 1125 } 1126 1127 FarDialogItem* AddListBox(int* SelectedItem, int Width, int Height, const int[] MessageIDs, size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 1128 { 1129 return AddListControl(FARDIALOGITEMTYPES.DI_LISTBOX, SelectedItem, null, Width, Height, MessageIDs, ItemCount, ItemFlags); 1130 } 1131 FarDialogItem* AddListBox(int* SelectedItem, int Width, int Height, const int[] MessageIDs, FARDIALOGITEMFLAGS ItemFlags) 1132 { 1133 return AddListBox(SelectedItem, Width, Height, MessageIDs, MessageIDs.length, ItemFlags); 1134 } 1135 }