Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Otros temas > Trucos
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Los mejores trucos

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 01-07-2006
Avatar de dec
dec dec is offline
Moderador
 
Registrado: dic 2004
Ubicación: Alcobendas, Madrid, España
Posts: 13.107
Poder: 34
dec Tiene un aura espectaculardec Tiene un aura espectacular
Primeros pasos con las Open Tools

Open Tools API es la nueva interfaz para la programación de extensiones de los IDE de Delphi y C++ Builder, que acompaña a las últimas versiones de estos productos. Estas extensiones nunca han sido bien documentadas por Borland pero, gracias a Dios, el excelente libro "Hidden Paths of Delphi", de Ray Lischner, es todavía la mejor introducción al tema que conozco. Aunque este libro no trata la nueva OpenTools API, puede encontrar ayuda sobre la misma en la página de Mr. Lischner: www.tempest-sw.com. De hecho, mi primera experiencia con OTAPI comenzó por la lectura de la información de esa página. Este artículo no intenta ser una exposición en profundidad de esta interfaz. Pero, siendo de nivel introductorio, puede ayudar a dar los primeros pasos a quien desee aprovechar la OTAPI para desarrollar expertos sencillos para uso personal.

¿Qué vamos a hacer ahora? Voy a explicar, paso a paso, cómo crear un experto sencillo que añada una opción de menú en el entorno de Delphi, y que permita realizar alguna operación sencilla sobre el formulario activo del proyecto activo (si es que hay alguno).

1. Crear un package para el experto.
2. Crear el objeto experto y registrarlo.
3. Añadir un módulo de datos.
4. Crear la estructura de menú de nuestro experto.
5. Funciones auxiliares que utilizan la OTAPI.
6. Las acciones del menú.

1. Crear un package para el experto

Limpie el espacio de trabajo del IDE con el comando File|Close all. Abra el depósito de objetos (File|New) y en la primera página realice un doble clic sobre Package. Guarde este fichero en un directorio aparte y bautícelo con un nombre sonoro, a su elección.

¿Por qué un package? Es que también se pueden crear expertos que residen en DLLs. Pero es más complicado instalarlos y desinstalarlos. Además, veremos que es mucho más fácil acceder directamente a los recursos del propio IDE si utilizamos un package en vez de una DLL.

Lo siguiente es modificar las opciones del package. Pulse el botón Options del editor del package. La opción que debe cambiar obligatoriamente es Usage options, que debe cambiarse a Designtime only. Es conveniente asignar Description (ojo con los apóstrofos que a veces dan problemas; yo los evito).

Ahora bien, para ahorrarme quebraderos de cabeza con toda la porquería que instalo y desinstalo habitualmente, yo tengo un directorio AddOns que cuelga directamente del directorio raíz de Delphi 4. Ahí es donde pruebo toda la bazofia que genero. Por lo tanto, para mí es muy importante ir a la página de directorios del diálogo de opciones, y asignar a Output directory la siguiente macro:

Código:
$(DELPHI)\AddOns
Si se tratase de un runtime package, también modificaría DCP output directory y Unit output directory.

2. Crear el objeto experto y registrarlo.

Ejecute nuevamente File|New y realice un doble clic sobre el objeto Unit. Esto creará una nueva unidad vacía; guárdela con un bonito nombre. Tendrá que ir nuevamente al editor del package para añadir la nueva unidad al proyecto. Utilice para esto el botón Add, y seleccione el fichero que contiene la unidad. Dentro de la unidad que acabamos de crear definiremos el objeto que representará al experto a los ojos de Delphi. Lo haremos en la sección interface de la unidad:

Código Delphi [-]
type
  TimExpertTemplate = class(TInterfacedObject, IOTAWizard, IOTANotifier)
    procedure AfterSave;
    procedure BeforeSave;
    procedure Destroyed;
    procedure Modified;
    function GetIDString: string;
    function GetName: string;
    function GetState: TWizardState;
    procedure Execute;
    constructor Create;
    destructor Destroy; override;
  end;

En la cláusula uses de la interfaz debemos añadir, obligatoriamente, la unidad ToolsAPI, que define las interfaces IOTAWizard y IOTANotifier. No voy a entrar en detalles de cómo se implementa una interfaz, ni qué es TInterfacedObject. Hay material suficiente para un libro sobre estos asuntos. El nombre que se la da a la clase no tiene importancia, pero trate por todos los medios que sea poco probable una colisión de nombres: en definitiva, esta clase va a integrarse con las clases de la VCL y del propio entorno de desarrollo de Delphi. Aquí yo he utilizado mi prefijo registrado: im.

Hay que darle un cuerpo a los métodos anteriormente definidos. Mostraré solamente cómo implementar el conjunto mínimo requerido:

Código Delphi [-]
procedure TimExpertTemplate.AfterSave;
begin
end;

procedure TimExpertTemplate.BeforeSave;
begin
end;

constructor TimExpertTemplate.Create;
begin
  imExpertModule := TimExpertModule.Create(nil);
  // Ver más adelante
end;

destructor TimExpertTemplate.Destroy;
begin
  imExpertModule.Free;
  // Ver más adelante
end;

procedure TimExpertTemplate.Destroyed;
begin
end;

procedure TimExpertTemplate.Execute;
begin
end;

function TimExpertTemplate.GetIDString: string;
begin
  Result := 'Su empresa.Nombre del experto';
end;

function TimExpertTemplate.GetName: string;
begin
  Result := 'El nombre descriptivo de su experto';
end;

function TimExpertTemplate.GetState: TWizardState;
begin
  Result := [wsEnabled];
end;

procedure TimExpertTemplate.Modified;
begin
end;

Como ve, la mayor parte de los métodos tienen implementaciones triviales. Pero me he adelantado un poco al implementar el constructor y el destructor. En esos procedimientos estoy creando y destruyendo un objeto de la clase TimExpertModule. Bien, esta clase corresponderá al módulo de datos que crearemos en el siguiente paso. Es decir, cada vez que nuestro experto se instale, creará una instancia del módulo que definiremos en breve.

Falta aún registrar el objeto como un experto para que Delphi pueda utilizarlo. En la sección interface tenemos que declarar el siguiente método:

Código Delphi [-]
procedure Register;

La implementación será así de fácil:

Código Delphi [-]
procedure Register;
begin
  RegisterPackageWizard(TimExpertTemplate.Create as IOTAWizard);
end;

3. Añadir un módulo de datos

Es el momento de añadir el módulo de datos que antes mencionábamos. Vaya a File|New, y realice un doble clic en Data module. Guarde la unidad y su DFM con un nombre que pueda posteriormente recordar. Y no olvide regresar a la unidad del paso anterior para incluir la referencia a la nueva unidad. Finalmente, cambie la propiedad Name del módulo a TimExpertModule.

¿Por qué necesitamos un módulo de datos? La respuesta es que nos servirá de contenedor a objetos componentes que necesitaremos añadir comandos de menú, definir imágenes o lo que nos apetezca.

4. Crear la estructura de menú de nuestro experto.

¿Cuántos comandos de menú añadirá nuestro experto? Por cada comando que añadamos, necesitaremos una acción. Por lo tanto, traiga un componente TActionList sobre el módulo de datos. Cree una acción por cada comando de menú que vaya a definir. No se preocupe de las propiedades ImageIndex (luego veremos por qué) ni Hint (me parece que Delphi no la utilizará). Tenga mucho cuidado con la propiedad ShortCut, pues puede provocar un conflicto de teclado con las opciones nativas de Delphi. Lo mismo le digo respecto a utilizar una letra subrayada en Caption. Para mi ejemplo, definiremos una sola acción, cuyo nombre será ac_im_ArrangeLabels, y su Caption será "Organizar etiquetas".

Lo siguiente es ir a la página Standard y añadir un TPopupMenu al módulo. Es ahí donde debemos crear los menu items que añadiremos al IDE. Si lo desea, puede utilizar separadores, submenús o el truco que le de la gana. Para este ejemplo, crearé solamente dos elementos de menú: el primero será un vulgar separador (Caption = '-'). El segundo comando debe ir inmediatamente debajo del separador, y debemos asociarlo a la única acción que hemos creado. A este elemento lo llamaremos mi_im_ArrangeLabels.

Detalle importante: ¿quiere utilizar un dibujo en su opción de menú? Cree el dibujo en un fichero BMP, a 16 colores y con 16x16 píxeles de área. Utilice la propiedad Bitmap del menu item que acabamos de crear (mi_im_ArrangeLabels) para cargar la imagen.

Cuando el experto se instale, debemos añadir estos comandos a la estructura de menú de Delphi.

Código Delphi [-]
procedure TimExpertModule.imExpertModuleCreate(Sender: TObject);
var
  I, InsertPosition: Integer;
  DataMenu, Item: TMenuItem;
begin
  with BorlandIDEServices as INTAServices do
  begin
    DataMenu := MainMenu.Items[7];  // ¡¡Cuente con cuidado!
    ac_im_ArrangeLabels.ImageIndex :=
      AddMasked(mi_im_ArrangeLabels.Bitmap, clSilver);
  end;
  InsertPosition := DataMenu.Count;
  for I := PopupMenu1.Items.Count - 1 downto 0 do
  begin
    Item := PopupMenu1.Items[i];
    PopupMenu1.Items.Delete(I);
    DataMenu.Insert(InsertPosition, Item);
  end;
