AXForum  
Вернуться   AXForum > Microsoft Dynamics AX > DAX: Программирование
All
Забыли пароль?
Зарегистрироваться Правила Справка Пользователи Сообщения за день Поиск

 
 
Опции темы Поиск в этой теме Опции просмотра
Старый 03.08.2009, 00:21   #1  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
HK Framework
Содержание:
  1. HK Framework - что это такое
  2. Как пользоваться
  3. Как добавить новый HK
  4. Базовая комплектация HK Framework
  5. Недостатки
  6. Инсталляция, совместимость
  7. Спасибо

HK Framework - что это такое:
  • HK Framework это инструмент, реализующий функциональность горячих клавиш в MS DAX. Особенностью HK Framework является то, что он не использует внешние компоненты (DLL, ActiveX) и ограничивается только XPO.
  • HK Framework позволяет назначать горячие клавиши для трех областей: Редактор X++, AOТ и глобально, для всего приложения.
  • HK Framework позволяет легко и быстро добавлять новую функциональность и вешать на неё свою комбинацию горячих клавиш. Принцип добавления такой же как и в EditorScripts.

Как пользоваться:

В тексте буду использовать сокращение HK (Hot key или Горячая клавиша).
Комбинации HK настраивается в форме HKSetup. По-умолчанию, форма вызывается нажатием "Ctrl + ~", либо запуском класса HKManager прямо из АОТ.

Описание полей формы:
  • Поля "Key 1", "Key 2", "Key 3" задают комбинацию клавиш.
  • Поля "Action type" и "Action\Element name" определяют назначение HK и область его действия. Возможны следующие комбинации:
    1. "AOT action" - если HK работает в AOT. При этом поле "Action\Element name" лукапится по методам класса HKAOT (методы-обработчики для АОТ)
    2. "Editor action" - если HK работает в редакторе X++. При этом поле "Action\Element name" лукапится по методам класса CLS HKEditor (методы-обработчики для Редактора X++)
    3. "Editor script" - аналогично "Editor action", но поле "Action\Element name" лукапится по методам стандартного класса EditorScripts. Т.е. HK можно повесить на стандартные скрипты редактора.
    4. "Open form", "Open table", "RunClass", "RunJob" - позволяет по HK запустить форму, таблицу, класс, джоб. При этом, в поле "Action\Element name" лукапятся по формам, таблицам, классам, джобам соответственно.
  • Флаг "Enabled" отключает текущую HK.
  • Флаг "Enable hotkeys" отключает все HK.
  • Кнопка "Default" задает исходные настройки HK.

Как добавить новый HK:
  • Если нужно добавить HK для запуска формы\класса\таблицы*\джоба то достаточно лишь создать запись в форме HKSetup, в соответствии с настройками, описанными выше.
  • Если нужно добавить HK для АОТ или редактора X++, то необходимо:
    1. Создать новый метод-обработчик** HK в классе HKAOT или HKEditor, в зависимости от того, где будет работать новый HK.
    2. Создать новую запись в форме HKSetup и выбрать в поле "Action\Element name"
      созданный обработчик.
    3. Закрыть форму кнопкой "OK"***.

* - под запуском таблицы понимается открытие её в обозревателе.
** - примеры методов-обработчиков можно посмотреть в умомянутых классах.
*** - Форма HKSetup представляет собой диалог, поэтому все изменения вступают в силу только после нажатия "OK".

Базовая комплектация HK Framework:

