Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2002-11

Updates

22 messages · 8 people ·

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Mattias Gaertner

Hi all,

You can now add custom components to the IDE. See the files in the components/custom/ directory.

Other updates:

- codetools parser and scanner can now be aborted in progress event
- LCL hints (ShowHint + Hint property)
- added Application.OnIdleEnd: the counterpart of Application.OnIdle
- added Application.OnUserInput: fired just before any mouse or keyboard event is sent to the LCL
- You can now also add any number of OnIdle, OnIdleEnd, OnUserInput handlers.
- TIdleTimer
- fixed moving button focus with LEFT and RIGHT in message dialogs
- started 'Smart Hints'. Move the mouse cursor over an identifier in the source editor and wait a second. Some info will appear.

Mattias

Reply by email

Mattias Gaertner

Hi all,

I added icons from Jens Arm for TStaticText, TUpDown and TShape.

Minors:
- fixed find declaration node cache bug for overloaded procedure

Mattias

Reply by email

Marc Weustink
to Mattias Gaertner: On 18 Nov 2002 11:45:02 -0600 Working out verbally ... ;) That you don't know the keys…
Tony Maro wrote 10 lines
On Mon, 2002-11-18 at 11:59, Mattias Gaertner wrote: > On 18 Nov 2002 11:45:02 -0600 > Tony Maro wrote: > > > > Actually, Ctrl+Mouse would do this in D4. Or was it shift? Now I'm > > confused. I know it wasn't Ctrl+Shift though. But, just for the sake of Lazarus, when I get home I'll try to remember to reboot and test this in Delphi 4 to see exactly what keys do what for multi-select.

I knew it was the opposite of what I think is logic, so I just checked it. It's Shift + Mouse

Marc

Reply by email

Mattias Gaertner
to Mattias Gaertner: On 18 Nov 2002 11:45:02 -0600 Working out verbally ... ;) That you don't know the keys…
Show quoted text 15 lines
At 14:21 18-11-2002 -0600, Tony Maro wrote: >On Mon, 2002-11-18 at 11:59, Mattias Gaertner wrote: > > On 18 Nov 2002 11:45:02 -0600 > > Tony Maro wrote: > > > > > > Actually, Ctrl+Mouse would do this in D4. Or was it shift? Now I'm > > > confused. I know it wasn't Ctrl+Shift though. > >But, just for the sake of Lazarus, when I get home I'll try to remember >to reboot and test this in Delphi 4 to see exactly what keys do what for >multi-select. I knew it was the opposite of what I think is logic, so I just checked it. It's Shift + Mouse

It seems, the selection handling needs some talk.
Because most users expects Delphi(TM) handling, I implemented it very Delphish. The Delphi way is IMO sufficient but not very comfortable, therefore there are some differences.

Delphi's selection tool:

Mouse down without any key on a component:
- If the pointed component was selected
then keep current selection
else select only the pointed component
- On Drag: Move selection

Mouse down without any key on the form:
- selects only the form
- On Drag: rubberband selection of form childs

Mouse down with Shift key on a component:
- add/remove component to/from selection (= xor selection)
- On Drag: nothing

Mouse down with Shift key on the form:
- Delphi: nothing
- On Drag: same as without Shift

Mouse down with Ctrl key on a component:
- select form
- On Drag: rubberband select of childs of
pointed component

Mouse down with Ctrl key on the form:
same as on a component

Ctrl+Shift is the same as Shift.

So, in general there are 5 cases.

Lazarus:

The rubberband is activated, when the mouse is moved and it is not a 'move selection'.

- The rubberband selects not only childs but also grand childs.
- The Shift key is always: xor selection
- The Ctrl key always means: only selects/unselects childs of pointed component
(I just repaired this, so update before you test this)

This is pretty much the same as under Delphi, but IMO more comfortable. For example:
Selecting the child components of two groupboxes:
Delphi: Shift + click, click, click, click, ...
Lazarus: 2 times Ctrl+Shift+Drag

Of course, we can add options:
- one option to switch Ctrl/Shift key
- one option, so that the rubberband does not select grand childs
- one option, so that the rubberband only selects components completely covered

Mattias

Reply by email

Mattias Gaertner

Hi all,

