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