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