end;

¿De que manga me he sacado el número 7 anterior? Mi interés es añadir el menú al final del submenú Database de Delphi. Y este submenú es el octavo elemento de la barra. Observe cómo podemos manipular directamente, sin necesidad de proxies, los propios objetos internos del IDE de Delphi. Observe además como añado una imagen a la lista global de imágenes de Delphi (AddMasked) y le asigno la posición en la que es añadida a la propiedad ImageIndex de la acción.

¿Por qué utilizo el ImageIndex de la acción y no me conformo con la propiedad Bitmap del propio comando de menú? Muy sencillo: si me limito a lo último, el bitmap no aparecerá desactivado cuando la acción desactive al comando por no ser aplicable.

5. Funciones auxiliares que utilizan la OTAPI.

Voy a definir una función sencilla que devuelva el puntero directo al formulario activo del proyecto activo, y que devuelva el puntero vacío nil cuando no exista tal objeto:

Código Delphi [-]
function CurrentForm: TCustomForm;
var
  CurrMod: IOTAModule;
  FormEdt: IOTAFormEditor;
  CurrCom: INTAComponent;
  Comp: TComponent;
  I: Integer;
begin
  Result := nil;
  CurrMod := (BorlandIDEServices as IOTAModuleServices).CurrentModule;
  if CurrMod <> nil then
    for I := 0 to CurrMod.GetModuleFileCount - 1 do
      if CurrMod.GetModuleFileEditor(I).QueryInterface(IOTAFormEditor,
          FormEdt) = S_OK then
        if FormEdt.GetRootComponent <> nil then
          if FormEdt.GetRootComponent.QueryInterface(INTAComponent,
            CurrCom) = S_OK then
          begin
            Comp := CurrCom.GetComponent;
            if Comp is TCustomForm then Result := TCustomForm(Comp);
            Exit;
          end;
end;

Si el lector conoce algo del modelo COM, se extrañará de que esté utilizando QueryInterface para averiguar información sobre interfaces, en vez de utilizar los operadores de alto nivel de Delphi is y as. La explicación es que con QueryInterface mato dos pájaros de un tiro: sé si el objeto dado soporta la interfaz indicada y además obtengo el puntero a dicha interfaz. En caso contrario, tendría primero que llamar a is, para evitar una excepción, y luego a as. Esta función puede modificarse muy fácilmente si lo que necesitamos saber es cuál es el proyecto activo.

6. Las acciones del menú.

Una vez definida la función anterior, podemos indicar muy fácilmente cuándo es aplicable la acción que ejecuta nuestro experto. Debemos interceptar el evento OnUpdate de la acción:

Código Delphi [-]
procedure TimExpertModule.ac_im_ArrangeLabelsUpdate(Sender: TObject);
begin
  TAction(Sender).Enabled := CurrentForm <> nil;
end;

Igual de fácil es ejecutar la acción:

Código Delphi [-]
procedure TimExpertModule.ac_im_ArrangeLabelsExecute(Sender: TObject);
begin
  OrganizarEtiquetas(CurrentForm);
end;

OrganizarEtiquetas es la función que contiene el meollo del experto. Este es el código de ejemplo:

Código Delphi [-]
function CanFormat(C: TControl): Boolean;
begin
  Result := (C is TCustomEdit) and not (C is TCustomMemo)
    or (C is TCustomComboBox)
    or (C is TDBLookupComboBox);
end;

procedure OrganizarEtiquetas(AForm: TCustomForm);
var
  I: Integer;
  ALabel: TLabel;
begin
  for I := 0 to AForm.ComponentCount - 1 do
    if AForm.Components[i] is TLabel then
    begin
      ALabel := TLabel(AForm.Components[i]);
      if (ALabel.FocusControl <> nil)
        and CanFormat(ALabel.FocusControl) then
        begin
          if not ALabel.AutoSize then ALabel.Alignment := taLeftJustify;
          ALabel.Left := ALabel.FocusControl.Left;
          ALabel.Top := ALabel.FocusControl.Top - ALabel.Height - 3;
        end
    end;
end;

Como podemos apreciar, OrganizarEtiquetas se limita a cambiar la posición de los componentes TLabel de un formulario que están asociados a cuadros de edición y combos, de modo que aparezcan por encima del control al que están enlazadas. Por supuesto, se trata de un algoritmo muy sencillo, pero constituye un buen punto de partida para que usted pueda montarse asistentes más sofisticados.
Responder Con Cita
Respuesta



Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro


La franja horaria es GMT +2. Ahora son las 20:11:41.


Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi
Copyright 1996-2007 Club Delphi