I applied a patch from Vincent Snijders:

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Attached is a patch or better couple of patches to implement timers for LCL. I compiled lazarus & examples on linux with the attached patches and it still works. On win32 some of the examples work, but for example progressbar.exe does not work (it gives a access violation) and I am not sure why. Maybe somehow a pointer get overwritten and is later on used. I just don't know how I can find out.

I implemented the timer on win32 by making a hidden window, which reacts to the wm_timer messages.

I left the gnome interface alone and untested.

List of changes:
- removed DoEvents from InterfaceBase and gtk and win32 decendants
- removed SetTimer and KillTimer from Interface-WinApi
- added CreateTimer and DestroyTimer to InterfaceBase and gtk and win32 decendants, CreateTimer takes as timer function of object as callback.
- application.handlemessage calls idle only if application is not terminated.
- customtimer: removed callback function and called CreateTimer and DestroyTimer in interface.
- Adapted the gtk interface to implement CreateTimer and DestroyTimer
- Implemented CreateTimer and DestroyTimer in win32.
- Simplified WaitMessage in win32
- Made HandleEvents in win32 non-blocking.

I hope this does not break anything serious and you can apply this patch.

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

Mattias

Reply by email

Tony Maro
to Mattias Gaertner: On 18 Nov 2002 11:45:02 -0600 Working out verbally ... ;) That you don't know the keys…
Mattias Gaertner wrote
It seems, the selection handling needs some talk. Because most users expects Delphi(TM) handling, I implemented it very Delphish. The Delphi way is IMO sufficient but not very comfortable, therefore there are some differences.

Why not a setting option "Delphi IDE Selection mode" versus "Lazarus IDE Selection mode"

Get the best of both worlds...

-Tony

Reply by email

Mattias Gaertner
to andrew johnson: AFAIK we still have no up2date win32 build, since lazarus still will not run under win32.…
Show quoted text 15 lines
Hi, Lately I have been lookint at the win32 interface, trying to make it work a bit better. I have never done any low level win32 api programming, so I am a bit in the dark. Still I want to share with you two things which trouble me a lot. Maybe some of you can help me a bit further. Firstly it is not clear what messages the LCL is expecting from an interface object and what messages the an interface object is suposed to handle. The only way is to look at the gtk interface and put some (a lot) writelns to see what it does. An example is the the LM_DESTROY message from the interface to the LCL. When you run lazarus (with gtk on linux) it only seems to be sent for TPage object and not for TForm.

This is because the LCL+gtk does not need any LM_Destroy messages sent back from the interface. But you are right, they should be sent for compatibility.

If you happen to do so on win32 you get into problems for freeing the form twice (once on the LM_DESTROY message and once when Application.Free is called).

This was a bug in the LCL. Fixed.

I think it would be great if there were a document which specified what messages the LCL expects and sends.

It would be cool to have any 'm currently improving the lazarus synedit and hope to write a little help browser, so that we can finally start the online help for lazarus. To your question:
I don't know the winapi either. When I implemented a message, I looked what the VCL does with it. Therefore many messages are VCL compatible, but not really win32 compatible.

Show quoted text 39 lines
Secondly I have problems writing a good windowproc. For example I have this window procedure to handle windows messages (see below). It only works if I comment out the assert, I don't understand why. If I don't comment out the assert in, I get an access violation when I create a window which uses this windowproc. Do I use the right calling convention (stdcall)? Are there some RTL-functions I may not use in a windowproc? Or is there somewhere else some memory corruption which shows up here. I just don't know enough of the win32 api. Very frustating. function WindowProc(Window: HWnd; Msg: UInt; WParam: WParam; LParam: LParam): LResult; stdcall; var LMessage: TLMessage; LCLObject: TObject; CallDefWindowProc: boolean; begin Assert(False, Format('Trace:WindowProc - Window Value: $%S-%d; Msg Value: %S; WParam: $%S; LParam: $%S', [IntToHex(Window, 4), Window, WM_To_String(Msg), IntToHex(WParam, 4), IntToHex(LParam, 4)])); CallDefWindowProc := true; case Msg of WM_CLOSE: Begin Assert(False, 'Trace:WindowProc - Got WM_CLOSE'); LMessage.Msg := LM_CLOSEQUERY; LCLObject := TObject(GetProp(Window, 'Lazarus')); Result := DeliverMessage(LCLObject,LMessage); CallDefWindowProc := false; End; end; if CallDefWindowProc then Result := DefWindowProc(Window, Msg, WParam, LParam); end; Hope somebody can give me some pointers in the right direction.