Для удобства, существующую функциональность, уже входящую в состав HK Framework буду называть плагинами.
  • aot2project: плагин для управление проектами разработки. Назначение - быстрое добавление по HK редактируемых элементов АОТ (можно добавлять сразу из редактора X++) в заранее указанный проект и проставление комментариев в ходе редактирования элементов. По "Alt+F3" настраивается имя проекта в АОТ, его описание и формат комментариев. Далее, по кнопке "Ctrl+K" (В АОТ либо в редакторе), текущий элемент добавляется в заранее указанный проект. При этом, в проекте создается нужная подгруппа для элемента а также соблюдается порядок сортировки, аналогичный дереву АОТ; для АОТ поддерживается multiSelect. По кнопкам "Ctrl+1", "Ctrl+2", "Ctrl+3" в редакторе X++ проставляется открывающий, простой и закрывающий комментарии соответственно.
  • aotJump (AOT action, Ctrl+J): Плагин для быстрого "скольжения" по элементам АОТ. С помощью клавиши Ctrl+J можно "скользить" по следующим цепочкам\шагам (каждый элемент АОТ открывается новое окно АОТ):
    • Класс --> Класс родитель --> Класс родитель --> ... --> Базовый класс
    • TableField --> EDT --> EDT родитель --> EDT родитель --> ... --> Базовый EDT --> [ENUM]
    • MenuItemButton --> MenuItem --> Класс\Форма\Отчет, в зависимости от типа MenuItem.
    • Menu --> MenuItem
    • FormDataSource --> Table\View
    • уже не помню всех цепочек
  • checkBestPractice (AOT action, Ctrl+B). Плагин проверяет текущий элемент на соответствие рекомендациям BestPractice
  • copy2clipboard (AOT action, Ctrl+C). Плагин копирует имя текущего элемента АОТ в буфер обмена. Поддерживается multiSelect.
  • tableBrowser (AOT action, Ctrl+O). Плагин открывает текущую таблицу в обозревателе
  • openTableBrowser (Editor action, Ctrl+Ноль) Плагин открывает таблицу в обозревателе на которой установлен курсор в редакторе X++
  • openNewWindow (Editor action, Ctrl+O) Плагин открывает элелемент АОТ в новом окне. Распознавание идет не по имени, а по типу.

Все HK, приведенные в описаниях плагинов можно переназначить в на уровне пользовательского интерфейса в форме HKSetup.
Самое главное - запомнить одну горячую клавишу - "Ctrl+~". По ней откроется форма HKSetup, где всегда можно увидеть весь список HK.

Недостатки:

- HK Framework не использует внешние компоненты (DLL, ActiveX). Вследствие этого, он не перекрывает стандартные горячие клавиши. Т.е. если Вы назначите какой-то HK, который пересекается со стандартным, то стандартный HK отработает параллельно. Это накладывает ограничение на набор возможных комбинаций клавиш, который вы захотите использовать. Некоторые комбинации клавиш вообще не работают. Например, любая комбинация с клавишей ALT в редакторе X++ не работает (удобно использовать Ctrl). В общем, к процессу назначения новой горячей клавиши нужно подойти творчески
- детектирование комбинации клавиш несколько отличается от стандартного перехвата. Здесь важен момент одновременного нажатия клавиш, в то время как в стандарте достаточно быстрой последовательности нажатия. Поэтому для некоторых пользователей чувствительность может показаться недостаточной.

Инсталляция:

Данная версия тестировалась на DAX 4.0, 5.0.
Для 3-ки нужно чуть подпилить. В скором времени адаптирую.

Инсталляция очень проста - две простых модификации стандартных методов:
\Classes\ClassFactory\formRunClassOnClient:

X++:
client static FormRun formRunClassOnClient(Args args)
{
    SysSetupFormRun sysSetupFormRun;
;

    // HK "Hot key framework", evo 30.07.2009 -->
    if (args && args.name() == formstr(SysEditorGotoLine))
    {
        return HKManager::editorCatchObject(args);
    }
    // HK "Hot key framework", evo 30.07.2009 <--

    sysSetupFormRun = SysSetupFormRun::construct(args);
    return sysSetupFormRun;
}
\Classes\Info\onEventGoingIdle:
X++:
client static FormRun formRunClassOnClient(Args args)
{
    SysSetupFormRun sysSetupFormRun;
;

    // HK "Hot key framework", evo 30.07.2009 -->
    if (args && args.name() == formstr(SysEditorGotoLine))
    {
        return HKManager::editorCatchObject(args);
    }
    // HK "Hot key framework", evo 30.07.2009 <--

    sysSetupFormRun = SysSetupFormRun::construct(args);
    return sysSetupFormRun;
}
Рекомендуется данные модификации сделать аккуратно и вручную, причем ТОЛЬКО после импорта и компиляции всех остальных элементов HKFramework. Данные классы живут в RunTime приложения, со всеми вытекающими...

