1

Тема: OpenDialog

Данный пример показывает использование компонента OpenDialog, который позволяет выбрать имя файла для открытия, сохранения а также для выбора директории.
Выбранное имя выводится в заголовок окна.
По нажатию клавиши происходит изменение фильтра.

Есть один нюанс, связанный с форматом структуры для фильтра файлов по расширению.
Это представляет собой разделённые нулями строковые последовательности, пример:

'txt'#0'png'#0'bmp'#0

перед такой последовательностью находится 4-ёх байтовое значение — размер структуры.
Почти как ShortString, только под размер выделено 4 байта вместо одного.
По-нормальному пользоваться этим не очень удобно.
Поэтому я сделал дополнительно функцию SetFilter, которая принимает в качестве параметра просто указатель на строку, в которой расширения перечислены через запятую, вот так:

SetFilter('txt,inc,asm');

затем эта функция преобразует строку вот так:

'txt,png,bmp' -> 'txt'#0'png'#0'bmp'#0

Я думаю, что правильнее будет вынести это в отдельный модуль, который при необходимости будет подключаться к проекту:

uses OpenDlg;

Ну и пока непонятно, в каком виде лучше оставить структуру TOpenDialogFilter.

код примера
program OpenDialogTest;

uses
  KolibriOS;

Const
// OpenDialog.Mode constants
  ODM_OPEN = 0;
  ODM_SAVE = 1;
  ODM_DIR  = 2;

// OpenDialog.Status constants
  ODS_CANCEL = 0;
  ODS_OK     = 1;
  ODS_ALTER  = 2;

type
// actually structure is:
//   first four bytes - size of this structure
//   and next are zero separated string values
  TOpenDialogFilter = packed record
  case Byte of
    0:(Size: LongWord;
      Text: array [0..SizeOf(ShortString) - 1] of AnsiChar;);
    1:(_: array [0..2] of Byte;
      Str: ShortString;)
  end;

  TOpenDialog = packed record
    Mode:           LongWord;
    ProcInfo:       Pointer;
    ComAreaName:    PAnsiChar;
    ComArea:        Pointer;
    OpenDirPath:    PAnsiChar;
    DirDefaultPath: PAnsiChar;
    StartPath:      PAnsiChar;
    DrawWindow:     procedure;
    Status:         LongWord;
    OpenFilePath:   PAnsiChar;
    FileNameArea:   PAnsiChar;
    FilterArea:     ^TOpenDialogFilter;
    XSize:          Word;     // at least 350
    XStart:         SmallInt;
    YSize:          Word;     // at least 250
    YStart:         SmallInt;
  end;

const
  FilterText  = 'txt'#0'png'#0'bmp'#0;

  PROC_INFO_BUFFER_SIZE      = SizeOf(TThreadInfo);
  OPEN_FILE_PATH_BUFFER_SIZE = 4096;
  FILE_NAME_AREA_BUFFER_SIZE = 1024;
  OPEN_DIR_PATH_BUFFER_SIZE  = OPEN_FILE_PATH_BUFFER_SIZE - FILE_NAME_AREA_BUFFER_SIZE;

const
  OPEN_FILE_BUTTON  = 1000;
  SAVE_FILE_BUTTON  = 2000;
  CHOOSE_DIR_BUTTON = 3000;

var
  ProcLib: Pointer;
  OpenDialogInit: procedure(var OpenDialog: TOpenDialog); stdcall;
  OpenDialogStart: procedure(var OpenDialog: TOpenDialog); stdcall;
  OpenDialog: TOpenDialog;
  OpenDialogFilter: TOpenDialogFilter = (Str: FilterText);

  FileNameAreaBuffer: array [0..FILE_NAME_AREA_BUFFER_SIZE - 1] of AnsiChar;
  OpenFilePathBuffer: array [0..OPEN_FILE_PATH_BUFFER_SIZE - 1] of AnsiChar;
  OpenDirPathBuffer: array [0..OPEN_DIR_PATH_BUFFER_SIZE - 1] of AnsiChar;
  ProcInfoBuffer: array [0..PROC_INFO_BUFFER_SIZE - 1] of Byte;

  WndLeft, WndTop, WndWidth, WndHeight: Integer;