Good luck.

Mattias

Reply by email

Mattias Gaertner

Hi all,

The form editor now uses InvalidateRect instead of brute force Form.Invalidate. This reduces the flickering.
Plz report if any artefacts are left.

Other updates:
- new form editor options: colors and rubberband
New selection option: You can now choose if the rubberband selects grand childs
- fixed a crashing bug in the resizing code.
- TControl.InvalidateControl is now VCL compatible.

Mattias

Reply by email

Tony Maro
to Florian Klaempfl: Hmmm? Why is Ctrl required? Normally, simply dragging is enough, without Ctrl? Usually,…
Florian Klaempfl wrote 8 lines
>- Press Ctrl+Shift to add/remove child controls to/from the current >selection. > (I don't know how to do this under Delphi. Ctrl+Shift seems appropiate to >me.) Usually, it's done with Ctrl+Mouse. At least D4/D5 doesn't support it though.

Actually, Ctrl+Mouse would do this in D4. Or was it shift? Now I'm confused. I know it wasn't Ctrl+Shift though.

-Tony

Reply by email

andrew johnson
to Christian F: Hi all Were i can download the uptodate win32 binary version of lazarus I know you made a…

AFAIK we still have no up2date win32 build, since lazarus still will not run under win32. it freezes on open. Native Win32 support is still pre-alpha. aka most stuff won't run yet, inspite of the fact a lot of code is in place. I know there has been discussion of working on Win32 and 2-3 people have sent in a few patches, but I cannot really say how long till it is usable. I am hoping not too long, since much of the code is LCL code, aka non-platform specific, but it really depends on how much people have a chance to work on it etc.

Andrew

Christian F wrote 16 lines
Hi all Were i can download the uptodate win32 binary version of lazarus I know you made a lot of progress for linux platform but what about native Win32 ? Thanks Christian F _________________________________________________________________ Add photos to your messages with MSN 8. Get 2 months FREE*. http://join.msn.com/?page=features/featuredemail _________________________________________________________________ To unsubscribe: mail with "unsubscribe" as the Subject archives at http://www.lazarus.freepascal.org/mailarchives

Reply by email

Mattias Gaertner
to Mattias Gaertner: On 18 Nov 2002 11:45:02 -0600 Working out verbally ... ;) That you don't know the keys…
Show quoted text 10 lines
On Tue, 2002-11-19 at 06:23, Mattias Gaertner wrote: > It seems, the selection handling needs some talk. > Because most users expects Delphi(TM) handling, I implemented it very > Delphish. The Delphi way is IMO sufficient but not very comfortable, > therefore there are some differences. Why not a setting option "Delphi IDE Selection mode" versus "Lazarus IDE Selection mode" Get the best of both worlds...

That's why I described the whole Delphi selection. Delphi has some holes, where it simply does nothing. I think, the best of both worlds would be something like a 'Lazarus' mode and a 'Delphi+Lazarus extensions' mode, where the Delphi holes are filled.
The only difference between them is:
- rubberband selects grand childs (Delphi: off, Lazarus: on) I don't think, that we should invent modes for just one option.

Of course we could add a further option:
- Keep Ctrl/Shift multiselection simple
(for users who like to press extra keys without any reason except finger jogging)

But I'm not sure if this will be used often.

Mattias

Reply by email

Florian Klaempfl
to Mattias Gaertner: I have improved the rubberband selection in the designer: - Press Ctrl and drag with the…
you wrote 7 lines
Hi all, I have improved the rubberband selection in the designer: - Press Ctrl and drag with the left mouse button to get a rubberband for all child controls. This is similar to Delphi, with the exception that also grandchilds are selected.

Hmmm? Why is Ctrl required? Normally, simply dragging is enough, without Ctrl?

