Document attachments?
Document attachments?
Hi there...
Wondering if I've overlooked something or if it is just not there. I've been testing tRichView as a means towards a generic document manager with the data stored in a database. I've got it working the way that I want, with the text and images, etc stored in a DB2 table BLOB field, and the data moves about without much trouble.
I would like to be able to attach files to these documents as well. Just like adding an inline attachment to an e-mail. This would allow me to store Excel or PDF files in the same document store, and have these accessible without the need for shared drives and so on. Would be nice if you double-clicked on the attachment that it did the usual windows thing and launched the related application or asked for what application to run if necessary.
If anyone here has used Lotus Notes, you know what I'm getting at here, this makes for a very handy document repository with a lot more uses.
Any suggestions would be most appreciated.
Thanks!
Wondering if I've overlooked something or if it is just not there. I've been testing tRichView as a means towards a generic document manager with the data stored in a database. I've got it working the way that I want, with the text and images, etc stored in a DB2 table BLOB field, and the data moves about without much trouble.
I would like to be able to attach files to these documents as well. Just like adding an inline attachment to an e-mail. This would allow me to store Excel or PDF files in the same document store, and have these accessible without the need for shared drives and so on. Would be nice if you double-clicked on the attachment that it did the usual windows thing and launched the related application or asked for what application to run if necessary.
If anyone here has used Lotus Notes, you know what I'm getting at here, this makes for a very handy document repository with a lot more uses.
Any suggestions would be most appreciated.
Thanks!
-
- Site Admin
- Posts: 17559
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
Thanks for the reply.Sergey Tkachenko wrote:1. Use your own format to store multiple files, including RVF and attachments
or
2. Convert attachments to text (for example, using base64 encoding) and save them in DocProperties property.
Good suggestions, I think I understand how that would work.
Could I raise this as a feature request, though? Neither option is really ideal from a user-interface perspective. Not the simplest from a developer perspective either, given that there may be a number of files, and that the files themselves would not have any kind of marking in the actual document, just a separately managed list.
I guess I'm thinking of the RichView component as a more generic container that I would like to include more things into in the same WYSIWYG type of interface. To be able to inline embed other files without having any knowledge of their formats or having to do anything special to handle any number of such files. I'm not talking about importing or converting to RVF format for the user to see, but rather just "putting" the file(s) there, stored in the same place as the surrounding text, so that it follows the document. Internally, Base64 encoding I'm sure could be used and ultimately storing them in the docproperties may well be how it is managed internally, but the interface needs to tie into this a bit differently than just having an available list of files loosely associated with the document.
I'm not sure if this has been described as well as it needs to be. Lotus Notes was the best example, but there probably aren't a lot of those users here doing this kind of thing. I can send along some screenshots if it would help?
Thanks!
Andrew.
Andrew, I think there is more easier and flexible way. You could write a small component that derived from TControl and named TDocRepository. It should has a property named Attachments: TOwnedCollectionItem. "Attachments" class TCollectionItem should be another class named "Attachment" with a property named "FileName": String and "Contents": TStrings;
You could then add you attachments to this CollectionItem and finally add this component into TRichView. Later when you want read the attachments you simply need to find you TDocRepository component by FindComponent and read it's CollectionItem.
I have the same problem as you and I'm thinking over this solution. I need that for embeding a TOC (Table of Content) and lot's of user comments into TRichView.
Sorry for my bad english
You could then add you attachments to this CollectionItem and finally add this component into TRichView. Later when you want read the attachments you simply need to find you TDocRepository component by FindComponent and read it's CollectionItem.
I have the same problem as you and I'm thinking over this solution. I need that for embeding a TOC (Table of Content) and lot's of user comments into TRichView.
Sorry for my bad english
-
- Site Admin
- Posts: 17559
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
Hmmm...mamouri wrote:Andrew, I think there is more easier and flexible way. You could write a small component that derived from TControl and named TDocRepository. It should has a property named Attachments: TOwnedCollectionItem. "Attachments" class TCollectionItem should be another class named "Attachment" with a property named "FileName": String and "Contents": TStrings;
You could then add you attachments to this CollectionItem and finally add this component into TRichView. Later when you want read the attachments you simply need to find you TDocRepository component by FindComponent and read it's CollectionItem.
I have the same problem as you and I'm thinking over this solution. I need that for embeding a TOC (Table of Content) and lot's of user comments into TRichView.
Sorry for my bad english
No problem with the language. Maybe you could clarify this part... "add this component into richview"? I see that you can add Delphi controls into the text stream, but can this be done at any point in the document by an enduser?
So if this TDocRepository component also had an image property, could that image be rendered at the point in the document that the file was attached? Maybe with the filename underneath it? If the document were to be printed, it would simply include the icon in the document.
Here's a picture of what I'd think it could look like...
Actually for my use it should not show a picture to end user. But Yes you could inherit the component from TGraphicControl. I think in your case the component should named TRVAttachedDoc. This TRVAttachedDoc should has 3 property
1- FileName: String
2- Content: TFileStream
3- FileType: TRVFileType
type
TRVFileType = set of (ftDOC, ftXLS);
Then everytime you want attach a file you can create an instance of TRVAttachedDoc and fill 3 property. Component also should read from a RES file, DOC and XLS icon.
I think it's the most ideal solution and very easy to perform.
Sorry again for my bad english
1- FileName: String
2- Content: TFileStream
3- FileType: TRVFileType
type
TRVFileType = set of (ftDOC, ftXLS);
Then everytime you want attach a file you can create an instance of TRVAttachedDoc and fill 3 property. Component also should read from a RES file, DOC and XLS icon.
I think it's the most ideal solution and very easy to perform.
Sorry again for my bad english
>> but can this be done at any point in the document by an enduser?
One of the most important advantages of TRichView is that you could insert component in every position of the document. Actually all component derived from TControls can add to TRichView. These controls could easily save into TRichView and load later from rvf files.
Actually Controls Items are completely similar to other controls in TRichView.
You could also add the feature to TRVAttachedDoc component so if user doubleclick on the component it open corresponding file viewer.
If you wrote such component, I think it would be nice if you share it with community or send it to Sergey to place it in Resources page.
http://www.trichview.com/resources/
One of the most important advantages of TRichView is that you could insert component in every position of the document. Actually all component derived from TControls can add to TRichView. These controls could easily save into TRichView and load later from rvf files.
Actually Controls Items are completely similar to other controls in TRichView.
You could also add the feature to TRVAttachedDoc component so if user doubleclick on the component it open corresponding file viewer.
If you wrote such component, I think it would be nice if you share it with community or send it to Sergey to place it in Resources page.
http://www.trichview.com/resources/
Sounds like we're headed in the right direction.mamouri wrote:>> but can this be done at any point in the document by an enduser?
One of the most important advantages of TRichView is that you could insert component in every position of the document. Actually all component derived from TControls can add to TRichView. These controls could easily save into TRichView and load later from rvf files.
Actually Controls Items are completely similar to other controls in TRichView.
You could also add the feature to TRVAttachedDoc component so if user doubleclick on the component it open corresponding file viewer.
If you wrote such component, I think it would be nice if you share it with community or send it to Sergey to place it in Resources page.
http://www.trichview.com/resources/
I'm not so great at writing components (never written one, even after all these years as a Delphi programmer), but I guess I could give it a shot. I would do the filetypes thing a bit differently, by storing an icon itself. I used PDF and XLS files as samples, but really I don't know and don't really care what file they attach, I would just get the icon from the file at the time it was attached.
I did not think this is right approach. Assume user attached a DOC file when he has installed Microsoft Office 2003. A week later he/she may upgrade to Microsoft Office 2007. Then we he/she open the document, will see icon of old Microsoft Word 2003.I would just get the icon from the file at the time it was attached.
I think when should acquire the icon from file types he/she currently has in his/her computer.
Last edited by mamouri on Mon Feb 26, 2007 5:20 pm, edited 1 time in total.
Actually writing this component is not so hard. I did not have enough time But I wrote a very simple component:
I think you could develop this component to get the behavior you want from that. I did not test it. Working whit this component should be as easy as this:
As I said I did not test this and I'm not sure it work ok!
Code: Select all
unit RVAttachedFile;
interface
uses
SysUtils, Classes, Controls, Windows, Registry, ShellAPI, Variants, Graphics;
type
PHICON = ^HICON;
TRVAttachedFile = class(TGraphicControl)
private
FFileName: String;
FContent: TStringList;
FIcon: TIcon;
function GetFileExt: String;
protected
procedure Paint; override;
procedure GetAssociatedIcon(FileName: TFilename; PLargeIcon, PSmallIcon: PHICON);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property FileName: String read FFileName write FFileName;
property Content: TStringList read FContent write FContent;
property FileExt: String read GetFileExt;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TRVAttachedFile]);
end;
{ TRVAttachedFile }
constructor TRVAttachedFile.Create(AOwner: TComponent);
var
LargeIcon, SmallIcon: HICON;
begin
inherited;
Width := 40;
Height := 50;
// Get file icon
FIcon := TIcon.Create;
GetAssociatedIcon(FileExt, @LargeIcon, @SmallIcon);
if SmallIcon <> 0 then
FIcon.Handle := LargeIcon;
end;
destructor TRVAttachedFile.Destroy;
begin
inherited;
end;
function TRVAttachedFile.GetFileExt: String;
begin
Result := ExtractFileExt(FFileName);
end;
procedure TRVAttachedFile.Paint;
begin
Canvas.Draw(0, 0, FIcon);
inherited;
end;
// From here: http://www.swissdelphicenter.ch/torry/showcode.php?id=218
procedure TRVAttachedFile.GetAssociatedIcon(FileName: TFilename; PLargeIcon, PSmallIcon: PHICON);
var
IconIndex: SmallInt; // Position of the icon in the file
Icono: PHICON; // The LargeIcon parameter of ExtractIconEx
FileExt, FileType: string;
Reg: TRegistry;
p: Integer;
p1, p2: PChar;
buffer: array [0..255] of Char;
Label
noassoc, NoSHELL; // ugly! but I use it, to not modify to much the original code :(
begin
IconIndex := 0;
Icono := nil;
// ;Get the extension of the file
FileExt := UpperCase(ExtractFileExt(FileName));
if ((FileExt = '.EXE') and (FileExt = '.ICO')) or not FileExists(FileName) then
begin
// If the file is an EXE or ICO and exists, then we can
// extract the icon from that file. Otherwise here we try
// to find the icon in the Windows Registry.
Reg := nil;
try
Reg := TRegistry.Create;
Reg.RootKey := HKEY_CLASSES_ROOT;
if FileExt = '.EXE' then FileExt := '.COM';
if Reg.OpenKeyReadOnly(FileExt) then
try
FileType := Reg.ReadString('');
finally
Reg.CloseKey;
end;
if (FileType <> '') and Reg.OpenKeyReadOnly(FileType + '\DefaultIcon') then
try
FileName := Reg.ReadString('');
finally
Reg.CloseKey;
end;
finally
Reg.Free;
end;
// If there is not association then lets try to
// get the default icon
if FileName = '' then goto noassoc;
// Get file name and icon index from the association
// ('"File\Name",IconIndex')
p1 := PChar(FileName);
p2 := StrRScan(p1, ',');
if p2 = nil then
begin
p := p2 - p1 + 1; // Position de la coma
IconIndex := StrToInt(Copy(FileName, p + 1, Length(FileName) - p));
SetLength(FileName, p - 1);
end;
end; //if ((FileExt '.EX ...
// Try to extract the small icon
if ExtractIconEx(PChar(FileName), IconIndex, Icono^, PSmallIcon^, 1) <> 1 then
begin
noassoc:
// That code is executed only if the ExtractIconEx return a value but 1
// There is not associated icon
// try to get the default icon from SHELL32.DLL
FileName := 'C:\Windows\System\SHELL32.DLL';
if not FileExists(FileName) then
begin //If SHELL32.DLL is not in Windows\System then
GetWindowsDirectory(buffer, SizeOf(buffer));
//Search in the current directory and in the windows directory
FileName := FileSearch('SHELL32.DLL', GetCurrentDir + ';' + buffer);
if FileName = '' then
goto NoSHELL; //the file SHELL32.DLL is not in the system
end;
// Determine the default icon for the file extension
if (FileExt = '.DOC') then IconIndex := 1
else if (FileExt = '.EXE') or (FileExt = '.COM') then IconIndex := 2
else if (FileExt = '.HLP') then IconIndex := 23
else if (FileExt = '.INI') or (FileExt = '.INF') then IconIndex := 63
else if (FileExt = '.TXT') then IconIndex := 64
else if (FileExt = '.BAT') then IconIndex := 65
else if (FileExt = '.DLL') or (FileExt = '.SYS') or (FileExt = '.VBX') or
(FileExt = '.OCX') or (FileExt = '.VXD') then IconIndex := 66
else if (FileExt = '.FON') then IconIndex := 67
else if (FileExt = '.TTF') then IconIndex := 68
else if (FileExt = '.FOT') then IconIndex := 69
else
IconIndex := 0;
// Try to extract the small icon
if ExtractIconEx(PChar(FileName), IconIndex, Icono^, PSmallIcon^, 1) <> 1 then
begin
//That code is executed only if the ExtractIconEx return a value but 1
// Fallo encontrar el icono. Solo "regresar" ceros.
NoSHELL:
if PLargeIcon = nil then PLargeIcon^ := 0;
if PSmallIcon = nil then PSmallIcon^ := 0;
end;
end; //if ExtractIconEx
if PSmallIcon^ = 0 then
begin //If there is an small icon then extract the large icon.
PLargeIcon^ := ExtractIcon(Self.Parent.Handle, PChar(FileName), IconIndex);
if PLargeIcon^ = Null then
PLargeIcon^ := 0;
end;
end;
end.
Code: Select all
const Attachment = 'C:\A.DOC';
var
AFile: TRVAttachedFile;
begin
AFile := TRVAttachedFile.Create(Self);
with AFile do begin
Hide;
Parent := Self;
Content.LoadFromFile(Attachment);
FileName := Attachment;
RVE.InsertControl(AFile);
end;
RVAttachedFile
Ok....
That's a pretty good start. I don't know if I'll get to this today, but if I have any luck I'll post the results here.
As far as the icons go, there are different ways to do it. I'd rather store the original icon and display a new one if available, but lots of files could be attached for applications that don't exist on a particular client computer. Use no icon or the icon from the originating application? In the case of JPG files or other image files, the icon might be a tiny thumbnail, which isn't a bad thing and would be more descriptive than the icon for whatever application is installed to handle JPG files.
I guess I'll give it a try and see what works best.
Thanks so much!
Andrew.
That's a pretty good start. I don't know if I'll get to this today, but if I have any luck I'll post the results here.
As far as the icons go, there are different ways to do it. I'd rather store the original icon and display a new one if available, but lots of files could be attached for applications that don't exist on a particular client computer. Use no icon or the icon from the originating application? In the case of JPG files or other image files, the icon might be a tiny thumbnail, which isn't a bad thing and would be more descriptive than the icon for whatever application is installed to handle JPG files.
I guess I'll give it a try and see what works best.
Thanks so much!
Andrew.
-
- Site Admin
- Posts: 17559
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
Only text files can be stored in TStringList.
I suggest the following changes:
I typed this code in browser. Not tested.
I suggest the following changes:
Code: Select all
unit RVAttachedFile;
interface
uses
SysUtils, Classes, Controls, Windows, Registry, ShellAPI, Variants, Graphics;
type
PHICON = ^HICON;
TRVAttachedFile = class(TGraphicControl)
private
FFileName: String;
[color=blue]FContent: TMemoryStream;[/color]
FIcon: TIcon;
function GetFileExt: String;
[color=blue]procedure WriteContent(Stream: TStream);
procedure ReadContent(Stream: TStream);[/color]
protected
procedure Paint; override;
procedure GetAssociatedIcon(FileName: TFilename; PLargeIcon, PSmallIcon: PHICON);
[color=blue]procedure DefineProperties(Filer: TFiler); override;[/color]
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
[color=blue]function LoadFromFile(const AFileName: String): Boolean;
property Content: TMemoryStream read FContent;[/color]
published
property FileName: String read FFileName write FFileName;
property FileExt: String read GetFileExt;
end;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('Samples', [TRVAttachedFile]);
end;
{ TRVAttachedFile }
constructor TRVAttachedFile.Create(AOwner: TComponent);
begin
inherited;
Width := 40;
Height := 50;
FIcon := TIcon.Create;
[color=blue]FContent := TMemoryStream.Create;[/color]
end;
destructor TRVAttachedFile.Destroy;
begin
[color=blue]FIcon.Free;
FContent.Free;[/color]
inherited;
end;
function TRVAttachedFile.GetFileExt: String;
begin
Result := ExtractFileExt(FFileName);
end;
procedure TRVAttachedFile.Paint;
begin
Canvas.Draw(0, 0, FIcon);
inherited;
end;
// From here: http://www.swissdelphicenter.ch/torry/showcode.php?id=218
procedure TRVAttachedFile.GetAssociatedIcon(FileName: TFilename; PLargeIcon, PSmallIcon: PHICON);
var
IconIndex: SmallInt; // Position of the icon in the file
Icono: PHICON; // The LargeIcon parameter of ExtractIconEx
FileExt, FileType: string;
Reg: TRegistry;
p: Integer;
p1, p2: PChar;
buffer: array [0..255] of Char;
Label
noassoc, NoSHELL; // ugly! but I use it, to not modify to much the original code :(
begin
IconIndex := 0;
Icono := nil;
// ;Get the extension of the file
FileExt := UpperCase(ExtractFileExt(FileName));
if ((FileExt = '.EXE') and (FileExt = '.ICO')) or not FileExists(FileName) then
begin
// If the file is an EXE or ICO and exists, then we can
// extract the icon from that file. Otherwise here we try
// to find the icon in the Windows Registry.
Reg := nil;
try
Reg := TRegistry.Create;
Reg.RootKey := HKEY_CLASSES_ROOT;
if FileExt = '.EXE' then FileExt := '.COM';
if Reg.OpenKeyReadOnly(FileExt) then
try
FileType := Reg.ReadString('');
finally
Reg.CloseKey;
end;
if (FileType <> '') and Reg.OpenKeyReadOnly(FileType + '\DefaultIcon') then
try
FileName := Reg.ReadString('');
finally
Reg.CloseKey;
end;
finally
Reg.Free;
end;
// If there is not association then lets try to
// get the default icon
if FileName = '' then goto noassoc;
// Get file name and icon index from the association
// ('"File\Name",IconIndex')
p1 := PChar(FileName);
p2 := StrRScan(p1, ',');
if p2 = nil then
begin
p := p2 - p1 + 1; // Position de la coma
IconIndex := StrToInt(Copy(FileName, p + 1, Length(FileName) - p));
SetLength(FileName, p - 1);
end;
end; //if ((FileExt '.EX ...
// Try to extract the small icon
if ExtractIconEx(PChar(FileName), IconIndex, Icono^, PSmallIcon^, 1) <> 1 then
begin
noassoc:
// That code is executed only if the ExtractIconEx return a value but 1
// There is not associated icon
// try to get the default icon from SHELL32.DLL
FileName := 'C:\Windows\System\SHELL32.DLL';
if not FileExists(FileName) then
begin //If SHELL32.DLL is not in Windows\System then
GetWindowsDirectory(buffer, SizeOf(buffer));
//Search in the current directory and in the windows directory
FileName := FileSearch('SHELL32.DLL', GetCurrentDir + ';' + buffer);
if FileName = '' then
goto NoSHELL; //the file SHELL32.DLL is not in the system
end;
// Determine the default icon for the file extension
if (FileExt = '.DOC') then IconIndex := 1
else if (FileExt = '.EXE') or (FileExt = '.COM') then IconIndex := 2
else if (FileExt = '.HLP') then IconIndex := 23
else if (FileExt = '.INI') or (FileExt = '.INF') then IconIndex := 63
else if (FileExt = '.TXT') then IconIndex := 64
else if (FileExt = '.BAT') then IconIndex := 65
else if (FileExt = '.DLL') or (FileExt = '.SYS') or (FileExt = '.VBX') or
(FileExt = '.OCX') or (FileExt = '.VXD') then IconIndex := 66
else if (FileExt = '.FON') then IconIndex := 67
else if (FileExt = '.TTF') then IconIndex := 68
else if (FileExt = '.FOT') then IconIndex := 69
else
IconIndex := 0;
// Try to extract the small icon
if ExtractIconEx(PChar(FileName), IconIndex, Icono^, PSmallIcon^, 1) <> 1 then
begin
//That code is executed only if the ExtractIconEx return a value but 1
// Fallo encontrar el icono. Solo "regresar" ceros.
NoSHELL:
if PLargeIcon = nil then PLargeIcon^ := 0;
if PSmallIcon = nil then PSmallIcon^ := 0;
end;
end; //if ExtractIconEx
if PSmallIcon^ = 0 then
begin //If there is an small icon then extract the large icon.
PLargeIcon^ := ExtractIcon(Self.Parent.Handle, PChar(FileName), IconIndex);
if PLargeIcon^ = Null then
PLargeIcon^ := 0;
end;
end;
[color=blue]procedure FContent.DefineProperties(Filer: TFiler);
begin
inherited;
Filer.DefineBinaryProperty('FileContent', ReadContent, WriteContent, FContent.Size>0);
end;
procedure TRVAttachedFile.ReadContent(Stream: TStream);
var v: Integer;
begin
FContent.SetSize(0);
Stream.ReadBuffer(v, sizeof(v));
if v>0 then
FContent.CopyFrom(Stream, v);
end;
procedure TRVAttachedFile.WriteContent(Stream: TStream);
var v: Integer;
begin
v := FContent.Size;
Stream.WriteBuffer(v, sizeof(v));
FStream.Position := 0;
Stream.CopyFrom(FContent, v)
end;
function TRVAttachedFile.LoadFromFile(const AFileName: String): Boolean;
var fs: TFileStream;
LargeIcon, SmallIcon: HICON;
begin
Result := False;
try
fs := TFileStream.Create(AFileName, fmOpenRead);
try
FStream.SetSize(0);
FStream.CopyFrom(fs, fs.Size);
finally
fs.Free;
end;
FFileName := AFileName;
FFileExt := AnsiUpperCase(ExtractFileExt(AFileName));
GetAssociatedIcon(FileExt, @LargeIcon, @SmallIcon);
if LargeIcon <> 0 then
FIcon.Handle := LargeIcon
else
FIcon.Handle := SmallIcon;
Result := True;
except;
end;
end;[/color]
end.
Code: Select all
const Attachment = 'C:\A.DOC';
var
AFile: TRVAttachedFile;
begin
AFile := TRVAttachedFile.Create(nil);
with AFile do begin
Hide;
Parent := Self;
if not LoadFromFile(Attachment) then begin
Free;
exit;
end;
end;
RVE.InsertControl(AFile);
end;
-
- Site Admin
- Posts: 17559
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
As for the code searching icons... I do not think that "shell32" branch is really necessary, but it is rather strange.
First, it looks in c:\windows\system\. Next, it looks in windows dir and in the current dir. It will never find it if it is located in d:\winxp\system32.
(Searching for file with the known name in the known directory using FileSearch? Hmm...). The file must be located in GetSystemDirectory.
First, it looks in c:\windows\system\. Next, it looks in windows dir and in the current dir. It will never find it if it is located in d:\winxp\system32.
(Searching for file with the known name in the known directory using FileSearch? Hmm...). The file must be located in GetSystemDirectory.