// copy comma separated string values from Filter
// to OpenDialogFilter and replace commas with zeroes
// 'txt,png,bmp' -> 'txt'#0'png'#0'bmp'#0
procedure SetFilter(Filter: PAnsiChar);
var
  i: LongWord;
begin
  i := 0;
  with OpenDialogFilter do
  begin
    Text[High(Text)] := #0;
    repeat
      if Filter[i] = ',' then
        Text[i] := #0
      else
        Text[i] := Filter[i];
      Inc(i);
    until (Filter[i - 1] = #0) or (i = High(Text));
    Size := i + SizeOf(Size);
  end;
end;

procedure On_Redraw;
begin
  BeginDraw;
  DrawWindow(WndLeft, WndTop, WndWidth, WndHeight, 'Test OpenDialog, press key to set filter to "txt,inc,asm"', $00FFFFFF,
    WS_SKINNED_SIZABLE + WS_CLIENT_COORDS + WS_CAPTION, CAPTION_MOVABLE);
  DrawButton(8, 8, 115, 33, $00DFDFFF, 0, OPEN_FILE_BUTTON);
  DrawButton(8, 48, 115, 33, $00DFFFDF, 0, SAVE_FILE_BUTTON);
  DrawButton(8, 88, 115, 33, $00FFDFDF, 0, CHOOSE_DIR_BUTTON);
  DrawText(13, 16, 'Open file', $00000070, 0, DT_CP866_8X16 + DT_TRANSPARENT_FILL + DT_ZSTRING, 0);
  DrawText(13, 56, 'Save file', $00007000, 0, DT_CP866_8X16 + DT_TRANSPARENT_FILL + DT_ZSTRING, 0);
  DrawText(13, 96, 'Choose dir', $00700000, 0, DT_CP866_8X16 + DT_TRANSPARENT_FILL + DT_ZSTRING, 0);
  EndDraw;
end;

begin
  ProcLib         := LoadLibrary('/sys/lib/proc_lib.obj');
  OpenDialogInit  := GetProcAddress(proclib, 'OpenDialog_init');
  OpenDialogStart := GetProcAddress(proclib, 'OpenDialog_start');

  with OpenDialogFilter do
    Size := Byte(Str[0]) + SizeOf(Size);

  with OpenDialog do
  begin
    Mode           := ODM_OPEN;
    ProcInfo       := @ProcInfoBuffer;
    ComAreaName    := 'FFFFFFFF_open_dialog';
    OpenDirPath    := @OpenDirPathBuffer[0];
    DirDefaultPath := '/sys';
    StartPath      := '/sys/File managers/opendial';
    DrawWindow     := On_Redraw;
    OpenFilePath   := @OpenFilePathBuffer[0];
    FileNameArea   := @FileNameAreaBuffer[0];
    FilterArea     := @OpenDialogFilter;
    XSize          := 400;
    YSize          := 640;
  end;

  OpenDialogInit(OpenDialog);

  with GetScreenSize do
  begin
    WndWidth := Width div 2;
    WndHeight := Height div 2;
    WndLeft := (Width - WndWidth) div 2;
    WndTop := (Height - WndHeight) div 2;
  end;

  while True do
    case WaitEvent of
      REDRAW_EVENT:
        On_Redraw;
      KEY_EVENT:
        begin
          GetKey;
          SetFilter('txt,inc,asm');
        end;
      BUTTON_EVENT:
        begin
          case GetButton.ID of
            OPEN_FILE_BUTTON:
              OpenDialog.Mode := ODM_OPEN;
            SAVE_FILE_BUTTON:
              OpenDialog.Mode := ODM_SAVE;
            CHOOSE_DIR_BUTTON:
              OpenDialog.Mode := ODM_DIR;
          else
            Break;
          end;
          OpenDialogStart(OpenDialog);
          with OpenDialog do
            if Status = ODS_OK then
              SetWindowCaption(OpenFilePath);
        end;
    end;
end.

2

Re: OpenDialog

В связи с вновь возникшими планами нужно сделать полноценную обертку proc_lib и расширить пример до использования обоих диалогов.