Спасибо:

Спасибо за внимание. Надеюсь, большое количество букв не сильно утомило и HK Framework для кого-нибудь окажется полезным.
Вложения
Тип файла: xpo PrivateProject_HKFramework_1_0.xpo (217.6 Кб, 700 просмотров)
За это сообщение автора поблагодарили: AlGol (1), denny (1), Logger (15), Denicce (1), andrewK (1), konopello (3), Link (1), Kolja (1), madm (1), Stainless (1), alex55 (1), _scorp_ (4), SRF (3), JeS (1), Eaglet (1), Silphidae (1), pedrozzz (1), Dumfag (1).
Старый 03.08.2009, 07:38   #2  
SRF is offline
SRF
Участник
MCBMSS
Axapta Retail User
 
375 / 562 (19) +++++++
Регистрация: 08.08.2007
Записей в блоге: 1
-> Небольшие штрихи
Цитата:
Сообщение от DSPIC Посмотреть сообщение

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

При импорте проекта в DAX 4.0 (Aplication Version 4.0.1633.11) возникли ошибки компиляции HKProject\buildAOTStruct, ошибка в строке
X++:
TreeNode        methodNode, classNode;
по всей видимости из-за того, что объет типа classNode уже существует в AOT, предлагаю такой вариант :
X++:
    TreeNode        methodNode;
    TreeNode        classNode;
Так же нашел небольшой побочный эффект в цепочке
Цитата:
Класс --> Класс родитель --> Класс родитель --> ... --> Базовый класс
В случае, когда базовый класс не имеет предков, но в нем есть ссылки на поля таблицы или меню итемов(в этом случае именно они и открываются в новом окне), к примеру, при нажатии на Ctrl + J на классе HKEditor в новом окне открывается не класс(по идее ничего не должно быть), а поле таблицы

