Здравствуйте, Аноним, Вы писали:
CEM>> HWND hRes = CreateDialogIndirect(GetModuleHandle(NULL), (LPDLGTEMPLATE) hgbl, hwndOwner, (DLGPROC) DialogBoxProc);
А>а вы, кстати, в курсе, что Proc у CreateDialog и DialogBox несколько не совпадают ?
На самом деле изначально там DialogBoxIndirect, который я и проверял. Уже потом, перед тем, как постить сюда, я начал функцию переписывать, потом решил запостить кусок кода, этот момент забыл вернуть назад
Изначально я взял весь кусок кода из msdn-а, не компилируя переделал те три контрола на свои — на заработало. Упростил — не заработало. Удалил всё нафиг и вставил опять код из msdn-а — не заработало, с одинм контролом — не заработало! Ну а дальше знаете уже
Здравствуйте, jyuyjiyuijyu, Вы писали:
J>Здравствуйте, CEMb, Вы писали:
J>можно создвать диалог в ресурсе потом сдампить этот кусок памяти в массив и запустить из массива заодно посмотреть что не так
Соглашусь с данным советом. От себя добавлю, что сборка Dialog Template-а в памяти — занятие не для слабонервных, и, как бы это помягче выразиться... не для быдлокодеров. Если все же неймется поработать напрямую с Dialog Template-ами, тогда для начала попробуйте загрузить диалоговый ресурс в память через FindResource->LoadResource->LockResource (я правильно понял выражение "сдампить этот кусок памяти в массив" ? ) и создать диалог из таким образом загруженного template-а. Посмотреть, что из этого получится [скорее всего получится]. Потом, в качестве разминки, попытайтесь заменить имя фонта диалога (у вас ведь стоит стиль DS_SETFONT?) и размер фонта. Если сразу не получится [а скорее всего не получится], то внесите поменяйте фонт в ресурсе и посмотрите, как изменился Dialog Template. После того, как научитесь программно менять фонт ресурса (и если Ваш пыл к этому времени не иссякнет) — можно приступать с "сборке диалогового ресурса в памяти". Удачи Вам на этом нелегком пути.
Здравствуйте, CEMb, Вы писали: CEM>Скопировал пример из msdn-а, он не работает
...
Не разобрался еще? Если еще актуально, выложи проект минимальный (желательно VS2008), времени нет а то с нуля делать, я попробую подшаманить, чтоб заработало.
Буквально неделю-две назад занимался подобным вопросом, там правда я CreateDialogIndirect() для создания WTL-ных диалогов использовал, получилось. Так что думаю проблема несложная.
Или вот из своего кода надергал, надеюсь ничего важного не забыл, может и сам разберешься:
Скрытый текст
static const UINT IDS_LISTTITLE = 101;
static const UINT IDL_LIST = 102;
LPWORD AlignDword(LPWORD lpIn) // align on DWORD bondary
{
return (LPWORD)(((DWORD)lpIn + 3) & ~3);
}
LPWORD AlignWord(LPWORD lpIn) // align on WORD boundary
{
return (LPWORD)(((DWORD)lpIn + 1) & ~1);
}
DWORD PushStr(LPWORD szAddr, LPWSTR szStr) // push string to specified address, return number of pushed bytes (include zero-terminator)
{
DWORD dwLen = wcslen(szStr) + 1;
CopyMemory(szAddr, szStr, dwLen * sizeof(WCHAR));
return dwLen;
}
LPDLGTEMPLATE CSelectDlg::CreateDlgTemplate()
{
// allocate memoryconst int nSize = 1024;
LPBYTE pBuf = (LPBYTE)malloc(nSize);
if (!pBuf)
return NULL;
SecureZeroMemory(pBuf, nSize);
LPDLGTEMPLATE lpdt = (LPDLGTEMPLATE)pBuf;
LPDLGITEMTEMPLATE lpdit = NULL;
LPWORD lpw = NULL;
// Define a dialog box
lpdt->style = DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU;
lpdt->dwExtendedStyle = WS_EX_WINDOWEDGE;
lpdt->cdit = 4; // Number of controls
lpdt->x = 0; lpdt->y = 0;
lpdt->cx = 388; lpdt->cy = 169;
lpw = (LPWORD)(lpdt + 1);
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 0; // No menu
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 0; // Predefined dialog box class (by default)
lpw = AlignWord(lpw); // Align on WORD boundary
lpw += PushStr(lpw, L""); // titleif (lpdt->style & DS_SETFONT)
{
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 8; // font size
lpw = AlignWord(lpw); // Align on WORD boundary
lpw += PushStr(lpw, L"Ms Shell Dlg"); // font family name
}
// define top static
lpw = AlignDword(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 12; lpdit->y = 6;
lpdit->cx = 366; lpdit->cy = 8;
lpdit->id = IDS_LISTTITLE;
lpdit->style = WS_CHILD | WS_VISIBLE | WS_GROUP | SS_CENTERIMAGE;
lpw = (LPWORD)(lpdit + 1);
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 0xFFFF;
*lpw++ = 0x0082; // Static class
lpw = AlignWord(lpw); // Align on WORD boundary
lpw += PushStr(lpw, L""); // text
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 0; // No creation data
// define List control
lpw = AlignDword(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 12; lpdit->y = 18;
lpdit->cx = 366; lpdit->cy = 126;
lpdit->id = IDL_LIST;
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_BORDER | LVS_REPORT | LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT;
lpw = (LPWORD)(lpdit + 1);
lpw = AlignWord(lpw); // Align on WORD boundary
lpw += PushStr(lpw, WC_LISTVIEWW);
lpw = AlignWord(lpw); // Align on WORD boundary
lpw += PushStr(lpw, L"");
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 0; // No creation data
// define OK button
lpw = AlignDword(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 264; lpdit->y = 150;
lpdit->cx = 54; lpdit->cy = 14;
lpdit->id = IDOK;
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_DEFPUSHBUTTON;
lpw = (LPWORD)(lpdit + 1);
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 0xFFFF;
*lpw++ = 0x0080; // Button class
lpw = AlignWord(lpw); // Align on WORD boundary
lpw += PushStr(lpw, L"OK");
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 0; // No creation data
// define Cancel button
lpw = AlignDword(lpw); // Align DLGITEMTEMPLATE on DWORD boundary
lpdit = (LPDLGITEMTEMPLATE)lpw;
lpdit->x = 324; lpdit->y = 150;
lpdit->cx = 54; lpdit->cy = 14;
lpdit->id = IDCANCEL;
lpdit->style = WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON;
lpw = (LPWORD)(lpdit + 1);
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 0xFFFF;
*lpw++ = 0x0080; // Button class
lpw = AlignWord(lpw); // Align on WORD boundary
lpw += PushStr(lpw, L"Отмена");
lpw = AlignWord(lpw); // Align on WORD boundary
*lpw++ = 0; // No creation data
// readyreturn lpdt;
}
Здравствуйте, programmater, Вы писали:
P>Здравствуйте, jyuyjiyuijyu, Вы писали:
J>>Здравствуйте, CEMb, Вы писали:
J>>можно создвать диалог в ресурсе потом сдампить этот кусок памяти в массив и запустить из массива заодно посмотреть что не так
P>Соглашусь с данным советом. От себя добавлю, что сборка Dialog Template-а в памяти — занятие не для слабонервных, и, как бы это помягче выразиться... не для быдлокодеров. Если все же неймется поработать напрямую с Dialog Template-ами, тогда для начала попробуйте загрузить диалоговый ресурс в память через FindResource->LoadResource->LockResource (я правильно понял выражение "сдампить этот кусок памяти в массив" ? ) и создать диалог из таким образом загруженного template-а. Посмотреть, что из этого получится [скорее всего получится]. Потом, в качестве разминки, попытайтесь заменить имя фонта диалога (у вас ведь стоит стиль DS_SETFONT?) и размер фонта. Если сразу не получится [а скорее всего не получится], то внесите поменяйте фонт в ресурсе и посмотрите, как изменился Dialog Template. После того, как научитесь программно менять фонт ресурса (и если Ваш пыл к этому времени не иссякнет) — можно приступать с "сборке диалогового ресурса в памяти". Удачи Вам на этом нелегком пути.
Где то тут на КЫВТ в старых статьях был код, который собирал диалог на лету. Работает и до сих пор. Из плюсов: код достаточно юзабельный, в виде класса. В распоряжении получаете достаточно высокоуровневые функции вида AddControl(тип_контрола, текст, CRect rSize) ну и так далее. Но вот с визуальными стилями этот код не дружил — приходилось ручками добавлять.
Здравствуйте, Baskak, Вы писали:
B>Не разобрался еще?
Не, но думаю, я дамп посмотрю, как выше написано, я примерную схему по коду знаю. Заодно пройду тест на быдлокодера
Задача была у меня такая:
У меня был контрол, лист-дерево, с кнопками-иконками в разных местах.
И тут меня смутили ближние люди, сказав: "окно настроек с таб-контролами — это прошлый век, это не расширяемо, это ненаглядно, это..."
Я подумал и сделал окно настроек так: бросил слева типа дерево, а справа лист.
В дерево тыкаешься — лист перезаполняется по контенту.
И тут встала проблема — лист из необходимого поддерживал только чеки(чекбоксы), другое было ненужно раньше, чеки были реализованы внутри листа и были рерурсонезависимы, что важно (чтоб легко перетаскивать контрол из проекта в проект).
С другой стороны, у меня есть небольшая система параметров — шаблонов, которые можно привязывать быстро привязывать к реестру и контролам, собирать в множества и т.д. И так как через них я делал настройки, захотелось это всё к листу добавить. Но такому параметру при настройке нужна была привязка к ресурсу. Ну, например, есть параметр [0..100], есть диалог настроек. Я кидаю туда едит, спин и слайдер, на каждый контрол привязываю этот параметр, и все три контрола становятся связаны с параметром и между собой (меняешь слайдер — меняется текст, меняешь спин — меняется текст и слайдер, удобно! причём "смешать" так можно скоко угодно элементов).
И вот мне захотелось на числовые параметры иметь на листе такие мини-диалоги, которые не надо описывать в ресурсах. И первое, что вспомнилось — CreateDialogIndirect. Вобщем, так как времени разбираться не было, я всё пока сделал через CreateWindowEx, но это не айс, по понятным причинам. Так что буду смотреть в дамп. Или, ещё более простой вариант (мне ж константный диалог нужен) — соберу в ресурсах нужный диалог, сниму дамп и пропишу побайтно в коде
Здравствуйте, Carc, Вы писали:
C>Где то тут на КЫВТ в старых статьях был код, который собирал диалог на лету. Работает и до сих пор. Из плюсов: код достаточно юзабельный, в виде класса. В распоряжении получаете достаточно высокоуровневые функции вида AddControl(тип_контрола, текст, CRect rSize) ну и так далее. Но вот с визуальными стилями этот код не дружил — приходилось ручками добавлять.
FindResource->LoadResource->LockResource (я правильно понял выражение "сдампить этот кусок памяти в массив" ?
можно еще так взять Restorator или другой менеджер ресурсов распаковать нужный диалог в файл потом открыть этот файл в WinHex там есть такая опция сохранить как C/C++ массив
Здравствуйте, CEMb, Вы писали:
CEM>Вот если контролов нету, т.е. lpdt->cdit = 0; то диалог создаётся, если lpdt->cdit = 1; то нет CEM>В примере было три контрола, но я урезал до одного.
CEM>При вызове сразу прилетает wm_destroy & wm_ncdestroy
Какие именно контролы ? Не common controls ? Если да — вызвана ли InitCommonControls ?
Здравствуйте, CEMb, Вы писали: CEM>Здравствуйте, Baskak, Вы писали: B>>Не разобрался еще? CEM>Не, но думаю, я дамп посмотрю, как выше написано, я примерную схему по коду знаю. Заодно пройду тест на быдлокодера CEM>Задача была у меня такая: CEM>У меня был контрол, лист-дерево, с кнопками-иконками в разных местах. CEM>И тут меня смутили ближние люди, сказав: "окно настроек с таб-контролами — это прошлый век, это не расширяемо, это ненаглядно, это..." CEM>Я подумал и сделал окно настроек так: бросил слева типа дерево, а справа лист. CEM>В дерево тыкаешься — лист перезаполняется по контенту. CEM>И тут встала проблема — лист из необходимого поддерживал только чеки(чекбоксы), другое было ненужно раньше, чеки были реализованы внутри листа и были рерурсонезависимы, что важно (чтоб легко перетаскивать контрол из проекта в проект). CEM>С другой стороны, у меня есть небольшая система параметров — шаблонов, которые можно привязывать быстро привязывать к реестру и контролам, собирать в множества и т.д. И так как через них я делал настройки, захотелось это всё к листу добавить. Но такому параметру при настройке нужна была привязка к ресурсу. Ну, например, есть параметр [0..100], есть диалог настроек. Я кидаю туда едит, спин и слайдер, на каждый контрол привязываю этот параметр, и все три контрола становятся связаны с параметром и между собой (меняешь слайдер — меняется текст, меняешь спин — меняется текст и слайдер, удобно! причём "смешать" так можно скоко угодно элементов). CEM>И вот мне захотелось на числовые параметры иметь на листе такие мини-диалоги, которые не надо описывать в ресурсах. И первое, что вспомнилось — CreateDialogIndirect. Вобщем, так как времени разбираться не было, я всё пока сделал через CreateWindowEx, но это не айс, по понятным причинам. Так что буду смотреть в дамп. Или, ещё более простой вариант (мне ж константный диалог нужен) — соберу в ресурсах нужный диалог, сниму дамп и пропишу побайтно в коде
Мне всегда говорили, что половина решения задачи содержится в правильной ее (задачи) постановке. Похоже что ты ринулся решать второстепенную проблему (создание диалога из памяти), до конца не продумав главную. Идея снабдить ListView шибко крутым интерфейсом напихав в него дочерних контролов (Ого! Ты даже до дочерних диалогов додумался! ) кажется привлекательной. Но только на первый взгляд. Ну допустим, научился ты создавать свои диаложки и напихал их в ListView, а дальше что? В общем дам тебе две проблемы, которые... [я в свое время решить не смог, поэтому отказался от этой идеи]. Что будешь делать в случае:
1. Сжатия/расширения SubItem-ов тасканием за Header (полностью решается только убийством хидера на корню: нет хидера — нет проблемы )
2. ScrollBar-ы в твоем ListView. Ну как, скажи мне, ты собрался перетаскивать дочерние диалоги согласно тасканию пимпочки на скролбаре? А клавиатурный скрол не забыл?
3. А если строчек 10 тыщ? Система не охренеет от такого количества дочерних диалогов?
В общем после размышлений я выбрал более-менее стандартную в таких случаях схему: Лист оставляем листом как есть, все эффекты делаем чезез StateImages (элементарно) / NM_CUSTOMDRAW (для продвинутых), а контрольчики (точнее один-единственный контрольчик) создаем в момент клика по SubItem-у и прямо на месте этого SubItem-а. После того, как юзер отредактировал элемент, достаем данные и з контрольчика, запихиваем их в лист и прибиваем контрольчик нахрен [Подсказка для быдлокодеров:
Скрытый текст
"прбиваем контрольчик нахрен" означает
::PostMessage(hWndControl, WM_CLOSE, 0, 0);
Не вздумай вызывать DestroyWindow в обработчике нотификации от этого контрола! Почему? Ну, когда программа рухнет — поймешь
]. При такой схеме набор контрольчиков существенно ограничен, но кое на что сгодится и такой. Что можно сделать?
1. Чекбоксы и радиобоксы. Элементарно делаются через StateImages. Обработка кликов (и изменение состояния по этим кликам) — самому. Но не так уж это и сложно.
2. Обыкновенный edit. Тоже все понятно. Кликнули->создали контрол->по Enter сохранили данные в Листе, по Esc или потере фокуса (важно!) просто прибили этот edit.
3. Комбо бокс. В общем то же самое, что и edit. Просто комбобокс после создания сразу раскрываешь. Если хочешь, чтобы выглядело все "по крутому" (т.е. чтобы у всех айтемов справа была кнопочка со стрелочкой как у нормальных свернутых комбобоксов) — делаешь это через CustomDraw [чем не тест на быдлокодера?]. С потерей фокуса немного сложнее, но подскажу: лови CBN_SELENDOK и CBN_SELENDCANCEL. Если все правильно сделаешь — этих двух сообщений будет достаточно.
4. Спин — туда же, куда и комбо. Сам не пробовал, так что как там с фокусом не подскажу, исследуй сам.
5. Кнопки. Не знаю, зачем они в листе, но если очень надо — NM_CUSTOMDRAW + DrawFrameControl. Клики — самому.
От слайдеров думаю придется отказаться. С ходу не придумаю как их туда воткнуть.
В общем как-то так. Ну или если решишь как быть со скролами/хидерами — напиши мне как.
Здравствуйте, programmater, Вы писали: P>Мне всегда говорили, что половина решения задачи содержится в правильной ее (задачи) постановке. Похоже что ты ринулся решать второстепенную проблему (создание диалога из памяти), до конца не продумав главную. Идея снабдить ListView шибко крутым интерфейсом напихав в него дочерних контролов (Ого! Ты даже до дочерних диалогов додумался! )
Хмм.
Вообще я сейчас вспомнил, что первый раз до такой штуки я додумался и реализовал более 10 лет назад, когда писал конвертеры для баз данных. Там надо было на ходу накидывать входящие поля и выбирать куда они будут перекочёвывать. Окно-контейнер имело скролл-бар и динамический набор дочерних диалогов, с кнопками управления и комбобоксом со списком полей экспортируемой базы данных.
И ещё один момент — это не ListView, он мне не подходит. Точнее, в процессе разработок я сделал свой лист-дерево, поэтому мне удобнее его использовать. P>кажется привлекательной. Но только на первый взгляд. Ну допустим, научился ты создавать свои диаложки и напихал их в ListView, а дальше что? В общем дам тебе две проблемы, которые... [я в свое время решить не смог, поэтому отказался от этой идеи]. Что будешь делать в случае: P>1. Сжатия/расширения SubItem-ов тасканием за Header (полностью решается только убийством хидера на корню: нет хидера — нет проблемы )
У меня нету сабитемов, за ненадобностью. P>2. ScrollBar-ы в твоем ListView. Ну как, скажи мне, ты собрался перетаскивать дочерние диалоги согласно тасканию пимпочки на скролбаре? А клавиатурный скрол не забыл?
Не забыл, это давно уже сделано, есть скролливание под фокус при клавиатурном вводе. Так как задумывалось дерево, я сделал возможность добавить тул-разрёртыш (который в обычном дереве [+], сначала и у меня был [+], но люди сказали "щас так не модно, модно кнопка с шевроном"... ну, блин, вот теперь у меня кнопка с аппаратным шевроном по умолчанию... хотя можно на неё влепить любую картинку на каждое состояние). Плюс, само наличие диалогов я заложил в лист изначально, где-то это даже использовалось. Диалог в итеме не виден, пока expand не нажмёшь. Там же под экспандом сидят дочерние итемы. Скроллбары работают. У меня ещё есть возможность обратного порядка заполнения, от низа к верху, как в фотошопе. Кроме скролинга у меня присутствует expand и ресайзинг и диалоги на дочерних итемах. И каждому итему можно задать персональную высоту. И всё это учитывается при скролинге, expand-е и ресайзе. Expand/collapse/переход на парента — с клавиатуры тоже можно. (Multi)выделение с shift/ctrl. Есть драг-н-дроп(но для дерева ещё не доделан нормально), с подсветкой. И весь этот зоопарк нотифаит родительское окно по клаве/мыши/change/command/drop и имеет возможность внешнего расширения итемов, так что можно делать навороты дальше, не меняя основной класс окна. Я не первый год делаю свои окна. Так что всё ок. P>3. А если строчек 10 тыщ? Система не охренеет от такого количества дочерних диалогов?
Охренеет ещё на трёх тысячах, в моём случае, но мне пока надо штук 10 максимум. По хорошему, конечно, надо динамически их создавать, но будем решать проблемы по мере их поступления(к тому же статика ближе к вышеописанной системе шаблонов-параметров, которые биндятся на старте). Три-четыре проекта назад я решил эту проблему просто подъёмом отдельных больших диалогов с настройками, так как настроек там было дофигищи на каждый итем, 2-4 закладки, с некоторых ещё диалоги поднимались... так что этот зоопарк в лист не влез бы. Но сейчас всё по-другому, один итем — оди параметр, а то я бы и сейчас сделал так же, но для одного параметра как-то странно подымать диалог. P>В общем после размышлений я выбрал более-менее стандартную в таких случаях схему: Лист оставляем листом как есть, все эффекты делаем чезез StateImages (элементарно) / NM_CUSTOMDRAW (для продвинутых), а контрольчики (точнее один-единственный контрольчик) создаем в момент клика по SubItem-у и прямо на месте этого SubItem-а. После того, как юзер отредактировал элемент, достаем данные и з контрольчика, запихиваем их в лист и прибиваем контрольчик нахрен [Подсказка для быдлокодеров:
Скрытый текст
"прбиваем контрольчик нахрен" означает P>
P>::PostMessage(hWndControl, WM_CLOSE, 0, 0);
P>
P> Не вздумай вызывать DestroyWindow в обработчике нотификации от этого контрола! Почему? Ну, когда программа рухнет — поймешь
].
А так я делал восемь-девять лет назад, когда надо было копировать свойства окна из таблички. Программа была на MFC, контрол я не убивал, а скрывал, потому как он был mfc-шный и сам создавался/убивался вместе с окном свойств. P>При такой схеме набор контрольчиков существенно ограничен, но кое на что сгодится и такой. Что можно сделать? P>[...]
Эту схему уже те же 10 лет мы используем на работе, только не на ListView а на другой таблице, но принцип тот же. Только спинов у нас нету, так как не нужны, а кнопки и чеки у этой таблицы штатные. Остальное ровно так же. P>От слайдеров думаю придется отказаться. С ходу не придумаю как их туда воткнуть.
да слайдер-то туда можно воткнуть и обработать, проблема в том, что он не информативен сам по себе. Нужен сбоку едит/статик, который показывает, каково значение на данный момент. А на едите полезен спин, чтоб сразу колесом его менять. Сразу получаем те самые три контрола. P>В общем как-то так. Ну или если решишь как быть со скролами/хидерами — напиши мне как.
С хидерами проблему можно попробовать решить через нотификации, хедер при смене размера-положения заголовков шлёт нотификации родителю. Я на это раньше затачивался, у меня была таблица, во второй колонке были числовые значения, и чтоб сэкономить место, я размещал там кнопку-чек "hex", при нажатии на которую значение выводилось в 16-м виде, при отжатии — в 10-м. Вот чтоб размещать кнопку в правильном месте, затачивался на нотификации от хедера. Но вот не помню, получилось у меня это полностью сделать или нет.
Здравствуйте, CEMb, Вы писали:
CEM>Здравствуйте, Carc, Вы писали:
C>>Где то тут на КЫВТ в старых статьях был код, который собирал диалог на лету. Работает и до сих пор. Из плюсов: код достаточно юзабельный, в виде класса. В распоряжении получаете достаточно высокоуровневые функции вида AddControl(тип_контрола, текст, CRect rSize) ну и так далее. Но вот с визуальными стилями этот код не дружил — приходилось ручками добавлять.
CEM>Пальцем покажи где?
Кажется вот он