An alternative MessageRequester [Windows]

Share your advanced PureBasic knowledge/code with the community.
User avatar
Zapman
Enthusiast
Enthusiast
Posts: 205
Joined: Tue Jan 07, 2020 7:27 pm

An alternative MessageRequester [Windows]

Post by Zapman »

This library attempts to offer a more complete and flexible requester than PureBasic's native 'MessageRequester' function.
Image
[Edit 03 feb. 2025] The code has been revised and optimized for multilanguage support.
[Edit 22 march 2025] The code has been improved for the dialog window positioning.

Due to the size limit on forum posts, the version below has no comments. A more complete (and probably more recent) version is available at the following address: https://www.editions-humanis.com/downlo ... /Alert.zip

All Zapman libraries are available here:https://www.editions-humanis.com/downlo ... ads_EN.htm

Code: Select all

;
;- *******************************************************************
;
;                           'Alert' library
;
;          An alternative to PureBasic's 'MessageRequester'.
;          For Windows only - Zapman, March 2025 - 4 - Forum
;
;        This file should be saved under the name "Alert.pbi".
;
; Due to the size limit on forum posts, this version has no comments.
; A more recent and complete version is available at the following address:
; [url]https://www.editions-humanis.com/downloads/PureBasic/Alert.zip[/url]
;
Global MyLanguage$
If MyLanguage$ = ""
  MyLanguage$ = "EN" 
