Find deleted appointment in Outlook from Delphi - delphi

I am attempting to integrate our program with outlook and my test code below (sort of works) but have the following issues.
In testing I was able to add the appointment. Then manually moved the appointment to another date/time and re-ran the test program and it moved back (as expected). But...
when an item is deleted the code below is still able to locate the item (somehow!). I even manually removed the item from the Deleted Items folder in Outlook.
as a result,since it 'found' the appointment, it then attempts to update it, resulting in a AV as well. I suspect there is something wrong in my use of the find function, but what I am trying to do is to use userProperties to add something from our system to add into the appointment item in outlook and update if needed. But also needs to be able to handle the case where a user might manually delete item from calendar as well.
Any assistance would be greatly appreciated.
folder := ns.GetDefaultFolder(olFolderCalendar);
if not VarIsNull(folder) and not VarIsEmpty(folder) then
begin
try
appointment := folder.Items.Find('[MyRecProperty2]=' + quotedStr(1001));
entryFound := true;
except
end;
if (not entryFound) or
(varType(Appointment)=varNull) or
(varType(Appointment)=varEmpty) then
begin
appointment := folder.Items.Add(olAppointmentItem);
prop := appointment.UserProperties.Add('MyRecProperty2',olText,True);
prop.Value := '1001';
NewAppointment(appointment);
end
else
begin
showmessage('updating appointment!');
FillAppointment(appointment, false);
end;
showmessage('saving appointment!');
appointment.Save;
//showmessage('display appointment!');
//appointment.Display(true);

Related

Odd behaviour when adding a toolbutton to the delphi ide

I was trying out some things and wanted to make a delphi IDE extension.
My basic idea was expanding the ToDo list feature that is currently in the IDE.
Step one was adding a toolbutton to the IDE which would open a form showing the todo items.
But I noticed some weird things that I hopefully caused myself since that would mean it can be easily fixed.
I am adding my toolbutton to the CustomToolbar, which is the one with the blue questionmark (see screenshot later)
The thing that happens: I install my package and the button is added with the correct image, right next to the existing button.
Now I close the modal form with the installed packages and then the blue questionmark changes.
Don't mind the icon I used, I will use a different one eventually but ok.
So basicly the existing item changes to my own icon but disabled for some reason. And I can't figure out why this happens.
As suggested in the guide I found online I used a TDatamodule to implement my code.
My code:
procedure TDatamoduleToDoList.Initialize;
var
LResource, LhInst: Cardinal;
begin
LhInst := FindClassHInstance(Self.ClassType);
if LhInst > 0 then
begin
LResource := FindResource(LhInst, 'icon', RT_Bitmap);
if LResource > 0 then
begin
FBMP := Vcl.Graphics.TBitmap.Create;
FBMP.LoadFromResourceName(LhInst, 'icon');
end
else
DoRaise('Resource not found');
end
else
DoRaise('HInstance Couldn''t be found');
FToDoAction := TTodoAction.Create(Self);
FToDoAction.Category := actionCat;
FToDoAction.ImageIndex := FIntaServices.ImageList.Add(FBMP, nil);
FToDoAction.Name := 'my_very_own_action_man';
end;
procedure TDatamoduleToDoList.DataModuleCreate(Sender: TObject);
begin
//Create extension
if Supports(BorlandIDEServices, INTAServices, FIntaServices) then
begin
Initialize;
if FToDoAction <> nil then
FCustBut := TSpeedButton(FIntaServices.AddToolButton(sCustomToolBar, 'CstmToDoList', FToDoAction))
else
DoRaise('Initialize failed');
end
else
DoRaise('Something went wrong');
end;
DoRaise is my own procedure that simply destroys all of my objects and raises an exception, did this to prevent mem leaks in the ide.
But, I think, I don't do anything weird but yet this problem occurs.
So I'm hoping someone here might have done something simular and sees the error in my code.
Thanks in advance.
P.s. if you need any more info or see the rest of the unit let me know and ill put the entire unit on github or something like that.
Edit:
Thanks to #Uwe Raabe I managed to solve this problem.
The problem was found in the comments of INTAServices.AddImages
AddImages takes all the images from the given image list and adds them
to the
main application imagelist. It also creates an internal mapping array from the
original image indices to the new indices in the main imagelist. This
mapping is used by AddActionMenu to remap the ImageIndex property of the
action object to the new ImageIndex. This should be the first method
called when adding actions and menu items to the main application window.
The return value is the first index in the main application image list of
the first image in the source list. Call this function with an nil
image list to clear the internal mapping array. Unlike the AddImages function from
the ancestor interface, this version takes an Ident that allows the same base index
to be re-used. This is useful when the IDE implements demand-loading of
personalities so that the images will only get registered once and the same image
indices can be used.
The solution eventually was adding my image to a local imagelist which was added to the imagelist of IntaServices
Code:
procedure TDatamoduleToDoList.DataModuleCreate(Sender: TObject);
begin
//Create extension
if Supports(BorlandIDEServices, INTAServices, FIntaServices) then
begin
Initialize;
if FToDoAction <> nil then
begin
FCustBut := TSpeedButton(FIntaServices.AddToolButton(sCustomToolBar, 'CstmToDoList', FToDoAction));
FToDoAction.ImageIndex := FIntaServices.AddImages(FImages);//This is the fix
end
else
DoRaise('Initialize failed');
end
else
DoRaise('Something went wrong');
end;
You are not supposed to fiddle around with the INTAServices.ImageList directly. Instead use either INTAServices.AddMasked or INTAServices.AddImages (in case you have a local imagelist in your datamodule).
You can safely use the INTAServices.ImageList to be connected to your controls, but you should neither Add nor Delete the images in it directly.

