1 (изменено: Li, 2014-08-09 10:42:48)

Тема: AHK: Панель быстрого доступа в WinXP как в Win7

Написал скрипт, который позволяет назначить до 12 сочетаний клавиш через графическое меню (Ctrl+F7) для переключения между окнами приложений + Анализатор логов Key Catcher (Ctrl+F2). Страничка помощи вызывается Ctrl+F1.

Можно сделать аналогичную (почти как) панель с использованием сочетаний Win+`,1,2,,,,-,= и Ctrl+`,1,2,...,-,=, то есть переключаться между окнами как в Windows 7.

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

+ открыть спойлер

; Проверка на наличие файла настроек скрипта (Путь к файлу настроек в ahk_script_settings) - quickpanel.ini
IfNotExist, %A_ScriptDir%\templates\ahkscriptsettings.txt
  MsgBox, % "Нет файла шаблона для файла настроек" %A_ScriptDir% "\templates\ahkscriptsettings.txt"
else
IfNotExist, %ahk_script_settings% ; Если файла нет, то он будет создан из шаблона
{
  ; Считаем шаблон в память
    FileRead, template_in_memory, *m1024 %A_ScriptDir%\templates\ahkscriptsettings.txt
    IF(ErrorLevel)
      MsgBox, % "Не могу загрузить шаблон " A_ScriptDir "\templates\ahkscriptsettings.txt"
    
    valueN_array1:=marker ; пока в массиве только 1 элемент
    ; Заменим значения в шаблоне на значения из массива valueN_array (Значения представлены в шаблоне в виде $%!value1!%$, $%!value2!%$, ...)
    valueN_Count=1
    Loop, %valueN_Count%
    {
      valueN=$`%!value%A_Index%!`%$
      valueN_paste:=valueN_array%A_Index%
      StringReplace, template_in_memory, template_in_memory, %valueN%, %valueN_paste%, All
    }
    fileappend, %template_in_memory%, %ahk_script_settings% ; Создадим файл настроек скрипта 
}

; Forming Window Title Array in Memory (Соответствие имени .exe файла приложения названию окна этого приложения)
WTArray_Count = 0
Loop, Read, %a_scriptdir%\windowtitles.txt
{
  WTArray_Count+=1
  WindowTitleArray%WTArray_Count% :=  A_LoopReadLine
}

; Извлечение тегов-запросов на добавленные авгенерируемые теги (теги добавляются в скрипт A_ScriptFullPath) из quickpanel.ini
 ; Запрос будет в виде строки [секция|tag_id|tag_data]. Запросы идут один за другим, разделенные знаком "|"
ahk_parsing_file:=ahk_script_settings
tag_data:=quickpanel_params_count ; Для секции quickpanel_settings в каждом теге-запросе содержится по 5 параметров для каждого 
; добавленного тега 
If((tag_count:=tag_modify("get", "quickpanel_settings", "template", tag_id_list, , tag_data))<0)
  MsgBox, Ошибка при извлечении тегов-запросов из файла %ahk_script_settings% Переменная tag_data = %tag_data%
ahk_parsing_file:=A_ScriptFullPath ; Вернем на место
If(tag_count>quickpanel_max_lines)
  MsgBox, Превышено максимально допустимое число тегов-запросов (max=%quickpanel_max_lines%) tag_count = %tag_count%
else
Loop, %tag_count%
{ ; Из tag_data нам нужны следующие параметры:
; 4-ый - Клавиша/HotKey
; 5-ый - WindowTitle
; 7-й - Путь к программе
StringGetPos, position_2, tag_data,|,L1
StringGetPos, position_3, tag_data,|,L2
StringGetPos, position_4, tag_data,|,L3
StringGetPos, position_5, tag_data,|,L4
StringGetPos, position_6, tag_data,|,L5
StringGetPos, position_7, tag_data,|,L6
StringGetPos, position_8, tag_data,|,L7

StringGetPos, position_id, tag_id_list,|,L%A_Index%
temp:=A_Index-1
StringGetPos, position_id_previous, tag_id_list,|,L%temp%
If(ErrorLevel==1)
  position_id_previous=-1
; MsgBox % position_id_previous ; debug

StringMid, str_right_part, tag_id_list, position_id_previous+2, position_id-position_id_previous-1
StringMid, str_temp, tag_data, position_2+2, position_3-position_2-1
str_temp .= "/"str_right_part
 ; MsgBox, % str_right_part ; debug
GuiControl, 3:,fileplaceapp%A_Index%id,%str_temp%

StringMid, str_temp, tag_data, position_4+2, position_5-position_4-1
GuiControl, 3:,fileplaceapp%A_Index%fk,%str_temp%
; MsgBox, % "TEST 777 = " fileplaceapp%A_Index%fk ; debug
StringMid, str_temp, tag_data, position_5+2, position_6-position_5-1
GuiControl, 3:,fileplaceapp%A_Index%t,%str_temp%
StringMid, str_temp, tag_data, position_7+3, position_8-position_7-3 ; потому что нужно удалить кавычку слева/справа
GuiControl, 3:,fileplaceapp%A_Index%v,%str_temp%
; MsgBox, % "TEST 777 = " fileplaceapp%A_Index%v " str_temp = " str_temp ; debug
; quickpanel_params_count+5 ; смотри функцию tag_modify() - там 5 определяет структуру тега-запроса
; Нужно убрать левую часть из tag_data
StringTrimLeft, tag_data, tag_data, position_8+1
}

; Данные о сгенерированных тегах хранятся в файле настроек скрипта "имя скрипта.ini" (ex. quickpanel.ini)

ahk_parsing_file:=ahk_script_settings
tag_data:=quickpanel_params_count ; Для секции quickpanel_settings в каждом теге-запросе содержится по 5 параметров для каждого 
If(tag_modify("get", "quickpanel_settings", "status", tag_id_list, 0, tag_data)<0)
  MsgBox, Ошибка при получении статуса секции файла %A_ScriptFullPath% Секция hot_keys
ahk_parsing_file:=A_ScriptFullPath ; Вернем на место

If((id_count:=tag_modify("get", "hot_keys", "status", 0, 0, 0))<0)
  MsgBox, Ошибка при получении статуса секции файла %A_ScriptFullPath% Секция hot_keys
ahk_generate_trigger=1
; by default ahk_generate_trigger=0   QuickPanel_Changes="0|0|0|0|0|0|0|0|0|0|0|0|"
Loop, %quickpanel_max_lines%
  QuickPanel_Changes%A_Index%=0
Loop, %tag_count%
  QuickPanel_Changes%A_Index%=1
QuickPanel_Changes:=""
Loop, %quickpanel_max_lines%
  QuickPanel_Changes .= QuickPanel_Changes%A_Index% "|"
; MsgBox, % "QuickPanel_Changes =" QuickPanel_Changes " tag_count = " tag_count " id_count = " id_count ; debug
; MsgBox, % tag_id_list debug
If(tag_count>0 and id_count==0) ; Секция пуста, а теги-запросы есть
{
  ahk_generate(ahk_generate_trigger, QuickPanel_Changes,1, tag_id_list) ; Recovery_bit=1 , то есть нужно восстановить теги
  Reload ; Перегрузка
}
; MsgBox, OK ; debug

