1 (изменено: teadrinker, 2015-11-03 01:51:59)

Тема: AHK: Пишем удобный менеджер буфера обмена

Мой вариант, описание первым комментарием в коде.

;********************************** Описание **************************************
Readme =
(
Скрипт сохраняет определённое в настройках количество состояний буфера обмена
(только текстовые данные, включая пути к скопрованным файлам) и даёт возможность
вводить их повторно.

Перед началом работы нескомпилированный скрипт загружает dll-файл с иконками,
должен быть доступ к интернету.

При каждом изменении буфера обмена новые данные помещаются в верхнюю ячейку окна
скрипта, остальные сдвигаются вниз по принципу стека. Ввести текст из ячейки можно
кликнув по ней левой кнопкой мыши. Также для ввода и удаления данных из ячеек
можно воспользоваться контекстным меню, появляющимся при клике правой кнопкой мыши.

По умолчанию текст вводится в активное окно (само окно скрипта всегда неактивно).
Это поведение можно изменить через меню «Настройки».

Скрипт сохраняет историю вставленных данных, переключиться на её отображение можно
через меню Содержимое -> Вставленное, а также кликнув по иконке окна в строке
заголовка.

Если содержимое ячейки не видно полностью, посмотреть его можно, нажав над ней
колесо мыши.

Окно скрипта не имеет кнопки на панели задач. Чтобы восстановить свёрнутое окно,
можно воспользоваться меню «Показать» иконки скрипта в трее, либо просто один раз
кликнуть по ней. Кроме того, есть возможность задать горячую клавишу для
показа/скрытия окна в меню «Настройки».

Скрипт регистрирует в системе сообщение "WM_INFO", чтобы можно было заблокировать
его работу извне на время. Для получения информации о текущем состоянии блокировки
окна отправьте любому окну скрипта сообщение "WM_INFO" с wp = 1, в ответ придёт,
если заблокировано — 1, если разблокировано — 0. Для блокировки окна отпрвьте
"WM_INFO" c lp = 1, для разблокировки lp = 0.

При завершении работы скрипт сохраняет все данные в файл `%A_ScriptDir`%\data, и
загружает их из него в следующей сессии.

Для корректной работы скомпилированного варианта из exe-файла все дефолтные иконки
должны быть удалены, вместо них должны быть добавлены иконки из dll-файла в том же
порядке, первая с именем 159 и далее.
Если файловая система NTFS, скомпилированный скрипт пишет сохраняемые данные не в
отдельный файл, а в NTFS-поток по адресу `%A_ScriptFullPath`%:data

Если вы используете в Windows 7 тему "Классика", при выделении ячейки цветом может
происходить неприятное мелькание текста. В этом случае через меню «Вид» можно
указать цвет выделения равный цвету фона ячейки, тогда при наведении курсора на
ячейку её цвет меняться не будет.

Вопросы по работе скрипта можно задать здесь:
http://forum.script-coding.com/viewtopic.php?id=10385

© teadrinker
)
/*
***************************** Структура файла "data" *****************************

UChar          // Количество блоков с данными из "скопированного"
Блоки с данными
{
   UInt        // Длина строки данных
   Str         // Строка данных
}

UChar          // Количество блоков с данными из "вставленного"
Блоки с данными
{
   UInt        // Длина строки данных
   Str         // Строка данных
}

UShort         // X-координата окна
UShort         // Y-координата окна
UShort         // ширина клиентской части окна
UShort         // высота клиентской части окна

UChar          // SendVariant, возможные значения 1, 3, 4
UChar          // Длина строки с названием и классом связанного окна
Str            // Строка с названием и классом связанного окна
UChar          // Длина строки с Match Mode
Str            // Строка с Match Mode, возможные значения: "1", "2", "3", "RegEx"

UChar          // Длина строки с названием шрифта
Str            // Строка с названием шрифта
UChar          // Размер шрифта, возможные значения: 8 - 15
UChar          // Качество рендеринга, возможные значения 0 - 5
UInt           // Цвет шрифта в RGB

UChar          // Количество строк в ячейке
UChar          // Общее количество ячеек

UInt           // Цвет основного фона окна
UInt           // Цвет фона ячеек
UInt           // Цвет выделения ячеек
UInt           // Цвет области кнопок
UChar          // Стиль рамки, возможные значения:
               //   7 (SS_BLACKFRAME), 8 (SS_GRAYFRAME), 9 (SS_WHITEFRAME)
UChar          // AlwaysPlaceIntoClipboard, возможные значения 0 и 1
UChar          // HideAfterSend, возможные значения 0 и 1

UChar          // Длина строки с названием горячей клавиши для показа/скрытия окна
Str            // Строка с названием горячей клавиши для показа/скрытия окна

UChar          // LoadOnStartup, возможные значения 0 и 1
UChar          // ToggleToSaved, возможные значения 0 и 1

**********************************************************************************
*/

#NoEnv
#SingleInstance, Off

if FileExist(A_ScriptDir "\BufferIcons.dll")
   FileDelete, % A_ScriptDir "\BufferIcons.dll"

