Como usar as janelas Abrir e Salvar do Windows Vista e 7 no Lazarus

por Marcos Elias

{essa é uma dica de programação para Win32/Win64 usando o Lazarus / FreePascal, não interessa a usuários comuns}

As versões atuais do Lazarus / FreePascal ainda não foram atualizadas com as chamadas da API do Windows que exibe as janelas novas de Abrir e Salvar (new OpenDialog and SaveDialog), usadas no Windows Vista e 7. Em algum momento isso deverá ocorrer, mas enquanto não atualizam…

Dá para chamá-las diretamente, sem se prender à LCL e/ou ao compilador. Eu não testei no Delphi, mas há algo parecido para as versões antigas, que também usavam os programas com janelas “velhas”. Fica meio feio usar um programa atualmente e as janelas comuns não corresponderem ao encontrado no restante do sistema. E não é exclusividade de Windows. Quem usa Linux sabe o desgosto visual que é lidar com as telas de Abrir e Salvar do GTK1 sobre GTK2 :P Além do visual tem a questão da praticidade. No Windows Vista+ há um campo “Pesquisar” nas telas Abrir e Salvar que serve como filtro de arquivos na pasta atual, isso ajuda demais.

Veja como são as janelas antigas:

E as novas:

Algo parecido ocorreu com a mudança do Windows 98 pro 2000/Me/XP, onde apareceu aquela barra “Locais” na esquerda, que também ajuda muito perto da janela simples do Windows 95/98. Eis a antigona:

Eu achei uma unit para isso no DotFusion:

{
  VistaOpenSaveDialog:
  A new Open/Save dialog that uses Windows API to display Vista
  compatiable Open or Save Dialog.
 
  Tested in Windows 2000/XP/Vista/Vista x64.
  Compiled with Lazarus 0.9.29 beta (21448) / FreePascal 2.2.4
  It should compile successfully with all newer and maybe some the versions.
 
  Created by Iskren Slavov (http://www.dotfusion.net/).
  No right reserved. Feel free to use for any of your applications
  regardless of the license.
 
  ##########################################
 
  Example usage:
  VistaOpenSaveDialg(Handle, '', '', '', 'Open file...', fileName,
    OFN_FILEMUSTEXIST, VDT_OPENDIALOG);
 
  Some usable flags under Windows Vista:
  OFN_READONLY, OFN_HIDEREADONLY,
  OFN_OVERWRITEPROMPT, OFN_FILEMUSTEXIST,
  OFN_PATHMUSTEXIST, OFN_FORCESHOWHIDDEN,
  OFN_DONTADDTORECENT
}
 
unit VistaOpenSaveDlg;
 
{$mode objfpc}{$H+}
 
interface
 
uses
  Windows, Messages, CommDlg, SysUtils;
 
type
  TVistaDlgType = (VDT_OPENDIALOG, VDT_SAVEDIALOG);
 
  function ReplaceStr(const S, Srch, Replace: string): string;
  function VistaOpenSaveDialog(Parent: HWND; const Filters,
    DefaultExtension, InitialDir, Title: string; var FileName: string;
    DlgFlags: DWORD; DlgType: TVistaDlgType): Boolean;
 
implementation
 
function ReplaceStr(const S, Srch, Replace: string): string;
var
  i: Integer;
  Source: string;
begin
  Source := S;
  Result := '';
  repeat
    i := Pos(UpperCase(Srch), UpperCase(Source));
    if i > 0 then
    begin
      Result := Result + Copy(Source, 1, i - 1) + Replace;
      Source := Copy(Source, i + Length(Srch), MaxInt);
    end
    else
      Result := Result + Source;
  until i <= 0;
end;
 
function VistaOpenSaveDialog(Parent: HWND; const Filters, DefaultExtension, InitialDir, Title: String;
  var FileName: String; DlgFlags: DWORD; DlgType: TVistaDlgType): Boolean;
var
  fnStruct: TOpenFileName;
  szFile: array[0..MAX_PATH] of Char;
begin
  Result := False;
  FillChar(fnStruct, SizeOf(TOpenFileName), 0);
  with fnStruct do
  begin
    hwndOwner := Parent;
    lStructSize := SizeOf(TOpenFileName);
    lpstrFile := szFile;
    StrPCopy(lpstrFile, FileName);
    nMaxFile := SizeOf(szFile);
    lpstrFilter := PChar(ReplaceStr(Filters, '|', #0) + #0#0);
    if (Title <> '') then
      lpstrTitle := PChar(Title);
    if (InitialDir <> '') then
      lpstrInitialDir := PChar(InitialDir);
    if DefaultExtension <> '' then
      lpstrDefExt := PChar(DefaultExtension);
 
    Flags := Flags or DlgFlags;
  end;
 
  case DlgType of
    VDT_OPENDIALOG:
      if GetOpenFileName(@fnStruct) then
      begin
        Result := True;
        FileName := StrPas(szFile);
      end;
    VDT_SAVEDIALOG:
      if GetSaveFileName(@fnStruct) then
      begin
        Result := True;
        FileName := StrPas(szFile);
      end;
  end;
end;
end.

Basta salvá-na num arquivo VistaOpenSaveDlg.pas na pasta do projeto, e incluir VistaOpenSaveDlg na uses. Para chamar as janelas você deverá criar uma variável para receber o retorno (o nome do arquivo), por exemplo:

Vista e Seven OpenDialog (abrir):

var
  arquivo: String;
begin
  VistaOpenSaveDialog(Handle,  'Text|*.txt|All|*.*', '.txt', '', 'Open file', arquivo, OFN_FILEMUSTEXIST, VDT_OPENDIALOG);
  //Faça alguma coisa com a variável arquivo...
end;

Vista e Seven SaveDialog (salvar):

var
  arquivo: String;
begin
  VistaOpenSaveDialog(Handle,  'Text|*.txt|All|*.*', '.txt', '', 'Save file', arquivo, OFN_OVERWRITEPROMPT, VDT_SAVEDIALOG);
  //Faça alguma coisa com a variável arquivo...
end;

As flags podem ser as citadas no comentário no começo da unit.

E no Linux, Mac OS X, Windows CE, Windows Mobile, etc?

Por fim, há um pequeno grande problema se suas aplicações forem multiplataforma: esse código é exclusivo para Windows. Por isso para usar em aplicações multiplataforma você deve usar um ifdef… Uma condicional para o compilador.

Um exemplo pronto, que estou usando no Mep LA (projeto que provavelmente substituirá o Mep Texto):

Na uses:

{$IFDEF WINDOWS}, Windows, VistaOpenSaveDlg{$ENDIF}

Para abrir e salvar, coloque um componente normal OpenDialog e SaveDialog. E chame-os assim:

Abrir:

var
arquivo: String;
...
  {$IFDEF WINDOWS}
  VistaOpenSaveDialog(Self.Handle, 'Text files|*.txt|All|*', '.txt', GetUserDir, 'Open file', arquivo, OFN_FILEMUSTEXIST, VDT_OPENDIALOG);
  {$ELSE}
  if not OpenDialog1.Execute then Exit;
  arquivo := OpenDialog1.Filename;
  {$ENDIF}

Salvar:

var
 ArqNovo: String;
...
ArqNovo := '';
   {$IFDEF WINDOWS}
   VistaOpenSaveDialog(Self.Handle, 'Text files|*.txt|All|*', '.txt', GetUserDir, 'Save file', ArqNovo, OFN_OVERWRITEPROMPT + OFN_PATHMUSTEXIST, VDT_SAVEDIALOG);
   if ArqNovo = '' then
     Exit;
   {$ELSE}
   if not SaveDialog1.Execute then Exit;
   ArqNovo := SaveDialog1.Filename;
   {$ENDIF}

Os exemplos devem ser adaptados ao seu gosto e caso, mas é basicamente isso. Bom que funciona sem precisar esperar o compilador do FreePascal implementar as novas chamadas. Assim que elas forem inclusas no compilador nativamente, você poderá voltar a usar os componentes OpenDialog e SaveDialog do Lazarus em todos os projetos.

comments powered by Disqus