Emoticons - Esteban Calabria

Anuncio
Al mejor estilo messenger.
Este truco es muy interesante debido a los pocos ejemplos que hay para
Delphi disponibles en internet. Sé también que a más de uno le va a
resultar muy interesante. Vamos insertar imagenes en un componente
TRrichEdit al mejor estilo Messenger o ICQ. Para ello vamos a utilizar la
Api de OLE.
El ejemplo propuesto esta hecho en Delphi 7 con Windows XP.
Lamentablemente no he tenido tiempo para hacerlo funcionar en 98 asi
que le dejo esa tarea al lector
Primero vamos a diseñar un formulario similar al siquiente con un
TRichEdit, un TPanel y 7 TImage donde vamos a mostrar los emoticons
que el usuario puede elegir. A fin de cuentas nos tiene que quedar algo de
la siquiente manera:
Para mayor claridad se muestra el dfm del formulario anterior.
object Form1: TForm1
Left = 475
Top = 230
Width = 245
Height = 183
Caption = 'RichEdit con Imagenes'
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'MS Sans Serif'
Font.Style = []
OldCreateOrder = False
PixelsPerInch = 96
TextHeight = 13
object Image1: TImage
Escrito por Hechicero (Esteban Cesar Calabria)
Fecha Ultima Modificacion: 24 de Julio del 2004
1
Left = 8
Top = 118
Width = 24
Height = 24
Picture.Data = {
0A544A504547496D616765D7030000FFD8FFE000104A46494600010101006000
[Datos Omitidos]
E0A2B746CEC0A6A251BDB929676CFBEE1EA5CB80B8F2DDD2B65F39C03FD2022C
1468738CFFD9}
OnClick = Button4Click
end
object Image2: TImage
Left = 40
Top = 117
Width = 24
Height = 24
Picture.Data = {
0A544A504547496D616765D4030000FFD8FFE000104A46494600010101006000
[Datos Omitidos]
50E5F1E6A44F6634625631EFF26ACA282701CEDDC4C9C0F5C292B4374B3DD6D7
CFFFD9}
OnClick = Button4Click
end
object Image3: TImage
Left = 72
Top = 117
Width = 24
Height = 24
Picture.Data = {
0A544A504547496D616765C7030000FFD8FFE000104A46494600010101006000
[Datos Omitidos]
CB4CD0720D093D257BE59145A08D0007010F033FFFD9}
OnClick = Button4Click
end
object Image4: TImage
Left = 104
Top = 117
Width = 24
Height = 24
Picture.Data = {
0A544A504547496D616765E0030000FFD8FFE000104A46494600010101006000
[Datos Omitidos]
8CB2706D0EF21028DE80720ACFFFD9}
OnClick = Button4Click
end
object Image5: TImage
Left = 136
Top = 117
Width = 24
Height = 24
Picture.Data = {
0A544A504547496D616765DB030000FFD8FFE000104A46494600010101006000
[Datos Omitidos]
45140A68C2F187BFFFD9}
OnClick = Button4Click
end
object Image6: TImage
Left = 168
Top = 117
Width = 24
Height = 24
Picture.Data = {
0A544A504547496D616765F9030000FFD8FFE000104A46494600010101006000
[Datos Omitidos]
03B8AFD6D73FFFD9}
OnClick = Button4Click
end
object Image7: TImage
Left = 200
Top = 117
Width = 24
Height = 24
Picture.Data = {
0A544A504547496D6167650F040000FFD8FFE000104A46494600010101006000
Escrito por Hechicero (Esteban Cesar Calabria)
Fecha Ultima Modificacion: 24 de Julio del 2004
2
[Datos Omitidos]
3FD075DA4CA3CAFC4A476E2C7661EA9873C36C449A09DD965DF555CFFFD9}
OnClick = Button4Click
end
object Panel1: TPanel
Left = 0
Top = 89
Width = 237
Height = 24
Align = alTop
Caption = 'Elija la imagen para insertar un Emoticon....'
TabOrder = 0
end
object RichEdit1: TRichEdit
Left = 0
Top = 0
Width = 237
Height = 89
Align = alTop
TabOrder = 1
end
end
La VCL no nos a las herramientas suficientes para poder insertar imagenes en
un RichEdit de forma directa. Debemos usar la Api de Ole. Los RichEdit
implementan la interfaz IRichEditOle que tiene el guid ['{00020D00-0000-0000C000-000000000046}']. Lamentablemente no existe en ninguna unit que viene
con delphi esa interfaz declarada. Pero a no desesperar, este truco muestra a
continuación como debiera ser la declaración de esa interfaz. Tan sólo hay que
crear una unit con el nombre RichOle y copiar el código :
//---------------------------------------------------------------//
// Converted from richole.h
// OLE Extensions to the Rich Text Editor
// Copyright (c) 1985-1996, Microsoft Corporation
//
//---------------------------------------------------------------unit
RichOle;
interface
uses
Windows, ActiveX, Richedit;
//--- Types -----------------------------------------------------// Structure passed to GetObject and InsertObject
type
TREObject = packed record
cbStruct : DWORD;
// Size of structure
cp : longint;
// Character position of object
clsid : TCLSID;
// Class ID of object
oleobj : IOleObject;
// OLE object interface
stg : IStorage;
// Associated storage interface
olesite : IOLEClientSite; // Associated client site interface
sizel : TSize;
// Size of object (may be 0,0)
dvaspect : DWORD;
// Display aspect to use
dwFlags : DWORD;
// Object status flags
dwUser : DWORD
// Dword for user's use
end;
//--- Constants -------------------------------------------------const
// Flags to specify which interfaces should be returned in the structure above
REO_GETOBJ_NO_INTERFACES = $00000000;
Escrito por Hechicero (Esteban Cesar Calabria)
Fecha Ultima Modificacion: 24 de Julio del 2004
3
REO_GETOBJ_POLEOBJ
REO_GETOBJ_PSTG
REO_GETOBJ_POLESITE
REO_GETOBJ_ALL_INTERFACES
=
=
=
=
$00000001;
$00000002;
$00000004;
$00000007;
// Place object at selection
REO_CP_SELECTION
= $FFFFFFFF;
// -1
// Use character position to specify object instead of index
REO_IOB_SELECTION
= $FFFFFFFF; // -1
REO_IOB_USE_CP
= $FFFFFFFE; // -2
// Object flags
REO_NULL
REO_READWRITEMASK
REO_DONTNEEDPALETTE
REO_BLANK
REO_DYNAMICSIZE
REO_INVERTEDSELECT
REO_BELOWBASELINE
REO_RESIZABLE
REO_LINK
REO_STATIC
REO_SELECTED
REO_OPEN
REO_INPLACEACTIVE
REO_HILITED
REO_LINKAVAILABLE
REO_GETMETAFILE
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
=
$00000000;
$0000003F;
$00000020;
$00000010;
$00000008;
$00000004;
$00000002;
$00000001;
$80000000;
$40000000;
$08000000;
$04000000;
$02000000;
$01000000;
$00800000;
$00400000;
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
//
// flags for IRichEditOle::GetClipboardData
()
// and IRichEditOleCallback.QueryAcceptData
RECO_PASTE
= $00000000; //
RECO_DROP
= $00000001; //
RECO_COPY
= $00000002; //
RECO_CUT
= $00000003; //
RECO_DRAG
= $00000004; //
No flags
Mask out RO bits
Object doesn't need palette
Object is blank
Object defines size always
Object drawn all inverted if sel
Object sits below the baseline
Object may be resized
Object is a link (RO)
Object is static (RO)
Object selected (RO)
Object open in its server (RO)
Object in place active (RO)
Object is to be hilited (RO)
Link believed available (RO)
Object requires metafile (RO)
(), IRichEditOleCallback.GetClipboardData
()
paste from clipboard
drop
copy to the clipboard
cut to the clipboard
drag
//--- IRichEditOle ----------------------------------------------// Interface used by the client of RichEdit to perform OLE-related operations.
type
IRichEditOle = interface
['{00020D00-0000-0000-C000-000000000046}']
function GetClientSite (out lplpolesite : IOLECLIENTSITE) : HResult; stdcall;
function GetObjectCount : longint; stdcall;
function GetLinkCount : longint; stdcall;
function GetObject (iob : longint; out reobject : TREObject; dwFlags : DWORD) :
HRESULT; stdcall;
function InsertObject (const reobject : TREObject) : HResult; stdcall;
function ConvertObject (iob : longint; const clsidNew : TCLSID; lpStrUserTypeNew :
LPCSTR) : HRESULT; stdcall;
function ActivateAs (const clsid, clsidAs : TCLSID) : HRESULT; stdcall;
function SetHostNames (lpstrContainerApp, lpstrContainerObj : LPCSTR) : HRESULT;
stdcall;
function SetLinkAvailable (iob : longint; fAvailable : BOOL) : HRESULT; stdcall;
function SetDvaspect (iob : longint; dvaspect : DWORD) : HRESULT; stdcall;
function HandsOffStorage (iob : longint) : HRESULT; stdcall;
function SaveCompleted (iob : longint; stg : IStorage) : HRESULT; stdcall;
function InPlaceDeactivate : HRESULT; stdcall;
function ContextSensitiveHelp (fEnterMode : BOOL) : HRESULT; stdcall;
function GetClipboardData (const chrg : TCharRange; reco : DWORD; out dataobj :
IDataObject) : HRESULT; stdcall;
function ImportDataObject (dataobj : IDataObject; cf : TClipFormat; hMetaPict :
HGLOBAL) : HRESULT; stdcall;
end;
//--- IRichEditOleCallback ----------------------------------------// Interface used by the RichEdit to get OLE-related stuff from the application
// using RichEdit.
IRichEditOleCallback = interface
['{00020D03-0000-0000-C000-000000000046}']
function GetNewStorage (out stg : IStorage) : HRESULT; stdcall;
function GetInPlaceContext (out Frame : IOleInPlaceFrame; out Doc :
Escrito por Hechicero (Esteban Cesar Calabria)
Fecha Ultima Modificacion: 24 de Julio del 2004
4
IOleInPlaceUIWindow; var FrameInfo : TOleInPlaceFrameInfo) : HRESULT; stdcall;
function ShowContainerUI (fShow : BOOL) : HRESULT; stdcall;
function QueryInsertObject (const clsid : TCLSID; stg : IStorage; cp : longint) :
HRESULT; stdcall;
function DeleteObject (oleobj : IOLEObject) : HRESULT; stdcall;
function QueryAcceptData (dataobj : IDataObject; var cfFormat : TClipFormat; reco :
DWORD; fReally : BOOL; hMetaPict : HGLOBAL) : HRESULT; stdcall;
function ContextSensitiveHelp (fEnterMode : BOOL) : HRESULT; stdcall;
function GetClipboardData (const chrg : TCharRange; reco : DWORD; out dataobj :
IDataObject) : HRESULT; stdcall;
function GetDragDropEffect (fDrag : BOOL; grfKeyState : DWORD; var dwEffect :
DWORD) : HRESULT; stdcall;
function GetContextMenu (seltype : Word; oleobj : IOleObject; const chrg :
TCharRange; var menu : HMENU) : HRESULT; stdcall;
end;
implementation
end.
Para obtener la interfaz anterior de nuestro componente TRichEdit de
delphi debemos utilizar la funcion de la api de windows SendMessage
para enviarle el mensaje EM_GETOLEINTERFACE a nuestro editor de
texto enriquecido. No obstante, para complicarnos la existencia, en
ninguna unit de Delphi esta declarado ese mensaje. Por lo tanto vamos a
tener que escribirlo a mano en la sección const de nuestra unit. En un acto
de originalidad la llamaremos “Unit1”.
El encabezado propuesto es:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, StdCtrls, ComCtrls, Mask, Activex, ComObj, Shlobj, RichOle,
jpeg;
const
EM_GETOLEINTERFACE = WM_USER + 60;
CLSID_NULL: TGUID = (
D1:$00000000;D2:$0000;D3:$0000;D4:($00,$00,$00,$00,$00,$00,$00,$00));
Acto seguido vamos a programar una clase que implemente la interfaz
IDataObject (esta ultima si ya viene declarada en la unit Activex) para
poder crear un objeto Ole que tenga una referencia al bitmap que
queremos insertar en el RichEdit. La declaración de esa clase tiene la
siguiente forma:
type
TImageDataObject = class(TInterfacedObject,IDataObject)
private
FRecMediul:STGMEDIUM;
FRecFormat: TFormatEtc;
procedure setBitmap(const Value: HBitmap);
published
property Bitmap:HBITMAP write setBitmap;
property RecFormat:TFormatEtc read FRecFormat;
protected
Escrito por Hechicero (Esteban Cesar Calabria)
Fecha Ultima Modificacion: 24 de Julio del 2004
5
function GetData(const formatetcIn: TFormatEtc; out medium: TStgMedium):
HResult; stdcall;
function GetDataHere(const formatetc: TFormatEtc; out medium: TStgMedium):
HResult; stdcall;
function QueryGetData(const formatetc: TFormatEtc): HResult;
stdcall;
function GetCanonicalFormatEtc(const formatetc: TFormatEtc;
out formatetcOut: TFormatEtc): HResult; stdcall;
function SetData(const formatetc: TFormatEtc; var medium: TStgMedium;
fRelease: BOOL): HResult; stdcall;
function EnumFormatEtc(dwDirection: Longint; out enumFormatEtc:
IEnumFormatEtc): HResult; stdcall;
function DAdvise(const formatetc: TFormatEtc; advf: Longint;
const advSink: IAdviseSink; out dwConnection: Longint): HResult; stdcall;
function DUnadvise(dwConnection: Longint): HResult; stdcall;
function EnumDAdvise(out enumAdvise: IEnumStatData): HResult;
stdcall;
end;
Lo unico que nos queda es la clase de nuestro formulario y la
implementación. Dada la característica pragmatica de este articulo no nos
vamos a detener en la explicación de esta última. Para mayor detalle de lo
que hacen las funciones de la API de Ole recomiendo hecharle un vistazo
a la MSDN Library de Microsoft.
A Continuación, la cereza del postre:
TForm1 = class(TForm)
Panel1: TPanel;
Image1: TImage;
RichEdit1: TRichEdit;
Image2: TImage;
Image3: TImage;
Image4: TImage;
Image5: TImage;
Image6: TImage;
Image7: TImage;
procedure Button4Click(Sender: TObject);
private
FPoints:TList;
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button4Click(Sender: TObject);
var
RE: IRichEditOle;
RCS: IOleClientSite;
JI: IOleObject;
ILB: ILockBytes;
SI: IStorage;
REO: TReObject;
ds:IDataObject;
b : TBitmap;
MyData : TImageDataObject;
Escrito por Hechicero (Esteban Cesar Calabria)
Fecha Ultima Modificacion: 24 de Julio del 2004
6
p : PFormatEtc;
begin
if (Sender is TImage) then
Begin
RE:=nil;
//mio
MyData := TImageDataObject.Create;
b := TBitmap.Create;
b.Assign((Sender as TImage).Picture.Graphic);
MyData.Bitmap := b.Handle;
//mio
SendMessage(RichEdit1.Handle,EM_GETOLEINTERFACE,0,integer(@RE));
OleCheck(RE.GetClientSite(RCS));
OleCheck(CreateILockBytesOnHGlobal(0,true,ILB));
OleCheck(StgCreateDocFileOnILockBytes(ILB,STGM_SHARE_EXCLUSIVE+STGM_CREATE
+STGM_READWRITE,0,SI));
//Si quisieramos leerla directamente de disco
// OleCreateFromFile(CLSID_NULL,'F:\Archivos de
programa\Borland\Delphi7\Projects\Proyectos mios\Imagenes en un
RichEdit\dibujo.bmp',IOleObject,OLERENDER_DRAW,nil,RCS,SI,JI);
OleCheck(OleCreateStaticFromData(MyData,IOleObject,OLERENDER_FORMAT,
@MyData.RecFormat,RCS,SI,JI));
OleSetContainedObject(JI,true);
FillChar(REO,sizeof(REO),0);
REO.cbStruct:=sizeof(REO);
REO.cp:=REO_CP_SELECTION;
REO.dvAspect:=DVASPECT_CONTENT;
REO.oleobj:=JI;
REO.olesite:=RCS;
REO.stg:=SI;
OleCheck(RE.InsertObject(REO));
end;
end;
{ TImageDataObject }
function TImageDataObject.DAdvise(const formatetc: TFormatEtc;
advf: Integer; const advSink: IAdviseSink;
out dwConnection: Integer): HResult;
begin
result := E_NOTIMPL;
end;
function TImageDataObject.DUnadvise(dwConnection: Integer): HResult;
begin
result := E_NOTIMPL;
end;
function TImageDataObject.EnumDAdvise(
out enumAdvise: IEnumStatData): HResult;
begin
result := E_NOTIMPL;
end;
function TImageDataObject.EnumFormatEtc(dwDirection: Integer;
out enumFormatEtc: IEnumFormatEtc): HResult;
begin
result :=
E_NOTIMPL;
end;
function TImageDataObject.GetCanonicalFormatEtc(
const formatetc: TFormatEtc; out formatetcOut: TFormatEtc): HResult;
begin
result := E_NOTIMPL;
end;
function TImageDataObject.GetData(const formatetcIn: TFormatEtc;
out medium: TStgMedium): HResult;
var
hDest : HBITMAP;
begin
Escrito por Hechicero (Esteban Cesar Calabria)
Fecha Ultima Modificacion: 24 de Julio del 2004
7
medium.tymed := TYMED_GDI;
medium.hBitmap := Self.FRecMediul.hBitmap;
medium.unkForRelease := nil;
result:= S_OK;
end;
function TImageDataObject.GetDataHere(const formatetc: TFormatEtc;
out medium: TStgMedium): HResult;
begin
result := E_NOTIMPL;
end;
function TImageDataObject.QueryGetData(const formatetc: TFormatEtc): HResult;
begin
result := E_NOTIMPL;
end;
procedure TImageDataObject.setBitmap(const Value: HBITMAP);
var
Medium : STGMEDIUM;
Format :TFormatEtc;
begin
Medium.tymed := TYMED_GDI;
Medium.hBitmap := Value;
Medium.unkForRelease := nil;
Format.cfFormat := CF_BITMAP;
Format.ptd := nil;
Format.dwAspect := DVASPECT_CONTENT;
Format.lindex := -1;
format.tymed := Tymed_Gdi;
Self.SetData(Format,Medium,true);
end;
function TImageDataObject.SetData(const formatetc: TFormatEtc;
var medium: TStgMedium; fRelease: BOOL): HResult;
begin
Self.FRecMediul := medium;
Self.FRecFormat := formatetc;
result := S_OK;
end;
end.
Así podemos insertar emoticons dentro de un Richedit. Ahora no tenemos
que envidiarle al MSN y al ICQ o a cualquier mensajero.
Escrito por Hechicero (Esteban Cesar Calabria)
Fecha Ultima Modificacion: 24 de Julio del 2004
8
Descargar