Предлагаю такой вариант устранения помарки(HKAOT\aotJump), вместо :
X++:
        case UtilElementType::Class:
            sysDictClass = new SysDictClass(className2Id(treeNode.treeNodeName()));
            if (sysDictClass && sysDictClass.extend())
                treeNodeTargetPath = strFmt(#ClassNamePath, classid2name(sysDictClass.extend()));
            break;
вот такой код
X++:
        case UtilElementType::Class:
            sysDictClass = new SysDictClass(className2Id(treeNode.treeNodeName()));
            if (sysDictClass && sysDictClass.extend())
                treeNodeTargetPath = strFmt(#ClassNamePath, classid2name(sysDictClass.extend()));
            else
            {
                return;
            }
            break;
И небольшая опечатка про \Classes\Info\onEventGoingIdle(видимо поздно было, когда выкладывали Framework ),подразумевался код
X++:
//Event fired by kernel when the client goes idle.
//It is not fired during CTRL-Break dialog.
void onEventGoingIdle()
{
    this.operationProgressClear();
    this.endLengthyOperation(true);
    // HK "Hot key framework", evo 30.07.2009 -->
    HKManager::startHKManager().run(false);
    // HK "Hot key framework", evo 30.07.2009 <--
}
Вообщем, спасибо!
__________________
Sergey Nefedov
Старый 03.08.2009, 14:49   #3  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
2 SRF: Спасибо, всё верно. Шероховатости наверное ещё будут, не было времени обкатать.

Ещё хочу добавить, что при установке в 5.0 (вообще для однозначности - при первой установке), нужно удалить метод \Classes\HKProject\aotStructList. При первом использовании плагина aot2project, он сгенерируется сам.
Старый 27.01.2010, 12:39   #4  
Stainless is offline
Stainless
Участник
MCBMSS
Columbus IT
 
34 / 114 (4) +++++
Регистрация: 26.01.2007
При использовании функции добавления текущего элемента AOT в текущий проект, при копировании из другого проекта, элемент копируется но группы создаются некорректно
Старый 16.02.2010, 12:28   #5  
Stainless is offline
Stainless
Участник
MCBMSS
Columbus IT
 
34 / 114 (4) +++++
Регистрация: 26.01.2007
:)
Разобрался в чем было дело, просто в различных приложениях узлы AOT в проекте называются по-разному, возможные варианты:
  • Data Dictionary
  • Data_Dictionary
  • DataDictionary
Соответственно функционал при добавлении узла в проект ищет родительский узел текущего элемента и не находит, чтобы минимизировать ошибка можно переписать метод findOrCreateProjectGroup класса HKProject следующим образом:
X++:
ProjectGroupNode findOrCreateProjectGroup(TreeNode _treeNode)
{   #AOT
    ProjectGroupNode    ret, groupNode, bufGroupNode;
    TreeNodePath        path;
    TreeNodeName        groupName;
    container           pathCon;

    if (!projectNode || !_treeNode)
        return null;

    // + KAnt
    //path    = strReplace(_treeNode.treeNodePath(), ' ', '_');
    path    = _treeNode.treeNodePath();
    // - KAnt

    pathCon = str2con(path, #AOTRootPath);
    pathCon = condel(pathCon, 1, 1); // remove first delimiter

    groupNode = projectNode;
    while (conlen(pathCon) > 1)
    {
        groupName = conpeek(pathCon, 1);

        bufGroupNode = groupNode.AOTfindChild( groupName );

        // + KAnt
        if(!bufGroupNode)
        {
            bufGroupNode = groupNode.AOTfindChild( strReplace(groupName, ' ', '_') );
        }

        if(!bufGroupNode)
        {
            bufGroupNode = groupNode.AOTfindChild( strReplace(groupName, ' ', '') );
        }
        // - KAnt

        if (!bufGroupNode)
        {
            bufGroupNode = groupNode.AOTadd(groupName);
            bufGroupNode.projectGroupType(SysTreeNode::path2ProjectGroupNodeType(_treeNode.treeNodePath()));

            this.changeGroupPos(bufGroupNode);
        }

        groupNode = bufGroupNode;

        pathCon = condel( pathCon, 1, 1);
    }

    ret = groupNode;

    return ret;
}
Старый 26.08.2010, 13:06   #6  
dyal is offline
dyal
Участник
 
8 / 10 (1) +
Регистрация: 20.03.2008
На формах настроенные горячие клавиши не срабатывают.
Старый 26.08.2010, 13:17   #7  
dyal is offline
dyal
Участник
 
8 / 10 (1) +
Регистрация: 20.03.2008
Попытался настроить вызов формы заказов по Ctrl+5. Открыл произвольную форму - нажимаю сочетание - никакой реакции
Старый 09.08.2011, 14:08   #8  
niksen is offline
niksen
Участник
Самостоятельные клиенты AX
 
284 / 28 (1) +++
Регистрация: 05.07.2011
Адрес: Татарстан
проект ещё разрабатывается? окончательный варинт можно скачать где-нибудь?
заметил, что блог
http://earlionakru.blogspot.com/
с этим проектом тоже давно не обновлялся...
Старый 10.08.2011, 00:16   #9  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
Больше не разрабатывается, ничего нового не придумал. На блоге последняя версия
Старый 15.08.2011, 13:35   #10  
Link is offline
Link
Британский учённый
Аватар для Link
Соотечественники
 
568 / 523 (19) +++++++
Регистрация: 25.11.2005
Адрес: UK
Записей в блоге: 9
Цитата:
Сообщение от DSPIC Посмотреть сообщение
Больше не разрабатывается, ничего нового не придумал. На блоге последняя версия
Я влюбился в эту утилиту, и много в неё добавил, так как активно пользуюсь.
Буду рад поделиться с общественностью, но не вижу смысла плодить версии да и авторское право не хочу нарушать.
Если есть время и желание расширить функционал могу выложить здесь или лучше выслать на мыло.

Вкратце что основного я добавил:
1) Новый класс для управления общими событиями:
AOTImport - импорт из АОТ, для версии ниже 2009
mainMenu - вызов главного меню
openDefaultProject - открыть проект по умолчанию
leftSideFull, leftSideLeft, leftSideRight, rightSideFull, rightSideLeft, rightSideRight - управление окнами (Tabax)
2) Добавлены методы в для управления AOT:
aot2compare - вызов утилиты сравнения
aot2xRef - вызов перекрестных ссылок
copyPath2clipboard - копировать полный путь (Tabax)
newWindow - открыть новое окно
3) Добавлены методы для управления редактором
goToDeclaration - известный скрипт, взят с форума
openObject - расширил openNewWindow
4) Добавил на форму интеграцию с настройками девелоперского функционала.
Миниатюры
Нажмите на изображение для увеличения
Название: HK1.jpg
Просмотров: 458
Размер:	155.1 Кб
ID:	7068   Нажмите на изображение для увеличения
Название: HK2.jpg
Просмотров: 532
Размер:	64.7 Кб
ID:	7069  

