TShellListView Lazarus - lazarus

Is it possible to set a item in a ShellListView to visible=false? I thought About something like ShelLlistView.Items.visible(false) but that does not exist and I have no idea for another solution so I hope someone can help me with this.
I can't post any Code for you yet because I don't have any ShellListView yet but I hope you can give me some advice how it could work.

Afaik, this is not possible because the ListItems shown in the TShellListView have no Visible property. However, according to Peter Below (TeamB), you can effectively "hide" an item by destroying it. See http://www.delphigroups.info/2/67/290279.html
Of course, if you wish to "unhide" an item hidden in this way, you would need to create & add a new node with the same Caption, etc.
This code works fine for me using the standard Lazarus TShellListView:
procedure TForm1.Button1Click(Sender: TObject);
var
Item : TListItem;
begin
Item := ShellListView1.Items[0];
Caption := Item.Caption;
Item.Free;
end;
and removes the first item in the list.
The following removes all the items in the ShellListView. THe downto is to account for the fact that the Count value decreases on each iteration of the loop.
procedure TForm1.Button1Click(Sender: TObject);
var
i : Integer;
begin
for i := ShellListView1.Items.Count - 1 downto 0 do
ShellListView1.Items[i].Free;
end;

Related

FMX: How to create a menu item shortcut for a single key?

I need this to create a shortcut for a single key in a TMainMenu and TMenuBar, like 'I' for example. Options for single keys are not selectable in the property editor for the shortcut property of a TMenuItem. I have also tried setting the shortcut for the menu item at run time using either of the methods below.
MenuItem.ShortCut := vkI;
MenuItem.ShortCut := TextToShortcut('I');
The shortcut character 'I' does show up in the menu, however, it doesn't work. I press the 'I' key and nothing happens. However, if I set the shortcut using the following then it does work correctly, so I assume the issue is just that it doesn't allow single key shortcuts.
MenuItem.ShortCut := TextToShortcut('Ctrl+I');
I have also tried linking the menu item to an action in a TActionList and setting the shortcut of the action in the same way, but the result is the same.
The solution I've found is to handle the key press in the FormKeyDown event to trigger the action, but this seems unnecessary. Why doesn't it work as expected?
I'm using Delphi 10.4 and building for Windows 32-bit.
Because your shortcut didn't contain a dialog key.
procedure TCommonCustomForm.IsDialogKey(const Key: Word; const KeyChar: WideChar; const Shift: TShiftState;
var IsDialog: boolean);
begin
IsDialog := (KeyChar < ' ') or ((Shift * [ssAlt, ssCtrl, ssCommand]) <> []);
end;
In TCommonCustomForm.KeyDown, the code start to check if your shortcut contain a dialog key, if yes it will check your menu and execute the action :
// 3. perform key in other Menus
for I := ChildrenCount - 1 downto 0 do
if Children[i] <> FocusPopup then
begin
if Children[I] is TMainMenu then
TMainMenu(Children[I]).DialogKey(Key, Shift)
else if Children[I] is TPopupMenu then
TPopupMenu(Children[I]).DialogKey(Key, Shift);
if Key = 0 then
Exit;
end;
You can override this method and return true for each key you press

Enable/Disable Item based on the value of the List Item

I am trying to code a listItem which will have 2 values "New" and "Edit". I also have a search (Push Button) in the same canvas. I want to disable the Search button when I have selected "new" in the list item and enable it when "Edit" is selected in the list item.
Here is my code : I am using Oracle Forms 6i , WHEN_LIST_CHANGED Trigger ..
begin
if :CONTROL.LI_DO='New' then
go_item('PB_SEARCH');
SET_ITEM_PROPERTY('PB_SEARCH',enabled,property_false);
else if :CONTROL.LI_DO='Edit' then
go_item('PB_SEARCH');
SET_ITEM_PROPERTY('PB_SEARCH',enabled,property_true);
end if;
end if;
end;
Any help is appreciated .
Been a while since I did forms, but can you disable an item that has current focus?
I.e. navigate (GO_ITEM) to another item then try to disable PB_SEARCH.
LI_DO.Functional."Elements in List" : New (value 0), Edit (value 1);
LI_DO.Data."Data Type" : Number;
LI_DO."Initial Value" : 1;
LI_DO.Required : "Yes";
After those regulations, you may use the code below for "WHEN-LIST-CHANGED";
begin
if :CONTROL.LI_DO = 0 then
--go_item('PB_SEARCH');
SET_ITEM_PROPERTY('PB_SEARCH',enabled,property_false);
--else if :CONTROL.LI_DO = 1 then
elsif :CONTROL.LI_DO = 1 then
--go_item('PB_SEARCH');
SET_ITEM_PROPERTY('PB_SEARCH',enabled,property_true);
end if;
--end if;
end;
You have to know the concept of using enabled property.
The following blog illustrate this with an example clears the point of misunderstanding of using 'enabled'property alone.
Pls. follow the solution steps you should be familiar with the four required properties in co-ordinate with each together to enable and disable an item

