1

Тема: AHK: удобный скрипт для сохранения скриншотов

Скрипт является развитием этой разработки.

ScreenCatcher - скрипт для быстрых скриншотов на AutoHotkey v1

ScreenCatcher делает снимки всего экрана, активного окна или выбранной области. Результат можно сохранить в файл, скопировать в буфер обмена или отправить на Imgbb.

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

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

Основные возможности

  • снимок всего экрана

  • снимок активного окна

  • снимок области под окном выделения

  • быстрое выделение области мышью

  • сохранение снимка в файл

  • копирование снимка в буфер обмена

  • отправка снимка на Imgbb

  • предпросмотр снимков перед сохранением

  • перемещение и изменение размера окон предпросмотра

  • контекстное меню предпросмотра: сохранить, сохранить в текущем размере, удалить

  • точная подгонка границ окна выделения колесом мыши с модификаторами

  • настройка формата файла: png, jpg, bmp, tif, gif

  • настройка качества JPEG

  • выбор папки для сохранения файлов

  • захват курсора мыши на скриншоте

  • уведомления после сохранения

  • запуск вместе с Windows

  • настройка цвета и прозрачности окна выделения

  • два варианта подсказок при изменении размера окна выделения: измененные курсоры или цветные маркеры

  • настраиваемые горячие клавиши

Два способа выбрать область для скриншота

В ScreenCatcher есть два разных сценария для снимка области.

1. Окно выделения

Это полупрозрачная окно, которое можно показать, скрыть, переместить, центрировать и точно изменить в размере, растягивая мышью. Скриншот делается с области, которая находится под этим окном. Скриншот можно делать по двойному клику по окну выделения, либо, если окно скрыто, по горячей клавише (по умолчанию F12)

Для точной подгонки окна можно использовать колесо мыши с модификаторами, когда окно выделения активно (кликните по нему, чтобы сделать активным):

  • Ctrl + колесо - сдвиг левой границы

  • Shift + колесо - сдвиг верхней границы

  • Alt + колесо - сдвиг правой границы

  • Win + колесо - сдвиг нижней границы

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

Такой способ удобен, когда нужно:

  • несколько раз снимать одну и ту же область

  • заранее выставить точный размер кадра

  • аккуратно подогнать рамку по границам окна, изображения или элемента интерфейса

  • быстро повторять снимки без нового выделения мышью каждый раз

2. Быстрое выделение мышью поверх экрана

Это режим одноразового выбора прямоугольника прямо поверх экрана. После вызова режима появляется крестик курсора; область выбирается протягиванием мыши. При выборе через F4 сначала включается режим ожидания, рядом с курсором показываются координаты. После нажатия левой кнопки начинается выделение. При быстром варианте через Ctrl+Alt+ЛКМ выделение начинается сразу, без предварительной плашки координат.

Такой способ удобен, когда нужно:

  • быстро снять произвольный фрагмент экрана

  • не двигать основное окно выделения

  • один раз выбрать область и сразу отправить ее в общий механизм сохранения, буфера обмена, предпросмотра или Imgbb

Отменить выбор можно клавишей Esc или правой кнопкой мыши.

Предпросмотр

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

Это удобно, если перед сохранением нужно проверить кадр, подогнать размер или оставить на экране несколько сделанных снимков.

Вывод снимков

В настройках можно выбрать один или несколько способов вывода:

  • в файл

  • в буфер обмена

  • на Imgbb.com

Способы можно комбинировать. Например, снимок может одновременно сохраняться в файл, копироваться в буфер обмена и отправляться на Imgbb.

Настройка отправки на Imgbb

Чтобы отправка на Imgbb работала, нужно получить API key.

Для этого:

1. Зарегистрироваться на Imgbb:
   https://imgbb.com/

2. Открыть страницу API:
   https://api.imgbb.com/

3. Получить ключ API.

4. Открыть файл ScreenCatcher.ini рядом со скриптом.

5. Вписать ключ в секцию [Imgbb]:

[Imgbb]
ClientID=YOUR_IMGBB_API_KEY

6. В настройках ScreenCatcher на вкладке "Вывод" включить Imgbb.com.

Горячие клавиши по умолчанию

  • F12 - скриншот области под окном выделения

  • Right Shift + PrintScreen - скриншот активного окна

  • PrintScreen - скриншот всего экрана

  • Pause - показать или скрыть окно выделения

  • ScrollLock - переместить окно выделения в центр экрана

  • Left Ctrl + Space - выделить окно предпросмотра

  • F4 - выбрать область для скриншота мышью поверх экрана

  • Ctrl + Alt + ЛКМ - быстро выбрать область для скриншота мышью

Все горячие клавиши можно изменить в настройках. Если горячая клавиша не нужна, ее можно очистить.

Настройки

В окне настроек доступны вкладки:

  • "Цвет" - цвет и прозрачность окна выделения

  • "Вывод" - куда отправлять скриншоты, формат файла, качество JPEG и папка сохранения

  • "Настройки" - захват курсора, предпросмотр, уведомления, запуск вместе с Windows и режим подсказок при изменении размера рамки

  • "Горячие клавиши" - назначение сочетаний клавиш и мыши

Во время открытия окна настроек рабочие горячие клавиши ScreenCatcher временно отключаются, чтобы они не мешали вводу новых сочетаний в поля настройки.

Трей-меню и меню окна выделения

Основные действия доступны из меню в трее и из контекстного меню окна выделения:

  • открыть настройки

  • сделать скриншот

  • показать или скрыть окно выделения

  • переместить окно выделения в центр экрана

  • открыть папку вывода

  • открыть справку

  • выйти из скрипта

#NoEnv
#SingleInstance, force
#MaxHotkeysPerInterval 200
SetWinDelay, 0
SetBatchLines, -1
SetTitleMatchMode, 3
CoordMode, Mouse
OnExit("ShutdownApplication")

global App := {Config: {}, Settings: {}, Menu: {}, Selection: {}, Windows: {}}

hIcon16 := CreateIconFromBase64(GetBase64String(16), 16)
Menu, Tray, Icon, HICON:*%hIcon16%

hIcon32 := CreateIconFromBase64(GetBase64String(32), 32)
DllCall("SendMessage", "Ptr", A_ScriptHwnd, "UInt", WM_SETICON := 0x80, "Ptr", ICON_BIG := 1, "Ptr", hIcon32)
App.GdipToken := GdipStartup()

				    ;  секция          ключ               имя переменной        умолчание
IniReadConfigSect("Color-Transparent", "ARGB"             , "ARGB"             , 0x82FFC700)

IniReadConfigSect("Output_Settings"  , "Output"           , "Output"           , "File"
									 , "Extension"        , "Ext"              , "png"
									 , "JPEG_Quality"     , "JPEGQuality"      , 100
									 , "Out_Directory"    , "OutDirectory"     , A_ScriptDir)

IniReadConfigSect("Program_Settings" , "Capture_Cursor"   , "CaptureCursor"    , 0
									 , "Show_Preview"     , "ShowPreview"      , 1
									 , "Notify"           , "Notify"           , 1
									 , "Resize_Pointers"  , "ResizePointers"   , "Cursors"
									 , "InterpolationMode", "InterpolationMode", 3)
										 
IniReadHotkeysSect("Hotkeys"         , "GetArea"          , "GetArea"          , "$F12"
								     , "GetActiveWindow"  , "GetActiveWindow"  , ">+PrintScreen"
								     , "GetScreen"        , "GetScreen"        , "PrintScreen"
								     , "ShowHideSelWindow", "ShowHideSelWindow", "Pause"
								     , "SelWindowToCenter", "SelWindowToCenter", "ScrollLock"
								     , "SelectPreview"    , "SelectPreview"    , "<^Space"
								     , "OpenOutputFolder" , "OpenOutputFolder" , " "
								     , "SelectAreaOverlay", "SelectAreaOverlay", "F4"
								     , "QuickSelectAreaOverlay", "QuickSelectAreaOverlay", "^LButton")
NormalizeLoadedSettings()

Menu, Tray, NoStandard
Menu, Tray, Add, Справка, ShowHelp
Menu, Tray, Add
Menu, Tray, Add, Вывод, ShowSettingsFromMenu
Menu, Tray, Add, Настройки, ShowSettingsFromMenu
Menu, Tray, Add
App.Menu.TrayHide := MenuHotkeyCaption("Скрыть окно выделения", App.Hotkeys.ShowHideSelWindow)
App.Menu.TrayShow := MenuHotkeyCaption("Показать окно выделения", App.Hotkeys.ShowHideSelWindow)
App.Menu.TrayCenter := MenuHotkeyCaption("Окно выделения в центр экрана", App.Hotkeys.SelWindowToCenter)
App.Menu.TrayOpenOutDir := MenuHotkeyCaption("Открыть папку вывода", App.Hotkeys.OpenOutputFolder)
Menu, Tray, Add, % App.Menu.TrayShow, ToggleSelectionWindow
Menu, Tray, Add, % App.Menu.TrayCenter, CenterSelectionWindow
Menu, Tray, Add, % App.Menu.TrayOpenOutDir, OpenOutputDirectory
if !A_IsCompiled {
	Menu, Tray, Add
	Menu, Tray, Add, Редактировать скрипт, EditScript
	Menu, Tray, Add, Перезагрузка, ReloadScript
}
Menu, Tray, Add
Menu, Tray, Add, Выход, ShutdownApplication

Menu, MainMenu, Add, Вывод, ShowSettingsFromMenu
Menu, MainMenu, Add, Настройки, ShowSettingsFromMenu
Menu, MainMenu, Add
App.Menu.MainArea := MenuHotkeyCaption("Сделать скриншот", App.Hotkeys.GetArea)
App.Menu.MainHide := MenuHotkeyCaption("Скрыть окно выделения", App.Hotkeys.ShowHideSelWindow)
App.Menu.MainOpenOutDir := MenuHotkeyCaption("Открыть папку вывода", App.Hotkeys.OpenOutputFolder)
Menu, MainMenu, Add, % App.Menu.MainArea, CaptureAreaScreenshot
Menu, MainMenu, Add, % App.Menu.MainHide, ToggleSelectionWindow
Menu, MainMenu, Add, % App.Menu.MainOpenOutDir, OpenOutputDirectory
Menu, MainMenu, Add
Menu, MainMenu, Add, Справка, ShowHelp

Menu, Interpolation, Add, Default, SetInterpolationMode
Menu, Interpolation, Add, LowQuality, SetInterpolationMode
Menu, Interpolation, Add, HighQuality, SetInterpolationMode
Menu, Interpolation, Add, Bilinear, SetInterpolationMode
Menu, Interpolation, Add, Bicubic, SetInterpolationMode
Menu, Interpolation, Add, NearestNeighbor, SetInterpolationMode
Menu, Interpolation, Add, HighQualityBilinear, SetInterpolationMode
Menu, Interpolation, Add, HighQualityBicubic, SetInterpolationMode
InterpolationModeString := "Default|LowQuality|HighQuality|Bilinear|"
	. "Bicubic|NearestNeighbor|HighQualityBilinear|HighQualityBicubic"
App.Menu.InterpolationItems := StrSplit(InterpolationModeString, "|")

Menu, Interpolation, Check, % App.Menu.InterpolationItems[App.Config.InterpolationMode]

Menu, PreviewSaveToMenu, Add, файл, PreviewSaveToMenuHandling
Menu, PreviewSaveToMenu, Add, буфер обмена, PreviewSaveToMenuHandling
Menu, PreviewSaveToMenu, Add, imgbb.com, PreviewSaveToMenuHandling

Menu, PreviewSaveOriginalToMenu, Add, файл, PreviewSaveToMenuHandling
Menu, PreviewSaveOriginalToMenu, Add, буфер обмена, PreviewSaveToMenuHandling
Menu, PreviewSaveOriginalToMenu, Add, imgbb.com, PreviewSaveToMenuHandling

Menu, PreviewSaveCurrentToMenu, Add, файл, PreviewSaveCurrentToMenuHandling
Menu, PreviewSaveCurrentToMenu, Add, буфер обмена, PreviewSaveCurrentToMenuHandling
Menu, PreviewSaveCurrentToMenu, Add, imgbb.com, PreviewSaveCurrentToMenuHandling

Menu, ScreenShotMenu, Add, Сохранить в первоначальном размере, ScreenShotMenuHandling
Menu, ScreenShotMenu, Add, Сохранить в первоначальном размере в, :PreviewSaveOriginalToMenu
Menu, ScreenShotMenu, Add, Сохранить в текущем размере, ScreenShotMenuHandling
Menu, ScreenShotMenu, Add, Сохранить в текущем размере в, :PreviewSaveCurrentToMenu
Menu, ScreenShotMenu, Add, Удалить, ScreenShotMenuHandling

Menu, ScreenShotMenu2, Add, Интерполяция при изменении размеров, :Interpolation
Menu, ScreenShotMenu2, Add, Сохранить в, :PreviewSaveToMenu
Menu, ScreenShotMenu2, Add, Сохранить, ScreenShotMenuHandling
Menu, ScreenShotMenu2, Add, Удалить, ScreenShotMenuHandling

Gui, +LastFound -Caption +AlwaysOnTop +Owner
StoreSelectionWindowHwnd()
WinSet, Transparent, 0, % "ahk_id " App.Selection.Hwnd
Gui, Color, % Format("{:X}", App.Config.ARGB & 0xFFFFFF)

Locations = x190 y190|x0 y190|x0 y0|x190 y0|x190 y95|x95 y190|x0 y95|x95 y0
App.Selection.MarkerHwnds := []
Loop, parse, Locations, |			; SS_BITMAP = 0xE
{
	Gui, Add, Text, % "w10 h10 +0xE hidden hwndMarkerHwnd " A_LoopField
	App.Selection.MarkerHwnds.Push(MarkerHwnd)
}
UpdateMarkerColor()

SetResizeHotkeyState("Off")

if !A_Args.Length() {
	CenterSelectionWindow()
} else {
	opts := "x" (A_ScreenWidth - 200)//2 " y" (A_ScreenHeight - 200)//2 " h200 w200 Hide"
	Gui, Show, % opts, ScreenСatcher
}
SetScriptHotkeys(1)

Esc := DllCall("RegisterWindowMessage", "Str", "Esc")
Wheel := DllCall("RegisterWindowMessage", "Str", "Wheel")

OnMessage( Esc,  "WM_ESC")
OnMessage(Wheel, "WM_WHEEL")
OnMessage(0x018, "WM_SHOWWINDOW")
OnMessage(0x020, "WM_OVERLAY_SETCURSOR")
OnMessage(0x201, "WM_LBUTTONDOWN")
OnMessage(0x203, "WM_LBUTTONDBLCLK")
OnMessage(0x204, "WM_RBUTTONDOWN")
OnMessage(0x404, "AHK_NOTIFYICON")
return

;###########################################################################################################
;*************************************** Окно Настроек *****************************************************
;###########################################################################################################