__________________
Людям физического труда для восстановления своих сил нужен 7-8 часовой ночной сон. Людям умственного труда нужно спать часов 9-10. Ну а программистов будить нельзя вообще.
Старый 15.08.2011, 13:43   #11  
Link is offline
Link
Британский учённый
Аватар для Link
Соотечественники
 
568 / 523 (19) +++++++
Регистрация: 25.11.2005
Адрес: UK
Записей в блоге: 9
Еще добавил фикс класса Info\onEventGoingIdle - проверка активного окна, что бы не срабатывали горячие клавиши, когда Аксапта свернута или открыто несколько клиентов.

X++:
    // HK "Hot key framework", mxk 30.07.2009 -->
    if (winApi::getForegroundWindow() == infolog.hWnd())
    {
        DEV_HKManager::startHKManager().run(false);
    }
    // HK "Hot key framework", mxk 30.07.2009 <--
__________________
Людям физического труда для восстановления своих сил нужен 7-8 часовой ночной сон. Людям умственного труда нужно спать часов 9-10. Ну а программистов будить нельзя вообще.
Старый 31.03.2013, 09:31   #12  
Stainless is offline
Stainless
Участник
MCBMSS
Columbus IT
 
34 / 114 (4) +++++
Регистрация: 26.01.2007
На AX2012 никто еще не портировал эти разработки?
Старый 01.04.2013, 10:50   #13  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
Апгрейд в целом сделан, но я не выклыдывал - нет временени. Если у кого-то есть силы довести до "релиза", вышлю обновленный исходник. Из ниансов:
- HK больше не работает в редакторе X++ (нужен анализ, возможно ли сейчас его прикрутить)
- Добавлен TaskList, Где видна история всех сделанных модификаций
- Form Digger и HK Framework теперь одно целое.
За это сообщение автора поблагодарили: Ace of Database (4), Stainless (1).
Старый 01.04.2013, 13:27   #14  
belugin is offline
belugin
Участник
Аватар для belugin
Сотрудники Microsoft Dynamics
Лучший по профессии 2017
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии 2011
Лучший по профессии 2009
 
4,622 / 2925 (107) +++++++++
Регистрация: 16.01.2004
Записей в блоге: 5
В редакторе X++ можно теперь использовать расширения для VS. Может и хоткеи так добавятся?
За это сообщение автора поблагодарили: mazzy (2).
Старый 01.04.2013, 17:10   #15  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
Цитата:
Сообщение от belugin Посмотреть сообщение
В редакторе X++ можно теперь использовать расширения для VS. Может и хоткеи так добавятся?
Я не нашел способа, как из .net экстеншена вернуть управление в X++, да ещё и с аксаптовским классом Editor в качестве параметра, которого на стороне .net в принципе не существует. Поэтому пока хз.
Старый 02.07.2015, 17:07   #16  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
Новая версия для 2012 R2, R3, от Декабря 2014г.
Вложение 9316

Что нового:
  • Form Digger и HKFramework консолидированы в единый XPO
  • Добавлен список тасков (TaskTable)
  • Shortcuts теперь снова работают в редакторе.
  • Много мелких фиксов и улучшений.