- Press Ctrl+Shift to add/remove child controls to/from the current selection. (I don't know how to do this under Delphi. Ctrl+Shift seems appropiate to me.)

Usually, it's done with Ctrl+Mouse. At least D4/D5 doesn't support it though.

Show quoted text 15 lines
Also the designer now respects the csNoDesignVisible flag. If a control or one of its parents have this flag, you can't select the control with the mouse. As a first example I have added this flag to the hidden pages of TNotebook. That means that Pages that are currently hidden below the current Page and all their child controls will not longer be auto selected by the rubberband, nor will the guidelines align to these controls. Mattias _________________________________________________________________ To unsubscribe: mail with "unsubscribe" as the Subject archives at http://www.lazarus.freepascal.org/mailarchives

Reply by email

Tony Maro
to Florian Klaempfl
Mattias Gaertner wrote 9 lines
On 18 Nov 2002 11:45:02 -0600 Tony Maro wrote: > > Actually, Ctrl+Mouse would do this in D4. Or was it shift? Now I'm > confused. I know it wasn't Ctrl+Shift though. Working out verbally ... ;) That you don't know the keys in Delphi makes me happy. It means, that you use lazarus more often than Delphi/Kylix.

hehe... I haven't touched Delphi but twice since I got Lazarus. In fact I'm getting to where my time between reboots to Windows is about 3-4 days. You'll notice that all the components I've developed for Lazarus were just plain out of need... I didn't set out to write a sound system one day, I needed one for my project! Every component I've released is used in CBTracker.

But, just for the sake of Lazarus, when I get home I'll try to remember to reboot and test this in Delphi 4 to see exactly what keys do what for multi-select.

The only reason I'll actively use Delphi again will be if I'm contracted to write another Windows database application.

-Tony

Reply by email

Vincent Snijders
to andrew johnson: AFAIK we still have no up2date win32 build, since lazarus still will not run under win32.…

Hi,

Lately I have been lookint at the win32 interface, trying to make it work a bit better.

I have never done any low level win32 api programming, so I am a bit in the dark. Still I want to share with you two things which trouble me a lot. Maybe some of you can help me a bit further.

Firstly it is not clear what messages the LCL is expecting from an interface object and what messages the an interface object is suposed to handle. The only way is to look at the gtk interface and put some (a lot) writelns to see what it does. An example is the the LM_DESTROY message from the interface to the LCL. When you run lazarus (with gtk on linux) it only seems to be sent for TPage object and not for TForm. If you happen to do so on win32 you get into problems for freeing the form twice (once on the LM_DESTROY message and once when Application.Free is called). I think it would be great if there were a document which specified what messages the LCL expects and sends.

Secondly I have problems writing a good windowproc. For example I have this window procedure to handle windows messages (see below). It only works if I comment out the assert, I don't understand why. If I don't comment out the assert in, I get an access violation when I create a window which uses this windowproc. Do I use the right calling convention (stdcall)? Are there some RTL-functions I may not use in a windowproc? Or is there somewhere else some memory corruption which shows up here. I just don't know enough of the win32 api. Very frustating.

function WindowProc(Window: HWnd; Msg: UInt; WParam: WParam; LParam:
LParam): LResult; stdcall;
var
  LMessage: TLMessage;
  LCLObject: TObject;
  CallDefWindowProc: boolean;
begin
  Assert(False,
    Format('Trace:WindowProc - Window Value: $%S-%d; Msg Value: %S; WParam:
$%S; LParam: $%S',
	[IntToHex(Window, 4), Window, WM_To_String(Msg), IntToHex(WParam, 4),
	IntToHex(LParam, 	4)]));
  CallDefWindowProc := true;
  case Msg of
    WM_CLOSE:
    Begin
      Assert(False, 'Trace:WindowProc - Got WM_CLOSE');
      LMessage.Msg := LM_CLOSEQUERY;
      LCLObject := TObject(GetProp(Window, 'Lazarus'));
      Result := DeliverMessage(LCLObject,LMessage);
      CallDefWindowProc := false;
    End;
  end;
  if CallDefWindowProc
    then Result := DefWindowProc(Window, Msg, WParam, LParam);
end;

Hope somebody can give me some pointers in the right direction.

Vincent Snijders.

Reply by email

Mattias Gaertner

Hi all,

I have improved the rubberband selection in the designer:

- Press Ctrl and drag with the left mouse button to get a rubberband for all child controls. This is similar to Delphi, with the exception that also grandchilds are selected.
- Press Ctrl+Shift to add/remove child controls to/from the current selection.
(I don't know how to do this under Delphi. Ctrl+Shift seems appropiate to me.)

Also the designer now respects the csNoDesignVisible flag. If a control or one of its parents have this flag, you can't select the control with the mouse.
As a first example I have added this flag to the hidden pages of TNotebook. That means that Pages that are currently hidden below the current Page and all their child controls will not longer be auto selected by the rubberband, nor will the guidelines align to these controls.

Mattias

Reply by email

Mattias Gaertner
to Mattias Gaertner
Show quoted text 11 lines
At 18:26 18.11.2002, you wrote: >Hi all, > >I have improved the rubberband selection in the designer: > >- Press Ctrl and drag with the left mouse button to get a rubberband > for all child controls. This is similar to Delphi, with the exception > that also grandchilds are selected. Hmmm? Why is Ctrl required? Normally, simply dragging is enough, without Ctrl?

If you start the dragging on the form, then you can omit the Ctrl. If you start on a control without Ctrl means 'Move Selection'.

Show quoted text 18 lines
>- Press Ctrl+Shift to add/remove child controls to/from the current >selection. > (I don't know how to do this under Delphi. Ctrl+Shift seems appropiate > to >me.) Usually, it's done with Ctrl+Mouse. At least D4/D5 doesn't support it though. >Also the designer now respects the csNoDesignVisible flag. If a control >or one of its parents have this flag, you can't select the control with >the mouse. >As a first example I have added this flag to the hidden pages of >TNotebook. That means that Pages that are currently hidden below the >current Page and all their child controls will not longer be auto >selected by the rubberband, nor will the guidelines align to these >controls.

Mattias

Reply by email

Mattias Gaertner
to Florian Klaempfl: Hmmm? Why is Ctrl required? Normally, simply dragging is enough, without Ctrl? Usually,…
Show quoted text 14 lines
On Mon, 2002-11-18 at 11:32, Florian Klaempfl wrote: > > >- Press Ctrl+Shift to add/remove child controls to/from the current > >selection. > > (I don't know how to do this under Delphi. Ctrl+Shift seems > > appropiate to > >me.) > > Usually, it's done with Ctrl+Mouse. At least D4/D5 doesn't support it > though. > Actually, Ctrl+Mouse would do this in D4. Or was it shift? Now I'm confused. I know it wasn't Ctrl+Shift though.

Working out verbally ... ;)
That you don't know the keys in Delphi makes me happy. It means, that you use lazarus more often than Delphi/Kylix.

Mattias

Reply by email

Mattias Gaertner

Hi all,

There is a new code completion feature: Local variable completion.

Probably everyone knows the boring ritual: You need a dummy variable for a loop variable or a temporary string, etc. Under c you can just write 'int i' at any place in the code, but pascal is a cleaner language and so you must move the cursor to the start of the procedure, add a 'var i:integer', and move back to the code, where you were actually working. Now, the IDE can do this for you:

For example:

procedure DoSomething;
begin
  for i:=0 to Count-1 do ;
end;

Move the cursor to the 'i' identifier and invoke the code completion (default is Shift+Ctrl+C).
The code completion looks for <identifier>:=<expression>, then test if the identifier already exists and if not, the code will be changed to:

procedure DoSomething;
var
  i: Integer;
begin
  for i:=0 to Count-1 do ;
end;
You can do this for most assignments. For instance:
  s:='Hi'; // string
  c:='a';  // char
  e:=3.3;  // extended
  f:=ord(clRed); // Integer

It will not work for Low(), High() or constructors and probably a few other constructions. This is not implemented yet.

A you can see, this completion is ambigious. A constant number like 3 can be assigned to many types. What options should be added, so that users can set their favourite types for constants?

Other updates:

- Modified source editors are now marked with a '*' in the pagename
- OI now shows 'X items selected' during multiselection
- find expression type of @String[] constructions
- fixed vertical cursor movement when lines contain tab chars
- TStatusbar now uses InvalidateRect
- new function: InvalidateFrame. This works like InvalidateRect, but invalidates only a rectangle without the content.

Mattias

Reply by email

Martin Smat
to Vincent Snijders: Lately I have been lookint at the win32 interface, trying to make it work a bit better. I…
Vincent Snijders wrote 15 lines
... Secondly I have problems writing a good windowproc. For example I have this window procedure to handle windows messages (see below). It only works if I comment out the assert, I don't understand why. If I don't comment out the assert in, I get an access violation when I create a window which uses this windowproc. Do I use the right calling convention (stdcall)? Are there some RTL-functions I may not use in a windowproc? Or is there somewhere else some memory corruption which shows up here. I just don't know enough of the win32 api. Very frustating. ... Vincent Snijders.

The stdcall convention is right.
For assertion support you must define your own AssertErrorProc procedure. For more info read FPC documentation (file fpc\doc\prog\node3.html). Lazarus defines this procedure in file lcl\utrace.pp.

Martin Smat.

Reply by email

Vincent Snijders
to Mattias Gaertner: On Fri, 29 Nov 2002 21:44:39 +0100 This is because the LCL+gtk does not need any…

Mattias,

Thanks for your answers. Still some questions left.

Show quoted text 6 lines
-----Oorspronkelijk bericht----- Van: Mattias Gaertner On Fri, 29 Nov 2002 21:44:39 +0100 "Vincent Snijders" wrote:

<snip>

Show quoted text 13 lines
> Firstly it is not clear what messages the LCL is expecting from an > interface object and what messages the an interface object is suposed to > handle. The only way is to look at the gtk interface and put some (a lot) > writelns to see what it does. An example is the the LM_DESTROY message > from the interface to the LCL. When you run lazarus (with gtk on linux) it > only seems to be sent for TPage object and not for TForm. This is because the LCL+gtk does not need any LM_Destroy messages sent back from the interface. But you are right, they should be sent for compatibility.

If the LCL + gtk doesn't need it, LCL doesn't need it. Why should any other interface send such a message for compatibility reasons?

Show quoted text 5 lines
> If you happen to do so on > win32 you get into problems for freeing the form twice (once on the > LM_DESTROY message and once when Application.Free is called). This was a bug in the LCL. Fixed.

Ok, now the CustomForm doesn't do anything with LM_DESTROY except calling inherited. Why not remove the message handler completely?

Reply by email

Mattias Gaertner
to Mattias Gaertner: On Fri, 29 Nov 2002 21:44:39 +0100 This is because the LCL+gtk does not need any…
Show quoted text 27 lines
Mattias, Thanks for your answers. Still some questions left. > -----Oorspronkelijk bericht----- > Van: Mattias Gaertner > > On Fri, 29 Nov 2002 21:44:39 +0100 > "Vincent Snijders" wrote: > <snip> > > Firstly it is not clear what messages the LCL is expecting from an > > interface object and what messages the an interface object is suposed > > to handle. The only way is to look at the gtk interface and put > some (a lot) > > writelns to see what it does. An example is the the LM_DESTROY message > > from the interface to the LCL. When you run lazarus (with gtk > on linux) it > > only seems to be sent for TPage object and not for TForm. > > This is because the LCL+gtk does not need any LM_Destroy messages > sent back > from the interface. But you are right, they should be sent for > compatibility. If the LCL + gtk doesn't need it, LCL doesn't need it.

No, it does only mean, that the "base" LCL doesn't need it. Every interface has some special behaviour and lacks some features. For example setting the size and position of a form heavily depends on the window manager.
Another example are the size messages. Under gtk size messages are sent in a very different way than under win32. The gtk resizes a widget and its inner widget independently. The VCL does not distinguish between them, because win32 does not distinguish. Therefore the LCL must support both.

Why should any other interface send such a message for compatibility reasons?

I'm not sure if any component will ever need it. The VCL uses it a few times, so guess we should keep the code. It doesn't hurt.

Show quoted text 7 lines
> > If you happen to do so on > > win32 you get into problems for freeing the form twice (once on the > > LM_DESTROY message and once when Application.Free is called). > > This was a bug in the LCL. Fixed. Ok, now the CustomForm doesn't do anything with LM_DESTROY except calling inherited. Why not remove the message handler completely?

Right. The VCL uses the message to clean up the menu. This is only needed under the win32 interface, so it should be done there. I deleted it.

Mattias

Reply by email