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

Перевод плагина на FAR 2.X

В FAR API 2.X был внесен ряд серьезных изменений, по сравнению с FAR API 1.X. Все подробности изложены в Encyclopedia for Developers. Я же хочу рассказать о тех изменениях, с которыми я столкнулся при переводе плагина Named Folders на FAR API 2.X.

wchar_t
Самое очевидное изменение: Far 2.0 - юникодный, поэтому char везде заменяется на wchar_t.

Экспорт функций
Раньше экспорт функций нужно было прописывать в коде плагина вручную:
extern "C" {
 void WINAPI _export SetStartupInfoW(struct PluginStartupInfo const* Info);
 HANDLE WINAPI _export OpenPluginW(int OpenFrom,int Item);
 ...
}
Теперь экспорт всех функций прописан в стандартном файле "plugin.hpp".

Изменились, так же, сигнатуры некоторых функций. В частности: PutFilesW, OpenPluginW, SetStartupInfoW, MakeDirectoryW и т.д.

Функция Control
Изменилась функция Control:
int WINAPI Control(HANDLE hPlugin, int Command,  void *Param); //Far 1.X
int WINAPI Control(HANDLE hPlugin, int Command,  int Param1,  LONG_PTR Param2); //Far 2.X
Соответственно, изменились и вызовы:
extern struct PluginStartupInfo g_PluginInfo; 

//Far 1.X
std::string dir_oem;
g_PluginInfo.Control(INVALID_HANDLE_VALUE
  , bActivePanel ? FCTL_SETPANELDIR : FCTL_SETANOTHERPANELDIR
  , (void*)dir_oem.c_str());

//Far 2.X
std::wstring dir;
g_PluginInfo.Control( (bActivePanel ? PANEL_ACTIVE : PANEL_PASSIVE)
  , FCTL_SETPANELDIR
  , 0 
  , reinterpret_cast<LONG_PTR>(dir.c_str()));

Структура PanelInfo
В PanelInfo пропали некоторые поля, например ColumnTypes, ColumnWidths, CurDir - код нужно изменять. Пример:
//Far 1.X
PanelInfo& panel_info = plugin.GetPanelInfo(true)
std::string cur_dir = panel_info.CurDir;

//Far 2.X
extern struct PluginStartupInfo g_PluginInfo; 
std::wstring GetPanelCurDir(bool bActivePanel) {
   wchar_t buffer[1024];
   if (! g_PluginInfo.Control( (bActivePanel ? PANEL_ACTIVE : PANEL_PASSIVE)
 , FCTL_GETPANELDIR
 , 1024
 , reinterpret_cast<LONG_PTR>(&buffer[0])
   )) return L"";
   return reinterpret_cast<whcar_t const*>(&buffer[0]);
}

std::wstring cur_dir = GetPanelCurDir(true);
Кроме того, из PanelInfo исчезли поля PanelItems и SelectedItems. Нужен список выделенных элементов панели? Придется написать что-то типа:
extern struct PluginStartupInfo g_PluginInfo; 
PluginPanelItem* allocate_PluginPanelItem(HANDLE hplugin
  , int Command, int nSelectedItem) 
{
  PluginPanelItem* ppi = reinterpret_cast<PluginPanelItem*>(
    malloc(g_PluginInfo.Control(hplugin, Command, nSelectedItem, NULL)));
  g_PluginInfo.Control(hplugin, Command, nSelectedItem
    , reinterpret_cast<LONG_PTR>(ppi));
  return ppi;
}

void deallocate_PluginPanelItem(PluginPanelItem* p) {
 free(p);
}
...
PluginPanelItem *ppi = allocate_PluginPanelItem(pPanel
  , FCTL_GETSELECTEDPANELITEM, nSelectedItem);
std::wstring file_name = ppi->FindData.lpwszFileName;
..
deallocate_PluginPanelItem(ppi);

FarDialogItem
В FarDialogItem два серьезных изменения. Во-первых, массив char Data[512] заменен на const wchar_t *PtrData. Т.е. строка инициализации элемента диалога более не хранится в FarDialogItem, хранится только указатель на нее. А это значит, что саму строку придется хранить где-то еще. Если у вас есть массив структур FarDialogItem, вам потребуется завести дополнительный массив строковых буферов.

Во-вторых, ранее, после закрытия диалога, в FarDialogItem сохранялись результирующие значения. Т.е через поля FarDialogItem можно было получить измененный текст, измененное значение флага checkbox и т.д. Теперь структура FarDialogItem является неизменной. А измененные значения получают через SendDlgMessage:
Far 2.X: //get modified data from dialog after dialog closing
extern struct PluginStartupInfo g_PluginInfo; 
HANDLE m_DialogHandle = g_PluginInfo.DialogInit(...)

std::wstring GetDialogItemValue(int dialogItemId) {
  assert(m_DialogHandle != 0);
  FarDialogItemData fdi;
  fdi.PtrLength = g_PluginInfo.SendDlgMessage(m_DialogHandle
     , DM_GETTEXTLENGTH, dialogItemId, 0);
  std::wstring buffer;
  buffer.resize(fdi.PtrLength + 1);  
  fdi.PtrData = &buffer[0];
  fdi.PtrData[0] = L'0';
  g_PluginInfo.SendDlgMessage(m_DialogHandle, DM_GETTEXT
     , dialogItemId, reinterpret_cast<LONG_PTRY>(&fdi));
  return fdi.PtrData;
}

bool IsDialogItemSelected(int dialogItemId) {
  assert(m_DialogHandle != 0);
  return g_PluginInfo.SendDlgMessage(m_DialogHandle
    , DM_GETCHECK, dialogItemId, 0) == BSTATE_CHECKED;
}

Вызов диалога
Вместо функций Dialog и DialogEx теперь нужно использовать DialogInit, DialogFree, DialogRun и объявлять собственную DlgProc.
Far 2.X:
extern struct PluginStartupInfo g_PluginInfo; 

static LONG_PTR WINAPI dlg_proc(HANDLE hDlg, int Msg
  , int Param1, LONG_PTR Param2) 
{
  return g_PluginInfo.DefDlgProc(hDlg, Msg
    , Param1, Param2);
}
...

//инициализация списка элементов диалога
FarDialogItem* dialog_items = ...; 
int dialog_items_count = ...;

HANDLE hdlg = g_PluginInfo.DialogInit(
  g_PluginInfo.ModuleNumber 
  , -1 
  , -1  
  , 20 //dialog width
  , 5 //dialog height
  , NULL //help topic
  , dialog_items
  , dialog_items_count
  , 0 //reserved
  , 0 //flags
  , &::dlg_proc
  , 0 //param
);  
int nChoosenItem = g_PluginInfo.DialogRun(hdlg); 
g_PluginInfo.DialogFree(hdlg);

Резюме
В целом, изменений не так много. Самое неприятное - пришлось заводить буферы для хранения строк для FarDialogItem. Вначале думал завести структуру FarDialogItem_wrapper, в которой хранить рядом FarDialogItem и буфер. Не получилось, т.к. во многих случаях требуется передавать в FAR массивы элементов FarDialogItem, и врапперы здесь не проходят. Так что теперь приходится в коде поддерживать два параллельных массива - FarDialogItem и boost::shared_ptr<std::wstring>.

Комментариев нет:

Отправить комментарий