; Set timers (wait the timer's value then trigger an action)
SetTimer, UpdateMemStatus, 300000 ; every 5 minutes
; SetTimer, DebugFileMove, 10000 ; every 10 sec

return ; really need it here! т.к. До этого места выполняются все действия, которые описаны выше, при каждой загрузке/перегрузке скрипта

; Кнопка "Save", сохранение изменений в файле скрипта (Окно Settings в Experimental Functionality)
generateahk:
; Запомним значения переменных до изменения пользователем
ahk_generate_trigger=0
Loop, %quickpanel_max_lines%
  QuickPanel_Changes%A_Index%=0
 Gui, 3:Submit, NoHide ; Сохранение изменений в окне Settings (CTRL+F7) в привязанные к элементам окна переменные 
; Найдем измененные значения, чтобы определить какие теги нам нужно править
; QuickPanel_Changes%A_Index% - массив, где отмечены какие строчки должны быть добавлены в качестве тегов
Loop, %quickpanel_max_lines%
{
  QuickPanel_Changes%A_Index%=0 ; нет изменений
  
  ; Клавиша/HotKey
  GuiControlGet, str_temp, 3:,fileplaceapp%A_Index%fk
  str_clean_spaces_tabs(str_temp)
  if(before_change%A_Index%fk!=str_temp)
  {
     ; MsgBox, % "YEAP |" before_change%A_Index%fk "|" ; debug
    If(before_change%A_Index%fk!="")
    {
    GuiControlGet, tag_id, 3:,fileplaceapp%A_Index%id
    StringGetPos, position, tag_id,/
    StringLeft, tag_id_left, tag_id, position
    StringTrimLeft, tag_id_right, tag_id, position+1
    ; MsgBox % "tag_id_left = "tag_id_left " tag_id_right = " tag_id_right ; debug
  ; MsgBox, % "1:" tag_id_left
    If(tag_modify("delete", "hot_keys", "template",tag_id_left,0,0)<0)
    {
      MsgBox, Ошибка при удалении тега tag_id = %tag_id%
      continue ; Пробуем следующую строчку из 12
    }
   ahk_parsing_file:=ahk_script_settings
   ; Удаление запроса на автогенерируемые теги в quickpanel.ini (Путь к файлу в ahk_script_settings)
   If(tag_modify("delete", "quickpanel_settings", "template",tag_id_right,0,0)<0)
   {
     MsgBox, Критическая ошибка при удалении тега-запроса в файле %ahk_script_settings%. tag_get_id = %tag_get_id%
     ahk_parsing_file:=A_ScriptFullPath ; вернем на место
     break ; если не можем удалить теги-запросы, то функциональность скрипта нарушена
   }
   ahk_parsing_file:=A_ScriptFullPath ; вернем на место
   }
   GuiControl, 3:,fileplaceapp%A_Index%fk,%str_temp% ; Сохраним скорректированное значение (без табуляции и пробелов)
   QuickPanel_Changes%A_Index%=1 ; Есть изменения
   ahk_generate_trigger=1
   continue ; тег и тег-запрос удален, не имеет смысла продолжать сравнивать по другим изменениям
  }

  ; Путь
  GuiControlGet, str_temp, 3:,fileplaceapp%A_Index%v
  str_clean_spaces_tabs(str_temp)
  if(before_change%A_Index%v!=str_temp)
  {
    ; MsgBox, Changed detected ; debug
    If(before_change%A_Index%v!="")
    {
    GuiControlGet, tag_id, 3:,fileplaceapp%A_Index%id
    StringGetPos, position, tag_id,/
    StringLeft, tag_id_left, tag_id, position
    StringTrimLeft, tag_id_right, tag_id, position+1
    ; MsgBox % "tag_id_left = "tag_id_left " tag_id_right = " tag_id_right ; debug
 ; MsgBox, % "2:" tag_id_left
    If(tag_modify("delete", "hot_keys", "template",tag_id_left,0,0)<0)
    {
      MsgBox, Ошибка при удалении тега tag_id = %tag_id%
      continue ; Пробуем следующую строчку из 12
    }
   ahk_parsing_file:=ahk_script_settings
   ; Удаление запроса на автогенерируемые теги в quickpanel.ini (Путь в ahk_script_settings)
   If(tag_modify("delete", "quickpanel_settings", "template",tag_id_right,0,0)<0)
   {
     MsgBox, Критическая ошибка при удалении тега-запроса в файле %ahk_script_settings%. tag_get_id = %tag_get_id%
     ahk_parsing_file:=A_ScriptFullPath ; вернем на место
     break ; если не можем удалить теги-запросы, то функциональность скрипта нарушена
   }
   ahk_parsing_file:=A_ScriptFullPath ; вернем на место
   }
  GuiControl, 3:,fileplaceapp%A_Index%v,%str_temp% ; Сохраним скорректированное значение (без табуляции и пробелов)
   QuickPanel_Changes%A_Index%=1 ; Есть изменения
   ahk_generate_trigger=1
   continue ; тег и тег-запрос удален, не имеет смысла продолжать сравнивать по другим изменениям
  }
  
  ; WindowTitle
  GuiControlGet, str_temp, 3:,fileplaceapp%A_Index%t
  str_clean_spaces_tabs(str_temp)
  if(before_change%A_Index%t!=str_temp)
  {
    ; MsgBox, Changed detected ; debug
    If(before_change%A_Index%t!="")
    {
    GuiControlGet, tag_id, 3:,fileplaceapp%A_Index%id
    StringGetPos, position, tag_id,/
    StringLeft, tag_id_left, tag_id, position
    StringTrimLeft, tag_id_right, tag_id, position+1
 ; MsgBox, % "3:" tag_id_left
    ; MsgBox % "tag_id_left = "tag_id_left " tag_id_right = " tag_id_right ; debug
    If(tag_modify("delete", "hot_keys", "template",tag_id_left,0,0)<0)
    {
      MsgBox, Ошибка при удалении тега tag_id = %tag_id%
      continue ; Пробуем следующую строчку из 12
    }
   ahk_parsing_file:=ahk_script_settings
   ; Удаление запроса на автогенерируемые теги в quickpanel.ini (Путь хранится в ahk_script_settings)
   If(tag_modify("delete", "quickpanel_settings", "template",tag_id_right,0,0)<0)
   {
     MsgBox, Критическая ошибка при удалении тега-запроса в файле %ahk_script_settings%. tag_get_id = %tag_get_id%
     ahk_parsing_file:=A_ScriptFullPath ; вернем на место
     break ; если не можем удалить теги-запросы, то функциональность скрипта нарушена
   }
   ahk_parsing_file:=A_ScriptFullPath ; вернем на место
   }
  GuiControl, 3:,fileplaceapp%A_Index%t,%str_temp% ; Сохраним скорректированное значение (без табуляции и пробелов)
   QuickPanel_Changes%A_Index%=1 ; Есть изменения
   ahk_generate_trigger=1
   continue ; тег и тег-запрос удален, не имеет смысла продолжать сравнивать по другим изменениям
  }
}
; Заглушка: Проверка на уникальность каждого HotKey, чтобы не совпадали
Sleep, 100
; Check left, right fields for correct pathes, window titles

; Generate AHK quickpanel's hot keys from quickpanelsample.txt
; use variables like $%!description!%$ in sample to fill in

; by default ahk_generate_trigger=0   QuickPanel_Changes="0|0|0|0|0|0|0|0|0|0|0|0|"
QuickPanel_Changes:=""
Loop, %quickpanel_max_lines%
  QuickPanel_Changes .= QuickPanel_Changes%A_Index% "|"
; MsgBox, % "generateahk  " QuickPanel_Changes ; debug
ahk_generate(ahk_generate_trigger, QuickPanel_Changes,0,"0|0|0|0|0|0|0|0|0|0|0|0|") ; Сгенерируем изменения в скрипте

Reload ; reload this instance of script to changes take place
return

; Кнопка "Titles", БД подсказок (Соответствие имени .exe файла приложения названию окна этого приложения) (Окно Settings в Experimental Functionality)
windowtitledatabase:
Gui, 5:Destroy
Gui, 5:Add, Text,, Window Titles Data Base
Gui, 5:Add, Text,, 
temp:=".exe  ->  "
Loop %WTArray_Count%
{
  StringReplace, str, WindowTitleArray%A_Index%,:,%temp%
  Gui, 5:Add, Text,, % A_Index "    " str
}
Gui, 5:Show, w400 h350, Create Quick Panel - Title's List
return

; ## Open file dialog
openfileapp:
FileSelectFile, newpath
; Try to autofill right field
If (newpath != "")
{
  GuiControl,3:, %A_GuiControl%v, %newpath% ; In left text field will display the path to your application
  
  ; In right text field will display the possible window title of your application
  StringGetPos, position_number, newpath,\,r1 ; Looking for "\" symbol's position at the end of the string
  StringTrimLeft, str, newpath, position_number+1 ; cutting off the path, in str will be only the name of app
  StringTrimRight, newpath, str, 4 ; cut off ".exe"

  position_number:=-1  
  Loop %WTArray_Count% ; Looking for possible titles in WT DataBase (WT - Window Titles)
  {
    StringGetPos, position_number, WindowTitleArray%A_Index%,%newpath%
    If(position_number!= -1)
    {
      StringGetPos, position_number, WindowTitleArray%A_Index%,: ; Looking for ":" symbol's position
      StringTrimLeft, str, WindowTitleArray%A_Index%, position_number+1 ; str - Window title
      GuiControl,3:, %A_GuiControl%t, %str% 
    }
  }
}
return

; Кнопка "Cancel", выход из окна (Окно Settings в Experimental Functionality)
exitdialogahk:
Gui, Cancel
return

; ## Загрузка памяти RAM (used RAM load.ahk from library catalog) web-link http://www.autohotkey.com/board/topic/35785-find-system-memory-ram/
UpdateMemStatus:
memload:=MemGetStats() 
if(memload>90)
    TrayTip, quickpanel Warning Message, RAM load = %memload%`%`nЕсли загрузка памяти достигнет 100`% будут дикие тормоза`, система встанет в лаге и выпадет в BSOD`, так как нет файла подкачки.`nНемедленно выгрузите приложения`, занимающие много памяти!, 2
return

MemGetStats() 
{ 
  ; See: 
  ; http://msdn.microsoft.com/library/default.asp?url=/library/en-us/memory/base/memorystatus_str.asp 

  VarSetCapacity(memorystatus, 4+4+4+4+4+4+4+4) 
  success := DllCall("kernel32.dll\GlobalMemoryStatus", "uint", &memorystatus) 
  stats := ReadInteger(&memorystatus,4,4, false)                          ; Memory Load (Percentage of memory in use) 
   
  return stats 
} 

ReadInteger( p_address, p_offset, p_size, p_hex=true ) 
{ 
  value = 0 
  old_FormatInteger := a_FormatInteger 
  if ( p_hex ) 
    SetFormat, integer, hex 
  else 
    SetFormat, integer, dec 
  loop, %p_size% 
    value := value+( *( ( p_address+p_offset )+( a_Index-1 ) ) << ( 8* ( a_Index-1 ) ) ) 
  SetFormat, integer, %old_FormatInteger% 
  return, value 
}
; ##

str_clean_spaces_tabs(ByRef str_temp) ; Удалим знаки табуляции везде и пробелы в конце/начале строки
{
 StringReplace, str_temp, str_temp,%A_Tab%,, All ; Удалим знаки табуляции
 ; Убираем пробелы в начале строки
 position_L=0
 While position_L==0
 {
   StringGetPos, position_L, str_temp,%A_Space%,L
   if(ErrorLevel==1) ; Не нашли ни одного пробела в строке
     break
   if (position_L ==0)
     StringTrimLeft, str_temp, str_temp, 1
 }
 ; Убираем пробелы в конце строки
 position_R=0
 While position_R==0
 {
   StringGetPos, position_R, str_temp,%A_Space%,R
   if(ErrorLevel==1) ; Не нашли ни одного пробела в строке
     break
   if (position_R == StrLen(str_temp)-1)
     StringTrimRight, str_temp, str_temp, 1
 }
  return 0
}

;####$$$$!!!! Generated section !!!!$$$$#### Don't touch it! # Start
;<hot_keys marker="^-_5@)+Hj*sDp{a2mark" id_count="1" id_next="45" hash="0">
;<template id="44" template="quickpanelsample.txt" hash="0">
; - App1 ; Description
#2:: ; HotKey
IfWinExist, - App1 ; Window Title
{
IfWinActive, - App1 ; Window Title
WinMinimize
else
WinActivate
return
}
else
    Run "C:\Users\Алексей\Downloads\greyforum_quickpanel\readme.txt" ; Program path
return
;</template>
;</hot_keys>
;####$$$$!!!! Generated section !!!!$$$$#### Don't touch it! # End

^F7:: ; Окно Settings в Experimental Functionality
Gui, 3:Show, x45 y142 h737 w1100, Create Quick Panel
; WinActivate, Create Quick Panel
 ; Sleep, 200
; PostMessage 0x50, 0, %en%,, A ; switch keyboard layout to English (send command to current window)
Loop, %quickpanel_max_lines%
{
  GuiControlGet, before_change%A_Index%fk, 3:,fileplaceapp%A_Index%fk
  str_clean_spaces_tabs(before_change%A_Index%fk)
  If(before_change%A_Index%fk=="Type HotKey")
    before_change%A_Index%fk:=""
  GuiControlGet, before_change%A_Index%v, 3:,fileplaceapp%A_Index%v
  str_clean_spaces_tabs(before_change%A_Index%v)
  GuiControlGet, before_change%A_Index%t, 3:,fileplaceapp%A_Index%t
  str_clean_spaces_tabs(before_change%A_Index%t)
}
return

 ^!r::Reload ; Assign Ctrl-Alt-R as a hotkey to restart the script

; Pressing Win+X once will suspend hot keys of the script. Pressing it again will unsuspend.
#x::
Suspend  ; suspend/unsuspend AHK script IMPORTANT: must be first string after "#a::", so "#a::" will not be suspended
if (A_IsSuspended==0) 
{
  Gui, Destroy
  Gui, font, s55 cGreen, Verdana ; font size
  Gui, Color, Red
  Gui +LastFound  ; Make the GUI window the last found window for use by the line below.
  WinSet, TransColor, Red
  Gui -Caption
  Gui, Add, Text,, ON
  Gui, Show
  Sleep, 500
  Gui, Destroy
  return
}
else
{
  Gui, Destroy
  Gui, font, s55 cRed, Verdana ; font size
  Gui, Color, Green
  Gui +LastFound  ; Make the GUI window the last found window for use by the line below.
  WinSet, TransColor, Green
  Gui -Caption
  Gui, Add, Text,, OFF
  Gui, Show
  Sleep, 500
  Gui, Destroy
  return
}



; ------------- Посчитаем количество нажатий различных комбинаций клавиш по логам
;  логи хранятся A_ScriptDir\Key Catcher\log.txt
^F2:: ; При каждом нажатии Ctrl+F2 комбинации (другими словами, переоткрытии окна) будет пересчитываться статистика
DateTime_Trigger = 0 ; Ловим начало снятия лога во времени 0-еще не нашли, 1-уже нашли
HotKeys_Count = 0 ; Число элементов массива "Сочетания клавиш"
HotKeysStats_Count = 0 ; Число элементов массива "Число нажатий клавиш"
Loop, Read, %log_path%
{
  temp := A_LoopReadLine
  ; Исключим из логов позиции курсора мыши и прокрутку 
  StringGetPos, position_number, temp,click,r1 ; курсор
  temp_ErrorLevel:=ErrorLevel
  StringGetPos, position_number, temp,scroll,r1 ; прокрутка (колесико мыши)
  if(ErrorLevel==1&&temp_ErrorLevel==1)
  {  
  if (DateTime_Trigger==0) ; Время начала периода сбора логов  - datetime_start
  {
     ; Пример вида строки лога: "temp = 31/01/2014 11:55:22 - {Win + 2}"
     StringLeft, datetime_start, temp, 19 ; Берем 19 символов слева из строки (это дата и время нажатия клавиши)
     DateTime_Trigger=1
  }
  ;Log_Count+=1
  datetime_end:=temp ; сохраним возможное время окончания сбора логов (после цикла почистим его и преобразуем)
  StringGetPos, position_number, temp,{,r1 ; найдем "{", чтобы отрезать левую часть строки (секция с временем нажатия)
  StringTrimLeft, temp, temp, position_number+1 ; вырежем время нажатия клавиши
  StringTrimRight, temp, temp, 1 ; удаляем "}" в конце строки
  
  ; Заполняем массив всех возможных сочетаний клавиш, но без повторов
  if (HotKeys_Count>0)
  {
    i=1
    While (i<=HotKeys_Count)&&(HotKeys%i%!=temp) ; Если такое сочетание клавиш уже было, то i<=HotKeys_Count, нет, то >
      i+=1
    if (i>HotKeys_Count)
    {
      HotKeys_Count+=1
      HotKeys%HotKeys_Count% := temp
      HotKeysStats_Count+=1
      HotKeysStats%HotKeysStats_Count% = 1 ; Одно вхождение
    }
    else
    {
      HotKeysStats%i% += 1 ; Нашли еще одно вхождение в логе уже найденного сочетания клавиш из массива HotKeys
    }
  }
  else ; На случай первого элемента массива
  {
    HotKeys_Count+=1 
    HotKeys%HotKeys_Count% := temp
    HotKeysStats_Count+=1
    HotKeysStats%HotKeysStats_Count% = 1 ; Одно вхождение
  }
  
  } 
} 

 ; Пример вида строки лога: "temp = 31/01/2014 11:55:22 - {Win + 2}"
 StringLeft, datetime_end, datetime_end, 19 ; Берем 19 символов слева из строки (это дата и время нажатия клавиши)
 ; Теперь в datetime_start, datetime_end хранится период снятия лога
 temp:= % datetime_start " - " datetime_end
 GuiControl,2:, vDateTime,%temp% ; Отобразим в окне период снятия логов

 Gui, 2:Show, w700 h700, AutoHotKey - Key Pressed Statistics ; Элементы окна подготовлены в начале скрипта
return

; Смотрим результаты вычисления статистики нажатия клавиш
HotKey_display:
Gui, 2:Submit, NoHide 
LV_Delete() ; Почистим предыдущий список клавиш
Loop %HotKeys_Count%
{
  temp := HotKeys%A_Index% ; Горячая клавиша
  temp2 := HotKeysStats%A_Index% ; Сколько раз ее нажали (за период ведения лога)
  temp_err2=1 ; В окне статистики есть 2 поля vKey, они позволяют сузить круг поиска клавиш
  if (vKey2!="") ; Если второе поле не пустое
  {
    StringGetPos, position_number, temp,% vKey2,r1 ; Проверим есть ли данное поле в названии сочетания клавиши
    temp_err2:= ErrorLevel
  }
  StringGetPos, position_number, temp,% vKey1,r1 ; есть ли первое поле
  if (temp2>vMin && (ErrorLevel==0 || temp_err2==0)) 
    LV_Add("",temp,temp2) ; Заполняем построчно список клавиш
}
 LV_ModifyCol()  ; Auto-size each column to fit its contents. 
 LV_ModifyCol(2, "Integer")  ; For sorting purposes, indicate that column 2 is an integer.
 Gui, 2:Show, w700 h700, AutoHotKey - Key Pressed Statistics
return
; -------------

; ## Help page
; display current hot keys help
^F1::
MsgBox, 0, Help page (Hot Keys & Timers), 
(
 Ctrl + F1  -  This help
 Ctrl + F4  -  Experimental Functionality Help
 Ctrl-Alt-R - Reload this AHK script
 Win + X - Pause/Unpause hot keys
_______________________
 Ctrl + F2 - Hot Keys Log Statistics
 Ctrl + F7    -  Quick Panel Settings
_______________________


Timers:

     Check RAM load (warning trigger >90`%) - every 5 minutes

