Выложил первый снапшот Named Folders 3.0. Основные изменения: рефакторинг и чистка кода, перевод плагина на FAR API 2.0 и юникод, выпуск 64-битной версии.
вторник, 19 октября 2010 г.
воскресенье, 17 октября 2010 г.
Перевод плагина на FAR 2.X
В FAR API 2.X был внесен ряд серьезных изменений, по сравнению с FAR API 1.X. Все подробности изложены в Encyclopedia for Developers. Я же хочу рассказать о тех изменениях, с которыми я столкнулся при переводе плагина Named Folders на FAR API 2.X.
суббота, 16 октября 2010 г.
Как запустить ярлык 64-битного приложения из 32-битного через ShellExecute.
В плагине FAR Named Folders (NF) есть полезная функция - запуск приложения из меню "Пуск". Набираете в командной строке команду "cs:a" и вам показывается список приложений, названия которых соответствуют маске "*a*". В Win7 меню "Пуск" было модернизировано, в нем появился подобный фильтр, но лично мне через Far программы запускать по-прежнему гораздо быстрее. К сожалению, после перехода на 64-битную версию Win7, я обнаружил, что некоторые приложения запускаться через NF перестали.
Расследование показало, что возникла проблема с запуском 64-битных приложений из 32-битного FAR. В NF запуск приложений реализован через функцию ShellExecute - через нее запускается ярлык (LNK-файл) приложения, хранящийся в меню "Пуск". Выяснилось, что ShellExecute, вызванная из 32-битного приложения, ярлыки 64-битных приложений обрабатывает неправильно.
Расследование показало, что возникла проблема с запуском 64-битных приложений из 32-битного FAR. В NF запуск приложений реализован через функцию ShellExecute - через нее запускается ярлык (LNK-файл) приложения, хранящийся в меню "Пуск". Выяснилось, что ShellExecute, вызванная из 32-битного приложения, ярлыки 64-битных приложений обрабатывает неправильно.
четверг, 14 октября 2010 г.
Программирование под Android. С чего начать.
Появилась необходимость написать проект под Android. Засел за гугл и пару дней шерстил интернет на предмет полезной информации. Вот результаты.
среда, 13 октября 2010 г.
Как подружить BOOST_FOREACH с контейнером без "iterator"
В библиотеке stlsoft есть полезный класс
winstl::findfile_sequence_w, который оборачивает вызовы системных функций FindFirstFileW, FindNextFileW в STL-контейнер. С его помощью перебор файлов реализуется так:
Не беда. Функциональность BOOST_FOREACH предусматривает возможность расширения на случай использования нестандартных коллекций.
Так что можно заставить BOOST_FOREACH использовать "const_iterator" вместо "iterator":
winstl::findfile_sequence_w, который оборачивает вызовы системных функций FindFirstFileW, FindNextFileW в STL-контейнер. С его помощью перебор файлов реализуется так:
#include <winstl/filesystem/findfile_sequence.hpp> using namespace stlsoft; using namespace winstl; findfile_sequence_w seq(L"c:\", L"*.*", findfile_sequence_w::files); findfile_sequence_w::const_iterator p = seq.begin(); while (p != seq.end()) { std::wstring filename = (*p).get_filename(); ++p; }Чтобы сделать код более компактным, можно применить BOOST_FOREACH:
findfile_sequence_w seq(L"c:\", L"*.*", findfile_sequence_w::files); BOOST_FOREACH(findfile_sequence_w::value_type const& t, f) { std::wstring filename = t.get_filename(); }Но здесь ожидает неприятный сюрприз: BOOST_FOREACH требует наличие двух типов итераторов: iterator и const_iterator. В findfile_sequence_w является псевдо-STL-контейнером - типа "iterator" в нем нет.
Не беда. Функциональность BOOST_FOREACH предусматривает возможность расширения на случай использования нестандартных коллекций.
Так что можно заставить BOOST_FOREACH использовать "const_iterator" вместо "iterator":
#include <boost/foreach.hpp> namespace boost { /*possibility to use BOOST_FOREACH with findfile_sequence_w; findfile_sequence_w doesn't contain "iterator", it contains only "const_iterator"*/ template<> struct range_mutable_iteratorПоскольку в findfile_sequence_w все функции константные, вреда от такой подмены не будет, а BOOST_FOREACH заработает как надо.{ typedef winstl::findfile_sequence_w::const_iterator type; }; } ... BOOST_FOREACH(findfile_sequence_w::value_type const& t, f){} //or, with same result: BOOST_FOREACH(findfile_sequence_w::value_type & t, f){}
четверг, 7 октября 2010 г.
Подводные камни google code: ваш email открыт для всех.
Прошло чуть больше недели, как я создал на google code проект Named Folders. Все шло хорошо, а сегодня я вдруг осознал, что название моего google-аккаунта лежит в открытом доступе... Его видно в issues, его видно в svn. Это значит, что мой email запросто могут заспамить.
Как это не печально, никнеймов на google code нет. Политика у них такая.
Пришлось повозиться и переключить проект на альтернативный аккаунт. Для этого сделал repository reset (естественно, пропала вся история ревизий) и пересоздал все issues.
Самое печальное, что в issues видны аккаунты тех, кто эти issues создавал. Кроме того, если у вас нет google аккаунта, то issue вы создать не сможете. В результате, польза от такого багтрекера весьма и весьма ограничена.
Так что у google code есть серьезные недостатки..
Update: из-за вот этого бага (фичи?) переключение проекта на альтернативный аккаунт не решило проблемы :(
Как это не печально, никнеймов на google code нет. Политика у них такая.
Пришлось повозиться и переключить проект на альтернативный аккаунт. Для этого сделал repository reset (естественно, пропала вся история ревизий) и пересоздал все issues.
Самое печальное, что в issues видны аккаунты тех, кто эти issues создавал. Кроме того, если у вас нет google аккаунта, то issue вы создать не сможете. В результате, польза от такого багтрекера весьма и весьма ограничена.
Так что у google code есть серьезные недостатки..
Update: из-за вот этого бага (фичи?) переключение проекта на альтернативный аккаунт не решило проблемы :(
воскресенье, 3 октября 2010 г.
Каждому списку - по фильтру.
Вам приходилось сталкиваться с программами, в которых значение поля выбирается из длинного списка, а поиска по списку не предусмотрено? Работать с такими программами - сущее мучение. Всегда снабжайте списки средствами быстрого поиска. Ваши пользователи скажут вам большое спасибо.
Одним из вариантов такого поиска является динамическая фильтрация списка по ключевым словам. Динамическая - значит налету, прямо в процессе ввода ключевых слов пользователем. Скажем, у вас есть список сотрудников, в котором отображаются: идентификатор, ФИО, год рождения, адрес. Вы набираете "77 кр". Список сокращается. В нем остаются только те сотрудники, в данных у которых встречаются число 77 и/или слово "кр". Например, с сотрудники с фамилиями "Кравцов", "Окрошкин", проживающие на улице "Крапивина", с почтовым индексом 667770, с 1977 годом рождения, c идентификаторами 77, 777, 6772 и т.п. Поиск может осуществляться по принципу '77 ИЛИ кр' или '77 И кр' - как удобнее.
Подобный способ фильтрации хорош тем, что практически любая информация, известная пользователю, быстро приведет его к нужной записи в списке. Причем приведет быстро, за пару секунд. Главное, чтобы необходимая информация в списке была.
Конечно, можно сделать специальную форму поиска с четырьмя полями: ФИО, адрес, год рождения, идентификатор. Да еще поместить ее в отдельное окно. Можно.. Но зачем? Такие формы оправданы только в случае больших массивов данных, когда поиск ведется с помощью SQL-запросов и для ускорения поиска используются индексы по полям таблиц базы данных. Если же список целиком сидит в памяти (десяток, сотня, несколько тысяч строк), то достаточно одной строки поиска, которая легко помещается рядом со списком. Согласитесь, что место для одного дополнительного поля ввода над/под списком выделить можно всегда.
Теперь о реализации такого фильтра на Delphi. Здесь два важных момента. Во-первых, фильтр должен прикручиваться к любой форме буквально одной-двумя строками кода. Тогда его будет удобно использовать. Во-вторых, фильтр должен быть ориентирован на работу TDataSet, т.к. именно этот класс (и его наследники) используется для хранения списков в клиентских приложениях для работы с базами данных.
Для себя я реализовал такой фильтр в виде модуля FastFilter. Делать отдельный компонент я не стал - фильтр должен годится и для приложений, использующих не только стандартный TEdit, но и сторонние компоненты ввода.
Чтобы прикрутить фильтр к форме достаточно создать экземпляр класса
TFastFilterCommon:
В фильтре работают клавиши Up и Down. Они позволяют перемещаться по датасету не выходя из поля ввода.
Модуль FastFilter содержит два класса: TFastFilter и TFastFilterCommon. Первый содержит только статические функции, которые реализуют необходимые обработчики в общем виде. Второй заточен под стандартный TEdit. Если используется нестандартный вариант TEdit, то потребуется создать дополнительный класс TFastFilterXXX (или вызывать функции TFastFilter прямо из формы).
Важное ограничение: FastFilter предполагает, что все данные, по которым нужно вести фильтрацию, содержатся в отображаемом на экране DataSet. Но так бывает далеко не всегда. Пример - фильтрация списка клиентов по номерам телефонов. Номера телефонов хранятся, естественно, в другом DataSet, отдельно от списка клиентов, т.к. у каждого клиента может быть по несколько телефонов. В подобных случаях стандартный TFastFilterCommon потребует доработки.
Исходные коды FastFilter и демонстрационного приложения.
Browse source codes,
download source codes, view short description in English.
Одним из вариантов такого поиска является динамическая фильтрация списка по ключевым словам. Динамическая - значит налету, прямо в процессе ввода ключевых слов пользователем. Скажем, у вас есть список сотрудников, в котором отображаются: идентификатор, ФИО, год рождения, адрес. Вы набираете "77 кр". Список сокращается. В нем остаются только те сотрудники, в данных у которых встречаются число 77 и/или слово "кр". Например, с сотрудники с фамилиями "Кравцов", "Окрошкин", проживающие на улице "Крапивина", с почтовым индексом 667770, с 1977 годом рождения, c идентификаторами 77, 777, 6772 и т.п. Поиск может осуществляться по принципу '77 ИЛИ кр' или '77 И кр' - как удобнее.
Подобный способ фильтрации хорош тем, что практически любая информация, известная пользователю, быстро приведет его к нужной записи в списке. Причем приведет быстро, за пару секунд. Главное, чтобы необходимая информация в списке была.
Конечно, можно сделать специальную форму поиска с четырьмя полями: ФИО, адрес, год рождения, идентификатор. Да еще поместить ее в отдельное окно. Можно.. Но зачем? Такие формы оправданы только в случае больших массивов данных, когда поиск ведется с помощью SQL-запросов и для ускорения поиска используются индексы по полям таблиц базы данных. Если же список целиком сидит в памяти (десяток, сотня, несколько тысяч строк), то достаточно одной строки поиска, которая легко помещается рядом со списком. Согласитесь, что место для одного дополнительного поля ввода над/под списком выделить можно всегда.
Теперь о реализации такого фильтра на Delphi. Здесь два важных момента. Во-первых, фильтр должен прикручиваться к любой форме буквально одной-двумя строками кода. Тогда его будет удобно использовать. Во-вторых, фильтр должен быть ориентирован на работу TDataSet, т.к. именно этот класс (и его наследники) используется для хранения списков в клиентских приложениях для работы с базами данных.
Для себя я реализовал такой фильтр в виде модуля FastFilter. Делать отдельный компонент я не стал - фильтр должен годится и для приложений, использующих не только стандартный TEdit, но и сторонние компоненты ввода.
Чтобы прикрутить фильтр к форме достаточно создать экземпляр класса
TFastFilterCommon:
TForm1 = class(TForm) ... private m_F: TFastFilterCommon; end; procedure TForm1.FormCreate(Sender: TObject); begin m_F := TFastFilterCommon.Create( editFilter //поле ввода фильтра , dataSetToFilter //фильтруемый DataSet , false //'a b' означает 'a ИЛИ b' (а не 'a И b') ); end; procedure TForm1.FormDestroy(Sender: TObject); begin m_F.Free; end;Фильтрация будет вестись по всем полям DataSet. Если нужна фильтрация только по выбранным полям, то нужно перечислить эти поля в конструкторе:
m_F := TFastFilterCommon.Create( editFilter //поле ввода фильтра , dataSetToFilter //фильтруемый DataSet , ['Name', 'Surname', 'Address'] //поля для фильтрации , false //'a b' означает 'a ИЛИ b' (а не 'a И b') );Класс содержит внутренний таймер, определяющий задержку между завершением ввода и началом фильтрации. Пользователь набрал слово, прошло 0.2 сек, если пользователь ничего больше не нажимает, начинается фильтрация. Если пользователь не хочет ждать, он может нажать Ctrl+Enter и фильтрация начнется мгновенно.
В фильтре работают клавиши Up и Down. Они позволяют перемещаться по датасету не выходя из поля ввода.
Модуль FastFilter содержит два класса: TFastFilter и TFastFilterCommon. Первый содержит только статические функции, которые реализуют необходимые обработчики в общем виде. Второй заточен под стандартный TEdit. Если используется нестандартный вариант TEdit, то потребуется создать дополнительный класс TFastFilterXXX (или вызывать функции TFastFilter прямо из формы).
Важное ограничение: FastFilter предполагает, что все данные, по которым нужно вести фильтрацию, содержатся в отображаемом на экране DataSet. Но так бывает далеко не всегда. Пример - фильтрация списка клиентов по номерам телефонов. Номера телефонов хранятся, естественно, в другом DataSet, отдельно от списка клиентов, т.к. у каждого клиента может быть по несколько телефонов. В подобных случаях стандартный TFastFilterCommon потребует доработки.
Browse source codes,
download source codes, view short description in English.
Подписаться на:
Сообщения (Atom)