IcoDll := A_IsCompiled ? A_ScriptFullPath : A_ScriptDir "\ClipMgrIcons.dll"
if !FileExist(IcoDll)  {
   if !Ping("dropbox.com")  {
      MsgBox, Невозможно загрузить файл с иконками`, проверьте подключение к интернету!
      ExitApp
   }
   URLDownloadToFile, % "http://content.screencast.com/users/teadrinker/folders/Files/media/7d4f94ae-cdd3-467d-b3d1-786f5e8af91c/ClipMgrIcons.dll?downloadOnly=true", % IcoDll
}
if !A_IsCompiled
   Menu, Tray, Icon, %IcoDll%, 1
SetBatchLines, -1
SetWinDelay, 0
CoordMode, Mouse
StringCaseSense, Locale

global HTCAPTION := 2, HTSYSMENU := 3, SC_MAXIMIZE := 0xF030, SC_MINIMIZE := 0xF020
     , SS_BLACKFRAME := 0x7, SS_GRAYFRAME := 0x8, SS_WHITEFRAME := 0x9, WS_EX_NOACTIVATE := 0x8000000
     , IMAGE_CURSOR := 2, OCR_HAND := 32649, LR_SHARED := 0x8000, LR_DEFAULTSIZE := 0x40
     , WM_SETCURSOR := 0x20, WM_NCMOUSEMOVE := 0xA0, WM_VSCROLL := 0x115, WS_VSCROLL := 0x200000, WM_MOUSEMOVE := 0x200
     , TargetWindow, AssociatedWindow, MatchMode, SendVariant, ScrollHeight, LastActiveWindowID
     , AlwaysPlaceIntoClipboard, HideAfterSend, ColorCell, ColorSelect
     , FontName, FontSize, FontQuality, FontColor, RowNumber
     , hMain, hBack, hSetWindow, hSetFont, hSetNumber, hSetColor, hReadme, hSetHotkey
     , hCursorHand := LoadCursorHand(), hBall, TextH, DisplayHkPrev
     , Block := 1, TopWork := 5, VisibleCell := 8, AllCell, oSaved := [], oEntered := [], CurrentContent := "Saved"
     , oQual := [ "DEFAULT_QUALITY"          , "DRAFT_QUALITY"        , "PROOF_QUALITY"
                , "NONANTIALIASED_QUALITY", "ANTIALIASED_QUALITY", "CLEARTYPE_QUALITY" ]
   
SB_LINEUP := 0, SB_LINEDOWN := 1
HKM_SETHOTKEY := 0x401
HOTKEYF_ALT := 0x4, HOTKEYF_CONTROL := 0x2, HOTKEYF_EXT := 0x80, HOTKEYF_SHIFT := 0x1
EM_SETCUEBANNER := 0x1501, EM_SETSEL := 0xB1, OBJID_VSCROLL := 0xFFFFFFFB
STATE_SYSTEM_INVISIBLE := 0x8000
oFonts := GetCyrillicFontNames()

VarSetCapacity(SBI, 60)
NumPut(60, SBI)
DriveGet, FIleSystem, FS, % SubStr(A_ScriptDir, 1, 2)
DataFile := A_ScriptDir "\" . ((FIleSystem = "NTFS" && A_IsCompiled) ? A_ScriptName . ":" : "") . "data"
param = %1%

if !FileExist(DataFile)
   MatchMode := 2, SendVariant := 3
   , PrevFontName    := FontName    := "Arial"
   , PrevFontSize    := FontSize    := 10
   , PrevFontQuality := FontQuality := 5
   , PrevFontColor   := FontColor   := 0
   , PrevRowNumber   := RowNumber   := 3
   , PrevAllCell     := AllCell     := 24
   , PrevColorBack   := ColorBack   := 0xFFFFFF
   , PrevColorCell   := ColorCell   := 0xFFFFFF
   , PrevColorSelect := ColorSelect := 0xFFFEAF
   , PrevColorButton := ColorButton := 0xD0D0D0
   , PrevFrameStyle  := FrameStyle  := SS_GRAYFRAME
   , AlwaysPlaceIntoClipboard := HideAfterSend := 0
   , Hk := DisplayHkPrev := "Нет"
   , LoadOnStartup := 0
   , ToggleToSaved := 1
else
{
   oFile := FileOpen(DataFile, "r", "UTF-16-RAW")
   Loop % oFile.ReadUChar()
      bytes := oFile.ReadUInt()
      , oSaved.Insert(oFile.Read(bytes))
      
   Loop % oFile.ReadUChar()
      bytes := oFile.ReadUInt()
      , oEntered.Insert(oFile.Read(bytes))

   MainGuiX := oFile.ReadUSHort(), MainGuiY := oFile.ReadUSHort()
   MainGuiW := oFile.ReadUSHort(), MainGuiH := oFile.ReadUSHort()

   SendVariant := oFile.ReadUChar()
   bytes := oFile.ReadUChar()
   AssociatedWindow := oFile.Read(bytes)
   bytes := oFile.ReadUChar()
   MatchMode := oFile.Read(bytes)
   
   Fontbytes := oFile.ReadUChar()
   PrevFontName    := FontName    := oFile.Read(Fontbytes)
   PrevFontSize    := FontSize    := oFile.ReadUChar()
   PrevFontQuality := FontQuality := oFile.ReadUChar()
   PrevFontColor   := FontColor   := oFile.ReadUInt()
   
   PrevRowNumber   := RowNumber   := oFile.ReadUChar()
   PrevAllCell     := AllCell     := oFile.ReadUChar()
   
   PrevColorBack   := ColorBack   := oFile.ReadUInt()
   PrevColorCell   := ColorCell   := oFile.ReadUInt()
   PrevColorSelect := ColorSelect := oFile.ReadUInt()
   PrevColorButton := ColorButton := oFile.ReadUInt()
   PrevFrameStyle  := FrameStyle  := oFile.ReadUChar()
   
   AlwaysPlaceIntoClipboard := oFile.ReadUChar()
   HideAfterSend := oFile.ReadUChar()
   
   bytes := oFile.ReadUChar()
   Hk := oFile.Read(bytes)
   DisplayHkPrev := GetDisplayHk(Hk)
   
   LoadOnStartup := oFile.ReadUChar()
   ToggleToSaved := oFile.ReadUChar()
   
   oFile.Close()

   if (MainGuiX = "" || MainGuiX > A_ScreenWidth - 50)
      Error .= "Ошибка чтения данных MainGuiX`nБудет присвоено значение по умолчанию`n`n", MainGuiX := ""
   if (MainGuiY = "" || MainGuiY > A_ScreenHeight - 50)
      Error .= "Ошибка чтения данных MainGuiY`nБудет присвоено значение по умолчанию`n`n", MainGuiY := ""
   if (MainGuiW = "" || MainGuiW > A_ScreenWidth)
      Error .= "Ошибка чтения данных MainGuiW`nБудет присвоено значение по умолчанию`n`n", MainGuiW := ""
   if (MainGuiH = "" || MainGuiH > A_ScreenHeight)
      Error .= "Ошибка чтения данных MainGuiH`nБудет присвоено значение по умолчанию`n`n", MainGuiH := ""
   if SendVariant not in 1,3,4
      Error .= "Ошибка чтения данных SendVariant`nБудет присвоено SendVariant := 3 (Посылать в активное окно)`n`n", SendVariant := 3
   if MatchMode not in 1,2,3,RegEx
      Error .= "Ошибка чтения данных MatchMode`nБудет присвоено MatchMode := 2`n`n", MatchMode := 2
   for k, font in (oFonts)
      if (FontName = font && Success := 1)
         break
   if !Success
      Error .= "Ошибка чтения данных FontName`nБудет присвоено FontName := ""Arial""`n`n", FontName := "Arial"
   if (FontSize < 8 || FontSize > 15)
      Error .= "Ошибка чтения данных FontSize`nБудет присвоено FontSize := 10`n`n", FontSize := 10
   if FontQuality not between 1 and 5
      Error .= "Ошибка чтения данных FontQuality`nБудет присвоено FontQuality := 5`n`n", FontQuality := 5
   if (FontColor > 0xFFFFFF || FontColor = "")
      Error .= "Ошибка чтения данных FontColor`nБудет присвоено FontColor := 0x000000`n`n", FontColor := 0
   if RowNumber not between 1 and 5
      Error .= "Ошибка чтения данных RowNumber`nБудет присвоено RowNumber := 3`n`n", RowNumber := 3
   if AllCell not between 8 and 64
      Error .= "Ошибка чтения данных AllCell`nБудет присвоено AllCell := 24`n`n", AllCell := 24
   if (ColorBack > 0xFFFFFF || ColorBack = "")
      Error .= "Ошибка чтения данных ColorBack`nБудет присвоено ColorBack := 0xFFFFFF`n`n", ColorBack := 0xFFFFFF
   if (ColorCell > 0xFFFFFF || ColorCell = "")
      Error .= "Ошибка чтения данных ColorCell`nБудет присвоено ColorCell := 0xFFFFFF`n`n", ColorCell := 0xFFFFFF
   if (ColorSelect > 0xFFFFFF || ColorSelect = "")
      Error .= "Ошибка чтения данных ColorSelect`nБудет присвоено ColorSelect := 0xFFFEAF`n`n", ColorSelect := 0xFFFEAF
   if (ColorButton > 0xFFFFFF || ColorButton = "")
      Error .= "Ошибка чтения данных ColorButton`nБудет присвоено ColorButton := 0xD0D0D0`n`n", ColorButton := 0xD0D0D0
   if FrameStyle not in 7,8,9
      Error .= "Ошибка чтения данных FrameStyle`nБудет присвоено FrameStyle := 0x8`n`n", FrameStyle := 8
   if AlwaysPlaceIntoClipboard not in 0,1
      Error .= "Ошибка чтения данных AlwaysPlaceIntoClipboard`nБудет присвоено AlwaysPlaceIntoClipboard := 0`n`n", AlwaysPlaceIntoClipboard := 0
   if HideAfterSend not in 0,1
      Error .= "Ошибка чтения данных HideAfterSend`nБудет присвоено HideAfterSend := 0`n`n", HideAfterSend := 0
   if Hk = ""
      Error .= "Ошибка чтения данных Hk`nБудет присвоено Hk := ""Нет""`n`n", Hk := "Нет"
   if LoadOnStartup not in 0,1
   {
      LoadOnStartup := param = "Hide"
      Error .= "Ошибка чтения данных LoadOnStartup`nБудет присвоено LoadOnStartup := " . LoadOnStartup . "`n`n"
   }
   if ToggleToSaved not in 0,1
      Error .= "Ошибка чтения данных ToggleToSaved`nБудет присвоено ToggleToSaved := 1`n`n", ToggleToSaved := 1
   
   if Error
      MsgBox, % Trim(Error, "`n")
   Error := Success := ""
}

Menu, Tray, NoStandard
Menu, Tray, Add, Открыть новое окно, OpenNewWindow
Menu, Tray, Add, % "Скрыть окно" . (DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev), HideShowMainWindow
If !A_IsCompiled
   Menu, Tray, Add, Reload, Reload
Menu, Tray, Add, Выход, MainGuiClose

Menu, Actions, Add, Удалить, Delete
Menu, Actions, Icon, Удалить, %IcoDll%, 2
Menu, Actions, Add, Отправить             LButton, Send
Menu, Actions, Icon, Отправить             LButton, %IcoDll%, 3

Menu, Settings, Add, Указать связанное окно, SetAssociatedWindow
Menu, Settings, Add, Выбрать целевое окно правой кнопкой, SelectWindow
Menu, Settings, Add
Menu, Settings, Add, Посылать в связанное окно, SendToAssociatedWindow
Menu, Settings, Add, Посылать в целевое окно, SendToTargetWindow
Menu, Settings, Add, Посылать в активное окно, SendToActiveWindow
Menu, Settings, Add, Только помещать в буфер обмена, SendOnlyIntoClipboard
Menu, Settings, Check, % SendVariant = 1 ? "Посылать в связанное окно"
                       : SendVariant = 3 ? "Посылать в активное окно" : "Только помещать в буфер обмена"
Menu, Settings, Add
Menu, Settings, Add, Всегда помещать в буфер обмена, AlwaysPlaceIntoClipboard
if AlwaysPlaceIntoClipboard
   Menu, Settings, Check, Всегда помещать в буфер обмена

Menu, Settings, Add, Скрывать после вставки и вывода курсора за пределы окна, HideAfterSend
if HideAfterSend
   Menu, Settings, Check, Скрывать после вставки и вывода курсора за пределы окна

Menu, Settings, Add, Переключать на «Скопированное» при изменении буфера обмена, ToggleToSaved
if ToggleToSaved
   Menu, Settings, Check, Переключать на «Скопированное» при изменении буфера обмена

Menu, Settings, Add
Menu, Settings, Add, % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHkPrev, SetHotkey
Menu, Settings, Add
Menu, Settings, Add, Загружать при старте системы, LoadOnStartup
if LoadOnStartup
   Menu, Settings, Check, Загружать при старте системы
Menu, Settings, Add
Menu, Settings, Add, Readme, ShowReadme
Menu, Settings, Icon, Readme, %IcoDll%, 7

Menu, View, Add, Выбрать шрифт, SetFont
Menu, View, Icon, Выбрать шрифт, %IcoDll%, 4
Menu, View, Add, Количество строк и ячеек, SetNumber
Menu, View, Icon, Количество строк и ячеек, %IcoDll%, 5

Menu, View, Add, Настройки цвета, SetColor
Menu, View, Icon, Настройки цвета, %IcoDll%, 6

Menu, Content, Add, Скопированное, ShowSaved
Menu, Content, Add, Вставленное, ShowEntered
Menu, Content, Check, Скопированное

Menu, MenuBar, Add, Настройки, :Settings
Menu, MenuBar, Add, Вид, :View
Menu, MenuBar, Add, Содержимое, :Content

Gosub, CreateMainWindow

OnMessage(WM_SETCURSOR  , "WM_MOUSEMOVE")
OnMessage(WM_NCMOUSEMOVE, "WM_MOUSEMOVE")
OnMessage(0x0A1, "WM_NCLBUTTONDOWN")
OnMessage(0x0A2, "WM_NCLBUTTONUP")
OnMessage(0x0A3, "WM_NCLBUTTONDBLCLK")
OnMessage(0x102, "WM_CHAR")
OnMessage(0x112, "WM_SYSCOMMAND")
OnMessage(0x115, "WM_VSCROLL")
OnMessage(0x200, "WM_MOUSEMOVE")
OnMessage(0x205, "WM_RBUTTONUP")
OnMessage(0x207, "WM_MBUTTONDOWN")
OnMessage(0x404, "AHK_NOTIFYICON")
OnMessage(DllCall("RegisterWindowMessage", Str, "WM_INFO"), "WM_INFO")
SetTimer, Unblock, -1000
OnExit, SaveData
if (Hk = "Нет" || Hk = "")
   Return

try  {
   Hotkey, If
   Hotkey, %Hk%, HideShowMainWindow, On
}
catch e  {
   Menu, Settings, Rename, % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHkPrev
                         , % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHkPrev := "Нет"
   Menu, Tray, Rename, % "Скрыть окно" . (DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev), Скрыть окно
   MsgBox, 16, Ошибка установки горячей клавиши, % e.Message "`nГорячая клавиша """ . Hk . """ для показа/скрытия окна не установлена!"
}
Return

CreateMainWindow:
   Gui, Main: New, +hwndhMain +AlwaysOnTop +E%WS_EX_NOACTIVATE% +Owner +Resize +MinSize218x -DPIScale
   Gui, Main: Menu, MenuBar
   Gui, Main: Color, % Format("{:X}", ColorButton)

   Gui, Back: +ParentMain -Caption +Border +hwndhBack +%WS_VSCROLL% -DPIScale
   Gui, Back: Color, % Format("{:X}", ColorBack)
   Loop % AllCell
   {
      Gui, Work%A_Index%: Default
      Gui, +ParentBack -Caption -DPIScale
      Gui, Margin, 0, 0
      Gui, Color, % Format("{:X}", ColorCell)
      Gui, Add, Text, vBorder +%FrameStyle% x0 y0 w208
      Gui, Font, s%FontSize% q%FontQuality%, % FontName
         
      Gui, Add, Text, % "vText gSend_ x2 y2 w204 r" . RowNumber . " BackgroundTrans -Wrap c" . Format("{:X}", FontColor)
      GuiControlGet, Text, Pos
      GuiControl, Move, Border, % "h" TextH + 4
      Gui, Show, % "x5 y" 5 + (A_Index - 1) * (TextH + 8) " w208 h" TextH + 4 " Hide"
   }
   Gui, Back: Show, % "NA x-1 y0 w218 h" (TextH + 8)*VisibleCell + 6

   Gui, Main: Default
   Gui, Font, s10, Verdana
   Gui, Add, Button, % "x6 y" (TextH + 8)*VisibleCell + 18 " w101 h22", Clear
   Gui, Add, Button, gBlock vBlock x+5 yp wp hp, Block
   
   SetContent()
   GuiControl, Work1: Focus, Static1
   ((MainGuiW = "" || MainGuiH = "") && (MainGuiW := 218) && (MainGuiH := (TextH + 8)*VisibleCell + 40))
   Bool := (MainGuiX != "" && MainGuiY != "")
   Gui, Show, % (Bool ? "x" MainGuiX " y" MainGuiY " " : "") . (param = "Hide" ? "Hide" : "NA") . " w" MainGuiW " h" MainGuiH, Скопированное
   
   if (param = "Hide")  {
      TrayMenuPostFix := DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev
      Menu, Tray, Rename, % "Скрыть окно" . TrayMenuPostFix, % "Показать окно" . TrayMenuPostFix
      param := ""
   }
   Return

MainGuiSize:
   DllCall("GetScrollBarInfo", Ptr, hBack, UInt, OBJID_VSCROLL, Ptr, &SBI)
   , delta := NumGet(&SBI + 12, "UInt") - NumGet(&SBI + 4, "UInt")
   
   Gui, Back:Show, % "NA x-1 y0 w" A_GuiWidth - delta " h" A_GuiHeight - 36
   Loop % AllCell
   {
      Gui, Work%A_Index%: Default
      Gui, Show, % "NA x5 y" TopWork + (A_Index - 1) * (TextH + 8) " w" w := A_GuiWidth - 10
      GuiControlGet, Text
      if (Text = "")
         Gui, Show, Hide
      GuiControl, MoveDraw, Border, % "w" w " h" TextH + 4
      GuiControl, Move, Text, % "w" w - 4 " h" TextH
   }
   GuiControl, Main:MoveDraw, Button1, % "y" A_GuiHeight - 28
   GuiControl, Main:MoveDraw, Button2, % "x" A_GuiWidth - 101 - 6 " y" A_GuiHeight - 28
   UpdateScrollBar(hBack, ScrollHeight)
   GuiW := A_GuiWidth, GuiH := A_GuiHeight
   Return
   
ShowSaved:
ShowEntered:
   if (CurrentContent = SubStr(A_ThisLabel, 5))
      Return
   
   Menu, Content, UseErrorLevel
   Menu, Content, UnCheck, Вставленное
   Menu, Content, UnCheck, Скопированное
   Menu, Content, Check, % A_ThisMenuItem
   CurrentContent := SubStr(A_ThisLabel, 5)
   
   SetContent()
   Gui, Main:Show, NA, % A_ThisMenuItem
   Return

Reload:
   Reload
   Return

HideShowMainWindow:
   b := WinExist("ahk_id" hMain)
   Gui, Main:Show, % b ? "Hide" : "NA"
   TrayMenuPostFix := DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev
   Menu, Tray, Rename, % (b ? "Скрыть окно" : "Показать окно") . TrayMenuPostFix
                     , % (b ? "Показать окно" : "Скрыть окно") . TrayMenuPostFix
   Return

OpenNewWindow:
   Run, % A_ScriptFullPath,,, PID
   WinWait, ahk_pid %PID%
   WinGetPos, X, Y
   Loop 15
   {
      Sleep, 20
      WinMove, X - 15 < 0 ? (X + A_Index) : (X - A_Index), Y + A_Index
   }
   Return
   
SetFont:
   oDDL := GetDDLstrings(FontName, oFonts, FontQuality, FontSize)
   Gui, SetFont:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetFont -DPIScale
   Gui, SetFont:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma
   
   Gui, Add, Text,, Название шрифта:
   Gui, Add, DDL, vFontName x+25 yp-3 w153 r20 Sort, % oDDL.Font
   GuiControlGet, FontName, Pos

   Gui, Add, Text, x15 y+14, Качество рендеринга:
   Gui, Add, DDL, x%FontNameX% yp-3 w%FontNameW% vQuality, % oDDL.Qual
   
   Gui, Add, Text, x15 y+15, Размер шрифта:
   Gui, Add, DDL, x%FontNameX% yp-3 w38 vFontSize, % oDDL.Size
   
   Gui, Add, Button, vDef gDef x+5 yp-1 hp+2, Сбросить всё
   GuiControlGet, Def, Pos
   GuiControl, Move, Def, % "x" FontNameX + FontNameW - DefW
   Gui, Add, Button, % "vCancel x" FontNameX " y+20 hp w" FontNameW//2 - 2, Применить
   Gui, Add, Button, % "vOk x" FontNameX + FontNameW - (FontNameW//2 - 2) " yp hp wp Default", OK
   Gui, Add, Button, % "vApply x" FontNameX - FontNameW//2 - 4 " yp hp wp", Отмена
   
   ShowOwnerWindow("Шрифт", hSetFont)
   Return
   
Def:
   oDDL := GetDDLstrings("Arial", oFonts, 5, 10)
   Gui, SetFont: Default
   GuiControl,, FontName, % "|" . oDDL.Font
   GuiControl,, Quality , % "|" . oDDL.Qual
   GuiControl,, FontSize, % "|" . oDDL.Size
   Return
   
SetFontButtonПрименить:
SetFontButtonOK:
   Gui, SetFont:Submit, NoHide
   for k, v in oQual  {
      if (v = Quality)  {
         FontQuality := k - 1
         break
      }
   }

SetFontButtonОтмена:
SetFontGuiClose:
SetFontGuiEscape:
   if (A_ThisLabel != "SetFontButtonПрименить")
   {
      Gui, SetFont:Destroy
      WinActivate, ahk_id %LastActiveWindowID%
   }
   if (FontName = PrevFontName && FontSize = PrevFontSize && FontQuality = PrevFontQuality)
      Return
   
   if A_ThisLabel in SetFontButtonOK,SetFontButtonПрименить
   {
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetFont: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetFont: +OwnerMain
   }
   PrevFontName := FontName, PrevFontSize := FontSize, PrevFontQuality := FontQuality
   Return
   
SetNumber:
   oDDL := GetDDLstrings(RowNumber, AllCell)
   Gui, SetNumber:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetNumber -DPIScale
   Gui, SetNumber:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma

   Gui, Add, Text,, Количество строк в ячейке:
   Gui, Add, DDL, vRowNumber x+20 yp-4 w40, % oDDL.Rows
   GuiControlGet, RowNumber, Pos

   Gui, Add, Text, x15 y+13, Общее количество ячеек:
   Gui, Add, DDL, vAllCell x%RowNumberX% yp-4 w40, % oDDL.Cells
   
   ButtonWidth := (RowNumberX + RowNumberW - 10 - 15)//3
   Gui, Add, Button, % "x15 y+20 w" ButtonWidth + 1 " h23", Отмена
   Gui, Add, Button, x+5 yp wp hp, Применить
   Gui, Add, Button, x+5 yp wp hp Default, OK
   ShowOwnerWindow("Количество строк и ячеек", hSetNumber)
   Return
   
SetNumberButtonПрименить:
SetNumberButtonOK:
   Gui, SetNumber:Submit, NoHide

SetNumberButtonОтмена:
SetNumberGuiClose:
SetNumberGuiescape:
   if (A_ThisLabel != "SetNumberButtonПрименить")
   {
      Gui, SetNumber: Destroy
      WinActivate, ahk_id %LastActiveWindowID%
   }
   if (RowNumber = PrevRowNumber && PrevAllCell = AllCell)
      Return
   
   if A_ThisLabel in SetNumberButtonOK,SetNumberButtonПрименить
   {
      oSaved.Remove(AllCell + 1, oSaved.MaxIndex())
      oEntered.Remove(AllCell + 1, oEntered.MaxIndex())
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetNumber: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetNumber: +OwnerMain
   }
   PrevRowNumber := RowNumber, PrevAllCell := AllCell
   Return
   
ControlsCreate(var, text)
{
   global
   Gui, Add, Text, x15 y+15, % text
   Gui, Add, Edit, v%var% gChangeColor x+8 yp-3 w62 Limit6 Uppercase Center, % Format("{:06X}", %var%)
   GuiControlGet, %var%, Pos
   GuiControl, Move, % var, x%ColorBackX%
   ColoredGuiShow(var, "SetColor", %var%, "ColorChoose", ColorBackW, ColorBackH)
}
   
SetColor:
   Gui, SetColor:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetColor -DPIScale
   Gui, SetColor:Default
   Gui, Margin, 15, 15
   Gui, Font, s8, Tahoma
   
   ControlsCreate("ColorBack"  , "Цвет общего фона ячеек:")
   ControlsCreate("ColorCell"  , "Цвет фона ячейки:")
   ControlsCreate("ColorSelect", "Цвет выделения ячейки:")
   ControlsCreate("FontColor"  , "Цвет шрифта:")
   ControlsCreate("ColorButton", "Цвет фона кнопок:")
   
   Gui, Add, Text, x15 y+15, Цвет рамки ячейки:
   Gui, Add, Radio, % "x+15 yp" . (FrameStyle = SS_BLACKFRAME ? " Checked" : "") . " vRadio", Black
   Gui, Add, Radio, % "x+5 yp"  . (FrameStyle = SS_GRAYFRAME  ? " Checked" : ""), Gray
   Gui, Add, Radio, % "x+5 yp"  . (FrameStyle = SS_WHITEFRAME ? " Checked" : ""), White
   GuiControlGet, White, Pos, White
   
   Gui, Add, Button, vApply x15 y+20, Применить
   Gui, Add, Button, xp yp wp, Сбросить
   Gui, Add, Button, vCancel x+3 yp wp, Отмена
   GuiControlGet, Cancel, Pos
   GuiControl, Move, Apply, % "x" CancelX + CancelW + 3
   
   Gui, Add, Button, % "vOK x" CancelX + CancelW*2 + 6 " yp wp Default", OK
   GuiControlGet, OK, Pos
   
   ControlMetr := WhiteX + WhiteW > OKX + OKW ? "White" : "OK"
   ColorGuis := "ColorBack|ColorCell|ColorSelect|FontColor|ColorButton"
   Loop, parse, ColorGuis, |
      Gui, %A_LoopField%:Show, % "NA x" %ControlMetr%X + %ControlMetr%W - %A_LoopField%W - 1 " y" %A_LoopField%Y
   
   GuiControl, Focus, Static1
   ShowOwnerWindow("Настройки цвета в RGB", hSetColor)
   Return

ChangeColor:
   GuiControlGet, Color, SetColor:, %A_GuiControl%
   Gui, %A_GuiControl%: Color, % "0x" Color
   Return
   
ColorChoose:
   GuiControlGet, Color, SetColor:, %A_Gui%
   if (Color := ChooseColor("0x" . Color, hSetColor)) = -1
      Return
   
   Gui, %A_Gui%:Color, % Color
   GuiControl, SetColor:, %A_Gui%, % SubStr("00000" . SubStr(Color, 3), -5)
   Return
   
SetColorButtonСбросить:
   Gui, SetColor:Default
   GuiControl,, Edit1, FFFFFF
   GuiControl,, Edit2, FFFFFF
   GuiControl,, Edit3, FFFEAF
   GuiControl,, Edit4, 000000
   GuiControl,, Edit5, D0D0D0
   GuiControl,, Gray, 1
   Return

SetColorButtonОтмена:
SetColorGuiEscape:
SetColorGuiClose:
   Gui, SetColor:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return

SetColorButtonOK:
SetColorButtonПрименить:
   Gui, SetColor:Submit, NoHide
   Loop, parse, ColorGuis, |
      %A_LoopField% := "0x" . %A_LoopField%
   FrameStyle := [SS_BLACKFRAME, SS_GRAYFRAME, SS_WHITEFRAME][Radio]
   
   if ( ColorBack   != PrevColorBack
     || ColorCell   != PrevColorCell
     || ColorSelect != PrevColorSelect
     || FontColor   != PrevFontColor
     || ColorButton != PrevColorButton
     || FrameStyle  != PrevFrameStyle )
   {
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetColor: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetColor: +OwnerMain
      PrevColorBack := ColorBack, PrevColorCell := ColorCell, PrevColorSelect := ColorSelect
      PrevFontColor := FontColor, PrevColorButton := ColorButton, PrevFrameStyle := FrameStyle
   }
   
   if (A_ThisLabel = "SetColorButtonOK")
      Gosub, SetColorGuiClose
   Return
   
SetAssociatedWindow:
   Gui, SetWindow:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetWindow -DPIScale
   Gui, SetWindow:Default
   Gui, Margin, 12, 12
   Gui, Add, Link, y10, Укажите <a href="http://ahkscript.org/docs/misc/WinTitle.htm">заголовок и класс</a> связанного окна:
   Gui, Add, Edit, y+10 wp h21 hwndhEdit vAssociatedWindow

   Gui, Add, Link, y+25, <a href="http://ahkscript.org/docs/commands/SetTitleMatchMode.htm">Match Mode:</a>
   Gui, Add, Radio, vMatchMode x+15 yp, 1
   GuiControlGet, Radio1, pos, Button1
   
   Gui, Add, Radio, x+7 yp, 2
   Gui, Add, Radio, x+7 yp, 3
   Gui, Add, Radio, x+7 yp, RegEx
   oRadio := {1: "Button1", 2: "Button2", 3: "Button3", RegEx: "Button4"}
   GuiControl,, % oRadio[MatchMode], 1
   GuiControlGet, Radio4, pos, Button4
   
   Gui, Add, GroupBox, % "x" (xGr := Radio1X - 9) " y" Radio1Y - 17
                      . " w" (wGr := Radio4X - Radio1X + 9 + Radio4W + 4) " h" Radio4H + 29
   GuiControl, Move, Edit1, % "w" xGr - 12 + wGr
   
   Gui, Add, Button, % "vSetWindow x" xGr + wGr - 90 " y+12 w90 h23 Default", OK
   Gui, Add, Button, gSetWindowGuiCancel xp-95 yp wp hp, Указать позже
   GuiControl, Focus, Button2
   if !AssociatedWindow
      PostMessage, EM_SETCUEBANNER, 0, " Блокнот ahk_class Notepad",, ahk_id %hEdit%
   else
   {
      GuiControl,, AssociatedWindow, % AssociatedWindow
      PostMessage, EM_SETSEL, 0, -1,, ahk_id %hEdit%
      GuiControl, Focus, Edit1
   }
   
   ShowOwnerWindow("Связанное окно", hSetWindow)
   Return
   
SetWindowButtonOK:
   Gui, SetWindow:Submit, NoHide
   (MatchMode = 4 && MatchMode := "RegEx")
   if (AssociatedWindow = "")
   {
      MsgBox, 4144,, Укажите связанное окно в поле редактирования!
      Return
   }
   Gosub, SendToAssociatedWindow

SetWindowGuiEscape:
SetWindowGuiCancel:
SetWindowGuiClose:
   Gui, SetWindow:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return
   
SendToAssociatedWindow:
SendToTargetWindow:
SendToActiveWindow:
SendOnlyIntoClipboard:
   oSendVariant := { SendToAssociatedWindow: {Variant: 1, Menu: "Посылать в связанное окно"}
                   , SendToTargetWindow:     {Variant: 2, Menu: "Посылать в целевое окно"}
                   , SendToActiveWindow:     {Variant: 3, Menu: "Посылать в активное окно"}
                   , SendOnlyIntoClipboard:  {Variant: 4, Menu: "Только помещать в буфер обмена"} }
   
   SendVariant := oSendVariant[A_ThisLabel].Variant
   for k, v in oSendVariant
      Menu, Settings, % k = A_ThisLabel ? "Check" : "UnCheck", % v.Menu
   Return

Send_:
   Send(A_Gui)
   Return

SelectWindow:
   CrossHair(), CrossHair := 1
   Hotkey, If
   Hotkey, RButton Up, Choice, On
   Return
   
Choice:
   CrossHair(0), CrossHair := ""
   Hotkey, RButton Up, Off
   MouseGetPos,,, TargetWindow
   if (TargetWindow = hMain)
      Goto, SendToActiveWindow
   Send, {Click}
   Gosub, SendToTargetWindow
   TargetWndShow(TargetWindow)
   Return
   
AlwaysPlaceIntoClipboard:
   Menu, Settings, ToggleCheck, Всегда помещать в буфер обмена
   AlwaysPlaceIntoClipboard := !AlwaysPlaceIntoClipboard
   Return
   
HideAfterSend:
   Menu, Settings, ToggleCheck, Скрывать после вставки и вывода курсора за пределы окна
   HideAfterSend := !HideAfterSend
   Return
   
ToggleToSaved:
   Menu, Settings, ToggleCheck, Переключать на «Скопированное» при изменении буфера обмена
   ToggleToSaved := !ToggleToSaved
   Return
   
SetHotkey:
   PrevHk := Hk, wParam := "", HIBYTE := 0, oMods := {"!": HOTKEYF_ALT, "+": HOTKEYF_SHIFT, "^": HOTKEYF_CONTROL}
   RegExMatch(Hk, "^(([!\+\^]+)|)(.*)", match)
   Loop, parse, match2
      HIBYTE |= oMods[A_LoopField]
   wParam := (HIBYTE << 8)|GetKeyVK(match3)
   
   Gui, SetHotkey:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetHotkey -DPIScale
   Gui, SetHotkey:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma
   
   Gui, Add, Text,, Введите горячую клавишу:
   Gui, Add, Hotkey, vHk gHk hwndhHk x+15 yp-3 w110
   GuiControlGet, Hk, Pos
   Gui, Add, Text, x15 y+7, Или напечатайте
   Gui, Add, Link, x15 y+2, в <a href="http://ahkscript.org/docs/Hotkeys.htm">AHK-совместимом формате</a>:
   Gui, Add, Edit, vEdit x%HkX% yp-8 w%HkW%
   
   Gui, Add, Button, vCancel x15 y+17, Отмена
   GuiControlGet, Cancel, Pos
   (CancelW < 80 && CancelW := 80)
   
   Gui, Add, Button, x15 yp, OK
   GuiControl, Move, OK, % "x" HkX + HkW - CancelW " w" CancelW
   GuiControl, Move, Cancel, % "x" HkX + HkW - CancelW*2 - 5 " w" CancelW
   
   ShowOwnerWindow("Горячая клавиша для показа/скрытия окна", hSetHotkey)

   SendMessage, HKM_SETHOTKEY, wParam,,, ahk_id %hHk%
   GuiControlGet, Hk
   Gosub, Hk
   Return
   
Hk:
   RegExMatch(Hk, "[A-Z]$", found)
   StringReplace, Hk, Hk, % found, % "vk" . Format("{:X}", GetKeyVK(found))
   RegExMatch(Hk, "Numpad.+$", found)
   StringReplace, Hk, Hk, % found, % "sc" . Format("{:X}", GetKeySC(found))
   GuiControl, SetHotkey:, Edit, % Hk
   Return

SetHotkeyButtonOK:
   GuiControlGet, Edit, SetHotkey:
   if InStr(Edit, " & ")  {
      MsgBox, Горячие клавиши с символом "&" не поддерживаются!
      Return
   }
   try  {
      Hotkey, If
      Hotkey, %Edit%, HideShowMainWindow, On
   }
   catch e  {
      Hk := PrevHk
      MsgBox, 16, Ошибка установки горячей клавиши, % e.Message "`nНовая горячая клавиша для показа/скрытия окна не установлена!"
                                                              . "`nОстаётся в силе горячая клавиша """ . DisplayHkPrev . """"
      Return
   }
   
   Hk := Edit
   if !(PrevHk = "Нет" || PrevHk = "" || PrevHk = Hk)
      Hotkey, %PrevHk%, Off
   DisplayHk := GetDisplayHk(Edit)
   Menu, Settings, UseErrorLevel
   Menu, Settings, Rename, % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHkPrev
                         , % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHk
   PrevTrayMenuName := WinExist("ahk_id" hMain) ? "Скрыть окно" : "Показать окно"
   Menu, Tray, Rename, % PrevTrayMenuName . (DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev)
                     , % PrevTrayMenuName . (DisplayHk = "Нет" ? "" : A_Tab . DisplayHk)
   DisplayHkPrev := DisplayHk
   
SetHotkeyButtonОтмена:
   if (A_ThisLabel = "SetHotkeyButtonОтмена")
      Hk := PrevHk
   Gui, SetHotkey:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return
   
LoadOnStartup:
   if !(LoadOnStartup := !LoadOnStartup)
      FileDelete, %A_Startup%\ClipboardMgr.lnk
   else  {
      try FileCreateShortcut, % A_ScriptFullPath, %A_Startup%\ClipboardMgr.lnk,, Hide
      catch  {
         MsgBox, 16, Ошибка создания ярлыка, Не удалось создать ярлык в папке автозагрузки
         LoadOnStartup := 0
         Return
      }
   }
   Menu, Settings, ToggleCheck, Загружать при старте системы
   Return
   
MainButtonClear:
   o%CurrentContent% := [], SetContent()
   Return

Block:
   GuiControl,, Block, % (Block := !Block) ? "Unblock" : "Block"
   Return

Unblock:
   Block := ""
   Return

OnClipboardChange:
   if Block || (Clipboard == oSaved[1] && StrLen(Clipboard) = StrLen(oSaved[1])) || A_EventInfo != 1
      Return
   
   RemoveExistingAddNew(oSaved, Clipboard)
   if (CurrentContent = "Entered")  {
      (ToggleToSaved && ChangeContent())
      Return
   }
   
   SetContent()
   Return

MainGuiClose:
   ExitApp
   
ShowReadme:
   Gui, Readme:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhReadme -DPIScale
   Gui, Readme:Default
   Gui, Color, White
   Gui, Margin, 0, 0
   Gui, Font, s9, Tahoma
   Gui, Add, Edit, ReadOnly, % Readme
   ShowOwnerWindow("Readme", hReadme)
   PostMessage, EM_SETSEL, 0, 0, Edit1
   Return
   
ReadmeGuiClose:
   Gui, Readme:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return
   
SaveData:
   DllCall("DestroyCursor", Ptr, hCursorHand)
   (CrossHair && CrossHair(0))
   
   oFile := FileOpen(DataFile, "w", "UTF-16-RAW")
   oFile.WriteUChar(oSaved.MaxIndex())
   for k, v in oSaved
      oFile.WriteUInt(StrLen(oSaved[A_Index]))
      , oFile.Write(oSaved[A_Index])
      
   oFile.WriteUChar(oEntered.MaxIndex())
   for k, v in oEntered
      oFile.WriteUInt(StrLen(oEntered[A_Index]))
      , oFile.Write(oEntered[A_Index])
   
   DetectHiddenWindows, On
   WinGetPos, X, Y,,, ahk_id %hMain%
   oFile.WriteUSHort(X), oFile.WriteUSHort(Y)
   oFile.WriteUSHort(GuiW), oFile.WriteUSHort(GuiH)
   
   (SendVariant = 2 && SendVariant := 3)
   oFile.WriteUChar(SendVariant)
   
   oFile.WriteUChar(StrLen(AssociatedWindow))
   oFile.Write(AssociatedWindow)
   
   oFile.WriteUChar(StrLen(MatchMode))
   oFile.Write(MatchMode)
   
   oFile.WriteUChar(StrLen(FontName))
   oFile.Write(FontName)
   
   oFile.WriteUChar(FontSize)
   oFile.WriteUChar(FontQuality)
   oFile.WriteUInt(FontColor)
   
   oFile.WriteUChar(RowNumber)
   oFile.WriteUChar(AllCell)
   
   oFile.WriteUInt(ColorBack)
   oFile.WriteUInt(ColorCell)
   oFile.WriteUInt(ColorSelect)
   oFile.WriteUInt(ColorButton)
   oFile.WriteUChar(FrameStyle)
   oFile.WriteUChar(AlwaysPlaceIntoClipboard)
   oFile.WriteUChar(HideAfterSend)

   oFile.WriteUChar(StrLen(Hk))
   oFile.Write(Hk)
   
   oFile.WriteUChar(LoadOnStartup)
   oFile.WriteUChar(ToggleToSaved)
   
   oFile.Close()
   ExitApp

#If MouseOverGui()
WheelDown::
WheelUp::
   Loop 4
      WM_VSCROLL(A_ThisHotkey = "WheelUp" ? SB_LINEUP : SB_LINEDOWN, 0, WM_VSCROLL, hBack)
   Return
   
WM_MOUSEMOVE(wp, lp, msg)
{
   static PrevColored, PrevGui
   
   if (msg = WM_MOUSEMOVE && ColorCell != ColorSelect)
   {
      if (A_Gui = PrevGui)
         Return
      
      if InStr(A_Gui, "Work")
      {
         if PrevColored
            Gui, %PrevColored%: Color, % Format("{:X}", ColorCell)
         Gui, %A_Gui%: Color, % Format("{:X}", ColorSelect)
         PrevColored := A_Gui
         SetTimer, WhiteColor, 100
      }
      else if PrevColored
      {
         Gui, %PrevColored%: Color, % Format("{:X}", ColorCell)
         PrevColored := ""
      }
      PrevGui := A_Gui
   }
   
   if (msg = WM_NCMOUSEMOVE)
   {
      if PrevColored  {
         Gui, %PrevColored%: Color, % Format("{:X}", ColorCell)
         PrevColored := PrevGui := ""
      }
      
      (wp = HTSYSMENU && DllCall("SetCursor", Ptr, hCursorHand))
   }
   
   if (msg = WM_SETCURSOR)
      if A_Gui in ColorBack,ColorCell,ColorSelect,FontColor,ColorButton
         Return DllCall("SetCursor", Ptr, hCursorHand)
   Return
   
WhiteColor:
   MouseGetPos,,, WinID
   if (WinID != hMain)
   {
      SetTimer, WhiteColor, Off
      if PrevColored
         Gui, %PrevColored%: Color, % Format("{:X}", ColorCell)
      PrevColored := ""
   }
   Return
}

WM_NCLBUTTONDOWN(wp)
{
   static HTMENU := 5, HTMINBUTTON := 8, HTLEFT := 10, HTRIGHT := 11
      , HTTOP := 12, HTTOPLEFT := 13, HTTOPRIGHT := 14, HTBOTTOM := 15
      , HTBOTTOMLEFT := 16, HTBOTTOMRIGHT := 17, HTCLOSE := 20
      
   if (A_Gui != "Main")
      Return
   
   if wp in %HTCAPTION%,%HTLEFT%,%HTRIGHT%,%HTTOP%,%HTBOTTOM%,%HTTOPLEFT%,%HTTOPRIGHT%,%HTBOTTOMLEFT%,%HTBOTTOMRIGHT%
   {
      LastActiveWindowID := WinExist("A")
      SetTimer, MouseWatch, 100
      WinActivate, ahk_id %hMain%
      if (wp != HTCAPTION)
      {
         ControlGetPos,, TopWork,,, AutoHotkeyGUI1, ahk_id %hBack%
         TopWork -= 1
      }
   }
   if (wp = HTMENU)
   {
      LastActiveWindowID := WinExist("A")
      WinActivate, ahk_id %hMain%
      SendInput, {LButton Down}
      Sleep, 100
      if WinExist("ahk_id" hSetWindow) || WinExist("ahk_id" hSetFont) || WinExist("ahk_id" hSetNumber)
       || WinExist("ahk_id" hSetColor) || WinExist("ahk_id" hReadme) || WinExist("ahk_id" hSetHotkey)
         WinActivate
      else
         WinActivate, ahk_id %LastActiveWindowID%
   }
   if (wp = HTMINBUTTON)
   {
      Gosub, HideShowMainWindow
      Return 1
   }
   if (wp = HTSYSMENU)
      Return 1
   
   if (wp = HTCLOSE && A_Gui = "Main")
      ExitApp
   Return
   
MouseWatch:
   if GetKeyState("LButton", "P")
      Return
   SetTimer, MouseWatch, Off
   WinActivate, ahk_id %LastActiveWindowID%
   Return
}

WM_NCLBUTTONUP(wp)
{
   (wp = HTSYSMENU && ChangeContent() && DllCall("SetCursor", Ptr, hCursorHand))
}

WM_NCLBUTTONDBLCLK(wp)
{
   if (A_Gui = "Main" && wp = HTSYSMENU)
      Return 1
   
   if (A_Gui != "Main" || wp != HTCAPTION)
      Return
   
   WM_SYSCOMMAND(SC_MAXIMIZE)
   Return 1
}

WM_CHAR(wp)
{
   CoordMode, Caret
   WinClose, ahk_id %hBall%
   if (A_Gui != "SetColor")
      Return
   
   GuiControlGet, Focus, %A_Gui%:Focus
   if !InStr(Focus, "Edit")
      Return

   if wp in 3,8,24,26   ; обработка Ctrl + C, BackSpace, Ctrl + X, Ctrl + Z
      Return

   if wp = 22   ; обработка Ctrl + V
   {
      Loop, parse, Clipboard
      {
         Text .= A_LoopField
         if A_LoopField not in 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,A,B,C,D,E,F
         {
            ShowBall("Буфер обмена содержит недопустимые символы."
               . "`nДопустимые символы:`n0123456789ABCDEF", "Ошибка!")
            Return 1
         }
      }
      Control, EditPaste, % Text, % Focus, Настройки цвета в RGB
      Return 1
   }

   Char := Chr(wp)
   if Char not in 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,A,B,C,D,E,F
   {
      ShowBall("Допустимые символы:`n0123456789ABCDEF", Char " — недопустимый символ")
      Return 1
   }
   Return
}

WM_SYSCOMMAND(wp)
{
   static YPrev, HPrev, SB_TOP := 6
   if (A_Gui != "Main")
      Return
   
   WinGetPos,,,,  H_Tray, ahk_class Shell_TrayWnd
   if (wp = SC_MAXIMIZE)
   {
      WinExist("ahk_id" hMain)
      WinGetPos,, Y,, H
      WM_VSCROLL(SB_TOP, 0, WM_VSCROLL, hBack)
      if (Y = 0 && H = A_ScreenHeight - H_Tray && YPrev != "" && HPrev != "")
         WinMove,,,, YPrev,, HPrev
      else
      {
         YPrev := Y, HPrev := H
         WinMove,,,, 0,, A_ScreenHeight - H_Tray
      }
      Return 1
   }
   
   if (wp = SC_MINIMIZE)
   {
      Gosub, HideShowMainWindow
      Return 1
   }
}

WM_VSCROLL(wParam, lParam, msg, hwnd)
{
   static SIF_ALL=0x17, SCROLL_STEP=10
   bar := msg=0x115 ; SB_HORZ=0, SB_VERT=1

   VarSetCapacity(si, 28, 0)
   NumPut(28, si, "UInt") ; cbSize
   NumPut(SIF_ALL, si, 4, "UInt") ; fMask
   if !DllCall("GetScrollInfo", Ptr, hwnd, Int, bar, Ptr, &si)
     return

   VarSetCapacity(rect, 16)
   DllCall("GetClientRect", Ptr, hwnd, Ptr, &rect)

   new_pos := NumGet(si, 20, "UInt") ; nPos

   action := wParam & 0xFFFF
   if action = 0 ; SB_LINEUP
     new_pos -= SCROLL_STEP
   else if action = 1 ; SB_LINEDOWN
     new_pos += SCROLL_STEP
   else if action = 2 ; SB_PAGEUP
     new_pos -= NumGet(rect, 12, "Int") - SCROLL_STEP
   else if action = 3 ; SB_PAGEDOWN
     new_pos += NumGet(rect, 12, "Int") - SCROLL_STEP
   else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
     new_pos := wParam>>16
   else if action = 6 ; SB_TOP
     new_pos := NumGet(si, 8, "Int") ; nMin
   else if action = 7 ; SB_BOTTOM
     new_pos := NumGet(si, 12, "Int") ; nMax
   else
     return

   min := NumGet(si, 8, "Int") ; nMin
   max := NumGet(si, 12, "Int") - NumGet(si, 16, "UInt") ; nMax-nPage
   new_pos := new_pos > max ? max : new_pos
   new_pos := new_pos < min ? min : new_pos

   old_pos := NumGet(si, 20, "Int") ; nPos

   y := old_pos-new_pos
   ; Scroll contents of window and invalidate uncovered area.
   DllCall("ScrollWindow", Ptr, hwnd, Int, 0, Int, y, UInt, 0, UInt, 0)

   ; Update scroll bar.
   NumPut(new_pos, si, 20, "Int") ; nPos
   DllCall("SetScrollInfo", Ptr, hwnd, Int, bar, Ptr, &si, Int, 1)
}

WM_RBUTTONUP()
{
   static GuiNum, GuiName
   if !InStr(A_Gui, "Work")
      Return
   
   GuiNum := SubStr(A_Gui, 5)
   Menu, Actions, Show
   Return
   
Delete:
   o%CurrentContent%.Remove(GuiNum)
   SetContent()
   Return
   
Send:
   Send(GuiName)
   Return
}

Send(GuiName)
{
   PrevBlock := Block, Block := 1
   SendText := o%CurrentContent%[SubStr(GuiName, 5)]
   if !(oEntered[1] == SendText)  {
      RemoveExistingAddNew(oEntered, SendText)
      if (CurrentContent = "Entered")
         SetContent()
   }
   
   if (SendVariant = 1)
   {
      if AssociatedWindow  {
         SetTitleMatchMode, % MatchMode
         IfWinExist, % AssociatedWindow
            WinActivate
         else
            Return ToolTipShowAndDel("Связанное окно не найдено! Укажите связанное окно.")
      }
      else
         Return ToolTipShowAndDel("Связанное окно не указано!")
   }
   
   if (SendVariant = 2)
   {
      if TargetWindow  {
         IfWinExist, ahk_id %TargetWindow%
            WinActivate
         else
            Return ToolTipShowAndDel("Целевое окно не найдено! Измените выбор.")
      }
      else
         Return ToolTipShowAndDel("Целевое окно не указано!")
   }
   
   if (SendVariant = 3 && WinExist("A") = hMain)
      Return ToolTipShowAndDel("Активное окно не найдено!")
   
   if (SendVariant = 4)
   {
      Clipboard := SendText
      Sleep, 200
      Block := PrevBlock
      ToolTipShowAndDel("Текст помещён в буфер обмена", 800)
   }

   if SendVariant in 1,2,3
   {
      TempClip := ClipboardAll
      Clipboard := SendText
      Sleep, 100
      SendInput, ^{vk56}   ; Ctrl + V
      if !AlwaysPlaceIntoClipboard  {
         Sleep, 100
         Clipboard := TempClip
      }
      Sleep, 200
      Block := PrevBlock, TempClip := ""
   }
   
   if HideAfterSend
      SetTimer, MouseTrack, 100
   Return
   
MouseTrack:
   MouseGetPos,,, WinID
   if (WinID != hMain)  {
      Gui, Main:Show, Hide
      TrayMenuPostFix := DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev
      Menu, Tray, Rename, % "Скрыть окно" . TrayMenuPostFix, % "Показать окно" . TrayMenuPostFix
      SetTimer, MouseTrack, Off
   }
   Return
}

ToolTipShowAndDel(text, time = 1500)
{
   ToolTip % text
   Sleep time
   ToolTip
}

WM_MBUTTONDOWN()
{
   if !InStr(A_Gui, "Work")
      Return
   
   ToolTip, % RegExReplace(o%CurrentContent%[SubStr(A_Gui, 5)], A_Tab, "    ")
   KeyWait, MButton
   ToolTip
}

AHK_NOTIFYICON(wp, lp)
{
   static WM_LBUTTONUP := 0x202, WM_LBUTTONDBLCLK := 0x203, Time
   if (lp = WM_LBUTTONUP)
   {
      if (Time && A_TickCount - Time < 350)
         Return 1
      Gosub, HideShowMainWindow
      Time := A_TickCount
   }
   
   if (lp = WM_LBUTTONDBLCLK)
      Return 1
}

WM_INFO(wp, lp)
{
   (wp = 0 && PrevBlock := Block)
   if !(lp = "")  {
      Block := lp
      GuiControl, Main:, Block, % Block ? "Unblock" : "Block"
   }
   Return PrevBlock
}

UpdateScrollBar(hGui, ScrollHeight)
{
   static SIF_RANGE := 0x1, SIF_PAGE := 0x2, SB_VERT := 1
   i := 0
   Loop % AllCell
   {
      GuiControlGet, Visible, Work%A_Index%:Visible, Text
      (Visible && i++)
   } until !Visible
   
   WinGetPos,,,, GuiHeight, ahk_id %hGui%
   VarSetCapacity(si, 28, 0)
   NumPut(28, si) ; cbSize
   NumPut(SIF_RANGE | SIF_PAGE, si, 4)
   NumPut(ScrollHeight, si, 12) ; nMax
   NumPut(GuiHeight, si, 16) ; nPage
   DllCall("SetScrollInfo", Ptr, hGui, UInt, SB_VERT, Ptr, &si, UInt, 1)

   ControlGetPos,, Top,,, AutoHotkeyGUI1, ahk_id %hBack%
   Top -= 6
   if (Top >= 0)
      Return
   
   ControlGetPos,, y,, h, AutoHotkeyGUI%i%, ahk_id %hBack%
   Bottom := y + h + 6
   if (Top < 0 && Bottom < GuiHeight)
   {
      y := (a := Abs(Top)) > (b := GuiHeight-Bottom) ? b : a
      DllCall("ScrollWindow", Ptr, hGui, Int, 0, Int, y, Ptr, 0, Ptr, 0)
      TopWork += y
   }
}

CrossHair(OnOff=1)
{
   static IDC_CROSS := 32515, SPI_SETCURSORS := 0x57
      , hCursor := DllCall("LoadCursor", Ptr, 0, UInt, IDC_CROSS, Ptr)
      , sys_cursors := [32512,32513,32514,32516,32642,32643,32644,32645,32646,32648,32649,32650]
   
   if !OnOff
      DllCall("SystemParametersInfo", UInt, SPI_SETCURSORS, UInt, 0, UInt, 0, UInt, 0)
   else
      for k, cursor in sys_cursors
         hCopy := DllCall("CopyImage", Ptr, hCursor, UInt, 2, Int, 0, Int, 0, UInt, 0)
         , DllCall("SetSystemCursor", Ptr, hCopy, UInt, cursor)
}

TargetWndShow(TargetWnd)
{
   VarSetCapacity(WI, 60)
   DllCall("GetWindowInfo", Ptr, TargetWnd, Ptr, &WI)
   X := NumGet(WI, 20, "UInt"), Y := NumGet(WI, 24, "UInt")
   W := NumGet(WI, 28, "UInt") - X, H := NumGet(WI, 32, "UInt") - Y
   Gui, Flash:Default
   Gui, +LastFound -Caption +AlwaysOnTop +Disabled +Owner +hwndID3
   Gui, Color, Red
   Gui, Show, x%X% y%Y% w%W% h%H% hide
   th := 5, w1 := w - th, h1 := h - th
   WinSet, Region, 0-0 %w%-0 %w%-%h% 0-%h% 0-0 %th%-%th% %w1%-%th% %w1%-%h1% %th%-%h1% %th%-%th%
   Loop 3
   {
      Gui, Show, NA
      Sleep, 300
      Gui, Show, Hide
      Sleep, 300
   }
   Gui, Destroy
}

MouseOverGui()
{
   MouseGetPos,,, ID
   Return ID = hMain
}

ShowBall(Text, Title="")
{
   global
   WinClose, ahk_id %hBall%
   hBall := TrackToolTip(Text, A_CaretX+1, A_CaretY+15, Title)
   SetTimer, BallDestroy, -2000
   Return

BallDestroy:
   WinClose, ahk_id %hBall%
   Return
}

ChooseColor(Color := 0, hWnd := 0, Flags := 3)   ; Flags := CC_RGBINIT=1 | CC_FULLOPEN=2
{
   static CustColors, CHOOSECOLOR
   Color := RGB_BGR(Color)   ; RGB -> BGR

   if !CHOOSECOLOR
   {
      VarSetCapacity(CustColors, 64)
      Loop 16
         NumPut(0xFFFFFF, CustColors, (A_Index - 1)*4, "UInt")

      VarSetCapacity(CHOOSECOLOR, Size := A_PtrSize*9, 0)
      NumPut(Size, CHOOSECOLOR)
      NumPut(&CustColors, CHOOSECOLOR, A_PtrSize*4)
   }
   NumPut(Flags, CHOOSECOLOR, A_PtrSize*5)
   NumPut(hWnd, CHOOSECOLOR, A_PtrSize)
   NumPut(Color, CHOOSECOLOR, A_PtrSize*3)
   
   if !DllCall("comdlg32\ChooseColor", Str, CHOOSECOLOR)
      Return -1
   
   PrevFormat := A_FormatInteger
   SetFormat, IntegerFast, H
   RGB := RGB_BGR(NumGet(&CHOOSECOLOR + A_PtrSize*3, "UInt")) . ""
   SetFormat, IntegerFast, % PrevFormat
   Return RGB
}

TrackToolTip( sText
            , x = ""   ; если не указаны, то вблизи курсора
            , y = ""
            , sTitle = ""
            , h_icon = 0   ; h_icon — 0: None, 1:Info, 2: Warning, 3: Error, n > 3: предполагается hIcon
            , CloseButton = 0
            , nColorBack = 0xFFFFE1
            , nColorText = 0
            , BallonTip = 0   ; BalloonTip — это ToolTip с хвостиком
            , w = 400 )  ; максимальная ширина
{
   TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80

   hWnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST := 8
                                   , Str, "tooltips_class32", Str, ""
                                   , UInt, TTS_NOPREFIX|TTS_ALWAYSTIP|(CloseButton ? TTS_CLOSE : 0)|(BallonTip ? TTS_BALLOON : 0)
                                   , Int, 0, Int, 0, Int, 0, Int, 0
                                   , Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)

   if (x != "" && y != "")
      xtt := x, ytt := y
   else
   {
      CoordMode, Mouse
      MouseGetPos, xtt, ytt
      xtt := x = "" ? xtt + 10 : x
      ytt := y = "" ? ytt + 10 : y
   }

   NumPut(VarSetCapacity(TOOLINFO, A_PtrSize = 4 ? 48 : 72, 0), TOOLINFO, "UInt")
   NumPut(0x20, TOOLINFO, 4, "UInt")      ; TTF_TRACK = 0x20
   NumPut(&sText, TOOLINFO, A_PtrSize = 4 ? 36 : 48, "UInt")

   DHW := A_DetectHiddenWindows
   DetectHiddenWindows, On
   WinWait, ahk_id %hWnd%

   WM_USER := 0x400
   SendMessage, WM_USER + 24,, w         ; TTM_SETMAXTIPWIDTH
   SendMessage, WM_USER + (A_IsUnicode ? 50 : 4),, &TOOLINFO   ; TTM_ADDTOOL
   SendMessage, WM_USER + 19, RGB_BGR(nColorBack)   ; TTM_SETTIPBKCOLOR
   SendMessage, WM_USER + 20, RGB_BGR(nColorText)   ; TTM_SETTIPTEXTCOLOR
   SendMessage, WM_USER + (A_IsUnicode ? 33 : 32), h_icon, &sTitle      ; TTM_SETTITLEA и TTM_SETTITLEW
   SendMessage, WM_USER + (A_IsUnicode ? 57 : 12),, &TOOLINFO     ; TTM_UPDATETIPTEXTA и TTM_UPDATETIPTEXTW
   SendMessage, WM_USER + 18,, xtt|(ytt<<16)   ; TTM_TRACKPOSITION
   SendMessage, WM_USER + 17, 1, &TOOLINFO ; TTM_TRACKACTIVATE

   if BallonTip
      xMax := A_ScreenWidth, yMax := A_ScreenHeight
   else
   {
      WinGetPos,,, W, H
      xMax := A_ScreenWidth - W - 10
      yMax := A_ScreenHeight - H - 10
   }
   
   if (xtt > xMax || ytt > yMax)
   {
      WinHide
      xtt := xtt > xMax ? xMax : xtt
      ytt := ytt > yMax ? yMax : ytt
      SendMessage, 1042,, xtt|(ytt<<16)   ; TTM_TRACKPOSITION
      WinShow
   }

   DetectHiddenWindows, % DHW
   Return hWnd
}

RGB_BGR(color)
{
   Return (color & 0xFF000000) | (color & 0xFF) << 16 | (color & 0xFF00) | (color >> 16)
}

GetCyrillicFontNames()
{
   hDC := DllCall("GetDC", UInt, 0, Ptr)
   VarSetCapacity(LOGFONT, 92, 0)
   NumPut(RUSSIAN_CHARSET := 204, &LOGFONT + 23, "UChar")
   DllCall("EnumFontFamiliesEx", Ptr, hDC
                               , Ptr, &LOGFONT
                               , Ptr, RegisterCallback("EnumFontFamExProc", "F", 4)
                               , Ptr, pFonts := Object(oFonts := [])
                               , UInt, 0)
   ObjRelease(pFonts), DllCall("ReleaseDC", Ptr, 0, Ptr, hDC)
   Return oFonts
}

EnumFontFamExProc(lpelfe, lpntme, FontType, lParam)
{
   if font := StrGet(lpelfe + 28)
      Return Object(lParam).Push(font)
}

ShowOwnerWindow(Title, hWnd)
{
   WinGetPos, X_Main, Y_Main, W_Main,, ahk_id %hMain%
   Gui, Show, Hide, % Title
   DetectHiddenWindows, On
   WinGetPos,,, W,, ahk_id %hWnd%
   Gui, Show, % "Hide x" X_Main - W " y" Y_Main
   WinGetPos, X, Y, W, H, ahk_id %hWnd%
   (X < 0 && X := X_Main + W_Main)
   (X + W > A_ScreenWidth && X := A_ScreenWidth - W)
   (Y < 0 && Y := 0)
   (Y + H > A_ScreenHeight && Y := A_ScreenHeight - H)
   WinMove, ahk_id %hWnd%,, X, Y, W, H
   Gui, Show
}

GetDDLstrings(param*)         
{                              
   if (param.MaxIndex() = 2)      ; RowNumber, AllCell
   {
      Loop 5
         RowStr .= (A_Index = 1 ? "" : "|") . A_Index . (A_Index = param[1] ? "|" : "")
      
      Loop 57
         CellStr .= (A_Index = 1 ? "" : "|") . (A_Index + 7) . (A_Index + 7 = param[2] ? "|" : "")
      
      Return { Rows: RegExReplace(RowStr, "\|$", "||")
            , Cells: RegExReplace(CellStr, "\|$", "||") }
   }
      
   if (param.MaxIndex() = 4)      ; FontName, oFonts, FontQuality, FontSize
   {
      for k, font in param[2]
         Fonts .= (A_Index = 1 ? "" : "|") . font . (font = param[1] ? "|" : "")

      for k, v in oQual
         Qual .= (A_Index = 1 ? "" : "|") . v . (k - 1 = param[3] ? "|" : "")
      
      Sizes := "8|9|10|11|12|13|14|15"
      Loop, parse, Sizes, |
         NewSizes .= (A_Index = 1 ? "" : "|") . A_LoopField . (A_LoopField = param[4] ? "|" : "")
      
      Return { Font: RegExReplace(Fonts, "\|$", "||")
             , Qual: RegExReplace(Qual, "\|$", "||")
             , Size: RegExReplace(NewSizes, "\|$", "||") }
   }
}

ColoredGuiShow(GuiName, Parent, Color, Label, W, H)
{
   Gui, %GuiName%: +Parent%Parent% -Caption -DPIScale
   Gui, %GuiName%: Color, % Format("{:X}", Color)
   Gui, %GuiName%: Add, Text, x0 y0 w%W% h%H% g%Label% +%SS_GRAYFRAME%
   Gui, %GuiName%: Show, NA w%W% h%H%
}

LoadCursorHand()
{
   Return DllCall("LoadImage", Ptr, 0
                             , UInt, OCR_HAND
                             , UInt, IMAGE_CURSOR
                             , Int, 0, Int, 0
                             , UInt, LR_DEFAULTSIZE|LR_SHARED, Ptr)
}

Ping(strHost)
{
   Loop 2
      bRet := ComObjGet("winmgmts:").Get("Win32_PingStatus.address='" . strHost . "'").StatusCode = 0
   until bRet
   return bRet
}

GetDisplayHk(Hk)
{
   if (Hk = "Нет" || Hk = "")
      Return "Нет"
   
   StringReplace, DisplayHk, Hk, ^, % "Ctrl@"
   StringReplace, DisplayHk, DisplayHk, +, % "Shift@"
   StringReplace, DisplayHk, DisplayHk, !, % "Alt@"
   StringReplace, DisplayHk, DisplayHk, @, +, All
   RegExMatch(DisplayHk, "i)(vk|sc)[0-9A-F]+$", found)
   StringReplace, DisplayHk, DisplayHk, % found, % GetKeyName(found)
   Return DisplayHk
}

SetContent()
{
   i := 0
   Loop % AllCell  {
      content := o%CurrentContent%[A_Index], clip := ""
      Loop, parse, content, `n, `r
         clip .= (A_Index = 1 ? "" : "`n`r") . A_LoopField
      until A_Index = RowNumber
      
      (clip != "" && i++)
      GuiControl, Work%A_Index%:, Text, % RegExReplace(clip, A_Tab, "    ")
      Gui, Work%A_Index%: Show, % clip = "" ? "Hide" : "NA"
   }
   UpdateScrollBar(hBack, ScrollHeight := 10 + (TextH + 8) * i - 3)
}

RemoveExistingAddNew(obj, data)
{
   for k, v in obj
      if (v == data && ExistSuchClip := 1)
         break
      
   (ExistSuchClip && obj.Remove(k))
   obj.Insert(1, data), obj.Remove(AllCell + 1, obj.MaxIndex())
}

ChangeContent()
{
   b := CurrentContent = "Saved"
   Menu, Content, UnCheck, % b ? "Скопированное" : "Вставленное"
   Menu, Content, Check, % b ? "Вставленное" : "Скопированное"
   CurrentContent := b ? "Entered" : "Saved"
   
   SetContent()
   Gui, Main:Show, % WinExist("ahk_id" hMain) ? "NA" : "Hide", % b ? "Вставленное" : "Скопированное"
   Return 1
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

2 (изменено: Malcev, 2015-02-02 19:12:08)

Re: AHK: Пишем удобный менеджер буфера обмена

При передвижении мыши с вариантов клипбоарда идет неприятное мелькание желтого цвета (Дрожат буквы).

3

Re: AHK: Пишем удобный менеджер буфера обмена

Тема "Классика"?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

4

Re: AHK: Пишем удобный менеджер буфера обмена

Да.

5

Re: AHK: Пишем удобный менеджер буфера обмена

Действительно, на ней мелькает сильно, избежать этого не удаётся. Отношу это к особенностям данной темы, т. к. даже в XP не мелькало. Можно лишь выбрать цвет выделения более тёмный, не так будет бросаться в глаза.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6

Re: AHK: Пишем удобный менеджер буфера обмена

И в режиме "Введенное" не стираются ячейки по клику правой кнопки мышки, а заместо этого стираются "сохраненные".

7

Re: AHK: Пишем удобный менеджер буфера обмена

Исправлено.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

8

Re: AHK: Пишем удобный менеджер буфера обмена

Действительно, на ней мелькает сильно, избежать этого не удаётся. Отношу это к особенностям данной темы,

Проверил - мелькать перестает при классической теме если в system Properties -> Perfomance options поставить галочки на Enable desktop composition и Use visual styles on windows and buttons.

9

Re: AHK: Пишем удобный менеджер буфера обмена

У меня всё это включено по умолчанию, но всё равно в "Классике" мелькает заметно. Но на всякий случай добавлю в описание, спасибо за совет.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

10

Re: AHK: Пишем удобный менеджер буфера обмена

Хорошо бы добавить возможность не вставлять текст при копировании.

11

Re: AHK: Пишем удобный менеджер буфера обмена

Кнопка "Block".

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

12

Re: AHK: Пишем удобный менеджер буфера обмена

teadrinker пишет:

Кнопка "Block".

В смысле эта кнопка делает возможным не вставлять копируя двойным щелчком? Что то не работает, все равно текст вставляется в активное окно блокнота.

13

Re: AHK: Пишем удобный менеджер буфера обмена

Нет, я неверно понял, она предотвращает сохранение текста в стеке скрипта при изменении буфера обмена.

avens пишет:

Хорошо бы добавить возможность не вставлять текст при копировании.

Что имеется в виду? Не совсем понял.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

14

Re: AHK: Пишем удобный менеджер буфера обмена

У Вас сейчас в программе есть настройки: "Посылать в активное окно", а так же в целевое и связанное, надо еще добавить возможность не посылать скопированное содержимое в окно, просто скопировать и скрыть окно программы. Мне например такая возможность в разы предпочтительней, чем посылать скопированный текст в окна.

15

Re: AHK: Пишем удобный менеджер буфера обмена

А, в смысле, в только буфер обмена текст поместить?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

16

Re: AHK: Пишем удобный менеджер буфера обмена

teadrinker пишет:

А, в смысле, в только буфер обмена текст поместить?

Да, поместить только в буфер обмена и скрыть окно программы..
А можно и две функции:
1. Копировать в буфер обмена
2. Копировать в буфер обмена и скрыть окно программы

Я вот недавно тоже задумался о необходимости менеджера буфера обмена на AHK.
В данный момент использую три менеджера буфера обмена, все они по своему хороши, но есть пожелания, связанные с удобством использования и добавлением функций.

17

Re: AHK: Пишем удобный менеджер буфера обмена

avens пишет:

А можно и две функции:
1. Копировать в буфер обмена
2. Копировать в буфер обмена и скрыть окно программы

Не против, если считаете, что будет полезно, добавлю.

avens пишет:

есть пожелания, связанные с удобством использования и добавлением функций.

Для этого тема и создана.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

18

Re: AHK: Пишем удобный менеджер буфера обмена

Например мне видится такой вариант удобным:
При нажатии горячей клавиши, вызывается окошко позади курсора, немного выше его, с однострочными или двухстрочными значениями. В окошке идет список например 10-15 последних значений буфера обмена, которые можно пролистывать. Далее при нажатии на выбранную ячейку с текстом она копируется в буфер обмена и окно программы скрывается.
В низу появляющегося окошка можно добавить поле с поиском текста по всей истории буфера обмена.
А также можно прятать окошко программы если курсор выводить за ее окно.

19

Re: AHK: Пишем удобный менеджер буфера обмена

Как я понимаю мелькание происходит из-за того что рамка и текст в ней на мгновение исчезают.
Может можно сделать копию этой рамки, которую положить под низ и которая не будет меняться, при наведении на нее мышью - соответственно не будет исчезать?

20

Re: AHK: Пишем удобный менеджер буфера обмена

avens пишет:

Например мне видится такой вариант удобным

Подумаю над всем этим. Возможно, потом добавлю окно поиска по всей истории, которую можно увеличить сверх нынешнего пока ограниченного 64 ячейками.

Malcev пишет:

Как я понимаю мелькание происходит из-за того что рамка и текст в ней на мгновение исчезают.

Нет, там просто окно перерисовывается при выделении цветом, из-за этого мелькание. Тут можно только вообще не использовать выделение (но хочется, чтобы окно как-то отзывалось при наведении курсора), либо использовать объект htmlfile, как у serzh82saratov (но это будет использование "сторонних" ресурсов, иногда с непредсказуемым результатом).

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

21

Re: AHK: Пишем удобный менеджер буфера обмена

А если использовать 2 окна, одно из которых сделать подложкой и не перерисовывать?

22 (изменено: dredj, 2015-02-02 23:47:06)

Re: AHK: Пишем удобный менеджер буфера обмена

avens пишет:

Например мне видится такой вариант удобным:

Полностью присоединяюсь к сказанному.
Ещё сценария: окно программы скрывается после "посыла в активное окно", clear и block можно скрыть например в меню "Настройки".
teadrinker, для чего пункт Содержимое - Введённое т.е. практический смысл? После копирование текста появляется окно сабжа, это так задумано или это только у меня?

23

Re: AHK: Пишем удобный менеджер буфера обмена

Malcev пишет:

А если использовать 2 окна, одно из которых сделать подложкой и не перерисовывать?

Уже про все такие варианты думал. Если окно выделения цветом просто накладывать сверху, тогда оно должно быть полупрозрачным, что будет ухудшать изображение текста под ним. Или ты как-то по-другому имел в виду?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

24

Re: AHK: Пишем удобный менеджер буфера обмена

dredj пишет:

teadrinker, для чего пункт Содержимое - Введённое т.е. практический смысл? После копирование текста появляется окно сабжа, это так задумано или это только у меня?

Так пока что задумано, но это я только сегодня писал на скорую руку, так что пока не всё продумано. Практический смысл — ну ведь может понадобиться попереключать туда-сюда.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

25

Re: AHK: Пишем удобный менеджер буфера обмена

Я думал так:

Gui, 1:Add, Text,, Text
Gui, 1:Show
Gui, 2:Add, Text,, Text
Gui, 2:Show

То есть второй Gui есть дубликат первого, который не перерисовывается при наведении мыши.

26

Re: AHK: Пишем удобный менеджер буфера обмена

А как будет одно видно из-под другого?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

27

Re: AHK: Пишем удобный менеджер буфера обмена

Я думал так:

Gui, 1:Add, Text,, qwertyuiopasdfghjkl
Gui, 1:Show
Gui, 2:Add, Text,, qwertyuiopasdfghjkl
Gui, 2:Show
sleep 1000
Gui, 2:Destroy
Gui, 2:Color, EEAA99
Gui, 2:Add, Text,, qwertyuiopasdfghjkl
Gui, 2:Show

28 (изменено: dredj, 2015-02-03 00:16:57)

Re: AHK: Пишем удобный менеджер буфера обмена

Практический смысл — ну ведь может понадобиться попереключать туда-сюда

Это понятно, имелось ввиду зачем вообще такой функционал, если это нужно для того чтобы знать что введено последнее, то можно просто переместить последнюю введённую ячейку вверх по списка в основном окне, так по моему логичнее, не?

29

Re: AHK: Пишем удобный менеджер буфера обмена

Malcev пишет:
Gui, 2:Destroy

Тогда будет появление нежелательных визуальных эффектов при включённой теме Аэро. В общем, попробуй написать полноценный демонстрационный вариант с изменением цвета при наведении мыши, хотя бы с несколькими ячейками. Может, тут и есть над чем подумать. Хотя, не думаю, что это хороший вариант — окно будет слишком утяжеляться, если каждую ячейку дублировать.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

30

Re: AHK: Пишем удобный менеджер буфера обмена

dredj пишет:

можно просто переместить последнюю введённую ячейку вверх по списка в основном окне, так по моему логичнее, не?

Можно подумать над этим, может, в этом и есть смысл. Если кто ещё читает, предлагаю обсудить этот вариант.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

31

Re: AHK: Пишем удобный менеджер буфера обмена

Аргумент против — при последующих изменениях буфера обмена последняя введённая ячейка будет всё равно уходить вниз. Как тогда получить быстрый доступ к последнему введённому?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

32 (изменено: dredj, 2015-02-03 01:46:03)

Re: AHK: Пишем удобный менеджер буфера обмена

Возможно такой функционал кому то важен - знать что введено последним, просто раньше такое не встречал в клипборд менеджерах, по крайне мере в тех что видел, где-то тридцать штук перепробовал).
teadrinker есть такой софт-комбайн http://virtassist.eu, вот там буфер реализован так как надо по моему, особенно GUI. Если интересно самому попробовать - https://yadi.sk/d/5KJqqU6PeRVN9, только проект это заброшен и кое-где недоделан.

33

Re: AHK: Пишем удобный менеджер буфера обмена

Непонимаю зачем перемещать вверх, если есть режим введённое. Вот только переключение между ними я бы сделал по кнопке без активации окна, а не выборе в меню.
Также при копировании думаю есть смысл удалять дубликаты из истории.
Как то регулировать рамки по высоте, то есть если одна строка то делать по ниже, и максимум по высоте три строки. Также тултип с полным содержимым ячейки, например по средней кнопке мыши.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

34

Re: AHK: Пишем удобный менеджер буфера обмена

dredj пишет:

Возможно такой функционал кому то важен - знать что введено последним

Так вот же вы об этом, вроде, пишете.

serzh82saratov пишет:

Вот только переключение между ними я бы сделал по кнопке без активации окна

Подумаю ещё, просто кнопка место дополнительное занимает.

serzh82saratov пишет:

Также при копировании думаю есть смысл удалять дубликаты из истории.

Если история большая и, особенно, если массивы текста большие, может занимать много времени при сравнении.

Как то регулировать рамки по высоте, то есть если одна строка то делать по ниже, и максимум по высоте три строки.

Так регулируется, вроде же.

Также тултип с полным содержимым ячейки, например по средней кнопке мыши.

Это есть уже, просто не написал в описании.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

35

Re: AHK: Пишем удобный менеджер буфера обмена

Так регулируется, вроде же.

У меня всегда высотой в 3 строки.

Это есть уже.

А, ну да.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

36

Re: AHK: Пишем удобный менеджер буфера обмена

serzh82saratov пишет:

У меня всегда высотой в 3 строки.

В меню Вид регулируется количество строк в ячейке. А если все разные делать — наверное некрасиво будет.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

37

Re: AHK: Пишем удобный менеджер буфера обмена

Так вот же вы об этом, вроде, пишете.

Это мы про кейлогер.

38

Re: AHK: Пишем удобный менеджер буфера обмена

Нет, там не кейлогер имелся в виду.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

39

Re: AHK: Пишем удобный менеджер буфера обмена

Значит я неправильно понял.

40

Re: AHK: Пишем удобный менеджер буфера обмена

А если все разные делать — наверное некрасиво будет.

Может быть, но в твоём варианте с рамками и отступами мне показалось вполне себе.

У всех окошек с настройками как бы отсутствует

Gui, -DPIScale

.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

41

Re: AHK: Пишем удобный менеджер буфера обмена

Тогда будет появление нежелательных визуальных эффектов при включённой теме Аэро. В общем, попробуй написать полноценный демонстрационный вариант с изменением цвета при наведении мыши, хотя бы с несколькими ячейками. Может, тут и есть над чем подумать. Хотя, не думаю, что это хороший вариант — окно будет слишком утяжеляться, если каждую ячейку дублировать.

Может проще сделать опциональный вариант - при наведении мыши на ячейку меняется только цвет текста?

42

Re: AHK: Пишем удобный менеджер буфера обмена

Так, вроде, пробовал, как-то не впечатлило.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

43

Re: AHK: Пишем удобный менеджер буфера обмена

serzh82saratov пишет:

Может быть, но в твоём варианте с рамками и отступами мне показалось вполне себе.

Не, будет разнобой.

У всех окошек с настройками как бы отсутствует Gui, -DPIScale

Нет, у всех есть, посмотри внимательно, там окна:
Gui, SetFont: (389 строка)
Gui, SetNumber: (456 строка)
Gui, SetColor: (516 строка)
Gui, SetWindow: (607 строка)
Везде есть. Может, не последний вариант у тебя.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

44

Re: AHK: Пишем удобный менеджер буфера обмена

А в 1398 строке например.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

45

Re: AHK: Пишем удобный менеджер буфера обмена

Но при этом всё равно в кнопке "применить" последний символ перенесён на следующую строку.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

46

Re: AHK: Пишем удобный менеджер буфера обмена

А, да, это цветные прямоугольники в окне настроек цвета, добавлю. А что, косяк получается?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

47

Re: AHK: Пишем удобный менеджер буфера обмена

Но при этом всё равно в кнопке "применить" последний символ перенесён на следующую строку.

Даже если добавить -DPIScale? Покажи скриншот.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

48

Re: AHK: Пишем удобный менеджер буфера обмена

http://savepic.net/6384929.png

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

49

Re: AHK: Пишем удобный менеджер буфера обмена

Брр... у меня совсем по-другому:

https://cloclo13.cloud.mail.ru/weblink/thumb/xw1/2fd8b0e61546/2015.02.03-01.21.26.6.jpg

Попробуй ещё раз скопировать код из первого поста, то же самое?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

50

Re: AHK: Пишем удобный менеджер буфера обмена

http://savepic.net/6389025.png

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

51

Re: AHK: Пишем удобный менеджер буфера обмена

Я смотрю, у тебя и шрифт другой. Есть в системе шрифт Tahoma?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

52

Re: AHK: Пишем удобный менеджер буфера обмена

И почему-то заголовка у окна нету.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

53

Re: AHK: Пишем удобный менеджер буфера обмена

Tahoma есть. Заголовка у меня нет всегда, если "+ToolWindow".

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

54

Re: AHK: Пишем удобный менеджер буфера обмена

Странно. А попробуй в 519 строке указать

Gui, Font, s8, Arial

Что выходит?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

55

Re: AHK: Пишем удобный менеджер буфера обмена

Никакой разницы.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

56

Re: AHK: Пишем удобный менеджер буфера обмена

Значит, не работает -DPIScale. Надо подумать.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

57

Re: AHK: Пишем удобный менеджер буфера обмена

Или проблема в том, что у тебя шрифты по-другому почему-то отображаются. Нужно окно иначе сконструировать, завтра попробую.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

58

Re: AHK: Пишем удобный менеджер буфера обмена

Наверное, на шрифты -DPIScale не действует.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

59

Re: AHK: Пишем удобный менеджер буфера обмена

Ну как, шрифты увеличивает, просто внутри кнопки чуть более чем необходимо.
На 100% шрифте у меня всё также как и у тебя, включая заголовок. Вообщем это всё уже несовершенство винды.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

60

Re: AHK: Пишем удобный менеджер буфера обмена

При захвате \ отпускании заголовка все строки истории исчезают \ появляются заново (мелькают).
При перетаскивании окна курсор может далеко уйти от заголовка.

По вопросам возмездной помощи пишите на E-Mail: serzh82saratov@mail.ru Telegram: https://t.me/sergiol982
Win10x64 AhkSpy, Hotkey, ClockGui

61

Re: AHK: Пишем удобный менеджер буфера обмена

А так:

/*
********************************** Описание **************************************

Скрипт сохраняет предустановленное в настройках количество состояний буфера обмена
(только текстовые данные, включая пути к скопрованным файлам) и даёт возможность
вводить их повторно.

Перед началом работы скрипт загружает dll-файл с иконками, должен быть доступ к
интернету.

При каждом изменении буфера обмена новые данные помещаются в верхнюю ячейку окна
скрипта, остальные сдвигаются вниз по принципу стека. Ввести текст из ячейки можно
кликнув по ней левой кнопкой мыши. Также для ввода и удаления данных из ячеек
можно воспользоваться контекстным меню, появляющимся при клике правой кнопкой мыши.

По умолчанию текст вводится в активное окно (само окно скрипта всегда неактивно).
Это поведение можно изменить через меню «Настройки».

Скрипт сохраняет историю введённых данных, переключиться на её отображение можно
через меню Содержимое -> Введённое.

Скрипт регистрирует в системе сообщение "WM_INFO", чтобы можно было заблокировать
его работу извне на время. Для блокировки отправьте главному скрытому окну скрипта
сообщение "WM_INFO" с wp = 1, для разблокировки wp = 0.

При завершении работы скрипт сохраняет все данные в файл %A_ScriptDir%\data, и
загружает их из него в следующей сессии.

Для корректной работы скомпилированного варианта из exe-файла все дефолтные иконки
должны быть удалены, вместо них должны быть добавлены иконки из dll-файла в том же
порядке. Если файловая система NTFS, скомпилированный скрипт пишет сохраняемые
данные в NTFS-поток по адресу %A_ScriptFullPath%:data

Если вы используете в Windows 7 тему "Классика", при выделении ячейки цветом может
происходить неприятное мелькание текста. Чтобы уменьшить этот эффект, в окне 
"Параметры быстродействия" (Run SystemPropertiesPerformance.exe) поставьте галки
"Включение композиции рабочего стола" и "Использование стилей отображения для
окон и кнопок" (спасибо за совет Malcev).

***************************** Структура файла "data" *****************************

UChar          // Количество блоков с данными из "сохранённого"
Блоки с данными
{
   UInt        // Длина строки данных
   Str         // Строка данных
}

UChar          // Количество блоков с данными из "введённого"
Блоки с данными
{
   UInt        // Длина строки данных
   Str         // Строка данных
}

UShort         // X-координата окна
UShort         // Y-координата окна
UShort         // ширина клиентской части окна
UShort         // высота клиентской части окна

UChar          // SendVariant, возможные значения 1, 3
UChar          // Длина строки с названием и классом связанного окна
Str            // Строка с названием и классом связанного окна
UChar          // Длина строки с Match Mode
Str            // Строка с Match Mode, возможные значения: "1", "2", "3", "RegEx"

UChar          // Длина строки с названием шрифта
Str            // Строка с названием шрифта
UChar          // Размер шрифта, возможные значения: 8 - 15
UChar          // Качество рендеринга, возможные значения 0 - 5
UInt           // Цвет шрифта в RGB

UChar          // Количество строк в ячейке
UChar          // Общее количество ячеек

UInt           // Цвет основного фона окна
UInt           // Цвет фона ячеек
UInt           // Цвет выделения ячеек
UInt           // Цвет области кнопок
UChar          // Стиль рамки, возможные значения:
               //   7 (SS_BLACKFRAME), 8 (SS_GRAYFRAME), 9 (SS_WHITEFRAME)

**********************************************************************************
*/

#SingleInstance, Off
IcoDll := A_IsCompiled ? A_ScriptFullPath : A_ScriptDir "\BufferIcons.dll"
if !FileExist(IcoDll)  {
   if !Ping("screencast.com")  {
      MsgBox, Невозможно загрузить файл с иконками`, проверьте подключение к интернету!
      ExitApp
   }
   URLDownloadToFile, % "http://content.screencast.com/users/teadrinker/folders/Files/media/"
                    .   "e72cd44f-6263-4917-b4ab-156dda0169b0/BufferIcons.dll?downloadOnly=true"
                    , %  IcoDll
}
if !A_IsCompiled
   Menu, Tray, Icon, %IcoDll%, 1
SetBatchLines, -1
SetWinDelay, 0
CoordMode, Mouse

global WS_EX_NOACTIVATE := 0x8000000, HTCAPTION := 2, SC_MAXIMIZE := 0xF030, SC_MINIMIZE := 0xF020
     , SS_BLACKFRAME := 0x7, SS_GRAYFRAME := 0x8, SS_WHITEFRAME := 0x9
     , IMAGE_CURSOR := 2, OCR_HAND := 32649, LR_SHARED := 0x8000, LR_DEFAULTSIZE := 0x40
     , WM_SETCURSOR := 0x20, WM_NCMOUSEMOVE := 0xA0, WM_VSCROLL := 0x115 , WM_MOUSEMOVE := 0x200
     , TargetWindow, AssociatedWindow, MatchMode, SendVariant, ScrollHeight, LastActiveWindowID
     , FontName, FontSize, FontQuality, FontColor
     , ColorCell, ColorSelect
     , hMain, hBack, hSetWindow, hSetFont, hSetNumber, hSetColor, hCursorHand, hBall
     , Block := 1, TopWork := 5, VisibleCell := 8, AllCell, oSaved := [], oEntered := [], CurrentContent := "Saved"
     , oQual := [ "DEFAULT_QUALITY"          , "DRAFT_QUALITY"        , "PROOF_QUALITY"
                , "NONANTIALIASED_QUALITY", "ANTIALIASED_QUALITY", "CLEARTYPE_QUALITY" ]
   
SB_LINEUP := 0, SB_LINEDOWN := 1
EM_SETCUEBANNER := 0x1501, EM_SETSEL := 0xB1, OBJID_VSCROLL := 0xFFFFFFFB
STATE_SYSTEM_INVISIBLE := 0x8000
oFonts := GetCyrillicFontNames()

VarSetCapacity(SBI, 60)
NumPut(60, SBI)
DriveGet, FIleSystem, FS, % SubStr(A_ScriptDir, 1, 2)
DataFile := A_ScriptDir "\" . ((FIleSystem = "NTFS" && A_IsCompiled) ? A_ScriptName . ":" : "") . "data"

if !FileExist(DataFile)
   MatchMode := 2, SendVariant := 3
   , PrevFontName    := FontName    := "Arial"
   , PrevFontSize    := FontSize    := 10
   , PrevFontQuality := FontQuality := 5
   , PrevFontColor   := FontColor   := 0
   , PrevRowNumber   := RowNumber   := 3
   , PrevAllCell     := AllCell     := 24
   , PrevColorBack   := ColorBack   := 0xFFFFFF
   , PrevColorCell   := ColorCell   := 0xFFFFFF
   , PrevColorSelect := ColorSelect := 0xFFFEAF
   , PrevColorButton := ColorButton := 0xD0D0D0
   , PrevFrameStyle  := FrameStyle  := SS_GRAYFRAME
else
{
   oFile := FileOpen(DataFile, "r", "UTF-16-RAW")
   Loop % oFile.ReadUChar()
      bytes := oFile.ReadUInt()
      , oSaved.Insert(oFile.Read(bytes))
      
   Loop % oFile.ReadUChar()
      bytes := oFile.ReadUInt()
      , oEntered.Insert(oFile.Read(bytes))

   MainGuiX := oFile.ReadUSHort(), MainGuiY := oFile.ReadUSHort()
   MainGuiW := oFile.ReadUSHort(), MainGuiH := oFile.ReadUSHort()

   SendVariant := oFile.ReadUChar()
   bytes := oFile.ReadUChar()
   AssociatedWindow := oFile.Read(bytes)
   bytes := oFile.ReadUChar()
   MatchMode := oFile.Read(bytes)
   
   Fontbytes := oFile.ReadUChar()
   PrevFontName    := FontName    := oFile.Read(Fontbytes)
   PrevFontSize    := FontSize    := oFile.ReadUChar()
   PrevFontQuality := FontQuality := oFile.ReadUChar()
   PrevFontColor   := FontColor   := oFile.ReadUInt()
   
   PrevRowNumber   := RowNumber   := oFile.ReadUChar()
   PrevAllCell     := AllCell     := oFile.ReadUChar()
   
   PrevColorBack   := ColorBack   := oFile.ReadUInt()
   PrevColorCell   := ColorCell   := oFile.ReadUInt()
   PrevColorSelect := ColorSelect := oFile.ReadUInt()
   PrevColorButton := ColorButton := oFile.ReadUInt()
   PrevFrameStyle  := FrameStyle  := oFile.ReadUChar()
   
   oFile.Close()

   if (MainGuiX = "" || MainGuiX > A_ScreenWidth - 50)
      Error .= "Ошибка чтения данных MainGuiX`nБудет присвоено значение по умолчанию`n`n", MainGuiX := ""
   if (MainGuiY = "" || MainGuiY > A_ScreenHeight - 50)
      Error .= "Ошибка чтения данных MainGuiY`nБудет присвоено значение по умолчанию`n`n", MainGuiY := ""
   if (MainGuiW = "" || MainGuiW > A_ScreenWidth)
      Error .= "Ошибка чтения данных MainGuiW`nБудет присвоено значение по умолчанию`n`n", MainGuiW := ""
   if (MainGuiH = "" || MainGuiH > A_ScreenHeight)
      Error .= "Ошибка чтения данных MainGuiH`nБудет присвоено значение по умолчанию`n`n", MainGuiH := ""
   if SendVariant not in 1,3
      Error .= "Ошибка чтения данных SendVariant`nБудет присвоено SendVariant := 3 (Посылать в активное окно)`n`n", SendVariant := 3
   if MatchMode not in 1,2,3,RegEx
      Error .= "Ошибка чтения данных MatchMode`nБудет присвоено MatchMode := 2`n`n", MatchMode := 2
   for k, font in (oFonts)
      if (FontName = font && Success := 1)
         break
   if !Success
      Error .= "Ошибка чтения данных FontName`nБудет присвоено FontName := ""Arial""`n`n", FontName := "Arial"
   if (FontSize < 8 || FontSize > 15)
      Error .= "Ошибка чтения данных FontSize`nБудет присвоено FontSize := 10`n`n", FontSize := 10
   if FontQuality not between 1 and 5
      Error .= "Ошибка чтения данных FontQuality`nБудет присвоено FontQuality := 5`n`n", FontQuality := 5
   if (FontColor > 0xFFFFFF || FontColor = "")
      Error .= "Ошибка чтения данных FontColor`nБудет присвоено FontColor := 0x000000`n`n", FontColor := 0
   if RowNumber not between 1 and 5
      Error .= "Ошибка чтения данных RowNumber`nБудет присвоено RowNumber := 3`n`n", RowNumber := 3
   if AllCell not between 8 and 64
      Error .= "Ошибка чтения данных AllCell`nБудет присвоено AllCell := 24`n`n", AllCell := 24
   if (ColorBack > 0xFFFFFF || ColorBack = "")
      Error .= "Ошибка чтения данных ColorBack`nБудет присвоено ColorBack := 0xFFFFFF`n`n", ColorBack := 0xFFFFFF
   if (ColorCell > 0xFFFFFF || ColorCell = "")
      Error .= "Ошибка чтения данных ColorCell`nБудет присвоено ColorCell := 0xFFFFFF`n`n", ColorCell := 0xFFFFFF
   if (ColorSelect > 0xFFFFFF || ColorSelect = "")
      Error .= "Ошибка чтения данных ColorSelect`nБудет присвоено ColorSelect := 0xFFFEAF`n`n", ColorSelect := 0xFFFEAF
   if (ColorButton > 0xFFFFFF || ColorButton = "")
      Error .= "Ошибка чтения данных ColorButton`nБудет присвоено ColorButton := 0xD0D0D0`n`n", ColorButton := 0xD0D0D0
   if FrameStyle not in 7,8,9
      Error .= "Ошибка чтения данных FrameStyle`nБудет присвоено FrameStyle := 0x8`n`n", FrameStyle := 8
   if Error
      MsgBox, % Trim(Error, "`n")
   Error := Success := ""
}

Menu, Tray, NoStandard
Menu, Tray, Add, Открыть новое окно, OpenNewWindow
Menu, Tray, Add, Скрыть окно, HideShowMainWindow
If !A_IsCompiled
   Menu, Tray, Add, Reload, Reload
Menu, Tray, Add, Выход, MainGuiClose

Menu, Actions, Add, Удалить, Delete
Menu, Actions, Icon, Удалить, %IcoDll%, 2
Menu, Actions, Add, Отправить             LButton, Send
Menu, Actions, Icon, Отправить             LButton, %IcoDll%, 3

Menu, Settings, Add, Указать связанное окно, SetAssociatedWindow
Menu, Settings, Add, Выбрать целевое окно правой кнопкой, SelectWindow
Menu, Settings, Add
Menu, Settings, Add, Посылать в связанное окно, SendToAssociatedWindow
Menu, Settings, Add, Посылать в целевое окно, SendToTargetWindow
Menu, Settings, Add, Посылать в активное окно, SendToActiveWindow
Menu, Settings, Check, % SendVariant = 3 ? "Посылать в активное окно" : "Посылать в связанное окно"

Menu, View, Add, Выбрать шрифт, SetFont
Menu, View, Icon, Выбрать шрифт, %IcoDll%, 4
Menu, View, Add, Количество строк и ячеек, SetNumber
Menu, View, Icon, Количество строк и ячеек, %IcoDll%, 5

Menu, View, Add, Настройки цвета, SetColor
Menu, View, Icon, Настройки цвета, %IcoDll%, 6

Menu, Content, Add, Сохранённое, ShowSaved
Menu, Content, Add, Введённое, ShowEntered
Menu, Content, Check, Сохранённое

Menu, MenuBar, Add, Настройки, :Settings
Menu, MenuBar, Add, Вид, :View
Menu, MenuBar, Add, Содержимое, :Content

Gosub, CreateMainWindow

OnMessage(WM_SETCURSOR  , "WM_MOUSEMOVE")
OnMessage(WM_NCMOUSEMOVE, "WM_MOUSEMOVE")
OnMessage(0x0A1, "WM_NCLBUTTONDOWN")
OnMessage(0x0A3, "WM_NCLBUTTONDBLCLK")
OnMessage(0x102, "WM_CHAR")
OnMessage(0x112, "WM_SYSCOMMAND")
OnMessage(0x115, "WM_VSCROLL")
OnMessage(0x200, "WM_MOUSEMOVE")
OnMessage(0x205, "WM_RBUTTONUP")
OnMessage(0x207, "WM_MBUTTONDOWN")
OnMessage(0x404, "AHK_NOTIFYICON")
OnMessage(DllCall("RegisterWindowMessage", Str, "WM_INFO"), "WM_INFO")
SetTimer, Unblock, -1000
OnExit, SaveData
Return

ShowSaved:
   if (CurrentContent = "Saved")
      Return
   
   Menu, Content, UseErrorLevel
   Menu, Content, UnCheck, Введённое
   Menu, Content, Check, Сохранённое
   CurrentContent := "Saved"
   
   Loop % AllCell   ; AllCell ?
      GuiControl, Work%A_Index%:, Text, % RegExReplace(oSaved[A_Index], A_Tab, "    ")
   ShowHide()
   Gui, Main:Show, NA, Сохранённое
   Return
   
ShowEntered:
   if (CurrentContent = "Entered")
      Return
   
   Menu, Content, UseErrorLevel
   Menu, Content, UnCheck, Сохранённое
   Menu, Content, Check, Введённое
   CurrentContent := "Entered"
   
   Loop % AllCell
      GuiControl, Work%A_Index%:, Text, % RegExReplace(oEntered[A_Index], A_Tab, "    ")
   ShowHide()
   Gui, Main:Show, NA, Введённое
   Return

Reload:
   Reload

HideShowMainWindow:
   if WinExist("ahk_id" hMain)
   {
      Gui, Main:Show, Hide
      Menu, Tray, Rename, Скрыть окно, Показать окно
   }
   else
   {
      Gui, Main:Show, NA
      Menu, Tray, Rename, Показать окно, Скрыть окно
   }
   Return

OpenNewWindow:
   Run, % A_ScriptFullPath,,, PID
   WinWait, ahk_pid %PID%
   WinGetPos, X, Y
   Loop 15
   {
      Sleep, 20
      WinMove, X - 15 < 0 ? (X + A_Index) : (X - A_Index), Y + A_Index
   }
   Return

CreateMainWindow:
   Gui, Main: New, +hwndhMain +AlwaysOnTop +E%WS_EX_NOACTIVATE% +Owner +Resize +MinSize218x -DPIScale
   Gui, Main: Menu, MenuBar
   Gui, Main: Color, % Dec2Hex(ColorButton)

   Gui, Back: +ParentMain -Caption +Border +hwndhBack -DPIScale
   Gui, Back: Color, % Dec2Hex(ColorBack)
   SetFormat, IntegerFast, H
   FontColor += 0, FontColor .= ""
   SetFormat, IntegerFast, D
   Loop % AllCell
   {
      Gui, Work%A_Index%: Default
      Gui, +ParentBack -Caption +hwndhWork%A_Index% -DPIScale
      Gui, Margin, 0, 0
      Gui, Color, % Dec2Hex(ColorCell)
      Gui, Add, Text, vBorder +%FrameStyle% x0 y0 w208
      Gui, Font, s%FontSize% q%FontQuality%, % FontName
      FontColor := Dec2Hex(FontColor)
      Gui, Add, Text, vText gSend_ x2 y2 w204 r%RowNumber% BackgroundTrans -Wrap c%FontColor%, % RegExReplace(oSaved[A_Index], A_Tab, "    ")
      GuiControlGet, Text, Pos
      GuiControl, Move, Border, % "h" TextH + 4
      Gui, Show, % "x5 y" 5 + (A_Index - 1) * (TextH + 8) " w208 h" TextH + 4 " Hide"
   }
   Gui, Back: Show, % "NA x-1 y0 w218 h" (TextH + 8)*VisibleCell + 6

   Gui, Main: Default
   Gui, Font, s10, Verdana
   Gui, Add, Button, % "x6 y" (TextH + 8)*VisibleCell + 18 " w101 h22", Clear
   Gui, Add, Button, gBlock vBlock x+5 yp wp hp, Block
   ShowHide()
   GuiControl, Work1: Focus, Static1
   ((MainGuiW = "" || MainGuiH = "") && (MainGuiW := 218) && (MainGuiH := (TextH + 8)*VisibleCell + 40))
   Bool := (MainGuiX != "" && MainGuiY != "")
   Gui, Show, % (Bool ? "x" MainGuiX " y" MainGuiY " " : "") . "NA w" MainGuiW " h" MainGuiH, Сохранённое
   Return

MainGuiSize:
   DllCall("GetScrollBarInfo", Ptr, hBack, UInt, OBJID_VSCROLL, Ptr, &SBI)
   , bool  := NumGet(&SBI + 36, "UInt") = STATE_SYSTEM_INVISIBLE
   , delta := NumGet(&SBI + 12, "UInt") - NumGet(&SBI + 4, "UInt")
   
   Gui, Back:Show, % "NA x-1 y0 w" A_GuiWidth + 1 " h" A_GuiHeight - 36 - (bool ? 0 : delta)
   Loop % AllCell
   {
      Gui, Work%A_Index%: Default
      Gui, Show, % "NA x5 y" TopWork + (A_Index - 1) * (TextH + 8) " w" w := A_GuiWidth - 10
      GuiControlGet, Text
      if (Text = "")
         Gui, Show, Hide
      GuiControl, MoveDraw, Border, % "w" w " h" TextH + 4
      GuiControl, Move, Text, % "w" w - 4 " h" TextH
   }
   GuiControl, Main:MoveDraw, Button1, % "y" A_GuiHeight - 28
   GuiControl, Main:MoveDraw, Button2, % "x" A_GuiWidth - 101 - 6 " y" A_GuiHeight - 28
   UpdateScrollBar(hBack, ScrollHeight)
   GuiW := A_GuiWidth, GuiH := A_GuiHeight
   Return
   
SetFont:
   oDDL := GetDDLstrings(FontName, oFonts, FontQuality, FontSize)
   Gui, SetFont:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetFont -DPIScale
   Gui, SetFont:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma
   
   Gui, Add, Text,, Название шрифта:
   Gui, Add, DDL, vFontName x+25 yp-3 w153 r20 Sort, % oDDL.Font
   GuiControlGet, FontName, Pos

   Gui, Add, Text, x15 y+14, Качество рендеринга:
   Gui, Add, DDL, x%FontNameX% yp-3 w%FontNameW% vQuality, % oDDL.Qual
   
   Gui, Add, Text, x15 y+15, Размер шрифта:
   Gui, Add, DDL, x%FontNameX% yp-3 w38 vFontSize, % oDDL.Size
   
   Gui, Add, Button, vDef gDef x+5 yp-1 hp+2, Сбросить всё
   GuiControlGet, Def, Pos
   GuiControl, Move, Def, % "x" FontNameX + FontNameW - DefW
   Gui, Add, Button, % "vCancel x" FontNameX " y+20 hp w" FontNameW//2 - 2, Применить
   Gui, Add, Button, % "vOk x" FontNameX + FontNameW - (FontNameW//2 - 2) " yp hp wp Default", OK
   Gui, Add, Button, % "vApply x" FontNameX - FontNameW//2 - 4 " yp hp wp", Отмена
   
   ShowOwnerWindow("Шрифт", hSetFont)
   Return
   
Def:
   oDDL := GetDDLstrings("Arial", oFonts, 5, 10)
   Gui, SetFont: Default
   GuiControl,, FontName, % "|" . oDDL.Font
   GuiControl,, Quality , % "|" . oDDL.Qual
   GuiControl,, FontSize, % "|" . oDDL.Size
   Return
   
SetFontButtonПрименить:
SetFontButtonOK:
   Gui, SetFont:Submit, NoHide
   for k, v in oQual  {
      if (v = Quality)  {
         FontQuality := k - 1
         break
      }
   }

SetFontButtonОтмена:
SetFontGuiClose:
SetFontGuiEscape:
   if (A_ThisLabel != "SetFontButtonПрименить")
   {
      Gui, SetFont:Destroy
      WinActivate, ahk_id %LastActiveWindowID%
   }
   if (FontName = PrevFontName && FontSize = PrevFontSize && FontQuality = PrevFontQuality)
      Return
   
   if A_ThisLabel in SetFontButtonOK,SetFontButtonПрименить
   {
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetFont: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetFont: +OwnerMain
   }
   PrevFontName := FontName, PrevFontSize := FontSize, PrevFontQuality := FontQuality
   Return
   
SetNumber:
   oDDL := GetDDLstrings(RowNumber, AllCell)
   Gui, SetNumber:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetNumber -DPIScale
   Gui, SetNumber:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma

   Gui, Add, Text,, Количество строк в ячейке:
   Gui, Add, DDL, vRowNumber x+20 yp-4 w40, % oDDL.Rows
   GuiControlGet, RowNumber, Pos

   Gui, Add, Text, x15 y+13, Общее количество ячеек:
   Gui, Add, DDL, vAllCell x%RowNumberX% yp-4 w40, % oDDL.Cells
   
   ButtonWidth := (RowNumberX + RowNumberW - 10 - 15)//3
   Gui, Add, Button, % "x15 y+20 w" ButtonWidth + 1 " h23", Отмена
   Gui, Add, Button, x+5 yp wp hp, Применить
   Gui, Add, Button, x+5 yp wp hp Default, OK
   ShowOwnerWindow("Количество строк и ячеек", hSetNumber)
   Return
   
SetNumberButtonПрименить:
SetNumberButtonOK:
   Gui, SetNumber:Submit, NoHide

SetNumberButtonОтмена:
SetNumberGuiClose:
SetNumberGuiescape:
   if (A_ThisLabel != "SetNumberButtonПрименить")
   {
      Gui, SetNumber: Destroy
      WinActivate, ahk_id %LastActiveWindowID%
   }
   if (RowNumber = PrevRowNumber && PrevAllCell = AllCell)
      Return
   
   if A_ThisLabel in SetNumberButtonOK,SetNumberButtonПрименить
   {
      oSaved.Remove(AllCell + 1, oSaved.MaxIndex())
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetNumber: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetNumber: +OwnerMain
   }
   PrevRowNumber := RowNumber, PrevAllCell := AllCell
   Return
   
ControlsCreate(var, text)
{
   global
   Gui, Add, Text, x15 y+15, % text
   SetFormat, IntegerFast, H
   Gui, Add, Edit, v%var% gChangeColor x+8 yp-3 w62 Limit6 Uppercase Center
      , % SubStr("00000" . SubStr(%var%+=0, 3), -5)
   SetFormat, IntegerFast, D
   GuiControlGet, %var%, Pos
   GuiControl, Move, % var, x%ColorBackX%
   ColoredGuiShow(var, "SetColor", %var%, "ColorChoose", ColorBackW, ColorBackH)
}
   
SetColor:
   Gui, SetColor:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetColor -DPIScale
   Gui, SetColor:Default
   Gui, Margin, 15, 15
   Gui, Font, s8, Tahoma
   
   ControlsCreate("ColorBack"  , "Цвет общего фона ячеек:")
   ControlsCreate("ColorCell"  , "Цвет фона ячейки:")
   ControlsCreate("ColorSelect", "Цвет выделения ячейки:")
   ControlsCreate("FontColor"  , "Цвет шрифта:")
   ControlsCreate("ColorButton", "Цвет фона кнопок:")
   
   Gui, Add, Text, x15 y+15, Цвет рамки ячейки:
   Gui, Add, Radio, % "x+15 yp" . (FrameStyle = SS_BLACKFRAME ? " Checked" : "") . " vRadio", Black
   Gui, Add, Radio, % "x+5 yp"  . (FrameStyle = SS_GRAYFRAME  ? " Checked" : ""), Gray
   Gui, Add, Radio, % "x+5 yp"  . (FrameStyle = SS_WHITEFRAME ? " Checked" : ""), White
   GuiControlGet, White, Pos, White
   
   Gui, Add, Button, vApply x15 y+20, Применить
   Gui, Add, Button, xp yp wp, Сбросить
   Gui, Add, Button, vCancel x+3 yp wp, Отмена
   GuiControlGet, Cancel, Pos
   GuiControl, Move, Apply, % "x" CancelX + CancelW + 3
   
   Gui, Add, Button, % "vOK x" CancelX + CancelW*2 + 6 " yp wp Default", OK
   GuiControlGet, OK, Pos
   
   ControlMetr := WhiteX + WhiteW > OKX + OKW ? "White" : "OK"
   ColorGuis := "ColorBack|ColorCell|ColorSelect|FontColor|ColorButton"
   Loop, parse, ColorGuis, |
      Gui, %A_LoopField%:Show, % "NA x" %ControlMetr%X + %ControlMetr%W - %A_LoopField%W - 1 " y" %A_LoopField%Y
   
   GuiControl, Focus, Static1
   ShowOwnerWindow("Настройки цвета в RGB", hSetColor)
   hCursorHand := LoadCursorHand()
   Return

ChangeColor:
   GuiControlGet, Color, SetColor:, %A_GuiControl%
   Gui, %A_GuiControl%: Color, % "0x" Color
   Return
   
ColorChoose:
   GuiControlGet, Color, SetColor:, %A_Gui%
   if (Color := ChooseColor("0x" . Color, hSetColor)) = -1
      Return
   
   Gui, %A_Gui%:Color, % Color
   GuiControl, SetColor:, %A_Gui%, % SubStr("00000" . SubStr(Color, 3), -5)
   Return
   
SetColorButtonСбросить:
   Gui, SetColor:Default
   GuiControl,, Edit1, FFFFFF
   GuiControl,, Edit2, FFFFFF
   GuiControl,, Edit3, FFFEAF
   GuiControl,, Edit4, 000000
   GuiControl,, Edit5, D0D0D0
   GuiControl,, Gray, 1
   Return

SetColorButtonОтмена:
SetColorGuiEscape:
SetColorGuiClose:
   Gui, SetColor:Destroy
   DllCall("DestroyCursor", Ptr, hCursorHand)
   WinActivate, ahk_id %LastActiveWindowID%
   Return

SetColorButtonOK:
SetColorButtonПрименить:
   Gui, SetColor:Submit, NoHide
   Loop, parse, ColorGuis, |
      %A_LoopField% := "0x" . %A_LoopField%
   FrameStyle := [SS_BLACKFRAME, SS_GRAYFRAME, SS_WHITEFRAME][Radio]
   
   if ( ColorBack   != PrevColorBack
     || ColorCell   != PrevColorCell
     || ColorSelect != PrevColorSelect
     || FontColor   != PrevFontColor
     || ColorButton != PrevColorButton
     || FrameStyle  != PrevFrameStyle )
   {
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetColor: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetColor: +OwnerMain
      PrevColorBack := ColorBack, PrevColorCell := ColorCell, PrevColorSelect := ColorSelect
      PrevFontColor := FontColor, PrevColorButton := ColorButton, PrevFrameStyle := FrameStyle
   }
   
   if (A_ThisLabel = "SetColorButtonOK")
      Gosub, SetColorGuiClose
   Return
   
SetAssociatedWindow:
   Gui, SetWindow:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetWindow -DPIScale
   Gui, SetWindow:Default
   Gui, Margin, 12, 12
   Gui, Add, Link, y10, Укажите <a href="http://ahkscript.org/docs/misc/WinTitle.htm">заголовок и класс</a> связанного окна:
   Gui, Add, Edit, y+10 wp h21 hwndhEdit vAssociatedWindow

   Gui, Add, Link, y+25, <a href="http://ahkscript.org/docs/commands/SetTitleMatchMode.htm">Match Mode:</a>
   Gui, Add, Radio, vMatchMode x+15 yp, 1
   GuiControlGet, Radio1, pos, Button1
   
   Gui, Add, Radio, x+7 yp, 2
   Gui, Add, Radio, x+7 yp, 3
   Gui, Add, Radio, x+7 yp, RegEx
   oRadio := {1: "Button1", 2: "Button2", 3: "Button3", RegEx: "Button4"}
   GuiControl,, % oRadio[MatchMode], 1
   GuiControlGet, Radio4, pos, Button4
   
   Gui, Add, GroupBox, % "x" (xGr := Radio1X - 9) " y" Radio1Y - 17
                      . " w" (wGr := Radio4X - Radio1X + 9 + Radio4W + 4) " h" Radio4H + 29
   GuiControl, Move, Edit1, % "w" xGr - 12 + wGr
   
   Gui, Add, Button, % "vSetWindow x" xGr + wGr - 90 " y+12 w90 h23 Default", OK
   Gui, Add, Button, gSetWindowGuiCancel xp-95 yp wp hp, Указать позже
   GuiControl, Focus, Button2
   if !AssociatedWindow
      PostMessage, EM_SETCUEBANNER, 0, " Блокнот ahk_class Notepad",, ahk_id %hEdit%
   else
   {
      GuiControl,, AssociatedWindow, % AssociatedWindow
      PostMessage, EM_SETSEL, 0, -1,, ahk_id %hEdit%
      GuiControl, Focus, Edit1
   }
   
   ShowOwnerWindow("Связанное окно", hSetWindow)
   Return
   
SetWindowButtonOK:
   Gui, SetWindow:Submit, NoHide
   (MatchMode = 4 && MatchMode := "RegEx")
   if (AssociatedWindow = "")
   {
      MsgBox, 4144,, Укажите связанное окно в поле редактирования!
      Return
   }
   Gosub, SendToAssociatedWindow

SetWindowGuiEscape:
SetWindowGuiCancel:
SetWindowGuiClose:
   Gui, SetWindow:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return
   
SendToAssociatedWindow:
SendToTargetWindow:
SendToActiveWindow:
   oSendVariant := { SendToAssociatedWindow: {Variant: 1, Menu: "Посылать в связанное окно"}
                   , SendToTargetWindow:     {Variant: 2, Menu: "Посылать в целевое окно"}
                   , SendToActiveWindow:     {Variant: 3, Menu: "Посылать в активное окно"} }
                   
   SendVariant := oSendVariant[A_ThisLabel].Variant
   Menu, Settings, UnCheck, Посылать в целевое окно
   Menu, Settings, UnCheck, Посылать в активное окно
   Menu, Settings, UnCheck, Посылать в связанное окно
   Menu, Settings, Check, % oSendVariant[A_ThisLabel].Menu
   Return

Send_:
   Send(A_Gui)
   Return

SelectWindow:
   CrossHair(), CrossHair := 1
   Hotkey, If
   Hotkey, RButton Up, Choice, On
   Return
   
Choice:
   CrossHair(0), CrossHair := ""
   Hotkey, RButton Up, Off
   MouseGetPos,,, TargetWindow
   if (TargetWindow = hMain)
      Goto, SendToActiveWindow
   Send, {Click}
   Gosub, SendToTargetWindow
   TargetWndShow(TargetWindow)
   Return

Unblock:
   Block := ""
   Return

Block:
   GuiControl,, Block, % (Block := !Block) ? "Unblock" : "Block"
   Return
   
MainButtonClear:
   Loop % AllCell
      GuiControl, Work%A_Index%:, Text
   o%CurrentContent% := []
   ShowHide()
   Return

OnClipboardChange:
   if Block || (Clipboard == oSaved[1] && StrLen(Clipboard) = StrLen(oSaved[1])) || A_EventInfo != 1
      Return
   
   oSaved.Insert(1, Clipboard)
   Loop % AllCell
      GuiControl, Work%A_Index%:, Text, % RegExReplace(oSaved[A_Index], A_Tab, "    ")
   ShowHide()
   
   Menu, Content, UseErrorLevel
   Menu, Content, UnCheck, Введённое
   Menu, Content, Check, Сохранённое
   CurrentContent := "Saved"
   Gui, Main:Show, NA, Сохранённое
   Return

MainGuiClose:
   ExitApp
   
SaveData:
   (CrossHair && CrossHair(0))
   
   oFile := FileOpen(DataFile, "w", "UTF-16-RAW")
   oFile.WriteUChar(oSaved.MaxIndex())
   for k, v in oSaved
      oFile.WriteUInt(StrLen(oSaved[A_Index]))
      , oFile.Write(oSaved[A_Index])
      
   oFile.WriteUChar(oEntered.MaxIndex())
   for k, v in oEntered
      oFile.WriteUInt(StrLen(oEntered[A_Index]))
      , oFile.Write(oEntered[A_Index])
   
   DetectHiddenWindows, On
   WinGetPos, X, Y,,, ahk_id %hMain%
   oFile.WriteUSHort(X), oFile.WriteUSHort(Y)
   oFile.WriteUSHort(GuiW), oFile.WriteUSHort(GuiH)
   
   (SendVariant = 2 && SendVariant := 3)
   oFile.WriteUChar(SendVariant)
   
   oFile.WriteUChar(StrLen(AssociatedWindow))
   oFile.Write(AssociatedWindow)
   
   oFile.WriteUChar(StrLen(MatchMode))
   oFile.Write(MatchMode)
   
   oFile.WriteUChar(StrLen(FontName))
   oFile.Write(FontName)
   
   oFile.WriteUChar(FontSize)
   oFile.WriteUChar(FontQuality)
   oFile.WriteUInt(FontColor)
   
   oFile.WriteUChar(RowNumber)
   oFile.WriteUChar(AllCell)
   
   oFile.WriteUInt(ColorBack)
   oFile.WriteUInt(ColorCell)
   oFile.WriteUInt(ColorSelect)
   oFile.WriteUInt(ColorButton)
   oFile.WriteUChar(FrameStyle)
   
   oFile.Close()
   ExitApp

#If MouseOverGui()
WheelDown::
WheelUp::
   Loop 4
      WM_VSCROLL(A_ThisHotkey = "WheelUp" ? SB_LINEUP : SB_LINEDOWN, 0, WM_VSCROLL, hBack)
   Return
   
WM_MOUSEMOVE(wp, lp, msg)
{
   static PrevColored, PrevGui
   
   if (msg = WM_MOUSEMOVE)
   {
      if (A_Gui = PrevGui)
         Return
      
      if InStr(A_Gui, "Work")
      {
         if PrevColored
            Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
         Gui, %A_Gui%: Color, % Dec2Hex(ColorSelect)
         PrevColored := A_Gui
         SetTimer, WhiteColor, 100
      }
      else if PrevColored
      {
         Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
         PrevColored := ""
      }
      PrevGui := A_Gui
   }
   
   if (msg = WM_NCMOUSEMOVE && PrevColored)
   {
      Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
      PrevColored := PrevGui := ""
   }
   
   if (msg = WM_SETCURSOR)
      if A_Gui in ColorBack,ColorCell,ColorSelect,FontColor,ColorButton
         Return DllCall("SetCursor", Ptr, hCursorHand)
   
   Return
   
WhiteColor:
   MouseGetPos,,, WinID
   if (WinID != hMain)
   {
      SetTimer, WhiteColor, Off
      if PrevColored
         Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
      PrevColored := ""
   }
   Return
}

WM_RBUTTONUP()
{
   static GuiNum, GuiName
   if !InStr(A_Gui, "Work")
      Return
   
   GuiName := A_Gui
   GuiNum := SubStr(A_Gui, 5)
   Menu, Actions, Show
   Return
   
Delete:
   o%CurrentContent%.Remove(GuiNum)
   Loop % AllCell
      GuiControl, Work%A_Index%:, Text, % RegExReplace(o%CurrentContent%[A_Index], A_Tab, "    ")
   ShowHide()
   Return
   
Send:
   Send(GuiName)
   Return
}

Send(GuiName)
{
   if (SendVariant = 1)
   {
      if AssociatedWindow  {
         SetTitleMatchMode, % MatchMode
         IfWinExist, % AssociatedWindow
            WinActivate
         else
            Return ToolTipShowAndDel("Связанное окно не найдено! Укажите связанное окно.")
      }
      else
         Return ToolTipShowAndDel("Связанное окно не указано!")
   }
   
   if (SendVariant = 2)
   {
      if TargetWindow  {
         IfWinExist, ahk_id %TargetWindow%
            WinActivate
         else
            Return ToolTipShowAndDel("Целевое окно не найдено! Измените выбор.")
      }
      else
         Return ToolTipShowAndDel("Целевое окно не указано!")
   }
   
   if (SendVariant = 3 && WinExist("A") = hMain)
      Return ToolTipShowAndDel("Активное окно не найдено!")

   SendText := o%CurrentContent%[SubStr(GuiName, 5)]
   if !(oEntered[1] == SendText)  {
      oEntered.Insert(1, SendText)
      if (CurrentContent = "Entered")  {
         Loop % AllCell
            GuiControl, Work%A_Index%:, Text, % RegExReplace(oEntered[A_Index], A_Tab, "    ")
         ShowHide()
      }
   }
   PrevBlock := Block, Block := 1
   TempClip := ClipboardAll
   Clipboard := SendText
   Sleep, 100
   SendInput, ^{vk56}   ; Ctrl + V
   Sleep, 100
   Clipboard := TempClip
   Sleep, 200
   Block := PrevBlock
}

ToolTipShowAndDel(text)
{
   ToolTip % text
   Sleep 1500
   ToolTip
}


WM_MBUTTONDOWN()
{
   if !InStr(A_Gui, "Work")
      Return
   
   GuiControlGet, Text, %A_Gui%:
   ToolTip, % Text
   KeyWait, MButton
   ToolTip
}

WM_NCLBUTTONDOWN(wp)
{
   static HTMENU := 5, HTMINBUTTON := 8, HTLEFT := 10, HTRIGHT := 11
      , HTTOP := 12, HTTOPLEFT := 13, HTTOPRIGHT := 14, HTBOTTOM := 15
      , HTBOTTOMLEFT := 16, HTBOTTOMRIGHT := 17, HTCLOSE := 20
      
   if (A_Gui != "Main")
      Return
   
   if wp in %HTCAPTION%,%HTLEFT%,%HTRIGHT%,%HTTOP%,%HTBOTTOM%,%HTTOPLEFT%,%HTTOPRIGHT%,%HTBOTTOMLEFT%,%HTBOTTOMRIGHT%
   {
      LastActiveWindowID := WinExist("A")
      SetTimer, MouseWatch, 100
      WinActivate, ahk_id %hMain%
      if (wp != HTCAPTION)
      {
         ControlGetPos,, TopWork,,, AutoHotkeyGUI1, ahk_id %hBack%
         TopWork -= 1
      }
   }
   if (wp = HTMENU)
   {
      LastActiveWindowID := WinExist("A")
      WinActivate, ahk_id %hMain%
      SendInput, {LButton Down}
      Sleep, 100
      if WinExist("ahk_id" hSetWindow) || WinExist("ahk_id" hSetFont)
      || WinExist("ahk_id" hSetNumber) || WinExist("ahk_id" hSetColor)
         WinActivate
      else
         WinActivate, ahk_id %LastActiveWindowID%
   }
   if (wp = HTMINBUTTON)
   {
      Gosub, HideShowMainWindow
      Return 1
   }
   
   if (wp = HTCLOSE && A_Gui = "Main")
      ExitApp
   Return
   
MouseWatch:
   if GetKeyState("LButton", "P")
      Return
   SetTimer, MouseWatch, Off
   WinActivate, ahk_id %LastActiveWindowID%
   Return
}

WM_NCLBUTTONDBLCLK(wp)
{
   if (A_Gui != "Main" || wp != HTCAPTION)
      Return
   
   WM_SYSCOMMAND(SC_MAXIMIZE)
   Return 1
}

WM_SYSCOMMAND(wp)
{
   static YPrev, HPrev, SB_TOP := 6
   if (A_Gui != "Main")
      Return
   
   WinGetPos,,,,  H_Tray, ahk_class Shell_TrayWnd
   if (wp = SC_MAXIMIZE)
   {
      WinExist("ahk_id" hMain)
      WinGetPos,, Y,, H
      WM_VSCROLL(SB_TOP, 0, WM_VSCROLL, hBack)
      if (Y = 0 && H = A_ScreenHeight - H_Tray && YPrev != "" && HPrev != "")
         WinMove,,,, YPrev,, HPrev
      else
      {
         YPrev := Y, HPrev := H
         WinMove,,,, 0,, A_ScreenHeight - H_Tray
      }
      Return 1
   }
   
   if (wp = SC_MINIMIZE)
   {
      Gosub, HideShowMainWindow
      Return 1
   }
}

ShowHide()
{
   global TextH, hBack
   i := 0
   Loop % AllCell
   {
      GuiControlGet, Text, Work%A_Index%:
      Gui, Work%A_Index%: Show, % Text = "" ? "Hide" : "NA"
      (Text != "" && i++)
   }
   UpdateScrollBar(hBack, ScrollHeight := 10 + (TextH + 8) * i - 3)
}

AHK_NOTIFYICON(wp, lp)
{
   static WM_LBUTTONUP := 0x202, WM_LBUTTONDBLCLK := 0x203, Time
   if (lp = WM_LBUTTONUP)
   {
      if (Time && A_TickCount - Time < 350)
         Return 1
      Gosub, HideShowMainWindow
      Time := A_TickCount
   }
   
   if (lp = WM_LBUTTONDBLCLK)
      Return 1
}

UpdateScrollBar(hGui, ScrollHeight)
{
   static SIF_RANGE := 0x1, SIF_PAGE := 0x2, SB_VERT := 1
   i := 0
   Loop % AllCell
   {
      GuiControlGet, Visible, Work%A_Index%:Visible, Text
      (Visible && i++)
   } until !Visible
   
   WinGetPos,,,, GuiHeight, ahk_id %hGui%
   VarSetCapacity(si, 28, 0)
   NumPut(28, si) ; cbSize
   NumPut(SIF_RANGE | SIF_PAGE, si, 4)
   NumPut(ScrollHeight, si, 12) ; nMax
   NumPut(GuiHeight, si, 16) ; nPage
   DllCall("SetScrollInfo", Ptr, hGui, UInt, SB_VERT, Ptr, &si, Int, 1)

   ControlGetPos,, Top,,, AutoHotkeyGUI1, ahk_id %hBack%
   Top -= 6
   if (Top >= 0)
      Return
   
   ControlGetPos,, y,, h, AutoHotkeyGUI%i%, ahk_id %hBack%
   Bottom := y + h + 6
   if (Top < 0 && Bottom < GuiHeight)
   {
      y := (a := Abs(Top)) > (b := GuiHeight-Bottom) ? b : a
      DllCall("ScrollWindow", Ptr, hGui, Int, 0, Int, y, Ptr, 0, Ptr, 0)
      TopWork += y
   }
}

WM_VSCROLL(wParam, lParam, msg, hwnd)
{
   static SIF_ALL=0x17, SCROLL_STEP=10
   bar := msg=0x115 ; SB_HORZ=0, SB_VERT=1

   VarSetCapacity(si, 28, 0)
   NumPut(28, si, "UInt") ; cbSize
   NumPut(SIF_ALL, si, 4, "UInt") ; fMask
   if !DllCall("GetScrollInfo", Ptr, hwnd, Int, bar, Ptr, &si)
     return

   VarSetCapacity(rect, 16)
   DllCall("GetClientRect", Ptr, hwnd, Ptr, &rect)

   new_pos := NumGet(si, 20, "UInt") ; nPos

   action := wParam & 0xFFFF
   if action = 0 ; SB_LINEUP
     new_pos -= SCROLL_STEP
   else if action = 1 ; SB_LINEDOWN
     new_pos += SCROLL_STEP
   else if action = 2 ; SB_PAGEUP
     new_pos -= NumGet(rect, 12, "Int") - SCROLL_STEP
   else if action = 3 ; SB_PAGEDOWN
     new_pos += NumGet(rect, 12, "Int") - SCROLL_STEP
   else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
     new_pos := wParam>>16
   else if action = 6 ; SB_TOP
     new_pos := NumGet(si, 8, "Int") ; nMin
   else if action = 7 ; SB_BOTTOM
     new_pos := NumGet(si, 12, "Int") ; nMax
   else
     return

   min := NumGet(si, 8, "Int") ; nMin
   max := NumGet(si, 12, "Int") - NumGet(si, 16, "UInt") ; nMax-nPage
   new_pos := new_pos > max ? max : new_pos
   new_pos := new_pos < min ? min : new_pos

   old_pos := NumGet(si, 20, "Int") ; nPos

   y := old_pos-new_pos
   ; Scroll contents of window and invalidate uncovered area.
   DllCall("ScrollWindow", Ptr, hwnd, Int, 0, Int, y, UInt, 0, UInt, 0)

   ; Update scroll bar.
   NumPut(new_pos, si, 20, "Int") ; nPos
   DllCall("SetScrollInfo", Ptr, hwnd, Int, bar, Ptr, &si, Int, 1)
}

CrossHair(OnOff=1)
{
   static IDC_CROSS := 32515, SPI_SETCURSORS := 0x57
      , hCursor := DllCall("LoadCursor", Ptr, 0, UInt, IDC_CROSS, Ptr)
      , sys_cursors := [32512,32513,32514,32516,32642,32643,32644,32645,32646,32648,32649,32650]
   
   if !OnOff
      DllCall("SystemParametersInfo", UInt, SPI_SETCURSORS, UInt, 0, UInt, 0, UInt, 0)
   else
      for k, cursor in sys_cursors
         hCopy := DllCall("CopyImage", Ptr, hCursor, UInt, 2, Int, 0, Int, 0, UInt, 0)
         , DllCall("SetSystemCursor", Ptr, hCopy, UInt, cursor)
}

TargetWndShow(TargetWnd)
{
   VarSetCapacity(WI, 60)
   DllCall("GetWindowInfo", Ptr, TargetWnd, Ptr, &WI)
   X := NumGet(WI, 20, "UInt"), Y := NumGet(WI, 24, "UInt")
   W := NumGet(WI, 28, "UInt") - X, H := NumGet(WI, 32, "UInt") - Y
   Gui, Flash:Default
   Gui, +LastFound -Caption +AlwaysOnTop +Disabled +Owner +hwndID3
   Gui, Color, Red
   Gui, Show, x%X% y%Y% w%W% h%H% hide
   th := 5, w1 := w - th, h1 := h - th
   WinSet, Region, 0-0 %w%-0 %w%-%h% 0-%h% 0-0 %th%-%th% %w1%-%th% %w1%-%h1% %th%-%h1% %th%-%th%
   Loop 3
   {
      Gui, Show, NA
      Sleep, 300
      Gui, Show, Hide
      Sleep, 300
   }
   Gui, Destroy
}

MouseOverGui()
{
   MouseGetPos,,, ID
   Return ID = hMain
}

WM_INFO(wp)
{
   Block := wp
}

WM_CHAR(wp)
{
   CoordMode, Caret
   WinClose, ahk_id %hBall%
   if (A_Gui != "SetColor")
      Return
   
   GuiControlGet, Focus, %A_Gui%:Focus
   if !InStr(Focus, "Edit")
      Return

   if wp in 3,8,24,26   ; обработка Ctrl + C, BackSpace, Ctrl + X, Ctrl + Z
      Return

   if wp = 22   ; обработка Ctrl + V
   {
      Loop, parse, Clipboard
      {
         Text .= A_LoopField
         if A_LoopField not in 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,A,B,C,D,E,F
         {
            ShowBall("Буфер обмена содержит недопустимые символы."
               . "`nДопустимые символы:`n0123456789ABCDEF", "Ошибка!")
            Return 1
         }
      }
      Control, EditPaste, % Text, % Focus, Настройки цвета в RGB
      Return 1
   }

   Char := Chr(wp)
   if Char not in 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,A,B,C,D,E,F
   {
      ShowBall("Допустимые символы:`n0123456789ABCDEF", Char " — недопустимый символ")
      Return 1
   }
   Return
}

ShowBall(Text, Title="")
{
   global
   WinClose, ahk_id %hBall%
   hBall := TrackToolTip(Text, A_CaretX+1, A_CaretY+15, Title)
   SetTimer, BallDestroy, -2000
   Return

BallDestroy:
   WinClose, ahk_id %hBall%
   Return
}

ChooseColor(Color := 0, hWnd := 0, Flags := 3)   ; Flags := CC_RGBINIT=1 | CC_FULLOPEN=2
{
   static CustColors, CHOOSECOLOR
   Color := RGB_BGR(Color)   ; RGB -> BGR

   if !CHOOSECOLOR
   {
      VarSetCapacity(CustColors, 64)
      Loop 16
         NumPut(0xFFFFFF, CustColors, (A_Index - 1)*4, "UInt")

      VarSetCapacity(CHOOSECOLOR, Size := A_PtrSize*9, 0)
      NumPut(Size, CHOOSECOLOR)
      NumPut(&CustColors, CHOOSECOLOR, A_PtrSize*4)
   }
   NumPut(Flags, CHOOSECOLOR, A_PtrSize*5)
   NumPut(hWnd, CHOOSECOLOR, A_PtrSize)
   NumPut(Color, CHOOSECOLOR, A_PtrSize*3)
   
   if !DllCall("comdlg32\ChooseColor", Str, CHOOSECOLOR)
      Return -1
   
   PrevFormat := A_FormatInteger
   SetFormat, IntegerFast, H
   RGB := RGB_BGR(NumGet(&CHOOSECOLOR + A_PtrSize*3, "UInt")) . ""
   SetFormat, IntegerFast, % PrevFormat
   Return RGB
}

TrackToolTip( sText
            , x = ""   ; если не указаны, то вблизи курсора
            , y = ""
            , sTitle = ""
            , h_icon = 0   ; h_icon — 0: None, 1:Info, 2: Warning, 3: Error, n > 3: предполагается hIcon
            , CloseButton = 0
            , nColorBack = 0xFFFFE1
            , nColorText = 0
            , BallonTip = 0   ; BalloonTip — это ToolTip с хвостиком
            , w = 400 )  ; максимальная ширина
{
   TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80

   hWnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST := 8
                                   , Str, "tooltips_class32", Str, ""
                                   , UInt, TTS_NOPREFIX|TTS_ALWAYSTIP|(CloseButton ? TTS_CLOSE : 0)|(BallonTip ? TTS_BALLOON : 0)
                                   , Int, 0, Int, 0, Int, 0, Int, 0
                                   , Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)

   if (x = "" || y = "")
   {
      CoordMode, Mouse
      MouseGetPos, xtt, ytt
      xtt := x = "" ? xtt + 10 : x
      ytt := y = "" ? ytt + 10 : y
   }
   Else
      xtt := x, ytt := y

   NumPut(VarSetCapacity(TOOLINFO, A_PtrSize = 4 ? 48 : 72, 0), TOOLINFO, "UInt")
   NumPut(0x20, TOOLINFO, 4, "UInt")      ; TTF_TRACK = 0x20
   NumPut(&sText, TOOLINFO, A_PtrSize = 4 ? 36 : 48, "UInt")

   DHW := A_DetectHiddenWindows
   DetectHiddenWindows, On
   WinWait, ahk_id %hWnd%

   WM_USER := 0x400
   SendMessage, WM_USER + 24,, w         ; TTM_SETMAXTIPWIDTH
   SendMessage, WM_USER + (A_IsUnicode ? 50 : 4),, &TOOLINFO   ; TTM_ADDTOOL
   SendMessage, WM_USER + 19, RGB_BGR(nColorBack)   ; TTM_SETTIPBKCOLOR
   SendMessage, WM_USER + 20, RGB_BGR(nColorText)   ; TTM_SETTIPTEXTCOLOR
   SendMessage, WM_USER + (A_IsUnicode ? 33 : 32), h_icon, &sTitle      ; TTM_SETTITLEA и TTM_SETTITLEW
   SendMessage, WM_USER + (A_IsUnicode ? 57 : 12),, &TOOLINFO     ; TTM_UPDATETIPTEXTA и TTM_UPDATETIPTEXTW
   SendMessage, WM_USER + 18,, xtt|(ytt<<16)   ; TTM_TRACKPOSITION
   SendMessage, WM_USER + 17, 1, &TOOLINFO ; TTM_TRACKACTIVATE

   if BallonTip
      xMax := A_ScreenWidth, yMax := A_ScreenHeight
   else
   {
      WinGetPos,,, W, H
      xMax := A_ScreenWidth - W - 10
      yMax := A_ScreenHeight - H - 10
   }
   
   if (xtt > xMax || ytt > yMax)
   {
      WinHide
      xtt := xtt > xMax ? xMax : xtt
      ytt := ytt > yMax ? yMax : ytt
      SendMessage, 1042,, xtt|(ytt<<16)   ; TTM_TRACKPOSITION
      WinShow
   }

   DetectHiddenWindows, % DHW
   Return hWnd
}

RGB_BGR(color)
{
   Return (color & 0xFF000000) | (color & 0xFF) << 16 | (color & 0xFF00) | (color >> 16)
}

GetCyrillicFontNames()
{
   hDC := DllCall("GetDC", UInt, 0, Ptr)
   VarSetCapacity(LOGFONT, 92, 0)
   NumPut(RUSSIAN_CHARSET := 204, &LOGFONT + 23, "UChar")
   DllCall("EnumFontFamiliesEx", Ptr, hDC
                               , Ptr, &LOGFONT
                               , Ptr, RegisterCallback("EnumFontFamExProc", "F", 4)
                               , Ptr, pFonts := Object(oFonts := [])
                               , UInt, 0)
   ObjRelease(pFonts), DllCall("ReleaseDC", Ptr, 0, Ptr, hDC)
   Return oFonts
}

EnumFontFamExProc(lpelfe, lpntme, FontType, lParam)
{
   if font := StrGet(lpelfe + 28)
      Return Object(lParam).Insert(font)
}

ShowOwnerWindow(Title, hWnd)
{
   WinGetPos, X_Main, Y_Main, W_Main,, ahk_id %hMain%
   Gui, Show, Hide, % Title
   DetectHiddenWindows, On
   WinGetPos,,, W,, ahk_id %hWnd%
   Gui, Show, % "Hide x" X_Main - W " y" Y_Main
   WinGetPos, X, Y, W, H, ahk_id %hWnd%
   (X < 0 && X := X_Main + W_Main)
   (X + W > A_ScreenWidth && X := A_ScreenWidth - W)
   (Y < 0 && Y := 0)
   (Y + H > A_ScreenHeight && Y := A_ScreenHeight - H)
   WinMove, ahk_id %hWnd%,, X, Y, W, H
   Gui, Show
}

GetDDLstrings(param*)         
{                              
   if (param.MaxIndex() = 2)      ; RowNumber, AllCell
   {
      Loop 5
         RowStr .= (A_Index = 1 ? "" : "|") . A_Index . (A_Index = param[1] ? "|" : "")
      
      Loop 57
         CellStr .= (A_Index = 1 ? "" : "|") . (A_Index + 7) . (A_Index + 7 = param[2] ? "|" : "")
      
      Return { Rows: RegExReplace(RowStr, "\|$", "||")
            , Cells: RegExReplace(CellStr, "\|$", "||") }
   }
      
   if (param.MaxIndex() = 4)      ; FontName, oFonts, FontQuality, FontSize
   {
      for k, font in param[2]
         Fonts .= (A_Index = 1 ? "" : "|") . font . (font = param[1] ? "|" : "")

      for k, v in oQual
         Qual .= (A_Index = 1 ? "" : "|") . v . (k - 1 = param[3] ? "|" : "")
      
      Sizes := "8|9|10|11|12|13|14|15"
      Loop, parse, Sizes, |
         NewSizes .= (A_Index = 1 ? "" : "|") . A_LoopField . (A_LoopField = param[4] ? "|" : "")
      
      Return { Font: RegExReplace(Fonts, "\|$", "||")
             , Qual: RegExReplace(Qual, "\|$", "||")
             , Size: RegExReplace(NewSizes, "\|$", "||") }
   }
}

ColoredGuiShow(GuiName, Parent, Color, Label, W, H)
{
   Gui, %GuiName%: +Parent%Parent% -Caption -DPIScale
   Gui, %GuiName%: Color, % Color
   Gui, %GuiName%: Add, Text, x0 y0 w%W% h%H% g%Label% +%SS_GRAYFRAME%
   Gui, %GuiName%: Show, NA w%W% h%H%
}

LoadCursorHand()
{
   Return DllCall("LoadImage", Ptr, 0
                             , UInt, OCR_HAND
                             , UInt, IMAGE_CURSOR
                             , Int, 0, Int, 0
                             , UInt, LR_DEFAULTSIZE|LR_SHARED, Ptr)
}

Dec2Hex(Number)
{
   PrevFormat := A_FormatInteger
   SetFormat, IntegerFast, H
   HexNumber := (Number+0) . ""
   SetFormat, IntegerFast, %PrevFormat%
   Return HexNumber
}

Ping(strHost)
{
   Loop 2
      bRet := ComObjGet("winmgmts:").Get("Win32_PingStatus.address='" . strHost . "'").StatusCode = 0
   until bRet
   return bRet
}

? Окно настроек цвета тоже исправлено.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

62

Re: AHK: Пишем удобный менеджер буфера обмена

Кстати, удобная вещь этот менеджер. Но вот то, что он каждый раз выскакивает при копировании, это, мне кажется, должно быть отключаемо.

63

Re: AHK: Пишем удобный менеджер буфера обмена

YMP пишет:

Кстати, удобная вещь этот менеджер.

Учитывая название темы, это ты хорошо сказал. Кнопка «Block», чтобы не выскакивал. Или свернуть в трей, чтобы было не видно.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

64

Re: AHK: Пишем удобный менеджер буфера обмена

Согласен с Ymp.
Имеется в виду чтобы при нажатиии Ctrl+C в буфер обмена информация заносилась, но окошко не выскакивало.

65

Re: AHK: Пишем удобный менеджер буфера обмена

Так если свернуто в трей, то не выскакивает, или у вас не так?

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

66

Re: AHK: Пишем удобный менеджер буфера обмена

У меня из трея выскакивает, когда что-то копирую. А если заблокировать, то не выскакивает, но тогда скопированное в него не помещается.

Да, и английские надписи кнопок как-то диссонируют с русским меню.

67

Re: AHK: Пишем удобный менеджер буфера обмена

Посмотрю, в чём там дело. Английские надписи просто короче, русские, боюсь, не влезут, или шрифт придётся уменьшать, тоже некрасиво будет.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

68

Re: AHK: Пишем удобный менеджер буфера обмена

Сначала не мог долго въехать, про какое "введённое" речь. Думаю, лучше бы его называть вставленным. Ведь мы вставляем что-то из буфера обмена, а вводим с клавиатуры.

69

Re: AHK: Пишем удобный менеджер буфера обмена

Тогда уж лучше «отправленное». «Вставленное» с моей точки зрения звучит как-то двусмысленно. Хотя, все эти варианты, конечно, не идеальны.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

70

Re: AHK: Пишем удобный менеджер буфера обмена

Исправлен косяк с выскакиванием из трея. Заменил «введённое» на «отправленное».

/*
********************************** Описание **************************************

Скрипт сохраняет предустановленное в настройках количество состояний буфера обмена
(только текстовые данные, включая пути к скопрованным файлам) и даёт возможность
вводить их повторно.

Перед началом работы скрипт загружает dll-файл с иконками, должен быть доступ к
интернету.

При каждом изменении буфера обмена новые данные помещаются в верхнюю ячейку окна
скрипта, остальные сдвигаются вниз по принципу стека. Ввести текст из ячейки можно
кликнув по ней левой кнопкой мыши. Также для ввода и удаления данных из ячеек
можно воспользоваться контекстным меню, появляющимся при клике правой кнопкой мыши.

По умолчанию текст вводится в активное окно (само окно скрипта всегда неактивно).
Это поведение можно изменить через меню «Настройки».

Скрипт сохраняет историю отправленных данных, переключиться на её отображение можно
через меню Содержимое -> Отправленное.

Если содержимое ячейки не видно полностью, посмотреть его можно, нажав над ней
колесо мыши.

Скрипт регистрирует в системе сообщение "WM_INFO", чтобы можно было заблокировать
его работу извне на время. Для блокировки отправьте главному скрытому окну скрипта
сообщение "WM_INFO" с wp = 1, для разблокировки wp = 0.

При завершении работы скрипт сохраняет все данные в файл %A_ScriptDir%\data, и
загружает их из него в следующей сессии.

Для корректной работы скомпилированного варианта из exe-файла все дефолтные иконки
должны быть удалены, вместо них должны быть добавлены иконки из dll-файла в том же
порядке. Если файловая система NTFS, скомпилированный скрипт пишет сохраняемые
данные в NTFS-поток по адресу %A_ScriptFullPath%:data

Если вы используете в Windows 7 тему "Классика", при выделении ячейки цветом может
происходить неприятное мелькание текста. Чтобы уменьшить этот эффект, в окне 
"Параметры быстродействия" (Run SystemPropertiesPerformance.exe) поставьте галки
"Включение композиции рабочего стола" и "Использование стилей отображения для
окон и кнопок" (спасибо за совет Malcev).

***************************** Структура файла "data" *****************************

UChar          // Количество блоков с данными из "сохранённого"
Блоки с данными
{
   UInt        // Длина строки данных
   Str         // Строка данных
}

UChar          // Количество блоков с данными из "отправленного"
Блоки с данными
{
   UInt        // Длина строки данных
   Str         // Строка данных
}

UShort         // X-координата окна
UShort         // Y-координата окна
UShort         // ширина клиентской части окна
UShort         // высота клиентской части окна

UChar          // SendVariant, возможные значения 1, 3
UChar          // Длина строки с названием и классом связанного окна
Str            // Строка с названием и классом связанного окна
UChar          // Длина строки с Match Mode
Str            // Строка с Match Mode, возможные значения: "1", "2", "3", "RegEx"

UChar          // Длина строки с названием шрифта
Str            // Строка с названием шрифта
UChar          // Размер шрифта, возможные значения: 8 - 15
UChar          // Качество рендеринга, возможные значения 0 - 5
UInt           // Цвет шрифта в RGB

UChar          // Количество строк в ячейке
UChar          // Общее количество ячеек

UInt           // Цвет основного фона окна
UInt           // Цвет фона ячеек
UInt           // Цвет выделения ячеек
UInt           // Цвет области кнопок
UChar          // Стиль рамки, возможные значения:
               //   7 (SS_BLACKFRAME), 8 (SS_GRAYFRAME), 9 (SS_WHITEFRAME)

**********************************************************************************
*/

#SingleInstance, Off
IcoDll := A_IsCompiled ? A_ScriptFullPath : A_ScriptDir "\BufferIcons.dll"
if !FileExist(IcoDll)  {
   if !Ping("screencast.com")  {
      MsgBox, Невозможно загрузить файл с иконками`, проверьте подключение к интернету!
      ExitApp
   }
   URLDownloadToFile, % "http://content.screencast.com/users/teadrinker/folders/Files/media/"
                    .   "e72cd44f-6263-4917-b4ab-156dda0169b0/BufferIcons.dll?downloadOnly=true"
                    , % IcoDll
}
if !A_IsCompiled
   Menu, Tray, Icon, %IcoDll%, 1
SetBatchLines, -1
SetWinDelay, 0
CoordMode, Mouse

global WS_EX_NOACTIVATE := 0x8000000, HTCAPTION := 2, SC_MAXIMIZE := 0xF030, SC_MINIMIZE := 0xF020
     , SS_BLACKFRAME := 0x7, SS_GRAYFRAME := 0x8, SS_WHITEFRAME := 0x9
     , IMAGE_CURSOR := 2, OCR_HAND := 32649, LR_SHARED := 0x8000, LR_DEFAULTSIZE := 0x40
     , WM_SETCURSOR := 0x20, WM_NCMOUSEMOVE := 0xA0, WM_VSCROLL := 0x115 , WM_MOUSEMOVE := 0x200
     , TargetWindow, AssociatedWindow, MatchMode, SendVariant, ScrollHeight, LastActiveWindowID
     , FontName, FontSize, FontQuality, FontColor
     , ColorCell, ColorSelect
     , hMain, hBack, hSetWindow, hSetFont, hSetNumber, hSetColor, hCursorHand, hBall
     , Block := 1, TopWork := 5, VisibleCell := 8, AllCell, oSaved := [], oEntered := [], CurrentContent := "Saved"
     , oQual := [ "DEFAULT_QUALITY"          , "DRAFT_QUALITY"        , "PROOF_QUALITY"
                , "NONANTIALIASED_QUALITY", "ANTIALIASED_QUALITY", "CLEARTYPE_QUALITY" ]
   
SB_LINEUP := 0, SB_LINEDOWN := 1
EM_SETCUEBANNER := 0x1501, EM_SETSEL := 0xB1, OBJID_VSCROLL := 0xFFFFFFFB
STATE_SYSTEM_INVISIBLE := 0x8000
oFonts := GetCyrillicFontNames()

VarSetCapacity(SBI, 60)
NumPut(60, SBI)
DriveGet, FIleSystem, FS, % SubStr(A_ScriptDir, 1, 2)
DataFile := A_ScriptDir "\" . ((FIleSystem = "NTFS" && A_IsCompiled) ? A_ScriptName . ":" : "") . "data"

if !FileExist(DataFile)
   MatchMode := 2, SendVariant := 3
   , PrevFontName    := FontName    := "Arial"
   , PrevFontSize    := FontSize    := 10
   , PrevFontQuality := FontQuality := 5
   , PrevFontColor   := FontColor   := 0
   , PrevRowNumber   := RowNumber   := 3
   , PrevAllCell     := AllCell     := 24
   , PrevColorBack   := ColorBack   := 0xFFFFFF
   , PrevColorCell   := ColorCell   := 0xFFFFFF
   , PrevColorSelect := ColorSelect := 0xFFFEAF
   , PrevColorButton := ColorButton := 0xD0D0D0
   , PrevFrameStyle  := FrameStyle  := SS_GRAYFRAME
else
{
   oFile := FileOpen(DataFile, "r", "UTF-16-RAW")
   Loop % oFile.ReadUChar()
      bytes := oFile.ReadUInt()
      , oSaved.Insert(oFile.Read(bytes))
      
   Loop % oFile.ReadUChar()
      bytes := oFile.ReadUInt()
      , oEntered.Insert(oFile.Read(bytes))

   MainGuiX := oFile.ReadUSHort(), MainGuiY := oFile.ReadUSHort()
   MainGuiW := oFile.ReadUSHort(), MainGuiH := oFile.ReadUSHort()

   SendVariant := oFile.ReadUChar()
   bytes := oFile.ReadUChar()
   AssociatedWindow := oFile.Read(bytes)
   bytes := oFile.ReadUChar()
   MatchMode := oFile.Read(bytes)
   
   Fontbytes := oFile.ReadUChar()
   PrevFontName    := FontName    := oFile.Read(Fontbytes)
   PrevFontSize    := FontSize    := oFile.ReadUChar()
   PrevFontQuality := FontQuality := oFile.ReadUChar()
   PrevFontColor   := FontColor   := oFile.ReadUInt()
   
   PrevRowNumber   := RowNumber   := oFile.ReadUChar()
   PrevAllCell     := AllCell     := oFile.ReadUChar()
   
   PrevColorBack   := ColorBack   := oFile.ReadUInt()
   PrevColorCell   := ColorCell   := oFile.ReadUInt()
   PrevColorSelect := ColorSelect := oFile.ReadUInt()
   PrevColorButton := ColorButton := oFile.ReadUInt()
   PrevFrameStyle  := FrameStyle  := oFile.ReadUChar()
   
   oFile.Close()

   if (MainGuiX = "" || MainGuiX > A_ScreenWidth - 50)
      Error .= "Ошибка чтения данных MainGuiX`nБудет присвоено значение по умолчанию`n`n", MainGuiX := ""
   if (MainGuiY = "" || MainGuiY > A_ScreenHeight - 50)
      Error .= "Ошибка чтения данных MainGuiY`nБудет присвоено значение по умолчанию`n`n", MainGuiY := ""
   if (MainGuiW = "" || MainGuiW > A_ScreenWidth)
      Error .= "Ошибка чтения данных MainGuiW`nБудет присвоено значение по умолчанию`n`n", MainGuiW := ""
   if (MainGuiH = "" || MainGuiH > A_ScreenHeight)
      Error .= "Ошибка чтения данных MainGuiH`nБудет присвоено значение по умолчанию`n`n", MainGuiH := ""
   if SendVariant not in 1,3
      Error .= "Ошибка чтения данных SendVariant`nБудет присвоено SendVariant := 3 (Посылать в активное окно)`n`n", SendVariant := 3
   if MatchMode not in 1,2,3,RegEx
      Error .= "Ошибка чтения данных MatchMode`nБудет присвоено MatchMode := 2`n`n", MatchMode := 2
   for k, font in (oFonts)
      if (FontName = font && Success := 1)
         break
   if !Success
      Error .= "Ошибка чтения данных FontName`nБудет присвоено FontName := ""Arial""`n`n", FontName := "Arial"
   if (FontSize < 8 || FontSize > 15)
      Error .= "Ошибка чтения данных FontSize`nБудет присвоено FontSize := 10`n`n", FontSize := 10
   if FontQuality not between 1 and 5
      Error .= "Ошибка чтения данных FontQuality`nБудет присвоено FontQuality := 5`n`n", FontQuality := 5
   if (FontColor > 0xFFFFFF || FontColor = "")
      Error .= "Ошибка чтения данных FontColor`nБудет присвоено FontColor := 0x000000`n`n", FontColor := 0
   if RowNumber not between 1 and 5
      Error .= "Ошибка чтения данных RowNumber`nБудет присвоено RowNumber := 3`n`n", RowNumber := 3
   if AllCell not between 8 and 64
      Error .= "Ошибка чтения данных AllCell`nБудет присвоено AllCell := 24`n`n", AllCell := 24
   if (ColorBack > 0xFFFFFF || ColorBack = "")
      Error .= "Ошибка чтения данных ColorBack`nБудет присвоено ColorBack := 0xFFFFFF`n`n", ColorBack := 0xFFFFFF
   if (ColorCell > 0xFFFFFF || ColorCell = "")
      Error .= "Ошибка чтения данных ColorCell`nБудет присвоено ColorCell := 0xFFFFFF`n`n", ColorCell := 0xFFFFFF
   if (ColorSelect > 0xFFFFFF || ColorSelect = "")
      Error .= "Ошибка чтения данных ColorSelect`nБудет присвоено ColorSelect := 0xFFFEAF`n`n", ColorSelect := 0xFFFEAF
   if (ColorButton > 0xFFFFFF || ColorButton = "")
      Error .= "Ошибка чтения данных ColorButton`nБудет присвоено ColorButton := 0xD0D0D0`n`n", ColorButton := 0xD0D0D0
   if FrameStyle not in 7,8,9
      Error .= "Ошибка чтения данных FrameStyle`nБудет присвоено FrameStyle := 0x8`n`n", FrameStyle := 8
   if Error
      MsgBox, % Trim(Error, "`n")
   Error := Success := ""
}

Menu, Tray, NoStandard
Menu, Tray, Add, Открыть новое окно, OpenNewWindow
Menu, Tray, Add, Скрыть окно, HideShowMainWindow
If !A_IsCompiled
   Menu, Tray, Add, Reload, Reload
Menu, Tray, Add, Выход, MainGuiClose

Menu, Actions, Add, Удалить, Delete
Menu, Actions, Icon, Удалить, %IcoDll%, 2
Menu, Actions, Add, Отправить             LButton, Send
Menu, Actions, Icon, Отправить             LButton, %IcoDll%, 3

Menu, Settings, Add, Указать связанное окно, SetAssociatedWindow
Menu, Settings, Add, Выбрать целевое окно правой кнопкой, SelectWindow
Menu, Settings, Add
Menu, Settings, Add, Посылать в связанное окно, SendToAssociatedWindow
Menu, Settings, Add, Посылать в целевое окно, SendToTargetWindow
Menu, Settings, Add, Посылать в активное окно, SendToActiveWindow
Menu, Settings, Check, % SendVariant = 3 ? "Посылать в активное окно" : "Посылать в связанное окно"

Menu, View, Add, Выбрать шрифт, SetFont
Menu, View, Icon, Выбрать шрифт, %IcoDll%, 4
Menu, View, Add, Количество строк и ячеек, SetNumber
Menu, View, Icon, Количество строк и ячеек, %IcoDll%, 5

Menu, View, Add, Настройки цвета, SetColor
Menu, View, Icon, Настройки цвета, %IcoDll%, 6

Menu, Content, Add, Сохранённое, ShowSaved
Menu, Content, Add, Отправленное, ShowEntered
Menu, Content, Check, Сохранённое

Menu, MenuBar, Add, Настройки, :Settings
Menu, MenuBar, Add, Вид, :View
Menu, MenuBar, Add, Содержимое, :Content

Gosub, CreateMainWindow

OnMessage(WM_SETCURSOR  , "WM_MOUSEMOVE")
OnMessage(WM_NCMOUSEMOVE, "WM_MOUSEMOVE")
OnMessage(0x0A1, "WM_NCLBUTTONDOWN")
OnMessage(0x0A3, "WM_NCLBUTTONDBLCLK")
OnMessage(0x102, "WM_CHAR")
OnMessage(0x112, "WM_SYSCOMMAND")
OnMessage(0x115, "WM_VSCROLL")
OnMessage(0x200, "WM_MOUSEMOVE")
OnMessage(0x205, "WM_RBUTTONUP")
OnMessage(0x207, "WM_MBUTTONDOWN")
OnMessage(0x404, "AHK_NOTIFYICON")
OnMessage(DllCall("RegisterWindowMessage", Str, "WM_INFO"), "WM_INFO")
SetTimer, Unblock, -1000
OnExit, SaveData
Return

ShowSaved:
   if (CurrentContent = "Saved")
      Return
   
   Menu, Content, UseErrorLevel
   Menu, Content, UnCheck, Отправленное
   Menu, Content, Check, Сохранённое
   CurrentContent := "Saved"
   
   Loop % AllCell   ; AllCell ?
      GuiControl, Work%A_Index%:, Text, % RegExReplace(oSaved[A_Index], A_Tab, "    ")
   ShowHide()
   Gui, Main:Show, NA, Сохранённое
   Return
   
ShowEntered:
   if (CurrentContent = "Entered")
      Return
   
   Menu, Content, UseErrorLevel
   Menu, Content, UnCheck, Сохранённое
   Menu, Content, Check, Отправленное
   CurrentContent := "Entered"
   
   Loop % AllCell
      GuiControl, Work%A_Index%:, Text, % RegExReplace(oEntered[A_Index], A_Tab, "    ")
   ShowHide()
   Gui, Main:Show, NA, Отправленное
   Return

Reload:
   Reload

HideShowMainWindow:
   if WinExist("ahk_id" hMain)
   {
      Gui, Main:Show, Hide
      Menu, Tray, Rename, Скрыть окно, Показать окно
   }
   else
   {
      Gui, Main:Show, NA
      Menu, Tray, Rename, Показать окно, Скрыть окно
   }
   Return

OpenNewWindow:
   Run, % A_ScriptFullPath,,, PID
   WinWait, ahk_pid %PID%
   WinGetPos, X, Y
   Loop 15
   {
      Sleep, 20
      WinMove, X - 15 < 0 ? (X + A_Index) : (X - A_Index), Y + A_Index
   }
   Return

CreateMainWindow:
   Gui, Main: New, +hwndhMain +AlwaysOnTop +E%WS_EX_NOACTIVATE% +Owner +Resize +MinSize218x -DPIScale
   Gui, Main: Menu, MenuBar
   Gui, Main: Color, % Dec2Hex(ColorButton)

   Gui, Back: +ParentMain -Caption +Border +hwndhBack -DPIScale
   Gui, Back: Color, % Dec2Hex(ColorBack)
   SetFormat, IntegerFast, H
   FontColor += 0, FontColor .= ""
   SetFormat, IntegerFast, D
   Loop % AllCell
   {
      Gui, Work%A_Index%: Default
      Gui, +ParentBack -Caption +hwndhWork%A_Index% -DPIScale
      Gui, Margin, 0, 0
      Gui, Color, % Dec2Hex(ColorCell)
      Gui, Add, Text, vBorder +%FrameStyle% x0 y0 w208
      Gui, Font, s%FontSize% q%FontQuality%, % FontName
      FontColor := Dec2Hex(FontColor)
      Gui, Add, Text, vText gSend_ x2 y2 w204 r%RowNumber% BackgroundTrans -Wrap c%FontColor%, % RegExReplace(oSaved[A_Index], A_Tab, "    ")
      GuiControlGet, Text, Pos
      GuiControl, Move, Border, % "h" TextH + 4
      Gui, Show, % "x5 y" 5 + (A_Index - 1) * (TextH + 8) " w208 h" TextH + 4 " Hide"
   }
   Gui, Back: Show, % "NA x-1 y0 w218 h" (TextH + 8)*VisibleCell + 6

   Gui, Main: Default
   Gui, Font, s10, Verdana
   Gui, Add, Button, % "x6 y" (TextH + 8)*VisibleCell + 18 " w101 h22", Clear
   Gui, Add, Button, gBlock vBlock x+5 yp wp hp, Block
   ShowHide()
   GuiControl, Work1: Focus, Static1
   ((MainGuiW = "" || MainGuiH = "") && (MainGuiW := 218) && (MainGuiH := (TextH + 8)*VisibleCell + 40))
   Bool := (MainGuiX != "" && MainGuiY != "")
   Gui, Show, % (Bool ? "x" MainGuiX " y" MainGuiY " " : "") . "NA w" MainGuiW " h" MainGuiH, Сохранённое
   Return

MainGuiSize:
   DllCall("GetScrollBarInfo", Ptr, hBack, UInt, OBJID_VSCROLL, Ptr, &SBI)
   , bool  := NumGet(&SBI + 36, "UInt") = STATE_SYSTEM_INVISIBLE
   , delta := NumGet(&SBI + 12, "UInt") - NumGet(&SBI + 4, "UInt")
   
   Gui, Back:Show, % "NA x-1 y0 w" A_GuiWidth + 1 " h" A_GuiHeight - 36 - (bool ? 0 : delta)
   Loop % AllCell
   {
      Gui, Work%A_Index%: Default
      Gui, Show, % "NA x5 y" TopWork + (A_Index - 1) * (TextH + 8) " w" w := A_GuiWidth - 10
      GuiControlGet, Text
      if (Text = "")
         Gui, Show, Hide
      GuiControl, MoveDraw, Border, % "w" w " h" TextH + 4
      GuiControl, Move, Text, % "w" w - 4 " h" TextH
   }
   GuiControl, Main:MoveDraw, Button1, % "y" A_GuiHeight - 28
   GuiControl, Main:MoveDraw, Button2, % "x" A_GuiWidth - 101 - 6 " y" A_GuiHeight - 28
   UpdateScrollBar(hBack, ScrollHeight)
   GuiW := A_GuiWidth, GuiH := A_GuiHeight
   Return
   
SetFont:
   oDDL := GetDDLstrings(FontName, oFonts, FontQuality, FontSize)
   Gui, SetFont:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetFont -DPIScale
   Gui, SetFont:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma
   
   Gui, Add, Text,, Название шрифта:
   Gui, Add, DDL, vFontName x+25 yp-3 w153 r20 Sort, % oDDL.Font
   GuiControlGet, FontName, Pos

   Gui, Add, Text, x15 y+14, Качество рендеринга:
   Gui, Add, DDL, x%FontNameX% yp-3 w%FontNameW% vQuality, % oDDL.Qual
   
   Gui, Add, Text, x15 y+15, Размер шрифта:
   Gui, Add, DDL, x%FontNameX% yp-3 w38 vFontSize, % oDDL.Size
   
   Gui, Add, Button, vDef gDef x+5 yp-1 hp+2, Сбросить всё
   GuiControlGet, Def, Pos
   GuiControl, Move, Def, % "x" FontNameX + FontNameW - DefW
   Gui, Add, Button, % "vCancel x" FontNameX " y+20 hp w" FontNameW//2 - 2, Применить
   Gui, Add, Button, % "vOk x" FontNameX + FontNameW - (FontNameW//2 - 2) " yp hp wp Default", OK
   Gui, Add, Button, % "vApply x" FontNameX - FontNameW//2 - 4 " yp hp wp", Отмена
   
   ShowOwnerWindow("Шрифт", hSetFont)
   Return
   
Def:
   oDDL := GetDDLstrings("Arial", oFonts, 5, 10)
   Gui, SetFont: Default
   GuiControl,, FontName, % "|" . oDDL.Font
   GuiControl,, Quality , % "|" . oDDL.Qual
   GuiControl,, FontSize, % "|" . oDDL.Size
   Return
   
SetFontButtonПрименить:
SetFontButtonOK:
   Gui, SetFont:Submit, NoHide
   for k, v in oQual  {
      if (v = Quality)  {
         FontQuality := k - 1
         break
      }
   }

SetFontButtonОтмена:
SetFontGuiClose:
SetFontGuiEscape:
   if (A_ThisLabel != "SetFontButtonПрименить")
   {
      Gui, SetFont:Destroy
      WinActivate, ahk_id %LastActiveWindowID%
   }
   if (FontName = PrevFontName && FontSize = PrevFontSize && FontQuality = PrevFontQuality)
      Return
   
   if A_ThisLabel in SetFontButtonOK,SetFontButtonПрименить
   {
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetFont: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetFont: +OwnerMain
   }
   PrevFontName := FontName, PrevFontSize := FontSize, PrevFontQuality := FontQuality
   Return
   
SetNumber:
   oDDL := GetDDLstrings(RowNumber, AllCell)
   Gui, SetNumber:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetNumber -DPIScale
   Gui, SetNumber:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma

   Gui, Add, Text,, Количество строк в ячейке:
   Gui, Add, DDL, vRowNumber x+20 yp-4 w40, % oDDL.Rows
   GuiControlGet, RowNumber, Pos

   Gui, Add, Text, x15 y+13, Общее количество ячеек:
   Gui, Add, DDL, vAllCell x%RowNumberX% yp-4 w40, % oDDL.Cells
   
   ButtonWidth := (RowNumberX + RowNumberW - 10 - 15)//3
   Gui, Add, Button, % "x15 y+20 w" ButtonWidth + 1 " h23", Отмена
   Gui, Add, Button, x+5 yp wp hp, Применить
   Gui, Add, Button, x+5 yp wp hp Default, OK
   ShowOwnerWindow("Количество строк и ячеек", hSetNumber)
   Return
   
SetNumberButtonПрименить:
SetNumberButtonOK:
   Gui, SetNumber:Submit, NoHide

SetNumberButtonОтмена:
SetNumberGuiClose:
SetNumberGuiescape:
   if (A_ThisLabel != "SetNumberButtonПрименить")
   {
      Gui, SetNumber: Destroy
      WinActivate, ahk_id %LastActiveWindowID%
   }
   if (RowNumber = PrevRowNumber && PrevAllCell = AllCell)
      Return
   
   if A_ThisLabel in SetNumberButtonOK,SetNumberButtonПрименить
   {
      oSaved.Remove(AllCell + 1, oSaved.MaxIndex())
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetNumber: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetNumber: +OwnerMain
   }
   PrevRowNumber := RowNumber, PrevAllCell := AllCell
   Return
   
ControlsCreate(var, text)
{
   global
   Gui, Add, Text, x15 y+15, % text
   SetFormat, IntegerFast, H
   Gui, Add, Edit, v%var% gChangeColor x+8 yp-3 w62 Limit6 Uppercase Center
      , % SubStr("00000" . SubStr(%var%+=0, 3), -5)
   SetFormat, IntegerFast, D
   GuiControlGet, %var%, Pos
   GuiControl, Move, % var, x%ColorBackX%
   ColoredGuiShow(var, "SetColor", %var%, "ColorChoose", ColorBackW, ColorBackH)
}
   
SetColor:
   Gui, SetColor:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetColor -DPIScale
   Gui, SetColor:Default
   Gui, Margin, 15, 15
   Gui, Font, s8, Tahoma
   
   ControlsCreate("ColorBack"  , "Цвет общего фона ячеек:")
   ControlsCreate("ColorCell"  , "Цвет фона ячейки:")
   ControlsCreate("ColorSelect", "Цвет выделения ячейки:")
   ControlsCreate("FontColor"  , "Цвет шрифта:")
   ControlsCreate("ColorButton", "Цвет фона кнопок:")
   
   Gui, Add, Text, x15 y+15, Цвет рамки ячейки:
   Gui, Add, Radio, % "x+15 yp" . (FrameStyle = SS_BLACKFRAME ? " Checked" : "") . " vRadio", Black
   Gui, Add, Radio, % "x+5 yp"  . (FrameStyle = SS_GRAYFRAME  ? " Checked" : ""), Gray
   Gui, Add, Radio, % "x+5 yp"  . (FrameStyle = SS_WHITEFRAME ? " Checked" : ""), White
   GuiControlGet, White, Pos, White
   
   Gui, Add, Button, vApply x15 y+20, Применить
   Gui, Add, Button, xp yp wp, Сбросить
   Gui, Add, Button, vCancel x+3 yp wp, Отмена
   GuiControlGet, Cancel, Pos
   GuiControl, Move, Apply, % "x" CancelX + CancelW + 3
   
   Gui, Add, Button, % "vOK x" CancelX + CancelW*2 + 6 " yp wp Default", OK
   GuiControlGet, OK, Pos
   
   ControlMetr := WhiteX + WhiteW > OKX + OKW ? "White" : "OK"
   ColorGuis := "ColorBack|ColorCell|ColorSelect|FontColor|ColorButton"
   Loop, parse, ColorGuis, |
      Gui, %A_LoopField%:Show, % "NA x" %ControlMetr%X + %ControlMetr%W - %A_LoopField%W - 1 " y" %A_LoopField%Y
   
   GuiControl, Focus, Static1
   ShowOwnerWindow("Настройки цвета в RGB", hSetColor)
   hCursorHand := LoadCursorHand()
   Return

ChangeColor:
   GuiControlGet, Color, SetColor:, %A_GuiControl%
   Gui, %A_GuiControl%: Color, % "0x" Color
   Return
   
ColorChoose:
   GuiControlGet, Color, SetColor:, %A_Gui%
   if (Color := ChooseColor("0x" . Color, hSetColor)) = -1
      Return
   
   Gui, %A_Gui%:Color, % Color
   GuiControl, SetColor:, %A_Gui%, % SubStr("00000" . SubStr(Color, 3), -5)
   Return
   
SetColorButtonСбросить:
   Gui, SetColor:Default
   GuiControl,, Edit1, FFFFFF
   GuiControl,, Edit2, FFFFFF
   GuiControl,, Edit3, FFFEAF
   GuiControl,, Edit4, 000000
   GuiControl,, Edit5, D0D0D0
   GuiControl,, Gray, 1
   Return

SetColorButtonОтмена:
SetColorGuiEscape:
SetColorGuiClose:
   Gui, SetColor:Destroy
   DllCall("DestroyCursor", Ptr, hCursorHand)
   WinActivate, ahk_id %LastActiveWindowID%
   Return

SetColorButtonOK:
SetColorButtonПрименить:
   Gui, SetColor:Submit, NoHide
   Loop, parse, ColorGuis, |
      %A_LoopField% := "0x" . %A_LoopField%
   FrameStyle := [SS_BLACKFRAME, SS_GRAYFRAME, SS_WHITEFRAME][Radio]
   
   if ( ColorBack   != PrevColorBack
     || ColorCell   != PrevColorCell
     || ColorSelect != PrevColorSelect
     || FontColor   != PrevFontColor
     || ColorButton != PrevColorButton
     || FrameStyle  != PrevFrameStyle )
   {
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetColor: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetColor: +OwnerMain
      PrevColorBack := ColorBack, PrevColorCell := ColorCell, PrevColorSelect := ColorSelect
      PrevFontColor := FontColor, PrevColorButton := ColorButton, PrevFrameStyle := FrameStyle
   }
   
   if (A_ThisLabel = "SetColorButtonOK")
      Gosub, SetColorGuiClose
   Return
   
SetAssociatedWindow:
   Gui, SetWindow:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetWindow -DPIScale
   Gui, SetWindow:Default
   Gui, Margin, 12, 12
   Gui, Add, Link, y10, Укажите <a href="http://ahkscript.org/docs/misc/WinTitle.htm">заголовок и класс</a> связанного окна:
   Gui, Add, Edit, y+10 wp h21 hwndhEdit vAssociatedWindow

   Gui, Add, Link, y+25, <a href="http://ahkscript.org/docs/commands/SetTitleMatchMode.htm">Match Mode:</a>
   Gui, Add, Radio, vMatchMode x+15 yp, 1
   GuiControlGet, Radio1, pos, Button1
   
   Gui, Add, Radio, x+7 yp, 2
   Gui, Add, Radio, x+7 yp, 3
   Gui, Add, Radio, x+7 yp, RegEx
   oRadio := {1: "Button1", 2: "Button2", 3: "Button3", RegEx: "Button4"}
   GuiControl,, % oRadio[MatchMode], 1
   GuiControlGet, Radio4, pos, Button4
   
   Gui, Add, GroupBox, % "x" (xGr := Radio1X - 9) " y" Radio1Y - 17
                      . " w" (wGr := Radio4X - Radio1X + 9 + Radio4W + 4) " h" Radio4H + 29
   GuiControl, Move, Edit1, % "w" xGr - 12 + wGr
   
   Gui, Add, Button, % "vSetWindow x" xGr + wGr - 90 " y+12 w90 h23 Default", OK
   Gui, Add, Button, gSetWindowGuiCancel xp-95 yp wp hp, Указать позже
   GuiControl, Focus, Button2
   if !AssociatedWindow
      PostMessage, EM_SETCUEBANNER, 0, " Блокнот ahk_class Notepad",, ahk_id %hEdit%
   else
   {
      GuiControl,, AssociatedWindow, % AssociatedWindow
      PostMessage, EM_SETSEL, 0, -1,, ahk_id %hEdit%
      GuiControl, Focus, Edit1
   }
   
   ShowOwnerWindow("Связанное окно", hSetWindow)
   Return
   
SetWindowButtonOK:
   Gui, SetWindow:Submit, NoHide
   (MatchMode = 4 && MatchMode := "RegEx")
   if (AssociatedWindow = "")
   {
      MsgBox, 4144,, Укажите связанное окно в поле редактирования!
      Return
   }
   Gosub, SendToAssociatedWindow

SetWindowGuiEscape:
SetWindowGuiCancel:
SetWindowGuiClose:
   Gui, SetWindow:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return
   
SendToAssociatedWindow:
SendToTargetWindow:
SendToActiveWindow:
   oSendVariant := { SendToAssociatedWindow: {Variant: 1, Menu: "Посылать в связанное окно"}
                   , SendToTargetWindow:     {Variant: 2, Menu: "Посылать в целевое окно"}
                   , SendToActiveWindow:     {Variant: 3, Menu: "Посылать в активное окно"} }
                   
   SendVariant := oSendVariant[A_ThisLabel].Variant
   Menu, Settings, UnCheck, Посылать в целевое окно
   Menu, Settings, UnCheck, Посылать в активное окно
   Menu, Settings, UnCheck, Посылать в связанное окно
   Menu, Settings, Check, % oSendVariant[A_ThisLabel].Menu
   Return

Send_:
   Send(A_Gui)
   Return

SelectWindow:
   CrossHair(), CrossHair := 1
   Hotkey, If
   Hotkey, RButton Up, Choice, On
   Return
   
Choice:
   CrossHair(0), CrossHair := ""
   Hotkey, RButton Up, Off
   MouseGetPos,,, TargetWindow
   if (TargetWindow = hMain)
      Goto, SendToActiveWindow
   Send, {Click}
   Gosub, SendToTargetWindow
   TargetWndShow(TargetWindow)
   Return

Unblock:
   Block := ""
   Return

Block:
   GuiControl,, Block, % (Block := !Block) ? "Unblock" : "Block"
   Return
   
MainButtonClear:
   Loop % AllCell
      GuiControl, Work%A_Index%:, Text
   o%CurrentContent% := []
   ShowHide()
   Return

OnClipboardChange:
   if Block || (Clipboard == oSaved[1] && StrLen(Clipboard) = StrLen(oSaved[1])) || A_EventInfo != 1
      Return
   
   oSaved.Insert(1, Clipboard)
   Loop % AllCell
      GuiControl, Work%A_Index%:, Text, % RegExReplace(oSaved[A_Index], A_Tab, "    ")
   ShowHide()
   
   Menu, Content, UseErrorLevel
   Menu, Content, UnCheck, Отправленное
   Menu, Content, Check, Сохранённое
   CurrentContent := "Saved"
   Gui, Main:Show, % WinExist("ahk_id" hMain) ? "NA" : "Hide", Сохранённое
   Return

MainGuiClose:
   ExitApp
   
SaveData:
   (CrossHair && CrossHair(0))
   
   oFile := FileOpen(DataFile, "w", "UTF-16-RAW")
   oFile.WriteUChar(oSaved.MaxIndex())
   for k, v in oSaved
      oFile.WriteUInt(StrLen(oSaved[A_Index]))
      , oFile.Write(oSaved[A_Index])
      
   oFile.WriteUChar(oEntered.MaxIndex())
   for k, v in oEntered
      oFile.WriteUInt(StrLen(oEntered[A_Index]))
      , oFile.Write(oEntered[A_Index])
   
   DetectHiddenWindows, On
   WinGetPos, X, Y,,, ahk_id %hMain%
   oFile.WriteUSHort(X), oFile.WriteUSHort(Y)
   oFile.WriteUSHort(GuiW), oFile.WriteUSHort(GuiH)
   
   (SendVariant = 2 && SendVariant := 3)
   oFile.WriteUChar(SendVariant)
   
   oFile.WriteUChar(StrLen(AssociatedWindow))
   oFile.Write(AssociatedWindow)
   
   oFile.WriteUChar(StrLen(MatchMode))
   oFile.Write(MatchMode)
   
   oFile.WriteUChar(StrLen(FontName))
   oFile.Write(FontName)
   
   oFile.WriteUChar(FontSize)
   oFile.WriteUChar(FontQuality)
   oFile.WriteUInt(FontColor)
   
   oFile.WriteUChar(RowNumber)
   oFile.WriteUChar(AllCell)
   
   oFile.WriteUInt(ColorBack)
   oFile.WriteUInt(ColorCell)
   oFile.WriteUInt(ColorSelect)
   oFile.WriteUInt(ColorButton)
   oFile.WriteUChar(FrameStyle)
   
   oFile.Close()
   ExitApp

#If MouseOverGui()
WheelDown::
WheelUp::
   Loop 4
      WM_VSCROLL(A_ThisHotkey = "WheelUp" ? SB_LINEUP : SB_LINEDOWN, 0, WM_VSCROLL, hBack)
   Return
   
WM_MOUSEMOVE(wp, lp, msg)
{
   static PrevColored, PrevGui
   
   if (msg = WM_MOUSEMOVE)
   {
      if (A_Gui = PrevGui)
         Return
      
      if InStr(A_Gui, "Work")
      {
         if PrevColored
            Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
         Gui, %A_Gui%: Color, % Dec2Hex(ColorSelect)
         PrevColored := A_Gui
         SetTimer, WhiteColor, 100
      }
      else if PrevColored
      {
         Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
         PrevColored := ""
      }
      PrevGui := A_Gui
   }
   
   if (msg = WM_NCMOUSEMOVE && PrevColored)
   {
      Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
      PrevColored := PrevGui := ""
   }
   
   if (msg = WM_SETCURSOR)
      if A_Gui in ColorBack,ColorCell,ColorSelect,FontColor,ColorButton
         Return DllCall("SetCursor", Ptr, hCursorHand)
   
   Return
   
WhiteColor:
   MouseGetPos,,, WinID
   if (WinID != hMain)
   {
      SetTimer, WhiteColor, Off
      if PrevColored
         Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
      PrevColored := ""
   }
   Return
}

WM_RBUTTONUP()
{
   static GuiNum, GuiName
   if !InStr(A_Gui, "Work")
      Return
   
   GuiName := A_Gui
   GuiNum := SubStr(A_Gui, 5)
   Menu, Actions, Show
   Return
   
Delete:
   o%CurrentContent%.Remove(GuiNum)
   Loop % AllCell
      GuiControl, Work%A_Index%:, Text, % RegExReplace(o%CurrentContent%[A_Index], A_Tab, "    ")
   ShowHide()
   Return
   
Send:
   Send(GuiName)
   Return
}

Send(GuiName)
{
   if (SendVariant = 1)
   {
      if AssociatedWindow  {
         SetTitleMatchMode, % MatchMode
         IfWinExist, % AssociatedWindow
            WinActivate
         else
            Return ToolTipShowAndDel("Связанное окно не найдено! Укажите связанное окно.")
      }
      else
         Return ToolTipShowAndDel("Связанное окно не указано!")
   }
   
   if (SendVariant = 2)
   {
      if TargetWindow  {
         IfWinExist, ahk_id %TargetWindow%
            WinActivate
         else
            Return ToolTipShowAndDel("Целевое окно не найдено! Измените выбор.")
      }
      else
         Return ToolTipShowAndDel("Целевое окно не указано!")
   }
   
   if (SendVariant = 3 && WinExist("A") = hMain)
      Return ToolTipShowAndDel("Активное окно не найдено!")

   SendText := o%CurrentContent%[SubStr(GuiName, 5)]
   if !(oEntered[1] == SendText)  {
      oEntered.Insert(1, SendText)
      if (CurrentContent = "Entered")  {
         Loop % AllCell
            GuiControl, Work%A_Index%:, Text, % RegExReplace(oEntered[A_Index], A_Tab, "    ")
         ShowHide()
      }
   }
   PrevBlock := Block, Block := 1
   TempClip := ClipboardAll
   Clipboard := SendText
   Sleep, 100
   SendInput, ^{vk56}   ; Ctrl + V
   Sleep, 100
   Clipboard := TempClip
   Sleep, 200
   Block := PrevBlock
}

ToolTipShowAndDel(text)
{
   ToolTip % text
   Sleep 1500
   ToolTip
}


WM_MBUTTONDOWN()
{
   if !InStr(A_Gui, "Work")
      Return
   
   GuiControlGet, Text, %A_Gui%:
   ToolTip, % Text
   KeyWait, MButton
   ToolTip
}

WM_NCLBUTTONDOWN(wp)
{
   static HTMENU := 5, HTMINBUTTON := 8, HTLEFT := 10, HTRIGHT := 11
      , HTTOP := 12, HTTOPLEFT := 13, HTTOPRIGHT := 14, HTBOTTOM := 15
      , HTBOTTOMLEFT := 16, HTBOTTOMRIGHT := 17, HTCLOSE := 20
      
   if (A_Gui != "Main")
      Return
   
   if wp in %HTCAPTION%,%HTLEFT%,%HTRIGHT%,%HTTOP%,%HTBOTTOM%,%HTTOPLEFT%,%HTTOPRIGHT%,%HTBOTTOMLEFT%,%HTBOTTOMRIGHT%
   {
      LastActiveWindowID := WinExist("A")
      SetTimer, MouseWatch, 100
      WinActivate, ahk_id %hMain%
      if (wp != HTCAPTION)
      {
         ControlGetPos,, TopWork,,, AutoHotkeyGUI1, ahk_id %hBack%
         TopWork -= 1
      }
   }
   if (wp = HTMENU)
   {
      LastActiveWindowID := WinExist("A")
      WinActivate, ahk_id %hMain%
      SendInput, {LButton Down}
      Sleep, 100
      if WinExist("ahk_id" hSetWindow) || WinExist("ahk_id" hSetFont)
      || WinExist("ahk_id" hSetNumber) || WinExist("ahk_id" hSetColor)
         WinActivate
      else
         WinActivate, ahk_id %LastActiveWindowID%
   }
   if (wp = HTMINBUTTON)
   {
      Gosub, HideShowMainWindow
      Return 1
   }
   
   if (wp = HTCLOSE && A_Gui = "Main")
      ExitApp
   Return
   
MouseWatch:
   if GetKeyState("LButton", "P")
      Return
   SetTimer, MouseWatch, Off
   WinActivate, ahk_id %LastActiveWindowID%
   Return
}

WM_NCLBUTTONDBLCLK(wp)
{
   if (A_Gui != "Main" || wp != HTCAPTION)
      Return
   
   WM_SYSCOMMAND(SC_MAXIMIZE)
   Return 1
}

WM_SYSCOMMAND(wp)
{
   static YPrev, HPrev, SB_TOP := 6
   if (A_Gui != "Main")
      Return
   
   WinGetPos,,,,  H_Tray, ahk_class Shell_TrayWnd
   if (wp = SC_MAXIMIZE)
   {
      WinExist("ahk_id" hMain)
      WinGetPos,, Y,, H
      WM_VSCROLL(SB_TOP, 0, WM_VSCROLL, hBack)
      if (Y = 0 && H = A_ScreenHeight - H_Tray && YPrev != "" && HPrev != "")
         WinMove,,,, YPrev,, HPrev
      else
      {
         YPrev := Y, HPrev := H
         WinMove,,,, 0,, A_ScreenHeight - H_Tray
      }
      Return 1
   }
   
   if (wp = SC_MINIMIZE)
   {
      Gosub, HideShowMainWindow
      Return 1
   }
}

ShowHide()
{
   global TextH, hBack
   i := 0
   Loop % AllCell
   {
      GuiControlGet, Text, Work%A_Index%:
      Gui, Work%A_Index%: Show, % Text = "" ? "Hide" : "NA"
      (Text != "" && i++)
   }
   UpdateScrollBar(hBack, ScrollHeight := 10 + (TextH + 8) * i - 3)
}

AHK_NOTIFYICON(wp, lp)
{
   static WM_LBUTTONUP := 0x202, WM_LBUTTONDBLCLK := 0x203, Time
   if (lp = WM_LBUTTONUP)
   {
      if (Time && A_TickCount - Time < 350)
         Return 1
      Gosub, HideShowMainWindow
      Time := A_TickCount
   }
   
   if (lp = WM_LBUTTONDBLCLK)
      Return 1
}

UpdateScrollBar(hGui, ScrollHeight)
{
   static SIF_RANGE := 0x1, SIF_PAGE := 0x2, SB_VERT := 1
   i := 0
   Loop % AllCell
   {
      GuiControlGet, Visible, Work%A_Index%:Visible, Text
      (Visible && i++)
   } until !Visible
   
   WinGetPos,,,, GuiHeight, ahk_id %hGui%
   VarSetCapacity(si, 28, 0)
   NumPut(28, si) ; cbSize
   NumPut(SIF_RANGE | SIF_PAGE, si, 4)
   NumPut(ScrollHeight, si, 12) ; nMax
   NumPut(GuiHeight, si, 16) ; nPage
   DllCall("SetScrollInfo", Ptr, hGui, UInt, SB_VERT, Ptr, &si, Int, 1)

   ControlGetPos,, Top,,, AutoHotkeyGUI1, ahk_id %hBack%
   Top -= 6
   if (Top >= 0)
      Return
   
   ControlGetPos,, y,, h, AutoHotkeyGUI%i%, ahk_id %hBack%
   Bottom := y + h + 6
   if (Top < 0 && Bottom < GuiHeight)
   {
      y := (a := Abs(Top)) > (b := GuiHeight-Bottom) ? b : a
      DllCall("ScrollWindow", Ptr, hGui, Int, 0, Int, y, Ptr, 0, Ptr, 0)
      TopWork += y
   }
}

WM_VSCROLL(wParam, lParam, msg, hwnd)
{
   static SIF_ALL=0x17, SCROLL_STEP=10
   bar := msg=0x115 ; SB_HORZ=0, SB_VERT=1

   VarSetCapacity(si, 28, 0)
   NumPut(28, si, "UInt") ; cbSize
   NumPut(SIF_ALL, si, 4, "UInt") ; fMask
   if !DllCall("GetScrollInfo", Ptr, hwnd, Int, bar, Ptr, &si)
     return

   VarSetCapacity(rect, 16)
   DllCall("GetClientRect", Ptr, hwnd, Ptr, &rect)

   new_pos := NumGet(si, 20, "UInt") ; nPos

   action := wParam & 0xFFFF
   if action = 0 ; SB_LINEUP
     new_pos -= SCROLL_STEP
   else if action = 1 ; SB_LINEDOWN
     new_pos += SCROLL_STEP
   else if action = 2 ; SB_PAGEUP
     new_pos -= NumGet(rect, 12, "Int") - SCROLL_STEP
   else if action = 3 ; SB_PAGEDOWN
     new_pos += NumGet(rect, 12, "Int") - SCROLL_STEP
   else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
     new_pos := wParam>>16
   else if action = 6 ; SB_TOP
     new_pos := NumGet(si, 8, "Int") ; nMin
   else if action = 7 ; SB_BOTTOM
     new_pos := NumGet(si, 12, "Int") ; nMax
   else
     return

   min := NumGet(si, 8, "Int") ; nMin
   max := NumGet(si, 12, "Int") - NumGet(si, 16, "UInt") ; nMax-nPage
   new_pos := new_pos > max ? max : new_pos
   new_pos := new_pos < min ? min : new_pos

   old_pos := NumGet(si, 20, "Int") ; nPos

   y := old_pos-new_pos
   ; Scroll contents of window and invalidate uncovered area.
   DllCall("ScrollWindow", Ptr, hwnd, Int, 0, Int, y, UInt, 0, UInt, 0)

   ; Update scroll bar.
   NumPut(new_pos, si, 20, "Int") ; nPos
   DllCall("SetScrollInfo", Ptr, hwnd, Int, bar, Ptr, &si, Int, 1)
}

CrossHair(OnOff=1)
{
   static IDC_CROSS := 32515, SPI_SETCURSORS := 0x57
      , hCursor := DllCall("LoadCursor", Ptr, 0, UInt, IDC_CROSS, Ptr)
      , sys_cursors := [32512,32513,32514,32516,32642,32643,32644,32645,32646,32648,32649,32650]
   
   if !OnOff
      DllCall("SystemParametersInfo", UInt, SPI_SETCURSORS, UInt, 0, UInt, 0, UInt, 0)
   else
      for k, cursor in sys_cursors
         hCopy := DllCall("CopyImage", Ptr, hCursor, UInt, 2, Int, 0, Int, 0, UInt, 0)
         , DllCall("SetSystemCursor", Ptr, hCopy, UInt, cursor)
}

TargetWndShow(TargetWnd)
{
   VarSetCapacity(WI, 60)
   DllCall("GetWindowInfo", Ptr, TargetWnd, Ptr, &WI)
   X := NumGet(WI, 20, "UInt"), Y := NumGet(WI, 24, "UInt")
   W := NumGet(WI, 28, "UInt") - X, H := NumGet(WI, 32, "UInt") - Y
   Gui, Flash:Default
   Gui, +LastFound -Caption +AlwaysOnTop +Disabled +Owner +hwndID3
   Gui, Color, Red
   Gui, Show, x%X% y%Y% w%W% h%H% hide
   th := 5, w1 := w - th, h1 := h - th
   WinSet, Region, 0-0 %w%-0 %w%-%h% 0-%h% 0-0 %th%-%th% %w1%-%th% %w1%-%h1% %th%-%h1% %th%-%th%
   Loop 3
   {
      Gui, Show, NA
      Sleep, 300
      Gui, Show, Hide
      Sleep, 300
   }
   Gui, Destroy
}

MouseOverGui()
{
   MouseGetPos,,, ID
   Return ID = hMain
}

WM_INFO(wp)
{
   Block := wp
}

WM_CHAR(wp)
{
   CoordMode, Caret
   WinClose, ahk_id %hBall%
   if (A_Gui != "SetColor")
      Return
   
   GuiControlGet, Focus, %A_Gui%:Focus
   if !InStr(Focus, "Edit")
      Return

   if wp in 3,8,24,26   ; обработка Ctrl + C, BackSpace, Ctrl + X, Ctrl + Z
      Return

   if wp = 22   ; обработка Ctrl + V
   {
      Loop, parse, Clipboard
      {
         Text .= A_LoopField
         if A_LoopField not in 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,A,B,C,D,E,F
         {
            ShowBall("Буфер обмена содержит недопустимые символы."
               . "`nДопустимые символы:`n0123456789ABCDEF", "Ошибка!")
            Return 1
         }
      }
      Control, EditPaste, % Text, % Focus, Настройки цвета в RGB
      Return 1
   }

   Char := Chr(wp)
   if Char not in 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,A,B,C,D,E,F
   {
      ShowBall("Допустимые символы:`n0123456789ABCDEF", Char " — недопустимый символ")
      Return 1
   }
   Return
}

ShowBall(Text, Title="")
{
   global
   WinClose, ahk_id %hBall%
   hBall := TrackToolTip(Text, A_CaretX+1, A_CaretY+15, Title)
   SetTimer, BallDestroy, -2000
   Return

BallDestroy:
   WinClose, ahk_id %hBall%
   Return
}

ChooseColor(Color := 0, hWnd := 0, Flags := 3)   ; Flags := CC_RGBINIT=1 | CC_FULLOPEN=2
{
   static CustColors, CHOOSECOLOR
   Color := RGB_BGR(Color)   ; RGB -> BGR

   if !CHOOSECOLOR
   {
      VarSetCapacity(CustColors, 64)
      Loop 16
         NumPut(0xFFFFFF, CustColors, (A_Index - 1)*4, "UInt")

      VarSetCapacity(CHOOSECOLOR, Size := A_PtrSize*9, 0)
      NumPut(Size, CHOOSECOLOR)
      NumPut(&CustColors, CHOOSECOLOR, A_PtrSize*4)
   }
   NumPut(Flags, CHOOSECOLOR, A_PtrSize*5)
   NumPut(hWnd, CHOOSECOLOR, A_PtrSize)
   NumPut(Color, CHOOSECOLOR, A_PtrSize*3)
   
   if !DllCall("comdlg32\ChooseColor", Str, CHOOSECOLOR)
      Return -1
   
   PrevFormat := A_FormatInteger
   SetFormat, IntegerFast, H
   RGB := RGB_BGR(NumGet(&CHOOSECOLOR + A_PtrSize*3, "UInt")) . ""
   SetFormat, IntegerFast, % PrevFormat
   Return RGB
}

TrackToolTip( sText
            , x = ""   ; если не указаны, то вблизи курсора
            , y = ""
            , sTitle = ""
            , h_icon = 0   ; h_icon — 0: None, 1:Info, 2: Warning, 3: Error, n > 3: предполагается hIcon
            , CloseButton = 0
            , nColorBack = 0xFFFFE1
            , nColorText = 0
            , BallonTip = 0   ; BalloonTip — это ToolTip с хвостиком
            , w = 400 )  ; максимальная ширина
{
   TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80

   hWnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST := 8
                                   , Str, "tooltips_class32", Str, ""
                                   , UInt, TTS_NOPREFIX|TTS_ALWAYSTIP|(CloseButton ? TTS_CLOSE : 0)|(BallonTip ? TTS_BALLOON : 0)
                                   , Int, 0, Int, 0, Int, 0, Int, 0
                                   , Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)

   if (x = "" || y = "")
   {
      CoordMode, Mouse
      MouseGetPos, xtt, ytt
      xtt := x = "" ? xtt + 10 : x
      ytt := y = "" ? ytt + 10 : y
   }
   Else
      xtt := x, ytt := y

   NumPut(VarSetCapacity(TOOLINFO, A_PtrSize = 4 ? 48 : 72, 0), TOOLINFO, "UInt")
   NumPut(0x20, TOOLINFO, 4, "UInt")      ; TTF_TRACK = 0x20
   NumPut(&sText, TOOLINFO, A_PtrSize = 4 ? 36 : 48, "UInt")

   DHW := A_DetectHiddenWindows
   DetectHiddenWindows, On
   WinWait, ahk_id %hWnd%

   WM_USER := 0x400
   SendMessage, WM_USER + 24,, w         ; TTM_SETMAXTIPWIDTH
   SendMessage, WM_USER + (A_IsUnicode ? 50 : 4),, &TOOLINFO   ; TTM_ADDTOOL
   SendMessage, WM_USER + 19, RGB_BGR(nColorBack)   ; TTM_SETTIPBKCOLOR
   SendMessage, WM_USER + 20, RGB_BGR(nColorText)   ; TTM_SETTIPTEXTCOLOR
   SendMessage, WM_USER + (A_IsUnicode ? 33 : 32), h_icon, &sTitle      ; TTM_SETTITLEA и TTM_SETTITLEW
   SendMessage, WM_USER + (A_IsUnicode ? 57 : 12),, &TOOLINFO     ; TTM_UPDATETIPTEXTA и TTM_UPDATETIPTEXTW
   SendMessage, WM_USER + 18,, xtt|(ytt<<16)   ; TTM_TRACKPOSITION
   SendMessage, WM_USER + 17, 1, &TOOLINFO ; TTM_TRACKACTIVATE

   if BallonTip
      xMax := A_ScreenWidth, yMax := A_ScreenHeight
   else
   {
      WinGetPos,,, W, H
      xMax := A_ScreenWidth - W - 10
      yMax := A_ScreenHeight - H - 10
   }
   
   if (xtt > xMax || ytt > yMax)
   {
      WinHide
      xtt := xtt > xMax ? xMax : xtt
      ytt := ytt > yMax ? yMax : ytt
      SendMessage, 1042,, xtt|(ytt<<16)   ; TTM_TRACKPOSITION
      WinShow
   }

   DetectHiddenWindows, % DHW
   Return hWnd
}

RGB_BGR(color)
{
   Return (color & 0xFF000000) | (color & 0xFF) << 16 | (color & 0xFF00) | (color >> 16)
}

GetCyrillicFontNames()
{
   hDC := DllCall("GetDC", UInt, 0, Ptr)
   VarSetCapacity(LOGFONT, 92, 0)
   NumPut(RUSSIAN_CHARSET := 204, &LOGFONT + 23, "UChar")
   DllCall("EnumFontFamiliesEx", Ptr, hDC
                               , Ptr, &LOGFONT
                               , Ptr, RegisterCallback("EnumFontFamExProc", "F", 4)
                               , Ptr, pFonts := Object(oFonts := [])
                               , UInt, 0)
   ObjRelease(pFonts), DllCall("ReleaseDC", Ptr, 0, Ptr, hDC)
   Return oFonts
}

EnumFontFamExProc(lpelfe, lpntme, FontType, lParam)
{
   if font := StrGet(lpelfe + 28)
      Return Object(lParam).Insert(font)
}

ShowOwnerWindow(Title, hWnd)
{
   WinGetPos, X_Main, Y_Main, W_Main,, ahk_id %hMain%
   Gui, Show, Hide, % Title
   DetectHiddenWindows, On
   WinGetPos,,, W,, ahk_id %hWnd%
   Gui, Show, % "Hide x" X_Main - W " y" Y_Main
   WinGetPos, X, Y, W, H, ahk_id %hWnd%
   (X < 0 && X := X_Main + W_Main)
   (X + W > A_ScreenWidth && X := A_ScreenWidth - W)
   (Y < 0 && Y := 0)
   (Y + H > A_ScreenHeight && Y := A_ScreenHeight - H)
   WinMove, ahk_id %hWnd%,, X, Y, W, H
   Gui, Show
}

GetDDLstrings(param*)         
{                              
   if (param.MaxIndex() = 2)      ; RowNumber, AllCell
   {
      Loop 5
         RowStr .= (A_Index = 1 ? "" : "|") . A_Index . (A_Index = param[1] ? "|" : "")
      
      Loop 57
         CellStr .= (A_Index = 1 ? "" : "|") . (A_Index + 7) . (A_Index + 7 = param[2] ? "|" : "")
      
      Return { Rows: RegExReplace(RowStr, "\|$", "||")
            , Cells: RegExReplace(CellStr, "\|$", "||") }
   }
      
   if (param.MaxIndex() = 4)      ; FontName, oFonts, FontQuality, FontSize
   {
      for k, font in param[2]
         Fonts .= (A_Index = 1 ? "" : "|") . font . (font = param[1] ? "|" : "")

      for k, v in oQual
         Qual .= (A_Index = 1 ? "" : "|") . v . (k - 1 = param[3] ? "|" : "")
      
      Sizes := "8|9|10|11|12|13|14|15"
      Loop, parse, Sizes, |
         NewSizes .= (A_Index = 1 ? "" : "|") . A_LoopField . (A_LoopField = param[4] ? "|" : "")
      
      Return { Font: RegExReplace(Fonts, "\|$", "||")
             , Qual: RegExReplace(Qual, "\|$", "||")
             , Size: RegExReplace(NewSizes, "\|$", "||") }
   }
}

ColoredGuiShow(GuiName, Parent, Color, Label, W, H)
{
   Gui, %GuiName%: +Parent%Parent% -Caption -DPIScale
   Gui, %GuiName%: Color, % Color
   Gui, %GuiName%: Add, Text, x0 y0 w%W% h%H% g%Label% +%SS_GRAYFRAME%
   Gui, %GuiName%: Show, NA w%W% h%H%
}

LoadCursorHand()
{
   Return DllCall("LoadImage", Ptr, 0
                             , UInt, OCR_HAND
                             , UInt, IMAGE_CURSOR
                             , Int, 0, Int, 0
                             , UInt, LR_DEFAULTSIZE|LR_SHARED, Ptr)
}

Dec2Hex(Number)
{
   PrevFormat := A_FormatInteger
   SetFormat, IntegerFast, H
   HexNumber := (Number+0) . ""
   SetFormat, IntegerFast, %PrevFormat%
   Return HexNumber
}

Ping(strHost)
{
   Loop 2
      bRet := ComObjGet("winmgmts:").Get("Win32_PingStatus.address='" . strHost . "'").StatusCode = 0
   until bRet
   return bRet
}
Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

71

Re: AHK: Пишем удобный менеджер буфера обмена

teadrinker пишет:

Тогда уж лучше «отправленное». «Вставленное» с моей точки зрения звучит как-то двусмысленно. Хотя, все эти варианты, конечно, не идеальны.

Почему двусмысленно? Скопировать в буфер обмена, вставить из буфера обмена. Да просто в любое контекстное меню загляни: вырезать, копировать, вставить. Как раз сохранённое и отправленное звучит двусмысленно. Куда отправленное? По сети, что ли?

72

Re: AHK: Пишем удобный менеджер буфера обмена

teadrinker пишет:

Посмотрю, в чём там дело. Английские надписи просто короче, русские, боюсь, не влезут, или шрифт придётся уменьшать, тоже некрасиво будет.

Можно вместо надписей значки метлы и замкА использовать, по-моему вполне будет узнаваемо.

73

Re: AHK: Пишем удобный менеджер буфера обмена

YMP пишет:

Как раз сохранённое и отправленное звучит двусмысленно.

Ну, мне не нравится, как звучит «вставленное». Можно вынести этот вопрос на обсуждение, кто за какой вариант? Мне вот даже более симпатично «История вставок».

DD пишет:

Можно вместо надписей значки метлы и замкА использовать, по-моему вполне будет узнаваемо.

Не думаю, что "метла" будет всем понятна, я вот никогда с таким обозначением не сталкивался. Кроме того, учитывая высоту кнопок, изображения будут слишком маленькими.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

74

Re: AHK: Пишем удобный менеджер буфера обмена

«Вставленное» двусмысленно потому, что непонятно, куда вставленное — сюда или отсюда. В случае «отправленного» такого вопроса не возникает.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

75

Re: AHK: Пишем удобный менеджер буфера обмена

teadrinker пишет:

«Вставленное» двусмысленно потому, что непонятно, куда вставленное — сюда или отсюда. В случае «отправленного» такого вопроса не возникает.

Странно, что тебе это непонятно. Речь же о буфере обмена идёт. В случае отправленного возникает другой вопрос, который я уже задал выше.

Эта программа, по сути, расширенный буфер обмена, поэтому и термины логично оставить привычные. Скопированное, вставленное.

Конечно, каждый может и сам прописать то, что ему больше нравится.

76

Re: AHK: Пишем удобный менеджер буфера обмена

А я метлу часто вижу (хоть и не дворник))), и вообще на значках по слову «clear», в основном метла фигурирует. А увеличивать высоту кнопок не стоит, это да.

77

Re: AHK: Пишем удобный менеджер буфера обмена

YMP пишет:

Странно, что тебе это непонятно. Речь же о буфере обмена идёт.

Ну так ведь из буфера обмена как раз в окно программы вставляется.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

78 (изменено: dredj, 2015-02-03 15:40:47)

Re: AHK: Пишем удобный менеджер буфера обмена

teadrinker пишет:

Посмотрю, в чём там дело. Английские надписи просто короче, русские, боюсь, не влезут, или шрифт придётся уменьшать, тоже некрасиво будет.

А спрятать где-нибудь в меню и заодно на русском, не вариант?

79

Re: AHK: Пишем удобный менеджер буфера обмена

Можно попросить вас выкладывать файлом? А то каждый раз выделять не очень удобно.

Как сказал мой дед - Я твой дед

80

Re: AHK: Пишем удобный менеджер буфера обмена

teadrinker пишет:

Ну так ведь из буфера обмена как раз в окно программы вставляется.

Это технические детали. А для пользователя эта программа и есть буфер обмена, просто расширенный. Я что-то копирую где-то, и оно попадает в твою программу. Я же его не вставляю туда каким-то отдельным действием. Для меня это копирование, скажем, из Блокнота в расширенный буфер обмена. Поэтому логично, что я там вижу список скопированного. Оттуда я могу что-то вставить обратно в Блокнот — логично, что я это буду воспринимать как вставленное.

81

Re: AHK: Пишем удобный менеджер буфера обмена

dredj пишет:

А спрятать где-нибудь в меню и заодно на русском, не вариант?

Но ведь это функции, к которым в ходе работы с программой предполагается обращаться довольно часто, поэтому искать их в меню — это больше неудобства, чем выгоды, имхо. А что, действительно так раздражает, что на английском? Я уже привык.

sergeiplugatyr пишет:

Можно попросить вас выкладывать файлом?

Ок, у меня просто копируется дополнением к FF.

YMP пишет:

Поэтому логично, что я там вижу список скопированного. Оттуда я могу что-то вставить обратно в Блокнот — логично, что я это буду воспринимать как вставленное.

Ну хорошо, думаю, это не принципиально, если для тебя это так важно, могу поменять в следующей версии.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

82

Re: AHK: Пишем удобный менеджер буфера обмена

teadrinker пишет:

Ну хорошо, думаю, это не принципиально, если для тебя это так важно, могу поменять в следующей версии.

Ради меня одного не стоит, я для себя могу сам поменять. Речь как раз об общем принципе. Есть уже определённый шаблон у пользователей, касающийся буфера обмена, определённый набор привычных терминов. И даже если человек понимает, как работает твоя программа внутри, он всё равно будет её воспринимать как буфер обмена, куда он что-то копирует и откуда что-то вставляет. Это естественный ход мысли. То, что содержимое этого буфера сохраняется на диск и не исчезает при перезагрузке, — просто дополнительное удобство. В самой концепции это ничего не меняет. Это именно буфер обмена.

83 (изменено: dredj, 2015-02-03 19:29:54)

Re: AHK: Пишем удобный менеджер буфера обмена

Наверно у каждого свой опыт пользования, для меня лично такой функционал вообще без надобности а кому-то он важен, всё на ваше усмотрение. Английский или русский имхо не принципиально, но соглашусь, на английском более лаконично.
Насчёт "вставленное" и "отправленное", первый вариант привычен, да.

84

Re: AHK: Пишем удобный менеджер буфера обмена

YMP пишет:

Речь как раз об общем принципе. Есть уже определённый шаблон у пользователей

Ну вот я и предлагаю пользователям высказаться, кому как нравится, но им, по-моему, в основном без разницы.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

85

Re: AHK: Пишем удобный менеджер буфера обмена

Напишу свой отзыв.
Было бы удобно по нажатию на ячейку не вставлять, а помещать в буфер. А вставлять уже самостоятельно по Ctrl+V.
Хорошо бы сохранять не только текст. Например, выбор при помощи checkbox:
- текст
- картинки
- объекты в Проводнике
- прочее

Были некоторые глюки с окном.
1) При растягивании окна загрузка ЦП прыгнула до 100% на минуту
2) Окно свернулось в узкий заголовок и не хотело разворачиваться

86 (изменено: Malcev, 2015-02-03 20:25:45)

Re: AHK: Пишем удобный менеджер буфера обмена

Предлагаю, сделать условие - в настройках цвета цвет фона ячейки равен цвету выделения ячейки, то gui не перерисовываем, а то раздражают эти дрожания.

87

Re: AHK: Пишем удобный менеджер буфера обмена

ypppu пишет:

Напишу свой отзыв.
Было бы удобно по нажатию на ячейку не вставлять, а помещать в буфер. А вставлять уже самостоятельно по Ctrl+V.

А в чём удобство? В какой ситуации? К тому же непонятно, что будет вставляться по Ктрл-В, если ячеек несколько.

88

Re: AHK: Пишем удобный менеджер буфера обмена

ypppu пишет:

Было бы удобно по нажатию на ячейку не вставлять, а помещать в буфер. А вставлять уже самостоятельно по Ctrl+V.

Уже это обсуждали, решили что будут ещё две настройки отправки:
• Только помещать в буфер обмена
• Закрывать окно после отправки при выводе курсора из его поля (открывать можно по горячей клавише, которую можно будет выбрать)

Хорошо бы сохранять не только текст.

Это возможно в дальнейшем, в семёрке усложнилось помещение изображений в буфер, я пока не разобрался.

Были некоторые глюки с окном.

Ну, очевидно просто не хватает мощности процессора, окно довольно "тяжёлое", особенно, когда скопированного много. Впрочем есть идея, как его "облегчить". Сейчас в контролы со скопированным текстом помещается весь текст полностью, несмотря на то, что весь он всё равно не виден. Достаточно помещать лишь ту часть, которая будет видна.

Malcev пишет:

Предлагаю, сделать условие - в настройках цвета цвет фона ячейки равен цвету выделения ячейки, то gui не перерисовываем, а то раздражают эти дрожания.

Хорошо, попробую.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

89

Re: AHK: Пишем удобный менеджер буфера обмена

teadrinker пишет:

Ну, очевидно просто не хватает мощности процессора

Этот глюк был одноразовый за 10 минут работы, окно было не "тяжёлое". Я не смог определить, с чем это связано, просто проинформировал.

А в чём удобство? В какой ситуации? К тому же непонятно, что будет вставляться по Ктрл-В, если ячеек несколько.

Как - что? Последнее взятое в буфер. Или выбранное из нового "менеджера буфера обмена". Удобство в том, что окно скрипта не будет загораживать часть окна моей программы.

90

Re: AHK: Пишем удобный менеджер буфера обмена

ypppu
Да, я просто не сразу понял, о чём речь.

91 (изменено: stealzy, 2015-02-04 16:46:55)

Re: AHK: Пишем удобный менеджер буфера обмена

Шрифты, значки и текст - это дело десятое.
Как по мне, в идеале никаких значков и текста "отсебя" в списке видно быть не должно.

Попробовал менеджеры из обзора lifehacker
Обзор ни о чем, никаких деталей, так что напишу свой, хе-хэ.

+ Сравнение 4 "the best" ClipBoard-менеджеров:

Ditto:
+живой поиск в выпадающем списке
+при вызове списка выделение стоит сразу на 2 объекте
+в списке работают {Home}{End}{PgDn}{PgUp}
+показывае скролл в списке только по наведению мышки
+отдельные клавиши для 2 и 3 объектов в списке
+автозамены; белый/черный список окон(WinTitle) для работы
-колхозный внешний вид
-окно не исчезает после вставки текста
-нерационально распределено место - в список вмещается мало записей

ClipX
+в зависимости от активности каретки показывает список либо возле нее, либо возле мышки
+при вызове списка выделение стоит сразу на 2 объекте
+по клике на иконку показывает список
+отдельный хоткей для 2 объекта в списке
-не продуманный "живой" поиск

CLCL
+возможность вывести список в несколько колонок
+настраиваемая задержка перед попадание объекта в список
+отдельные настройки для окон (Win Title & Class); возможность вызова функций dll по условию или хоткею
-нет живого поиска

VirtualAssist (по предложению  dredj)
+красивый гуи (несистемный)
+по наведению курсора на объект в списке  появляются кнопки для удаления и редактирования объекта
-все прочее
(а интеграция с заметками, пунто-свитчером, проверкой орфографии
к менеджеру буфера отношения все же не имеет)

Прежде, чем начать писать свой мопед, решил глянуть на форуме AHK.
Вот что нашел:
ClipJump
Живой поиск, он нереально крут! ( галка в настройках)
Модульный, няшный, поддерживает плагины -).
Я еще не все изучил..
У него масса недостатков, но это все можно поправить. Главное что написано на AHK.
(Брр, опять залипают модификаторы из-за ControlGetFocus. Почему только у меня так? Ладно, поправить дело нехитрое=))
В любом случае сделать форк мне кажется разумнее, чем писать с 0.
А может и пулл-реквеста хватит, это же гитхаб=).
------------
2 dredj: Добавил в обзор.

92

Re: AHK: Пишем удобный менеджер буфера обмена

stealzy пишет:

Вот кстати, что нашел на autohotkey.com:
ClipJump
Модульный, няшный. Можно писать плагины -).

Это не наш путь. Поддержим отечественного производителя!

93 (изменено: dredj, 2015-02-04 14:08:28)

Re: AHK: Пишем удобный менеджер буфера обмена

stealzy, этот пробовали?

teadrinker есть такой софт-комбайн http://virtassist.eu, вот там буфер реализован так как надо по моему, особенно GUI. Если интересно самому попробовать - https://yadi.sk/d/5KJqqU6PeRVN9, только проект это заброшен и кое-где недоделан.

94

Re: AHK: Пишем удобный менеджер буфера обмена

dredj пишет:

В любом случае сделать форк мне кажется разумнее, чем писать с 0.

Может, и разумнее, но менее интересно. А за обзор спасибо, в своём варианте можно будет взять всё лучшее из других, и избежать их недостатков.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

95

Re: AHK: Пишем удобный менеджер буфера обмена

Новая версия. Ссылка на код в виде файла.

;********************************** Описание **************************************
Readme =
(
Скрипт сохраняет определённое в настройках количество состояний буфера обмена
(только текстовые данные, включая пути к скопрованным файлам) и даёт возможность
вводить их повторно.

Перед началом работы нескомпилированный скрипт загружает dll-файл с иконками,
должен быть доступ к интернету.

При каждом изменении буфера обмена новые данные помещаются в верхнюю ячейку окна
скрипта, остальные сдвигаются вниз по принципу стека. Ввести текст из ячейки можно
кликнув по ней левой кнопкой мыши. Также для ввода и удаления данных из ячеек
можно воспользоваться контекстным меню, появляющимся при клике правой кнопкой мыши.

По умолчанию текст вводится в активное окно (само окно скрипта всегда неактивно).
Это поведение можно изменить через меню «Настройки».

Скрипт сохраняет историю вставленных данных, переключиться на её отображение можно
через меню Содержимое -> Вставленное, а также кликнув по иконке окна в строке
заголовка.

Если содержимое ячейки не видно полностью, посмотреть его можно, нажав над ней
колесо мыши.

Окно скрипта не имеет кнопки на панели задач. Чтобы восстановить свёрнутое окно,
можно воспользоваться меню «Показать» иконки скрипта в трее, либо просто один раз
кликнуть по ней. Кроме того, есть возможность задать горячую клавишу для
показа/скрытия окна в меню «Настройки».

Скрипт регистрирует в системе сообщение "WM_INFO", чтобы можно было заблокировать
его работу извне на время. Для получения информации о текущем состоянии блокировки
окна отправьте любому окну скрипта сообщение "WM_INFO" с wp = 1, в ответ придёт,
если заблокировано — 1, если разблокировано — 0. Для блокировки окна отпрвьте
"WM_INFO" c lp = 1, для разблокировки lp = 0.

При завершении работы скрипт сохраняет все данные в файл `%A_ScriptDir`%\data, и
загружает их из него в следующей сессии.

Для корректной работы скомпилированного варианта из exe-файла все дефолтные иконки
должны быть удалены, вместо них должны быть добавлены иконки из dll-файла в том же
порядке, первая с именем 159 и далее.
Если файловая система NTFS, скомпилированный скрипт пишет сохраняемые данные не в
отдельный файл, а в NTFS-поток по адресу `%A_ScriptFullPath`%:data

Если вы используете в Windows 7 тему "Классика", при выделении ячейки цветом может
происходить неприятное мелькание текста. В этом случае через меню «Вид» можно
указать цвет выделения равный цвету фона ячейки, тогда при наведении курсора на
ячейку её цвет меняться не будет.

Вопросы по работе скрипта можно задать здесь:
http://forum.script-coding.com/viewtopic.php?id=10385

© teadrinker
)
/*
***************************** Структура файла "data" *****************************

UChar          // Количество блоков с данными из "скопированного"
Блоки с данными
{
   UInt        // Длина строки данных
   Str         // Строка данных
}

UChar          // Количество блоков с данными из "вставленного"
Блоки с данными
{
   UInt        // Длина строки данных
   Str         // Строка данных
}

UShort         // X-координата окна
UShort         // Y-координата окна
UShort         // ширина клиентской части окна
UShort         // высота клиентской части окна

UChar          // SendVariant, возможные значения 1, 3, 4
UChar          // Длина строки с названием и классом связанного окна
Str            // Строка с названием и классом связанного окна
UChar          // Длина строки с Match Mode
Str            // Строка с Match Mode, возможные значения: "1", "2", "3", "RegEx"

UChar          // Длина строки с названием шрифта
Str            // Строка с названием шрифта
UChar          // Размер шрифта, возможные значения: 8 - 15
UChar          // Качество рендеринга, возможные значения 0 - 5
UInt           // Цвет шрифта в RGB

UChar          // Количество строк в ячейке
UChar          // Общее количество ячеек

UInt           // Цвет основного фона окна
UInt           // Цвет фона ячеек
UInt           // Цвет выделения ячеек
UInt           // Цвет области кнопок
UChar          // Стиль рамки, возможные значения:
               //   7 (SS_BLACKFRAME), 8 (SS_GRAYFRAME), 9 (SS_WHITEFRAME)
UChar          // AlwaysPlaceIntoClipboard, возможные значения 0 и 1
UChar          // HideAfterSend, возможные значения 0 и 1

UChar          // Длина строки с названием горячей клавиши для показа/скрытия окна
Str            // Строка с названием горячей клавиши для показа/скрытия окна

UChar          // LoadOnStartup, возможные значения 0 и 1
UChar          // ToggleToSaved, возможные значения 0 и 1

**********************************************************************************
*/

#NoEnv
#SingleInstance, Off

if FileExist(A_ScriptDir "\BufferIcons.dll")
   FileDelete, % A_ScriptDir "\BufferIcons.dll"

IcoDll := A_IsCompiled ? A_ScriptFullPath : A_ScriptDir "\ClipMgrIcons.dll"
if !FileExist(IcoDll)  {
   if !Ping("dropbox.com")  {
      MsgBox, Невозможно загрузить файл с иконками`, проверьте подключение к интернету!
      ExitApp
   }
   URLDownloadToFile, % "https://dl.dropboxusercontent.com/s/lsuumcs9f5orag6/ClipMgrIcons.dll", % IcoDll
}
if !A_IsCompiled
   Menu, Tray, Icon, %IcoDll%, 1
SetBatchLines, -1
SetWinDelay, 0
CoordMode, Mouse
StringCaseSense, Locale

global HTCAPTION := 2, HTSYSMENU := 3, SC_MAXIMIZE := 0xF030, SC_MINIMIZE := 0xF020
     , SS_BLACKFRAME := 0x7, SS_GRAYFRAME := 0x8, SS_WHITEFRAME := 0x9, WS_EX_NOACTIVATE := 0x8000000
     , IMAGE_CURSOR := 2, OCR_HAND := 32649, LR_SHARED := 0x8000, LR_DEFAULTSIZE := 0x40
     , WM_SETCURSOR := 0x20, WM_NCMOUSEMOVE := 0xA0, WM_VSCROLL := 0x115 , WM_MOUSEMOVE := 0x200
     , TargetWindow, AssociatedWindow, MatchMode, SendVariant, ScrollHeight, LastActiveWindowID
     , AlwaysPlaceIntoClipboard, HideAfterSend, ColorCell, ColorSelect
     , FontName, FontSize, FontQuality, FontColor, RowNumber
     , hMain, hBack, hSetWindow, hSetFont, hSetNumber, hSetColor, hReadme, hSetHotkey
     , hCursorHand := LoadCursorHand(), hBall, TextH
     , Block := 1, TopWork := 5, VisibleCell := 8, AllCell, oSaved := [], oEntered := [], CurrentContent := "Saved"
     , oQual := [ "DEFAULT_QUALITY"          , "DRAFT_QUALITY"        , "PROOF_QUALITY"
                , "NONANTIALIASED_QUALITY", "ANTIALIASED_QUALITY", "CLEARTYPE_QUALITY" ]
   
SB_LINEUP := 0, SB_LINEDOWN := 1
HKM_SETHOTKEY := 0x401
HOTKEYF_ALT := 0x4, HOTKEYF_CONTROL := 0x2, HOTKEYF_EXT := 0x80, HOTKEYF_SHIFT := 0x1
EM_SETCUEBANNER := 0x1501, EM_SETSEL := 0xB1, OBJID_VSCROLL := 0xFFFFFFFB
STATE_SYSTEM_INVISIBLE := 0x8000
oFonts := GetCyrillicFontNames()

VarSetCapacity(SBI, 60)
NumPut(60, SBI)
DriveGet, FIleSystem, FS, % SubStr(A_ScriptDir, 1, 2)
DataFile := A_ScriptDir "\" . ((FIleSystem = "NTFS" && A_IsCompiled) ? A_ScriptName . ":" : "") . "data"
param = %1%

if !FileExist(DataFile)
   MatchMode := 2, SendVariant := 3
   , PrevFontName    := FontName    := "Arial"
   , PrevFontSize    := FontSize    := 10
   , PrevFontQuality := FontQuality := 5
   , PrevFontColor   := FontColor   := 0
   , PrevRowNumber   := RowNumber   := 3
   , PrevAllCell     := AllCell     := 24
   , PrevColorBack   := ColorBack   := 0xFFFFFF
   , PrevColorCell   := ColorCell   := 0xFFFFFF
   , PrevColorSelect := ColorSelect := 0xFFFEAF
   , PrevColorButton := ColorButton := 0xD0D0D0
   , PrevFrameStyle  := FrameStyle  := SS_GRAYFRAME
   , AlwaysPlaceIntoClipboard := HideAfterSend := 0
   , Hk := DisplayHkPrev := "Нет"
   , LoadOnStartup := 0
   , ToggleToSaved := 1
else
{
   oFile := FileOpen(DataFile, "r", "UTF-16-RAW")
   Loop % oFile.ReadUChar()
      bytes := oFile.ReadUInt()
      , oSaved.Insert(oFile.Read(bytes))
      
   Loop % oFile.ReadUChar()
      bytes := oFile.ReadUInt()
      , oEntered.Insert(oFile.Read(bytes))

   MainGuiX := oFile.ReadUSHort(), MainGuiY := oFile.ReadUSHort()
   MainGuiW := oFile.ReadUSHort(), MainGuiH := oFile.ReadUSHort()

   SendVariant := oFile.ReadUChar()
   bytes := oFile.ReadUChar()
   AssociatedWindow := oFile.Read(bytes)
   bytes := oFile.ReadUChar()
   MatchMode := oFile.Read(bytes)
   
   Fontbytes := oFile.ReadUChar()
   PrevFontName    := FontName    := oFile.Read(Fontbytes)
   PrevFontSize    := FontSize    := oFile.ReadUChar()
   PrevFontQuality := FontQuality := oFile.ReadUChar()
   PrevFontColor   := FontColor   := oFile.ReadUInt()
   
   PrevRowNumber   := RowNumber   := oFile.ReadUChar()
   PrevAllCell     := AllCell     := oFile.ReadUChar()
   
   PrevColorBack   := ColorBack   := oFile.ReadUInt()
   PrevColorCell   := ColorCell   := oFile.ReadUInt()
   PrevColorSelect := ColorSelect := oFile.ReadUInt()
   PrevColorButton := ColorButton := oFile.ReadUInt()
   PrevFrameStyle  := FrameStyle  := oFile.ReadUChar()
   
   AlwaysPlaceIntoClipboard := oFile.ReadUChar()
   HideAfterSend := oFile.ReadUChar()
   
   bytes := oFile.ReadUChar()
   Hk := oFile.Read(bytes)
   DisplayHkPrev := GetDisplayHk(Hk)
   
   LoadOnStartup := oFile.ReadUChar()
   ToggleToSaved := oFile.ReadUChar()
   
   oFile.Close()

   if (MainGuiX = "" || MainGuiX > A_ScreenWidth - 50)
      Error .= "Ошибка чтения данных MainGuiX`nБудет присвоено значение по умолчанию`n`n", MainGuiX := ""
   if (MainGuiY = "" || MainGuiY > A_ScreenHeight - 50)
      Error .= "Ошибка чтения данных MainGuiY`nБудет присвоено значение по умолчанию`n`n", MainGuiY := ""
   if (MainGuiW = "" || MainGuiW > A_ScreenWidth)
      Error .= "Ошибка чтения данных MainGuiW`nБудет присвоено значение по умолчанию`n`n", MainGuiW := ""
   if (MainGuiH = "" || MainGuiH > A_ScreenHeight)
      Error .= "Ошибка чтения данных MainGuiH`nБудет присвоено значение по умолчанию`n`n", MainGuiH := ""
   if SendVariant not in 1,3,4
      Error .= "Ошибка чтения данных SendVariant`nБудет присвоено SendVariant := 3 (Посылать в активное окно)`n`n", SendVariant := 3
   if MatchMode not in 1,2,3,RegEx
      Error .= "Ошибка чтения данных MatchMode`nБудет присвоено MatchMode := 2`n`n", MatchMode := 2
   for k, font in (oFonts)
      if (FontName = font && Success := 1)
         break
   if !Success
      Error .= "Ошибка чтения данных FontName`nБудет присвоено FontName := ""Arial""`n`n", FontName := "Arial"
   if (FontSize < 8 || FontSize > 15)
      Error .= "Ошибка чтения данных FontSize`nБудет присвоено FontSize := 10`n`n", FontSize := 10
   if FontQuality not between 1 and 5
      Error .= "Ошибка чтения данных FontQuality`nБудет присвоено FontQuality := 5`n`n", FontQuality := 5
   if (FontColor > 0xFFFFFF || FontColor = "")
      Error .= "Ошибка чтения данных FontColor`nБудет присвоено FontColor := 0x000000`n`n", FontColor := 0
   if RowNumber not between 1 and 5
      Error .= "Ошибка чтения данных RowNumber`nБудет присвоено RowNumber := 3`n`n", RowNumber := 3
   if AllCell not between 8 and 64
      Error .= "Ошибка чтения данных AllCell`nБудет присвоено AllCell := 24`n`n", AllCell := 24
   if (ColorBack > 0xFFFFFF || ColorBack = "")
      Error .= "Ошибка чтения данных ColorBack`nБудет присвоено ColorBack := 0xFFFFFF`n`n", ColorBack := 0xFFFFFF
   if (ColorCell > 0xFFFFFF || ColorCell = "")
      Error .= "Ошибка чтения данных ColorCell`nБудет присвоено ColorCell := 0xFFFFFF`n`n", ColorCell := 0xFFFFFF
   if (ColorSelect > 0xFFFFFF || ColorSelect = "")
      Error .= "Ошибка чтения данных ColorSelect`nБудет присвоено ColorSelect := 0xFFFEAF`n`n", ColorSelect := 0xFFFEAF
   if (ColorButton > 0xFFFFFF || ColorButton = "")
      Error .= "Ошибка чтения данных ColorButton`nБудет присвоено ColorButton := 0xD0D0D0`n`n", ColorButton := 0xD0D0D0
   if FrameStyle not in 7,8,9
      Error .= "Ошибка чтения данных FrameStyle`nБудет присвоено FrameStyle := 0x8`n`n", FrameStyle := 8
   if AlwaysPlaceIntoClipboard not in 0,1
      Error .= "Ошибка чтения данных AlwaysPlaceIntoClipboard`nБудет присвоено AlwaysPlaceIntoClipboard := 0`n`n", AlwaysPlaceIntoClipboard := 0
   if HideAfterSend not in 0,1
      Error .= "Ошибка чтения данных HideAfterSend`nБудет присвоено HideAfterSend := 0`n`n", HideAfterSend := 0
   if Hk = ""
      Error .= "Ошибка чтения данных Hk`nБудет присвоено Hk := ""Нет""`n`n", Hk := "Нет"
   if LoadOnStartup not in 0,1
   {
      LoadOnStartup := param = "Hide"
      Error .= "Ошибка чтения данных LoadOnStartup`nБудет присвоено LoadOnStartup := " . LoadOnStartup . "`n`n"
   }
   if ToggleToSaved not in 0,1
      Error .= "Ошибка чтения данных ToggleToSaved`nБудет присвоено ToggleToSaved := 1`n`n", ToggleToSaved := 1
   
   if Error
      MsgBox, % Trim(Error, "`n")
   Error := Success := ""
}

Menu, Tray, NoStandard
Menu, Tray, Add, Открыть новое окно, OpenNewWindow
Menu, Tray, Add, % "Скрыть окно" . (DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev), HideShowMainWindow
If !A_IsCompiled
   Menu, Tray, Add, Reload, Reload
Menu, Tray, Add, Выход, MainGuiClose

Menu, Actions, Add, Удалить, Delete
Menu, Actions, Icon, Удалить, %IcoDll%, 2
Menu, Actions, Add, Отправить             LButton, Send
Menu, Actions, Icon, Отправить             LButton, %IcoDll%, 3

Menu, Settings, Add, Указать связанное окно, SetAssociatedWindow
Menu, Settings, Add, Выбрать целевое окно правой кнопкой, SelectWindow
Menu, Settings, Add
Menu, Settings, Add, Посылать в связанное окно, SendToAssociatedWindow
Menu, Settings, Add, Посылать в целевое окно, SendToTargetWindow
Menu, Settings, Add, Посылать в активное окно, SendToActiveWindow
Menu, Settings, Add, Только помещать в буфер обмена, SendOnlyIntoClipboard
Menu, Settings, Check, % SendVariant = 1 ? "Посылать в связанное окно"
                       : SendVariant = 3 ? "Посылать в активное окно" : "Только помещать в буфер обмена"
Menu, Settings, Add
Menu, Settings, Add, Всегда помещать в буфер обмена, AlwaysPlaceIntoClipboard
if AlwaysPlaceIntoClipboard
   Menu, Settings, Check, Всегда помещать в буфер обмена

Menu, Settings, Add, Скрывать после вставки и вывода курсора за пределы окна, HideAfterSend
if HideAfterSend
   Menu, Settings, Check, Скрывать после вставки и вывода курсора за пределы окна

Menu, Settings, Add, Переключать на «Скопированное» при изменении буфера обмена, ToggleToSaved
if ToggleToSaved
   Menu, Settings, Check, Переключать на «Скопированное» при изменении буфера обмена

Menu, Settings, Add
Menu, Settings, Add, % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHkPrev, SetHotkey
Menu, Settings, Add
Menu, Settings, Add, Загружать при старте системы, LoadOnStartup
if LoadOnStartup
   Menu, Settings, Check, Загружать при старте системы
Menu, Settings, Add
Menu, Settings, Add, Readme, ShowReadme
Menu, Settings, Icon, Readme, %IcoDll%, 7

Menu, View, Add, Выбрать шрифт, SetFont
Menu, View, Icon, Выбрать шрифт, %IcoDll%, 4
Menu, View, Add, Количество строк и ячеек, SetNumber
Menu, View, Icon, Количество строк и ячеек, %IcoDll%, 5

Menu, View, Add, Настройки цвета, SetColor
Menu, View, Icon, Настройки цвета, %IcoDll%, 6

Menu, Content, Add, Скопированное, ShowSaved
Menu, Content, Add, Вставленное, ShowEntered
Menu, Content, Check, Скопированное

Menu, MenuBar, Add, Настройки, :Settings
Menu, MenuBar, Add, Вид, :View
Menu, MenuBar, Add, Содержимое, :Content

Gosub, CreateMainWindow

OnMessage(WM_SETCURSOR  , "WM_MOUSEMOVE")
OnMessage(WM_NCMOUSEMOVE, "WM_MOUSEMOVE")
OnMessage(0x0A1, "WM_NCLBUTTONDOWN")
OnMessage(0x0A2, "WM_NCLBUTTONUP")
OnMessage(0x0A3, "WM_NCLBUTTONDBLCLK")
OnMessage(0x102, "WM_CHAR")
OnMessage(0x112, "WM_SYSCOMMAND")
OnMessage(0x115, "WM_VSCROLL")
OnMessage(0x200, "WM_MOUSEMOVE")
OnMessage(0x205, "WM_RBUTTONUP")
OnMessage(0x207, "WM_MBUTTONDOWN")
OnMessage(0x404, "AHK_NOTIFYICON")
OnMessage(DllCall("RegisterWindowMessage", Str, "WM_INFO"), "WM_INFO")
SetTimer, Unblock, -1000
OnExit, SaveData
if (Hk = "Нет" || Hk = "")
   Return

try  {
   Hotkey, If
   Hotkey, %Hk%, HideShowMainWindow, On
}
catch e  {
   Menu, Settings, Rename, % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHkPrev
                         , % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHkPrev := "Нет"
   Menu, Tray, Rename, % "Скрыть окно" . (DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev), Скрыть окно
   MsgBox, 16, Ошибка установки горячей клавиши, % e.Message "`nГорячая клавиша """ . Hk . """ для показа/скрытия окна не установлена!"
}
Return

CreateMainWindow:
   Gui, Main: New, +hwndhMain +AlwaysOnTop +E%WS_EX_NOACTIVATE% +Owner +Resize +MinSize218x -DPIScale
   Gui, Main: Menu, MenuBar
   Gui, Main: Color, % Dec2Hex(ColorButton)

   Gui, Back: +ParentMain -Caption +Border +hwndhBack -DPIScale
   Gui, Back: Color, % Dec2Hex(ColorBack)
   SetFormat, IntegerFast, H
   FontColor += 0, FontColor .= ""
   SetFormat, IntegerFast, D
   Loop % AllCell
   {
      Gui, Work%A_Index%: Default
      Gui, +ParentBack -Caption +hwndhWork%A_Index% -DPIScale
      Gui, Margin, 0, 0
      Gui, Color, % Dec2Hex(ColorCell)
      Gui, Add, Text, vBorder +%FrameStyle% x0 y0 w208
      Gui, Font, s%FontSize% q%FontQuality%, % FontName
      FontColor := Dec2Hex(FontColor)
         
      Gui, Add, Text, vText gSend_ x2 y2 w204 r%RowNumber% BackgroundTrans -Wrap c%FontColor%
      GuiControlGet, Text, Pos
      GuiControl, Move, Border, % "h" TextH + 4
      Gui, Show, % "x5 y" 5 + (A_Index - 1) * (TextH + 8) " w208 h" TextH + 4 " Hide"
   }
   Gui, Back: Show, % "NA x-1 y0 w218 h" (TextH + 8)*VisibleCell + 6

   Gui, Main: Default
   Gui, Font, s10, Verdana
   Gui, Add, Button, % "x6 y" (TextH + 8)*VisibleCell + 18 " w101 h22", Clear
   Gui, Add, Button, gBlock vBlock x+5 yp wp hp, Block
   
   SetContent()
   GuiControl, Work1: Focus, Static1
   ((MainGuiW = "" || MainGuiH = "") && (MainGuiW := 218) && (MainGuiH := (TextH + 8)*VisibleCell + 40))
   Bool := (MainGuiX != "" && MainGuiY != "")
   Gui, Show, % (Bool ? "x" MainGuiX " y" MainGuiY " " : "") . (param = "Hide" ? "Hide" : "NA") . " w" MainGuiW " h" MainGuiH, Скопированное
   
   if (param = "Hide")  {
      TrayMenuPostFix := DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev
      Menu, Tray, Rename, % "Скрыть окно" . TrayMenuPostFix, % "Показать окно" . TrayMenuPostFix
   }
   Return

MainGuiSize:
   DllCall("GetScrollBarInfo", Ptr, hBack, UInt, OBJID_VSCROLL, Ptr, &SBI)
   , bool  := NumGet(&SBI + 36, "UInt") = STATE_SYSTEM_INVISIBLE
   , delta := NumGet(&SBI + 12, "UInt") - NumGet(&SBI + 4, "UInt")
   
   Gui, Back:Show, % "NA x-1 y0 w" A_GuiWidth + 1 " h" A_GuiHeight - 36 - (bool ? 0 : delta)
   Loop % AllCell
   {
      Gui, Work%A_Index%: Default
      Gui, Show, % "NA x5 y" TopWork + (A_Index - 1) * (TextH + 8) " w" w := A_GuiWidth - 10
      GuiControlGet, Text
      if (Text = "")
         Gui, Show, Hide
      GuiControl, MoveDraw, Border, % "w" w " h" TextH + 4
      GuiControl, Move, Text, % "w" w - 4 " h" TextH
   }
   GuiControl, Main:MoveDraw, Button1, % "y" A_GuiHeight - 28
   GuiControl, Main:MoveDraw, Button2, % "x" A_GuiWidth - 101 - 6 " y" A_GuiHeight - 28
   UpdateScrollBar(hBack, ScrollHeight)
   GuiW := A_GuiWidth, GuiH := A_GuiHeight
   Return
   
ShowSaved:
ShowEntered:
   if (CurrentContent = SubStr(A_ThisLabel, 5))
      Return
   
   Menu, Content, UseErrorLevel
   Menu, Content, UnCheck, Вставленное
   Menu, Content, UnCheck, Скопированное
   Menu, Content, Check, % A_ThisMenuItem
   CurrentContent := SubStr(A_ThisLabel, 5)
   
   SetContent()
   Gui, Main:Show, NA, % A_ThisMenuItem
   Return

Reload:
   Reload
   Return

HideShowMainWindow:
   b := WinExist("ahk_id" hMain)
   Gui, Main:Show, % b ? "Hide" : "NA"
   TrayMenuPostFix := DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev
   Menu, Tray, Rename, % (b ? "Скрыть окно" : "Показать окно") . TrayMenuPostFix
                     , % (b ? "Показать окно" : "Скрыть окно") . TrayMenuPostFix
   Return

OpenNewWindow:
   Run, % A_ScriptFullPath,,, PID
   WinWait, ahk_pid %PID%
   WinGetPos, X, Y
   Loop 15
   {
      Sleep, 20
      WinMove, X - 15 < 0 ? (X + A_Index) : (X - A_Index), Y + A_Index
   }
   Return
   
SetFont:
   oDDL := GetDDLstrings(FontName, oFonts, FontQuality, FontSize)
   Gui, SetFont:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetFont -DPIScale
   Gui, SetFont:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma
   
   Gui, Add, Text,, Название шрифта:
   Gui, Add, DDL, vFontName x+25 yp-3 w153 r20 Sort, % oDDL.Font
   GuiControlGet, FontName, Pos

   Gui, Add, Text, x15 y+14, Качество рендеринга:
   Gui, Add, DDL, x%FontNameX% yp-3 w%FontNameW% vQuality, % oDDL.Qual
   
   Gui, Add, Text, x15 y+15, Размер шрифта:
   Gui, Add, DDL, x%FontNameX% yp-3 w38 vFontSize, % oDDL.Size
   
   Gui, Add, Button, vDef gDef x+5 yp-1 hp+2, Сбросить всё
   GuiControlGet, Def, Pos
   GuiControl, Move, Def, % "x" FontNameX + FontNameW - DefW
   Gui, Add, Button, % "vCancel x" FontNameX " y+20 hp w" FontNameW//2 - 2, Применить
   Gui, Add, Button, % "vOk x" FontNameX + FontNameW - (FontNameW//2 - 2) " yp hp wp Default", OK
   Gui, Add, Button, % "vApply x" FontNameX - FontNameW//2 - 4 " yp hp wp", Отмена
   
   ShowOwnerWindow("Шрифт", hSetFont)
   Return
   
Def:
   oDDL := GetDDLstrings("Arial", oFonts, 5, 10)
   Gui, SetFont: Default
   GuiControl,, FontName, % "|" . oDDL.Font
   GuiControl,, Quality , % "|" . oDDL.Qual
   GuiControl,, FontSize, % "|" . oDDL.Size
   Return
   
SetFontButtonПрименить:
SetFontButtonOK:
   Gui, SetFont:Submit, NoHide
   for k, v in oQual  {
      if (v = Quality)  {
         FontQuality := k - 1
         break
      }
   }

SetFontButtonОтмена:
SetFontGuiClose:
SetFontGuiEscape:
   if (A_ThisLabel != "SetFontButtonПрименить")
   {
      Gui, SetFont:Destroy
      WinActivate, ahk_id %LastActiveWindowID%
   }
   if (FontName = PrevFontName && FontSize = PrevFontSize && FontQuality = PrevFontQuality)
      Return
   
   if A_ThisLabel in SetFontButtonOK,SetFontButtonПрименить
   {
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetFont: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetFont: +OwnerMain
   }
   PrevFontName := FontName, PrevFontSize := FontSize, PrevFontQuality := FontQuality
   Return
   
SetNumber:
   oDDL := GetDDLstrings(RowNumber, AllCell)
   Gui, SetNumber:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetNumber -DPIScale
   Gui, SetNumber:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma

   Gui, Add, Text,, Количество строк в ячейке:
   Gui, Add, DDL, vRowNumber x+20 yp-4 w40, % oDDL.Rows
   GuiControlGet, RowNumber, Pos

   Gui, Add, Text, x15 y+13, Общее количество ячеек:
   Gui, Add, DDL, vAllCell x%RowNumberX% yp-4 w40, % oDDL.Cells
   
   ButtonWidth := (RowNumberX + RowNumberW - 10 - 15)//3
   Gui, Add, Button, % "x15 y+20 w" ButtonWidth + 1 " h23", Отмена
   Gui, Add, Button, x+5 yp wp hp, Применить
   Gui, Add, Button, x+5 yp wp hp Default, OK
   ShowOwnerWindow("Количество строк и ячеек", hSetNumber)
   Return
   
SetNumberButtonПрименить:
SetNumberButtonOK:
   Gui, SetNumber:Submit, NoHide

SetNumberButtonОтмена:
SetNumberGuiClose:
SetNumberGuiescape:
   if (A_ThisLabel != "SetNumberButtonПрименить")
   {
      Gui, SetNumber: Destroy
      WinActivate, ahk_id %LastActiveWindowID%
   }
   if (RowNumber = PrevRowNumber && PrevAllCell = AllCell)
      Return
   
   if A_ThisLabel in SetNumberButtonOK,SetNumberButtonПрименить
   {
      oSaved.Remove(AllCell + 1, oSaved.MaxIndex())
      oEntered.Remove(AllCell + 1, oEntered.MaxIndex())
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetNumber: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetNumber: +OwnerMain
   }
   PrevRowNumber := RowNumber, PrevAllCell := AllCell
   Return
   
ControlsCreate(var, text)
{
   global
   Gui, Add, Text, x15 y+15, % text
   SetFormat, IntegerFast, H
   Gui, Add, Edit, v%var% gChangeColor x+8 yp-3 w62 Limit6 Uppercase Center
      , % SubStr("00000" . SubStr(%var%+=0, 3), -5)
   SetFormat, IntegerFast, D
   GuiControlGet, %var%, Pos
   GuiControl, Move, % var, x%ColorBackX%
   ColoredGuiShow(var, "SetColor", %var%, "ColorChoose", ColorBackW, ColorBackH)
}
   
SetColor:
   Gui, SetColor:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetColor -DPIScale
   Gui, SetColor:Default
   Gui, Margin, 15, 15
   Gui, Font, s8, Tahoma
   
   ControlsCreate("ColorBack"  , "Цвет общего фона ячеек:")
   ControlsCreate("ColorCell"  , "Цвет фона ячейки:")
   ControlsCreate("ColorSelect", "Цвет выделения ячейки:")
   ControlsCreate("FontColor"  , "Цвет шрифта:")
   ControlsCreate("ColorButton", "Цвет фона кнопок:")
   
   Gui, Add, Text, x15 y+15, Цвет рамки ячейки:
   Gui, Add, Radio, % "x+15 yp" . (FrameStyle = SS_BLACKFRAME ? " Checked" : "") . " vRadio", Black
   Gui, Add, Radio, % "x+5 yp"  . (FrameStyle = SS_GRAYFRAME  ? " Checked" : ""), Gray
   Gui, Add, Radio, % "x+5 yp"  . (FrameStyle = SS_WHITEFRAME ? " Checked" : ""), White
   GuiControlGet, White, Pos, White
   
   Gui, Add, Button, vApply x15 y+20, Применить
   Gui, Add, Button, xp yp wp, Сбросить
   Gui, Add, Button, vCancel x+3 yp wp, Отмена
   GuiControlGet, Cancel, Pos
   GuiControl, Move, Apply, % "x" CancelX + CancelW + 3
   
   Gui, Add, Button, % "vOK x" CancelX + CancelW*2 + 6 " yp wp Default", OK
   GuiControlGet, OK, Pos
   
   ControlMetr := WhiteX + WhiteW > OKX + OKW ? "White" : "OK"
   ColorGuis := "ColorBack|ColorCell|ColorSelect|FontColor|ColorButton"
   Loop, parse, ColorGuis, |
      Gui, %A_LoopField%:Show, % "NA x" %ControlMetr%X + %ControlMetr%W - %A_LoopField%W - 1 " y" %A_LoopField%Y
   
   GuiControl, Focus, Static1
   ShowOwnerWindow("Настройки цвета в RGB", hSetColor)
   Return

ChangeColor:
   GuiControlGet, Color, SetColor:, %A_GuiControl%
   Gui, %A_GuiControl%: Color, % "0x" Color
   Return
   
ColorChoose:
   GuiControlGet, Color, SetColor:, %A_Gui%
   if (Color := ChooseColor("0x" . Color, hSetColor)) = -1
      Return
   
   Gui, %A_Gui%:Color, % Color
   GuiControl, SetColor:, %A_Gui%, % SubStr("00000" . SubStr(Color, 3), -5)
   Return
   
SetColorButtonСбросить:
   Gui, SetColor:Default
   GuiControl,, Edit1, FFFFFF
   GuiControl,, Edit2, FFFFFF
   GuiControl,, Edit3, FFFEAF
   GuiControl,, Edit4, 000000
   GuiControl,, Edit5, D0D0D0
   GuiControl,, Gray, 1
   Return

SetColorButtonОтмена:
SetColorGuiEscape:
SetColorGuiClose:
   Gui, SetColor:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return

SetColorButtonOK:
SetColorButtonПрименить:
   Gui, SetColor:Submit, NoHide
   Loop, parse, ColorGuis, |
      %A_LoopField% := "0x" . %A_LoopField%
   FrameStyle := [SS_BLACKFRAME, SS_GRAYFRAME, SS_WHITEFRAME][Radio]
   
   if ( ColorBack   != PrevColorBack
     || ColorCell   != PrevColorCell
     || ColorSelect != PrevColorSelect
     || FontColor   != PrevFontColor
     || ColorButton != PrevColorButton
     || FrameStyle  != PrevFrameStyle )
   {
      MainGuiW := GuiW, MainGuiH := GuiH
      WinGetPos, MainGuiX, MainGuiY,,, ahk_id %hMain%
      Gui, SetColor: -OwnerMain
      Gosub, CreateMainWindow
      Gui, SetColor: +OwnerMain
      PrevColorBack := ColorBack, PrevColorCell := ColorCell, PrevColorSelect := ColorSelect
      PrevFontColor := FontColor, PrevColorButton := ColorButton, PrevFrameStyle := FrameStyle
   }
   
   if (A_ThisLabel = "SetColorButtonOK")
      Gosub, SetColorGuiClose
   Return
   
SetAssociatedWindow:
   Gui, SetWindow:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetWindow -DPIScale
   Gui, SetWindow:Default
   Gui, Margin, 12, 12
   Gui, Add, Link, y10, Укажите <a href="http://ahkscript.org/docs/misc/WinTitle.htm">заголовок и класс</a> связанного окна:
   Gui, Add, Edit, y+10 wp h21 hwndhEdit vAssociatedWindow

   Gui, Add, Link, y+25, <a href="http://ahkscript.org/docs/commands/SetTitleMatchMode.htm">Match Mode:</a>
   Gui, Add, Radio, vMatchMode x+15 yp, 1
   GuiControlGet, Radio1, pos, Button1
   
   Gui, Add, Radio, x+7 yp, 2
   Gui, Add, Radio, x+7 yp, 3
   Gui, Add, Radio, x+7 yp, RegEx
   oRadio := {1: "Button1", 2: "Button2", 3: "Button3", RegEx: "Button4"}
   GuiControl,, % oRadio[MatchMode], 1
   GuiControlGet, Radio4, pos, Button4
   
   Gui, Add, GroupBox, % "x" (xGr := Radio1X - 9) " y" Radio1Y - 17
                      . " w" (wGr := Radio4X - Radio1X + 9 + Radio4W + 4) " h" Radio4H + 29
   GuiControl, Move, Edit1, % "w" xGr - 12 + wGr
   
   Gui, Add, Button, % "vSetWindow x" xGr + wGr - 90 " y+12 w90 h23 Default", OK
   Gui, Add, Button, gSetWindowGuiCancel xp-95 yp wp hp, Указать позже
   GuiControl, Focus, Button2
   if !AssociatedWindow
      PostMessage, EM_SETCUEBANNER, 0, " Блокнот ahk_class Notepad",, ahk_id %hEdit%
   else
   {
      GuiControl,, AssociatedWindow, % AssociatedWindow
      PostMessage, EM_SETSEL, 0, -1,, ahk_id %hEdit%
      GuiControl, Focus, Edit1
   }
   
   ShowOwnerWindow("Связанное окно", hSetWindow)
   Return
   
SetWindowButtonOK:
   Gui, SetWindow:Submit, NoHide
   (MatchMode = 4 && MatchMode := "RegEx")
   if (AssociatedWindow = "")
   {
      MsgBox, 4144,, Укажите связанное окно в поле редактирования!
      Return
   }
   Gosub, SendToAssociatedWindow

SetWindowGuiEscape:
SetWindowGuiCancel:
SetWindowGuiClose:
   Gui, SetWindow:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return
   
SendToAssociatedWindow:
SendToTargetWindow:
SendToActiveWindow:
SendOnlyIntoClipboard:
   oSendVariant := { SendToAssociatedWindow: {Variant: 1, Menu: "Посылать в связанное окно"}
                   , SendToTargetWindow:     {Variant: 2, Menu: "Посылать в целевое окно"}
                   , SendToActiveWindow:     {Variant: 3, Menu: "Посылать в активное окно"}
                   , SendOnlyIntoClipboard:  {Variant: 4, Menu: "Только помещать в буфер обмена"} }
   
   SendVariant := oSendVariant[A_ThisLabel].Variant
   for k, v in oSendVariant
      Menu, Settings, % k = A_ThisLabel ? "Check" : "UnCheck", % v.Menu
   Return

Send_:
   Send(A_Gui)
   Return

SelectWindow:
   CrossHair(), CrossHair := 1
   Hotkey, If
   Hotkey, RButton Up, Choice, On
   Return
   
Choice:
   CrossHair(0), CrossHair := ""
   Hotkey, RButton Up, Off
   MouseGetPos,,, TargetWindow
   if (TargetWindow = hMain)
      Goto, SendToActiveWindow
   Send, {Click}
   Gosub, SendToTargetWindow
   TargetWndShow(TargetWindow)
   Return
   
AlwaysPlaceIntoClipboard:
   Menu, Settings, ToggleCheck, Всегда помещать в буфер обмена
   AlwaysPlaceIntoClipboard := !AlwaysPlaceIntoClipboard
   Return
   
HideAfterSend:
   Menu, Settings, ToggleCheck, Скрывать после вставки и вывода курсора за пределы окна
   HideAfterSend := !HideAfterSend
   Return
   
ToggleToSaved:
   Menu, Settings, ToggleCheck, Переключать на «Скопированное» при изменении буфера обмена
   ToggleToSaved := !ToggleToSaved
   Return
   
SetHotkey:
   PrevHk := Hk, wParam := "", HIBYTE := 0, oMods := {"!": HOTKEYF_ALT, "+": HOTKEYF_SHIFT, "^": HOTKEYF_CONTROL}
   RegExMatch(Hk, "^(([!\+\^]+)|)(.*)", match)
   Loop, parse, match2
      HIBYTE |= oMods[A_LoopField]
   wParam := (HIBYTE << 8)|GetKeyVK(match3)
   
   Gui, SetHotkey:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhSetHotkey -DPIScale
   Gui, SetHotkey:Default
   Gui, Margin, 15, 15
   Gui, Font,, Tahoma
   
   Gui, Add, Text,, Введите горячую клавишу:
   Gui, Add, Hotkey, vHk gHk hwndhHk x+15 yp-3 w110
   GuiControlGet, Hk, Pos
   Gui, Add, Text, x15 y+7, Или напечатайте
   Gui, Add, Link, x15 y+2, в <a href="http://ahkscript.org/docs/Hotkeys.htm">AHK-совместимом формате</a>:
   Gui, Add, Edit, vEdit x%HkX% yp-8 w%HkW%
   
   Gui, Add, Button, vCancel x15 y+17, Отмена
   GuiControlGet, Cancel, Pos
   (CancelW < 80 && CancelW := 80)
   
   Gui, Add, Button, x15 yp, OK
   GuiControl, Move, OK, % "x" HkX + HkW - CancelW " w" CancelW
   GuiControl, Move, Cancel, % "x" HkX + HkW - CancelW*2 - 5 " w" CancelW
   
   ShowOwnerWindow("Горячая клавиша для показа/скрытия окна", hSetHotkey)

   SendMessage, HKM_SETHOTKEY, wParam,,, ahk_id %hHk%
   GuiControlGet, Hk
   Gosub, Hk
   Return
   
Hk:
   RegExMatch(Hk, "[A-Z]$", found)
   StringReplace, Hk, Hk, % found, % "vk" . SubStr(Dec2Hex(GetKeyVK(found)), 3)
   RegExMatch(Hk, "Numpad.+$", found)
   StringReplace, Hk, Hk, % found, % "sc" . SubStr(Dec2Hex(GetKeySC(found)), 3)
   GuiControl, SetHotkey:, Edit, % Hk
   Return

SetHotkeyButtonOK:
   GuiControlGet, Edit, SetHotkey:
   if InStr(Edit, " & ")  {
      MsgBox, Горячие клавиши с символом "&" не поддерживаются!
      Return
   }
   try  {
      Hotkey, If
      Hotkey, %Edit%, HideShowMainWindow, On
   }
   catch e  {
      Hk := PrevHk
      MsgBox, 16, Ошибка установки горячей клавиши, % e.Message "`nНовая горячая клавиша для показа/скрытия окна не установлена!"
                                                              . "`nОстаётся в силе горячая клавиша """ . DisplayHkPrev . """"
      Return
   }
   
   Hk := Edit
   if !(PrevHk = "Нет" || PrevHk = "" || PrevHk = Hk)
      Hotkey, %PrevHk%, Off
   DisplayHk := GetDisplayHk(Edit)
   Menu, Settings, UseErrorLevel
   Menu, Settings, Rename, % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHkPrev
                         , % "Указать горячую клавишу для показа/скрытия окна" A_Tab DisplayHk
   PrevTrayMenuName := WinExist("ahk_id" hMain) ? "Скрыть окно" : "Показать окно"
   Menu, Tray, Rename, % PrevTrayMenuName . (DisplayHkPrev = "Нет" ? "" : A_Tab . DisplayHkPrev)
                     , % PrevTrayMenuName . (DisplayHk = "Нет" ? "" : A_Tab . DisplayHk)
   DisplayHkPrev := DisplayHk
   
SetHotkeyButtonОтмена:
   if (A_ThisLabel = "SetHotkeyButtonОтмена")
      Hk := PrevHk
   Gui, SetHotkey:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return
   
LoadOnStartup:
   if !(LoadOnStartup := !LoadOnStartup)
      FileDelete, %A_Startup%\ClipboardMgr.lnk
   else  {
      try FileCreateShortcut, % A_ScriptFullPath, %A_Startup%\ClipboardMgr.lnk,, Hide
      catch  {
         MsgBox, 16, Ошибка создания ярлыка, Не удалось создать ярлык в папке автозагрузки
         LoadOnStartup := 0
         Return
      }
   }
   Menu, Settings, ToggleCheck, Загружать при старте системы
   Return
   
MainButtonClear:
   o%CurrentContent% := [], SetContent()
   Return

Block:
   GuiControl,, Block, % (Block := !Block) ? "Unblock" : "Block"
   Return

Unblock:
   Block := ""
   Return

OnClipboardChange:
   if Block || (Clipboard == oSaved[1] && StrLen(Clipboard) = StrLen(oSaved[1])) || A_EventInfo != 1
      Return
   
   RemoveExistingAddNew(oSaved, Clipboard)
   if (CurrentContent = "Entered")  {
      (ToggleToSaved && ChangeContent())
      Return
   }
   
   SetContent()
   Return

MainGuiClose:
   ExitApp
   
ShowReadme:
   Gui, Readme:New, +ToolWindow +OwnerMain +AlwaysOnTop +LastFound +hwndhReadme -DPIScale
   Gui, Readme:Default
   Gui, Color, White
   Gui, Margin, 0, 0
   Gui, Font, s9, Tahoma
   Gui, Add, Edit, ReadOnly, % Readme
   ShowOwnerWindow("Readme", hReadme)
   PostMessage, EM_SETSEL, 0, 0, Edit1
   Return
   
ReadmeGuiClose:
   Gui, Readme:Destroy
   WinActivate, ahk_id %LastActiveWindowID%
   Return
   
SaveData:
   DllCall("DestroyCursor", Ptr, hCursorHand)
   (CrossHair && CrossHair(0))
   
   oFile := FileOpen(DataFile, "w", "UTF-16-RAW")
   oFile.WriteUChar(oSaved.MaxIndex())
   for k, v in oSaved
      oFile.WriteUInt(StrLen(oSaved[A_Index]))
      , oFile.Write(oSaved[A_Index])
      
   oFile.WriteUChar(oEntered.MaxIndex())
   for k, v in oEntered
      oFile.WriteUInt(StrLen(oEntered[A_Index]))
      , oFile.Write(oEntered[A_Index])
   
   DetectHiddenWindows, On
   WinGetPos, X, Y,,, ahk_id %hMain%
   oFile.WriteUSHort(X), oFile.WriteUSHort(Y)
   oFile.WriteUSHort(GuiW), oFile.WriteUSHort(GuiH)
   
   (SendVariant = 2 && SendVariant := 3)
   oFile.WriteUChar(SendVariant)
   
   oFile.WriteUChar(StrLen(AssociatedWindow))
   oFile.Write(AssociatedWindow)
   
   oFile.WriteUChar(StrLen(MatchMode))
   oFile.Write(MatchMode)
   
   oFile.WriteUChar(StrLen(FontName))
   oFile.Write(FontName)
   
   oFile.WriteUChar(FontSize)
   oFile.WriteUChar(FontQuality)
   oFile.WriteUInt(FontColor)
   
   oFile.WriteUChar(RowNumber)
   oFile.WriteUChar(AllCell)
   
   oFile.WriteUInt(ColorBack)
   oFile.WriteUInt(ColorCell)
   oFile.WriteUInt(ColorSelect)
   oFile.WriteUInt(ColorButton)
   oFile.WriteUChar(FrameStyle)
   oFile.WriteUChar(AlwaysPlaceIntoClipboard)
   oFile.WriteUChar(HideAfterSend)

   oFile.WriteUChar(StrLen(Hk))
   oFile.Write(Hk)
   
   oFile.WriteUChar(LoadOnStartup)
   oFile.WriteUChar(ToggleToSaved)
   
   oFile.Close()
   ExitApp

#If MouseOverGui()
WheelDown::
WheelUp::
   Loop 4
      WM_VSCROLL(A_ThisHotkey = "WheelUp" ? SB_LINEUP : SB_LINEDOWN, 0, WM_VSCROLL, hBack)
   Return
   
WM_MOUSEMOVE(wp, lp, msg)
{
   static PrevColored, PrevGui
   
   if (msg = WM_MOUSEMOVE && ColorCell != ColorSelect)
   {
      if (A_Gui = PrevGui)
         Return
      
      if InStr(A_Gui, "Work")
      {
         if PrevColored
            Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
         Gui, %A_Gui%: Color, % Dec2Hex(ColorSelect)
         PrevColored := A_Gui
         SetTimer, WhiteColor, 100
      }
      else if PrevColored
      {
         Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
         PrevColored := ""
      }
      PrevGui := A_Gui
   }
   
   if (msg = WM_NCMOUSEMOVE)
   {
      if PrevColored  {
         Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
         PrevColored := PrevGui := ""
      }
      
      (wp = HTSYSMENU && DllCall("SetCursor", Ptr, hCursorHand))
   }
   
   if (msg = WM_SETCURSOR)
      if A_Gui in ColorBack,ColorCell,ColorSelect,FontColor,ColorButton
         Return DllCall("SetCursor", Ptr, hCursorHand)
   
   Return
   
WhiteColor:
   MouseGetPos,,, WinID
   if (WinID != hMain)
   {
      SetTimer, WhiteColor, Off
      if PrevColored
         Gui, %PrevColored%: Color, % Dec2Hex(ColorCell)
      PrevColored := ""
   }
   Return
}

WM_NCLBUTTONDOWN(wp)
{
   static HTMENU := 5, HTMINBUTTON := 8, HTLEFT := 10, HTRIGHT := 11
      , HTTOP := 12, HTTOPLEFT := 13, HTTOPRIGHT := 14, HTBOTTOM := 15
      , HTBOTTOMLEFT := 16, HTBOTTOMRIGHT := 17, HTCLOSE := 20
      
   if (A_Gui != "Main")
      Return
   
   if wp in %HTCAPTION%,%HTLEFT%,%HTRIGHT%,%HTTOP%,%HTBOTTOM%,%HTTOPLEFT%,%HTTOPRIGHT%,%HTBOTTOMLEFT%,%HTBOTTOMRIGHT%
   {
      LastActiveWindowID := WinExist("A")
      SetTimer, MouseWatch, 100
      WinActivate, ahk_id %hMain%
      if (wp != HTCAPTION)
      {
         ControlGetPos,, TopWork,,, AutoHotkeyGUI1, ahk_id %hBack%
         TopWork -= 1
      }
   }
   if (wp = HTMENU)
   {
      LastActiveWindowID := WinExist("A")
      WinActivate, ahk_id %hMain%
      SendInput, {LButton Down}
      Sleep, 100
      if WinExist("ahk_id" hSetWindow) || WinExist("ahk_id" hSetFont) || WinExist("ahk_id" hSetNumber)
       || WinExist("ahk_id" hSetColor) || WinExist("ahk_id" hReadme) || WinExist("ahk_id" hSetHotkey)
         WinActivate
      else
         WinActivate, ahk_id %LastActiveWindowID%
   }
   if (wp = HTMINBUTTON)
   {
      Gosub, HideShowMainWindow
      Return 1
   }
   if (wp = HTSYSMENU)
      Return 1
   
   if (wp = HTCLOSE && A_Gui = "Main")
      ExitApp
   Return
   
MouseWatch:
   if GetKeyState("LButton", "P")
      Return
   SetTimer, MouseWatch, Off
   WinActivate, ahk_id %LastActiveWindowID%
   Return
}

WM_NCLBUTTONUP(wp)
{
   (wp = HTSYSMENU && ChangeContent() && DllCall("SetCursor", Ptr, hCursorHand))
}

WM_NCLBUTTONDBLCLK(wp)
{
   if (A_Gui = "Main" && wp = HTSYSMENU)
      Return 1
   
   if (A_Gui != "Main" || wp != HTCAPTION)
      Return
   
   WM_SYSCOMMAND(SC_MAXIMIZE)
   Return 1
}

WM_CHAR(wp)
{
   CoordMode, Caret
   WinClose, ahk_id %hBall%
   if (A_Gui != "SetColor")
      Return
   
   GuiControlGet, Focus, %A_Gui%:Focus
   if !InStr(Focus, "Edit")
      Return

   if wp in 3,8,24,26   ; обработка Ctrl + C, BackSpace, Ctrl + X, Ctrl + Z
      Return

   if wp = 22   ; обработка Ctrl + V
   {
      Loop, parse, Clipboard
      {
         Text .= A_LoopField
         if A_LoopField not in 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,A,B,C,D,E,F
         {
            ShowBall("Буфер обмена содержит недопустимые символы."
               . "`nДопустимые символы:`n0123456789ABCDEF", "Ошибка!")
            Return 1
         }
      }
      Control, EditPaste, % Text, % Focus, Настройки цвета в RGB
      Return 1
   }

   Char := Chr(wp)
   if Char not in 0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,A,B,C,D,E,F
   {
      ShowBall("Допустимые символы:`n0123456789ABCDEF", Char " — недопустимый символ")
      Return 1
   }
   Return
}

WM_SYSCOMMAND(wp)
{
   static YPrev, HPrev, SB_TOP := 6
   if (A_Gui != "Main")
      Return
   
   WinGetPos,,,,  H_Tray, ahk_class Shell_TrayWnd
   if (wp = SC_MAXIMIZE)
   {
      WinExist("ahk_id" hMain)
      WinGetPos,, Y,, H
      WM_VSCROLL(SB_TOP, 0, WM_VSCROLL, hBack)
      if (Y = 0 && H = A_ScreenHeight - H_Tray && YPrev != "" && HPrev != "")
         WinMove,,,, YPrev,, HPrev
      else
      {
         YPrev := Y, HPrev := H
         WinMove,,,, 0,, A_ScreenHeight - H_Tray
      }
      Return 1
   }
   
   if (wp = SC_MINIMIZE)
   {
      Gosub, HideShowMainWindow
      Return 1
   }
}

WM_VSCROLL(wParam, lParam, msg, hwnd)
{
   static SIF_ALL=0x17, SCROLL_STEP=10
   bar := msg=0x115 ; SB_HORZ=0, SB_VERT=1

   VarSetCapacity(si, 28, 0)
   NumPut(28, si, "UInt") ; cbSize
   NumPut(SIF_ALL, si, 4, "UInt") ; fMask
   if !DllCall("GetScrollInfo", Ptr, hwnd, Int, bar, Ptr, &si)
     return

   VarSetCapacity(rect, 16)
   DllCall("GetClientRect", Ptr, hwnd, Ptr, &rect)

   new_pos := NumGet(si, 20, "UInt") ; nPos

   action := wParam & 0xFFFF
   if action = 0 ; SB_LINEUP
     new_pos -= SCROLL_STEP
   else if action = 1 ; SB_LINEDOWN
     new_pos += SCROLL_STEP
   else if action = 2 ; SB_PAGEUP
     new_pos -= NumGet(rect, 12, "Int") - SCROLL_STEP
   else if action = 3 ; SB_PAGEDOWN
     new_pos += NumGet(rect, 12, "Int") - SCROLL_STEP
   else if (action = 5 || action = 4) ; SB_THUMBTRACK || SB_THUMBPOSITION
     new_pos := wParam>>16
   else if action = 6 ; SB_TOP
     new_pos := NumGet(si, 8, "Int") ; nMin
   else if action = 7 ; SB_BOTTOM
     new_pos := NumGet(si, 12, "Int") ; nMax
   else
     return

   min := NumGet(si, 8, "Int") ; nMin
   max := NumGet(si, 12, "Int") - NumGet(si, 16, "UInt") ; nMax-nPage
   new_pos := new_pos > max ? max : new_pos
   new_pos := new_pos < min ? min : new_pos

   old_pos := NumGet(si, 20, "Int") ; nPos

   y := old_pos-new_pos
   ; Scroll contents of window and invalidate uncovered area.
   DllCall("ScrollWindow", Ptr, hwnd, Int, 0, Int, y, UInt, 0, UInt, 0)

   ; Update scroll bar.
   NumPut(new_pos, si, 20, "Int") ; nPos
   DllCall("SetScrollInfo", Ptr, hwnd, Int, bar, Ptr, &si, Int, 1)
}

WM_RBUTTONUP()
{
   static GuiNum, GuiName
   if !InStr(A_Gui, "Work")
      Return
   
   GuiNum := SubStr(A_Gui, 5)
   Menu, Actions, Show
   Return
   
Delete:
   o%CurrentContent%.Remove(GuiNum)
   SetContent()
   Return
   
Send:
   Send(GuiName)
   Return
}

Send(GuiName)
{
   PrevBlock := Block, Block := 1
   SendText := o%CurrentContent%[SubStr(GuiName, 5)]
   if !(oEntered[1] == SendText)  {
      RemoveExistingAddNew(oEntered, SendText)
      if (CurrentContent = "Entered")
         SetContent()
   }
   
   if (SendVariant = 1)
   {
      if AssociatedWindow  {
         SetTitleMatchMode, % MatchMode
         IfWinExist, % AssociatedWindow
            WinActivate
         else
            Return ToolTipShowAndDel("Связанное окно не найдено! Укажите связанное окно.")
      }
      else
         Return ToolTipShowAndDel("Связанное окно не указано!")
   }
   
   if (SendVariant = 2)
   {
      if TargetWindow  {
         IfWinExist, ahk_id %TargetWindow%
            WinActivate
         else
            Return ToolTipShowAndDel("Целевое окно не найдено! Измените выбор.")
      }
      else
         Return ToolTipShowAndDel("Целевое окно не указано!")
   }
   
   if (SendVariant = 3 && WinExist("A") = hMain)
      Return ToolTipShowAndDel("Активное окно не найдено!")
   
   if (SendVariant = 4)
   {
      Clipboard := SendText
      Sleep, 200
      Block := PrevBlock
      ToolTipShowAndDel("Текст помещён в буфер обмена", 800)
   }

   if SendVariant in 1,2,3
   {
      TempClip := ClipboardAll
      Clipboard := SendText
      Sleep, 100
      SendInput, ^{vk56}   ; Ctrl + V
      if !AlwaysPlaceIntoClipboard  {
         Sleep, 100
         Clipboard := TempClip
      }
      Sleep, 200
      Block := PrevBlock, TempClip := ""
   }
   
   if HideAfterSend
      SetTimer, MouseTrack, 100
   Return
   
MouseTrack:
   MouseGetPos,,, WinID
   if (WinID != hMain)  {
      Gui, Main:Show, Hide
      Menu, Tray, Rename, Скрыть окно, Показать окно
      SetTimer, MouseTrack, Off
   }
   Return
}

ToolTipShowAndDel(text, time = 1500)
{
   ToolTip % text
   Sleep time
   ToolTip
}

WM_MBUTTONDOWN()
{
   if !InStr(A_Gui, "Work")
      Return
   
   ToolTip, % RegExReplace(o%CurrentContent%[SubStr(A_Gui, 5)], A_Tab, "    ")
   KeyWait, MButton
   ToolTip
}

AHK_NOTIFYICON(wp, lp)
{
   static WM_LBUTTONUP := 0x202, WM_LBUTTONDBLCLK := 0x203, Time
   if (lp = WM_LBUTTONUP)
   {
      if (Time && A_TickCount - Time < 350)
         Return 1
      Gosub, HideShowMainWindow
      Time := A_TickCount
   }
   
   if (lp = WM_LBUTTONDBLCLK)
      Return 1
}

WM_INFO(wp, lp)
{
   (wp = 0 && PrevBlock := Block)
   if !(lp = "")  {
      Block := lp
      GuiControl, Main:, Block, % Block ? "Unblock" : "Block"
   }
   Return PrevBlock
}

UpdateScrollBar(hGui, ScrollHeight)
{
   static SIF_RANGE := 0x1, SIF_PAGE := 0x2, SB_VERT := 1
   i := 0
   Loop % AllCell
   {
      GuiControlGet, Visible, Work%A_Index%:Visible, Text
      (Visible && i++)
   } until !Visible
   
   WinGetPos,,,, GuiHeight, ahk_id %hGui%
   VarSetCapacity(si, 28, 0)
   NumPut(28, si) ; cbSize
   NumPut(SIF_RANGE | SIF_PAGE, si, 4)
   NumPut(ScrollHeight, si, 12) ; nMax
   NumPut(GuiHeight, si, 16) ; nPage
   DllCall("SetScrollInfo", Ptr, hGui, UInt, SB_VERT, Ptr, &si, Int, 1)

   ControlGetPos,, Top,,, AutoHotkeyGUI1, ahk_id %hBack%
   Top -= 6
   if (Top >= 0)
      Return
   
   ControlGetPos,, y,, h, AutoHotkeyGUI%i%, ahk_id %hBack%
   Bottom := y + h + 6
   if (Top < 0 && Bottom < GuiHeight)
   {
      y := (a := Abs(Top)) > (b := GuiHeight-Bottom) ? b : a
      DllCall("ScrollWindow", Ptr, hGui, Int, 0, Int, y, Ptr, 0, Ptr, 0)
      TopWork += y
   }
}

CrossHair(OnOff=1)
{
   static IDC_CROSS := 32515, SPI_SETCURSORS := 0x57
      , hCursor := DllCall("LoadCursor", Ptr, 0, UInt, IDC_CROSS, Ptr)
      , sys_cursors := [32512,32513,32514,32516,32642,32643,32644,32645,32646,32648,32649,32650]
   
   if !OnOff
      DllCall("SystemParametersInfo", UInt, SPI_SETCURSORS, UInt, 0, UInt, 0, UInt, 0)
   else
      for k, cursor in sys_cursors
         hCopy := DllCall("CopyImage", Ptr, hCursor, UInt, 2, Int, 0, Int, 0, UInt, 0)
         , DllCall("SetSystemCursor", Ptr, hCopy, UInt, cursor)
}

TargetWndShow(TargetWnd)
{
   VarSetCapacity(WI, 60)
   DllCall("GetWindowInfo", Ptr, TargetWnd, Ptr, &WI)
   X := NumGet(WI, 20, "UInt"), Y := NumGet(WI, 24, "UInt")
   W := NumGet(WI, 28, "UInt") - X, H := NumGet(WI, 32, "UInt") - Y
   Gui, Flash:Default
   Gui, +LastFound -Caption +AlwaysOnTop +Disabled +Owner +hwndID3
   Gui, Color, Red
   Gui, Show, x%X% y%Y% w%W% h%H% hide
   th := 5, w1 := w - th, h1 := h - th
   WinSet, Region, 0-0 %w%-0 %w%-%h% 0-%h% 0-0 %th%-%th% %w1%-%th% %w1%-%h1% %th%-%h1% %th%-%th%
   Loop 3
   {
      Gui, Show, NA
      Sleep, 300
      Gui, Show, Hide
      Sleep, 300
   }
   Gui, Destroy
}

MouseOverGui()
{
   MouseGetPos,,, ID
   Return ID = hMain
}

ShowBall(Text, Title="")
{
   global
   WinClose, ahk_id %hBall%
   hBall := TrackToolTip(Text, A_CaretX+1, A_CaretY+15, Title)
   SetTimer, BallDestroy, -2000
   Return

BallDestroy:
   WinClose, ahk_id %hBall%
   Return
}

ChooseColor(Color := 0, hWnd := 0, Flags := 3)   ; Flags := CC_RGBINIT=1 | CC_FULLOPEN=2
{
   static CustColors, CHOOSECOLOR
   Color := RGB_BGR(Color)   ; RGB -> BGR

   if !CHOOSECOLOR
   {
      VarSetCapacity(CustColors, 64)
      Loop 16
         NumPut(0xFFFFFF, CustColors, (A_Index - 1)*4, "UInt")

      VarSetCapacity(CHOOSECOLOR, Size := A_PtrSize*9, 0)
      NumPut(Size, CHOOSECOLOR)
      NumPut(&CustColors, CHOOSECOLOR, A_PtrSize*4)
   }
   NumPut(Flags, CHOOSECOLOR, A_PtrSize*5)
   NumPut(hWnd, CHOOSECOLOR, A_PtrSize)
   NumPut(Color, CHOOSECOLOR, A_PtrSize*3)
   
   if !DllCall("comdlg32\ChooseColor", Str, CHOOSECOLOR)
      Return -1
   
   PrevFormat := A_FormatInteger
   SetFormat, IntegerFast, H
   RGB := RGB_BGR(NumGet(&CHOOSECOLOR + A_PtrSize*3, "UInt")) . ""
   SetFormat, IntegerFast, % PrevFormat
   Return RGB
}

TrackToolTip( sText
            , x = ""   ; если не указаны, то вблизи курсора
            , y = ""
            , sTitle = ""
            , h_icon = 0   ; h_icon — 0: None, 1:Info, 2: Warning, 3: Error, n > 3: предполагается hIcon
            , CloseButton = 0
            , nColorBack = 0xFFFFE1
            , nColorText = 0
            , BallonTip = 0   ; BalloonTip — это ToolTip с хвостиком
            , w = 400 )  ; максимальная ширина
{
   TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80

   hWnd := DllCall("CreateWindowEx", UInt, WS_EX_TOPMOST := 8
                                   , Str, "tooltips_class32", Str, ""
                                   , UInt, TTS_NOPREFIX|TTS_ALWAYSTIP|(CloseButton ? TTS_CLOSE : 0)|(BallonTip ? TTS_BALLOON : 0)
                                   , Int, 0, Int, 0, Int, 0, Int, 0
                                   , Ptr, 0, Ptr, 0, Ptr, 0, Ptr, 0)

   if (x = "" || y = "")
   {
      CoordMode, Mouse
      MouseGetPos, xtt, ytt
      xtt := x = "" ? xtt + 10 : x
      ytt := y = "" ? ytt + 10 : y
   }
   Else
      xtt := x, ytt := y

   NumPut(VarSetCapacity(TOOLINFO, A_PtrSize = 4 ? 48 : 72, 0), TOOLINFO, "UInt")
   NumPut(0x20, TOOLINFO, 4, "UInt")      ; TTF_TRACK = 0x20
   NumPut(&sText, TOOLINFO, A_PtrSize = 4 ? 36 : 48, "UInt")

   DHW := A_DetectHiddenWindows
   DetectHiddenWindows, On
   WinWait, ahk_id %hWnd%

   WM_USER := 0x400
   SendMessage, WM_USER + 24,, w         ; TTM_SETMAXTIPWIDTH
   SendMessage, WM_USER + (A_IsUnicode ? 50 : 4),, &TOOLINFO   ; TTM_ADDTOOL
   SendMessage, WM_USER + 19, RGB_BGR(nColorBack)   ; TTM_SETTIPBKCOLOR
   SendMessage, WM_USER + 20, RGB_BGR(nColorText)   ; TTM_SETTIPTEXTCOLOR
   SendMessage, WM_USER + (A_IsUnicode ? 33 : 32), h_icon, &sTitle      ; TTM_SETTITLEA и TTM_SETTITLEW
   SendMessage, WM_USER + (A_IsUnicode ? 57 : 12),, &TOOLINFO     ; TTM_UPDATETIPTEXTA и TTM_UPDATETIPTEXTW
   SendMessage, WM_USER + 18,, xtt|(ytt<<16)   ; TTM_TRACKPOSITION
   SendMessage, WM_USER + 17, 1, &TOOLINFO ; TTM_TRACKACTIVATE

   if BallonTip
      xMax := A_ScreenWidth, yMax := A_ScreenHeight
   else
   {
      WinGetPos,,, W, H
      xMax := A_ScreenWidth - W - 10
      yMax := A_ScreenHeight - H - 10
   }
   
   if (xtt > xMax || ytt > yMax)
   {
      WinHide
      xtt := xtt > xMax ? xMax : xtt
      ytt := ytt > yMax ? yMax : ytt
      SendMessage, 1042,, xtt|(ytt<<16)   ; TTM_TRACKPOSITION
      WinShow
   }

   DetectHiddenWindows, % DHW
   Return hWnd
}

RGB_BGR(color)
{
   Return (color & 0xFF000000) | (color & 0xFF) << 16 | (color & 0xFF00) | (color >> 16)
}

GetCyrillicFontNames()
{
   hDC := DllCall("GetDC", UInt, 0, Ptr)
   VarSetCapacity(LOGFONT, 92, 0)
   NumPut(RUSSIAN_CHARSET := 204, &LOGFONT + 23, "UChar")
   DllCall("EnumFontFamiliesEx", Ptr, hDC
                               , Ptr, &LOGFONT
                               , Ptr, RegisterCallback("EnumFontFamExProc", "F", 4)
                               , Ptr, pFonts := Object(oFonts := [])
                               , UInt, 0)
   ObjRelease(pFonts), DllCall("ReleaseDC", Ptr, 0, Ptr, hDC)
   Return oFonts
}

EnumFontFamExProc(lpelfe, lpntme, FontType, lParam)
{
   if font := StrGet(lpelfe + 28)
      Return Object(lParam).Insert(font)
}

ShowOwnerWindow(Title, hWnd)
{
   WinGetPos, X_Main, Y_Main, W_Main,, ahk_id %hMain%
   Gui, Show, Hide, % Title
   DetectHiddenWindows, On
   WinGetPos,,, W,, ahk_id %hWnd%
   Gui, Show, % "Hide x" X_Main - W " y" Y_Main
   WinGetPos, X, Y, W, H, ahk_id %hWnd%
   (X < 0 && X := X_Main + W_Main)
   (X + W > A_ScreenWidth && X := A_ScreenWidth - W)
   (Y < 0 && Y := 0)
   (Y + H > A_ScreenHeight && Y := A_ScreenHeight - H)
   WinMove, ahk_id %hWnd%,, X, Y, W, H
   Gui, Show
}

GetDDLstrings(param*)         
{                              
   if (param.MaxIndex() = 2)      ; RowNumber, AllCell
   {
      Loop 5
         RowStr .= (A_Index = 1 ? "" : "|") . A_Index . (A_Index = param[1] ? "|" : "")
      
      Loop 57
         CellStr .= (A_Index = 1 ? "" : "|") . (A_Index + 7) . (A_Index + 7 = param[2] ? "|" : "")
      
      Return { Rows: RegExReplace(RowStr, "\|$", "||")
            , Cells: RegExReplace(CellStr, "\|$", "||") }
   }
      
   if (param.MaxIndex() = 4)      ; FontName, oFonts, FontQuality, FontSize
   {
      for k, font in param[2]
         Fonts .= (A_Index = 1 ? "" : "|") . font . (font = param[1] ? "|" : "")

      for k, v in oQual
         Qual .= (A_Index = 1 ? "" : "|") . v . (k - 1 = param[3] ? "|" : "")
      
      Sizes := "8|9|10|11|12|13|14|15"
      Loop, parse, Sizes, |
         NewSizes .= (A_Index = 1 ? "" : "|") . A_LoopField . (A_LoopField = param[4] ? "|" : "")
      
      Return { Font: RegExReplace(Fonts, "\|$", "||")
             , Qual: RegExReplace(Qual, "\|$", "||")
             , Size: RegExReplace(NewSizes, "\|$", "||") }
   }
}

ColoredGuiShow(GuiName, Parent, Color, Label, W, H)
{
   Gui, %GuiName%: +Parent%Parent% -Caption -DPIScale
   Gui, %GuiName%: Color, % Color
   Gui, %GuiName%: Add, Text, x0 y0 w%W% h%H% g%Label% +%SS_GRAYFRAME%
   Gui, %GuiName%: Show, NA w%W% h%H%
}

LoadCursorHand()
{
   Return DllCall("LoadImage", Ptr, 0
                             , UInt, OCR_HAND
                             , UInt, IMAGE_CURSOR
                             , Int, 0, Int, 0
                             , UInt, LR_DEFAULTSIZE|LR_SHARED, Ptr)
}

Dec2Hex(Number)
{
   PrevFormat := A_FormatInteger
   SetFormat, IntegerFast, H
   HexNumber := (Number+0) . ""
   SetFormat, IntegerFast, %PrevFormat%
   Return HexNumber
}

Ping(strHost)
{
   Loop 2
      bRet := ComObjGet("winmgmts:").Get("Win32_PingStatus.address='" . strHost . "'").StatusCode = 0
   until bRet
   return bRet
}

GetDisplayHk(Hk)
{
   if (Hk = "Нет" || Hk = "")
      Return "Нет"
   
   StringReplace, DisplayHk, Hk, ^, % "Ctrl@"
   StringReplace, DisplayHk, DisplayHk, +, % "Shift@"
   StringReplace, DisplayHk, DisplayHk, !, % "Alt@"
   StringReplace, DisplayHk, DisplayHk, @, +, All
   RegExMatch(DisplayHk, "i)(vk|sc)[0-9A-F]+$", found)
   StringReplace, DisplayHk, DisplayHk, % found, % GetKeyName(found)
   Return DisplayHk
}

SetContent()
{
   i := 0
   Loop % AllCell  {
      content := o%CurrentContent%[A_Index], clip := ""
      Loop, parse, content, `n, `r
         clip .= (A_Index = 1 ? "" : "`n`r") . A_LoopField
      until A_Index = RowNumber
      
      (clip != "" && i++)
      GuiControl, Work%A_Index%:, Text, % RegExReplace(clip, A_Tab, "    ")
      Gui, Work%A_Index%: Show, % clip = "" ? "Hide" : "NA"
   }
   UpdateScrollBar(hBack, ScrollHeight := 10 + (TextH + 8) * i - 3)
}

RemoveExistingAddNew(obj, data)
{
   for k, v in obj
      if (v == data && ExistSuchClip := 1)
         break
      
   (ExistSuchClip && obj.Remove(k))
   obj.Insert(1, data), obj.Remove(AllCell + 1, obj.MaxIndex())
}

ChangeContent()
{
   b := CurrentContent = "Saved"
   Menu, Content, UnCheck, % b ? "Скопированное" : "Вставленное"
   Menu, Content, Check, % b ? "Вставленное" : "Скопированное"
   CurrentContent := b ? "Entered" : "Saved"
   
   SetContent()
   Gui, Main:Show, % WinExist("ahk_id" hMain) ? "NA" : "Hide", % b ? "Вставленное" : "Скопированное"
   Return 1
}

Учтены высказанные пожелания. Добавил возможность переключения между «Скопированным» и «Вставленным» кликом по иконке окна. Из списков теперь удаляются повторы.
Изменена dll с иконками, будет скачана новая, старая удалится. Если в папке скрипта сохранился прежний файл "data", при загрузке скрипта появится сообщение об ошибках чтения данных (т. к. добавлены новые), на это можно не обращать внимания.
В дальнейшем попытаюсь создать расширенную историю с поиском, как только продумаю, как это оформить.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

96

Re: AHK: Пишем удобный менеджер буфера обмена

Если у мыши обменены левая и правая кнопки (через настройки в Панели управления), то ни одно меню в менюбаре не открывается. Только активируется окно на короткое время. У прошлой версии так же. Кнопки внизу тем не менее работают нормально. Вставка кликом левой работает. Контекстное меню на скопированном тоже работает.

97

Re: AHK: Пишем удобный менеджер буфера обмена

Нашёл, как исправить. В строке 1077 заменить SendInput, {LButton Down} на SendInput, {Click Down}. Click учитывает обмен кнопок.

Кстати, не логичнее ли будет #SingleInstance, Force? Какой смысл в нескольких экземплярах скрипта?

98

Re: AHK: Пишем удобный менеджер буфера обмена

Не загружает dll файл с иконками:
https://i.imgur.com/ZObt2kJ.png

Как сказал мой дед - Я твой дед

99

Re: AHK: Пишем удобный менеджер буфера обмена

YMP пишет:

Нашёл, как исправить.

Ага, ок, исправлю.

YMP пишет:

Кстати, не логичнее ли будет #SingleInstance, Force? Какой смысл в нескольких экземплярах скрипта?

Идея была, что удобно иногда запускать два экземпляра скрипта, один заблокированный с шаблонами, которые нужно вставлять постоянно.

sergeiplugatyr пишет:

Не загружает dll файл с иконками

Не знаю, в чём причина, может, файл плохо загрузился. Попробуйте удалить и перезагрузить скрипт. Проверьте, не блокирует ли фаервол доступ скрипта в интернет.

Вспомнил, что еще хотел добавить возможность редактирования данных в ячейках.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

100

Re: AHK: Пишем удобный менеджер буфера обмена

может, файл плохо загрузился

Действительно, через скрипт не загружался полностью, скачал вручную.

Как сказал мой дед - Я твой дед