Script location: %A_ScriptFullPath%
Created by Li, 22.05.2014, modified 5.08.2014
), 3600
return


; #Include %A_ScriptDir%\ahk_library.ahk ; Подключаем библиотеку с описанием функций для Quick Panel (tag_modify(), IfPathIsBad())
; Добавление/удаление/изменение тегов в секциях, получение данных о тегах (Универсальная функция)
tag_modify(tag_action, section_name, tag_name, ByRef tag_id=0, tag_hash=0, ByRef tag_data="TEST") 
; добавление/удаление нового тега в заданную секцию, tag_name также определяет структуру тела самого тега
{ ; По tag_name также автоматически может быть выбрано и имя секции (определенные теги, могут быть только в определенных секциях)
  global marker ; берем глобальное значение marker
  global ahk_parsing_file ; Какой файл будем парсить
  global ahk_script_settings ; Путь к файлу настроек скрипта (quickpanel.ini)
  return_value=0
  
  ; MsgBox, DEBUG ; debug
  if (tag_action=="add"||tag_action=="delete"||tag_action=="get")
  {
  if !FileExist(ahk_parsing_file)
  {
    MsgBox % "Нет файла для парсинга " ahk_parsing_file "      Error code = 1"
    return -1
  }
  if (section_name==tag_name)
    {
      MsgBox % "Имя тега не может совпадать с именем секции section_name = " section_name "    tag_name = " tag_name "      Error Code = 5" 
      return -5
    }
    ; Часть текста AHK скрипта до секции будет в массиве FirstPart%FirstPart_Count%
    ; Заголовок секции будет в строке Section_head
    ; Тело секции из файла AHK скрипта в массив Section_body%Section_Count%, чтобы потом изменить
    ; Часть текста AHK скрипта после секции будет в массиве ThirdPart%ThirdPart_Count%
   Search_Count=0
    section_found=0 ; 0 - секцию не нашли, 1 - секция найдена
    section_close_tag_found=0 ; 0 - закрывающий тег не нашли, 1 - закрывающий тег найден
    Section_head="" ; Первая строка секции <section_name ...
    section_open_tag:=% ";<" section_name " marker=""" marker """ " ; Открывающий тег
    section_close_tag:=% ";</" section_name ">" ; Так выглядит закрывающий тег
    Section_Count=0 ; Количество строк в секции
    ThirdPart_Count=0
    test_ahk:="" ; в эту переменную наберем все то, что мы хотим записать в файл (64 Мб макс размер ее, но можно увеличить, но можно увеличить с помощью #MaxMem Megabytes (от 1 до 4095 Мб))
    Loop, Read, %ahk_parsing_file% ; test.ahk 
    {
    ; Ищем вхождение marker в файл AHK скрипта  и проверяем, если в той же строке имя тега
     if(section_found==0)
    {
        Search_Count+=1
        position_section_name=0
        position_section_name:=InStr(A_LoopReadLine,section_open_tag)
         if(position_section_name>0) ; Нашли заголовок секции
         {
           Section_head:=A_LoopReadLine ; Сохраним строку начала секции <section_name ...
           section_found=1
         }
         else ; Запомним очередную строчку перед началом секции
             test_ahk .= A_LoopReadLine "`n"
    }
     else
     {
      ; И так Search_Count -     номер строки в файле, где есть marker и tag_name
      ; position_marker - номер символа (начиная с 1), где находится значение marker в этой строке
      ; position_tag_name - номер символа (начиная с 1), где находится tag_name в этой строке
      ; Проверим действительно ли это секция
    
      ; Запомним все строки до закрывающего тега </tag_name>
      if(section_close_tag_found==0)
      {
      if(A_LoopReadLine==section_close_tag)
      {
        section_close_tag_found=1
      }
      else
      {
        Section_Count+=1
        Section_body%Section_Count%:=A_LoopReadLine
      }
      }
      else
      { ; Запомним очередную строчку за закрывающим тегом
      ThirdPart_Count+=1
      ThirdPart%ThirdPart_Count%:=A_LoopReadLine
      }
     }
    }
    ;MsgBox % "Search_Count= " Search_Count ", position_marker= " position_marker ", marker= " marker ; debug
    if (position_marker==0)
    {
      MsgBox % "Не могу найти marker = " marker "      Error Code = 2" 
      return -2
    }
    if (position_section_name==0)
    {
      MsgBox % "Не могу найти section_name = " section_name "      Error Code = 3"
      return -3
    }
    if (section_close_tag_found==0)
    {
      MsgBox % "Не могу найти закрывающий тег для section_name = " section_name "      Error Code = 4"
      return -4
    }
    ; Теперь имеем:
    ; Section_head - заголовок секции <section_name ...
    ; Section_body%Section_Count% - массив строк тела секции, закрывающий тег нам не нужен
    
    ; Заглушка - Проверка целостности секции (на будущее реализовать проверку)
    
    ; Секция проверена, hash совпадают, теперь нам надо вставить в нее еще один тег
    
    ; ####   Работа с секцией
    ahk_script_changes=0 ; Не нужно делать изменения в файле AHK скрипта
    tag_open_position=0
    tag_close_position=0
    
    ; Вытащим значение id_count из головы секции
    str_temp:=" id_count="""
    StringGetPos, position, Section_head,%str_temp%,l1
    If (ErrorLevel==0)
    {
      temp:=position+11 ; Позиция и длина самой записи [ id_count="]
      StringTrimLeft, str_temp, Section_head,%temp%
    }
    else
    {
      MsgBox, % "Не могу найти id_count в Section_head = " Section_head "      Error Code = 12"
      return -12
    }
    StringGetPos, position, str_temp,",l1
    If (ErrorLevel==0)
        StringLeft, id_count, str_temp,%position%
    else
    {
      MsgBox, % "Не могу найти символ "" в модифицированном Section_head = " Section_head "      Error Code = 13"
      return -13
    }
    
    ; Вытащим значение id_next из головы секции
    str_temp:=" id_next="""
    StringGetPos, position, Section_head,%str_temp%,l1
    If (ErrorLevel==0)
    {
      temp:=position+10 ; Позиция и длина самой записи [ id_next="]
      StringTrimLeft, str_temp, Section_head,%temp%
    }
    else
    {
      MsgBox, % "Не могу найти id_next в Section_head = " Section_head "      Error Code = 14"
      return -14
    }
    StringGetPos, position, str_temp,",l1
    If (ErrorLevel==0)
        StringLeft, id_next, str_temp,%position%
    else
    {
      MsgBox, % "Не могу найти символ "" в модифицированном Section_head = " Section_head "      Error Code = 15"
      return -15
    }
    
    ; ### Здесь описаны действия с тегами
    If IsLabel("Case-" . tag_action) ; Создаем аналог оператора CASE
    Loop 1
    {
    Goto Case-%tag_action%
    
    Case-add: ; Добавление тега в секцию
    
    If IsLabel("Case-add-" . tag_name) ; Создаем вложенный CASE для tag_action="add"
    Loop 1
    {
    Goto Case-add-%tag_name%
    
    Case-add-TEST_TAG:
    ; TEST
    ; Добавляем данные в заголовок секции
    ;<current_settings marker="TEST" id_count="0" id_next="1" hash="0">
    id_count+=1 ; Число тегов увеличиться на 1
    id_next++
    hash:="0"
    Section_head:=% ";<" section_name " marker=""" marker """ id_count=""" id_count """ id_next=""" id_next """ hash=""" hash """>"
    
    ; Добавляем тег в тело секции
    Section_Count+=1
    tag_id:=id_next-1
    template_name:="None"
    tag_hash:="0"
    Section_body%Section_Count%:=% ";<" tag_name " id=""" tag_id """ template=""" template_name """ hash=""" tag_hash """>"
    Section_Count+=1
    Section_body%Section_Count%:=% "tag_data_test:=""" tag_data """"
    Section_Count+=1
    Section_body%Section_Count%:=% ";</" tag_name ">"
    ahk_script_changes=1
    
    break
    
    Case-add-template: ; Добавление тега согласно шаблону
     ; Данные представлены в следующем виде tag_data="file_template_name|number_of_values|value1|value2|value3|value4" 
     ; Для примера Используется шаблон C:\i_files\i_scripts\templates\quickpanelsample.txt
     ; Тег "quickpanelsample" содержит следующие значения: Имя файла шаблона|Число параметров|Описание|Горячая клавиша|Заголовок окна|Полный путь к программе
    
    
    ; Вытащим имя файла шаблона (ex. quickpanelsample.txt)
    StringGetPos, position, tag_data,|,l1
    If (ErrorLevel==0)
      StringLeft, file_template_name, tag_data, position
    else
    {
      MsgBox, % "Не могу извлечь имя файла шаблона из tag_data = " tag_data "      Error Code = 9"
      return -9
    }
    
    ; Вытащим число параметров для данного template
    StringGetPos, position_2, tag_data,|,l2
    If (ErrorLevel==0)
      StringMid, valueN_array_Count, tag_data, position+2, position_2-position-1
    else
    {
      MsgBox, % "Не могу извлечь число параметров шаблона из tag_data = " tag_data "      Error Code = 10"
      return -10
    }
    
    ; Сформируем массив значений для шаблона (valueN_array), далее $%!value1!%$ будет соответствовать valueN_array1
    position_2:=StrLen(tag_data)
    Loop, %valueN_array_Count%
    {
      StringGetPos, position, tag_data,|,r%A_Index% ; Значения value ищем с конца строки tag_data
      If (ErrorLevel==0)
      {
      StringMid, str_temp, tag_data, position+2, position_2-position-1 
      temp_index:=valueN_array_Count-A_Index+1
      ; MsgBox, temp_index = %temp_index% +++ str_temp = %str_temp% ; debug
      valueN_array%temp_index%:=str_temp
      position_2:=position
      }
      else
      {
        MsgBox, % "Не могу извлечь " A_Index " параметр из tag_data = " tag_data "      Error Code = 11"
        return -11
      }
    }
    
    ; Считаем шаблон в память
    FileRead, template_in_memory, *m1024 %A_ScriptDir%\templates\%file_template_name%
    IF(ErrorLevel)
    {
        MsgBox, % "Не могу загрузить шаблон " A_ScriptDir "\templates\" file_template_name "      Error Code = 19"
        return -19
    }
    
    ; Заменим значения в шаблоне на значения из массива valueN_array
    Loop, %valueN_array_Count%
    {
      valueN=$`%!value%A_Index%!`%$
      valueN_paste:=valueN_array%A_Index%
      StringReplace, template_in_memory, template_in_memory, %valueN%, %valueN_paste%, All
    }
    ; Теперь код согласно шаблону file_template_name сформирован
    
    ; Добавляем тег в тело секции
     ; MsgBox % template_in_memory
    id_count+=1 ; Число тегов увеличиться на 1
    hash:="0"
    If(tag_id=="NEW") ; если новый тег добавляется, то формируем его ID, если восстанавливаем, то берем старый ID из tag_id
    {
      id_next++
      tag_id:=id_next-1
    }
    Section_head:=% ";<" section_name " marker=""" marker """ id_count=""" id_count """ id_next=""" id_next """ hash=""" hash """>"
    template_name:=file_template_name ; Используемый шаблон
    tag_hash:="0"
    Section_Count++
    Section_body%Section_Count%:=% ";<" tag_name " id=""" tag_id """ template=""" template_name """ hash=""" tag_hash """>"
    Section_Count++
    Section_body%Section_Count%:=template_in_memory ; тело тега
    Section_Count++
    Section_body%Section_Count%:=% ";</" tag_name ">"
    ahk_script_changes=1 ; Триггер на внесение изменений в файл
    
    template_in_memory= ; Освободим память
    break
    } else {
    MsgBox % "Не умею добавлять такой тег tag_name = " tag_name "      Error Code = 6"
    return -6 
    }
      break
    
    Case-delete: ; Удаление тега tag_name с tag_id из секции
    ;MsgBox Зашли в удаление; debug
    
    id_count-=1 ; Число тегов уменьшится на 1
    hash:="0"
    Section_head:=% ";<" section_name " marker=""" marker """ id_count=""" id_count """ id_next=""" id_next """ hash=""" hash """>"
    
    ; Ищем тег для удаления
    tag_open_tag:=% ";<" tag_name " id=""" tag_id """ " ; Открывающий тег
    tag_close_tag:=% ";</" tag_name ">"
    tag_found=0
    tag_open_position=0
    tag_close_position=0
    Loop %Section_Count%
    {
      if (tag_found==0)
      {
        tag_found:=InStr( Section_body%A_Index% ,tag_open_tag)
         if(tag_found>0) ; Нашли заголовок секции
         {
           Tag_head:= Section_body%A_Index% ; Сохраним строку начала тега <tag_name ...
           tag_open_position = %A_Index%
         }
      }
      else 
      if (Section_body%A_Index%==tag_close_tag)
      {
        tag_close_position = %A_Index%
        break
        }
    }
    if(tag_open_position>0&&tag_close_position>0)
    {
      ahk_script_changes=1
      ;MsgBox % "tag_open_position = " tag_open_position "tag_close_position = " tag_close_position "Section_Count = " Section_Count ; debug
      }
      else
      {
      MsgBox % "Не нашел открывающий и закрывающий тег для tag_name = " tag_name "      Error Code = 8"
      return -8
      }
    break
    
    Case-get: ; Извлечение данных из уже сгенерированных тегов
    
    If IsLabel("Case-get-" . tag_name) ; Создаем вложенный CASE для tag_action="get"
    Loop 1
    {
    Goto Case-get-%tag_name%
    
    Case-get-template:
    ; Извлечение запросов на авгенерируемые теги из quickpanel.ini (Переменная ahk_script_settings)
    ; Содержание секции без открывающего и закрывающего тега находится в массиве Section_body%Section_Count%
    ; В tag_data подразумевается, что находится число строк с параметрами тега. Тег-запрос выглядит следующим образом:

;<template id="2" template="quickpanelsettings.txt" hash="0">
; section=hot_keys
; tag_id=1
; tag_data
; - App1
; ^F11
; - App1
; - App1
; "C:\Program Files (x86)\Notepad++\functionList.xml"
;</template>

; Итого имеем: 5 строк с параметрами и 5 строк структуры (открывающий и закрывающий теги - 2 строки и 3 строки данные
; о местонахождении добавленного тега). Число строк с параметрами отличается в зависимости от типа тегов
; Замечание: В каждой секции могут быть только однотипные теги-запросы (мб в будущем реализую для разных типов)
; Данное замечание не относится к тегам (только к тегам-запросам)

    ; Проверим совпадает ли общее число тегов в секции (id_count) с фактическим числом строк в секции (всего теги содержат
    ; одинаковое количество строк
    tag_length_params:=tag_data
    tag_length:=5+tag_length_params
    If(id_count*tag_length!=Section_Count)
    {
    MsgBox % "Реальное число тегов в секции не соответствует общему числу тегов в секции id_count = " id_count "      Error Code = 17"
    return -17 
    }
    
    return_value:= id_count ; возвращаем общее число тегов-запросов
    tag_data:="" ; В tag_data будут возвращаться параметры запросов добавленных тегов
    tag_id:="" ; В tag_id будет возвращен список id тегов-запросов
    Loop, %id_count%
    {
      ; Берем id тега-запроса между первой и второй кавычкой (в дальнейшем переписать, чтобы брать точно из id="23" )
      i:=1+(A_Index-1)*tag_length
      str_temp:=Section_body%i%
      StringGetPos, position_2, str_temp,`",L1
      StringGetPos, position_3, str_temp,`",L2
      StringMid, str_temp, str_temp, position_2+2, position_3-position_2-1
      tag_id .= str_temp "|"
      
      i:=2+(A_Index-1)*tag_length
      str_temp:=Section_body%i%
      StringTrimLeft, str_temp, str_temp, 10 ; Убираем начало строки "; section=" 
      tag_data .= str_temp "|"
      i:=3+(A_Index-1)*tag_length
      str_temp:=Section_body%i%
      StringTrimLeft, str_temp, str_temp, 9 ; Убираем начало строки "; tag_id=" 
      tag_data .= str_temp "|"
      
      B_Index:=A_Index
      Loop, %tag_length_params%
      {
       i:=5+A_Index-1+(B_Index-1)*tag_length
       str_temp:=Section_body%i%
       StringTrimLeft, str_temp, str_temp, 2 ; Убираем начало строки "; " 
       tag_data .= str_temp "|"
      }
    }
    ; Теперь в tag_data один за другим находятся запросы на добавленные теги
    ; MsgBox, DEBUG get template Имеем tag_data = %tag_data% ; debug
    break
    
    Case-get-status: 
    return_value:= id_count ; возвращаем общее число сгенерированных тегов (Для проверки на пустоту секции)
    
    If(ahk_parsing_file==ahk_script_settings) ; Парсим файл настроек скрипта
    { ; в tag_id будет возвращен список id тегов для восстановления
    tag_length_params:=tag_data
    tag_length:=5+tag_length_params
    If(id_count*tag_length!=Section_Count)
    {
    MsgBox % "Реальное число тегов в секции не соответствует общему числу тегов в секции id_count = " id_count "      Error Code = 17"
    return -17 
    }
    tag_id:="" ; В tag_id будет возвращен список id тегов (для случая восстановления, нужны id тегов)
    Loop, %id_count%
    {
      i:=3+(A_Index-1)*tag_length
      str_temp:=Section_body%i%
      StringTrimLeft, str_temp, str_temp, 9 ; Убираем начало строки "; tag_id=" 
      tag_id .= str_temp "|"
    }
    }
    break
    } else {
    MsgBox % "Не умею извлекать данные для такого тега tag_name = " tag_name "      Error Code = 16"
    return -16 
    }
      break
    
    } else {
    MsgBox % "Не умею делать действие tag_action = " tag_action "      Error Code = 7"
    return -7
    }
    ; ####   Конец работы с секцией
    
    ; Теперь нам нужно рассчитать новый hash для секции и добавить его в строку Section_head
    
    ; Заглушка - Проверка целостности секции (на будущее реализовать проверку)
    
    ; Часть текста AHK скрипта до секции будет в массиве FirstPart%FirstPart_Count%
    ; Заголовок секции будет в строке Section_head
    ; Тело секции из файла AHK скрипта в массив Section_body%Section_Count%, чтобы потом изменить
    ; Часть текста AHK скрипта после секции будет в массиве ThirdPart%ThirdPart_Count%
   
   ;time=%a_dd%/%a_mm%/%a_yyyy% %a_hour%:%a_min%:%a_sec%
   
   ;MsgBox Пишем в файл ; debug
   ; ####  Запишем проведеные изменения в файл 
   if (ahk_script_changes>0)
   { ; Текст до начала секции уже находится в test_ahk
   test_ahk .= Section_head "`n" ; заголовок секции
   Loop %Section_Count% ; тело секции
   {
     if (A_Index < tag_open_position || tag_close_position < A_Index) ; Для операции удаления тега из секции
     { ; Просто не пишем в файл те части секции, которые нужно удалить
       temp_string:= Section_body%A_Index%
       test_ahk .= temp_string "`n" 
      }
   }
   temp_string:=% ";</" section_name ">" ; добавим закрывающий тег секции
   test_ahk .= temp_string "`n" 
   Loop %ThirdPart_Count% ; текст после секции
   {
    temp_string:= ThirdPart%A_Index%
     test_ahk .= temp_string "`n" 
   }
   ; Проверка, чтобы не было временного файла с нужным нам именем
   IfExist, %A_ScriptDir%\temp.ahk 
     FileDelete, %A_ScriptDir%\temp.ahk
    ; Записываем все изменения 
  ; Запись идет в файл именно одной переменной, так как каждую строчку массива сохранять в файл занимает гораздо дольше по времени
   fileappend, %test_ahk%, %A_ScriptDir%\temp.ahk
   If(ErrorLevel!=0)
     MsgBox Error fileappend
   filemove, %A_ScriptDir%\temp.ahk, %ahk_parsing_file%, 1
   If(ErrorLevel!=0)
     MsgBox % "Error filemove() ahk_parsing_file = " ahk_parsing_file
   ; emptymem()
    test_ahk= ; Очистка памяти
   ; MsgBox Файл Готов ; debug
   }
   ; ####  Файл записан
   
   return %return_value%
  }
  else
  {
    MsgBox % "Не умею выполнять такое действие tag_action = " tag_action "      Error Code = 18"
    return -18 
  }
  
}

; Запись сгенерированных тегов в файл скрипта (quickpanel.ahk) и тегов-запросов в файл настроек скрипта (quickpanel.ini)
ahk_generate(ahk_generate_trigger=0, QuickPanel_Changes="0|0|0|0|0|0|0|0|0|0|0|0|", Recovery_bit=0, tag_id_list="0|0|0|0|0|0|0|0|0|0|0|0|")
{
global

; Проверка на открытие файла скрипта (Путь к файлу скрипта в A_ScriptFullPath) - quickpanel.ahk
quickpanelahkscript:= FileOpen(A_ScriptFullPath,"rw") ; open AHK script file
if !IsObject(quickpanelahkscript)
{
    MsgBox Can't open AHK script "%A_ScriptFullPath%".
    return
}
quickpanelahkscript.Close() ; Close AHK script file
; Проверка на открытие файла настроек скрипта (Путь к файлу настроек в ahk_script_settings) - quickpanel.ini
quickpanelahkscript:= FileOpen(ahk_script_settings,"rw") ; open AHK script file
if !IsObject(quickpanelahkscript)
{
    MsgBox Can't open settings for AHK script "%ahk_script_settings%".
    return
}
quickpanelahkscript.Close() ; Close AHK script file

; Примеры tag_modify()
; tag_modify("add", "TEST", "TEST_TAG", , , "TEST TEST TEST") 
; tag_modify("delete", "TEST", "TEST_TAG",1,0,0) 
; tag_modify("add", "TEST", "template", , , "quickpanelsample.txt|5|TEST|^6|- Opera|- Opera|""c:\Opera.exe""")
; tag_modify("delete", "TEST", "template",6,0,0) 

; Связность:
; Клавиша/HotKey - value2 - fileplaceapp%A_Index%fk
; Путь к программе/Path - value5 - fileplaceapp%A_Index%v
; Заголовок окна/Windows Title - value3,value4,value1 - fileplaceapp%A_Index%t

; !!! Каждый сгенерированный тег дублируется соответствующей записью о нем в файле настроек скрипта quickpanel.ini 
; (Путь к файлу в ahk_script_settings) !!!
Loop, %quickpanel_max_lines%
{
  position:=(A_Index-1)*2+1
  StringMid, QuickPanel_Changes%A_Index%, QuickPanel_Changes, position, 1
}  
;  MsgBox, % "ahk_generate_trigger = " ahk_generate_trigger " QuickPanel_Changes1 = " QuickPanel_Changes1 " QuickPanel_Changes2 = " QuickPanel_Changes2 " quickpanel_max_lines = " quickpanel_max_lines ; debug
If(ahk_generate_trigger==1)
{
Loop, %quickpanel_max_lines% ; максимум можно 12 горячих клавиш назначить
{
If(QuickPanel_Changes%A_Index%==1) ; Если в данной строке есть изменения, то тег и тег-запрос выше были удалены, либо
{ ; их не было изначально, то есть нам нужно добавить тег и тег-запрос
GuiControlGet, str_path, 3:,fileplaceapp%A_Index%v ; Путь к программе
str_clean_spaces_tabs(str_path) ; Удалим знаки табуляции везде и пробелы в конце/начале строки

If(!IfPathIsBad(str_path, error_code, error_description))
{ ; Путь действителен, добавим тег по шаблону quickpanelsample.txt
 str_path="%str_path%"
 GuiControlGet, str_windowtitle, 3:,fileplaceapp%A_Index%t ; Window Title
 str_description:=str_windowtitle ; На данный момент описание берется из Windows Title
 GuiControlGet, str_hotkey, 3:,fileplaceapp%A_Index%fk
 tag_data_right:=str_description "|" str_hotkey "|" str_windowtitle "|" str_windowtitle "|" str_path
 tag_data:="quickpanelsample.txt|5|" tag_data_right
 If(Recovery_bit=="0")
   tag_id:="NEW"
 else
 {
   ; Восстановление: здесь в tag_id мы должны получить id тега для его восстановления, список id нам передан в tag_id_list
   StringGetPos, position_id, tag_id_list,|,L%A_Index%
   temp:=A_Index-1
   StringGetPos, position_id_previous, tag_id_list,|,L%temp%
   If(ErrorLevel==1)
     position_id_previous=-1
   StringMid, tag_id, tag_id_list, position_id_previous+2, position_id-position_id_previous-1
    ; MsgBox % "tag_id = " tag_id
 }
 If(tag_modify("add", "hot_keys", "template", tag_id, , tag_data)<0)
 {
   MsgBox, Ошибка при добавлении тега tag_data = %tag_data%
   continue ; Пробуем следующую строчку из 12
 }
 ; MsgBox, tag_id = %tag_id%  tag_data = %tag_data% ; debug
 
If(Recovery_bit=="0") ; При восстановлении тегов в секции, не нужно добавлять ничего в файл настроек скрипта, так как восстановление
{ ; идет из этого файла
 ; Запишем запрос на добавление тега в настройки quickpanel.ini (ahk_script_settings) по тому же принципу автогенерируемых секций
 ; Запрос будет в виде строки str_tag = [секция|tag_id|tag_data]
 ahk_parsing_file:=ahk_script_settings
 
 ; Запись запросов на автогенерируемые теги в quickpanel.ini (ahk_script_settings)
 tag_data:="quickpanelsettings.txt|7|hot_keys|" tag_id "|" tag_data_right
 ; MsgBox, XXX tag_data = %tag_data% ; debug
 If(tag_modify("add", "quickpanel_settings", "template", "NEW", , tag_data)<0)
 {
   MsgBox, Ошибка при добавлении тега tag_data = %tag_data% в файл %ahk_script_settings%. Попробуем откатить изменения...
   ahk_parsing_file:=A_ScriptFullPath ; вернем на место
   If(tag_modify("delete", "hot_keys", "template", tag_id,0,0)<0) ; Придется удалить только что добавленный тег, так как настройки
   { ; для него не сохранились в файле quickpanel.ini (ahk_script_settings)
   MsgBox, Critical Error! Не удалось удалить только что сгенерированный тег. Обратитесь к разработчику tag_id = %tag_id% section = hot_keys
   }
 }
 
 ahk_parsing_file:=A_ScriptFullPath ; вернем на место
}
}
}
}
} ; if
}

; Проверка на пустоту, ошибки в строке пути к файлу/папке
; >=2 error codes
IfPathIsBad(str_temp, ByRef error_code, ByRef error_description)
{
    error_code=0
    error_description:=""
    if(str_temp=="") 
      return true
   StringGetPos, position, str_temp,<,L
   if(ErrorLevel!=1) ; Нашли символ "<", тобиш вместо пути к файлу имеем тег
   {
     error_code=2
      error_description:="Не найден путь к файлу. Нужно сделать так: строка с тегом <tag_name>, ниже строка с путем к файлу/папке, закрывающий тег не нужен"
     return true
    }
    return false ; true
}

Суть функциональности скрипта заключается в встраивании в свой код (секция hot_keys) кусков кода согласно шаблону:

; $%!value1!%$ ; Description
$%!value2!%$:: ; HotKey
IfWinExist, $%!value3!%$ ; Window Title
{
IfWinActive, $%!value4!%$ ; Window Title
WinMinimize
else
WinActivate
return
}
else
    Run $%!value5!%$ ; Program path
return

Скрипт модифицирует свой код и перегружается, чтобы изменения вступили в силу. Пока готов только один шаблон (quickpanel функциональность), но можно использовать произвольный шаблон (в коде используется понятие тег и тег-запрос, сам тег - шаблон, с уже подставленными значениями переменных, тег-запрос - шаблон, с подставленными переменными, но для файла настроек скрипта "имя скрипта.ini", служит для удобного считывания данных о тегах и на случай восстановления при обновлении/затирании самого скрипта), подготовленный следующим образом:

1. Шаблон разместить в папке templates\Имя шаблона.txt
2. То, что нужно заменить в шаблоне, обозначить в виде переменных $%!valueNN!%$, где NN номер переменной.
3. Создать если нужно новую секцию по аналогии с секцией hot_keys, либо размещать в самой hot_keys. Предусмотреть дублирование данных и создать новую секцию в файле настроек скрипта "имя скрипта.ini"
4. С помощью функций ahk_generate(), tag_modify() организовать редактирование скрипта. Примеры использования функций есть в коде скрипта.
5. Создать, если нужно, графическое окошко, для работы с переменными шаблона, смотреть ^F7, метку generateahk для примера.


Удобные сочетания: #`,#q,#w,#s,^`,^1,^-,^=,#a,#x (Свободны, по крайней мере в Windows 7)

Распаковать в любую папку, например C:\AHK.
Для автоматического запуска в Планировщике Задач настроить запуск двух скриптов:
C:\AHK\Autohotkey.exe C:\AHK\greyforum_quickpanel.ahk
C:\AHK\Key Catcher\Key Catcher 1.00.3b modified.exe
Триггер: при разблокировке ПК, при входе такого-то пользователя
Не запускать, если уже запущен.

В архиве находится скрипт, шаблоны к нему, скрипт логирования, AHK Unicode 32 bit 1.1.15.0.

Совместим с версией AHK Unicode 32 bit 1.1.15.0 (С другими не тестировался)
Поддерживается в ОС: WinXP 32bit, WinVista 64 (сочетания ^!r, #x не работают, нужно их заменить), Win7 32bit&64bit


Скрипт логирования слегка переделан из Key Catcher автор Daniel Green Copyright 2010-2011
Отдельное спасибо Malcev за функцию FileRead(), что позволило мне оптимизировать код и слегка расширить функциональность шаблона.

2

Re: AHK: Панель быстрого доступа в WinXP как в Win7

Подправил битый архив.

3

Re: AHK: Панель быстрого доступа в WinXP как в Win7

Анализатор логов Key Catcher

А что это?

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

4

Re: AHK: Панель быстрого доступа в WinXP как в Win7

serzh82saratov,

Key Catcher  - AHK скрипт, который логирует нажатия клавиш и клики мыши и пишет все это в log.txt в каталог, где находится сам.
Я его нашел на официальном форуме, но уже не помню где точно. Я отключил в нем мышь и набор текста (чтобы пароли не логировал) и добавил комбинации клавиш.

Анализатор логов - это небольшое графическое меню с 3 полями фильтра, чтобы удобно выбрать нужную статистику по логу log.txt. Запускаешь на месяц Key Catcher, потом смотришь какие клавиши чаще нажимаешь и если они далеко от твоего привычного положения руки, переназначаешь данное действие на клавиши поближе, а те, где мало нажатий подальше, как правило, не так уж много сочетаний нужно для работы, вот анализатор поможет понять, а что чаще всего используется.