OpenSettingsWindow(ThisMenuItemPos) {
	HideHelpWindow()
	App.Config.RunOnWinStart := IsRunOnWindowsStartEnabled()
	SelectionHwnd := App.Selection.Hwnd
	Gui, 2:Default
	if WindowExistsByHwnd(App.Settings.WindowHwnd) {
		if WindowExistsByHwnd(App.Settings.HotkeysHwnd)
			Gui, 4:Hide
		GuiControl, Choose, % App.Settings.TabHwnd, % ThisMenuItemPos
		SuspendScriptHotkeysForSettings()
		Gui, 2:Show
		UpdateHotkeysTabVisibility()
		return
	}
	SavePreviousSettingsState()
	SuspendScriptHotkeysForSettings()
	Width := 310, Height := 230
	Gui, 2:+LastFound +hwndSettingsHwnd
	App.Settings.WindowHwnd := SettingsHwnd
	App.Windows[2] := Object("Hwnd", SettingsHwnd)
	Gui, Font, s10 q5, Calibri
	Gui, Add, Tab3, gUpdateHotkeysTabVisibility x0 y0 w%Width% h%Height% hwndSettingsTabHwnd AltSubmit -Wrap
		, % "   Цвет| Вывод|Настройки|Горячие клавиши"
	App.Settings.TabHwnd := SettingsTabHwnd
	GuiControl, Choose, % App.Settings.TabHwnd, % ThisMenuItemPos

;********************************** Первая вкладка "Цвет" ***********************************

	Gui, Add, Pic, % "x10 y+10 h184 +0x8 w" Width - 21		; SS_GRAYFRAME = 0x8

	Gui, Font, s10, Segoe UI
	Gui, Add, Text, % "Center c5021AC x15 yp+10 w" Width-30, Настройки цвета и прозрачности
	Gui, Font, s8 q5, Verdana
	Gui, Add, Text, x25 yp+37 w30 cRed Center, Red
	Gui, Add, Text, xp-2 yp+30 wp cGreen Center, Green
	Gui, Add, Text, xp yp+30 wp cBlue Center, Blue
	Gui, Add, Text, xp yp+30 wp cEECC00 Center, Trans

	ColorState := App.Settings.Color := Object()
	ColorState.Red := (App.Config.ARGB & 0xFF0000)>>16
	ColorState.Green := (App.Config.ARGB & 0xFF00)>>8
	ColorState.Blue := App.Config.ARGB & 0xFF
	ColorState.Trans := App.Config.ARGB>>24

	Options := "x65 w" Width - 115 " h20 Range0-255 ToolTip AltSubmit"
	Gui, Add, Slider, gUpdateSelectionColorFromGui %Options% yp-91 hwndRedSliderHwnd, % ColorState.Red
	Gui, Add, Slider, gUpdateSelectionColorFromGui %Options% yp+30 hwndGreenSliderHwnd, % ColorState.Green
	Gui, Add, Slider, gUpdateSelectionColorFromGui %Options% yp+30 hwndBlueSliderHwnd, % ColorState.Blue
	Gui, Add, Slider, gUpdateSelectionTransparencyFromGui %Options% yp+30 hwndTransSliderHwnd, % ColorState.Trans

	Gui, Add, Text, xp+195 yp-89 w30 cRed Center hwndRedValueHwnd, % ColorState.Red
	Gui, Add, Text, xp yp+30 wp cGreen Center hwndGreenValueHwnd, % ColorState.Green
	Gui, Add, Text, xp yp+30 wp cBlue Center hwndBlueValueHwnd, % ColorState.Blue
	Gui, Add, Text, xp yp+30 wp cEECC00 Center hwndTransValueHwnd, % ColorState.Trans
	App.Settings.RedSliderHwnd := RedSliderHwnd
	App.Settings.GreenSliderHwnd := GreenSliderHwnd
	App.Settings.BlueSliderHwnd := BlueSliderHwnd
	App.Settings.TransSliderHwnd := TransSliderHwnd
	App.Settings.RedValueHwnd := RedValueHwnd
	App.Settings.GreenValueHwnd := GreenValueHwnd
	App.Settings.BlueValueHwnd := BlueValueHwnd
	App.Settings.TransValueHwnd := TransValueHwnd

;********************************* Вторая вкладка "Вывод" ************************************

	Gui, Tab, 2
	Gui, Font, s10, Segoe UI
	Gui, Add, Text, % "Center c5021AC x5 y+6 w" Width-10, Как сохранять скриншоты:
	Gui, Font, s8 q5, Verdana
	Gui, Add, Checkbox
		, % "gUpdateOutputTargetFromGui x15 yp+25 hwndOutputFileHwnd" (InStr(App.Config.Output, "File") ? " Checked" : ""), В файл
	Gui, Add, Checkbox
		, % "gUpdateOutputTargetFromGui xp+75 yp hwndOutputClipHwnd" (InStr(App.Config.Output, "Clip") ? " Checked" : ""), В буфер обмена
	Gui, Add, Checkbox
		, % "gUpdateOutputTargetFromGui xp+125 yp hwndOutputImgbbHwnd" (InStr(App.Config.Output, "Imgbb") ? " Checked" : ""), Imgbb.com
	App.Settings.OutputFileHwnd := OutputFileHwnd
	App.Settings.OutputClipHwnd := OutputClipHwnd
	App.Settings.OutputImgbbHwnd := OutputImgbbHwnd

	Gui, Add, Pic, % "x2 yp+26 h2 +0x8 w" Width - 4		; SS_GRAYFRAME = 0x8

	Gui, Font, s10, Segoe UI
	Gui, Add, Text, % "Center c5021AC x5 yp+9 w" Width-10, Тип файлов для вывода
	Gui, Font, s8 q5, Verdana
	Gui, Add, Radio, % "gSetOutputExtensionFromGui x25 yp+23 hwndExtensionPngHwnd" (App.Config.Ext = "png" ? " Checked" : ""), png
	Gui, Add, Radio, % "gSetOutputExtensionFromGui xp+55 yp hwndExtensionJpgHwnd" (App.Config.Ext = "jpg" ? " Checked" : ""), jpg
	Gui, Add, Radio, % "gSetOutputExtensionFromGui xp+55 yp hwndExtensionBmpHwnd" (App.Config.Ext = "bmp" ? " Checked" : ""), bmp
	Gui, Add, Radio, % "gSetOutputExtensionFromGui xp+55 yp hwndExtensionTifHwnd" (App.Config.Ext = "tif" ? " Checked" : ""), tif
	Gui, Add, Radio, % "gSetOutputExtensionFromGui xp+55 yp hwndExtensionGifHwnd" (App.Config.Ext = "gif" ? " Checked" : ""), gif
	App.Settings.ExtensionPngHwnd := ExtensionPngHwnd
	App.Settings.ExtensionJpgHwnd := ExtensionJpgHwnd
	App.Settings.ExtensionBmpHwnd := ExtensionBmpHwnd
	App.Settings.ExtensionTifHwnd := ExtensionTifHwnd
	App.Settings.ExtensionGifHwnd := ExtensionGifHwnd

	Gui, Font, s10, Segoe UI
	Gui, Add, Text, Left c5021AC x20 yp+23, Качество jpg:
	Gui, Add, Slider
		, gSetJpegQuality xp+107 yp w128 h20 Range50-100 ToolTip AltSubmit hwndJpegQualitySliderHwnd, % App.Config.JPEGQuality
	Gui, Font, s8 q5, Verdana
	Gui, Add, Text, Center c5021AC xp+132 yp+2 w30 hwndJpegQualityPercentHwnd, % App.Config.JPEGQuality "`%"
	App.Settings.JpegQualitySliderHwnd := JpegQualitySliderHwnd
	App.Settings.JpegQualityPercentHwnd := JpegQualityPercentHwnd

	Gui, Add, Pic, % "x2 yp+26 h2 +0x8 w" Width - 4		; SS_GRAYFRAME = 0x8

	Gui, Font, s10, Segoe UI
	Gui, Add, Text, % "Center c5021AC x5 yp+9 w" Width-10, Папка для сохранения файлов
	Gui, Font, s8 q5, Verdana
	Gui, Add, Edit, % "hwndOutDirectoryEditHwnd x10 yp+24 h21 w" Width-50, % App.Config.OutDirectory
	App.Settings.OutDirectoryEditHwnd := OutDirectoryEditHwnd
	Gui, Font, s10
	Gui, Add, Button, % "gSelectOutputDirectory x" Width-35 " yp hp w23", ...

;************************** Третья вкладка "Настройки программы" ****************************

	Gui, Tab, 3
	Gui, Font, s8 q5, Verdana
	Gui, Add, Checkbox
		, % "x20 y+15 hwndCaptureCursorHwnd" (App.Config.CaptureCursor ? " Checked" : "")
		, Включать курсор в скриншот
	Gui, Add, Checkbox, % "xp yp+25 hwndShowPreviewHwnd" (App.Config.ShowPreview ? " Checked" : "")
		, Показывать скриншот перед сохранением
	Gui, Add, Checkbox, % "xp yp+25 hwndNotifyHwnd" (App.Config.Notify ? " Checked" : "")
		, Уведомлять о сохранении скриншота

	Gui, Add, Checkbox
		, % "xp yp+25 hwndRunOnWinStartHwnd" (App.Config.RunOnWinStart ? " Checked" : "")
		, Запускать программу при загрузке системы
	App.Settings.CaptureCursorHwnd := CaptureCursorHwnd
	App.Settings.ShowPreviewHwnd := ShowPreviewHwnd
	App.Settings.NotifyHwnd := NotifyHwnd
	App.Settings.RunOnWinStartHwnd := RunOnWinStartHwnd
	Gui, Add, Checkbox, xp yp+25, Проверять обновления при запуске
	; Gui, Add, Pic, % "x2 yp+30 h2 +0x8 w" Width - 4		; SS_GRAYFRAME = 0x8

	Gui, Font, s10, Segoe UI
	Gui, Add, Text, % "Center c5021AC x5 yp+25 w" Width-10
		, Индикаторы при изменении размеров`nокна выделения
	Gui, Font, s8 q5, Verdana
	Gui, Add, Radio, xp+14 yp+40 hwndResizeCursorsHwnd, Изменённые курсоры
	Gui, Add, Radio, xp+153 yp hwndResizeMarkersHwnd, Цветные маркеры
	GuiControl,, % (App.Config.ResizePointers = "Cursors" ? ResizeCursorsHwnd : ResizeMarkersHwnd), 1
	App.Settings.ResizeCursorsHwnd := ResizeCursorsHwnd
	App.Settings.ResizeMarkersHwnd := ResizeMarkersHwnd
	
	Gui, Tab
	
;*************************** Четвёртая вкладка "Горячие клавиши" *****************************

	HotkeysTabPageHwnd := DllCall("GetWindow", "Ptr", App.Settings.TabHwnd, "UInt", GW_HWNDNEXT := 2)
	WinGetPos,,, W, H, ahk_id %HotkeysTabPageHwnd%
	
	Gui, 4: Default
	Gui, +Parent%HotkeysTabPageHwnd% -Caption +ToolWindow +hwndHotkeysHwnd
	App.Settings.HotkeysHwnd := HotkeysHwnd
	Gui, Add, Tab3, % "gFocusSettingsSafeButton x" 5 " y" 5 " w" W - 8 " h" H - 9 " hwndHotkeysSubTabHwnd", Скриншоты|Окна скрипта|Служебные
	App.Settings.HotkeysSubTabHwnd := HotkeysSubTabHwnd
	Handles := []
	HotkeyControls := []
	HotkeyNames := ScreenCatcherHotkeyNames()
	for k, v in [ "Скриншот под окном выделения"
				, "Скриншот активного окна"
				, "Скриншот всего экрана"
				, "Скрыть/показать окно выделения"
				, "Окно выделения в центр экрана"
				, "Выделить превью скриншота"
				, "Открыть папку вывода"
				, "Выбрать область для скриншота"
				, "Быстро выделить область для скриншота" ]
	{
		if k = 4
			Gui, Tab, 2
		if k = 7
			Gui, Tab, 3
		Gui, Font, s10, Segoe UI
		Gui, Add, Text, % "x0 y+8 w" . (Width - 6) . " Center c5021AC", %v%:
		Gui, Font, s11, calibri
		Gui, Add, Edit, % "Center ReadOnly x15 y+5 h23 w" . (Width-36) . " hwndhHotkey" . k
		SetStaticColor(hHotkey%k%, 0xFFFFFF)
		Handles.Push(hHotkey%k%)
		Options := "KDMLR"
		HotkeyControls.Push([HotkeyNames[k], hHotkey%k%, Options])
	}
	Hotkey_Register(HotkeyControls*)
	SetHotkeyControlsFromVars()
	Gui, Show, Hide x0 y0 w%W% h%H%
	App.Settings.MessageHandlers := {}
	App.Settings.MessageHandlers.WM_COMMAND := Func("WM_COMMAND").Bind(Handles)
	App.Settings.MessageHandlers.WM_SETCURSOR := Func("WM_SETCURSOR").Bind(Handles)
	OnMessage( 0x111, App.Settings.MessageHandlers.WM_COMMAND )
	OnMessage( 0x20, App.Settings.MessageHandlers.WM_SETCURSOR )
	
;***********************************************************************************************

	Gui, 2:Default
	
	Gui, Font, s8 q5, Verdana
	Gui, Add, Button, % "gConfirmSettingsFromGui x" Width - 286 " y" Height + 14 " w85 h23", OK
	Gui, Add, Button, gApplySettingsFromGui xp+90 yp wp hp, Применить
	Gui, Add, Button, gCancelSettingsFromGui xp+90 yp wp hp, Отмена
	
	if WinExist("ahk_id " SelectionHwnd) {
		WinGetPos, X1, Y1, W1, H1, ahk_id %SelectionHwnd%
		Gui, Show, % "w" Width-1 " h" Height+51 " Hide", Настройки
		DetectHiddenWindows, On
		WinGetPos,,, W2, H2, % "ahk_id " App.Settings.WindowHwnd
		X := ((X2 := X1 + (W1 - W2)//2) + W2 > A_ScreenWidth) ? A_ScreenWidth - W2 : X2 < 0 ? 0 : X2
		Y := ((Y2 := Y1 + H1 + 20) + H2 < A_ScreenHeight) ? Y2 : (Y2 := Y1 - 20 - H2) < 0 ? 0 : Y2
		Gui, Show, x%X% y%Y%, Настройки
		DetectHiddenWindows, Off
	} else {
		Gui, Show, % "w" Width-1 " h" Height+51, Настройки
	}
	return
}

2GuiEscape() {
	CancelSettingsFromGui()
}

2GuiClose() {
	CancelSettingsFromGui()
}

;^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Конец окна Настроек ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

ScreenShotMenu() {
	ScreenShotMenuHandling()
}

SetInterpolationMode() {
	SetInterpolationModeFromMenu(A_ThisMenuItemPos)
}

ReloadScript() {
	SelectionHwnd := App.Selection.Hwnd
	if WinExist("ahk_id " SelectionHwnd) {
		Reload
		return
	}

	Run, % """" A_AhkPath """ /restart """ A_ScriptFullPath """ HideSelection", % A_ScriptDir
	ExitApp
}

EditScript() {
	Edit
}

ShowHelp() {
	HideSettingsWindow()
	if !IsObject(App.Help)
		App.Help := {}

	HelpText := BuildHelpText()
	if WindowExistsByHwnd(App.Help.WindowHwnd) {
		GuiControl, 6:, % App.Help.TextHwnd, % HelpText
		Gui, 6:Show,, Справка
		return
	}

	Width := 520, TextHeight := 410, Margin := 14, ButtonWidth := 90
	TextWidth := Width - Margin * 2
	WindowHeight := TextHeight + 62

	Gui, 6:+LastFound +hwndHelpHwnd
	App.Help.WindowHwnd := HelpHwnd
	Gui, 6:Margin, %Margin%, %Margin%
	Gui, 6:Font, s10 q5, Segoe UI
	Gui, 6:Add, Edit, % "ReadOnly +VScroll w" TextWidth " h" TextHeight " hwndHelpTextHwnd", % HelpText
	SetStaticColor(HelpTextHwnd, 0xFFFFFF, 0x000000)
	App.Help.TextHwnd := HelpTextHwnd
	Gui, 6:Add, Button, % "gCloseHelpWindow Default x" (Width - Margin - ButtonWidth) " y+10 w" ButtonWidth " h24", OK
	Gui, 6:Show, % "w" Width " h" WindowHeight, Справка
	PostMessage, 0x00B1, 0, 0, , ahk_id %HelpTextHwnd%   ;  EM_SETSEL
	return
}

6GuiEscape() {
	HideHelpWindow()
}

6GuiClose() {
	HideHelpWindow()
}

CloseHelpWindow(CtrlHwnd := "", GuiEvent := "", EventInfo := "", EventErrorLevel := "") {
	HideHelpWindow()
}

HideHelpWindow() {
	if !IsObject(App.Help)
		return
	if WindowExistsByHwnd(App.Help.WindowHwnd)
		Gui, 6:Hide
}

HideSettingsWindow() {
	if WindowExistsByHwnd(App.Settings.HotkeysHwnd)
		Gui, 4:Hide
	if WindowExistsByHwnd(App.Settings.WindowHwnd) {
		Gui, 2:Hide
		ResumeScriptHotkeysAfterSettings()
	}
}

BuildHelpText() {
Text =
(
ScreenCatcher

ScreenCatcher делает снимки экрана, активного окна и выбранной области. Снимок можно сохранить в файл, скопировать в буфер обмена, открыть в предпросмотре или загрузить на Imgbb.

Возможности:
- снимок всего экрана, активного окна или выбранной области;
- выбор области рамкой или быстрым выделением мышью поверх экрана;
- точное позиционирование, центрирование и изменение размера окна выделения;
- предпросмотр снимков: окна можно перемещать, изменять в размере и открывать по ним контекстное меню;
- настройка формата файла, качества JPEG и папки сохранения;
- захват курсора, уведомления и запуск вместе с Windows;
- настройка цвета, прозрачности и маркеров окна выделения.
)
	Text .= "`r`n`r`nГорячие клавиши:`r`n" . BuildHelpHotkeysText()
	Text .= "`r`n`r`nПодсказки:`r`n"
	Text .= "- Если горячая клавиша не назначена, действие можно вызвать из меню трея.`r`n"
	Text .= "- При быстром выделении мышью выберите нужную область; Esc или правая кнопка отменяют выбор.`r`n"
	Text .= "- Горячие клавиши можно изменить в настройках."
	return Text
}

BuildHelpHotkeysText() {
	Labels := ScreenCatcherHotkeyLabels()
	Text := ""
	for k, HotkeyName in ScreenCatcherHotkeyNames()
		Text .= Labels[HotkeyName] ": " HelpHotkeyText(App.Hotkeys[HotkeyName]) "`r`n"
	return RTrim(Text, "`r`n")
}

HelpHotkeyText(HotkeyValue) {
	Text := Hotkey_HKToStr(HotkeyValue)
	return (Text = "Нет" || !IsHotkeyEnabled(HotkeyValue)) ? "не назначена" : Text
}

WindowExistsByHwnd(Hwnd) {
	if !Hwnd
		return false
	DetectHiddenWindowsWasOn := A_DetectHiddenWindows
	DetectHiddenWindows, On
	Exists := WinExist("ahk_id " Hwnd)
	DetectHiddenWindows, %DetectHiddenWindowsWasOn%
	return Exists
}

ShowSettingsFromMenu(ItemName := "", ItemPos := "", MenuName := "") {
	OpenSettingsWindow(SettingsTabFromMenu(MenuName, ItemPos))
}

SettingsTabFromMenu(MenuName, ItemPos) {
	if (MenuName = "Tray")
		return ItemPos - 1
	return ItemPos + 1
}

StoreSelectionWindowHwnd() {
	WinGet, SelectionHwnd
	App.Selection.Hwnd := SelectionHwnd
}

ShutdownApplication() {
	SelectionHwnd := App.Selection.Hwnd

	Loop {
		if (MaxIndex := App.Windows.MaxIndex()) {
			Gui, %MaxIndex%:Destroy
			App.Windows.Remove(MaxIndex, "")
			Sleep, 50
		}
	} until !MaxIndex

	if WinExist("ahk_id " SelectionHwnd)
		Melt(SelectionHwnd, App.Config.ARGB>>24, 0)
	if IsObject(App.Selection.Overlay)
		App.Selection.Overlay.Dispose()

	RestoreCursors()
	GdipShutdown(App.GdipToken)

	SaveSettings()
	ExitApp
}

MenuHotkeyCaption(Text, HotkeyValue) {
	hk := Hotkey_HKToStr(HotkeyValue)
	return Text . (hk = "Нет" ? "" : A_Tab . hk)
}

UpdateMenuHotkeyCaptions() {
	SelectionHwnd := App.Selection.Hwnd

	NewTrayMenuHide := MenuHotkeyCaption("Скрыть окно выделения", App.Hotkeys.ShowHideSelWindow)
	NewTrayMenuShow := MenuHotkeyCaption("Показать окно выделения", App.Hotkeys.ShowHideSelWindow)
	NewTrayMenuCenter := MenuHotkeyCaption("Главное окно в центр экрана", App.Hotkeys.SelWindowToCenter)
	NewTrayMenuOpenOutDir := MenuHotkeyCaption("Открыть папку вывода", App.Hotkeys.OpenOutputFolder)
	NewMainMenuArea := MenuHotkeyCaption("Сделать скриншот", App.Hotkeys.GetArea)
	NewMainMenuHide := MenuHotkeyCaption("Скрыть окно выделения", App.Hotkeys.ShowHideSelWindow)
	NewMainMenuOpenOutDir := MenuHotkeyCaption("Открыть папку вывода", App.Hotkeys.OpenOutputFolder)

	if WinExist("ahk_id " SelectionHwnd)
		Menu, Tray, Rename, % App.Menu.TrayHide, % NewTrayMenuHide
	else
		Menu, Tray, Rename, % App.Menu.TrayShow, % NewTrayMenuShow
	Menu, Tray, Rename, % App.Menu.TrayCenter, % NewTrayMenuCenter
	Menu, Tray, Rename, % App.Menu.TrayOpenOutDir, % NewTrayMenuOpenOutDir

	Menu, MainMenu, Rename, % App.Menu.MainArea, % NewMainMenuArea
	Menu, MainMenu, Rename, % App.Menu.MainHide, % NewMainMenuHide
	Menu, MainMenu, Rename, % App.Menu.MainOpenOutDir, % NewMainMenuOpenOutDir

	App.Menu.TrayHide := NewTrayMenuHide
	App.Menu.TrayShow := NewTrayMenuShow
	App.Menu.TrayCenter := NewTrayMenuCenter
	App.Menu.TrayOpenOutDir := NewTrayMenuOpenOutDir
	App.Menu.MainArea := NewMainMenuArea
	App.Menu.MainHide := NewMainMenuHide
	App.Menu.MainOpenOutDir := NewMainMenuOpenOutDir
}

SaveSettings() {
	S_FormatInteger := A_FormatInteger
	SetFormat, IntegerFast, H
					;  секция          ключ                значение
	IniWriteSect("Color-Transparent", "ARGB"             , App.Config.ARGB)

	SetFormat, IntegerFast, D
	IniWriteSect("Output_Settings"  , "Output"           , App.Config.Output
								    , "Extension"        , App.Config.Ext
								    , "JPEG_Quality"     , App.Config.JPEGQuality
								    , "Out_Directory"    , App.Config.OutDirectory)

	IniWriteSect("Program_Settings" , "Capture_Cursor"   , App.Config.CaptureCursor
									, "Show_Preview"     , App.Config.ShowPreview
									, "Notify"           , App.Config.Notify
									, "Resize_Pointers"  , App.Config.ResizePointers
									, "InterpolationMode", App.Config.InterpolationMode)

	IniWriteSect("Hotkeys"          , "GetArea"          , App.Hotkeys.GetArea
									, "GetActiveWindow"  , App.Hotkeys.GetActiveWindow
									, "GetScreen"        , App.Hotkeys.GetScreen
									, "ShowHideSelWindow", App.Hotkeys.ShowHideSelWindow
									, "SelWindowToCenter", App.Hotkeys.SelWindowToCenter
									, "SelectPreview"    , App.Hotkeys.SelectPreview
									, "OpenOutputFolder" , App.Hotkeys.OpenOutputFolder
									, "SelectAreaOverlay", App.Hotkeys.SelectAreaOverlay
									, "QuickSelectAreaOverlay", App.Hotkeys.QuickSelectAreaOverlay)
	SetFormat, IntegerFast, %S_FormatInteger%
}

SettingsStateVars() {
	return "ARGB|Output|ext|JPEGQuality|OutDirectory|CaptureCursor|ShowPreview|Notify|"
		 . "RunOnWinStart|ResizePointers|GetArea|GetActiveWindow|GetScreen|"
		 . "ShowHideSelWindow|SelWindowToCenter|SelectPreview|OpenOutputFolder|"
		 . "SelectAreaOverlay|QuickSelectAreaOverlay"
}

SavePreviousSettingsState(StateVars := "") {
	if (StateVars = "")
		StateVars := SettingsStateVars()
	App.Settings.Previous := {}
	Loop, parse, StateVars, |
		App.Settings.Previous[A_LoopField] := SettingsStateValue(A_LoopField)
}

RestorePreviousSettingsState(StateVars := "") {
	SelectionHwnd := App.Selection.Hwnd

	Previous := App.Settings.Previous
	if !IsObject(Previous)
		return

	if (App.Config.ARGB != Previous.ARGB)
		ColorChange := App.Config.ARGB

	if WinExist("ahk_id " SelectionHwnd)
		SetResizePointerTimer("Off")

	if (StateVars = "")
		StateVars := SettingsStateVars()
	Loop, parse, StateVars, |
		SetSettingsStateValue(A_LoopField, Previous[A_LoopField])
	App.Config.ResizePointers := NormalizeResizePointersValue(App.Config.ResizePointers)
	SyncSettingsColorFromARGB()
	SetHotkeyControlsFromVars()

	if WinExist("ahk_id " SelectionHwnd)
		SetResizePointerTimer(50)

	if ColorChange {
		if WinExist("ahk_id " SelectionHwnd)
			Melt(SelectionHwnd, ColorChange>>24, 0), PrevExist := 1
		Gui, 1:Color, % Format("{:X}", App.Config.ARGB & 0xFFFFFF)
		UpdateMarkerColor()
		if PrevExist
			PrevExist := "", Melt(SelectionHwnd, App.Config.ARGB>>24, 1)
		ColorChange := ""
	}
}

SyncSettingsColorFromARGB() {
	if !IsObject(App.Settings.Color)
		App.Settings.Color := {}
	App.Settings.Color.Red := (App.Config.ARGB & 0xFF0000)>>16
	App.Settings.Color.Green := (App.Config.ARGB & 0xFF00)>>8
	App.Settings.Color.Blue := App.Config.ARGB & 0xFF
	App.Settings.Color.Trans := App.Config.ARGB>>24
}

SettingsStateValue(Name) {
	for index, HotkeyName in ScreenCatcherHotkeyNames()
		if (Name = HotkeyName)
			return App.Hotkeys[HotkeyName]

	for index, ConfigName in SettingsConfigNames()
		if (Name = ConfigName)
			return App.Config[ConfigName]

	if (Name = "ResizePointers")
		return NormalizeResizePointersValue(App.Config.ResizePointers)
	if (Name = "InterpolationMode")
		return App.Config.InterpolationMode
	return ""
}

SetSettingsStateValue(Name, Value) {
	for index, HotkeyName in ScreenCatcherHotkeyNames() {
		if (Name = HotkeyName) {
			App.Hotkeys[HotkeyName] := Value
			return
		}
	}

	for index, ConfigName in SettingsConfigNames() {
		if (Name = ConfigName) {
			App.Config[ConfigName] := Value
			return
		}
	}

	if (Name = "ResizePointers")
		App.Config.ResizePointers := NormalizeResizePointersValue(Value)
	else if (Name = "InterpolationMode")
		App.Config.InterpolationMode := Value
}

SettingsConfigNames() {
	return [ "ARGB", "Output", "ext", "JPEGQuality", "OutDirectory"
		   , "CaptureCursor", "ShowPreview", "Notify", "RunOnWinStart" ]
}

CloseSettingsWindow() {
	if IsObject(App.Settings.MessageHandlers) {
		OnMessage( 0x111, App.Settings.MessageHandlers.WM_COMMAND, 0 )
		OnMessage( 0x20, App.Settings.MessageHandlers.WM_SETCURSOR, 0 )
		App.Settings.MessageHandlers := ""
	}
	Gui, 2:Destroy
	ResumeScriptHotkeysAfterSettings()
}

SuspendScriptHotkeysForSettings() {
	if App.Settings.ScriptHotkeysSuspended
		return
	SetScriptHotkeys(0)
	App.Settings.ScriptHotkeysSuspended := true
}

ResumeScriptHotkeysAfterSettings() {
	if !App.Settings.ScriptHotkeysSuspended
		return
	App.Settings.ScriptHotkeysSuspended := false
	SetScriptHotkeys(1)
}

ApplySettingsFromGui(CtrlHwnd := "", GuiEvent := "", EventInfo := "", EventErrorLevel := "") {
	if !ApplyHotkeySettings()
		return false
	ApplyProgramSettings()
	CheckImgbbClientIdOnApply()
	SaveSettings()
	SavePreviousSettingsState()
	return true
}

CheckImgbbClientIdOnApply() {
	if !InStr(App.Config.Output, "Imgbb")
		return

	EnsureImgbbClientId()
}

EnsureImgbbClientId() {
	IniPath := IniFilePath()
	MissingValue := Chr(1) . "__ScreenCatcherMissing__"
	IniRead, ClientID, % IniPath, Imgbb, ClientID, % MissingValue
	if (ClientID = MissingValue) {
		EmptyValue := ""
		IniWrite, % EmptyValue, % IniPath, Imgbb, ClientID
		ClientID := ""
	}

	if (Trim(ClientID) != "")
		return ClientID

	Message := "Для отправки скриншотов на Imgbb нужен API key.`n`n"
		. "Зарегистрируйтесь: https://imgbb.com/`n"
		. "Получите ключ: https://api.imgbb.com/`n`n"
		. "Впишите ключ в файл ScreenCatcher.ini:`n"
		. "[Imgbb]`nClientID=..."
	SetOwnDialogsForOpenWindows()
	MsgBox, 48, Нужен ключ Imgbb, %Message%
	Run, % "notepad.exe """ IniPath """"
	return ""
}

SetOwnDialogsForOpenWindows() {
	if WindowExistsByHwnd(App.Selection.Hwnd)
		Gui, 1:+OwnDialogs
	if WindowExistsByHwnd(App.Settings.WindowHwnd)
		Gui, 2:+OwnDialogs
	if WindowExistsByHwnd(App.Settings.HotkeysHwnd)
		Gui, 4:+OwnDialogs
	if WindowExistsByHwnd(App.Menu.HighlightHwnd) || WinExist("Imgbb.com ahk_class AutoHotkeyGUI")
		Gui, 5:+OwnDialogs
	if IsObject(App.Help) && WindowExistsByHwnd(App.Help.WindowHwnd)
		Gui, 6:+OwnDialogs
	if IsObject(App.Selection.Overlay) && IsObject(App.Selection.Overlay.overlay)
			&& WindowExistsByHwnd(App.Selection.Overlay.overlay.hwnd)
		Gui, SelectionOverlay:+OwnDialogs

	for GuiNumber, WindowData in App.Windows {
		if IsObject(WindowData) && WindowExistsByHwnd(WindowData.Hwnd)
			Gui, %GuiNumber%:+OwnDialogs
	}
}

ConfirmSettingsFromGui(CtrlHwnd := "", GuiEvent := "", EventInfo := "", EventErrorLevel := "") {
	if !ApplySettingsFromGui()
		return false
	CloseSettingsWindow()
	return true
}

CancelSettingsFromGui(CtrlHwnd := "", GuiEvent := "", EventInfo := "", EventErrorLevel := "") {
	RestorePreviousSettingsState()
	CloseSettingsWindow()
}

ScreenCatcherHotkeyNames() {
	return ["GetArea", "GetActiveWindow", "GetScreen", "ShowHideSelWindow"
		  , "SelWindowToCenter", "SelectPreview", "OpenOutputFolder"
		  , "SelectAreaOverlay", "QuickSelectAreaOverlay"]
}

ScreenCatcherHotkeyLabels() {
	return { GetArea:           "Скриншот под окном выделения"
		   , GetActiveWindow:   "Скриншот активного окна"
		   , GetScreen:         "Скриншот всего экрана"
		   , ShowHideSelWindow: "Скрыть/показать окно выделения"
		   , SelWindowToCenter: "Окно выделения в центр экрана"
		   , SelectPreview:     "Выделить превью скриншота"
		   , OpenOutputFolder:  "Открыть папку вывода"
		   , SelectAreaOverlay: "Начать выделение мышью"
		   , QuickSelectAreaOverlay: "Быстро выделить область мышью" }
}

ScreenCatcherHotkeyIndex(Name) {
	for k, v in ScreenCatcherHotkeyNames() {
		if (v = Name)
			return k
	}
	return 0
}

GetScriptHotkeys() {
	return { GetArea: App.Hotkeys.GetArea
		   , GetActiveWindow: App.Hotkeys.GetActiveWindow
		   , GetScreen: App.Hotkeys.GetScreen
		   , ShowHideSelWindow: App.Hotkeys.ShowHideSelWindow
		   , SelWindowToCenter: App.Hotkeys.SelWindowToCenter
		   , SelectPreview: App.Hotkeys.SelectPreview
		   , OpenOutputFolder: App.Hotkeys.OpenOutputFolder
		   , SelectAreaOverlay: App.Hotkeys.SelectAreaOverlay
		   , QuickSelectAreaOverlay: App.Hotkeys.QuickSelectAreaOverlay }
}

SetHotkeyControlsFromVars() {
	for k, name in ScreenCatcherHotkeyNames()
		Hotkey_Set(name, App.Hotkeys[name])
}

ApplyHotkeySettings() {
	if (Duplicate := FindDuplicateHotkey()) {
		ShowDuplicateHotkey(Duplicate)
		return false
	}

	oldHotkeys := GetScriptHotkeys()
	for k, name in ScreenCatcherHotkeyNames()
		App.Hotkeys[name] := Hotkey_Value(name)

	if !ScriptHotkeysEqual(oldHotkeys, GetScriptHotkeys()) {
		SetScriptHotkeys(0, oldHotkeys)
		if !App.Settings.ScriptHotkeysSuspended
			SetScriptHotkeys(1)
		UpdateMenuHotkeyCaptions()
	}
	return true
}

ScriptHotkeysEqual(a, b) {
	for k, v in a
		if (v != b[k])
			return false
	return true
}

IsHotkeyEnabled(HotkeyValue) {
	return Trim(HotkeyValue) != ""
}

UpdateHotkeysTabVisibility(CtrlHwnd := "", GuiEvent := "", EventInfo := "", EventErrorLevel := "") {
	ControlGet, NTab, Tab,,, % "ahk_id " App.Settings.TabHwnd
	if NTab = 4
		Gui, 4:Show, NA
	else
		Gui, 4:Show, Hide
}

UpdateSelectionColorFromGui(CtrlHwnd := "", GuiEvent := "", EventInfo := "", EventErrorLevel := "") {
	ReadColorControls()
	UpdateSelectionColor()
	UpdateColorValueLabels()
	UpdateMarkerColor()
}

ReadColorControls() {
	GuiControlGet, Red, 2:, % App.Settings.RedSliderHwnd
	GuiControlGet, Green, 2:, % App.Settings.GreenSliderHwnd
	GuiControlGet, Blue, 2:, % App.Settings.BlueSliderHwnd
	GuiControlGet, Trans, 2:, % App.Settings.TransSliderHwnd
	App.Settings.Color.Red := Red
	App.Settings.Color.Green := Green
	App.Settings.Color.Blue := Blue
	App.Settings.Color.Trans := Trans
}

UpdateColorValueLabels() {
	ColorState := App.Settings.Color
	GuiControl, 2:, % App.Settings.RedValueHwnd, % ColorState.Red
	GuiControl, 2:, % App.Settings.GreenValueHwnd, % ColorState.Green
	GuiControl, 2:, % App.Settings.BlueValueHwnd, % ColorState.Blue
	GuiControl, 2:, % App.Settings.TransValueHwnd, % ColorState.Trans
}

UpdateSelectionColor() {
	ColorState := App.Settings.Color
	ColorValue := (ColorState.Red<<16)|(ColorState.Green<<8)|ColorState.Blue
	App.Config.ARGB := (App.Config.ARGB & 0xFF000000)|ColorValue
	SetFormat, IntegerFast, H
	Gui, 1:Color, % ColorValue
	SetFormat, IntegerFast, D
}

UpdateSelectionTransparencyFromGui(CtrlHwnd := "", GuiEvent := "", EventInfo := "", EventErrorLevel := "") {
	GuiControlGet, Trans, 2:, % App.Settings.TransSliderHwnd
	App.Settings.Color.Trans := Trans
	UpdateSelectionTransparency()
	UpdateColorValueLabels()
}

UpdateMarkerColor() {
	MarkerColor := (App.Config.ARGB & 0xFFFFFF)^0xFFFFFF
	for index, MarkerHwnd in App.Selection.MarkerHwnds
		SetColorToControl(MarkerHwnd, MarkerColor)
}

UpdateSelectionTransparency() {
	SelectionHwnd := App.Selection.Hwnd

	Trans := App.Settings.Color.Trans
	WinSet, Transparent, % Trans, ahk_id %SelectionHwnd%
	App.Config.ARGB := Trans<<24|(App.Config.ARGB & 0xFFFFFF)
}

UpdateOutputTargetFromGui(CtrlHwnd := "", GuiEvent := "", EventInfo := "", EventErrorLevel := "") {
	UpdateOutputTarget(OutputControlName(CtrlHwnd), CtrlHwnd)
}

OutputControlName(CtrlHwnd) {
	switch CtrlHwnd {
		case App.Settings.OutputFileHwnd  : return "File"
		case App.Settings.OutputClipHwnd  : return "Clip"
		case App.Settings.OutputImgbbHwnd : return "Imgbb"
		default: return ""
	}
}

UpdateOutputTarget(ControlName, CtrlHwnd := "") {
	if (ControlName = "")
		return
	GuiControlGet, OutVar, 2:, %CtrlHwnd%
	if OutVar
		App.Config.Output .= ControlName
	else {
		Output := App.Config.Output
		StringReplace, Output, Output, % ControlName,, 1
		App.Config.Output := Output
	}
}

SetOutputExtensionFromGui(CtrlHwnd := "", GuiEvent := "", EventInfo := "", EventErrorLevel := "") {
	SetOutputExtension(OutputExtensionFromHwnd(CtrlHwnd))
}

OutputExtensionFromHwnd(CtrlHwnd) {
	switch CtrlHwnd {
		case App.Settings.ExtensionPngHwnd: return "png"
		case App.Settings.ExtensionJpgHwnd: return "jpg"
		case App.Settings.ExtensionBmpHwnd: return "bmp"
		case App.Settings.ExtensionTifHwnd: return "tif"
		case App.Settings.ExtensionGifHwnd: return "gif"
		default: return ""
	}
}

SetOutputExtension(Extension) {
	if (Extension != "")
		App.Config.Ext := Extension
}

SetJpegQuality() {
	GuiControlGet, JPEGQualityValue, 2:, % App.Settings.JpegQualitySliderHwnd
	App.Config.JPEGQuality := JPEGQualityValue
	GuiControl, 2:, % App.Settings.JpegQualityPercentHwnd, % App.Config.JPEGQuality "`%"
}

FocusSettingsSafeButton() {
	GuiControl, 2:Focus, Button17
}

ApplyProgramSettings() {
	SelectionHwnd := App.Selection.Hwnd

	OldResizePointers := App.Config.ResizePointers
	OldRunOnWinStart := App.Config.RunOnWinStart
	GuiControlGet, CaptureCursorChecked, 2:, % App.Settings.CaptureCursorHwnd
	App.Config.CaptureCursor := CaptureCursorChecked
	GuiControlGet, ShowPreviewChecked, 2:, % App.Settings.ShowPreviewHwnd
	App.Config.ShowPreview := ShowPreviewChecked
	GuiControlGet, NotifyChecked, 2:, % App.Settings.NotifyHwnd
	App.Config.Notify := NotifyChecked
	GuiControlGet, RunOnWinStartChecked, 2:, % App.Settings.RunOnWinStartHwnd
	App.Config.RunOnWinStart := RunOnWinStartChecked
	GuiControlGet, CursorsChecked, 2:, % App.Settings.ResizeCursorsHwnd
	App.Config.ResizePointers := CursorsChecked ? "Cursors" : "Markers"

	if (OldRunOnWinStart != App.Config.RunOnWinStart)
		SetRunOnWindowsStart(App.Config.RunOnWinStart)

	if (OldResizePointers != App.Config.ResizePointers && WinExist("ahk_id " SelectionHwnd)) {
		SetResizePointerTimer("Off", OldResizePointers)
		SetResizePointerTimer(50, App.Config.ResizePointers)
	}
}

SelectOutputDirectory() {
	SelectionHwnd := App.Selection.Hwnd

	Old_OutDirectory := App.Config.OutDirectory
	SelectionWasVisible := WinExist("ahk_id " SelectionHwnd)
	if (SelectionWasVisible)
		ToggleSelectionWindow()
	Gui, 2:+OwnDialogs
	CurrentOutDirectory := App.Config.OutDirectory
	FileSelectFolder, OutDir, *%CurrentOutDirectory%, 3, Выберите папку для вывода:
	App.Config.OutDirectory := OutDir ? OutDir : App.Config.OutDirectory
	if (App.Config.OutDirectory != Old_OutDirectory)
		GuiControl, 2:, % App.Settings.OutDirectoryEditHwnd, % App.Config.OutDirectory
	if (SelectionWasVisible && !WinExist("ahk_id " SelectionHwnd))
		ToggleSelectionWindow()
}

IsRunOnWindowsStartEnabled() {
	FileGetShortcut, % GetRunOnWindowsStartShortcutPath(), OutTarget,, OutArgs
	return (OutTarget = A_ScriptFullPath && OutArgs = 1)
}

SetRunOnWindowsStart(Enable) {
	ShortcutPath := GetRunOnWindowsStartShortcutPath()
	if (Enable)
		FileCreateShortcut, % A_ScriptFullPath, % ShortcutPath, % A_ScriptDir, 1
	else
		FileDelete, % ShortcutPath
}

GetRunOnWindowsStartShortcutPath() {
	return A_Startup "\" RegExReplace(A_ScriptName, "(.*)\..*", "$1.lnk")
}

OpenOutputDirectory() {
	Run, % App.Config.OutDirectory
}

UpdateResizePointerHover(Mode) {
	if (Mode = "Cursors")
		App.Selection.ResizeHandle := Cursors1()
	else
		App.Selection.ResizeHandle := Markers1()
}

GuiSize() {
	MoveSelectionResizeMarkers(A_GuiWidth, A_GuiHeight)
}

MoveSelectionResizeMarkers(Width, Height) {
	GuiControl, Move, Static1, % "x" Width - 10 "y" Height - 10
	GuiControl, Move, Static2, % "x0 y" Height - 10
	GuiControl, Move, Static3, % "x0 y0"
	GuiControl, Move, Static4, % "x" Width - 10 "y0"
	GuiControl, Move, Static5, % "x" Width - 10 "y" (Height - 10)/2
	GuiControl, Move, Static6, % "x" (Width - 10)/2 "y" Height - 10
	GuiControl, Move, Static7, % "x0 y" (Height - 10)/2
	GuiControl, Move, Static8, % "x" (Width - 10)/2 "y0"
}

GuiContextMenu() {
	RestoreCursors()
	Menu, MainMenu, Show
}

; Окно выделения в центр экрана
CenterSelectionWindow() {
	SelectionHwnd := App.Selection.Hwnd

	if WinExist("ahk_id " SelectionHwnd)
		Melt(SelectionHwnd, App.Config.ARGB>>24, 0)
	else {
		Menu, Tray, Rename, % App.Menu.TrayShow, % App.Menu.TrayHide
		SetResizePointerTimer(50)
	}
	Gui, 1:Show, % "x" (A_ScreenWidth - 200)//2 " y" (A_ScreenHeight - 200)//2 " h200 w200 Hide"
	Melt(SelectionHwnd, App.Config.ARGB>>24, 1)
}

GuiClose() {
	ToggleSelectionWindow()
}

GuiEscape() {
	ToggleSelectionWindow()
}

HideShow() {
	ToggleSelectionWindow()
}

ToggleSelectionWindow() {
	SelectionHwnd := App.Selection.Hwnd

	Exist := WinExist("ahk_id " SelectionHwnd)
	Melt(SelectionHwnd, App.Config.ARGB>>24, !Exist)
	Menu, Tray, Rename, % Exist ? App.Menu.TrayHide : App.Menu.TrayShow
							, % Exist ? App.Menu.TrayShow : App.Menu.TrayHide
	SetResizePointerTimer(Exist ? "Off" : 50)
	( Exist && NormalizeResizePointersValue(App.Config.ResizePointers) = "Cursors" && RestoreCursors() )
}

CaptureAreaScreenshot() {
	CoordMode, Pixel, Screen
	MouseGetPos, mx, my
	prev_color := 0
	Loop {
		PixelGetColor, cur_color, %mx%, %my%
		if (cur_color = prev_color)
			break
		prev_color := cur_color
		Sleep, 30
	}
	GetScreenshot("Area")
}

SetInterpolationModeFromMenu(MenuItemPos) {
	Menu, Interpolation, UnCheck, % App.Menu.InterpolationItems[App.Config.InterpolationMode]
	App.Config.InterpolationMode := MenuItemPos
	Menu, Interpolation, Check, % App.Menu.InterpolationItems[App.Config.InterpolationMode]
}

FindDuplicateHotkey() {
	Names := ScreenCatcherHotkeyNames()
	Loop % Names.MaxIndex() {
		Name1 := Names[A_Index]
		Value1 := Hotkey_Value(Name1)
		if !IsHotkeyEnabled(Value1)
			continue
		i := A_Index + 1
		Loop % Names.MaxIndex() - A_Index {
			Name2 := Names[i]
			Value2 := Hotkey_Value(Name2)
			if (IsHotkeyEnabled(Value2) && Hotkey_Equal(Value1, Value2))
				return {Name: Name2, OtherName: Name1, Hwnd: Hotkey_ID(Name2), Value: Value2}
			i++
		}
	}
	return ""
}

ShowDuplicateHotkey(Duplicate) {
	Labels := ScreenCatcherHotkeyLabels()
	ActivateHotkeyControl(Duplicate.Name)
	Hwnd := Duplicate.Hwnd
	WinGetPos, X, Y, W, H, ahk_id %Hwnd%
	Text := "Эта горячая клавиша уже назначена:`n" Labels[Duplicate.OtherName]
	new ToolTip({ text: Text, x: X, y: Y + H + 8, TimeOut: -3500
				, BackColor: 0x0055aa, TextColor: "white", FontSize: 10, FontName: "calibri" })
	BlinkWindow(Hwnd)
}

ActivateHotkeyControl(Name) {
	Index := ScreenCatcherHotkeyIndex(Name)
	if !Index
		return
	SubTab := Index < 4 ? 1 : Index < 7 ? 2 : 3
	GuiControl, 2:Choose, % App.Settings.TabHwnd, 4
	Gui, 4:Show, NA
	GuiControl, 4:Choose, % App.Settings.HotkeysSubTabHwnd, %SubTab%
	Hwnd := Hotkey_ID(Name)
	ControlFocus,, ahk_id %Hwnd%
	Sleep, 50
}

BlinkWindow(Hwnd) {
	Loop 6 {
		DllCall("ShowWindowAsync", "Ptr", Hwnd, "Int", Mod(A_Index, 2) ? 0 : 4)
		Sleep, 70
	}
	DllCall("ShowWindowAsync", "Ptr", Hwnd, "Int", 4)
}

SetScriptHotkeys(Enable, Values := "") {
	if !IsObject(Values)
		Values := GetScriptHotkeys()

	Hotkey, IfWinActive
	if IsHotkeyEnabled(Values.GetArea) {
		if Enable {
			obj := Func("GetScreenshot").Bind("Area")
			Hotkey, % Values.GetArea, % obj, On
		} else {
			Hotkey, % Values.GetArea, Off
		}
	}
	if IsHotkeyEnabled(Values.GetActiveWindow) {
		if Enable {
			obj := Func("GetScreenshot").Bind("ActiveWindow")
			Hotkey, % Values.GetActiveWindow, % obj, On
		} else {
			Hotkey, % Values.GetActiveWindow, Off
		}
	}
	if IsHotkeyEnabled(Values.GetScreen) {
		if Enable {
			obj := Func("GetScreenshot").Bind("Screen")
			Hotkey, % Values.GetScreen, % obj, On
		} else {
			Hotkey, % Values.GetScreen, Off
		}
	}
	if IsHotkeyEnabled(Values.ShowHideSelWindow) {
		if Enable
			Hotkey, % Values.ShowHideSelWindow, HideShow, On
		else
			Hotkey, % Values.ShowHideSelWindow, Off
	}
	if IsHotkeyEnabled(Values.SelWindowToCenter) {
		if Enable
			Hotkey, % Values.SelWindowToCenter, CenterSelectionWindow, On
		else
			Hotkey, % Values.SelWindowToCenter, Off
	}
	if IsHotkeyEnabled(Values.OpenOutputFolder) {
		if Enable
			Hotkey, % Values.OpenOutputFolder, OpenOutputDirectory, On
		else
			Hotkey, % Values.OpenOutputFolder, Off
	}
	if IsHotkeyEnabled(Values.SelectAreaOverlay) {
		if Enable {
			obj := Func("StartOverlaySelection").Bind("Arm")
			Hotkey, % Values.SelectAreaOverlay, % obj, On
		} else {
			Hotkey, % Values.SelectAreaOverlay, Off
		}
	}
	if IsHotkeyEnabled(Values.QuickSelectAreaOverlay) {
		if Enable {
			obj := Func("StartOverlaySelection").Bind("Quick")
			Hotkey, % Values.QuickSelectAreaOverlay, % obj, On
		} else {
			Hotkey, % Values.QuickSelectAreaOverlay, Off
		}
	}

	if IsHotkeyEnabled(Values.SelectPreview) {
		Hotkey, if, WinExist("ScreenShot ahk_class AutoHotkeyGUI")
		if Enable
			Hotkey, % Values.SelectPreview, FindHighlight, On
		else
			Hotkey, % Values.SelectPreview, Off
	}
	Hotkey, if
}

SetResizeHotkeyState(State) {
	Hotkey, IfWinActive
	Hotkey, LButton, WinResize, %State%
	Hotkey, if
}

Cursors1() {
	static
	SelectionHwnd := App.Selection.Hwnd
	S = 10
	MouseGetPos, X_M, Y_M, ID_Win
	for index, value in App.Windows {
		if (value.Hwnd = ID_Win) {
			if Cursor {
				RestoreCursors()
				SetResizeHotkeyState("Off")
				Cursor := ""
			}
			return
		}
	}
	WinGetPos, X, Y, W, H, ahk_id %SelectionHwnd%
	XW := X + W, YH := Y + H
	if ((X_M <= XW) && (X_M > XW - 2*S) && (Y_M <= YH) && (Y_M > YH - S))
					|| ((X_M <= XW) && (X_M > XW - S) && (Y_M <= YH) && (Y_M > YH - 2*S))
		Var := 1

	else if ((X_M < X + 2*S) && (X_M >= X) && (Y_M <= YH) && (Y_M > YH - S))
					|| ((X_M < X + S) && (X_M >= X) && (Y_M <= YH) && (Y_M > YH - 2*S))
		Var := 2

	else if ((X_M < X + 2*S) && (X_M >= X) && (Y_M < Y + S) && (Y_M >= Y))
					|| ((X_M < X + S) && (X_M >= X) && (Y_M < Y + 2*S) && (Y_M >= Y))
		Var := 3

	else if ((X_M <= XW ) && (X_M > XW - 2*S) && (Y_M < Y + S) && (Y_M >= Y))
					|| ((X_M <= XW ) && (X_M > XW - S) && (Y_M < Y + 2*S) && (Y_M >= Y))
		Var := 4

	else if (X_M <= XW) && (X_M > XW - S) && (Y_M <= YH - S) && (Y_M >= Y + S)
		Var := 5
	else if (X_M <= XW - S) && (X_M >= X + S) && (Y_M <= YH) && (Y_M > YH - S)
		Var := 6
	else if (X_M < X + S) && (X_M >= X) && (Y_M <= YH - S) && (Y_M >= Y + S)
		Var := 7
	else if (X_M <= XW - S) && (X_M >= X + S) && (Y_M < Y + S) && (Y_M >= Y)
		Var := 8
	else
		Var := ""

	if Var {
		SetResizeHotkeyState("On")
		if (Var = 1 || Var = 3) && (Cursor != "IDC_SIZENWSE")
			RestoreCursors(), SetSystemCursor("IDC_SIZENWSE"), Cursor := "IDC_SIZENWSE"

		if (Var = 2 || Var = 4) && (Cursor != "IDC_SIZENESW")
			RestoreCursors(), SetSystemCursor("IDC_SIZENESW"), Cursor := "IDC_SIZENESW"

		if (Var = 5 || Var = 7) && (Cursor != "IDC_SIZEWE")
			RestoreCursors(), SetSystemCursor("IDC_SIZEWE"), Cursor := "IDC_SIZEWE"

		if (Var = 6 || Var = 8) && (Cursor != "IDC_SIZENS")
			RestoreCursors(), SetSystemCursor("IDC_SIZENS"), Cursor := "IDC_SIZENS"
	} else {
		SetResizeHotkeyState("Off")
		RestoreCursors(), Cursor := ""
	}
	return Var
}

Markers1() {
	SelectionHwnd := App.Selection.Hwnd
	MouseGetPos, X_M, Y_M, ID_Win
	for index, value in App.Windows {
		if (value.Hwnd = ID_Win) {
			SetResizeHotkeyState("Off")
			Loop 8
				GuiControl, Hide, static%a_index%
			return
		}
	}
	WinGetPos, X, Y, W, H, ahk_id %SelectionHwnd%
	XW := X + W, YH := Y + H

	if (X_M <= XW + 20) && (X_M >= XW - 10) && (Y_M <= YH + 20) && (Y_M >= YH - 10)
		Var := 1

	else if (X_M <= X + 10) && (X_M >= X - 20) && (Y_M <= YH + 20) && (Y_M >= YH - 10)
		Var := 2

	else if (X_M <= X + 10) && (X_M >= X - 20) && (Y_M <= Y + 10) && (Y_M >= Y - 20)
		Var := 3

	else if (X_M <= XW + 20) && (X_M >= XW - 10) && (Y_M <= Y + 10) && (Y_M >= Y - 20)
		Var := 4

	else if (X_M <= XW + 20) && (X_M >= XW - 10) && (Y_M < YH - 10) && (Y_M > Y + 10)
		Var := 5

	else if (X_M < XW - 10) && (X_M > X + 10) && (Y_M <= YH + 20) && (Y_M >= YH - 10)
		Var := 6

	else if (X_M <= X + 10) && (X_M >= X - 20) && (Y_M < YH - 10) && (Y_M > Y + 10)
		Var := 7

	else if (X_M < XW - 10) && (X_M > X + 10) && (Y_M <= Y + 10) && (Y_M >= Y - 20)
		Var := 8

	else Var := ""

	if Var {
		SetResizeHotkeyState("On")
		GuiControl, Show, static%Var%
		Loop 8 {
			if (a_index != Var)
				GuiControl, Hide, static%a_index%
		}
	} else {
		SetResizeHotkeyState("Off")
		Loop 8 {
			GuiControl, Hide, static%a_index%
		}
	}
	return Var
}

WinResize() {
	SelectionHwnd := App.Selection.Hwnd
	ResizeVar := App.Selection.ResizeHandle
	SetResizePointerTimer("Off")
	MouseGetPos, X_M_Old, Y_M_Old
	WinGetPos, X, Y, W, H, ahk_id %SelectionHwnd%
	while GetKeyState("LButton", "P") {
		MouseGetPos, X_M, Y_M
		dX := X_M - X_M_Old, dY := Y_M - Y_M_Old
		IfEqual, ResizeVar, 1, WinMove, ahk_id %SelectionHwnd%,,,, W + dX, H + dY
		IfEqual, ResizeVar, 2, WinMove, ahk_id %SelectionHwnd%,, X + dX,, W - dX, H + dY
		IfEqual, ResizeVar, 3, WinMove, ahk_id %SelectionHwnd%,, X + dX, Y + dY, W - dX, H - dY
		IfEqual, ResizeVar, 4, WinMove, ahk_id %SelectionHwnd%,,, Y + dY, W + dX, H - dY
		IfEqual, ResizeVar, 5, WinMove, ahk_id %SelectionHwnd%,,,, W + dX
		IfEqual, ResizeVar, 6, WinMove, ahk_id %SelectionHwnd%,,,,, H + dY
		IfEqual, ResizeVar, 7, WinMove, ahk_id %SelectionHwnd%,, X + dX,, W - dX
		IfEqual, ResizeVar, 8, WinMove, ahk_id %SelectionHwnd%,,, Y + dY,, H - dY
		Sleep, 10
	}
	SetResizePointerTimer("On")
}

IsSelectionWindowActive() {
	SelectionHwnd := App.Selection.Hwnd
	return (SelectionHwnd && WinActive("ahk_id " SelectionHwnd))
}

NudgeSelectionEdge(Edge, Delta) {
	static MinSize := 1

	SelectionHwnd := App.Selection.Hwnd
	if !WinExist("ahk_id " SelectionHwnd)
		return

	WinGetPos, X, Y, W, H, ahk_id %SelectionHwnd%
	if (Edge = "Left") {
		if (W - Delta < MinSize)
			return
		WinMove, ahk_id %SelectionHwnd%,, X + Delta, Y, W - Delta, H
	} else if (Edge = "Top") {
		if (H - Delta < MinSize)
			return
		WinMove, ahk_id %SelectionHwnd%,, X, Y + Delta, W, H - Delta
	} else if (Edge = "Right") {
		if (W + Delta < MinSize)
			return
		WinMove, ahk_id %SelectionHwnd%,, X, Y, W + Delta, H
	} else if (Edge = "Bottom") {
		if (H + Delta < MinSize)
			return
		WinMove, ahk_id %SelectionHwnd%,, X, Y, W, H + Delta
	}
}

NormalizeLoadedSettings() {
	if !IsIntegerSetting(App.Config.ARGB)
		App.Config.ARGB := 0x82FFC700

	if !RegExMatch(App.Config.Output, "^(File|Clip|Imgbb)+$")
		App.Config.Output := "File"

	if !RegExMatch(App.Config.Ext, "^(png|jpg|bmp|tif|gif)$")
		App.Config.Ext := "png"

	if (!IsIntegerSetting(App.Config.JPEGQuality) || App.Config.JPEGQuality < 50 || App.Config.JPEGQuality > 100)
		App.Config.JPEGQuality := 100

	if (App.Config.OutDirectory = "" || App.Config.OutDirectory = "OutDirectory")
		App.Config.OutDirectory := A_ScriptDir

	if !IsBooleanSetting(App.Config.CaptureCursor)
		App.Config.CaptureCursor := 0
	if !IsBooleanSetting(App.Config.ShowPreview)
		App.Config.ShowPreview := 1
	if !IsBooleanSetting(App.Config.Notify)
		App.Config.Notify := 1

	App.Config.ResizePointers := NormalizeResizePointersValue(App.Config.ResizePointers)

	if (!IsIntegerSetting(App.Config.InterpolationMode) || App.Config.InterpolationMode < 1 || App.Config.InterpolationMode > 8)
		App.Config.InterpolationMode := 3
}

NormalizeResizePointersValue(Value) {
	return Value = "Markers" ? "Markers" : "Cursors"
}

SetResizePointerTimer(State, Value := "") {
	TimerName := NormalizeResizePointersValue(Value = "" ? App.Config.ResizePointers : Value)
	SetTimer, %TimerName%, %State%
}

Cursors() {
	UpdateResizePointerHover(A_ThisFunc)
}

Markers() {
	UpdateResizePointerHover(A_ThisFunc)
}

IsIntegerSetting(Value) {
	return RegExMatch(Value, "i)^\s*(?:0x[0-9a-f]+|\d+)\s*$")
}

IsBooleanSetting(Value) {
	return Value = 0 || Value = 1
}

IniReadConfigSect(Section, params*) {
; Читает секцию Section ini-файла в App.Config
	ConfigNames := [ "ARGB", "Output", "Ext", "JPEGQuality", "OutDirectory", "CaptureCursor"
		           , "ShowPreview", "Notify", "ResizePointers", "InterpolationMode" ]
	IniReadObjectSect(Section, App.Config, ConfigNames, params*)
}

IniReadHotkeysSect(Section, params*) {
; Читает секцию Section ini-файла в App.Hotkeys.
	if !IsObject(App.Hotkeys)
		App.Hotkeys := {}
	IniReadObjectSect(Section, App.Hotkeys, ScreenCatcherHotkeyNames(), params*)
}

IniReadObjectSect(Section, Target, AllowedNames, params*) {
; Читает секцию Section ini-файла в объект Target.
	IniName := IniFilePath()
	for index, param in params {
		if Mod(index, 3) = 1
			Key := param
		else if Mod(index, 3) = 2
			Name := param
		else {
			IniRead, Value, % IniName, % Section, % Key, % param
			SetObjectValue(Target, AllowedNames, Name, Value)
		}
	}
}

SetObjectValue(Target, AllowedNames, Name, Value) {
	for index, AllowedName in AllowedNames {
		if (Name = AllowedName) {
			Target[AllowedName] := Value
			return
		}
	}
}

IniWriteSect(Section, params*) {
; Пишет секцию Section ini-файла в директории скрипта.
    IniName := IniFilePath()
    for index, param in params {
		if Mod(index, 2)
			Key := param
		else
			IniWrite, % param, % IniName, % Section, % Key
	}
}

IniFilePath() {
	return RegExReplace(A_ScriptFullPath, "(.*)\..*", "$1.ini")
}

Melt(hWnd, Trans, Appear, N = 20) {
	WinShow, ahk_id %hWnd%
	while (i := A_Index * N) < Trans {
		WinSet, Transparent, % Appear ? i : Trans - i, ahk_id %hWnd%
		Sleep, 10
	}
	if Appear
		WinSet, Transparent, % Trans, ahk_id %hWnd%
	else
		WinHide, ahk_id %hWnd%
}

SetSystemCursor(IDC) {
	static IDC_SIZENWSE := 32642, IDC_SIZENESW := 32643, IDC_SIZEWE := 32644
		 , IDC_SIZENS   := 32645, IDC_ARROW    := 32512
		 , sys_cursors  := [32512,32513,32514,32516,32642,32643,32644,32645,32646,32648,32649,32650]

	hCursor := DllCall("LoadCursor", "UInt", 0, "Int", %IDC%)
	for k, v in sys_cursors {
		hCopy := DllCall("CopyImage", "Ptr", hCursor, "UInt", 2, "Int", 0, "Int", 0, "UInt", 0)
		DllCall("SetSystemCursor", "UInt", hCopy, "Int", v)
	}
}

RestoreCursors() {
    DllCall("SystemParametersInfo", "UInt", SPI_SETCURSORS:=0x57, "UInt", 0, "UInt", 0, "UInt", 0)
}

SetColorToControl(hwnd, Color) {	; окрашивает static-контрол со стилем SS_BITMAP = 0xE в цвет Color
	WinGetPos,,, w, h, ahk_id %hwnd%

	DllCall("gdiplus\GdipCreateSolidFill", "UInt", 0xFF000000|Color, "Ptr*", pBrush)
	DllCall("gdiplus\GdipCreateBitmapFromScan0", "Int", w, "Int", h, "Int", 0, "Int", 0x26200A, "Ptr", 0, "Ptr*", pBitmap)
	DllCall("gdiplus\GdipGetImageGraphicsContext", "Ptr", pBitmap, "Ptr*", G)
	DllCall("gdiplus\GdipFillRectangle", "Ptr", G, "Ptr", pBrush, "Float", 0, "Float", 0, "Float", w, "Float", h)
	DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "Ptr*", hBitmap, "Int", 0xFFFFFFFF)
	SendMessage, STM_SETIMAGE := 0x172,, hBitmap,, ahk_id %hwnd%
	DllCall("DeleteObject", "Ptr", ErrorLevel)
	DllCall("gdiplus\GdipDeleteBrush", "Ptr", pBrush)
	DllCall("gdiplus\GdipDeleteGraphics", "Ptr", G)
	DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
	DllCall("DeleteObject", "Ptr", hBitmap)
}

DDLCreate(N) {
	Loop % N
		String .= A_Index-1 "|"
	return SubStr(String, 1, -1)
}

WM_LBUTTONDOWN(wParam, lParam, msg, hwnd) {
	if IsOverlaySelectionHwnd(hwnd)
		return
	if (A_Gui = 1 || A_Gui > 9) {
		if (A_Gui > 9) {
			App.Menu.CancelPreviewIntroHwnd := hwnd
			HidePreviewHighlight()
		}
		PostMessage, WM_NCLBUTTONDOWN := 0xA1, HTCAPTION := 2,,, % "ahk_id " hwnd
	}

	else if (A_Gui = 2 && WhichPageOfTab(App.Settings.TabHwnd) = 4)
	{
		MouseGetPos,,,, Control
		if InStr(Control, "ComboBox")
		{
			GuiControlGet, OutputVar, 3:Enabled, % Control
			if OutputVar
				return
			else
				GuiControl, 3:Focus, TextT
		}
		else
			GuiControl, 3:Focus, TextT
	}
}

WM_LBUTTONDBLCLK(wParam, lParam, msg, hwnd) {
	if IsOverlaySelectionHwnd(hwnd)
		return
	(A_Gui = 1) && GetScreenshot("Area")
}

WM_RBUTTONDOWN(wParam, lParam, msg, hwnd) {
	if IsOverlaySelectionHwnd(hwnd)
		return
	if (A_Gui < 10 || A_Gui = "Imgbb")
		return
	App.Menu.CurrentPreviewGui := A_Gui
	Menu, % App.Windows[A_Gui].Scale = 1 ? "ScreenShotMenu" : "ScreenShotMenu2", Show
}

AHK_NOTIFYICON(wp, lp) {
	static WM_LBUTTONDOWN := 0x201
	(lp = WM_LBUTTONDOWN) && ToggleSelectionWindow()
}

WhichPageOfTab(SettingsTabHwnd) {
	ControlGet, NTab, Tab,,, ahk_id %SettingsTabHwnd%
	return NTab
}

GetControlFocus(NGui) {
	GuiControlGet, Focus, %NGui%:Focus
	return Focus
}

GetOverlaySelection() {
	if !IsObject(App.Selection.Overlay) {
		gap := 5
		labelSpecs := [{style: {fontStyle: 1}
					  , provider: Func("OverlayTopLabelProvider").Bind(gap)}
					 , {style: {fontStyle: 1}
					  , provider: Func("OverlayBottomLabelProvider").Bind(gap)}]
		armedStyle := {fontStyle: 1, offsetX: 12, offsetY: 12}
		App.Selection.Overlay := new OverlaySelection(2, 0xFFAA00, labelSpecs, armedStyle)
		Gui, 1:Default
	}
	return App.Selection.Overlay
}

StartOverlaySelection(Mode) {
	Overlay := GetOverlaySelection()
	Callback := Func("CaptureOverlaySelection")
	if (Mode = "Quick")
		Overlay.Quick(Callback)
	else
		Overlay.Arm(Callback)
}

CaptureOverlaySelection(Rect) {
	if (Rect.w < 1 || Rect.h < 1)
		return
	GetScreenshot("Area", Rect)
}

OverlayTopLabelProvider(Gap, Context) {
	Rect := Context.r
	return {text: Rect.x . ", " . Rect.y . "  " . Rect.w . "x" . Rect.h
		  , ax: Rect.x
		  , ay: Rect.y - Context.t - Round(Gap * Context.scale)
		  , side: "top"}
}

OverlayBottomLabelProvider(Gap, Context) {
	Rect := Context.r
	return {text: (Rect.x + Rect.w) . ", " . (Rect.y + Rect.h)
		  , ax: Rect.x + Rect.w
		  , ay: Rect.y + Rect.h + Context.t + Round(Gap * Context.scale)
		  , side: "bottom"}
}

IsOverlaySelectionState(State) {
	return IsObject(App.Selection.Overlay) && App.Selection.Overlay.state = State
}

BeginOverlaySelectionDrag() {
	if IsObject(App.Selection.Overlay)
		App.Selection.Overlay.BeginDrag()
}

CancelOverlaySelection() {
	if IsObject(App.Selection.Overlay)
		App.Selection.Overlay.Cancel()
}

FinishOverlaySelection() {
	if IsObject(App.Selection.Overlay)
		App.Selection.Overlay.Finish()
}

GetScreenshot(Area, SelectionRect := "") {
	SelectionHwnd := App.Selection.Hwnd
	SRCCOPY := 0x00CC0020, CAPTUREBLT := 0x40000000
	Critical
	if (Area = "Area") {
		IfWinExist, ahk_id %SelectionHwnd%
		{
			Vis = 1
			WinHide	; Melt(SelectionHwnd, App.Config.ARGB>>24, 0, 50)
		}
		if IsObject(SelectionRect) {
			X := SelectionRect.x
			Y := SelectionRect.y
			W := SelectionRect.w
			H := SelectionRect.h
		} else {
			DetectHiddenWindows, On
			WinGetPos, X, Y, W, H, ahk_id %SelectionHwnd%
		}
		Rect := X "|" Y "|" W "|" H
		pBitmap := BitmapFromScreen(Rect, App.Config.CaptureCursor, SRCCOPY|CAPTUREBLT)
		if Vis
		{
			Sleep, 50
			WinShow	; Melt(SelectionHwnd, App.Config.ARGB>>24, 1, 50)
		}
	} else if (Area = "ActiveWindow") {
		WinGet, hwnd, ID, A
		if !GetVisibleWindowRect(hwnd, X, Y, W, H)
			WinGetPos, X, Y, W, H, ahk_id %hwnd%
		pBitmap := BitmapFromScreen(X "|" Y "|" W "|" H, App.Config.CaptureCursor, SRCCOPY|CAPTUREBLT)
	} else {
		X := Y := 0
		W := A_ScreenWidth
		H := A_ScreenHeight
		pBitmap := BitmapFromScreen(0, App.Config.CaptureCursor, SRCCOPY|CAPTUREBLT)
	}
	Critical, Off   ; capture is done; let the message queue flow for preview/notification

	Time := A_Year "." A_MM  "." A_DD "-" A_Hour "." A_Min "." A_Sec "." SubStr(A_MSec, 1, 1)

	if (App.Config.ShowPreview) {
		ShowPreview(pBitmap, Time, X, Y, W, H)
	} else {
		SaveScreenshot(pBitmap, Time)
	}
}

BitmapFromHWND(hwnd) {
	WinGetPos,,, Width, Height, ahk_id %hwnd%
	hbm := CreateDIBSection(Width, Height)
	hdc := DllCall("CreateCompatibleDC", "Ptr", 0)
	obm := DllCall("SelectObject", "Ptr", hdc, "Ptr", hbm)
	DllCall("PrintWindow", "Ptr", hwnd, "Ptr", hdc, "UInt", PW_RENDERFULLCONTENT := 2)
	DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hbm, "Ptr", 0, "Ptr*", pBitmap)
	DllCall("SelectObject", "Ptr", hdc, "Ptr", obm)
	DllCall("DeleteObject", "Ptr", hbm)
	DllCall("DeleteDC", "Ptr", hdc)
	return pBitmap
}

GetBitmapFromWindow(hwnd) {
	WinGetPos,,, W, H, ahk_id %hwnd%
	hWDC := DllCall("GetWindowDC", "Ptr", hwnd, "Ptr")
	hdc := DllCall("CreateCompatibleDC", "Ptr", hWDC, "Ptr")
	hbm := DllCall("CreateCompatibleBitmap", "Ptr", hWDC, "Int", W, "Int", H)
	obm := DllCall("SelectObject", "Ptr", hdc, "Ptr", hbm)
	DllCall("BitBlt", "Ptr", hdc, "Int", 0, "Int", 0, "Int", W, "Int", H, "Ptr", hWDC, "Int", 0, "Int", 0, "UInt", 0x00CC0020)
	DllCall("ReleaseDC", "Ptr", hwnd, "Ptr", hWDC)
	DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hbm, "Ptr", 0, "Ptr*", pBitmap)
	DllCall("SelectObject", "Ptr", hdc, "Ptr", obm)
	DllCall("DeleteObject", "Ptr", hbm)
	DllCall("DeleteDC", "Ptr", hdc)
	return pBitmap
}

ShowPreview(pBitmap, Time, X, Y, W, H) {
	static WS_EX_LAYERED := 0x80000
	SelectionHwnd := App.Selection.Hwnd

	while App.Windows[i := A_Index+9]
		continue

	Gui, %i%: -Caption +E%WS_EX_LAYERED% +LastFound +AlwaysOnTop +ToolWindow +Owner
	WinGet, PreviewHwnd
	Gui, %i%: Show, NA, ScreenShot

	hbm := CreateDIBSection(W, H)
	hdc := DllCall("CreateCompatibleDC", "Ptr", 0)
	obm := DllCall("SelectObject", "Ptr", hdc, "Ptr", hbm)
	DllCall("gdiplus\GdipCreateFromHDC", "Ptr", hdc, "Ptr*", G)
	DrawImage(G, pBitmap, 0, 0, W, H, 0, 0, W, H)

	UpdateLayeredWindow(PreviewHwnd, hdc, X, Y, W, H)
	WinActivate, ahk_id %PreviewHwnd%

	App.Windows[i] := Object("Hwnd", PreviewHwnd, "Time", Time, "pBM", pBitmap)

	DllCall("SelectObject", "Ptr", hdc, "Ptr", obm)
	DllCall("DeleteObject", "Ptr", hbm)
	DllCall("DeleteDC", "Ptr", hdc), DllCall("gdiplus\GdipDeleteGraphics", "Ptr", G)

	SetWinDelay, 10
	Loop 10 {
		if (App.Menu.CancelPreviewIntroHwnd = PreviewHwnd)
			break
		WinMove, ahk_id %PreviewHwnd%,, X+A_Index, Y-A_Index
	}

	DetectHiddenWindows, Off
	if !WinExist("ahk_id " SelectionHwnd)
		ShowPreviewHighlight(PreviewHwnd)
	if (App.Menu.CancelPreviewIntroHwnd = PreviewHwnd)
		App.Menu.CancelPreviewIntroHwnd := ""
}

ShowPreviewHighlight(PreviewHwnd) {
	Critical
	if (App.Menu.CancelPreviewIntroHwnd = PreviewHwnd) {
		Critical, Off
		return
	}
	Highlight(PreviewHwnd)
	Critical, Off
	Sleep, 300
	HidePreviewHighlight()
}

Highlight(TargetHwnd) {
	HidePreviewHighlight()
	WinActivate, ahk_id %TargetHwnd%
	; WS_EX_LAYERED := 0x80000, WS_EX_TRANSPARENT := 0x20
	Gui, 5:-Caption +E0x80020 +LastFound +AlwaysOnTop +ToolWindow +Owner
	WinGet, HighlightHwnd
	App.Menu.HighlightHwnd := HighlightHwnd
	Gui, 5:Show, NA
	WinGetPos, X, Y, W, H, ahk_id %TargetHwnd%
	hbm := CreateDIBSection(W, H)
	hdc := DllCall("CreateCompatibleDC", "Ptr", 0)
	obm := DllCall("SelectObject", "Ptr", hdc, "Ptr", hbm)
	DllCall("gdiplus\GdipCreateFromHDC", "Ptr", hdc, "Ptr*", G)

	DllCall("gdiplus\GdipCreateSolidFill", "UInt", 0xffffaa00, "Ptr*", pBrush)
	TH := 5	; толщина линии подсветки
	Points := "0,0|" W ",0|" W "," H "|0," H "|0," TH "|" TH "," 
		. TH "|" TH "," H - TH "|" W - TH "," H - TH "|" W - TH "," TH "|0," TH
	FillPolygon(G, pBrush, Points)
	DllCall("gdiplus\GdipDeleteBrush", "Ptr", pBrush)

	UpdateLayeredWindow(HighlightHwnd, hdc, X, Y, W, H)

	DllCall("SelectObject", "Ptr", hdc, "Ptr", obm)
	DllCall("DeleteObject", "Ptr", hbm), DllCall("DeleteDC", "Ptr", hdc)
	DllCall("gdiplus\GdipDeleteGraphics", "Ptr", G)
}

HidePreviewHighlight() {
	HighlightHwnd := App.Menu.HighlightHwnd
	if (HighlightHwnd && WinExist("ahk_id " HighlightHwnd))
		Gui, 5:Destroy
	App.Menu.HighlightHwnd := ""
}

FindHighlight() {
	static CurrentScr

	if !CurrentScr
		CurrentScr := App.Windows.MaxIndex()
	else {
		while !App.Windows[CurrentScr := CurrentScr = 10 ? App.Windows.MaxIndex() : CurrentScr - 1]
			continue
	}
	Highlight(App.Windows[CurrentScr].Hwnd)
	KeyWait, Space
	HidePreviewHighlight()
}

SaveScreenshot(pBitmap, OutFileName, OutputOverride := "") {
	Output := OutputOverride = "" ? App.Config.Output : OutputOverride
	OutDirectory := App.Config.OutDirectory
	Ext := App.Config.Ext
	JPEGQuality := App.Config.JPEGQuality
	ImgbbSent := false
	MissingImgbbClientID := false

	if (File := InStr(Output, "File")) {
		Critical
		IfNotExist, % OutDirectory
		{
			FileCreateDir, % OutDirectory
			if ErrorLevel {
				File := ""
				MsgBox, 48, Невозможно создать папку
					, Невозможно создать папку вывода.`nОшибка %A_LastError%
			}
		}

		if (Result := SaveBitmap(pBitmap, OutDirectory "\" OutFileName "." Ext, JPEGQuality)) {
			File := ""
			Errors := { 2: "Could not get a list of encoders on system"
						 , 3: "Could not find matching encoder for specified file format"
						 , 4: "Could not save file to disk" }
			MsgBox, 48, Ошибка записи в файл, % Errors[-Result]
		}
	}
	if Clip := InStr(Output, "Clip")
		SetBitmapToClipboard(pBitmap)
	if InStr(Output, "Imgbb") {
		ClientID := EnsureImgbbClientId()
		if (ClientID = "")
			MissingImgbbClientID := true
		else {
			SendImageToImgbb(pBitmap, ClientID)
			ImgbbSent := true
		}
	}
	if (App.Config.Notify) {
		if File && Clip && ImgbbSent
			NotifyText := "Скриншот сохранён в файл,`nпомещён в буфер обмена`nи отправлен на imgbb.com"
		else {
			NotifyText := "Скриншот "
			if File
				NotifyText .= (NotifyText = "Скриншот " ? "" : "`nи ") . "сохранён в файл"
			if Clip
				NotifyText .= (NotifyText = "Скриншот " ? "" : "`nи ") . "помещён в буфер обмена"
			if ImgbbSent
				NotifyText .= (NotifyText = "Скриншот " ? "" : "`nи ") . "отправлен на imgbb.com"
			if MissingImgbbClientID {
				if (NotifyText = "Скриншот ")
					NotifyText .= "не отправлен на Imgbb!`nВпишите ClientID в ScreenCatcher.ini"
				else
					NotifyText .= "`nно не отправлен на Imgbb"
			}
		}
		if (NotifyText = "Скриншот ")
			NotifyText := MissingImgbbClientID
				? "Скриншот не отправлен на Imgbb!`nВпишите ClientID в ScreenCatcher.ini"
				: "Скриншот не сохранён!`nУкажите в настройках во вкладке ""Вывод""`nвариант сохранения скриншота!"
		new ToolTip({ text: NotifyText, FontSize: 14, FontName: "calibri", TrayTip: true
							, TimeOut: -3500, BackColor: 0x0055aa, TextColor: "white"
							, Margin: [10, 5, 10, 5] })
	}
	DllCall("gdiplus\GdipDisposeImage", "Ptr", pBitmap)
}

SendImageToImgbb(pBitmap, apiKey) {
	Ext := App.Config.Ext
	JPEGQuality := App.Config.JPEGQuality
	buff := Ext
	if ( (size := SaveBitmap(pBitmap, buff, JPEGQuality, 1)) < 0 ) {
		Errors := { 2: "Could not get a list of encoders on system"
				  , 3: "Could not find matching encoder for specified file format"
				  , 4: "Не удалось сохранить битмап в буфер" }
		MsgBox, 48, Ошибка записи в буфер, % Errors[-size]
		return
	}
    strBase64 := EncodeDecodeURI(CryptBinaryToStringBASE64(&buff, size, 1))
    http := ComObjCreate("WinHttp.WinHttpRequest.5.1")
    http.open("POST", "https://api.imgbb.com/1/upload", true)
    http.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded")
    body := "key=" . apiKey . "&image=" . strBase64
    http.send(body)
    http.WaitForResponse()
    if (http.status != 200)
       throw "failed to upload image. Status: " . http.status
	responsetext := http.responsetext
    RegExMatch(responsetext, """url"":""(.*?)""", match)
    link := StrReplace(match1, "\")
	Gui, 5: Default
	try Gui, Destroy
	Gui, Font, s9
	Gui, Add, Edit, % "w250 h" . (link ? 24 : 120), % link ? link : responsetext
	Gui, Font
	Gui, Add, Button, x+-80 y+7 w80 h24 gCopyResponse Default, Copy
	GuiControl, Focus, Button1
	Gui, Show,, Imgbb.com
	VarSetCapacity(strBase64, 0), VarSetCapacity(buff, 0)
}

EncodeDecodeURI(str, encode := true, component := true) {
    static document := "", JS := ""
    if !document {
        document := ComObjCreate("htmlfile")
        document.write("<meta http-equiv=""X-UA-Compatible"" content=""IE=9"">")
        JS := document.parentWindow
        (document.documentMode < 9 && JS.execScript())
    }
    return JS[(encode ? "en" : "de") . "codeURI" . (component ? "Component" : "")](str)
}

CopyResponse() {
	GuiControlGet, Clipboard, 5:, Edit1
	Gui, 5: Destroy
}

WM_SHOWWINDOW(wp, lp, msg, hwnd) {
	if IsOverlaySelectionHwnd(hwnd)
		return
	if (A_Gui < 10 || wp || A_Gui = "Imgbb")	; окно показывается — wp = true, скрывается — wp = false
		return

	Gui, %A_Gui%:Destroy
	DllCall("gdiplus\GdipDisposeImage", "Ptr", App.Windows[A_Gui].pBM)
	App.Windows.Remove(A_Gui, "")
}

WM_COMMAND(info, wp, lp) {
	static EN_SETFOCUS := 0x100, EN_KILLFOCUS := 0x200
	
	notify := wp >> 16
	if !(notify = EN_SETFOCUS || notify = EN_KILLFOCUS)
		return
	
	for k, v in info {
		if (v = lp) {
			( notify = EN_SETFOCUS && DllCall("HideCaret", "Ptr", v) )
			SetStaticColor(v, notify = EN_SETFOCUS ? 0xFECC53 : 0xFFFFFF, 0)
		}
	}
}

WM_SETCURSOR(info, wp) {
   static hCursor, OCR_NORMAL := 32512
   ( !hCursor && hCursor := LoadCursor(OCR_NORMAL) )
	for k, v in info {
		if (v = wp)
			return DllCall("SetCursor", "Ptr", hCursor)
	}
}

WM_OVERLAY_SETCURSOR(wParam, lParam, msg, hwnd) {
	static hCross := DllCall("LoadCursor", "Ptr", 0, "Ptr", 32515, "Ptr")
	if !IsOverlaySelectionHwnd(hwnd)
		return
	DllCall("SetCursor", "Ptr", hCross)
	return true
}

IsOverlaySelectionHwnd(hwnd) {
	return hwnd && IsObject(App.Selection.Overlay)
		&& IsObject(App.Selection.Overlay.overlay)
		&& hwnd = App.Selection.Overlay.overlay.hwnd
}

LoadCursor(cursorId) {
   static IMAGE_CURSOR := 2, LR_DEFAULTSIZE := 0x40, LR_SHARED := 0x8000
   return DllCall("LoadImage", "Ptr", 0
                             , "UInt", cursorId
                             , "UInt", IMAGE_CURSOR
                             , "Int", 0, "Int", 0
                             , "UInt", LR_DEFAULTSIZE|LR_SHARED, "Ptr")
}

WM_ESC() {
	Gui, %A_Gui%:Cancel
}

WM_WHEEL(wp, lp, msg, hwnd) {
	static PrevTime = 0
	Critical

	App.Windows[A_Gui].Scale := 1		; значит, размер скриншота изменён

	pBitmap := App.Windows[A_Gui].pBM
	GetImageDimensions(pBitmap, WidthBM, HeightBM)
	RatioBM := WidthBM/HeightBM
	SquareBM := WidthBM*HeightBM

	WinGetPos, X, Y, W, H, A
	DeltaW := Round(W/(!wp ? 10 : 11))
	CtrlState := GetKeyState("Ctrl", "P")
	DeltaW := !wp ? (CtrlState ? 1 : DeltaW) : -(CtrlState ? 1 : DeltaW)
	ResultW := W + DeltaW
	ResultH := Round(ResultW/RatioBM)
	ResultX := X - (CtrlState ? 0 : Round(DeltaW/2))
	ResultY := Y - (CtrlState ? 0 : Round((ResultH - H)/2))
	SquareWin := ResultW*ResultH
	if (!wp && SquareWin > 13000000) || (wp && SquareWin < 1000 && SquareWin < SquareBM) {
		if (A_TickCount - PrevTime > 200) {
			SoundBeep
			PrevTime := A_TickCount
		}
		return
	}

	hbm := CreateDIBSection(ResultW, ResultH)
	hdc := DllCall("CreateCompatibleDC", "Ptr", 0)
	obm := DllCall("SelectObject", "Ptr", hdc, "Ptr", hbm)
	DllCall("gdiplus\GdipCreateFromHDC", "Ptr", hdc, "Ptr*", G)
	DllCall("gdiplus\GdipSetInterpolationMode", "Ptr", G, "Int", App.Config.InterpolationMode-1)
	DrawImage(G, pBitmap, 0, 0, ResultW, ResultH, 0, 0, WidthBM, HeightBM)
	UpdateLayeredWindow(hwnd, hdc, ResultX, ResultY, ResultW, ResultH)

	DllCall("SelectObject", "Ptr", hdc, "Ptr", obm)
	DllCall("DeleteObject", "Ptr", hbm), DllCall("DeleteDC", "Ptr", hdc), DllCall("gdiplus\GdipDeleteGraphics", "Ptr", G)
}

GetImageDimensions(pBitmap, ByRef Width, ByRef Height) {
	DllCall("gdiplus\GdipGetImageWidth", "Ptr", pBitmap, "UInt*", Width)
	DllCall("gdiplus\GdipGetImageHeight", "Ptr", pBitmap, "UInt*", Height)
}

GetVisibleWindowRect(hwnd, ByRef X, ByRef Y, ByRef W, ByRef H) {
	static DWMWA_EXTENDED_FRAME_BOUNDS := 9

	VarSetCapacity(rect, 16, 0)
	if DllCall("dwmapi\DwmGetWindowAttribute", "Ptr", hwnd, "UInt", DWMWA_EXTENDED_FRAME_BOUNDS
													, "Ptr", &rect, "UInt", 16)
		return false

	left   := NumGet(rect,  0, "Int")
	top    := NumGet(rect,  4, "Int")
	right  := NumGet(rect,  8, "Int")
	bottom := NumGet(rect, 12, "Int")
	if (right <= left || bottom <= top)
		return false

	X := left, Y := top, W := right - left, H := bottom - top
	return true
}

FillPolygon(pGraphics, pBrush, Points, FillMode=0) {
	StringSplit, Points, Points, |
	VarSetCapacity(PointF, 8*Points0)   
	Loop, %Points0%
	{
		StringSplit, Coord, Points%A_Index%, `,
		NumPut(Coord1, PointF, 8*(A_Index-1), "float"), NumPut(Coord2, PointF, (8*(A_Index-1))+4, "float")
	}   
	return DllCall("gdiplus\GdipFillPolygon", "Ptr", pGraphics, "Ptr", pBrush, "Ptr", &PointF, "Int", Points0, "Int", FillMode)
}

DrawImage(pGraphics, pBitmap, dx="", dy="", dw="", dh="", sx="", sy="", sw="", sh="") {
	if (sx = "" && sy = "" && sw = "" && sh = "") {
		if (dx = "" && dy = "" && dw = "" && dh = "") {
			sx := dx := 0, sy := dy := 0
			GetImageDimensions(pBitmap, sw, sh)
			dw := sw, dh := sh
		} else {
			sx := sy := 0
			GetImageDimensions(pBitmap, sw, sh)
		}
	}

	E := DllCall("gdiplus\GdipDrawImageRectRect", "Ptr", pGraphics, "Ptr", pBitmap, "Float", dx, "Float", dy, "Float", dw, "Float", dh
															  , "Float", sx, "Float", sy, "Float", sw, "Float", sh, "int", 2, "Ptr", 0, "Ptr", 0, "Ptr", 0)
	return E
}

BitmapFromScreen(Screen, Cursor, Raster="") {
	if (Screen = 0) {
		Sysget, x, 76
		Sysget, y, 77
		Sysget, w, 78
		Sysget, h, 79
	} else {
		StringSplit, S, Screen, |
		x := S1, y := S2, w := S3, h := S4
	}
	chdc := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
	VarSetCapacity(bi, 40, 0)
	NumPut(w, bi, 4, "UInt"), NumPut(h, bi, 8, "UInt"), NumPut(40, bi, 0, "UInt"), NumPut(1, bi, 12, "UShort")
	NumPut(0, bi, 16), NumPut(32, bi, 14, "UShort")
	hbm := DllCall("CreateDIBSection", "Ptr" , chdc, "Ptr" , &bi, "UInt" , 0, "Ptr*", 0, "Ptr" , 0, "UInt" , 0, "Ptr")
	obm := DllCall("SelectObject", "Ptr", chdc, "Ptr", hbm, "Ptr")
	hhdc := DllCall("GetDC", "Ptr", 0, "Ptr")
	DllCall("BitBlt", "Ptr", chdc, "Int", 0, "Int", 0, "Int", w, "Int", h, "Ptr", hhdc, "Int", x, "Int", y, "UInt", Raster)
	DllCall("ReleaseDC", "Ptr", 0, "Ptr", hhdc)
	( Cursor && CaptureCursor(chdc, x, y) )
	DllCall("gdiplus\GdipCreateBitmapFromHBITMAP", "Ptr", hbm, "Ptr", 0, "Ptr*", pBitmap)
	DllCall("SelectObject", "Ptr", chdc, "Ptr", obm, "Ptr")
	DllCall("DeleteObject", "Ptr", hbm)
	DllCall("DeleteDC", "Ptr", hhdc)
	DllCall("DeleteDC", "Ptr", chdc)
	return pBitmap
}

CaptureCursor(hDC, x, y) {
	VarSetCapacity(CURSORINFO, szCI := 4*2 + A_PtrSize + 8, 0)
	NumPut(szCI, CURSORINFO)
	DllCall("GetCursorInfo", "Ptr", &CURSORINFO)
	bShow   := NumGet(CURSORINFO, 4, "UInt")
	hCursor := NumGet(CURSORINFO, 4*2)
	xCursor := NumGet(CURSORINFO, 4*2 + A_PtrSize, "Int")
	yCursor := NumGet(CURSORINFO, 4*2 + A_PtrSize + 4, "Int")

	if bShow && hCursor := DllCall("CopyIcon", "Ptr", hCursor, "Ptr") {
		VarSetCapacity(ICONINFO, 4*2 + A_PtrSize*3, 0)
		DllCall("GetIconInfo", "Ptr", hCursor, "Ptr", &ICONINFO)
		bIcon    := NumGet(ICONINFO, "UInt")
		xHotspot := NumGet(ICONINFO, 4, "UInt")
		yHotspot := NumGet(ICONINFO, 4*2, "UInt")
		hBMMask  := NumGet(ICONINFO, 4*2 + A_PtrSize)
		hBMColor := NumGet(ICONINFO, 4*2 + A_PtrSize*2)

		DllCall("DrawIcon", "Ptr", hDC, "Int", xCursor - xHotspot - x, "Int", yCursor - yHotspot - y, "Ptr", hCursor)
		DllCall("DestroyIcon", "Ptr", hCursor)
		( hBMMask && DllCall("DeleteObject", "Ptr", hBMMask) )
		( hBMColor && DllCall("DeleteObject", "Ptr", hBMColor) )
	}
}

ScreenShotMenuHandling() {
	CurrentGui := App.Menu.CurrentPreviewGui
	pBitmap := App.Windows[CurrentGui].pBM
	Time := App.Windows[CurrentGui].Time

	if (A_ThisMenuItem = "Сохранить" || A_ThisMenuItem = "Сохранить в первоначальном размере")
		SaveScreenshot(pBitmap, Time)
	else if (A_ThisMenuItem = "Сохранить в текущем размере")
		SavePreviewCurrentSize(CurrentGui, pBitmap, Time)
	Gui, %CurrentGui%:Cancel
}

PreviewSaveToMenuHandling() {
	CurrentGui := App.Menu.CurrentPreviewGui
	pBitmap := App.Windows[CurrentGui].pBM
	Time := App.Windows[CurrentGui].Time
	Output := PreviewSaveToOutput(A_ThisMenuItem)

	if (Output != "")
		SaveScreenshot(pBitmap, Time, Output)
	Gui, %CurrentGui%:Cancel
}

PreviewSaveCurrentToMenuHandling() {
	CurrentGui := App.Menu.CurrentPreviewGui
	pBitmap := App.Windows[CurrentGui].pBM
	Time := App.Windows[CurrentGui].Time
	Output := PreviewSaveToOutput(A_ThisMenuItem)

	if (Output != "")
		SavePreviewCurrentSize(CurrentGui, pBitmap, Time, Output)
	Gui, %CurrentGui%:Cancel
}

SavePreviewCurrentSize(CurrentGui, pBitmap, Time, OutputOverride := "") {
	WinGetPos,,, W, H, % "ahk_id " App.Windows[CurrentGui].Hwnd
	DllCall("gdiplus\GdipCreateBitmapFromScan0", "Int", W, "Int", H, "Int", 0, "Int", 0x26200A, "Ptr", 0, "Ptr*", pBM)
	DllCall("gdiplus\GdipGetImageGraphicsContext", "Ptr", pBM, "Ptr*", G)
	DllCall("gdiplus\GdipSetInterpolationMode", "Ptr", G, "Int", App.Config.InterpolationMode-1)

	GetImageDimensions(pBitmap, WidthBM, HeightBM)
	DrawImage(G, pBitmap, -Round(W/400), -Round(H/400)
		, Ceil(W*1.005), Ceil(H*1.005), 0, 0, WidthBM, HeightBM)

	DllCall("gdiplus\GdipDeleteGraphics", "Ptr", G)
	SaveScreenshot(pBM, Time, OutputOverride)
}

PreviewSaveToOutput(MenuItem) {
	if (MenuItem = "файл")
		return "File"
	if (MenuItem = "буфер обмена")
		return "Clip"
	if (MenuItem = "imgbb.com")
		return "Imgbb"
	return ""
}

SaveBitmap(pBitmap, ByRef info, Quality := 75, tobuff := "") {
	if tobuff
		Extension := info
	else
		SplitPath, info,,, Extension
   if Extension not in BMP,DIB,RLE,JPG,JPEG,JPE,JFIF,GIF,TIF,TIFF,PNG
      return -1

   DllCall("gdiplus\GdipGetImageEncodersSize", "UInt*", nCount, "UInt*", nSize)
   VarSetCapacity(ci, nSize)
   DllCall("gdiplus\GdipGetImageEncoders", "UInt", nCount, "UInt", nSize, "Ptr", &ci)
   if !(nCount && nSize)
      return -2
   
	Loop, % nCount {
		sString := StrGet(NumGet(ci, (idx := (48+7*A_PtrSize)*(A_Index-1))+32+3*A_PtrSize), "UTF-16")
		if !InStr(sString, "*." Extension)
			continue
		
		pCodec := &ci+idx
		break
	}
   
   if !pCodec
      return -3
	
	if RegExMatch(Extension, "i)^J(PG|PEG|PE|FIF)$") && Quality != 75 {
		DllCall("gdiplus\GdipGetEncoderParameterListSize", "Ptr", pBitmap, "Ptr", pCodec, "UInt*", nSize)
		VarSetCapacity(EncoderParameters, nSize, 0)
		DllCall("gdiplus\GdipGetEncoderParameterList", "Ptr", pBitmap, "Ptr", pCodec, "UInt", nSize, "Ptr", &EncoderParameters)
		Loop, % NumGet(EncoderParameters, "UInt") {
			elem := (24+A_PtrSize)*(A_Index-1) + 4 + (pad := A_PtrSize = 8 ? 4 : 0)
			if (NumGet(EncoderParameters, elem+16, "UInt") = 1) && (NumGet(EncoderParameters, elem+20, "UInt") = 6) {
				p := elem+&EncoderParameters-pad-4
				NumPut(Quality, NumGet(NumPut(4, NumPut(1, p+0)+20, "UInt")), "UInt")
				break
			}
		}      
	}
	if !tobuff {
		if A_IsUnicode
			pOutput := &info
		else {
			VarSetCapacity(wOutput, StrPut(info, "UTF-16")*2, 0)
			StrPut(info, &wOutput, "UTF-16")
			pOutput := &wOutput
		}
		E := DllCall("gdiplus\GdipSaveImageToFile", "Ptr", pBitmap, "Ptr", pOutput, "Ptr", pCodec, "UInt", p ? p : 0)
	}
	else {
		DllCall( "ole32\CreateStreamOnHGlobal", "UInt", 0, "Int", 1, "Ptr*", pStream )
		if E := DllCall( "gdiplus\GdipSaveImageToStream", "Ptr", pBitmap, "Ptr", pStream, "Ptr", pCodec, "UInt", p ? p : 0 )
			DllCall( NumGet( NumGet( 1*pStream ) + A_PtrSize*2 ), "Ptr", pStream )
		else {
			DllCall( "ole32\GetHGlobalFromStream", "Ptr", pStream, "Ptr*", hData )
			pData := DllCall( "GlobalLock", "Ptr", hData )
			nSize := DllCall( "GlobalSize", "Ptr", hData )
			VarSetCapacity( info, 0), VarSetCapacity( info, nSize, 0 )
			DllCall( "RtlMoveMemory", "Ptr", &info, "Ptr", pData, "UInt", nSize )
			DllCall( "GlobalUnlock", "Ptr", hData )
			DllCall( NumGet( NumGet( 1*pStream ) + A_PtrSize*2 ), "Ptr", pStream )
			DllCall( "GlobalFree", "Ptr", hData )
		}
	}
   return E ? -4 : tobuff ? nSize : 0
}

SetBitmapToClipboard(pBitmap) {
	off1 := A_PtrSize = 8 ? 52 : 44, off2 := A_PtrSize = 8 ? 32 : 24
	DllCall("gdiplus\GdipCreateHBITMAPFromBitmap", "Ptr", pBitmap, "Ptr*", hBitmap, "Int", 0xFFFFFFFF)
	DllCall("GetObject", "Ptr", hBitmap, "int", VarSetCapacity(oi, A_PtrSize = 8 ? 104 : 84, 0), "Ptr", &oi)
	hdib := DllCall("GlobalAlloc", "UInt", 2, "Ptr", 40+NumGet(oi, off1, "UInt"), "Ptr")
	pdib := DllCall("GlobalLock", "Ptr", hdib, "Ptr")
	DllCall("RtlMoveMemory", "Ptr", pdib, "Ptr", &oi+off2, "Ptr", 40)
	DllCall("RtlMoveMemory", "Ptr", pdib+40, "Ptr", NumGet(oi, off2 - A_PtrSize, "Ptr"), "Ptr", NumGet(oi, off1, "UInt"))
	DllCall("GlobalUnlock", "Ptr", hdib)
	DllCall("DeleteObject", "Ptr", hBitmap)
	DllCall("OpenClipboard", "Ptr", 0)
	DllCall("EmptyClipboard")
	DllCall("SetClipboardData", "UInt", 8, "Ptr", hdib)
	DllCall("CloseClipboard")
}

CreateDIBSection(w, h, hdc="", bpp=32, ByRef ppvBits=0) {
	hdc2 := hdc ? hdc : DllCall("GetDC", "Ptr", 0)
	VarSetCapacity(bi, 40, 0)
	
	NumPut(w, bi, 4, "uint")
	NumPut(h, bi, 8, "uint")
	NumPut(40, bi, 0, "uint")
	NumPut(1, bi, 12, "ushort")
	NumPut(0, bi, 16, "uInt")
	NumPut(bpp, bi, 14, "ushort")
	
	hbm := DllCall("CreateDIBSection", "Ptr", hdc2, "Ptr", &bi, "UInt", 0, "Ptr*", ppvBits, "Ptr", 0, "UInt", 0, "Ptr")

	( !hdc && DllCall("ReleaseDC", "Ptr", 0, "Ptr", hdc2) )
	return hbm
}

UpdateLayeredWindow(hwnd, hdc, x="", y="", w="", h="", Alpha=255) {
	if ((x != "") && (y != ""))
		VarSetCapacity(pt, 8), NumPut(x, pt, 0, "UInt"), NumPut(y, pt, 4, "UInt")

	if (w = "") ||(h = "")
		WinGetPos,,, w, h, ahk_id %hwnd%
   
	return DllCall( "UpdateLayeredWindow", "Ptr", hwnd, "Ptr", 0, "Ptr", ((x = "") && (y = "")) ? 0 : &pt, "Int64*", w|h<<32
													, "Ptr", hdc, "Int64*", 0, "UInt", 0, "UInt*", Alpha<<16|1<<24, "UInt", 2 )
}

GdipStartup() {
	if !DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
		DllCall("LoadLibrary", "Str", "gdiplus")
	VarSetCapacity(si, A_PtrSize = 8 ? 24 : 16, 0), si := Chr(1)
	DllCall("gdiplus\GdiplusStartup", "Ptr*", pToken, "Ptr", &si, "Ptr", 0)
	return pToken
}

GdipShutdown(pToken) {
	DllCall("gdiplus\GdiplusShutdown", "Ptr", pToken)
	if hModule := DllCall("GetModuleHandle", "Str", "gdiplus", "Ptr")
		DllCall("FreeLibrary", "Ptr", hModule)
	return 0
}

SetStaticColor(hStatic, b_color, f_color := 0) {
	static arr := [], GWL_WNDPROC := -4
	b_color := DllCall("Ws2_32\ntohl", "UInt", b_color << 8, "UInt")
	f_color := DllCall("Ws2_32\ntohl", "UInt", f_color << 8, "UInt")
	hGui := DllCall("GetParent", "Ptr", hStatic, "Ptr")
	if !arr.HasKey(hGui) {
		arr[hGui] := {}, arr[hGui].Statics := []
		arr[hGui].ProcOld := DllCall("SetWindowLong" . (A_PtrSize = 8 ? "Ptr" : ""), "Ptr", hGui, "Int", GWL_WNDPROC
														, "Ptr", RegisterCallback("WindowProc", "", 4, Object(arr[hGui])), "Ptr")
	}
	else if arr[hGui].Statics.HasKey(hStatic)
		DllCall("DeleteObject", "Ptr", arr[hGui].Statics[hStatic].hBrush)
	arr[hGui].Statics[hStatic] := { b_color: b_color, f_color: f_color
								  , hBrush: DllCall("CreateSolidBrush", "UInt", b_color, "Ptr") }
	WinSet, Redraw,, ahk_id %hStatic%
}

WindowProc(hwnd, uMsg, wParam, lParam) {
	Critical
	static WM_CTLCOLORSTATIC := 0x138
	obj := Object(A_EventInfo)
	if (uMsg = WM_CTLCOLORSTATIC && k := obj.Statics[lParam]) {
		DllCall("SetBkColor", "Ptr", wParam, "UInt", k.b_color)
		DllCall("SetTextColor", "Ptr", wParam, "UInt", k.f_color)
		return k.hBrush
	}
	return DllCall("CallWindowProc", "Ptr", obj.ProcOld, "Ptr", hwnd, "UInt", uMsg, "Ptr", wParam, "Ptr", lParam)
}

CryptBinaryToStringBASE64(pData, Bytes, NOCRLF = "") {
    static CRYPT_STRING_BASE64 := 1, CRYPT_STRING_NOCRLF := 0x40000000
    CRYPT := CRYPT_STRING_BASE64 | (NOCRLF ? CRYPT_STRING_NOCRLF : 0)
    
    DllCall("Crypt32\CryptBinaryToString", "Ptr", pData, "UInt", Bytes, "UInt", CRYPT, "Ptr", 0, "UInt*", Chars)
    VarSetCapacity(OutData, Chars * (A_IsUnicode ? 2 : 1))
    DllCall("Crypt32\CryptBinaryToString", "Ptr", pData, "UInt", Bytes, "UInt", CRYPT, "Str", OutData, "UInt*", Chars)
    return OutData
}

CreateIconFromBase64(StringBASE64, Size) {
    DllCall("Crypt32.dll\CryptStringToBinary", "Ptr", &StringBase64
       , "UInt", StrLen(StringBase64), "UInt", CRYPT_STRING_BASE64 := 1, "UInt", 0, "UInt*", Bytes, "UInt*", 0, "UInt*", 0)
 
    VarSetCapacity(IconData, Bytes) 
    DllCall("Crypt32.dll\CryptStringToBinary", "Ptr", &StringBase64 
       , "UInt", StrLen(StringBase64), "UInt", CRYPT_STRING_BASE64, "Str", IconData, "UInt*", Bytes, "UInt*", 0, "UInt*", 0)
       
    return DllCall("CreateIconFromResourceEx", "Ptr", &IconData + 4
       , "UInt", NumGet(&IconData, "UInt"), "UInt", true, "UInt", 0x30000, "Int", Size, "Int", Size, "UInt", 0, "Ptr")
}

GetBase64String(Size) {
	Icon16 = 
	(RTrim Join
	aAQAACgAAAAQAAAAIAAAAAEAIAAAAAAAQAQAAAAAAAAAAAAAAAAAAAAAAAD///+3
	////////////////////////////////////////////////////////////////
	//////////////+3/////8H2/v865Pz/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/zLi/P+u9P7///////////8w4vz/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/H+D8////////////
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P///////////xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z///////////8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8////////////
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P///////////xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z///////////8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8////////////
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P///////////xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z///////////8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8////////////
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P///////////zDi/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8f4Pz////////////G9/7/Sub9/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P9A5Pz/tPT+//////////+3
	////////////////////////////////////////////////////////////////
	//////////////+3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
	)
	
	Icon32 = 
	(RTrim Join
	qBAAACgAAAAgAAAAQAAAAAEAIAAAAAAAgBAAAAAAAAAAAAAAAAAAAAAAAAC9vr5avb6+5L2+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/
	vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7kvb6+Wr2+vvO9vr7/vb6+/72+vv+9vr7/
	vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/
	vb6+/72+vv+9vr7zvb6+/72+vv+LyNH/Ldv1/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/y3b9f+LyNH/vb6+/72+vv+9vr7/vb6+/ybd+P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/ybd+P+9vr7/vb6+/72+vv+zwML/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/7PAwv+9vr7/vb6+/7PAwv8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/s8DC/72+vv+9vr7/s8DC/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P+zwML/vb6+/72+vv+zwML/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/7PAwv+9vr7/vb6+/7PAwv8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/s8DC/72+vv+9vr7/s8DC/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P+zwML/vb6+/72+vv+zwML/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/7PAwv+9vr7/vb6+/7PAwv8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/s8DC/72+vv+9vr7/s8DC/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P+zwML/vb6+/72+vv+zwML/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/7PAwv+9vr7/vb6+/7PAwv8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/s8DC/72+vv+9vr7/s8DC/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P+zwML/vb6+/72+vv+zwML/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/7PAwv+9vr7/vb6+/7PAwv8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/s8DC/72+vv+9vr7/s8DC/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P+zwML/vb6+/72+vv+zwML/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/7PAwv+9vr7/vb6+/7PAwv8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/s8DC/72+vv+9vr7/s8DC/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P+zwML/vb6+/72+vv+zwML/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/7PAwv+9vr7/vb6+/7PAwv8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/s8DC/72+vv+9vr7/s8DC/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P+zwML/vb6+/72+vv+zwML/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/7PAwv+9vr7/vb6+/7PAwv8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/s8DC/72+vv+9vr7/s8DC/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P+zwML/vb6+/72+vv+9vr7/Jt34/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Jt34/72+vv+9vr7/vb6+/72+vv+NyND/O9jw/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/
	Gt/8/xrf/P8a3/z/Gt/8/xrf/P8a3/z/Gt/8/zvY8P+NyND/vb6+/72+vv+9vr72vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/
	vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+9r2+vmC9vr7wvb6+/72+vv+9vr7/
	vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/vb6+/72+vv+9vr7/
	vb6+/72+vvC9vr5gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
	AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
	)
	return Icon%Size%
}

; Full-screen mouse selection overlay.
class OverlaySelection {
    static WM_MOUSEMOVE := 0x0200

    __New(thickness := 2, color := 0x00FF00, labelSpecs := "", armedStyle := "") {
        this.scale := A_ScreenDPI / 96
        this.overlay := new SelectionOverlayFrame(thickness, color, this.scale)
        this.state := "idle"
        this.busy := false
        this.startX := 0
        this.startY := 0
        this.curX := 0
        this.curY := 0
        this.callback := ""
        this.callbackTimer := ""
        this.disposed := false
        this.onMove := ObjBindMethod(this, "OnMouseMove")
        this.labels := []
        this.providers := []

        specs := IsObject(labelSpecs) ? labelSpecs : OverlaySelection.DefaultSpecs()
        for index, spec in specs {
            style := spec.HasKey("style") ? spec.style : {}
            this.labels.Push(new SelectionOverlayLabel(style, this.scale))
            this.providers.Push(spec.provider)
        }

        if !IsObject(armedStyle)
            armedStyle := {}
        this.armedLabel := new SelectionOverlayLabel(armedStyle, this.scale)
        this.armedOffsetX := armedStyle.HasKey("offsetX") ? armedStyle.offsetX : 12
        this.armedOffsetY := armedStyle.HasKey("offsetY") ? armedStyle.offsetY : 12
    }

    DefaultSpecs() {
        gap := 5
        return [ {style: {}, provider: Func("OverlayTopLabelProvider").Bind(gap)}
               , {style: {}, provider: Func("OverlayBottomLabelProvider").Bind(gap)} ]
    }

    Arm(callback, showArmedLabel := true) {
        if (this.state != "idle" || this.busy)
            return

        this.callback := callback
        this.overlay.ClearFrame()
        this.overlay.Show()
        this.state := "armed"
        OnMessage(this.WM_MOUSEMOVE, this.onMove)
        MouseGetPos, mx, my
        this.curX := mx
        this.curY := my
        if showArmedLabel
            this.UpdateArmed()
    }

    BeginDrag() {
        if (this.state != "armed")
            return

        MouseGetPos, mx, my
        this.startX := mx
        this.startY := my
        this.curX := mx
        this.curY := my
        this.overlay.ClearFrame()
        this.state := "dragging"
        this.Update()
    }

    Quick(callback) {
        if (this.state != "idle" || this.busy)
            return

        this.Arm(callback, false)
        this.BeginDrag()
        if !GetKeyState("LButton", "P")
            this.Finish()
    }

    Cancel() {
        if (this.state != "armed" && this.state != "dragging")
            return

        OnMessage(this.WM_MOUSEMOVE, this.onMove, 0)
        this.overlay.Hide()
        this.state := "idle"
    }

    OnMouseMove(wParam, lParam, msg, hwnd) {
        if (hwnd != this.overlay.hwnd)
            return

        pt := this.ClientPoint(lParam)
        this.curX := pt.x
        this.curY := pt.y
        if (this.state = "dragging")
            this.Update()
        else if (this.state = "armed")
            this.UpdateArmed()
    }

    ClientPoint(lParam) {
        x := lParam & 0xFFFF
        y := (lParam >> 16) & 0xFFFF
        if (x > 0x7FFF)
            x -= 0x10000
        if (y > 0x7FFF)
            y -= 0x10000

        VarSetCapacity(pt, 8, 0)
        NumPut(x, pt, 0, "Int")
        NumPut(y, pt, 4, "Int")
        DllCall("ClientToScreen", "Ptr", this.overlay.hwnd, "Ptr", &pt)
        return {x: NumGet(pt, 0, "Int"), y: NumGet(pt, 4, "Int")}
    }

    Rect() {
        x := Min(this.startX, this.curX)
        y := Min(this.startY, this.curY)
        w := Abs(this.curX - this.startX)
        h := Abs(this.curY - this.startY)
        return {x: x, y: y, w: w, h: h}
    }

    Update() {
        r := this.Rect()
        ctx := {r: r, t: this.overlay.PhysT(), scale: this.scale}
        for index, provider in this.providers {
            spec := provider.Call(ctx)
            this.labels[index].SetText(spec.text, spec.ax, spec.ay, spec.side)
        }
        this.overlay.Render(r, this.labels)
    }

    UpdateArmed() {
        x := this.curX
        y := this.curY
        anchorX := x - Round(this.armedOffsetX * this.scale)
        anchorY := y - Round(this.armedOffsetY * this.scale)
        this.armedLabel.SetText(x . ", " . y, anchorX, anchorY, "topLeft")
        this.overlay.RenderLabels([this.armedLabel])
    }

    Finish() {
        if (this.state != "dragging")
            return

        this.state := "idle"
        OnMessage(this.WM_MOUSEMOVE, this.onMove, 0)
        this.overlay.Hide()
        callback := this.callback
        if IsObject(callback) {
            this.busy := true
            r := this.Rect()
            this.callbackTimer := ObjBindMethod(this, "InvokeCallback", callback, r)
            timer := this.callbackTimer
            SetTimer, % timer, -1
        }
    }

    InvokeCallback(callback, r) {
        try callback.Call(r)
        finally {
            this.busy := false
            this.callbackTimer := ""
        }
    }

    Dispose() {
        if (this.disposed)
            return
        this.disposed := true

        if (this.state = "armed" || this.state = "dragging")
            OnMessage(this.WM_MOUSEMOVE, this.onMove, 0)
        if IsObject(this.callbackTimer) {
            timer := this.callbackTimer
            SetTimer, % timer, Delete
            this.callbackTimer := ""
        }
        for index, label in this.labels
            label.Dispose()
        if IsObject(this.armedLabel)
            this.armedLabel.Dispose()
        if IsObject(this.overlay)
            this.overlay.Dispose()
        this.state := "idle"
    }

    __Delete() {
        this.Dispose()
    }
}

class SelectionOverlayFrame {
    __New(thickness, color, scale := 1) {
        this.t := thickness
        this.scale := scale
        this.color := 0xFF000000 | color
        this.w := A_ScreenWidth
        this.h := A_ScreenHeight
        this.prev := ""
        this.disposed := false
		this.guiName := "SelectionOverlay"

		Gui, % this.guiName . ":New", +AlwaysOnTop -Caption +ToolWindow -DPIScale +E0x80000 +hwndOverlayHwnd
        this.hwnd := OverlayHwnd
		this.hArrow := DllCall("LoadCursor", "Ptr", 0, "Ptr", 32512, "Ptr")
        this.CreateBuffer()
    }

    CreateBuffer() {
        this.hdcMem := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")

        VarSetCapacity(bi, 40, 0)
        NumPut(40, bi, 0, "UInt")
        NumPut(this.w, bi, 4, "Int")
        NumPut(-this.h, bi, 8, "Int")
        NumPut(1, bi, 12, "UShort")
        NumPut(32, bi, 14, "UShort")
        NumPut(0, bi, 16, "UInt")

        this.hbm := DllCall("CreateDIBSection", "Ptr", this.hdcMem, "Ptr", &bi
            , "UInt", 0, "Ptr*", pBits, "Ptr", 0, "UInt", 0, "Ptr")
        this.pBits := pBits
        this.oldBm := DllCall("SelectObject", "Ptr", this.hdcMem, "Ptr", this.hbm, "Ptr")

        DllCall("gdiplus\GdipCreateBitmapFromScan0", "Int", this.w, "Int", this.h
            , "Int", this.w * 4, "Int", 0xE200B, "Ptr", this.pBits, "Ptr*", pBitmap)
        this.pBitmap := pBitmap
        DllCall("gdiplus\GdipGetImageGraphicsContext", "Ptr", this.pBitmap, "Ptr*", pGraphics)
        this.pGraphics := pGraphics
        DllCall("gdiplus\GdipSetCompositingMode", "Ptr", this.pGraphics, "Int", 1)
        DllCall("gdiplus\GdipSetTextRenderingHint", "Ptr", this.pGraphics, "Int", 4)
        DllCall("gdiplus\GdipSetSmoothingMode", "Ptr", this.pGraphics, "Int", 3)

        DllCall("gdiplus\GdipCreateSolidFill", "UInt", this.color, "Ptr*", pBrush)
        this.pBrush := pBrush
        DllCall("gdiplus\GdipCreateSolidFill", "UInt", 0x01000000, "Ptr*", pErase)
        this.pErase := pErase
        DllCall("gdiplus\GdipGraphicsClear", "Ptr", this.pGraphics, "UInt", 0x01000000)
    }

    Show() {
        options := "NA x0 y0 w" . this.w . " h" . this.h
		Gui, % this.guiName . ":Show", % options
    }

    Hide() {
        if (this.disposed)
            return
        DllCall("gdiplus\GdipGraphicsClear", "Ptr", this.pGraphics, "UInt", 0x01000000)
        this.prev := ""
        this.Flush()
		Gui, % this.guiName . ":Hide"
		DllCall("SetCursor", "Ptr", this.hArrow)
    }

    ClearFrame() {
        DllCall("gdiplus\GdipGraphicsClear", "Ptr", this.pGraphics, "UInt", 0x01000000)
        this.prev := ""
        this.Flush()
    }

    SetRect(x, y, w, h) {
        cur := {x: x, y: y, w: w, h: h}
        if IsObject(this.prev)
            this.Strips(this.prev, this.pErase)
        this.Strips(cur, this.pBrush)
        this.prev := cur
        this.Flush()
    }

    Render(rect, labels) {
        for index, label in labels
            label.Erase(this.pGraphics, this.pErase)
        if IsObject(this.prev)
            this.Strips(this.prev, this.pErase)

        cur := {x: rect.x, y: rect.y, w: rect.w, h: rect.h}
        this.Strips(cur, this.pBrush)
        this.prev := cur
        for index, label in labels
            label.Draw(this.pGraphics, this.w, this.h)

        this.Flush()
        for index, label in labels
            label.Commit()
    }

    RenderLabels(labels) {
        for index, label in labels
            label.Erase(this.pGraphics, this.pErase)
        for index, label in labels
            label.Draw(this.pGraphics, this.w, this.h)

        this.Flush()
        for index, label in labels
            label.Commit()
    }

    PhysT() {
        return Max(1, Round(this.t * this.scale))
    }

    Strips(r, brush) {
        t := this.PhysT()
        this.Fill(brush, r.x - t, r.y - t, r.w + 2 * t, t)
        this.Fill(brush, r.x - t, r.y + r.h, r.w + 2 * t, t)
        this.Fill(brush, r.x - t, r.y, t, r.h)
        this.Fill(brush, r.x + r.w, r.y, t, r.h)
    }

    Fill(brush, x, y, w, h) {
        if (w < 1 || h < 1)
            return
        DllCall("gdiplus\GdipFillRectangle", "Ptr", this.pGraphics
            , "Ptr", brush, "Float", x, "Float", y, "Float", w, "Float", h)
    }

    Flush() {
        VarSetCapacity(pt, 8, 0)
        VarSetCapacity(sz, 8, 0)
        NumPut(this.w, sz, 0, "Int")
        NumPut(this.h, sz, 4, "Int")
        VarSetCapacity(src, 8, 0)
        VarSetCapacity(blend, 4, 0)
        NumPut(255, blend, 2, "UChar")
        NumPut(1, blend, 3, "UChar")

        DllCall("UpdateLayeredWindow", "Ptr", this.hwnd, "Ptr", 0
            , "Ptr", &pt, "Ptr", &sz, "Ptr", this.hdcMem, "Ptr", &src
            , "UInt", 0, "Ptr", &blend, "UInt", 2)
    }

    Dispose() {
        if (this.disposed)
            return
        this.disposed := true

        if (this.pBrush)
            DllCall("gdiplus\GdipDeleteBrush", "Ptr", this.pBrush)
        if (this.pErase)
            DllCall("gdiplus\GdipDeleteBrush", "Ptr", this.pErase)
        if (this.pGraphics)
            DllCall("gdiplus\GdipDeleteGraphics", "Ptr", this.pGraphics)
        if (this.pBitmap)
            DllCall("gdiplus\GdipDisposeImage", "Ptr", this.pBitmap)
        if (this.hbm) {
            DllCall("SelectObject", "Ptr", this.hdcMem, "Ptr", this.oldBm)
            DllCall("DeleteObject", "Ptr", this.hbm)
        }
        if (this.hdcMem)
            DllCall("DeleteDC", "Ptr", this.hdcMem)
		if (this.hwnd && DllCall("IsWindow", "Ptr", this.hwnd))
			Gui, % this.guiName . ":Destroy"

        this.pBrush := 0
        this.pErase := 0
        this.pGraphics := 0
        this.pBitmap := 0
        this.hbm := 0
        this.hdcMem := 0
		this.hArrow := 0
        this.hwnd := 0
    }

    __Delete() {
        this.Dispose()
    }
}

class SelectionOverlayLabel {
    __New(style := "", scale := 1) {
        if !IsObject(style)
            style := {}

        fontName := this.StyleValue(style, "fontName", "Segoe UI")
        fontSize := this.StyleValue(style, "fontSize", 14) * scale
        fontStyle := this.StyleValue(style, "fontStyle", 0)
        textColor := this.StyleValue(style, "textColor", 0xFFFFFF)
        backColor := this.StyleValue(style, "backColor", 0x202020)
        this.padX := Round(this.StyleValue(style, "padX", 6) * scale)
        this.padY := Round(this.StyleValue(style, "padY", 3) * scale)
        this.radius := Round(this.StyleValue(style, "radius", 6) * scale)
        this.text := ""
        this.box := ""
        this.eraseBox := ""
        this.prevErase := ""
        this.disposed := false

        DllCall("gdiplus\GdipCreateFontFamilyFromName", "WStr", fontName
            , "Ptr", 0, "Ptr*", family)
        this.family := family
        DllCall("gdiplus\GdipCreateFont", "Ptr", this.family, "Float", fontSize
            , "Int", fontStyle, "Int", 2, "Ptr*", font)
        this.font := font

        DllCall("gdiplus\GdipCreateStringFormat", "Int", 0, "Int", 0, "Ptr*", fmt)
        this.fmt := fmt
        DllCall("gdiplus\GdipSetStringFormatAlign", "Ptr", this.fmt, "Int", 0)
        DllCall("gdiplus\GdipSetStringFormatLineAlign", "Ptr", this.fmt, "Int", 1)

        DllCall("gdiplus\GdipCreateSolidFill", "UInt", 0xFF000000 | textColor, "Ptr*", textBrush)
        this.textBrush := textBrush
        DllCall("gdiplus\GdipCreateSolidFill", "UInt", 0xFF000000 | backColor, "Ptr*", backBrush)
        this.backBrush := backBrush
    }

    StyleValue(style, key, defaultValue) {
        return style.HasKey(key) ? style[key] : defaultValue
    }

    SetText(text, anchorX, anchorY, side := "top") {
        this.text := text
        this.ax := anchorX
        this.ay := anchorY
        this.side := side
    }

    Measure(pGraphics) {
        VarSetCapacity(layout, 16, 0)
        NumPut(0, layout, 0, "Float")
        NumPut(0, layout, 4, "Float")
        NumPut(9999, layout, 8, "Float")
        NumPut(9999, layout, 12, "Float")
        VarSetCapacity(bounds, 16, 0)
        DllCall("gdiplus\GdipMeasureString", "Ptr", pGraphics, "WStr", this.text
            , "Int", -1, "Ptr", this.font, "Ptr", &layout, "Ptr", this.fmt
            , "Ptr", &bounds, "Int*", chars, "Int*", lines)
        return {w: NumGet(bounds, 8, "Float"), h: NumGet(bounds, 12, "Float")}
    }

    ComputeBox(pGraphics, screenW, screenH) {
        metrics := this.Measure(pGraphics)
        bw := Ceil(metrics.w) + this.padX * 2
        bh := Ceil(metrics.h) + this.padY * 2

        if (this.side = "top") {
            bx := this.ax
            by := this.ay - bh - 1
        } else if (this.side = "bottom") {
            bx := this.ax - bw
            by := this.ay
        } else if (this.side = "left") {
            bx := this.ax - bw
            by := this.ay - bh // 2
        } else if (this.side = "right") {
            bx := this.ax
            by := this.ay - bh // 2
        } else if (this.side = "topLeft") {
            bx := this.ax - bw
            by := this.ay - bh - 1
        } else {
            bx := this.ax - bw // 2
            by := this.ay - bh // 2
        }

        bx := Max(0, Min(bx, screenW - bw))
        by := Max(0, Min(by, screenH - bh))
        return {x: bx, y: by, w: bw, h: bh}
    }

    Draw(pGraphics, screenW, screenH) {
        if (this.text = "") {
            this.box := ""
            this.eraseBox := ""
            return
        }

        box := this.ComputeBox(pGraphics, screenW, screenH)
        this.box := box
        ex := Max(0, box.x - 1)
        ey := Max(0, box.y - 1)
        er := Min(screenW, box.x + box.w + 1)
        eb := Min(screenH, box.y + box.h + 1)
        this.eraseBox := {x: ex, y: ey, w: er - ex, h: eb - ey}

        DllCall("gdiplus\GdipSetCompositingMode", "Ptr", pGraphics, "Int", 0)
        this.FillRoundRect(pGraphics, box.x, box.y, box.w, box.h, this.radius, this.backBrush)

        VarSetCapacity(rect, 16, 0)
        NumPut(box.x + this.padX, rect, 0, "Float")
        NumPut(box.y, rect, 4, "Float")
        NumPut(box.w, rect, 8, "Float")
        NumPut(box.h, rect, 12, "Float")
        DllCall("gdiplus\GdipDrawString", "Ptr", pGraphics, "WStr", this.text
            , "Int", -1, "Ptr", this.font, "Ptr", &rect, "Ptr", this.fmt
            , "Ptr", this.textBrush)
        DllCall("gdiplus\GdipSetCompositingMode", "Ptr", pGraphics, "Int", 1)
    }

    FillRoundRect(pGraphics, x, y, w, h, radius, brush) {
        radius := Min(radius, Min(w // 2, h // 2))
        diameter := radius * 2
        DllCall("gdiplus\GdipCreatePath", "Int", 0, "Ptr*", path)
        if (radius > 0) {
            DllCall("gdiplus\GdipAddPathArc", "Ptr", path, "Float", x
                , "Float", y, "Float", diameter, "Float", diameter, "Float", 180, "Float", 90)
            DllCall("gdiplus\GdipAddPathArc", "Ptr", path, "Float", x + w - diameter
                , "Float", y, "Float", diameter, "Float", diameter, "Float", 270, "Float", 90)
            DllCall("gdiplus\GdipAddPathArc", "Ptr", path, "Float", x + w - diameter
                , "Float", y + h - diameter, "Float", diameter, "Float", diameter, "Float", 0, "Float", 90)
            DllCall("gdiplus\GdipAddPathArc", "Ptr", path, "Float", x
                , "Float", y + h - diameter, "Float", diameter, "Float", diameter, "Float", 90, "Float", 90)
            DllCall("gdiplus\GdipClosePathFigure", "Ptr", path)
        } else {
            DllCall("gdiplus\GdipAddPathRectangle", "Ptr", path
                , "Float", x, "Float", y, "Float", w, "Float", h)
        }

        DllCall("gdiplus\GdipSetSmoothingMode", "Ptr", pGraphics, "Int", 4)
        DllCall("gdiplus\GdipFillPath", "Ptr", pGraphics, "Ptr", brush, "Ptr", path)
        DllCall("gdiplus\GdipSetSmoothingMode", "Ptr", pGraphics, "Int", 3)
        DllCall("gdiplus\GdipDeletePath", "Ptr", path)
    }

    Erase(pGraphics, eraseBrush) {
        if !IsObject(this.prevErase)
            return
        box := this.prevErase
        DllCall("gdiplus\GdipFillRectangle", "Ptr", pGraphics, "Ptr", eraseBrush
            , "Float", box.x, "Float", box.y, "Float", box.w, "Float", box.h)
    }

    Commit() {
        this.prevErase := this.eraseBox
    }

    Dispose() {
        if (this.disposed)
            return
        this.disposed := true

        if (this.textBrush)
            DllCall("gdiplus\GdipDeleteBrush", "Ptr", this.textBrush)
        if (this.backBrush)
            DllCall("gdiplus\GdipDeleteBrush", "Ptr", this.backBrush)
        if (this.fmt)
            DllCall("gdiplus\GdipDeleteStringFormat", "Ptr", this.fmt)
        if (this.font)
            DllCall("gdiplus\GdipDeleteFont", "Ptr", this.font)
        if (this.family)
            DllCall("gdiplus\GdipDeleteFontFamily", "Ptr", this.family)

        this.textBrush := 0
        this.backBrush := 0
        this.fmt := 0
        this.font := 0
        this.family := 0
    }

    __Delete() {
        this.Dispose()
    }
}

class ToolTip {
    ; Default options. Any option omitted by the caller falls back to the value here.
    ; Color options use "" to mean "leave the system default" (0 is a valid black).
    DefaultOptions() {
        return { text: "", title: "", icon: 0
               , x: "", y: "", TimeOut: "", ShowNow: true
               , FontName: "", FontSize: 0, FontStyle: ""
               , BackColor: "", TextColor: "", Margin: ""
               , BalloonTip: false, CloseButton: false
			   , transparent: true, RightBottom: false, TrayTip: false }
    }

    __New( options ) {
        defaults := this.DefaultOptions()
        for k, v in defaults
            this[k] := options.HasKey(k) ? options[k] : v
        this._CreateToolTip()
        ( this.ShowNow && this.Show(this.x, this.y, this.TimeOut) )
    }
    
    _CreateToolTip() {
        static WS_POPUP := 0x80000000, WS_EX_TOPMOST := 8, WS_EX_TRANSPARENT := 0x20
              , TTS_NOPREFIX := 2, TTS_ALWAYSTIP := 1, TTS_BALLOON := 0x40, TTS_CLOSE := 0x80
              , TTF_TRACK := 0x20, TTF_ABSOLUTE := 0x80, szTI := A_PtrSize = 4 ? 48 : 72
      
        VarSetCapacity(TOOLINFO, szTI, 0)
        this.pTI := &TOOLINFO
        NumPut(szTI, TOOLINFO)
        (this.TrayTip && this.BalloonTip := true)
        (this.CloseButton && this.transparent := false)
        NumPut(TTF_TRACK|(this.BalloonTip ? 0 : TTF_ABSOLUTE), &TOOLINFO + 4)
        
        this.hwnd := DllCall("CreateWindowEx", "UInt", WS_EX_TOPMOST|(this.transparent ? WS_EX_TRANSPARENT : 0)
          , "Str", "tooltips_class32", "Str", ""
          , "UInt", WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP | (this.CloseButton ? TTS_CLOSE : 0) | (this.BalloonTip ? TTS_BALLOON : 0)
          , "Int", 0, "Int", 0, "Int", 0, "Int", 0, "Ptr", 0, "Ptr", 0, "Ptr", 0, "Ptr", 0)
          
        if (this.FontName || this.FontSize || this.FontStyle)
            this._SetFont()
        
        if (this.TextColor != "" || this.BackColor != "")
            this._SetColor()
        
        this._SetInfo()
    }
    
    _SetFont() {
        static WM_GETFONT := 0x31, mult := A_IsUnicode ? 2 : 1, szLF := 28 + 32 * mult
             , LOGPIXELSY := 90, ANTIALIASED_QUALITY := 4
             , styles := { bold: {value: 700, offset: 16, size: "Int"}
                         , italic: {value: 1, offset: 20, size: "Char"}
                         , underline: {value: 1, offset: 21, size: "Char"}
                         , strikeout: {value: 1, offset: 22, size: "Char"} }
                              
        hPrevFont := this._SendMessage(WM_GETFONT)
        VarSetCapacity(LOGFONT, szLF, 0)
        DllCall("GetObject", "Ptr", hPrevFont, "Int", szLF, "Ptr", &LOGFONT)
        DllCall("DeleteObject", "Ptr", hPrevFont)
        
        if this.FontSize {
            hDC := DllCall("GetDC", "Ptr", this.hwnd, "Ptr")
			picH := DllCall("GetDeviceCaps", "Ptr", hDC, "Int", LOGPIXELSY)
            height := -DllCall("MulDiv", "Int", this.FontSize, "Int", picH, "Int", 72)
            DllCall("ReleaseDC", "Ptr", this.hwnd, "Ptr", hDC)
            NumPut(height, LOGFONT, "Int")
        }
        FontStyle := this.FontStyle
        Loop, parse, FontStyle, %A_Space%
		{
            if obj := styles[A_LoopField]
                NumPut(obj.value, &LOGFONT + obj.offset, obj.size)
		}
        (this.FontSize > 24 && NumPut(ANTIALIASED_QUALITY, &LOGFONT + 26, "Char"))
        this.FontName && StrPut(this.FontName, &LOGFONT + 28, StrLen(this.FontName) * mult, A_IsUnicode ? "UTF-16" : "CP0")
        this.hFont := DllCall("CreateFontIndirect", "Ptr", &LOGFONT, "Ptr")
    }
    
    _SetColor() {
        static WM_USER := 0x400, TTM_SETTIPBKCOLOR := WM_USER + 19, TTM_SETTIPTEXTCOLOR := WM_USER + 20
        
        VarSetCapacity(empty, 2, 0)
        DllCall("UxTheme\SetWindowTheme", "Ptr", this.hwnd, "Ptr", 0, "Ptr", &empty)   
        ( this.TextColor != "" && this._SendMessage(TTM_SETTIPTEXTCOLOR, this._GetColor(this.TextColor)) )
        ( this.BackColor != "" && this._SendMessage(TTM_SETTIPBKCOLOR, this._GetColor(this.BackColor)) )
    }
    
    _GetColor(color) {
        static WS_CHILD := 0x40000000, WM_CTLCOLORSTATIC := 0x138
        
        Gui, New, +hwndhGui +%WS_CHILD%
        Gui, Color, % color + 0 = "" ? color : Format("{:x}", color)
        Gui, Add, Text, hwndhText
        hdc := DllCall("GetDC", "Ptr", hText, "Ptr")
        SendMessage, WM_CTLCOLORSTATIC, hdc, hText,, ahk_id %hGui%
        clr := DllCall("GetBkColor", "Ptr", hdc)
        DllCall("ReleaseDC", "Ptr", hText, "Ptr", hdc)
        Gui, Destroy
        return clr
    }
    
    _SetInfo() {
        static WM_USER := 0x400, TTM_SETMAXTIPWIDTH := WM_USER + 24
              , TTM_ADDTOOL := WM_USER + (A_IsUnicode ? 50 : 4), WM_SETFONT := 0x30
        
        this._SendMessage(WM_SETFONT, this.hFont, 1)
        this._SendMessage(TTM_ADDTOOL, 0, this.pTI)
        this.SetTitle(this.icon, this.title)
        this._SendMessage(TTM_SETMAXTIPWIDTH, 0, A_ScreenWidth)
        this._SetMargin()
        this.SetText(this.text)
    }
    
    _SetMargin() {
        static WM_USER := 0x400, TTM_SETMARGIN := WM_USER + 26
        
        margin := this.Margin
        if !(IsObject(margin) && margin.MaxIndex() = 4)
            return
        VarSetCapacity(RECT, 16, 0)
        for i, offset in margin
            NumPut(offset, &RECT + (i - 1) * 4, "Int")
        this._SendMessage(TTM_SETMARGIN, 0, &RECT)
    }
    
    SetText(text) {
        static WM_USER := 0x400, TTM_UPDATETIPTEXT := WM_USER + (A_IsUnicode ? 57 : 12)
        NumPut(&text, this.pTI + (A_PtrSize = 4 ? 36 : 48))
        this._SendMessage(TTM_UPDATETIPTEXT, 0, this.pTI)
    }
    
    SetTitle(icon := "", title := "") {
        static WM_USER := 0x400, TTM_SETTITLE := WM_USER + (A_IsUnicode ? 33 : 32), TTM_UPDATE := WM_USER + 29
        
        ((icon || this.CloseButton) && title = "") && title := " "
        this._SendMessage(TTM_SETTITLE, icon, &title)
        (icon > 3 && DllCall("DestroyIcon", "Ptr", icon))
        this._SendMessage(TTM_UPDATE)
    }
    
    Show(x := "", y := "", timeout := "") {
        static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17, TTM_TRACKPOSITION := WM_USER + 18
        
        SysGet, MonPrimary, MonitorPrimary
        SysGet, Mon, MonitorWorkArea, %MonPrimary%
        xTT := x, yTT := y
        
        ; TrayTip mode: anchor the balloon to the script's tray icon (one-shot)
        if this.TrayTip {
            this._GetTrayIconCoords(xTT, yTT)
        ; RightBottom mode: push beyond the work area so the post-activation
        ; clamp below pins the tip to the bottom-right corner, above the taskbar
        } else if this.RightBottom {
            xTT := MonRight, yTT := MonBottom
        } else if (xTT = "" || yTT = "") {
            CoordMode, Mouse
            MouseGetPos, xm, ym
            (xTT = "" && xTT := xm + 10)
            (yTT = "" && yTT := ym + 10)
        }
        
        this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
        this._SendMessage(TTM_TRACKACTIVATE, 1, this.pTI)
        
        if this.BalloonTip
            xMax := MonRight, yMax := MonBottom
        else {
            WinGetPos,,, W, H, % "ahk_id" this.hwnd
            xMax := MonRight - W - 10
            yMax := MonBottom - H - 10
        }
        
        if (xTT > xMax || yTT > yMax) {
            (xTT > xMax && xTT := xMax)
            (yTT > yMax && yTT := yMax)
            this._SendMessage(TTM_TRACKPOSITION, 0, xTT|(yTT<<16))
        }

        if timeout {
            timer := this.timer
            try SetTimer, % timer, Delete
            this.timer := timer := ObjBindMethod(this, timeout > 0 ? "Hide" : "Destroy")
            SetTimer % timer, % "-" . Abs(timeout)
        }
    }
    
    Hide() {
        static WM_USER := 0x400, TTM_TRACKACTIVATE := WM_USER + 17
        this._SendMessage(TTM_TRACKACTIVATE, 0, this.pTI)
    }
    
    Destroy() {
        this.__Delete()
        this.SetCapacity(0)
        this.base := ""
    }
    
    _SendMessage(msg, wp := 0, lp := 0) {
        return DllCall("SendMessage", "Ptr", this.hwnd, "UInt", msg, "Ptr", wp, "Ptr", lp, "Ptr")
    }
    
    _GetTrayIconCoords(ByRef x, ByRef y) {
        static AHK_NOTIFYICON := 0x404, szNII := 16 + 3 * A_PtrSize
        
        ; Modern, Win11-safe way: ask the shell for the icon's screen rect directly.
        VarSetCapacity(NII, szNII, 0)
        NumPut(szNII, NII, 0, "UInt")
        NumPut(A_ScriptHwnd, NII, A_PtrSize, "Ptr")
        NumPut(AHK_NOTIFYICON, NII, A_PtrSize * 2, "UInt")
        VarSetCapacity(RECT, 16, 0)
        
        if (DllCall("Shell32\Shell_NotifyIconGetRect", "Ptr", &NII, "Ptr", &RECT, "UInt") = 0) {
            xL := NumGet(RECT, 0, "Int"), yT := NumGet(RECT, 4, "Int")
            xR := NumGet(RECT, 8, "Int")
            x := (xL + xR) // 2   ; balloon anchors its tail at this point
            y := yT
        } else {
            ; Fallback: aim at the tray notification area as a whole.
            for k, v in ["TrayNotifyWnd", "SysPager", "ToolbarWindow32"]
                hTray := DllCall("FindWindowEx", "Ptr", k = 1 ? WinExist("ahk_class Shell_TrayWnd") : hTray, "Ptr", 0, "Str", v, "UInt", 0, "Ptr")
            WinGetPos, xTray, yTray, wTray,, % "ahk_id" hTray
            x := xTray + wTray // 2, y := yTray
        }
    }
    
    __Delete() {
        (this.hFont && DllCall("DeleteObject", "Ptr", this.hFont))
        (this.Icon > 3 && DllCall("DestroyIcon", "Ptr", this.Icon))
        DllCall("DestroyWindow", "Ptr", this.hwnd)
    }
}

;*************************************************************** HotKeys ***************************************************************

;  Автор - serzh82saratov
;  E-Mail: serzh82saratov@mail.ru
;  Описание - http://forum.script-coding.com/viewtopic.php?id=8343

Hotkey_Register(Controls*) {
    static IsStart
    for k, v in Controls
    {
        Hotkey_ID(v[2], v[1]), Hotkey_ID(v[1], v[2])
        Hotkey_Options(v[2], v[3] = "" ? "K" : v[3])
        Hotkey_Value(v[2], Hotkey_Value(v[1]))
        PostMessage, 0x00CF, 1, , , % "ahk_id" v[2]   ;  EM_SETREADONLY
        if RegExMatch(v[3], "i)G(\d+)", g)
            Hotkey_Group("Set", v[1], g1)
    }
    if IsStart
        return Hotkey_IsRegFocus()
    #HotkeyInterval 0
    Hotkey_SetWinEventHook(0x8005, 0x8005, 0, RegisterCallback("Hotkey_EventFocus", "F"), 0, 0, 0)   ;  EVENT_OBJECT_FOCUS := 0x8005
    Hotkey_InitHotkeys(), Hotkey_IsRegFocus(), IsStart := 1
}

Hotkey_Main(Param1, Param2 = "") {
    static OnlyMods, ControlHandle, Hotkey, KeyName, K := {}
    , Prefix := {"LAlt":"<!","LCtrl":"<^","LShift":"<+","LWin":"<#"
                ,"RAlt":">!","RCtrl":">^","RShift":">+","RWin":">#"
                ,"Alt":"!","Ctrl":"^","Shift":"+","Win":"#"}
    , EngSym := {"sc2":"1","sc3":"2","sc4":"3","sc5":"4","sc6":"5","sc7":"6"
                ,"sc8":"7","sc9":"8","scA":"9","scB":"0","scC":"-","scD":"="
                ,"sc10":"Q","sc11":"W","sc12":"E","sc13":"R","sc14":"T","sc15":"Y"
                ,"sc16":"U","sc17":"I","sc18":"O","sc19":"P","sc1A":"[","sc1B":"]"
                ,"sc1E":"A","sc1F":"S","sc20":"D","sc21":"F","sc22":"G","sc23":"H"
                ,"sc24":"J","sc25":"K","sc26":"L","sc27":"`;","sc28":"'","sc29":"``"
                ,"sc2B":"\","sc2C":"Z","sc2D":"X","sc2E":"C","sc2F":"V","sc30":"B"
                ,"sc31":"N","sc32":"M","sc33":",","sc34":".","sc35":"/"}

    if Param1 = GetMod
        return K.MLCtrl K.MRCtrl K.MLAlt K.MRAlt K.MLShift K.MRShift K.MLWin K.MRWin K.MCtrl K.MAlt K.MShift K.MWin
    if Param2
    {
        if OnlyMods
        {
            SendMessage, 0xC, 0, "" Hotkey_Arr("Empty"), , ahk_id %ControlHandle%
            OnlyMods := 0, K := {}
        }
        ControlHandle := Param2
        Hotkey_Arr("Hook", Hotkey_Options(ControlHandle))
        PostMessage, 0x00B1, -1, -1, , ahk_id %ControlHandle%   ;  EM_SETSEL
    }
    else if Hotkey_Arr("Hook")
    {
        Hotkey_Arr("Hook", 0), K := {}
        if OnlyMods
            SendMessage, 0xC, 0, "" Hotkey_Arr("Empty"), , ahk_id %ControlHandle%
        SetTimer, Hotkey_IsRegFocus, -200
    }
    return

Hotkey_Mods:
    if InStr(Hotkey_Arr("Hook"), "S")
        GoTo Hotkey_View
    IsMod := InStr(Hotkey_Arr("Hook"), "D") ? A_ThisHotkey : SubStr(A_ThisHotkey, 2)
    if (K["M" IsMod] != "")
        return
    K["M" IsMod] := IsMod "+", K["P" IsMod] := Prefix[IsMod]
    GoTo Hotkey_ViewMod

Hotkey_ModsUp:
    if InStr(Hotkey_Arr("Hook"), "S")
        return
    IsMod := InStr(Hotkey_Arr("Hook"), "D") ? SubStr(A_ThisHotkey, 1, -3) : SubStr(A_ThisHotkey, 2, -3)
    K["M" IsMod] := "", K["P" IsMod] := ""
    if (Hotkey != "")
        return

Hotkey_ViewMod:
    Hotkey := "", OnlyMods := 1, Hotkey_Value(Hotkey_ID(ControlHandle), K.Prefix Hotkey), Hotkey_Value(ControlHandle, K.Prefix Hotkey)
    K.Mods := K.MLCtrl K.MRCtrl K.MLAlt K.MRAlt K.MLShift K.MRShift K.MLWin K.MRWin K.MCtrl K.MAlt K.MShift K.MWin
    Text := K.Mods = "" ? Hotkey_Arr("Empty") : K.Mods
    SendMessage, 0xC, 0, &Text, , ahk_id %ControlHandle%
    return

Hotkey_ViewNum:
    if InStr(Hotkey_Arr("Hook"), "N")
        KeyName := Hotkey := GetKeyName(A_ThisHotkey)
    else
        KeyName := GetKeyName(A_ThisHotkey), Hotkey := Format("sc{:x}", GetKeySC(A_ThisHotkey))
    GoTo, Hotkey_Put

Hotkey_ViewSC:
    KeyName := Hotkey_Arr("OnlyEngSym") ? EngSym[A_ThisHotkey] : Format("{:U}", GetKeyName(A_ThisHotkey))
    Hotkey := A_ThisHotkey
    GoTo, Hotkey_Put

Hotkey_View:
    if (A_ThisHotkey = "Backspace")
        GoTo, Hotkey_Clear
    if Hotkey_IsMouseInput(A_ThisHotkey)
        Hotkey_SyncPhysicalModifiers(K)
    KeyName := Hotkey := A_ThisHotkey

Hotkey_Put:
    OnlyMods := 0
    K.Prefix := K.PLCtrl K.PRCtrl K.PLAlt K.PRAlt K.PLShift K.PRShift K.PLWin K.PRWin K.PCtrl K.PAlt K.PShift K.PWin
    Hotkey_Value(Hotkey_ID(ControlHandle), K.Prefix Hotkey), Hotkey_Value(ControlHandle, K.Prefix Hotkey)
    K.Mods := K.MLCtrl K.MRCtrl K.MLAlt K.MRAlt K.MLShift K.MRShift K.MLWin K.MRWin K.MCtrl K.MAlt K.MShift K.MWin
    Text := K.Mods KeyName = "" ? Hotkey_Arr("Empty") : K.Mods KeyName
    SendMessage, 0xC, 0, &Text, , ahk_id %ControlHandle%
    if Hotkey_Group("Get", Hotkey_ID(ControlHandle)) && Hotkey_Group("SaveCheck", ControlHandle)
        SetTimer, Hotkey_Group, -70
    return

Hotkey_Clear:
    OnlyMods := 0, K := {}
    Hotkey_Value(Hotkey_ID(ControlHandle), " "), Hotkey_Value(ControlHandle, " ")
    Text := Hotkey_Arr("Empty")
    SendMessage, 0xC, 0, &Text, , ahk_id %ControlHandle%
    if Hotkey_Group("Get", Hotkey_ID(ControlHandle)) && Hotkey_Group("SaveCheck", ControlHandle)
        SetTimer, Hotkey_Group, -70
    return
}

Hotkey_InitHotkeys() {
    static nmMods := "LAlt|RAlt|LCtrl|RCtrl|LShift|RShift|LWin|RWin"
    , nmMouse := "MButton|WheelDown|WheelUp|WheelRight|WheelLeft|XButton1|XButton2"
    , scSymb := "2|3|4|5|6|7|8|9|A|B|C|D|10|11|12|13|14|15|16|17|18|19|1A|1B|"
        . "1E|1F|20|21|22|23|24|25|26|27|28|29|2B|2C|2D|2E|2F|30|31|32|33|34|35"
    ; , scNumSymb := "53|52|4F|50|51|4B|4C|4D|47|48|49"
    , scOther := "1|E|F|1C|37|39|3A|3B|3C|3D|3E|3F|40|41|42|43|44|45|46|4A|4E|54|57|58|63|64|65|"
        . "66|67|68|69|6A|6B|6C|6D|6E|76|11C|135|147|148|149|14B|14D|14F|150|151|152|153|15D"
    , vkNum := "2E|6E|60|2D|61|23|62|28|63|22|64|25|65|C|66|27|67|24|68|26|69"
    , vkOther := "3|13|5F|A6|A7|A8|A9|AA|AB|AC|AD|AE|AF|B0|B1|B2|B3|B4|B5|B6|B7"
    S_BatchLines := A_BatchLines
    SetBatchLines, -1
    #IF Hotkey_IsRegControl()
    #IF Hotkey_Hook("K")
    #IF Hotkey_Hook("M")
    #IF Hotkey_Hook("L") && Hotkey_HasKeyboardModifier()
    #IF Hotkey_Hook("R") && Hotkey_HasKeyboardModifier()
    #IF Hotkey_Hook("J") && !Hotkey_Main("GetMod")
    #IF Hotkey_Arr("Hook") && !Hotkey_Hook("K")
    #IF
    Hotkey, IF, Hotkey_Hook("M")
    Loop, Parse, nmMouse, |
        Hotkey, % A_LoopField, Hotkey_View
    Hotkey, IF, Hotkey_Hook("K")
    Loop, Parse, nmMods, |
    {
        Hotkey, % A_LoopField, Hotkey_Mods
        Hotkey, % A_LoopField " Up", Hotkey_ModsUp
    }
    Loop, Parse, scSymb, |
        Hotkey, % "sc" A_LoopField, Hotkey_ViewSC
    Loop, Parse, scOther, |
        Hotkey, % GetKeyName("sc" A_LoopField), Hotkey_View
    Loop, Parse, vkNum, |
        Hotkey, % "vk" A_LoopField, Hotkey_ViewNum
    Loop, Parse, vkOther, |
        Hotkey, % GetKeyName("vk" A_LoopField), Hotkey_View
	Hotkey, IF, Hotkey_Hook("L") && Hotkey_HasKeyboardModifier()
    Hotkey, LButton, Hotkey_View
    Hotkey, IF, Hotkey_Hook("R") && Hotkey_HasKeyboardModifier()
    Hotkey, RButton, Hotkey_View
    S_FormatInteger := A_FormatInteger
    SetFormat, IntegerFast, D
    Hotkey, IF, Hotkey_Hook("J") && !Hotkey_Main("GetMod")
    Loop, 128
        Hotkey % Ceil(A_Index / 32) "Joy" Mod(A_Index - 1, 32) + 1, Hotkey_View
    SetFormat, IntegerFast, %S_FormatInteger%
    Hotkey, IF, Hotkey_IsRegControl()
    Hotkey, RButton, Hotkey_RButton
    Hotkey, RButton Up, Hotkey_Return
    Hotkey, IF, Hotkey_Arr("Hook") && !Hotkey_Hook("K")
    Hotkey, AppsKey Up, Hotkey_Return
    Hotkey, +F10, Hotkey_Return
    Hotkey, IF
    SetBatchLines, %S_BatchLines%
    return

    Hotkey_RButton:
        Click
    Hotkey_Return:
        return
}

Hotkey_IsRegControl() {
    MouseGetPos,,,, Control, 2
    return Hotkey_ID(Control) != ""
}

Hotkey_IsMouseInput(Key) {
    static MouseInputs := "|LButton|RButton|MButton|WheelDown|WheelUp|WheelRight|WheelLeft|XButton1|XButton2|"
    return InStr(MouseInputs, "|" Key "|")
}

Hotkey_HasKeyboardModifier() {
    return GetKeyState("Ctrl", "P") || GetKeyState("Shift", "P")
        || GetKeyState("Alt", "P") || GetKeyState("LWin", "P")
        || GetKeyState("RWin", "P")
}

Hotkey_SyncPhysicalModifiers(K) {
    static Modifiers := { LCtrl: "<^", RCtrl: ">^", LAlt: "<!", RAlt: ">!"
                        , LShift: "<+", RShift: ">+", LWin: "<#", RWin: ">#" }
    for Name, Prefix in Modifiers {
        IsDown := GetKeyState(Name, "P")
        K["M" Name] := IsDown ? Name "+" : ""
        K["P" Name] := IsDown ? Prefix : ""
    }
    K.MCtrl := K.MAlt := K.MShift := K.MWin := ""
    K.PCtrl := K.PAlt := K.PShift := K.PWin := ""
}

Hotkey_IsRegFocus() {
    ControlGetFocus, ControlNN, A
    ControlGet, hFocus, Hwnd, , %ControlNN%, A
    Hotkey_ID(hFocus) != "" ? Hotkey_Main("Control", hFocus) : 0
}

Hotkey_EventFocus(hWinEventHook, event, hwnd) {
    Hotkey_ID(hwnd) != "" ? Hotkey_Main("Control", hwnd) : Hotkey_Main("Control")
}

Hotkey_SetWinEventHook(eventMin, eventMax, hmodWinEventProc, lpfnWinEventProc, idProcess, idThread, dwFlags) {
    return DllCall("SetWinEventHook" , "UInt", eventMin, "UInt", eventMax, "Ptr", hmodWinEventProc
            , "Ptr", lpfnWinEventProc, "UInt", idProcess, "UInt", idThread, "UInt", dwFlags, "Ptr")
}

; -------------------------------------- Save and get --------------------------------------

Hotkey_Arr(P*) {
    static Arr := {"Empty":"Нет"}
    return P.MaxIndex() = 1 ? Arr[P[1]] : (Arr[P[1]] := P[2])
}

Hotkey_ID(P*) {
    static Arr := {}
    return P.MaxIndex() = 1 ? Arr[P[1]] : P.MaxIndex() = 2 ? (Arr[P[1]] := P[2]) : Arr.Delete(P[1])
}

Hotkey_Value(P*) {
    static Arr := {}
    return P.MaxIndex() = 1 ? Arr[P[1]] : P.MaxIndex() = 2 ? (Arr[P[1]] := P[2]) : Arr.Delete(P[1])
}

Hotkey_Options(P*) {
    static Arr := {}
    return P.MaxIndex() = 1 ? Arr[P[1]] : P.MaxIndex() = 2 ? (Arr[P[1]] := P[2]) : Arr.Delete(P[1])
}

Hotkey_ChangeOption(ID, Option) {
    (ID + 0 = "") ? (Hwnd := Hotkey_ID(ID), Name := ID) : (Hwnd := ID, Name := Hotkey_ID(ID))
    if RegExMatch(Hotkey_Options(Hwnd), "i)G(\d+)", g)
        Hotkey_Group("Delete", Name)
    if RegExMatch(Option, "i)G(\d+)", g)
        Hotkey_Group("Set", Name, g1)
    return Hotkey_Options(Hwnd, Option)
}

Hotkey_Hook(Option) {
    return Hotkey_Arr("Hook") && !!InStr(Hotkey_Arr("Hook"), Option)
}

Hotkey_Delete(ID, Destroy = 0) {
    (ID + 0 = "") ? (Hwnd := Hotkey_ID(ID), Name := ID) : (Hwnd := ID, Name := Hotkey_ID(ID))
    Hotkey_Group("Delete", Name)
    Hotkey_ID(Hwnd, "", 1), Hotkey_ID(Name, "", 1)
    Hotkey_Value(Hwnd, "", 1), Hotkey_Value(Name, "", 1)
    Hotkey_Options(Hwnd, "", 1)
    ControlGetFocus, ControlNN, A
    ControlGet, hFocus, Hwnd, , %ControlNN%, A
    (hFocus = Hwnd ? Hotkey_Main("Control") : 0)
    if Destroy
        DllCall("DestroyWindow", "Ptr", Hwnd)
    else
        PostMessage, 0x00CF, 0, , , % "ahk_id" Hwnd		;  EM_SETREADONLY
    return Hwnd
}

Hotkey_Set(Name, Value = "") {
    Text := Hotkey_HKToStr(Value)
    SendMessage, 0xC, 0, &Text, , % "ahk_id" Hotkey_ID(Name)
    return Text, Hotkey_Value(Name, Value), Hotkey_Value(Hotkey_ID(Name), Value)
}

Hotkey_Read(Name, Section = "", FilePath = "") {
    HK := Hotkey_IniRead(Name, Section, FilePath), Hotkey_Value(Name, HK)
    return Hotkey_HKToStr(HK)
}

Hotkey_IniPath(Path = "") {
    return Path = "" ? Hotkey_Arr("IniPath") : Hotkey_Arr("IniPath", Path)
}

Hotkey_IniSection(Section = "") {
    return Section = "" ? Hotkey_Arr("IniSection") : Hotkey_Arr("IniSection", Section)
}

Hotkey_IniRead(Name, Section = "", FilePath = "") {
    IniRead, Data, % FilePath = "" ? Hotkey_IniPath() : FilePath, % Section = "" ? Hotkey_IniSection() : Section, % Name, % A_Space
    return Data
}

Hotkey_IniWrite(ID, Section = "", FilePath = "") {
    Key := (ID + 0 = "") ? ID : Hotkey_ID(ID)
    if (Key != "")
        IniWrite, % Hotkey_Value(ID), % FilePath = "" ? Hotkey_IniPath() : FilePath, % Section = "" ? Hotkey_IniSection() : Section, % Key
}

; -------------------------------------- Group --------------------------------------

Hotkey_Group(Key = "", p1 = "", p2 = "") {
    static NG := {}, GN := [], Blink := [], SaveCheck := [], i := 0
    if (Key = "") {
        for k, Name in SaveCheck {
            if ((Value := Hotkey_Value(Name)) != "") {
                (f := Hotkey_Arr("GroupEvents")) != "" && (r := {}, r.names := [])
                for m, n in GN[NG[Name]] {
                    if (n != Name && Hotkey_Equal(Value, Hotkey_Value(n))) {
                        Hotkey_Set(Name)
                        (f != "") && (r.names.Push(n), r.this := Name, r.value := Value, r.group := NG[Name])
                        if !DllCall("IsWindowVisible", "Ptr", Hotkey_ID(n))
                            continue
                        DllCall("ShowWindowAsync", "Ptr", Hotkey_ID(n), "Int", 0)
                        Blink[Hotkey_ID(n)] := 1, i := 3
                        SetTimer, Hotkey_BlinkControl, -50
                    }
                }
            }
            SaveCheck.Delete(k)
        }
        (f != "") && (r.this != "") && %f%(r)
    }
    else if (Key = "Set")
        NG[p1] := p2, IsObject(GN[p2]) ? GN[p2].Push(p1) : GN[p2] := [p1]
    else if (Key = "SaveCheck")
        return 1, SaveCheck[p1] := Hotkey_ID(p1)
    else if (Key = "Get")
        return NG[p1] != ""
    else if (Key = "CheckAll") {
        for k, v in GN {
            for k, n in v {
                if ((Value := Hotkey_Value(n)) != "") {
                    for k, m in v {
                        if (n != m && Hotkey_Equal(Value, Hotkey_Value(m)))
                            Hotkey_Set(m)
					}
				}
			}
		}
    }
    else if (Key = "Delete") {
        for k, v in GN[NG[p1]] {
            if (v = p1) {
                GN[NG[p1]].RemoveAt(k)
                break
            }
		}
        NG.Delete(p1)
    }
    return

    Hotkey_BlinkControl:
        for k in Blink
            DllCall("ShowWindowAsync", "Ptr", k, "Int", Mod(i, 2) ? 4 : 0)
        if (--i > 0) || !(Blink := [])
            SetTimer, Hotkey_BlinkControl, -50
        return
}

Hotkey_Equal(HK1, HK2) {
    if (HK2 = "")
        return 0
    if (HK1 = HK2)
        return 1
    if !(HK1 ~= "S)[\^\+!#]") || !(HK2 ~= "S)[\^\+!#]")
        return 0
    if (HK1 ~= "S)[<>]") && (HK2 ~= "S)[<>]")
        return 0
    return (Hotkey_ModsSub(HK1) = Hotkey_ModsSub(HK2))
}

Hotkey_ModsSub(Value) {
    if !(Value ~= "[<>]")
        return Value
    Value := StrReplace(Value, "<")
    Value := StrReplace(Value, ">")
    Value := StrReplace(Value, "^^", "^", , 1)
    Value := StrReplace(Value, "!!", "!", , 1)
    Value := StrReplace(Value, "++", "+", , 1)
    return StrReplace(Value, "##", "#", , 1)
}

; -------------------------------------- Format --------------------------------------

; http://forum.script-coding.com/viewtopic.php?pid=105023#p105023

Hotkey_HKToStr(HK) {
    static LRPrefix := [["<^","LCtrl"],[">^","RCtrl"],["<!","LAlt"],[">!","RAlt"]
                    ,["<+","LShift"],[">+","RShift"],["<#","LWin"],[">#","RWin"]]
    , Prefix := [["^","Ctrl"],["!","Alt"],["+","Shift"],["#","Win"]]
    , EngSym := {"sc2":"1","sc3":"2","sc4":"3","sc5":"4","sc6":"5","sc7":"6"
                ,"sc8":"7","sc9":"8","scA":"9","scB":"0","scC":"-","scD":"="
                ,"sc10":"Q","sc11":"W","sc12":"E","sc13":"R","sc14":"T","sc15":"Y"
                ,"sc16":"U","sc17":"I","sc18":"O","sc19":"P","sc1A":"[","sc1B":"]"
                ,"sc1E":"A","sc1F":"S","sc20":"D","sc21":"F","sc22":"G","sc23":"H"
                ,"sc24":"J","sc25":"K","sc26":"L","sc27":"`;","sc28":"'","sc29":"``"
                ,"sc2B":"\","sc2C":"Z","sc2D":"X","sc2E":"C","sc2F":"V","sc30":"B"
                ,"sc31":"N","sc32":"M","sc33":",","sc34":".","sc35":"/"

                ,"vk31":"1","vk32":"2","vk33":"3","vk34":"4","vk35":"5","vk36":"6"
                ,"vk37":"7","vk38":"8","vk39":"9","vk30":"0","vkBD":"-","vkBB":"="
                ,"vk51":"Q","vk57":"W","vk45":"E","vk52":"R","vk54":"T","vk59":"Y"
                ,"vk55":"U","vk49":"I","vk4F":"O","vk50":"P","vkDB":"[","vkDD":"]"
                ,"vk41":"A","vk53":"S","vk44":"D","vk46":"F","vk47":"G","vk48":"H"
                ,"vk4A":"J","vk4B":"K","vk4C":"L","vkBA":"`;","vkDE":"'","vkC0":"``"
                ,"vkDC":"\","vk5A":"Z","vk58":"X","vk43":"C","vk56":"V","vk42":"B"
                ,"vk4E":"N","vk4D":"M","vkBC":",","vkBE":".","vkBF":"/"}

    RegExMatch(HK, "S)^\s*([~\*\$\^\+!#<>]*)\{?(.*?)}?\s*$", K)
    if (K2 = "")
        return "" Hotkey_Arr("Empty")
    if (InStr("|" K2, "|sc") || InStr("|" K2, "|vk"))
        K2 := Hotkey_Arr("OnlyEngSym") && EngSym.HasKey(K2) ? EngSym[K2] : GetKeyName(K2)
    if (K1 != "")
        for I, V in K1 ~= "[<>]" ? LRPrefix : Prefix
            K1 := StrReplace(K1, V[1], "", R), R && (M .= V[2] "+")
    return M . (StrLen(K2) = 1 ? Format("{:U}", K2) : K2)
}

Hotkey_HKToSend(HK, Section = "", FilePath = "") {
    static LRPrefix := [["<^","LCtrl"],[">^","RCtrl"],["<!","LAlt"],[">!","RAlt"]
                       ,["<+","LShift"],[">+","RShift"],["<#","LWin"],[">#","RWin"]]
         , Prefix := [["^","LCtrl"],["!","LAlt"],["+","LShift"],["#","LWin"]]
    if (HK = "")
        return
    if (Section != "")
        IniRead, HK, % FilePath = "" ? Hotkey_IniPath() : FilePath, % Section, % HK, % A_Space
    RegExMatch(HK, "S)^\s*([~\*\$\^\+!#<>]*)\{?(.*?)}?\s*$", K)
    if (K1 != "") {
        for I, V in K1 ~= "[<>]" ? LRPrefix : Prefix {
            K1 := StrReplace(K1, V[1], "", R)
            , R ? (M1 .= "{" V[2] " Down}", M2 .= "{" V[2] " Up}") : 0
		}
	}
    return M1 . "{" K2 "}" . M2
}

#if WinExist("ScreenShot ahk_class AutoHotkeyGUI")
#if

#if IsOverlaySelectionState("armed")
LButton:: BeginOverlaySelectionDrag()
RButton:: CancelOverlaySelection()
Esc:: CancelOverlaySelection()
#if

#if IsOverlaySelectionState("dragging")
*LButton Up:: FinishOverlaySelection()
RButton:: CancelOverlaySelection()
Esc:: CancelOverlaySelection()
#if

#if IsSelectionWindowActive()
^WheelUp::   NudgeSelectionEdge("Left"  , -1)
^WheelDown:: NudgeSelectionEdge("Left"  ,  1)
+WheelUp::   NudgeSelectionEdge("Top"   , -1)
+WheelDown:: NudgeSelectionEdge("Top"   ,  1)
!WheelUp::   NudgeSelectionEdge("Right" ,  1)
!WheelDown:: NudgeSelectionEdge("Right" , -1)
#WheelUp::   NudgeSelectionEdge("Bottom", -1)
#WheelDown:: NudgeSelectionEdge("Bottom",  1)
#if

#if WinActive("ScreenShot ahk_class AutoHotkeyGUI")
Esc:: PostMessage, Esc,,,, A
*WheelDown::
*WheelUp:: PostMessage, Wheel, A_ThisHotkey = "*WheelUp" ? 0 : 1,,, A
#if

Сохраните скрипт в кодировке UTF-8 с BOM, (иначе получите кракозябры вместо кириллицы).
Если кто-нибудь захочет задать вопрос по работе скрипта или сообщить о замеченных багах — прошу писать в эту тему.

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