How to open MSI table in Delphi?

I need to open some tables from MSI database, read this and place some rows in this with using Delphi (in my example is Delphi 7 but allowed other versions if it needed).
For example it would look like ORCA. Msi must be open, written to the table where it's can be edited and written to the msi file.
By default, Delphi can't open MSI tables as I thing but I found a JEDI Windows API where exists libraris like JwaMsi and JwaMsiQuery. But I can't find documentations or examples of using functions like
function MsiOpenProduct(szProduct: LPCTSTR; var hProduct: MSIHANDLE): UINT; stdcall;
{$EXTERNALSYM MsiOpenProduct}
By the way, while I search information about this I found this code:
const msilib = 'msi.dll';
type
MSIHANDLE = DWORD;
TMsiHandle = MSIHANDLE;
function MsiCloseHandle(hAny: MSIHANDLE):UINT;stdcall;external msilib name 'MsiCloseHandle';
function MsiOpenProduct(szProduct:LPCSTR;var hProduct:MSIHANDLE):UINT;stdcall;external msilib name 'MsiOpenProductA';
function MsiGetProductProperty(hProduct:MSIHANDLE;szProperty:LPCSTR;lpValueBuf:LPSTR;pcchValueBuf:LPDWORD):UINT;stdcall; external msilib name 'MsiGetProductPropertyA';
function MsiSetInternalUI(dwUILevel:INSTALLUILEVEL;phWnd:LPHWND):INSTALLUILEVEL;stdcall; external msilib name 'MsiSetInternalUI';
function GetMSIProperty(aProductCode:string):string;
var
msi:TMSIHandle;
t:string;
function _getmsiproperty(_name:string):string;
var
txt:PChar;
sz:DWORD;
begin
sz:=MAX_PATH;
txt:=AllocMem(sz+1);
if MsiGetProductProperty(msi,PChar(_name),txt,#sz)=ERROR_MORE_DATA then
begin
ReAllocMem(txt,sz+1);
MsiGetProductProperty(msi,PChar(_name),txt,#sz);
end;
SetString(Result,txt,sz);
FreeMem(txt,sz+1);
end;
begin
MsiSetInternalUI(2,nil); // скрываем GUI/hide GUI
if MsiOpenProduct(PChar(aProductCode),msi)=ERROR_SUCCESS then
begin
t:=_getmsiproperty('ARPPRODUCTICON'); // главная иконка приложения/main program icon
if t='' then t:=_getmsiproperty('ProductIcon');
if t='' then t:=_getmsiproperty('CompleteSetupIcon');
if t='' then t:=_getmsiproperty('CustomSetupIcon');
if t='' then t:=_getmsiproperty('InfoIcon');
if t='' then t:=_getmsiproperty('InstallerIcon');
if t='' then t:=_getmsiproperty('RemoveIcon');
if t='' then t:=_getmsiproperty('RepairIcon');
Result:=t;
MsiCloseHandle(msi);
end;
end;
What is better to use and where I can see documentation and/or examples?
P.S. Sorry for my English
I would use the COM-based API to MSI.
See this thread and the MSI API documentation
So, solution exist and I do it! It's not so gracefully but it's working...
At first, read articles from MSDN (in my case is articles about work with MSI database).
At second, you must understand what you need to do. In my case it is:
Open msi database
Read table names
Read needed table and show it
If it needed - make changes and save it
Here I show how I do the three first steps.
Preparing
Download JEDI Windows AP (for easer work with msi) and add to project JwaMsi.pas, JwaMsiDefs.pas and JwaMsiQuery.pas (do not forget about USES list). Dependencies will be added automatically.
Next, place on form all needed components.
Let's code!
1 Open msi database and 2 Read table names
Define variables for handlers and for buffers
var msi_handler_DB, msi_handler_view, msi_handler_record:TMSIHandle;
txt:PChar;
sz:DWORD;
Now we need take a list of tables (more info here) and place it in ListBox, for example
begin
sz:=MAX_PATH; //initialise
txt:=AllocMem(sz+1); //variables
OpenDialog1.Execute; //select file
If MsiOpenDatabase(PChar(OpenDialog1.FileName), MSIDBOPEN_DIRECT, msi_handler_DB)=ERROR_SUCCESS //check if DB is open
then begin //start reading
Listbox1.Clear; //prepare listbox for filling
MsiDatabaseOpenView(msi_handler_DB, 'SELECT * FROM _Tables',msi_handler_view); //prepare query to _Table
MsiViewExecute(msi_handler_view, msi_handler_record); //execute...
While not MsiViewFetch(msi_handler_view, msi_handler_record)=ERROR_NO_MORE_ITEMS //and fetch it in cycle until end of table
do begin
MsiRecordGetString(msi_handler_record,1,txt,sz); //read string
ListBox1.Items.Add(txt); //and write to listbox
end; //end of fetch cycle
MsiCloseAllHandles; //close handles (we don't need they more)
end; //stop reading
end;
3 Read needed table and show it
It's easy!
begin
edit1.text:=Listbox1.Items.ValueFromIndex[ListBox1.ItemIndex];
If MsiOpenDatabase(PChar(OpenDialog1.FileName), MSIDBOPEN_DIRECT, msi_handler_DB)=ERROR_SUCCESS //open database again
then begin
MsiDatabaseExport(msi_handler_DB, pchar(ListBox1.Items.Strings[ListBox1.ItemIndex]), 'C:\Windows\Temp', Pchar(ListBox1.Items.Strings[ListBox1.ItemIndex]+'.idt')); //export table to .idt
MsiCloseAllHandles; //and close handler again
//...
//here must be placed code for
//parsing .idt as tabulation separated file
//with wordwrap and show it.
//for example - in StringGrid
//...
DeleteFile('C:\Windows\Temp\'+ ListBox1.Items.Strings[ListBox1.ItemIndex]+'.idt'); //do not forget delete temporary .idt file. save our planet! :)
end;
end;
4 If it needed - make changes and save it
Just repeat third step from the end to beginning: export StringGrid to file (about .idt and it's structure you can read here: one, two, three), import in to MSI with property MsiDatabaseImport (do not forget open database before and append it after, also remember about close handlers) and delete temporary .idt file.
Good luck!
P.S. Sorry for my English, part two :)

WHMCS API addticketreply not working with message

I have tried everything I can think of and can't get line breaks to go along with the addticketreply command to the API of WHMCS. It's coming from a window's application not PHP or anything. Simple query string, all characters are included before the first line break. After that line break nothing else of the message is included.
I've tried a urlencode method, no go, the API just inserts all text including the encoded part ha ha and doesn't unencode. I've tried replacing line breaks \n with <br> but still WHMCS just shows the <br> ha ha
Any clues how to get full messages through the API including new lines? Documentation is really lacking on this one.
Also the variable adminusername doesn't work at all! I've tried a full name, nope, I've tried username and userid and nope, still inserts my logged in username for ticket replies. The user I am using is a super admin with full access.
The query string for the API looks like this:
accesskey=key&adminusername=Name of Person&ticketid=488&message=asdf asdf
Nothing past that first line goes :(
sad sad sad&action=addticketreply&responsetype=json&username=admin&password=mypassword
I should mention I am using Indy for the Post. Everything else works, any other command passed works, heck even the ticket is updated, the message is updated. It just won't go past that first line break.
EDIT CODE: As I mentioned above I DO let IdHTTP encode my text using the post params, so it should work, AND as I also mentioned above I have tried to pre-encode it, with the results being that WHMCS won't un-encode then and just shows the %20's and other's. Since it's been asked I should mention, I am using Indy 10! So I am up to date with the most recent version.
j := TStringList.Create;
ret := TStringStream.Create('');
j.Text := k+params+'action='+method+'&responsetype=json&username='+username1.Text+'&password='+password;
try
htp1.Post('http'+s+'://'+url1.Text+'/includes/api.php', j, ret);
except
on E: EIdHTTPProtocolException do
result := '{"result":"error", "message":"'+htp1.ResponseText+'"}';
end;
Line breaks, spaces, and other reserved characters need to be URL-encoded, eg:
accesskey=key&adminusername=Name%20of%20Person&ticketid=488&message=asdf%20asdf%0D%0ANothing%20past%20that%20first%20line%20goes%20%3A%28%0D%0Asad%20sad%20sad&action=addticketreply&responsetype=json&username=admin&password=mypassword
If you are sending the values via a POST request, then make sure you are usiug an up-to-date version of Indy. TIdHTTP.Post(TStrings) handles the encoding for you, eg:
var
Params: TStringList;
begin
Params := TStringList.Create;
try
Params.Add('accesskey=key');
Params.Add('adminusername=Name of Person');
Params.Add('ticketid=488');
Params.Add('message=asdf asdf'+CRLF+'Nothing past that first line goes :('+CRLF+'sad sad sad');
Params.Add('action=addticketreply');
Params.Add('responsetype=json');
Params.Add('username=admin');
Params.Add('password=mypassword');
IdHTTP1.Post(URL, Params);
finally
Params.Free;
end;
end;
However, if you are sending the values via a URL query string, then you have to manually encode the URL, TdHTTP will not handle that for you, eg:
var
URL: String;
begin
URL := 'http://host/path?accesskey=key&adminusername=Name of Person&ticketid=488&message=asdf asdf'+CRLF+'Nothing past that first line goes :('+CRLF+'sad sad sad&action=addticketreply&responsetype=json&username=admin&password=mypassword';
IdHTTP1.Get(TIdURI.URLEncode(URL));
end;
Or:
var
URL: String;
Params: TStringList;
I: Integer;
begin
Params := TStringList.Create;
try
Params.Add('accesskey=key');
Params.Add('adminusername=Name of Person');
Params.Add('ticketid=488');
Params.Add('message=asdf asdf'+CRLF+'Nothing past that first line goes :('+CRLF+'sad sad sad');
Params.Add('action=addticketreply');
Params.Add('responsetype=json');
Params.Add('username=admin');
Params.Add('password=mypassword');
for I := 0 to Params.Count-1 do
Params[i] := TIdURI.ParamsEncode(Params[i]);
Params.Delimiter := '&';
Params.StrictDelimiter := True;
Params.Quotechar := #0;
URL := 'http://host/' + TIdURI.PathEncode('path') + '?' + Params.DelimitedText;
IdHTTP1.Get(URL);
finally
Params.Free;
end;
end;

searching a text file the writing result to memo, lazarus

this segment in my program first ads a customer to a textfile (declared in public variables) and saves it to a texfile. onbutton1click is the procudere to search the string thats in the editbox and return the relevant customer details to memo. the add customer works fine and adds to textfile, however when i search it returns nothing onto the memo, just the memo caption, memo1. any way i can resolve this? sorry im a newb to this.
procedure TForm2.btnsaveClick(Sender: TObject);
begin
cusfname:= edit1.text ;
cuslname:= edit2.text;
adress:= edit3.text;
phone:= edit4.text;
password:= edit5.Text;
AssignFile(F, 'Data.txt');
append(F);
WriteLn(F, cusfname);
WriteLn(F, cuslname);
WriteLn(F, adress);
WriteLn(F, phone);
WriteLn(F, password);
CloseFile(F);
end;
procedure TForm2.Button1Click(Sender: TObject);
var
SearchFile : Textfile;
found: boolean;
search: string;
begin
search := edit1.text;
Assignfile(SearchFile, 'data.txt');
Reset(SearchFile);
found:= false;
repeat
found:= search = phone
until eof(searchfile) or found;
if found then
memo1.append(phone);
memo1.append(cusfname);
memo1.append(adress);
if not found then
showmessage('member not found');
end;
wonder where are the read statements? In the write function you have Write() statements, but in the reading code no read() statements?
In your code you do not read() from file. In other similar question (probably your own): runerror(102) file not assigned? there is read(). But I think you should use readln(), or even better use TStringList class from Classes unit with its LoadFromFile() method and Lines property.

Resources