вторник, 19 октября 2010 г.

Named Folders 3.0. Первый снапшот

Выложил первый снапшот Named Folders 3.0. Основные изменения: рефакторинг и чистка кода, перевод плагина на FAR API 2.0 и юникод, выпуск 64-битной версии.

воскресенье, 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-битных приложений обрабатывает неправильно.

четверг, 14 октября 2010 г.

Программирование под Android. С чего начать.

Появилась необходимость написать проект под Android. Засел за гугл и пару дней шерстил интернет на предмет полезной информации. Вот результаты.

среда, 13 октября 2010 г.

Как подружить BOOST_FOREACH с контейнером без "iterator"

В библиотеке stlsoft есть полезный класс
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 {
      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){}
Поскольку в findfile_sequence_w все функции константные, вреда от такой подмены не будет, а BOOST_FOREACH заработает как надо.

четверг, 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: из-за вот этого бага (фичи?) переключение проекта на альтернативный аккаунт не решило проблемы :(

воскресенье, 3 октября 2010 г.

Каждому списку - по фильтру.

Вам приходилось сталкиваться с программами, в которых значение поля выбирается из длинного списка, а поиска по списку не предусмотрено? Работать с такими программами - сущее мучение. Всегда снабжайте списки средствами быстрого поиска. Ваши пользователи скажут вам большое спасибо.

Одним из вариантов такого поиска является динамическая фильтрация списка по ключевым словам. Динамическая - значит налету, прямо в процессе ввода ключевых слов пользователем. Скажем, у вас есть список сотрудников, в котором отображаются: идентификатор, ФИО, год рождения, адрес. Вы набираете "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 потребует доработки.

Исходные коды FastFilter и демонстрационного приложения.

Browse source codes,
download source codes, view short description in English.