Tratamento de Eventos: Uma ação para vários IDs
Autor: Edgar Diniz | Publicado: 2009-09-17T12:31:00-03:00

Quando falei sobre o mapa de mensagens (aqui), eu adicionei quatro mapeamentos. Entretanto, todos eles eram do mesmo tipo e chamavam a mesma ação; vejam:

FXMAPFUNC(SEL_COMMAND,
  FoxTutorialMainWindow::ID_INFORMATION,
  FoxTutorialMainWindow::onCmdMessage),

FXMAPFUNC(SEL_COMMAND,
  FoxTutorialMainWindow::ID_QUESTION,
  FoxTutorialMainWindow::onCmdMessage),

FXMAPFUNC(SEL_COMMAND,
  FoxTutorialMainWindow::ID_WARNING,
  FoxTutorialMainWindow::onCmdMessage),

FXMAPFUNC(SEL_COMMAND,
  FoxTutorialMainWindow::ID_ERROR,
  FoxTutorialMainWindow::onCmdMessage),
Uma situação dessas ocorre quando eu tenho várias ações muito semelhantes: em vez de definir um callback para cada ação, eu defino apenas um; a diferença entre eles é feita dentro do corpo da função.

O FOX Toolkit fornece uma macro auxiliar para facilitar essa tarefa: FXMAPFUNCS. Neste tópico, explicarei como usá-la.


Definindo uma ação para vários IDs

Diante de uma situação dessas, seria bem mais fácil se eu pudesse diminuir o número de linhas a serem escritas. A propósito, o FOX Toolkit tem um slogan que diz: "Cada linha de código não escrita é uma linha correta". Pense no caso de uma calculadora: cada botão executa uma ação bem semelhante um ao outro: adicionar o seu valor à expressão e atualizar a caixa de texto que a exibe.

Imagina se eu tivesse que escrever uma função para cada botão? Seriam 10 botões de dígitos + 5 operações (contanto com a igualdade), isso apenas para uma calculadora básica. 15 funções, 15 entradas no mapa.

Para facilitar, pode-se definir apenas uma função e ligá-las todas aos botões da calculadora. E para evitar as quinze entradas no mapa, utiliza-se a macro FXMAPFUNCS. Para não ficar maçante, e reaproveitar os códigos já escritos, vou voltar ao exemplo do tutorial sobre as mensagens exibidas ao usuário.


Declarando as mensagens

Para poder utilizar a macro FXMAPFUNCS, é necessário que os IDs das mensagens sejam declarados seqüencialmente. Isso porque a macro pega um intervalo de IDs. No nosso exemplo:

enum {
  ID_INFORMATION = FXMainWindow::ID_LAST,
  ID_QUESTION,
  ID_WARNING,
  ID_ERROR,

  ID_LAST,
};

Com isso, basta usar a macro mágica. A sintaxe dela é:

FXMAPFUNCS(tipo, ID_min, ID_max, callback)

No nosso exemplo, o mapa de mensagens fica assim:

FXDEFMAP(FoxTutorialMainWindow) FoxTutorialMainWindowMap[] = {
  FXMAPFUNCS(SEL_COMMAND,
             FoxTutorialMainWindow::ID_INFORMATION,
             FoxTutorialMainWindow::ID_ERROR,
             FoxTutorialMainWindow::onCmdMessage),
};

Ou seja, com apenas uma chamada, eu defini a mesma ação para quatro mensagens diferentes.


Diferenciando as mensagens

Já que cada evento vai disparar a mesma ação, é preciso diferenciar os diferentes eventos. Todo callback no FOX Toolkit tem o mesmo formato (com os nomes típicos dos parâmetros):

long onAction(FXObject *sender, FXSelector sel, void *ptr);

Neste caso, o que mais importa é o segundo parâmetro (sempre que um parâmetro não for utilizado, pode permanecer anônimo). Um FXSelector nada mais é que uma mistura entre o tipo de mensagem e o ID da mensagem. Essa mistura é feita automaticamente pela macro FXSEL ao se acrescentar uma entrada no mapa. Apenas adiantando uma informação, essa macro também é utilizada ao se delegar ações; falarei sobre isso no futuro.

Já que o FOX junta essas duas informações, ele pode separá-las: FXSELID recupera o ID da mensagem, e FXSELTYPE recupera o tipo da mensagem. Sendo assim, fica fácil saber quem enviou a mensagem, sem precisar recorrer a ponteiros (como normalmente acontece com uma certa linguagem que dizem não ter ponteiros). Um teste switch resolve o caso:

long FoxTutorialMainWindow::onCmdMessage(
        FXObject*, FXSelector sel, void*) {
  switch (FXSELID(sel)) {
    case ID_INFORMATION:
      FXMessageBox::information(this, MBOX_OK, "Informação",
              "Operação finalizada");
    break;

    case ID_QUESTION:
      FXMessageBox::question(this, MBOX_YES_NO, "Sair",
              "Deseja realmente sair do programa?");
    break;

    case ID_WARNING:
      FXMessageBox::warning(this, MBOX_OK, "Aviso",
              "Valor não especificado.\nAtribuindo padrão 1.");
    break;

    case ID_ERROR:
      FXMessageBox::error(this, MBOX_OK, "Erro",
              "%s: Arquivo não encontrado", filename.text());
    break;

    default: break;
  }

  return 1;
}

Percebam que como eu não uso nem o primeiro nem o último parâmetro, deixei eles anônimos. Dar um nome a eles pode gerar um warning enjoado por parte do compilador.


Conclusão

Neste exemplo, utilizei FXSELID para recuperar o ID da mensagem dentro do FXSelector. Mas eu falei também de FXSELTYPE, para recuperar o tipo. Isso me dá a chance de definir a mesma ação para diferentes tipos e diferenciá-los dentro da função.

Não tenho certeza se dá para fazer algo semelhante no mapa; existem duas macros, FXMAPTYPE e FXMAPTYPES, mas nunca as utilizei. Parece que elas definem um mapeamento de um tipo de mensagem (ou intervalo de tipos, no caso da segunda) para todos os IDs possíveis, chamando a mesma ação. Resumindo: melhor não usar.

Com relação ao mapa de mensagens, acho que só falta falar de quando uma janela não trata nenhum evento. Mas se eu lembrar de mais alguma outra coisa, eu posto aqui. Até a próxima.