По установке:
  • Файл HKFramework2012R3(R2).dll (в зависимости от версии AX) положить в папку c:\Program Files (x86)\Microsoft Dynamics AX\60\Client\Bin\EditorComponents\
  • Смержить SharedProject_HKFramework_December2014.xpo

По работе:
  • Заходим в TaskTable (Ctrl+Q или Tools\TaskTable)
  • Создаем новый таск (заполняем TaskId, Description)
  • Делаем созданный таск активным (нажав кнопку "Set default")
  • Создаем проект АОТ (нажимая кнопку Open)
  • Комментарии будут подтягиваться из этого проекта, а элементы добавляться в него

Вложение 9313

Еще пару слов:
Вероятнее всего это последняя версия, по причинам:
  • С грядущей версией AX7.0 данный продукт не совместим. Имею возможность пощупать - вся работа ведется в Visual Studio.
  • Катастрофически нет времени на суппорт
  • В целом тул самодостаточный из наиболее часто возникающих потребностей
  • На базе HKFramework и других схожих продуктов зреет более мощная версия для AX2012 by Link
  • Спасибо всем за отзывы и благодарности.

Последний раз редактировалось DSPIC; 28.09.2017 в 01:50.
За это сообщение автора поблагодарили: Logger (10), Ace of Database (10), Link (5), alex55 (5), S.Kuskov (5).
Старый 26.07.2016, 15:45   #17  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
По просьбе qwosy выкладываю исходник HKFramework2012R3.dll
Вложение 10886

- если не затруднит, выложите сюда пересобранную версию и исходник для CU11
- если возможно, переименуйте финальную dll в Microsoft.Dynamics.AX.Editor.HKFramework.dll, ибо режет глаз
- проект под VS2010, можно переделать в VS2015

Последний раз редактировалось DSPIC; 28.09.2017 в 01:50.
За это сообщение автора поблагодарили: mazzy (2).
Старый 27.07.2016, 08:43   #18  
qwosy is offline
qwosy
Участник
 
1 / 24 (1) +++
Регистрация: 17.10.2011
Собранная в VS2010 рабочая Microsoft.Dynamics.AX.Editor.HKFramework.dll с библиотеками от AX 2012 R3 CU11:
За это сообщение автора поблагодарили: DSPIC (14).
Старый 27.07.2016, 08:50   #19  
mazzy is offline
mazzy
Участник
Аватар для mazzy
Лучший по профессии 2015
Лучший по профессии 2014
Лучший по профессии AXAWARD 2013
Лучший по профессии 2011
Лучший по профессии 2009
 
29,472 / 4494 (208) ++++++++++
Регистрация: 29.11.2001
Адрес: Москва
Записей в блоге: 10
а может положите на github?
Старый 27.07.2016, 10:30   #20  
DSPIC is offline
DSPIC
Боец
 
1,077 / 1243 (44) ++++++++
Регистрация: 11.04.2008
Цитата:
Сообщение от mazzy Посмотреть сообщение
а может положите на github?
Ok, will care about it.
Теги
ax2009, ax4.0, formdigger, hk, hkframework, hotkey, горячие клавиши, полезное, утилиты

 

Похожие темы
Тема Автор Раздел Ответов Посл. сообщение
palleagermark: Intelligent Data Management Framework For Microsoft Dynamics AX (Pre-Release) Blog bot DAX Blogs 0 01.07.2009 00:05
Inside Dynamics AX 4.0: The Security Framework Blog bot DAX Blogs 0 31.10.2007 11:40
Inside Dynamics AX 4.0: The Send Framework Blog bot DAX Blogs 0 05.10.2007 23:31
Inside Dynamics AX 4.0: RunBase Framework Extension Part IV Blog bot DAX Blogs 0 02.10.2007 04:49
Inside Dynamics AX 4.0: RunBase Framework Extension Part I Blog bot DAX Blogs 0 30.09.2007 09:20

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

BB коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход

Рейтинг@Mail.ru
Часовой пояс GMT +3, время: 21:29.