EndIf
CompilerIf Not Defined(LanguageListStructure, #PB_Structure)
  Structure LanguageListStructure
    Language$
    LanguageEntry$
    LanguageTranslation$
  EndStructure
  Global NewList LanguageList.LanguageListStructure()
CompilerEndIf
CompilerIf Not Defined(GetTextFromCatalog, #PB_Procedure)
  Procedure.s GetTextFromCatalog(SName$)
    ForEach LanguageList()
      If LanguageList()\Language$ = MyLanguage$ And LCase(LanguageList()\LanguageEntry$) = LCase(SName$)
        ProcedureReturn LanguageList()\LanguageTranslation$
      EndIf
    Next
    ProcedureReturn SName$
  EndProcedure
CompilerEndIf
CompilerIf Not Defined(FillLanguageList, #PB_Procedure)
  Procedure FillLanguageList(Language$, LanguageEntry$, LanguageTranslation$)
    Protected PosInList = 0, KeyWord$, Found
    Repeat
      PosInList + 1
      KeyWord$ = StringField(LanguageEntry$, PosInList, ",")
      Found = 0
      ForEach LanguageList()
        If LanguageList()\Language$ = Language$ And LCase(LanguageList()\LanguageEntry$) = LCase(KeyWord$)
          Found = 1
          Break
        EndIf
      Next
      If Found = 0
        AddElement(LanguageList())
        LanguageList()\Language$ = Language$
        LanguageList()\LanguageEntry$ = KeyWord$
        LanguageList()\LanguageTranslation$ = StringField(LanguageTranslation$, PosInList, ",")
      EndIf
    Until KeyWord$ = ""
  EndProcedure
CompilerEndIf
Define LanguageEntry$ = "SWTitle,SWSearch,SWReplaceTitle,SWReplace,SWReplaceAll,SWCaseSensitive,"
LanguageEntry$ + "SWWholeWord,SWInAllDocument,SWUnableToFind,SWReplacementMade,SWSearchFromStart,"
LanguageEntry$ + "SWSearchFromEnd,Attention,Quit,Yes,No,OK,Cancel,Save,SaveAs,Copy,CopyAll,TextFile,"
Define LanguageTranslation$ = "Search,Search:,Replace by:,Replace,Replace all,Case sensitive,"
LanguageTranslation$ + "Whole word,In the entire document,Unable to find,replacement(s) made.,Search from the beginning?,"
LanguageTranslation$ + "Search from the end?,Warning,Quit,Yes,No,OK,Cancel,Save,Save as...,Copy,Copy all,Text Files|*.txt"
FillLanguageList("EN", LanguageEntry$, LanguageTranslation$)
Define LanguageTranslation$ = "Buscar,Buscar:,Reemplazar por:,Reemplazar,Reemplazar todo,Sensible a mayúsculas,"
LanguageTranslation$ + "Palabra completa,En todo el documento,No se pudo encontrar,reemplazo(s) realizado(s).,¿Buscar desde el principio?,"
LanguageTranslation$ + "¿Buscar desde el final?,Advertencia,Salir,Sí,No,OK,Cancelar,Guardar,Guardar como...,Copiar,Copiar todo,Archivos de texto|*.txt"
FillLanguageList("ES", LanguageEntry$, LanguageTranslation$)
Define LanguageTranslation$ = "搜索,搜索:,替换为:,替换,全部替换,区分大小写,"
LanguageTranslation$ + "整词匹配,全文搜索,无法找到,替换成功。,从开头搜索?,"
LanguageTranslation$ + "从结尾搜索?,警告,退出,是,否,确定,取消,保存,另存为...,全部复制,文本文件|*.txt"
FillLanguageList("ZH", LanguageEntry$, LanguageTranslation$)
LanguageTranslation$ = "Rechercher,Chercher :,Remplacer par :,Remplacer,Tout remplacer,Respecter la casse,"
LanguageTranslation$ + "Mot entier,Dans tout le document,Impossible de trouver,remplacement(s) effectué(s).,Rechercher depuis le début ?,"
LanguageTranslation$ + "Rechercher depuis la fin ?,Attention,Quitter,Oui,Non,OK,Annuler,Enregistrer,Enregistrer sous...,Copier,Tout copier,Fichiers texte|*.txt"
FillLanguageList("FR", LanguageEntry$, LanguageTranslation$)
LanguageTranslation$ = "Suchen,Suchen :,Ersetzen durch :,Ersetzen,Alle ersetzen,Groß-/Kleinschreibung beachten," 
LanguageTranslation$ + "Ganzes Wort,Im gesamten Dokument,Keine Treffer,Ersetzung(en) vorgenommen.,Von Anfang an suchen?," 
LanguageTranslation$ + "Von Ende an suchen?,Achtung,Beenden,Ja,Nein,OK,Abbrechen,Speichern,Speichern unter...,Kopieren,Alles kopieren,Textdateien|*.txt" 
FillLanguageList("DE", LanguageEntry$, LanguageTranslation$) 
LanguageTranslation$ = "Найти,Искать :,Заменить на :,Заменить,Заменить всё,Учитывать регистр," 
LanguageTranslation$ + "Целое слово,Во всём документе,Ничего не найдено,замена(ы) выполнена(ы).,Искать с начала?," 
LanguageTranslation$ + "Искать с конца?,Внимание,Выйти,Да,Нет,OK,Отмена,Сохранить,Сохранить как...,Копировать,Копировать всё,Текстовые файлы|*.txt" 
FillLanguageList("RU", LanguageEntry$, LanguageTranslation$)
Define LanguageTranslation$ = "Cerca,Cerca:,Sostituisci con:,Sostituisci,Sostituisci tutto,Sensibile al maiuscolo/minuscolo,"
LanguageTranslation$ + "Parola intera,Nell'intero documento,Impossibile trovare,sostituzione(i) effettuata(e).,Cerca dall'inizio?,"
LanguageTranslation$ + "Cerca dalla fine?,Attenzione,Esci,Sì,No,OK,Annulla,Salva,Salva con nome...,Copia,Copia tutto,File di testo|*.txt"
FillLanguageList("IT", LanguageEntry$, LanguageTranslation$)
Global PBBAllGadgetsFont
If PBBAllGadgetsFont = 0
  PBBAllGadgetsFont = FontID(LoadFont(#PB_Any, "Segoe UI", 9))
EndIf
Enumeration CWO_Positioning
  #CWO_ActiveWindowPos = -1
  #CWO_AbsolutePos = -2
  #CWO_MonitorPos = -3
EndEnumeration
Enumeration CWO_PositionAnchor
  #CWO_Center = 0
  #CWO_TopLeft
  #CWO_TopRight
  #CWO_BottomLeft
  #CWO_BottomRight
EndEnumeration
CompilerIf Not Defined(ComputeWinOrigins, #PB_Procedure)
  Procedure ComputeWinOrigins(*OX.Integer, *OY.Integer, WWidth, WHeight, ParentWindow = #CWO_ActiveWindowPos, XShiftOrPos = 0, YShiftOrPos = 0, ParentAnchor = #CWO_Center, WindowAnchor = #CWO_Center)
    Protected DesktopLeft, DesktopRight, DesktopTop, DesktopBottom
    Protected ParentWindowID, MFWindow, hMonitor, mi.MONITORINFO
    If ParentWindow = #CWO_AbsolutePos
      *OX\i = XShiftOrPos
      *OY\i = YShiftOrPos
      ProcedureReturn 0
    EndIf
    If ParentWindow = #CWO_ActiveWindowPos And IsWindow(GetActiveWindow())
      ParentWindow = GetActiveWindow()
    EndIf
    MFWindow = ParentWindow
    If Not(IsWindow(MFWindow))
      MFWindow = GetActiveWindow()
    EndIf
    If IsWindow(MFWindow)
      MFWindow = WindowID(MFWindow)
    Else
      MFWindow = 0
    EndIf
    hMonitor = MonitorFromWindow_(MFWindow, #MONITOR_DEFAULTTONEAREST)
    mi\cbSize = SizeOf(MONITORINFO)
    GetMonitorInfo_(hMonitor, @mi)
    DesktopLeft = DesktopUnscaledX(mi\rcWork\left)
    DesktopRight = DesktopUnscaledX(mi\rcWork\Right)
    DesktopTop = DesktopUnscaledY(mi\rcWork\top)
    DesktopBottom = DesktopUnscaledY(mi\rcWork\Bottom)
    If IsWindow(ParentWindow)
      ParentWindowID = WindowID(ParentWindow)
      *OX\i = WindowX(ParentWindow)
      *OY\i = WindowY(ParentWindow)
      If ParentAnchor = #CWO_Center
        *OX\i + WindowWidth(ParentWindow) / 2
        *OY\i + WindowHeight(ParentWindow) / 2
      ElseIf ParentAnchor = #CWO_TopRight
        *OX\i + WindowWidth(ParentWindow)
      ElseIf ParentAnchor = #CWO_BottomLeft
        *OY\i + WindowHeight(ParentWindow)
      ElseIf ParentAnchor = #CWO_BottomRight
        *OX\i + WindowWidth(ParentWindow)
        *OY\i + WindowHeight(ParentWindow)
      EndIf
    Else
      *OX\i = DeskTopLeft
      *OY\i = DesktopTop
      If ParentAnchor = #CWO_Center
        *OX\i / 2 + DesktopRight / 2
        *OY\i / 2 + DesktopBottom / 2
      ElseIf ParentAnchor = #CWO_TopRight
        *OX\i = DesktopRight
      ElseIf ParentAnchor = #CWO_BottomLeft
        *OY\i = DesktopBottom
      ElseIf ParentAnchor = #CWO_BottomRight
        *OX\i = DesktopRight
        *OY\i = DesktopBottom
      EndIf
    EndIf
    WHeight + MenuHeight() + 4 
    If WindowAnchor = #CWO_Center
      *OX\i - WWidth / 2
      *OY\i - WHeight / 2
    ElseIf WindowAnchor = #CWO_TopRight
      *OX\i - WWidth
    ElseIf WindowAnchor = #CWO_BottomLeft
      *OY\i - WHeight
    ElseIf WindowAnchor = #CWO_BottomRight
      *OX\i - WWidth
      *OY\i - WHeight
    EndIf
    *OX\i + XShiftOrPos
    *OY\i + YShiftOrPos
    If *OX\i < DesktopLeft
      *OX\i = DesktopLeft + 10
    ElseIf *OX\i + WWidth > DesktopRight
      *OX\i = DesktopRight - WWidth - 10
    EndIf
    If *OY\i < DesktopTop
      *OY\i = DesktopTop + 10
    ElseIf *OY\i + WHeight > DesktopBottom
      *OY\i = DesktopBottom - WHeight - 10
    EndIf
    ProcedureReturn ParentWindowID
  EndProcedure
CompilerEndIf
Procedure ApplyFontToGadgetList(GadgetList$)
  Protected PosInGadgetList = 0, Gadget
  Repeat
    PosInGadgetList + 1
    Gadget = Val(StringField(GadgetList$, PosInGadgetList, ","))
    If IsGadget(Gadget)
      SetGadgetFont(Gadget, PBBAllGadgetsFont)
    EndIf
  Until StringField(GadgetList$, PosInGadgetList, ",") = ""
EndProcedure
Procedure ClearKeyboardBuffer(WindowID = 0)
  If IsWindow(WindowID)
    While WindowEvent() : Wend
  EndIf
  Protected msg.MSG
  While PeekMessage_(@msg, 0, #WM_KEYFIRST, #WM_KEYLAST, #PM_REMOVE)
  Wend
  Protected Dim keystate.b(256)
  Protected i
  GetKeyboardState_(@keystate())
  For i = 0 To 255
    If keystate(i) & $80
      While GetAsyncKeyState_(i) & $8000
      Wend
    EndIf
  Next i
EndProcedure
Procedure.l GetTextSurfPix(gadget, Text$, *Width.Double, *Height.Double)
  If IsGadget(gadget)
    Protected Font = GetGadgetFont(Gadget)
    Protected Image = CreateImage(#PB_Any, 200, 200)
    If Image And StartDrawing(ImageOutput(Image))
      DrawingFont(Font)
      *Width\d = DesktopUnscaledX(TextWidth(Text$))
      *Height\d = DesktopUnscaledY(TextHeight(Text$))
      StopDrawing()
      FreeImage(Image)
    EndIf
  EndIf
EndProcedure
Procedure.l GetTextWidthPix(gadget, Text$)
  Protected TWidth.d, THeight.d
  GetTextSurfPix(gadget, Text$, @TWidth.d, @THeight.d)
  ProcedureReturn TWidth
EndProcedure
Procedure.l RE_StreamStringOutCallback(*dwCookiePtr, pbBuff, cb, pcb)
  Protected result, StrPtr, ms
  result = 0
  If *dwCookiePtr 
    StrPtr.i = PeekI(*dwCookiePtr)
    If StrPtr = 0
      StrPtr = AllocateMemory(cb)
      CopyMemory(pbBuff, StrPtr, cb)
    Else
      ms = MemorySize(StrPtr)
      StrPtr = ReAllocateMemory(StrPtr, ms + cb)
      CopyMemory(pbBuff, StrPtr + ms, cb)
    EndIf
    PokeI(*dwCookiePtr, StrPtr)
  EndIf
  PokeL(pcb, cb)
  If cb = 0
    result = 1
  EndIf
  ProcedureReturn result
EndProcedure
Procedure.s RE_GetContent_RTF(Gadget, format = #SF_RTF, UseDirectID = 0)
  Protected edstr.EDITSTREAM, StrPtr, Ghdl
  Protected Str$ 
  #SF_USECODEPAGE = $20
  #CP_UTF8 = 65001
  If UseDirectID = 0
    Ghdl = GadgetID(Gadget)
  Else
    Ghdl = Gadget
  EndIf
  If format = 0 Or format & #SF_TEXT : format | (#CP_UTF8 << 16) | #SF_USECODEPAGE | #SF_TEXT : EndIf
  StrPtr = 0
  edstr\dwCookie.i = @StrPtr 
  edstr\pfnCallback = @RE_StreamStringOutCallback()
  edstr\dwError = 0
  SendMessage_(Ghdl, #EM_STREAMOUT, format, edstr)
  If edstr\dwError
    Str$ = ""
    If StrPtr : FreeMemory(StrPtr) : EndIf
  ElseIf StrPtr
    Str$ = PeekS(StrPtr, MemorySize(StrPtr), #PB_UTF8 | #PB_ByteLength)
    FreeMemory(StrPtr)
  EndIf
  ProcedureReturn Str$
EndProcedure
Procedure.s RE_GetGadgetSelectedText(Gadget, format = #SFF_SELECTION | #SF_TEXT)
  ProcedureReturn RE_GetContent_RTF(Gadget, format)
EndProcedure
Procedure RE_GetSelection(Gadget, *txtrange.CHARRANGE)
  ProcedureReturn SendMessage_(GadgetID(Gadget), #EM_EXGETSEL, 0, *txtrange)
EndProcedure
Procedure RE_SetSelection(Gadget, PosStart, PosEnd)
  Protected txtrange.CHARRANGE\cpMin = PosStart 
  txtrange\cpMax = PosEnd 
  ProcedureReturn SendMessage_(GadgetID(Gadget), #EM_EXSETSEL, 0, @txtrange)
EndProcedure
Procedure RE_ReplaceSelection(Gadget, ReplaceString$)
  ProcedureReturn SendMessage_(GadgetID(Gadget), #EM_REPLACESEL, 1, @ReplaceString$)
EndProcedure
Procedure.s RE_GetGadgetWholeText(Gadget, format = #SF_TEXT)
  ProcedureReturn RE_GetContent_RTF(Gadget, format)
EndProcedure
Procedure RE_SaveContent(Gadget, FileName$, format = #SF_RTF, DelPict = 0)
  Protected ct, p, pr, po, pf, hFile, Result
  Protected Content$ = RE_GetContent_RTF(Gadget, format)
  If Content$
    Content$ = ReplaceString(Content$, "\red255\green255\blue255", "\red0\green0\blue0")
    If DelPict
      ct = 0
      Repeat
        p = FindString(Content$, "{\pict")
        If p
          ct = 1
          pr = p + 5
          Repeat
            po = FindString(Content$, "{", pr + 1)
            pf = FindString(Content$, "}", pr + 1)
            If po And (po < pf Or pf = 0)
              pr = po
              ct + 1
            EndIf
            If pf And (pf < po Or po = 0)
              pr = pf
              ct - 1
            EndIf
          Until ct = 0 Or (pr = 0 And pf = 0)
          If pr
            pr + 1
            While Mid(Content$, pr, 1) = " " : pr + 1 : Wend
            While Mid(Content$, p - 1, 1) = " " : p - 1 : Wend
            Content$ = Left(Content$, p - 1) + Mid(Content$, pr)
          EndIf
        EndIf
      Until p = 0
    EndIf
    hFile = CreateFile(#PB_Any, Filename$)
    If hFile
      Result = WriteString(hFile, Trim(Content$))
      CloseFile(hFile)
    EndIf
    ProcedureReturn Result
  EndIf
EndProcedure
Procedure RE_AdjustZoom(NoGadget, AdjFactor)
  Protected num.l, denom.l, Factor.f
  SendMessage_(GadgetID(NoGadget), #EM_GETZOOM, @num, @denom)
  If num And denom
    Factor = num / denom
  Else
    Factor = 1
  EndIf
  If AdjFactor
    Factor * (AdjFactor + 100) / 100
  Else
    Factor = 1
  EndIf
  denom = 10
  num = denom * Factor
  SendMessage_(GadgetID(NoGadget), #EM_SETZOOM, num, denom)
EndProcedure
Procedure RE_SearchString(Gadget, SearchString$, MATCHCASE = 0, WHOLEWORD = 0, UP = 0, startPos = -2)
  Protected McpMin, McpMax, NUP, flg, Res
  Protected text.FINDTEXT\lpstrText = @SearchString$
  If startPos <> -2 
    text\chrg\cpMin = startPos
    text\chrg\cpMax = startPos
    SendMessage_(GadgetID(Gadget), #EM_EXSETSEL, 0, @text\chrg) 
  EndIf
  SendMessage_(GadgetID(Gadget), #EM_EXGETSEL, 0, @text\chrg) 
  If (text\chrg\cpMin <> text\chrg\cpMax)
    If UP = 0
      text\chrg\cpMin = text\chrg\cpMax
    Else
      text\chrg\cpMax = text\chrg\cpMin
    EndIf
  EndIf
  McpMin = text\chrg\cpMin 
  McpMax = text\chrg\cpMax
  text\chrg\cpMax = -1 
  If UP : NUP = 0 : Else : NUP = 1 : EndIf
  flg = (MATCHCASE * #FR_MATCHCASE) | (WHOLEWORD * #FR_WHOLEWORD) | (NUP * #FR_DOWN)
  Res = SendMessage_(GadgetID(Gadget), #EM_FINDTEXT, flg, @text)
  If Res > -1 
    SendMessage_(GadgetID(Gadget), #EM_SETSEL, Res, Res + Len(SearchString$)) 
  Else
    If UP = 0 And McpMin > 0 : Res = -2 : EndIf
    If UP And McpMax <> -1 
      text\chrg\cpMin = -1
      text\chrg\cpMax = -1
      SendMessage_(GadgetID(Gadget), #EM_EXSETSEL, 0, @text\chrg) 
      SendMessage_(GadgetID(Gadget), #EM_EXGETSEL, 0, @text\chrg) 
      If McpMax <> text\chrg\cpMax : Res = -2 : EndIf 
      text\chrg\cpMin = McpMin 
      text\chrg\cpMax = McpMax
      SendMessage_(GadgetID(Gadget), #EM_EXSETSEL, 0, @text\chrg) 
    EndIf
  EndIf
  ProcedureReturn Res
EndProcedure
: 
Enumeration REFR_ShortCuts
  #REFRShortCut_Quit
  #REFRShortCut_Search
  #REFRShortCut_Tab
EndEnumeration
Procedure RE_FindReplace(Gadget, Replace = 0, ParentID = #PB_Default, XShiftOrPos = #PB_Default, YShiftOrPos = #PB_Default, ParentAnchor = #CWO_Center, WindowAnchor = #CWO_Center)
  Protected WParam, WWidth, WHeight 
  Protected FRWindow, SearchStringGadget, BPrevious, BNext, VPos, ReplaceStringGadget 
  Protected BReplace, BReplaceAll, BQuit, chk1, chk2, chk3 
  Protected SelectedText$, DoSearch, Event, find.s, RewindSearch
  Protected Pos, StartPos, FirstSearch, NbReplace, msg.s
  Protected txtrange.CHARRANGE, repl.s
  Protected OX, OY, TG1, GadgetList$, TG2
  Protected ParentWindow = #PB_Default
  If IsWindow(ParentID)
    ParentWindow = ParentID
    ParentID = WindowID(ParentID)
  ElseIf ParentID <> #PB_Default And IsWindow_(ParentID)
    ParentWindow = GetProp_(ParentID, "PB_WindowID") - 1
  ElseIf IsWindow(GetActiveWindow()) And ParentID = #PB_Default
    ParentWindow = GetActiveWindow()
    ParentID = WindowID(ParentWindow)
  EndIf
  If ParentWindow = #PB_Default
    ParentWindow = ParentID
  EndIf
  If Replace
    WHeight = 160
  Else
    WHeight = 120
  EndIf
  WWidth = 510
  If XShiftOrPos = #PB_Default
    XShiftOrPos = 400
  EndIf
  If YShiftOrPos = #PB_Default
    YShiftOrPos = 100
  EndIf
  WParam = #PB_Window_Invisible | #PB_Window_SystemMenu
  Protected ParentWindowID = ComputeWinOrigins(@OX, @OY, WWidth, WHeight, ParentWindow, XShiftOrPos, YShiftOrPos, ParentAnchor, WindowAnchor)
  FRWindow = OpenWindow(#PB_Any, OX, OY, WWidth, WHeight, GetTextFromCatalog("SWTitle"), WParam, ParentWindowID)
  If FRWindow
    CompilerIf Defined(ApplyDarkModeToWindow, #PB_Procedure)
      ApplyDarkModeToWindow(FRWindow)
    CompilerEndIf
    StickyWindow(FRWindow, #True)
    TG1 = TextGadget(#PB_Any, 10, 20, 100, 22, GetTextFromCatalog("SWSearch"))
    GadgetList$ + Str(TG1) + ","
    SearchStringGadget = StringGadget(#PB_Any, 100, 15, 300, 25, "")
    GadgetList$ + Str(SearchStringGadget) + ","
    BPrevious = ButtonGadget(#PB_Any, 410, 15, 35, 26, "<<", #PB_Button_Default)
    GadgetList$ + Str(BPrevious) + ","
    BNext = ButtonGadget(#PB_Any, 465, 15, 35, 26, ">>", #PB_Button_Default)
    GadgetList$ + Str(BNext) + ","
    DisableGadget(BNext, #True)
    If Replace
      VPos = 55
      TG2 = TextGadget(#PB_Any, 10, VPos, 100, 22, GetTextFromCatalog("SWReplaceTitle"))
      GadgetList$ + Str(TG2) + ","
      ReplaceStringGadget = StringGadget(#PB_Any, 100, VPos - 5, 300, 25, "")
      GadgetList$ + Str(ReplaceStringGadget) + ","
      BReplace = ButtonGadget(#PB_Any, 410, VPos - 5, 90, 26, GetTextFromCatalog("SWReplace"))
      GadgetList$ + Str(BReplace) + ","
      DisableGadget(BReplace, #True)
      BReplaceAll = ButtonGadget(#PB_Any, 410, VPos + 30, 90, 26, GetTextFromCatalog("SWReplaceAll"))
      GadgetList$ + Str(BReplaceAll) + ","
      DisableGadget(BReplaceAll, #True)
    EndIf
    BQuit = ButtonGadget(#PB_Any, 410, (WindowHeight(FRWindow) - 33), 90, 26, GetTextFromCatalog("Quit"))
    GadgetList$ + Str(BQuit) + ","
    VPos = 85
    If Replace = 0
      VPos - 40
    EndIf
    chk1 = CheckBoxGadget(#PB_Any, 10, VPos, 135, 26, GetTextFromCatalog("SWCaseSensitive"))
    GadgetList$ + Str(chk1) + ","
    chk2 = CheckBoxGadget(#PB_Any, 155, VPos, 100, 26, GetTextFromCatalog("SWWholeWord"))
    GadgetList$ + Str(chk2) + ","
    If Replace
      chk3 = CheckBoxGadget(#PB_Any, 255, VPos, 150, 26, GetTextFromCatalog("SWInAllDocument"))
      GadgetList$ + Str(chk3) + ","
    EndIf
    SendMessage_(GadgetID(Gadget), #EM_HIDESELECTION, #False, 0)
    SelectedText$ = RE_GetContent_RTF(Gadget, #SFF_SELECTION | #SF_TEXT)
    If Len(SelectedText$) < 128
      SetGadgetText(SearchStringGadget, SelectedText$)
      If IsGadget(ReplaceStringGadget) : SetActiveGadget(ReplaceStringGadget) : EndIf
      DisableGadget(BNext, #False)
      If Replace
        DisableGadget(BReplace, #False)
        DisableGadget(BReplaceAll, #False)
      EndIf
    EndIf
    SetActiveGadget(SearchStringGadget)
    AddKeyboardShortcut(FRWindow, #PB_Shortcut_Return, #REFRShortCut_Search)
    AddKeyboardShortcut(FRWindow, #PB_Shortcut_Escape, #REFRShortCut_Quit)
    AddKeyboardShortcut(FRWindow, #PB_Shortcut_Tab, #REFRShortCut_Tab)
    ApplyFontToGadgetList(GadgetList$)
    CompilerIf Defined(SetGadgetsColorsFromTheme, #PB_Procedure)
      If ListSize(InterfaceColorPresets()) > 0
        SetGadgetsColorsFromTheme(FRWindow, InterfaceColorPresets(), GadgetList$)
      EndIf
    CompilerEndIf
    HideWindow(FRWindow, #False)
    Repeat
      event = WaitWindowEvent()
      DoSearch = 0
      If EventWindow() <> FRWindow
        SetActiveWindow(FRWindow)
      Else
        If event = #PB_Event_Menu
          If EventMenu() = #REFRShortCut_Tab
            If GetActiveGadget() = SearchStringGadget
              If IsGadget(ReplaceStringGadget)
                While WindowEvent() : Wend
                SetActiveGadget(ReplaceStringGadget)
              EndIf
            ElseIf IsGadget(ReplaceStringGadget) And GetActiveGadget() = ReplaceStringGadget
              While WindowEvent() : Wend
              SetActiveGadget(SearchStringGadget)
            EndIf
          ElseIf EventMenu() = #REFRShortCut_Search
            DoSearch = 1
            event = #PB_Event_Gadget
          ElseIf EventMenu() = #REFRShortCut_Quit
            Break
          EndIf
        ElseIf event = #PB_Event_CloseWindow
          Break
        EndIf
        If event = #PB_Event_Gadget
          If EventGadget() = BNext
            DoSearch = 1
          EndIf
          If EventGadget() = SearchStringGadget
            If Len(GetGadgetText(SearchStringGadget))
              DisableGadget(BPrevious, #False)
              DisableGadget(BNext, #False)
              If Replace
                DisableGadget(BReplace, #False)
                DisableGadget(BReplaceAll, #False)
              EndIf
            Else
              DisableGadget(BPrevious, #True)
              DisableGadget(BNext, #True)
              If Replace
                DisableGadget(BReplace, #True)
                DisableGadget(BReplaceAll, #True)
              EndIf
            EndIf
          ElseIf Replace And EventGadget() = ReplaceStringGadget
          ElseIf DoSearch Or EventGadget() = BPrevious Or (Replace And EventGadget() = BReplace Or EventGadget() = BReplaceAll)
            find.s = GetGadgetText(SearchStringGadget)
            If Replace And (EventGadget() = BReplace Or EventGadget() = BReplaceAll) 
              SendMessage_(GadgetID(Gadget), #EM_EXGETSEL, 0, @txtrange.CHARRANGE) 
              If (txtrange\cpMin <> txtrange\cpMax) 
                RE_SetSelection(Gadget, txtrange\cpMin, txtrange\cpMax)
              EndIf
            EndIf
            If EventGadget() = BPrevious : RewindSearch = 1 : Else : RewindSearch = 0 : EndIf
            StartPos = -2
            If IsGadget(chk3) And GetGadgetState(chk3)
              SetGadgetState(chk3, 0)
              If RewindSearch : StartPos = -1 : Else : StartPos = 0 : EndIf
            EndIf
            FirstSearch = 0
            NbReplace = 0
            RE_RebootSearch: 
            FirstSearch + 1
            Repeat
              pos = RE_SearchString(Gadget, find, GetGadgetState(chk1), GetGadgetState(chk2), RewindSearch, StartPos)
              If Replace And pos > -1 And (EventGadget() = BReplace Or EventGadget() = BReplaceAll)
                repl.s = GetGadgetText(ReplaceStringGadget)
                RE_ReplaceSelection(Gadget, repl)
                RE_GetSelection(Gadget, @txtrange.CHARRANGE)
                txtrange\cpMin = txtrange\cpMax - Len(repl)
                If EventGadget() = BReplaceAll Or (EventGadget() = BReplace And FindString(repl, find, 0))
                  txtrange\cpMin = txtrange\cpMax
                EndIf
                RE_SetSelection(Gadget, txtrange\cpMin, txtrange\cpMax)
                NbReplace + 1
              EndIf
            Until EventGadget() <> BReplaceAll Or pos < 0 Or Replace = 0
            If pos = -2 
              If NbReplace = 0
                msg.s = GetTextFromCatalog("SWUnableToFind") + " " + find + #CRLF$
              Else
                msg = ""
                If EventGadget() = BReplaceAll
                  msg.s = Str(NbReplace) + " " + GetTextFromCatalog("SWReplacementMade") + #CRLF$
                EndIf
              EndIf
              If RewindSearch
                msg + GetTextFromCatalog("SWSearchFromEnd")
                StartPos = -1
              Else
                msg + GetTextFromCatalog("SWSearchFromStart")
                StartPos = 0
              EndIf
              If MessageRequester(GetTextFromCatalog("SWTitle"), msg, #PB_MessageRequester_YesNo) = #PB_MessageRequester_Yes
                Goto RE_RebootSearch
                SetActiveGadget(Gadget)
              EndIf
            EndIf
            msg.s = ""
            If Replace And EventGadget() = BReplaceAll
              msg.s = Str(NbReplace) + " " + GetTextFromCatalog("SWReplacementMade")
            ElseIf pos = -1
              msg.s = GetTextFromCatalog("SWUnableToFind") + " " + find
            EndIf
            If msg
              MessageRequester(GetTextFromCatalog("SWTitle"), msg, #PB_MessageRequester_Ok)
              SetActiveGadget(Gadget)
            EndIf
          EndIf
          If EventGadget() = BQuit
            Break
          EndIf
        EndIf
      EndIf
      SetForegroundWindow_(WindowID(FRWindow))
      SetActiveWindow(FRWindow)
    ForEver
    CloseWindow(FRWindow)
  EndIf
EndProcedure
Structure AlertWindowGList
  WindowNum.i
  Margins.i
  BHeight.i
  BWidth.i
  CopyMenu.i
  RE_gadget.i
  YesNoCancel.i
  BOK.i
  BCancel.i
  BYes.i
  BNo.i
  BSearch.i
  BCopy.i
  BSave.i
EndStructure
Enumeration AlertYesNoCancel
  #AW_AlertOnly
  #AW_YesOrNo_YesByDefault
  #AW_YesOrNo_NoByDefault
  #AW_OKOrCancel_OKByDefault
  #AW_OKOrCancel_CancelByDefault
  #AW_YesNoOrCancel_YesByDefault
  #AW_YesNoOrCancel_NoByDefault
  #AW_YesNoOrCancel_CancelByDefault
EndEnumeration
Procedure ResizeAlertWindow()
  Shared AlertGList.AlertWindowGList 
  Protected WWidth, WHeight, Margins, LowBPos, YesNoCancel
  WWidth = WindowWidth(AlertGList\WindowNum)
  WHeight = WindowHeight(AlertGList\WindowNum)
  Margins = AlertGList\Margins
  LowBPos = WHeight - AlertGList\BHeight - Margins
  YesNoCancel = AlertGList\YesNoCancel
  ResizeGadget(AlertGList\RE_gadget, #PB_Ignore, #PB_Ignore, (WWidth - Margins * 2), (WHeight - AlertGList\BHeight - Margins * 3))
  If IsGadget(AlertGList\BOK) And (YesNoCancel = #AW_AlertOnly Or YesNoCancel = #AW_OKOrCancel_OKByDefault)
    ResizeGadget(AlertGList\BOK, WWidth - AlertGList\BWidth - Margins, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BYes) And (YesNoCancel = #AW_YesOrNo_YesByDefault Or YesNoCancel = #AW_YesNoOrCancel_YesByDefault)
    ResizeGadget(AlertGList\BYes, WWidth - AlertGList\BWidth - Margins, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BNo) And (YesNoCancel = #AW_YesOrNo_NoByDefault Or YesNoCancel = #AW_YesNoOrCancel_NoByDefault)
    ResizeGadget(AlertGList\BNo, WWidth - AlertGList\BWidth - Margins, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BCancel) And (YesNoCancel = #AW_OKOrCancel_CancelByDefault Or YesNoCancel = #AW_YesNoOrCancel_CancelByDefault)
    ResizeGadget(AlertGList\BCancel, WWidth - AlertGList\BWidth - Margins, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BOK) And (YesNoCancel = #AW_OKOrCancel_CancelByDefault)
    ResizeGadget(AlertGList\BOK, WWidth - (AlertGList\BWidth + Margins) * 2, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BYes) And (YesNoCancel = #AW_YesOrNo_NoByDefault Or YesNoCancel = #AW_YesNoOrCancel_NoByDefault)
    ResizeGadget(AlertGList\BYes, WWidth - (AlertGList\BWidth + Margins) * 2, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BNo) And (YesNoCancel = #AW_YesOrNo_YesByDefault Or YesNoCancel = #AW_YesNoOrCancel_YesByDefault Or YesNoCancel = #AW_YesNoOrCancel_CancelByDefault)
    ResizeGadget(AlertGList\BNo, WWidth - (AlertGList\BWidth + Margins) * 2, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BCancel) And YesNoCancel = #AW_OKOrCancel_OKByDefault
    ResizeGadget(AlertGList\BCancel, WWidth - (AlertGList\BWidth + Margins) * 2, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BYes) And YesNoCancel = #AW_YesNoOrCancel_CancelByDefault
    ResizeGadget(AlertGList\BYes, WWidth - (AlertGList\BWidth + Margins) * 3, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BCancel) And (YesNoCancel = #AW_YesNoOrCancel_YesByDefault Or YesNoCancel = #AW_YesNoOrCancel_NoByDefault)
    ResizeGadget(AlertGList\BCancel, WWidth - (AlertGList\BWidth + Margins) * 3, LowBPos, #PB_Ignore, #PB_Ignore)
  EndIf
  If IsGadget(AlertGList\BCopy) And YesNoCancel = #AW_AlertOnly
    ResizeGadget(AlertGList\BCopy, WWidth - (AlertGList\BWidth + Margins) * 2, LowBPos, #PB_Ignore, #PB_Ignore)
    ResizeGadget(AlertGList\BSave, WWidth - (AlertGList\BWidth + Margins) * 3, LowBPos, #PB_Ignore, #PB_Ignore)
    ResizeGadget(AlertGList\BSearch, WWidth - (AlertGList\BWidth + Margins) * 4, LowBPos, #PB_Ignore, #PB_Ignore)
    If WWidth < (AlertGList\BWidth + Margins) * 3 + Margins
      HideGadget(AlertGList\BCopy, #True)
      HideGadget(AlertGList\BSave, #True)
      HideGadget(AlertGList\BSearch, #True)
    ElseIf WWidth < (AlertGList\BWidth + Margins) * 4 + Margins
      HideGadget(AlertGList\BCopy, #False)
      HideGadget(AlertGList\BSave, #False)
      HideGadget(AlertGList\BSearch, #True)
    Else
      HideGadget(AlertGList\BCopy, #False)
      HideGadget(AlertGList\BSave, #False)
      If (GadgetWidth(AlertGList\RE_gadget) * GadgetHeight(AlertGList\RE_gadget)) < 160000
        HideGadget(AlertGList\BSearch, #True)
      Else
        HideGadget(AlertGList\BSearch, #False)
      EndIf
    EndIf
  EndIf
EndProcedure
Enumeration AlertWShortcuts
  #AW_Shortcut_Quit
  #AW_Shortcut_DefButton
  #AW_Shortcut_Search
  #AW_Shortcut_Save
  #AW_MenuCopy
  #AW_MenuCopyAll
EndEnumeration
#RTF_Marker = 23675
Procedure THG(EventGadget, Gadget)
  If IsGadget(Gadget) And EventGadget = Gadget
    ProcedureReturn #True
  EndIf
EndProcedure
Procedure Alert(Message$, WindowTitle$ = "", WSticky = 1, WhiteBackGround = 0, Txtleft = 0, FixedWidth = 0, TabList$ = "", YesNoCancel = #AW_AlertOnly, ParentID = #PB_Default, XShiftOrPos = 0, YShiftOrPos = 0, ParentAnchor = #CWO_Center, WindowAnchor = #CWO_Center)
  Shared AlertGList.AlertWindowGList 
  Protected ParentWindow = #PB_Default
  If IsWindow(ParentID)
    ParentWindow = ParentID
    ParentID = WindowID(ParentID)
  ElseIf ParentID <> #PB_Default And IsWindow_(ParentID)
    ParentWindow = GetProp_(ParentID, "PB_WindowID") - 1
  ElseIf IsWindow(GetActiveWindow()) And ParentID = #PB_Default
    ParentWindow = GetActiveWindow()
    ParentID = WindowID(ParentWindow)
  EndIf
  If ParentWindow = #PB_Default
    ParentWindow = ParentID
  EndIf
  Protected WWidth, WHeight, RTF, hMonitor, mi.MONITORINFO
  Protected TextSurf, TW.d, TH.d, ratio.f, SL
  Protected OX, OY
  Protected EventID, QuitAll, NFile$
  Protected *tabStops, PosInList, TabPos$
  Protected *mem, BDefault
  Protected BoundWidthMin, BoundHeightMin, BoundWidthMax, BoundHeightMax
  Protected iGwlStyle, *GadgetAdress, GadgetList$, EventGadget
  If WindowTitle$ = "" : WindowTitle$ = GetTextFromCatalog("Attention") : EndIf
  If Message$
    WWidth = 250
    If FixedWidth
      WWidth = FixedWidth
    EndIf
    WHeight = 100
    If PeekC(@Message$) = #RTF_Marker
      RTF = 1
    ElseIf LCase(Left(Message$, 5)) = "{\rtf" Or LCase(Left(Message$, 6)) = "{\urtf"
      RTF = 2
    Else
      RTF = 0
    EndIf
    Protected WParam = #PB_Window_Invisible | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget | #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar
    Protected ActiveWindow = ParentWindow
    If Not(IsWindow(ActiveWindow))
      ActiveWindow = GetActiveWindow()
    EndIf
    Protected ParentWindowID = ComputeWinOrigins(@OX, @OY, WWidth, WHeight, ParentWindow, XShiftOrPos, YShiftOrPos, ParentAnchor, WindowAnchor)
    AlertGList\WindowNum = OpenWindow(#PB_Any, OX, OY, WWidth, WHeight, WindowTitle$, WParam, ParentWindowID)
    If IsWindow(AlertGList\WindowNum)
      If IsWindow(ActiveWindow) And IsWindowEnabled_(WindowID(ActiveWindow))
        DisableWindow(ActiveWindow, #True)
        Protected ParentHasBeenDisabled = #True
      EndIf
      BoundWidthMin = WWidth
      BoundHeightMin = WHeight
      hMonitor = MonitorFromWindow_(WindowID(AlertGList\WindowNum), #MONITOR_DEFAULTTONEAREST)
      mi\cbSize = SizeOf(MONITORINFO)
      GetMonitorInfo_(hMonitor, @mi)
      BoundWidthMax = DesktopUnscaledX(mi\rcWork\Right - mi\rcWork\left) - 80
      BoundHeightMax = DesktopUnscaledY(mi\rcWork\Bottom - mi\rcWork\top) - 80
      WindowBounds(AlertGList\WindowNum, BoundWidthMin, BoundHeightMin, #PB_Ignore, #PB_Ignore)
      CompilerIf Defined(ApplyDarkModeToWindow, #PB_Procedure)
        ApplyDarkModeToWindow(AlertGList\WindowNum)
      CompilerEndIf
      If WSticky
        StickyWindow(AlertGList\WindowNum, 1)
      EndIf
      AlertGList\BHeight = 24
      AlertGList\BWidth = 90
      AlertGList\Margins = 8
      AlertGList\YesNoCancel = YesNoCancel
      AlertGList\RE_gadget = EditorGadget(#PB_Any, AlertGList\Margins, AlertGList\Margins, 1, 1)
      SendMessage_(GadgetID(AlertGList\RE_gadget), #EM_SETTARGETDEVICE, #Null, 0)
      SendMessage_(GadgetID(AlertGList\RE_gadget), #EM_LIMITTEXT, -1, 0) 
      SendMessage_(GadgetID(AlertGList\RE_gadget), #EM_SETTEXTMODE, #TM_RICHTEXT, 0) 
      If TabList$
        RE_SetSelection(AlertGList\RE_gadget, 0, -1)
        *tabStops = AllocateMemory((CountString(TabList$, ",") + 1) * 4)
        PosInList = 0
        Repeat
          PosInList + 1
          TabPos$ = StringField(TabList$, PosInList, ",")
          If TabPos$
            PokeW(*tabStops + 4 * (PosInList - 1), Val(TabPos$))
          EndIf
        Until TabPos$ = ""
        SendMessage_(GadgetID(AlertGList\RE_gadget), #EM_SETTABSTOPS, CountString(TabList$, ",") + 1, *tabStops)
        FreeMemory(*tabStops)
      EndIf
      If RTF = 2
        *mem = AllocateMemory(StringByteLength(Message$, #PB_UTF8) + 1)
        PokeS(*mem, Message$, -1, #PB_UTF8) 
        SendMessage_(GadgetID(AlertGList\RE_gadget), #EM_REPLACESEL, 0, PeekS(*mem, -1, #PB_Unicode))
        FreeMemory(*mem)
      Else
        SendMessage_(GadgetID(AlertGList\RE_gadget), #EM_REPLACESEL, 0, @Message$)
      EndIf
      SendMessage_(GadgetID(AlertGList\RE_gadget), #EM_SETREADONLY, #True, 0)
      If YesNoCancel = #AW_AlertOnly Or YesNoCancel = #AW_OKOrCancel_OKByDefault Or YesNoCancel = #AW_OKOrCancel_CancelByDefault
        If YesNoCancel = #AW_AlertOnly Or YesNoCancel = #AW_OKOrCancel_OKByDefault
          BDefault = 1
        Else
          BDefault = 0
        EndIf
        AlertGList\BOK = ButtonGadget(#PB_Any, 1, 1, AlertGList\BWidth, AlertGList\BHeight, GetTextFromCatalog("OK"), #PB_Button_Default * BDefault)
      EndIf
      If YesNoCancel = #AW_OKOrCancel_OKByDefault Or YesNoCancel = #AW_OKOrCancel_CancelByDefault Or YesNoCancel = #AW_YesNoOrCancel_YesByDefault Or YesNoCancel = #AW_YesNoOrCancel_NoByDefault Or YesNoCancel = #AW_YesNoOrCancel_CancelByDefault
        If YesNoCancel = #AW_OKOrCancel_CancelByDefault Or YesNoCancel = #AW_YesNoOrCancel_CancelByDefault
          BDefault = 1
        Else
          BDefault = 0
        EndIf
        AlertGList\BCancel = ButtonGadget(#PB_Any, 1, 1, AlertGList\BWidth, AlertGList\BHeight, GetTextFromCatalog("Cancel"), #PB_Button_Default * BDefault)
      EndIf
      If YesNoCancel = #AW_YesOrNo_YesByDefault Or YesNoCancel = #AW_YesOrNo_NoByDefault Or YesNoCancel = #AW_YesNoOrCancel_YesByDefault Or YesNoCancel = #AW_YesNoOrCancel_NoByDefault Or YesNoCancel = #AW_YesNoOrCancel_CancelByDefault
        If YesNoCancel = #AW_YesOrNo_YesByDefault Or YesNoCancel = #AW_YesNoOrCancel_YesByDefault
          BDefault = 1
        Else
          BDefault = 0
        EndIf
        AlertGList\BYes = ButtonGadget(#PB_Any, 1, 1, AlertGList\BWidth, AlertGList\BHeight, GetTextFromCatalog("Yes"), #PB_Button_Default * BDefault)
        If YesNoCancel = #AW_YesOrNo_NoByDefault Or YesNoCancel = #AW_YesNoOrCancel_NoByDefault
          BDefault = 1
        Else
          BDefault = 0
        EndIf
        AlertGList\BNo = ButtonGadget(#PB_Any, 1, 1, AlertGList\BWidth, AlertGList\BHeight, GetTextFromCatalog("No"), #PB_Button_Default * BDefault)
      EndIf
      If YesNoCancel = #AW_AlertOnly
        AlertGList\BCopy = ButtonGadget(#PB_Any, 1, 1, AlertGList\BWidth, AlertGList\BHeight, GetTextFromCatalog("Copy"))
        AlertGList\BSave = ButtonGadget(#PB_Any, 1, 1, AlertGList\BWidth, AlertGList\BHeight, GetTextFromCatalog("Save"))
        AlertGList\BSearch = ButtonGadget(#PB_Any, 1, 1, AlertGList\BWidth, AlertGList\BHeight, GetTextFromCatalog("SWTitle"))
      EndIf
      Message$ = ReplaceString(GetGadgetText(AlertGList\RE_gadget), #LF$, "")
      RE_SetSelection(AlertGList\RE_gadget, 0, -1)
      GetTextSurfPix(AlertGList\RE_gadget, Message$, @TW, @TH)
      TextSurf = TW * TH * 1.4
      If RTF = 0
        If TextSurf < 50000 And Txtleft = 0 And TabList$ = ""
          Protected paraFormat.PARAFORMAT2
          paraFormat\cbSize = SizeOf(PARAFORMAT2)
          paraFormat\dwMask = #PFM_ALIGNMENT
          paraFormat\wAlignment = #PFA_CENTER
          SendMessage_(GadgetID(AlertGList\RE_gadget), #EM_SETPARAFORMAT, #SCF_SELECTION, @paraFormat)
        EndIf
        Protected format.PARAFORMAT
        format\cbSize = SizeOf(PARAFORMAT)
        format\dwMask = #PFM_RIGHTINDENT | #PFM_OFFSETINDENT
        format\dxStartIndent = 100
        format\dxRightIndent = 100
        SendMessage_(GadgetID(AlertGList\RE_gadget), #EM_SETPARAFORMAT, #SCF_SELECTION, @format)
      EndIf
      AW_ComputeWidthAndHeightFromSurf: 
      If FixedWidth
        WWidth = FixedWidth
        SL = 0
      Else
        If TextSurf = 0
          TextSurf = 400 * 500
        EndIf
        WWidth = Sqr(TextSurf)
        ratio = 1 + (800 - WWidth) / 1600
        If ratio < 1
          WWidth = 800
        Else
          WWidth * ratio * 1.2
        EndIf
        SL = 0
        If WWidth > 800 : WWidth = 800 : SL = 1 : EndIf
        If WWidth > BoundWidthMax : WWidth = BoundWidthMax : SL = 1 : EndIf
        If WWidth < BoundWidthMin : WWidth = BoundWidthMin : EndIf
      EndIf
      WHeight = TextSurf / WWidth
      If WHeight > BoundHeightMax : WHeight = BoundHeightMax : SL = 1 : EndIf
      If WHeight < BoundHeightMin : WHeight = BoundHeightMin : EndIf
      ComputeWinOrigins(@OX, @OY, WWidth, WHeight, ParentWindow, XShiftOrPos, YShiftOrPos, ParentAnchor, WindowAnchor)
      ResizeWindow(AlertGList\WindowNum, OX, OY, WWidth, WHeight)
      ResizeAlertWindow()
      If SL = 0
        iGwlStyle = GetWindowLongPtr_(GadgetID(AlertGList\RE_gadget), #GWL_STYLE)
        If (iGwlStyle & #WS_VSCROLL)
          TextSurf + TH * WWidth
          Goto AW_ComputeWidthAndHeightFromSurf
        EndIf
      EndIf
      RE_SetSelection(AlertGList\RE_gadget, 0, 0)
      *GadgetAdress = @AlertGList\YesNoCancel
      Repeat
        GadgetList$ + Str(PeekI(*GadgetAdress)) + ","
        *GadgetAdress + SizeOf(Integer)
      Until *GadgetAdress > @AlertGList\BSave
      ApplyFontToGadgetList(GadgetList$)
      GadgetList$ + Str(AlertGList\RE_gadget) + ","
      CompilerIf Defined(SetGadgetsColorsFromTheme, #PB_Procedure)
        If ListSize(InterfaceColorPresets()) > 0
          SetGadgetsColorsFromTheme(AlertGList\WindowNum, InterfaceColorPresets(), GadgetList$)
          If InterfaceColorPresets()\BackgroundColor <> -1
            WhiteBackGround = 1
          EndIf
        EndIf
      CompilerEndIf
      If WhiteBackGround = 0
        SetGadgetColor(AlertGList\RE_gadget, #PB_Gadget_BackColor, $F0FDFD)
      EndIf
      HideWindow(AlertGList\WindowNum, #False)
      BindEvent(#PB_Event_SizeWindow, @ResizeAlertWindow(), AlertGList\WindowNum)
      AddKeyboardShortcut(AlertGList\WindowNum, #PB_Shortcut_Return, #AW_Shortcut_DefButton)
      AddKeyboardShortcut(AlertGList\WindowNum, #PB_Shortcut_Escape, #AW_Shortcut_Quit)
      AddKeyboardShortcut(AlertGList\WindowNum, #PB_Shortcut_Control | #PB_Shortcut_F, #AW_Shortcut_Search)
      AddKeyboardShortcut(AlertGList\WindowNum, #PB_Shortcut_Control | #PB_Shortcut_S, #AW_Shortcut_Save)
      AlertGList\CopyMenu = CreatePopupMenu(#PB_Any)
      MenuItem(#AW_MenuCopy, GetTextFromCatalog("Copy"))
      MenuItem(#AW_MenuCopyAll, GetTextFromCatalog("CopyAll"))
      ClearKeyboardBuffer(AlertGList\WindowNum)
      Repeat
        EventID = WaitWindowEvent()
        If EventWindow() <> AlertGList\WindowNum And WSticky
          SetActiveWindow(AlertGList\WindowNum)
        Else
          If EventID = #PB_Event_Gadget
            EventGadget = EventGadget()
          ElseIf EventID = #PB_Event_CloseWindow
            QuitAll = #PB_MessageRequester_Cancel
          ElseIf EventID = #PB_Event_Menu
            If EventMenu() = #AW_Shortcut_Quit
              QuitAll = #PB_MessageRequester_Cancel
            ElseIf EventMenu() = #AW_Shortcut_DefButton
              If YesNoCancel = #AW_YesOrNo_YesByDefault Or YesNoCancel = #AW_YesNoOrCancel_YesByDefault Or YesNoCancel = #AW_OKOrCancel_OKByDefault
                QuitAll = #PB_MessageRequester_Yes
              ElseIf YesNoCancel = #AW_YesOrNo_NoByDefault Or YesNoCancel = #AW_YesNoOrCancel_NoByDefault
                QuitAll = #PB_MessageRequester_No
              Else
                QuitAll = #PB_MessageRequester_Cancel
              EndIf
            ElseIf EventMenu() = #AW_Shortcut_Search
              EventID = #PB_Event_Gadget
              EventGadget = AlertGList\BSearch
            ElseIf EventMenu() = #AW_Shortcut_Save
              EventID = #PB_Event_Gadget
              EventGadget = AlertGList\BSave
            ElseIf EventMenu() = #AW_MenuCopy
              SetClipboardText(RE_GetGadgetSelectedText(AlertGList\RE_gadget))
            ElseIf EventMenu() = #AW_MenuCopyAll
              SetClipboardText(RE_GetGadgetWholeText(AlertGList\RE_gadget))
              RE_SetSelection(AlertGList\RE_gadget, 0, -1)
            EndIf
          EndIf
          If EventID = #PB_Event_Gadget
            If THG(EventGadget, AlertGList\BOK) Or THG(EventGadget, AlertGList\BYes)
              QuitAll = #PB_MessageRequester_Yes
            ElseIf THG(EventGadget, AlertGList\BCancel)
              QuitAll = #PB_MessageRequester_Cancel
            ElseIf THG(EventGadget, AlertGList\BNo)
              QuitAll = #PB_MessageRequester_No
            ElseIf THG(EventGadget, AlertGList\BSearch)
              RE_FindReplace(AlertGList\RE_gadget, 0, AlertGList\WindowNum)
            ElseIf THG(EventGadget, AlertGList\BCopy)
              If RE_GetGadgetSelectedText(AlertGList\RE_gadget)
                DisplayPopupMenu(AlertGList\CopyMenu, WindowID(AlertGList\WindowNum))
              Else
                SetClipboardText(RE_GetGadgetWholeText(AlertGList\RE_gadget))
                RE_SetSelection(AlertGList\RE_gadget, 0, -1)
              EndIf
            ElseIf THG(EventGadget, AlertGList\BSave)
              NFile$ = SaveFileRequester(GetTextFromCatalog("SaveAs"), "", GetTextFromCatalog("TextFile"), 1)
              If NFile$
                If GetExtensionPart(NFile$) <> "txt" : NFile$ + ".txt" : EndIf
                RE_SaveContent(AlertGList\RE_gadget, NFile$, 0)
              EndIf
            EndIf
          EndIf
        EndIf
      Until QuitAll
    EndIf
    If IsWindow(AlertGList\WindowNum)
      ClearKeyboardBuffer(AlertGList\WindowNum)
      CloseWindow(AlertGList\WindowNum)
    EndIf
  EndIf
  If ParentHasBeenDisabled
    DisableWindow(ActiveWindow, #False)
  EndIf
  ProcedureReturn QuitAll
EndProcedure
Procedure AlertYesNoCancel(Message$, Title$ = "", Type = #AW_YesOrNo_YesByDefault, ParentID = #PB_Default, XShiftOrPos = 0, YShiftOrPos = 0)
  ProcedureReturn Alert(Message$, Title$, 1, 0, 0, 0, "", Type, ParentID)
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
  Alert("This a simple message.")
  tx$ = "{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1036{\fonttbl{\f0\fnil\fcharset0 Bauhaus 93;}{\f1\fnil\fcharset0 Calibri;}}" + #CR$ +
        "{\colortbl ;\red192\green80\blue77;}" + #CR$ +
        "{\*\generator Riched20 10.0.19041}\viewkind4\uc1 " + #CR$ +
        "\pard\sa200\sl276\slmult1\f0\fs22\lang12 Demonstration \f1\fs28 text \fs22 in \cf1\b\fs28 RTF \cf0\b0\fs22 format.\par" + #CR$ +
        "}"
  Alert(tx$)
  tx$ = "This example shows how the 'Alert()' function window is able to automatically resize itself so that it can display " +
        "text of any length without the need to rewrite code each time you need to display a message of particular length." + #CR$ +
        "There is no limit to the size of the message you can include here." + #CR$ +
        "The size of the window will be increased as much as possible, then a scrollbar will appear to the right of the text box," +
        " if the message is too large to fit entirely in the window."
  Alert(tx$, "My title")
  tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ +
        #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$
  tx$ = "When the printed text is really large, a “search” button automatically appears to allow a search within the text." + #CR$ + #CR$ + #CR$ + #CR$ + tx$
  Alert(tx$)
  tx$ = "A variant of the Alert() function, named AlertYesNoCancel() is intended to replace MessageRequester() with the parameters #PB_MessageRequester_YesNo or #PB_MessageRequester_YesNoCancel." + #CR$ +
        "It offers exactly the same flexibility As the 'Alert()' function."
  AlertYesNoCancel(tx$, "Demonstration")
  tx$ + #CR$ + #CR$ + "Possible parameters for this variant are:" + #CR$ +
        " #AW_AlertOnly" + #CR$ +
        " #AW_YesOrNo_YesByDefault" + #CR$ +
        " #AW_YesOrNo_NoByDefault" + #CR$ +
        " #AW_OKOrCancel_OKByDefault" + #CR$ +
        " #AW_OKOrCancel_CancelByDefault" + #CR$ +
        " #AW_YesNoOrCancel_YesByDefault" + #CR$ +
        " #AW_YesNoOrCancel_NoByDefault" + #CR$ +
        " #AW_YesNoOrCancel_CancelByDefault" + #CR$ + #CR$ +
        "Here is an exemple of result with #AW_YesNoOrCancel_CancelByDefault" + #CR$ + #CR$ +
        "The return values are the same as those of the MessageRequester function." + #CR$
  AlertYesNoCancel(tx$, "Last Demonstration", #AW_YesNoOrCancel_CancelByDefault)
CompilerEndIf
Last edited by Zapman on Sat Mar 22, 2025 11:47 am, edited 13 times in total.
User avatar
NicTheQuick
Addict
Addict
Posts: 1503
Joined: Sun Jun 22, 2003 7:43 pm
Location: Germany, Saarbrücken
Contact:

Re: An alternative MessageRequester

Post by NicTheQuick »

Looks like it would only work on Windows.

What can it do better?
The english grammar is freeware, you can use it freely - But it's not Open Source, i.e. you can not change it or publish it in altered way.
User avatar
Zapman
Enthusiast
Enthusiast
Posts: 205
Joined: Tue Jan 07, 2020 7:27 pm

Re: An alternative MessageRequester

Post by Zapman »

NicTheQuick wrote: Wed Oct 09, 2024 5:58 pm Looks like it would only work on Windows.

What can it do better?
You're right Nic, it's only for Windows. I've added a mention in the title.
To know what it does better, the best is to try it.
Image Image Image
But, in summary, this message window has "Search" and "Save" buttons. It adapts better to different message sizes, especially if you use it to display long texts. It also supports messages in RTF format.
By modifying a single input parameter, you get a window with the buttons "Yes", "No" and "Cancel" and you choose the default button (or "OK" and "Cancel", always with the choice of default button).
Possible parameters are:
#AW_AlertOnly
#AW_YesOrNo_YesByDefault
#AW_YesOrNo_NoByDefault
#AW_OKOrCancel_OKByDefault
#AW_OKOrCancel_CancelByDefault
#AW_YesNoOrCancel_YesByDefault
#AW_YesNoOrCancel_NoByDefault
#AW_YesNoOrCancel_CancelByDefault
Denis
Enthusiast
Enthusiast
Posts: 778
Joined: Fri Apr 25, 2003 5:10 pm
Location: Doubs - France

Re: An alternative MessageRequester

Post by Denis »

Salutttttttttttt Zapman !
(It's been a while)


Excellent these requesters.
A+
Denis
User avatar
idle
Always Here
Always Here
Posts: 5835
Joined: Fri Sep 21, 2007 5:52 am
Location: New Zealand

Re: An alternative MessageRequester

Post by idle »

Thanks that looks good.
User avatar
Zapman
Enthusiast
Enthusiast
Posts: 205
Joined: Tue Jan 07, 2020 7:27 pm

Re: An alternative MessageRequester

Post by Zapman »

Denis wrote: Fri Oct 11, 2024 6:07 am Salutttttttttttt Zapman !
(It's been a while)
Excellent these requesters.
Et il y a juste une ligne à mettre en commentaire pour avoir la version française, tu verras ça dans le code :)

A big while, yes. But I'm still alive.
Nice to hear from you too.
Denis
Enthusiast
Enthusiast
Posts: 778
Joined: Fri Apr 25, 2003 5:10 pm
Location: Doubs - France

Re: An alternative MessageRequester

Post by Denis »

Zapman wrote: Fri Oct 11, 2024 5:26 pm Et il y a juste une ligne à mettre en commentaire pour avoir la version française, tu verras ça dans le code :)
Hé hé :wink:
A+
Denis
User avatar
Zapman
Enthusiast
Enthusiast
Posts: 205
Joined: Tue Jan 07, 2020 7:27 pm

Re: An alternative MessageRequester [Windows]

Post by Zapman »

The code has been revised and optimized for multilanguage support and compatibility with 'ApplyColorThemes.pb'
The version figuring in the first post had been updated.
User avatar
skywalk
Addict
Addict
Posts: 4210
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: An alternative MessageRequester [Windows]

Post by skywalk »

The demo code hangs with examples 4 and 6?

Code: Select all

; *************************************************************************************
;
;-                                      5. DEMO
;
; *************************************************************************************
;
;-{ TEST
CompilerIf 1  ;-! _SET_TEST
  #TRY_TEST = 4 ;<-- 4 And 6 hang on Windows 11.
  CompilerSelect #TRY_TEST
  CompilerCase 1
  Alert("This a simple message.")
  CompilerCase 2
  tx$ = "{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1036{\fonttbl{\f0\fnil\fcharset0 Bauhaus 93;}{\f1\fnil\fcharset0 Calibri;}}" + #CR$
  tx$ + "{\colortbl ;\red192\green80\blue77;}" + #CR$
  tx$ + "{\*\generator Riched20 10.0.19041}\viewkind4\uc1 " + #CR$
  tx$ + "\pard\sa200\sl276\slmult1\f0\fs22\lang12 Demonstration \f1\fs28 text \fs22 in \cf1\b\fs28 RTF \cf0\b0\fs22 format.\par" + #CR$
  tx$ + "}"
  Alert(tx$)
  CompilerCase 3
  tx$ = "This example shows how the 'Alert()' function window is able to automatically resize itself so that it can display "
  tx$ + "text of any length without the need to rewrite code each time you need to display a message of particular length." + #CR$
  tx$ + "There is no limit to the size of the message you can include here." + #CR$
  tx$ + "The size of the window will be increased as much as possible, then a scrollbar will appear to the right of the text box,"
  tx$ + " if the message is too large to fit entirely in the window."
  Alert(tx$)
  CompilerCase 4;BUG
  tx$ = "This example shows how the 'Alert()' function window is able to automatically resize itself so that it can display "
  tx$ + "text of any length without the need to rewrite code each time you need to display a message of particular length." + #CR$
  tx$ + "There is no limit to the size of the message you can include here." + #CR$
  tx$ + "The size of the window will be increased as much as possible, then a scrollbar will appear to the right of the text box,"
  tx$ + " if the message is too large to fit entirely in the window."
  tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$
  tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$ + tx$ + #CR$
  tx$ = "When the printed text is really large, a “search” button automatically appears to allow a search within the text."   + #CR$ + #CR$ + #CR$ + #CR$ + tx$
  Alert(tx$)
  CompilerCase 5
  tx$ = "A variant of the Alert() function, named AlertYesNoCancel() is intended to replace MessageRequester() with the parameters #PB_MessageRequester_YesNo or #PB_MessageRequester_YesNoCancel." + #CR$
  tx$ + "It offers exactly the same flexibility As the 'Alert()' function."
  AlertYesNoCancel(tx$, "Demonstration")
  CompilerCase 6;BUG
  tx$ = "A variant of the Alert() function, named AlertYesNoCancel() is intended to replace MessageRequester() with the parameters #PB_MessageRequester_YesNo or #PB_MessageRequester_YesNoCancel." + #CR$
  tx$ + "It offers exactly the same flexibility As the 'Alert()' function."
  tx$ + #CR$ + #CR$ + "Possible parameters for this variant are:" + #CR$
  tx$ + "  #AW_AlertOnly" + #CR$
  tx$ + "  #AW_YesOrNo_YesByDefault" + #CR$
  tx$ + "  #AW_YesOrNo_NoByDefault" + #CR$
  tx$ + "  #AW_OKOrCancel_OKByDefault" + #CR$
  tx$ + "  #AW_OKOrCancel_CancelByDefault" + #CR$
  tx$ + "  #AW_YesNoOrCancel_YesByDefault" + #CR$
  tx$ + "  #AW_YesNoOrCancel_NoByDefault" + #CR$
  tx$ + "  #AW_YesNoOrCancel_CancelByDefault" + #CR$ + #CR$
  tx$ + "Here is an exemple of result with #AW_YesNoOrCancel_CancelByDefault" + #CR$ + #CR$
  tx$ + "The return values are the same as those of the MessageRequester function." + #CR$
  AlertYesNoCancel(tx$, "Demonstration", #AW_YesNoOrCancel_CancelByDefault)
  CompilerEndSelect
CompilerEndIf
;-} TEST
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
ShadowStorm
Enthusiast
Enthusiast
Posts: 303
Joined: Tue Feb 14, 2017 12:07 pm

Re: An alternative MessageRequester [Windows]

Post by ShadowStorm »

Nice code, thanks ZapMan :D

I would highly recommend a small addition that can be very useful, being able to copy selected text
with a dedicated “Copy” menu, it can be useful sometimes to copy just part of the text.
I am French, I do not speak English.
My apologies for the mistakes.

I have sometimes problems of expression
I am sometimes quite clumsy, please excuse me and let me know.
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: An alternative MessageRequester [Windows]

Post by ChrisR »

Seeing tx$ = "blabla" and all the tx$ + "blablabla" in the demo example, no real impact here but just to let you know, it is really much faster to write it all at once (with + to continue on the next line), it avoids searching for the end of the string each time.

Code: Select all

DisableDebugger
Count = 100000
t1 = ElapsedMilliseconds()
For I = 1 To Count
  tx$ = "A variant of the Alert() function, named AlertYesNoCancel() is intended to replace MessageRequester() with the parameters #PB_MessageRequester_YesNo or #PB_MessageRequester_YesNoCancel." + #CR$
  tx$ + "It offers exactly the same flexibility As the 'Alert()' function."
  tx$ + #CR$ + #CR$ + "Possible parameters for this variant are:" + #CR$
  tx$ + "  #AW_AlertOnly" + #CR$
  tx$ + "  #AW_YesOrNo_YesByDefault" + #CR$
  tx$ + "  #AW_YesOrNo_NoByDefault" + #CR$
  tx$ + "  #AW_OKOrCancel_OKByDefault" + #CR$
  tx$ + "  #AW_OKOrCancel_CancelByDefault" + #CR$
  tx$ + "  #AW_YesNoOrCancel_YesByDefault" + #CR$
  tx$ + "  #AW_YesNoOrCancel_NoByDefault" + #CR$
  tx$ + "  #AW_YesNoOrCancel_CancelByDefault" + #CR$ + #CR$
  tx$ + "Here is an exemple of result with #AW_YesNoOrCancel_CancelByDefault" + #CR$ + #CR$
Next
t2 = ElapsedMilliseconds()
For I = 1 To Count
  tx$ = "A variant of the Alert() function, named AlertYesNoCancel() is intended to replace MessageRequester() with the parameters #PB_MessageRequester_YesNo or #PB_MessageRequester_YesNoCancel." + #CR$+
        "It offers exactly the same flexibility As the 'Alert()' function."+
        #CR$ + #CR$ + "Possible parameters for this variant are:" + #CR$+
        "  #AW_AlertOnly" + #CR$+
        "  #AW_YesOrNo_YesByDefault" + #CR$+
        "  #AW_YesOrNo_NoByDefault" + #CR$+
        "  #AW_OKOrCancel_OKByDefault" + #CR$+
        "  #AW_OKOrCancel_CancelByDefault" + #CR$+
        "  #AW_YesNoOrCancel_YesByDefault" + #CR$+
        "  #AW_YesNoOrCancel_NoByDefault" + #CR$+
        "  #AW_YesNoOrCancel_CancelByDefault" + #CR$ + #CR$+
        "Here is an exemple of result with #AW_YesNoOrCancel_CancelByDefault" + #CR$ + #CR$
Next
t3 = ElapsedMilliseconds()
EnableDebugger
Debug "Time text$ + = " + Str(t2-t1) + " ms vs text$ in one line = " + Str(t3-t2)  + " ms"
Here: Time text$ + = 414 ms vs text$ in one line = 35 ms

Thanks for the alternative MessageRequester, it looks very good :)
AZJIO
Addict
Addict
Posts: 2141
Joined: Sun May 14, 2017 1:48 am

Re: An alternative MessageRequester [Windows]

Post by AZJIO »

ChrisR
I also wanted to report this, but then it would be my consent with the embedding of languages. I am not a supporter for the texts to be stored in the executable file. We have 193 countries and a lot of languages. If you use 20 languages, then in any case we ignore the other many languages. Make the best option in one language, and connect the rest of the languages ​​using external files.
In total, scientists know about 2676 languages ​​and 7000 dialects
When the programmer will connect the module, this is his task in order to integrate the tongue. You can, for example:

Code: Select all

#text$ = "text"
#a_string_with_spaces$ = "a string with spaces"
The programmer will redo the language support system in his own way (CodeLocalization). You can replace the constants with variables or arrays. SetLangTxt() - Reads lines from an external file.
ChrisR wrote: Fri Feb 07, 2025 1:07 am Here: Time text$ + = 414 ms vs text$ in one line = 35 ms
In one case, the compiler creates a pointer per line. In another case, the compiler creates many lines in the executable file and dynamically concatenations them during the launch of the program. What could be composed before the launch of the executable file is actually stacked in the process of starting the program.
User avatar
ChrisR
Addict
Addict
Posts: 1466
Joined: Sun Jan 08, 2017 10:27 pm
Location: France

Re: An alternative MessageRequester [Windows]

Post by ChrisR »

I agree, and that's where the limit is for the alternative messageRequester, we can't compete with Microsoft's MUI translations.
AZJIO
Addict
Addict
Posts: 2141
Joined: Sun May 14, 2017 1:48 am

Re: An alternative MessageRequester [Windows]

Post by AZJIO »

The string can be conveyed as a function parameter (MsgAsk).
User avatar
Zapman
Enthusiast
Enthusiast
Posts: 205
Joined: Tue Jan 07, 2020 7:27 pm

Re: An alternative MessageRequester [Windows]

Post by Zapman »

skywalk wrote: Thu Feb 06, 2025 6:37 pm The demo code hangs with examples 4 and 6?
Hi skywalk. Thank you for testing.
No problem here on three different computers with Windows 7 (x86), 10 (x64) and 11 (x64) running your code, and you seem to be the only user reporting this. I'm intrigued. Could you make more advanced tests? On what line does it hang exactly? Have you got an error message?
Post Reply