1 /* 2 Dynamic construction of dialogs for FAR Manager 3.0 build 5577 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 { 237 //BUGBUG: two columns items are not supported 238 m_DialogItems[i].X2 = m_DialogItems[i].X1 + MaxWidth - 1; 239 } 240 241 if (m_DialogItems[i].Y2 > MaxHeight) 242 { 243 MaxHeight = m_DialogItems[i].Y2; 244 } 245 } 246 247 Title.X2 += m_Indent; 248 Title.Y2 = MaxHeight + 1; 249 m_Indent = 0; 250 } 251 252 intptr_t MaxTextWidth() 253 { 254 intptr_t MaxWidth = 0; 255 for (int i = 1; i < m_DialogItemsCount; i++) 256 { 257 if (m_DialogItems[i].X1 == SECOND_COLUMN) 258 continue; 259 DialogItemBinding!T Binding = FindBinding(m_DialogItems + i); 260 intptr_t Width = ItemWidth(m_DialogItems[i]); 261 if (Binding && Binding.BeforeLabelID != -1) 262 Width += ItemWidth(m_DialogItems[Binding.BeforeLabelID]) + 1; 263 if (Binding && Binding.AfterLabelID != -1) 264 Width += 1 + ItemWidth(m_DialogItems[Binding.AfterLabelID]); 265 266 if (MaxWidth < Width) 267 MaxWidth = Width; 268 } 269 intptr_t ColumnsWidth = 2 * m_ColumnMinWidth + 1; 270 if (MaxWidth < ColumnsWidth) 271 MaxWidth = ColumnsWidth; 272 if (MaxWidth < m_ButtonsWidth) 273 MaxWidth = m_ButtonsWidth; 274 return MaxWidth; 275 } 276 277 void UpdateSecondColumnPosition() 278 { 279 intptr_t SecondColumnX1 = 4 + (m_DialogItems[0].X2 - m_DialogItems[0].X1 + 1) / 2; 280 for (int i = 0; i < m_DialogItemsCount; i++) 281 { 282 if (m_DialogItems[i].X1 == SECOND_COLUMN) 283 { 284 DialogItemBinding!T Binding = FindBinding(m_DialogItems + i); 285 286 int before = Binding.BeforeLabelID; 287 long BeforeWidth = 0; 288 if (Binding && Binding.BeforeLabelID != -1) 289 BeforeWidth = m_DialogItems[before].X2 - m_DialogItems[before].X1 + 1 + 1; 290 291 intptr_t Width = m_DialogItems[i].X2 - m_DialogItems[i].X1 + 1; 292 m_DialogItems[i].X1 = SecondColumnX1 + BeforeWidth; 293 m_DialogItems[i].X2 = m_DialogItems[i].X1 + Width - 1; 294 295 if (Binding && Binding.AfterLabelID != -1) 296 { 297 int after = Binding.AfterLabelID; 298 long AfterWidth = m_DialogItems[after].X2 - m_DialogItems[after].X1 + 1; 299 m_DialogItems[after].X1 = m_DialogItems[i].X2 + 1 + 1; 300 m_DialogItems[after].X2 = m_DialogItems[after].X1 + AfterWidth - 1; 301 } 302 } 303 } 304 } 305 306 void InitDialogItem(T* NewDialogItem, in wchar* Text) 307 { 308 } 309 310 int TextWidth(ref const T Item) 311 { 312 return 0; 313 } 314 315 void SetLastItemBinding(DialogItemBinding!T Binding) 316 { 317 if (m_Bindings[m_DialogItemsCount - 1]) 318 m_Bindings[m_DialogItemsCount - 1].destroy(); 319 m_Bindings[m_DialogItemsCount - 1] = Binding; 320 } 321 322 int GetItemID(T* Item) const 323 { 324 int Index = cast(int)(Item - m_DialogItems); 325 if (Index >= 0 && Index < m_DialogItemsCount) 326 return Index; 327 return -1; 328 } 329 330 DialogItemBinding!T FindBinding(T* Item) 331 { 332 int Index = cast(int)(Item - m_DialogItems); 333 if (Index >= 0 && Index < m_DialogItemsCount) 334 return m_Bindings[Index]; 335 return null; 336 } 337 338 void SaveValues() 339 { 340 int RadioGroupIndex = 0; 341 for (int i = 0; i < m_DialogItemsCount; i++) 342 { 343 if (m_DialogItems[i].Flags & DIF_GROUP) 344 RadioGroupIndex = 0; 345 else 346 RadioGroupIndex++; 347 348 if (m_Bindings[i]) 349 m_Bindings[i].SaveValue(&m_DialogItems[i], RadioGroupIndex); 350 } 351 } 352 353 const(wchar)* GetLangString(int MessageID) 354 { 355 return null; 356 } 357 358 intptr_t DoShowDialog() 359 { 360 return -1; 361 } 362 363 DialogItemBinding!T CreateCheckBoxBinding(int* Value, int Mask) 364 { 365 return null; 366 } 367 368 DialogItemBinding!T CreateRadioButtonBinding(int* Value) 369 { 370 return null; 371 } 372 373 this() 374 { 375 m_NextY = 2; 376 m_SingleBoxIndex = -1; 377 m_FirstButtonID = -1; 378 m_CancelButtonID = -1; 379 m_ColumnStartIndex = -1; 380 m_ColumnBreakIndex = -1; 381 m_ColumnStartY = -1; 382 m_ColumnEndY = -1; 383 } 384 385 ~this() 386 { 387 for (int i = 0; i < m_DialogItemsCount; i++) 388 { 389 if (m_Bindings[i]) 390 m_Bindings[i].destroy(); 391 } 392 m_DialogItems.destroy(); 393 m_Bindings.destroy(); 394 } 395 396 public: 397 398 int GetLastID() const 399 { 400 return m_DialogItemsCount - 1; 401 } 402 403 // Добавляет статический текст, расположенный на отдельной строке в диалоге. 404 T* AddText(int LabelId) 405 { 406 return AddText(LabelId == -1 ? "" : GetLangString(LabelId)); 407 } 408 409 // Добавляет статический текст, расположенный на отдельной строке в диалоге. 410 T* AddText(in wchar* Label) 411 { 412 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_TEXT, Label); 413 SetNextY(Item); 414 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 415 return Item; 416 } 417 418 // Добавляет чекбокс. 419 T* AddCheckbox(in wchar* TextMessage, int* Value, int Mask = 0, bool ThreeState = false) 420 { 421 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_CHECKBOX, TextMessage); 422 if (ThreeState && !Mask) 423 Item.Flags |= DIF_3STATE; 424 SetNextY(Item); 425 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 426 if (!Mask) 427 Item.Selected = *Value; 428 else 429 Item.Selected = (*Value & Mask) != 0; 430 SetLastItemBinding(CreateCheckBoxBinding(Value, Mask)); 431 return Item; 432 } 433 434 // Добавляет чекбокс. 435 T* AddCheckbox(int TextMessageId, int* Value, int Mask = 0, bool ThreeState = false) 436 { 437 return AddCheckbox(GetLangString(TextMessageId), Value, Mask, ThreeState); 438 } 439 440 // Добавляет группу радиокнопок. 441 T* AddRadioButtons(int* Value, int OptionCount, const int[] MessageIDs, bool FocusOnSelected = false) 442 { 443 T* firstButton; 444 for (int i = 0; i < OptionCount; i++) 445 { 446 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_RADIOBUTTON, GetLangString(MessageIDs[i])); 447 SetNextY(Item); 448 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 449 if (!i) 450 { 451 Item.Flags |= DIF_GROUP; 452 firstButton = Item; 453 } 454 if (*Value == i) 455 { 456 Item.Selected = TRUE; 457 if (FocusOnSelected) 458 Item.Flags |= DIF_FOCUS; 459 } 460 SetLastItemBinding(CreateRadioButtonBinding(Value)); 461 } 462 return firstButton; 463 } 464 465 void AddRadioButtons(int* Value, const int[] MessageIDs, bool FocusOnSelected = false) 466 { 467 AddRadioButtons(Value, cast(int)MessageIDs.length, MessageIDs, FocusOnSelected); 468 } 469 470 // Добавляет поле типа FARDIALOGITEMTYPES.DI_FIXEDIT для редактирования указанного числового значения. 471 T* AddIntEditField(int* Value, int Width) 472 { 473 return null; 474 } 475 476 T* AddUIntEditField(uint* Value, int Width) 477 { 478 return null; 479 } 480 481 // Добавляет указанную текстовую строку слева от элемента RelativeTo. 482 T* AddTextBefore(T* RelativeTo, in wchar* Label) 483 { 484 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_TEXT, Label); 485 Item.Y1 = Item.Y2 = RelativeTo.Y1; 486 Item.X1 = 5 + m_Indent; 487 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 488 489 intptr_t RelativeToWidth = RelativeTo.X2 - RelativeTo.X1 + 1; 490 RelativeTo.X1 = Item.X2 + 1 + 1; 491 RelativeTo.X2 = RelativeTo.X1 + RelativeToWidth - 1; 492 493 DialogItemBinding!T Binding = FindBinding(RelativeTo); 494 if (Binding) 495 Binding.BeforeLabelID = GetItemID(Item); 496 497 return Item; 498 } 499 500 // Добавляет указанную текстовую строку слева от элемента RelativeTo. 501 T* AddTextBefore(T* RelativeTo, int LabelId) 502 { 503 return AddTextBefore(RelativeTo, GetLangString(LabelId)); 504 } 505 506 // Добавляет указанную текстовую строку справа от элемента RelativeTo. 507 T* AddTextAfter(T* RelativeTo, in wchar* Label) 508 { 509 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_TEXT, Label); 510 Item.Y1 = Item.Y2 = RelativeTo.Y1; 511 Item.X1 = RelativeTo.X2 + 1 + 1; 512 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 513 514 DialogItemBinding!T Binding = FindBinding(RelativeTo); 515 if (Binding) 516 Binding.AfterLabelID = GetItemID(Item); 517 518 return Item; 519 } 520 521 T* AddTextAfter(T* RelativeTo, int LabelId) 522 { 523 return AddTextAfter(RelativeTo, GetLangString(LabelId)); 524 } 525 526 // Добавляет кнопку справа от элемента RelativeTo. 527 T* AddButtonAfter(T* RelativeTo, in wchar* Label) 528 { 529 T* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_BUTTON, Label); 530 Item.Y1 = Item.Y2 = RelativeTo.Y1; 531 Item.X1 = RelativeTo.X2 + 1 + 1; 532 Item.X2 = Item.X1 + ItemWidth(*Item) - 1; 533 534 DialogItemBinding!T Binding = FindBinding(RelativeTo); 535 if (Binding) 536 Binding.AfterLabelID = GetItemID(Item); 537 538 return Item; 539 } 540 541 // Добавляет кнопку справа от элемента RelativeTo. 542 T* AddButtonAfter(T* RelativeTo, int LabelId) 543 { 544 return AddButtonAfter(RelativeTo, GetLangString(LabelId)); 545 } 546 547 // Начинает располагать поля диалога в две колонки. 548 void StartColumns() 549 { 550 m_ColumnStartIndex = m_DialogItemsCount; 551 m_ColumnStartY = m_NextY; 552 } 553 554 // Завершает колонку полей в диалоге и переходит к следующей колонке. 555 void ColumnBreak() 556 { 557 m_ColumnBreakIndex = m_DialogItemsCount; 558 m_ColumnEndY = m_NextY; 559 m_NextY = m_ColumnStartY; 560 } 561 562 // Завершает расположение полей диалога в две колонки. 563 void EndColumns() 564 { 565 for (int i = m_ColumnStartIndex; i < m_DialogItemsCount; i++) 566 { 567 intptr_t Width = ItemWidth(m_DialogItems[i]); 568 DialogItemBinding!T Binding = FindBinding(m_DialogItems + i); 569 long BindingAdd = 0; 570 if (Binding) 571 { 572 if (Binding.BeforeLabelID != -1) 573 BindingAdd += ItemWidth(m_DialogItems[Binding.BeforeLabelID]) + 1; 574 if (Binding.AfterLabelID != -1) 575 BindingAdd += 1 + ItemWidth(m_DialogItems[Binding.AfterLabelID]); 576 } 577 578 if (Width + BindingAdd > m_ColumnMinWidth) 579 m_ColumnMinWidth = Width + BindingAdd; 580 581 if (i >= m_ColumnBreakIndex) 582 { 583 m_DialogItems[i].X1 = SECOND_COLUMN; 584 m_DialogItems[i].X2 = SECOND_COLUMN + Width - 1; 585 } 586 } 587 588 m_ColumnStartIndex = -1; 589 m_ColumnBreakIndex = -1; 590 591 if (m_NextY < m_ColumnEndY) 592 m_NextY = m_ColumnEndY; 593 } 594 595 // Начинает располагать поля диалога внутри single box 596 void StartSingleBox(int MessageId = -1, bool LeftAlign = false) 597 { 598 T* SingleBox = AddDialogItem(FARDIALOGITEMTYPES.DI_SINGLEBOX, MessageId == -1 ? "" : GetLangString(MessageId)); 599 SingleBox.Flags = LeftAlign ? DIF_LEFTTEXT : DIF_NONE; 600 SingleBox.X1 = 5; 601 SingleBox.Y1 = m_NextY++; 602 m_Indent = 2; 603 m_SingleBoxIndex = m_DialogItemsCount - 1; 604 } 605 606 // Завершает расположение полей диалога внутри single box 607 void EndSingleBox() 608 { 609 if (m_SingleBoxIndex != -1) 610 { 611 m_DialogItems[m_SingleBoxIndex].Y2 = m_NextY++; 612 m_Indent = 0; 613 m_SingleBoxIndex = -1; 614 } 615 } 616 617 // Добавляет пустую строку. 618 void AddEmptyLine() 619 { 620 m_NextY++; 621 } 622 623 // Добавляет сепаратор. 624 void AddSeparator(int MessageId = -1) 625 { 626 return AddSeparator(MessageId == -1 ? "" : GetLangString(MessageId)); 627 } 628 629 void AddSeparator(in wchar* Text) 630 { 631 T* Separator = AddDialogItem(FARDIALOGITEMTYPES.DI_TEXT, Text); 632 Separator.Flags = DIF_SEPARATOR; 633 Separator.X1 = -1; 634 Separator.Y1 = Separator.Y2 = m_NextY++; 635 } 636 637 // Добавляет сепаратор, кнопки OK и Cancel. 638 void AddOKCancel(int OKMessageId, int CancelMessageId, int ExtraMessageId = -1, bool Separator = true) 639 { 640 if (Separator) 641 AddSeparator(); 642 643 const int[3] MsgIDs = [OKMessageId, CancelMessageId, ExtraMessageId]; 644 const int NumButtons = (ExtraMessageId != -1) ? 3 : (CancelMessageId != -1 ? 2 : 1); 645 646 AddButtons(NumButtons, MsgIDs, 0, 1); 647 } 648 649 // Добавляет линейку кнопок. 650 void AddButtons(int ButtonCount, const int[] MessageIDs, int defaultButtonIndex = 0, int cancelButtonIndex = -1) 651 { 652 int LineY = m_NextY++; 653 T* PrevButton = null; 654 655 for (int i = 0; i < ButtonCount; i++) 656 { 657 T* NewButton = AddDialogItem(FARDIALOGITEMTYPES.DI_BUTTON, GetLangString(MessageIDs[i])); 658 NewButton.Flags = DIF_CENTERGROUP; 659 NewButton.Y1 = NewButton.Y2 = LineY; 660 if (PrevButton) 661 { 662 NewButton.X1 = PrevButton.X2 + 1; 663 } 664 else 665 { 666 NewButton.X1 = 2 + m_Indent; 667 m_FirstButtonID = m_DialogItemsCount - 1; 668 } 669 NewButton.X2 = NewButton.X1 + ItemWidth(*NewButton) - 1 + 1; 670 671 if (defaultButtonIndex == i) 672 { 673 NewButton.Flags |= DIF_DEFAULTBUTTON; 674 } 675 if (cancelButtonIndex == i) 676 m_CancelButtonID = m_DialogItemsCount - 1; 677 678 PrevButton = NewButton; 679 } 680 auto Width = PrevButton.X2 - 1 - m_DialogItems[m_FirstButtonID].X1 + 1; 681 if (m_ButtonsWidth < Width) 682 m_ButtonsWidth = Width; 683 } 684 685 void AddButtons(const int[] MessageIDs, int defaultButtonIndex = 0, int cancelButtonIndex = -1) 686 { 687 AddButtons(cast(int)MessageIDs.length, MessageIDs, defaultButtonIndex, cancelButtonIndex); 688 } 689 690 intptr_t ShowDialogEx() 691 { 692 UpdateBorderSize(); 693 UpdateSecondColumnPosition(); 694 intptr_t Result = DoShowDialog(); 695 if (Result >= 0 && Result != m_CancelButtonID) 696 { 697 SaveValues(); 698 } 699 700 if (m_FirstButtonID >= 0 && Result >= m_FirstButtonID) 701 { 702 Result -= m_FirstButtonID; 703 } 704 return Result; 705 } 706 707 bool ShowDialog() 708 { 709 intptr_t Result = ShowDialogEx(); 710 return Result >= 0 && (m_CancelButtonID < 0 || Result + m_FirstButtonID != m_CancelButtonID); 711 } 712 713 } 714 715 class DialogAPIBinding : DialogItemBinding!FarDialogItem 716 { 717 protected: 718 const(PluginStartupInfo)* Info; 719 HANDLE* DialogHandle; 720 int ID; 721 722 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID) 723 { 724 Info = aInfo; 725 DialogHandle = aHandle; 726 ID = aID; 727 } 728 } 729 730 class PluginCheckBoxBinding : DialogAPIBinding 731 { 732 int* Value; 733 int Mask; 734 735 public: 736 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue, int aMask) 737 { 738 super(aInfo, aHandle, aID); 739 Value = aValue; 740 Mask = aMask; 741 } 742 743 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 744 { 745 int Selected = cast(int)(Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCHECK, ID, null)); 746 if (!Mask) 747 { 748 *Value = Selected; 749 } 750 else 751 { 752 if (Selected) 753 *Value |= Mask; 754 else 755 *Value &= ~Mask; 756 } 757 } 758 } 759 760 class PluginRadioButtonBinding : DialogAPIBinding 761 { 762 private: 763 int* Value; 764 765 public: 766 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue) 767 { 768 super(aInfo, aHandle, aID); 769 Value = aValue; 770 } 771 772 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 773 { 774 if (Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCHECK, ID, null)) 775 *Value = RadioGroupIndex; 776 } 777 } 778 779 class PluginEditFieldBinding : DialogAPIBinding 780 { 781 private: 782 wchar* Value; 783 int MaxSize; 784 785 public: 786 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, wchar* aValue, int aMaxSize) 787 { 788 super(aInfo, aHandle, aID); 789 Value = aValue; 790 MaxSize = aMaxSize; 791 } 792 793 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 794 { 795 const(wchar)* DataPtr = cast(const(wchar)*)Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCONSTTEXTPTR, ID, null); 796 lstrcpynW(Value, DataPtr, MaxSize); 797 } 798 } 799 800 class PluginIntEditFieldBinding : DialogAPIBinding 801 { 802 private: 803 int* Value; 804 wchar[32] Buffer; 805 wchar[32] Mask; 806 807 public: 808 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue, int Width) 809 { 810 super(aInfo, aHandle, aID); 811 Value = aValue; 812 aInfo.FSF.sprintf(Buffer.ptr, "%d"w.ptr, *aValue); 813 int MaskWidth = Width < 31 ? Width : 31; 814 for (int i = 1; i < MaskWidth; i++) 815 Mask[i] = '9'; 816 Mask[0] = '#'; 817 Mask[MaskWidth] = '\0'; 818 } 819 820 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 821 { 822 wchar* DataPtr = cast(wchar*)Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCONSTTEXTPTR, ID, null); 823 *Value = Info.FSF.atoi(DataPtr); 824 } 825 826 wchar* GetBuffer() 827 { 828 return Buffer.ptr; 829 } 830 831 const(wchar)* GetMask() const 832 { 833 return Mask.ptr; 834 } 835 } 836 837 class PluginUIntEditFieldBinding : DialogAPIBinding 838 { 839 private: 840 uint* Value; 841 wchar[32] Buffer; 842 wchar[32] Mask; 843 844 public: 845 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, uint* aValue, int Width) 846 { 847 super(aInfo, aHandle, aID); 848 Value = aValue; 849 aInfo.FSF.sprintf(Buffer.ptr, "%u"w.ptr, *aValue); 850 int MaskWidth = Width < 31 ? Width : 31; 851 for (int i = 1; i < MaskWidth; i++) 852 Mask[i] = '9'; 853 Mask[0] = '#'; 854 Mask[MaskWidth] = '\0'; 855 } 856 857 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 858 { 859 wchar* DataPtr = cast(wchar*)Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETCONSTTEXTPTR, ID, null); 860 *Value = cast(uint)Info.FSF.atoi64(DataPtr); 861 } 862 863 wchar* GetBuffer() 864 { 865 return Buffer.ptr; 866 } 867 868 const(wchar)* GetMask() const 869 { 870 return Mask.ptr; 871 } 872 } 873 874 class PluginListControlBinding : DialogAPIBinding 875 { 876 private: 877 int* SelectedIndex; 878 wchar* TextBuf; 879 FarList* List; 880 881 public: 882 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue, wchar* aText, FarList* aList) 883 { 884 super(aInfo, aHandle, aID); 885 SelectedIndex = aValue; 886 TextBuf = aText; 887 List = aList; 888 } 889 890 this(in PluginStartupInfo* aInfo, HANDLE* aHandle, int aID, int* aValue, FarList* aList) 891 { 892 super(aInfo, aHandle, aID); 893 SelectedIndex = aValue; 894 List = aList; 895 } 896 897 ~this() 898 { 899 if (List) 900 { 901 List.Items.destroy(); 902 } 903 List.destroy(); 904 } 905 906 override void SaveValue(FarDialogItem* Item, int RadioGroupIndex) 907 { 908 if (SelectedIndex) 909 { 910 *SelectedIndex = cast(int)(Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_LISTGETCURPOS, ID, null)); 911 } 912 if (TextBuf) 913 { 914 FarDialogItemData fdid = {FarDialogItemData.sizeof, 0, TextBuf}; 915 Info.SendDlgMessage(*DialogHandle, FARMESSAGE.DM_GETTEXT, ID, &fdid); 916 } 917 } 918 } 919 920 /* 921 Версия класса для динамического построения диалогов, используемая в плагинах к Far. 922 */ 923 class PluginDialogBuilder : DialogBuilderBase!FarDialogItem 924 { 925 protected: 926 const(PluginStartupInfo)* Info; 927 HANDLE DialogHandle; 928 const(wchar)* HelpTopic; 929 GUID PluginId; 930 GUID Id; 931 FARWINDOWPROC DlgProc; 932 void* UserParam; 933 FARDIALOGFLAGS Flags; 934 935 override void InitDialogItem(FarDialogItem* Item, in wchar* Text) 936 { 937 memset(Item, 0, FarDialogItem.sizeof); 938 Item.Data = Text; 939 } 940 941 override int TextWidth(ref const FarDialogItem Item) 942 { 943 return lstrlenW(Item.Data); 944 } 945 946 override const(wchar)* GetLangString(int MessageID) 947 { 948 return Info.GetMsg(&PluginId, MessageID); 949 } 950 951 override intptr_t DoShowDialog() 952 { 953 intptr_t Width = m_DialogItems[0].X2 + 4; 954 intptr_t Height = m_DialogItems[0].Y2 + 2; 955 DialogHandle = Info.DialogInit(&PluginId, &Id, -1, -1, Width, Height, HelpTopic, m_DialogItems, 956 m_DialogItemsCount, 0, Flags, DlgProc, UserParam); 957 return Info.DialogRun(DialogHandle); 958 } 959 960 override DialogItemBinding!FarDialogItem CreateCheckBoxBinding(int* Value, int Mask) 961 { 962 return new PluginCheckBoxBinding(Info, &DialogHandle, m_DialogItemsCount - 1, Value, Mask); 963 } 964 965 override DialogItemBinding!FarDialogItem CreateRadioButtonBinding(BOOL* Value) 966 { 967 return new PluginRadioButtonBinding(Info, &DialogHandle, m_DialogItemsCount - 1, Value); 968 } 969 970 FarDialogItem* AddListControl(FARDIALOGITEMTYPES Type, int* SelectedItem, wchar* Text, int Width, 971 int Height, in wchar*[] ItemsText, size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 972 { 973 FarDialogItem* Item = AddDialogItem(Type, Text ? Text : ""); 974 SetNextY(Item); 975 Item.X2 = Item.X1 + Width; 976 Item.Y2 = Item.Y2 + Height; 977 Item.Flags |= ItemFlags; 978 979 m_NextY += Height; 980 981 FarListItem* ListItems; 982 if (ItemsText) 983 { 984 ListItems = new FarListItem[ItemCount].ptr; 985 for (size_t i = 0; i < ItemCount; i++) 986 { 987 ListItems[i].Text = ItemsText[i]; 988 ListItems[i].Flags = SelectedItem && (*SelectedItem == cast(int)i) ? LIF_SELECTED : 0; 989 } 990 } 991 FarList* List = new FarList; 992 List.StructSize = FarList.sizeof; 993 List.Items = ListItems; 994 List.ItemsNumber = ListItems ? ItemCount : 0; 995 Item.ListItems = List; 996 997 SetLastItemBinding(new PluginListControlBinding(Info, &DialogHandle, m_DialogItemsCount - 1, SelectedItem, Text, List)); 998 return Item; 999 } 1000 1001 FarDialogItem* AddListControl(FARDIALOGITEMTYPES Type, int* SelectedItem, wchar* Text, int Width, 1002 int Height, const int[] MessageIDs, size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 1003 { 1004 const(wchar)*[] ItemsText; 1005 if (MessageIDs) 1006 { 1007 ItemsText = new const(wchar)*[ItemCount]; 1008 for (size_t i = 0; i < ItemCount; i++) 1009 { 1010 ItemsText[i] = GetLangString(MessageIDs[i]); 1011 } 1012 } 1013 1014 FarDialogItem* result = AddListControl(Type, SelectedItem, Text, Width, Height, ItemsText, ItemCount, ItemFlags); 1015 1016 ItemsText.destroy(); 1017 1018 return result; 1019 } 1020 1021 public: 1022 this(in PluginStartupInfo* aInfo, ref const GUID aPluginId, ref const GUID aId, int TitleMessageID, 1023 in wchar* aHelpTopic, FARWINDOWPROC aDlgProc = null, void* aUserParam = null, FARDIALOGFLAGS aFlags = FDLG_NONE) 1024 { 1025 Info = aInfo; 1026 HelpTopic = aHelpTopic; 1027 PluginId = aPluginId; 1028 Id = aId; 1029 DlgProc = aDlgProc; 1030 UserParam = aUserParam; 1031 Flags = aFlags; 1032 AddBorder(PluginDialogBuilder.GetLangString(TitleMessageID)); 1033 } 1034 1035 this(in PluginStartupInfo* aInfo, ref const GUID aPluginId, ref const GUID aId, in wchar* TitleMessage, 1036 in wchar* aHelpTopic, FARWINDOWPROC aDlgProc = null, void* aUserParam = null, FARDIALOGFLAGS aFlags = FDLG_NONE) 1037 { 1038 Info = aInfo; 1039 HelpTopic = aHelpTopic; 1040 PluginId = aPluginId; 1041 Id = aId; 1042 DlgProc = aDlgProc; 1043 UserParam = aUserParam; 1044 Flags = aFlags; 1045 AddBorder(TitleMessage); 1046 } 1047 1048 ~this() 1049 { 1050 Info.DialogFree(DialogHandle); 1051 } 1052 1053 override FarDialogItem* AddIntEditField(int* Value, int Width) 1054 { 1055 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_FIXEDIT, ""); 1056 Item.Flags |= DIF_MASKEDIT; 1057 auto Binding = new PluginIntEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount - 1, Value, Width); 1058 Item.Data = Binding.GetBuffer(); 1059 Item.Mask = Binding.GetMask(); 1060 SetNextY(Item); 1061 Item.X2 = Item.X1 + Width - 1; 1062 SetLastItemBinding(Binding); 1063 return Item; 1064 } 1065 1066 override FarDialogItem* AddUIntEditField(uint* Value, int Width) 1067 { 1068 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_FIXEDIT, ""); 1069 Item.Flags |= DIF_MASKEDIT; 1070 auto Binding = new PluginUIntEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount - 1, Value, Width); 1071 Item.Data = Binding.GetBuffer(); 1072 Item.Mask = Binding.GetMask(); 1073 SetNextY(Item); 1074 Item.X2 = Item.X1 + Width - 1; 1075 SetLastItemBinding(Binding); 1076 return Item; 1077 } 1078 1079 FarDialogItem* AddEditField(wchar* Value, int MaxSize, int Width, in wchar* HistoryID = null, bool UseLastHistory = false) 1080 { 1081 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_EDIT, Value); 1082 SetNextY(Item); 1083 Item.X2 = Item.X1 + Width - 1; 1084 if (HistoryID) 1085 { 1086 Item.History = HistoryID; 1087 Item.Flags |= DIF_HISTORY; 1088 if (UseLastHistory) 1089 Item.Flags |= DIF_USELASTHISTORY; 1090 } 1091 1092 SetLastItemBinding(new PluginEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount - 1, Value, MaxSize)); 1093 return Item; 1094 } 1095 1096 FarDialogItem* AddPasswordField(wchar* Value, int MaxSize, int Width) 1097 { 1098 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_PSWEDIT, Value); 1099 SetNextY(Item); 1100 Item.X2 = Item.X1 + Width - 1; 1101 1102 SetLastItemBinding(new PluginEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount - 1, Value, MaxSize)); 1103 return Item; 1104 } 1105 1106 FarDialogItem* AddFixEditField(wchar* Value, int MaxSize, int Width, in wchar* Mask = null) 1107 { 1108 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_FIXEDIT, Value); 1109 SetNextY(Item); 1110 Item.X2 = Item.X1 + Width - 1; 1111 if (Mask) 1112 { 1113 Item.Mask = Mask; 1114 Item.Flags |= DIF_MASKEDIT; 1115 } 1116 1117 SetLastItemBinding(new PluginEditFieldBinding(Info, &DialogHandle, m_DialogItemsCount - 1, Value, MaxSize)); 1118 return Item; 1119 } 1120 1121 FarDialogItem* AddReadonlyEditField(const(wchar)* Value, int Width) 1122 { 1123 FarDialogItem* Item = AddDialogItem(FARDIALOGITEMTYPES.DI_EDIT, Value); 1124 SetNextY(Item); 1125 Item.X2 = Item.X1 + (Width > 0 ? Width : TextWidth(*Item)) - 1; 1126 Item.Flags |= DIF_READONLY; 1127 return Item; 1128 } 1129 1130 FarDialogItem* AddComboBox(int* SelectedItem, wchar* Text, int Width, in wchar*[] ItemsText, 1131 size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 1132 { 1133 return AddListControl(FARDIALOGITEMTYPES.DI_COMBOBOX, SelectedItem, Text, Width, 0, ItemsText, ItemCount, ItemFlags); 1134 } 1135 1136 FarDialogItem* AddComboBox(int* SelectedItem, wchar* Text, int Width, in wchar*[] ItemsText, FARDIALOGITEMFLAGS ItemFlags) 1137 { 1138 return AddComboBox(SelectedItem, Text, Width, ItemsText, ItemsText.length, ItemFlags); 1139 } 1140 1141 FarDialogItem* AddComboBox(int* SelectedItem, wchar* Text, int Width, const int[] MessageIDs, 1142 size_t ItemCount, FARDIALOGITEMFLAGS ItemFlags) 1143 { 1144 return AddListControl(FARDIALOGITEMTYPES.DI_COMBOBOX, SelectedItem, Text, Width, 0, MessageIDs, ItemCount, ItemFlags); 1145 } 1146 1147 FarDialogItem* AddComboBox(int* SelectedItem, wchar* Text, int Width, const int[] MessageIDs, FARDIALOGITEMFLAGS ItemFlags) 1148 { 1149 return AddComboBox(SelectedItem, Text, Width, MessageIDs, MessageIDs.length, ItemFlags); 1150 } 1151 1152 FarDialogItem* AddListBox(int* SelectedItem, int Width, int Height, in wchar*[] ItemsText, size_t ItemCount, 1153 FARDIALOGITEMFLAGS ItemFlags) 1154 { 1155 return AddListControl(FARDIALOGITEMTYPES.DI_LISTBOX, SelectedItem, null, Width, Height, ItemsText, ItemCount, ItemFlags); 1156 } 1157 1158 FarDialogItem* AddListBox(int* SelectedItem, int Width, int Height, in wchar*[] ItemsText, FARDIALOGITEMFLAGS ItemFlags) 1159 { 1160 return AddListBox(SelectedItem, Width, Height, ItemsText, ItemsText.length, ItemFlags); 1161 } 1162 1163 FarDialogItem* AddListBox(int* SelectedItem, int Width, int Height, const int[] MessageIDs, size_t ItemCount, 1164 FARDIALOGITEMFLAGS ItemFlags) 1165 { 1166 return AddListControl(FARDIALOGITEMTYPES.DI_LISTBOX, SelectedItem, null, Width, Height, MessageIDs, 1167 ItemCount, ItemFlags); 1168 } 1169 1170 FarDialogItem* AddListBox(int* SelectedItem, int Width, int Height, const int[] MessageIDs, FARDIALOGITEMFLAGS ItemFlags) 1171 { 1172 return AddListBox(SelectedItem, Width, Height, MessageIDs, MessageIDs.length, ItemFlags); 1173 } 1174 }