Cannot Destroy Dynamically created Menu Item in Delphi

Firstly, yes I have looked all over the net and still cannot seem to destroy dynamically created menu items. Using Delphi XE. I create the items thus (for the purposes of the exercise SubMenuName is 'Test1':
MenuItemCreated := TMenuItem.Create(PopupMenu1);
MenuItemCreated.Caption:= SubMenuCaption
MenuItemCreated.Hint := SubMenuHint;
MenuItemCreated.Name := SubMenuName;
MenuItemCreated.OnClick := SubMenuClick;
MenuItemCreated.AutoHotkeys := maManual;
MySubMenu.Add(MenuItemCreated);
There is no issue using the sub-menu(s) created. The procedure SubMenuClick works as it should, and I identify the correct subMenu item so no issues there. What I then do is an application logout which is supposed to free the dynamically created sub-menus using this code (although I have tried many variations):
// Get rid of the menu items created
While MySubMenu.Count > 0 do
begin
Itemtodelete := MySubMenu.Items[0];
FreeandNil(ItemtoDelete);
end;
I have put in showmessage() debug lines that show the component names of the menu items being freeandnil'd and they are what I'd expect, ie. 'Test1' and any others I've created. I then log back in to my application (which was still running, but with me logged out). The software then tries to recreate the same sub menus with the same names (as nothing has changed as far as my application is concerned and they were previously disposed of (supposedly)). I immediately get the exception raised:
Error: A component Named Test1 already exists
I am at a complete loss as to how to dispose of the submenu items so that I can recreate them later with the same names.
Any help greatly appreciated.
Thanks,
KB
You did not say it, so I have to assume that MySubMenu is a MenuItem of PopupMenu1. If not please clarify.
To delete items from MySubMenu in order to recreate them again later, it's easyest to call the Clear method:
procedure TForm5.Button2Click(Sender: TObject);
begin
MySubMenu.Clear;
end;
which deletes all menu items of MySubMenu and frees their memory.
In order to recreate the items later, you can not use Delete() or Remove(), without also freeing the memory because they do not free the memory of the items. This is documented in help:
http://docwiki.embarcadero.com/Libraries/XE7/en/Vcl.Menus.TMenuItem.Delete
http://docwiki.embarcadero.com/Libraries/XE7/en/Vcl.Menus.TMenuItem.Remove
With these methods you must free the memory yourself, before you recreate the menu items. But then, it's not necessary to even call Delete or Remove, you can just simply Free the items:
procedure TForm5.Button2Click(Sender: TObject);
var
mi: TMenuItem;
begin
while MySubMenu.Count > 0 do
begin
mi := MySubMenu.Items[0];
mi.Free;
end;
end;
There's no need to call FreeAndNil.
This last option looks very much as yours, with which you had problems when recreating the menu items. I can't reproduce the error except when using Delete() or Remove() without freeing.
Since the Popup menu owns the items, you do not Free it. Instead of FreeAndNil use MySubMenu.Delete(0) OR more appropriately MySubMenu.Items.Clear instead of the entire While routine.
On App shutdown the popup menu will clear it, there's no need to do it manually unless you're rebuilding the menu.

Deleting items within Listbox that contains Group Headers

I am using Delphi XE5, working on an iOS application. I have come across an access violation that occurs during my application when deleting all items from a Listbox using for example:
ListBox1.beginUpdate;
ListBox1.items.clear;
// do work, to re-add new data to list box
...
Listbox1.endUpdate;
Normally, the code above works fine and no errors occur, in fact, all the items currently get deleted from that list box. EXCEPT - I get an access violation. The only difference with this TListBox in particular compared to others, is that it has TListboxGroupHeaderItems.
My work-around has been the following solution:
ListBox1.BeginUpdate;
p := Listbox1.Items.Count;
while p <> 0 do begin
ListBox1.Items.Delete(p-1)
p := p - 1;
end;
// Do Work, re-add new data, etc.
....
ListBox1.endUpdate;
Anyone know of a proper way to clear all items, including groupHeaders from a TListbox without triggering an access violation ? Or is it something else wrong that I am doing ?
When ran in the debugger, it does break on ListBox1.items.clear;
Update (10/14/2013 2:14PM):
No Error message when working in iOS 6 Device/Simulator, but error does occur when working with iOS7
I've had a simular problem. My solution was the following:
for I := Listbox1.Count -1 downto 0 do
begin
Listbox1.RemoveObject(Listbox1.ListItems[i]);
end;

Update existing Google calendar entry using TMS Cloud component

I use the following code to add a new entry to my Google calendar using TMS Cloud component
I have the new entries in a table in a database and they are added OK
But I would like to be able to update an entry also, but that I cant find any help about.
The demo that is supplied with the controls has the option, but I don't want to load all entries from the calendar and select the one to edit there. I want to save some sort of entryID that it is given and use that to update.
DevExpress has an option that lets you sync a table to and from Outlook calendar (works very nice) so I think it should be possible to do with TMS
The question is: how do I get an ID of a calendar entry when creating it so I can store it with the record in table and use it afterwards for updating?
var
ci: TGCalendarItem;
begin
AdvGCalendar.App.Key := Settings.Google.Key;
AdvGCalendar.App.Secret := Settings.Google.Secret;
AdvGCalendar.Logging := true;
if not AdvGCalendar.TestTokens then
AdvGCalendar.RefreshAccess;
if not AdvGCalendar.TestTokens then
AdvGCalendar.DoAuth
else
Connected := True;
ci := AdvGCalendar.Items.Add;
AdvGCalendar.GetCalendars();
ci.CalendarID := Settings.Google.Calendar;
if ci.CalendarID <> '' then
begin
ci.Location := CiLocation;
ci.Description := CiDescription;
ci.Summary := CiSummary;
ci.StartTime := EncodeDateTime(YearOf(StartDate), MonthOf(StartDate), DayOf(StartDate), HourOf(StartTime), MinuteOf(StartTime), 0, 0);
ci.EndTime := EncodeDateTime(YearOf(StopDate), MonthOf(StopDate), DayOf(StopDate), HourOf(StopTime), MinuteOf(StopTime), 0, 0);
ci.IsAllDay := False;
ci.Visibility := viPrivate;
AdvGCalendar.Add(ci);
end;
end;
The problem was related to the fact that there was an error in the version I was using of the component so that the ID was not returned OK.
Upgrading to a newer version fixed this so now it is working.

Is there some better way to copy all DataSet Fields and their properties to another DataSet?

I'm cloning a TClientDataSet and I want to copy all the fields to the clone (which is a new DataSet), I know I can loop through the Fields and copy the info, or make 2 instances of my class and just clone the cursor, but is there some better way? Something like create a new DataSet and assign the fields info?
EDIT:
The following class helper method works for me:
procedure TDataSetHelper.CopyFieldDefs(Source: TDataSet);
var
Field, NewField: TField;
FieldDef: TFieldDef;
begin
for Field in Source.Fields do
begin
FieldDef := FieldDefs.AddFieldDef;
FieldDef.DataType := Field.DataType;
FieldDef.Size := Field.Size;
FieldDef.Name := Field.FieldName;
NewField := FieldDef.CreateField(Self);
NewField.Visible := Field.Visible;
NewField.DisplayLabel := Field.DisplayLabel;
NewField.DisplayWidth := Field.DisplayWidth;
NewField.EditMask := Field.EditMask;
if IsPublishedProp(Field, 'currency') then
SetPropValue(NewField, 'currency', GetPropValue(Field, 'currency'));
end;
end;
Anyone has a better way for doing this?
If you just want to copy the field definitions you can do the following:
ds2.FieldDefs.Assign(ds1.FieldDefs);
ds2.CreateDataSet;
ds2.Open;
Of course this assumes you created FieldDefs for ds1.
Are you looking for a more aesthetic way of doing it or a faster way of doing it?
If the former, create your own classes that hide the loop.
If the latter, don't even worry about it. A very wise coder once said to me: disk access costs; network access costs; maybe screen access costs; everything else is free.
Don't confuse the size of the source code with execution time. Looping a thousand times through memory and copying bits is undetectable compared to the initial handshake of a database connection.
Cheers
If you're going to loop through the dataset to make a copy, remember to call DisableControls on it before, and EnableControl afterwards.
Without that, things can get really slow if you've got visual controls showing the data of the dataset on your form.
Would CloneCursor work for you?
NON-PROGRAMMABLE METHOD
first tclientdataset: open fields editor. add all fields if not already shown. select all fields. copy to clipboard.
second tclientdataset: open fields editor. paste clipboard in fields editor.
done
you should now see identical fieldDefs for both tclientdatasets now.

Resources