Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have created a custom form, to resamble a dialog. Then I overloaded the MessageDlg function in a special unit to call this form. Great, its working A-OK.
When I call the form, it's shown as a Modal, and inside this modal I need the caller form name.
Example: FormA calls unit U_Functions that overloads MessageDlg. Then U_Functions calls FormDLG and it's shown. Inside FormDLG I execute function "GetParentFormName" and it returns "FormA".
I already tried GetForegroundWindow, but it returns the same thing as Self. Self.Parent is null. How can I get the modal caller's reference(TForm)?
Example of flow
FormA:
procedure TFormA.Button1Click(Sender: TObject);
begin
MessageDlg('Call Dialog', mtWarning, [mbOK], 0);
end;
U_Functions
function MessageDlg(Msg: String; Icone: TMsgDlgType; Botoes: TMsgDlgButtons): Integer; overload;
begin
Result := FormDialog.fn_ShowMessage(msg, Icone, Botoes);
end;
FormDialog
function FormDialog.fn_ShowMessage(Msg: String; Icone: TMsgDlgType; Botoes: TMsgDlgButtons): Integer;
begin
// Get FormA's name
end;
Remy Lebeau's approach (Screen.ActiveForm) accomplished exactly what I was looking for. Thank you so much for your time.
Since there's a middle unit, it gathered the callers name, and sent via parameter to the third form (Dialog).
Related
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 26 days ago.
Improve this question
According to the Parameter hints in Delphi 11.1 the MessageDlg should support custom button names defined in an array of strings in the last parameter.
e.g.
I cannot get this to work. I declared a constant 3 element array of string but the compiler claims there is no overloaded version of MessageDlg with this format.
Can anyone show me how this should work or is it an error in the parameter help.
no problem it works fine
var
CustomButtonCaptions: array of string;
begin
SetLength(CustomButtonCaptions , 5 );
CustomButtonCaptions[0] := 'Button-1';
CustomButtonCaptions[1] := 'Button-2';
CustomButtonCaptions[2] := 'Button-3';
CustomButtonCaptions[3] := 'Button-4';
CustomButtonCaptions[4] := 'Button-5';
case MessageDlg('MSG',TMsgDlgType.mtConfirmation,mbYesAllNoAllCancel,0,TMsgDlgBtn.mbClose,CustomButtonCaptions) of
0:
begin
end;
end;
end;
// Or
MessageDlg('MSG',TMsgDlgType.mtConfirmation,mbYesAllNoAllCancel,0,TMsgDlgBtn.mbClose, ['Button-1','Button-2','Button-3','Button-4','Button-5']);
This question already has answers here:
What is the default value of 'Result' in Delphi?
(3 answers)
Closed 2 years ago.
Due to a bug in my code I stumbled upon this scenario by accident: -
function Bogus: string;
begin
end;
var
s: string;
begin
s:='Original value';
s:=Bogus;
MessageDlg(s, mtInformation, [mbOK], 0);
end.
Will show a dialog of Original value where the non-result of my function call has no effect on my var; I would have expected it to be empty.
Is this the expected behaviour? Can it be relied upon? Would anyone actually implement this on purpose??!?
I've been using Delphi since 1995 and have never encountered this before.
(Even to this day I still won't select text from the cursor placed by the IDE on a compile error as that used to crash Delphi 1.0!!!)
Behind the scenes, the string return value of Bogus() is passed using a hidden var parameter (not an out parameter, like you are thinking of), as if you had written your code like this instead:
procedure Bogus(var Result: string);
begin
end;
var
s: string;
begin
s:='Original value';
Bogus(s);
MessageDlg(s, mtInformation, [mbOK], 0);
end.
Since Bogus() does not assign anything to its Result, the original string value is preserved (undefined behavior), which is why you are able to see it in the MessageDlg() afterwards.
However, this is a widely known implementation detail that you should NOT rely on. You should always assign something to the Result of a function. The documentation merely says:
If the function exits without assigning a value to Result or the function name, then the function's return value is undefined.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
I have a form class TfrmWelcome and I want to be able to dynamically add
a memo to it when a button is clicked in the main part of the form (frmWelcome.MainPanelSourceEditor).
My unsuccessful attempt at some code is below. I get the error
"undeclared identifier WelcomeMemo". How could I get this to compile and work?
type
WelcomeMemo : TMemo;
end;
implementation
procedure SetHelpWelcome;
begin
WelcomeMemo : TMemo.Create(frmWelcome);
with TMemo(FindComponent('WelcomeMemo')) do
begin
Parent := frmWelcome.MainPanelSourceEditor;
If what you are trying to do is to add a memo to your frmWelcome at runtime, a better (but still not very good) way to do it would be like this:
procedure SetHelpWelcome;
var
WelcomeMemo : TMemo;
begin
WelcomeMemo := TMemo.Create(frmWelcome);
WelcomeMemo.Parent := frmWelcome.MainPanelSourceEditor;
// set any other properties of WelcomeMemo here.
end;
This avoids the with (which you should never use especially if you are a beginner) and the completely avoidable FindComponent to find something you don't need to find in the first place if you capture it by the assignment to the WelcomeMemo local variable.
But that's still a fairly naff way of doing what you want. It would be better to have the WelcomeMemo as a member of your form, and define a method of the form to create and initialise it; you could then call the method from the OnClick handler of the button you want to use to create it. Something like (untested)
TfrmWelcome = Class(TForm)
private
fWelcomeMemo : TMemo;
procedure SetUpWelcomeMemo;
[...]
end;
procedure TfrmWelcome.SetUpWelcomeMemo;
begin
if fWelcomeMemo <> Nil then exit; // to avoid creating it more than once
fWelcomeMemo := TMemo.Create(Self);
fWelcomeMemo.Parent := Self.MainPanelSourceEditor;
// set any other properties of WelcomeMemo here.
end;
Apart from anything else, this avoids the memo's owner being set to the specific TfrmWelcome instance frmWelcome, which is an accident waiting to happen because it may not be the instance you are actually wanting to work with.
But like #J.. said, you really need to look at a beginner's tutorial if you are blundering around using trial and error the way it sounds like you are.
I'm working on an application with hundreds of forms and a corresponding help file with over 2,000 topics. I have one particular form which I am assigning a Context ID, but when I press "F1", the help file opens on its default page (Which means the ID passed to it wasn't found). I need to find out what ID is being passed to the help file to further debug why it's not bringing up its proper topic. How do I find this number?
I discovered the solution as I was writing this question, so I am answering this question Q&A style...
The Application component has an event OnHelp which is triggered when the help file is to be opened. Assign a handler function to this event and then read the Data parameter to get this context ID.
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.OnHelp:= AppHelp;
end;
function TForm1.AppHelp(Command: Word; Data: THelpEventData; var CallHelp: Boolean): Boolean;
begin
ShowMessage(IntToStr(Data));
end;
On a further side note, you can change the CallHelp parameter to False to make your application cancel the call to the help file, just before it opens.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I would like to build a program which lets lyrics of a song run over the screen. Something like this:
http://www.youtube.com/watch?v=kIAiBvD9njM
Can you help me?
Algorithm:
pushes the marker to the right of a line fitting the music
lets a line above the current line disappear
inserts a new line above the current line
What is needed?
lyrics of the song (line per line)
time to text data? (when does a line start/end)
Some approaches would help me a lot. Pseudo-code or even Delphi code of any part would be fantastic.
If you're interested in karaoke code in pascal, make sure to take a look at UltraStar Deluxe.
It's a super slick and very popular karaoke application. The project is active and it's open source. It can be compiled to various platforms with FPC. You can compile it from both Delphi and Lazarus.. nice.
http://ultrastardx.sourceforge.net/
My neighbours thought that my dog was their worst nightmare until I found this program.
See it in action: po-po-po-pokerface po-po-pokerface.. mum mum mum mah! :)
let's assume you have a text file with the text to be shown and the annotated time of when to hightlight it (kind of a subtitles file, for example the standard proposal w3c timed text (http://www.w3.org/AudioVideo/TT/) or the SUB - Movie subtitle file format in use by several media players.
Your program must first read and parse the text file, and decode the annotated time. Insert it in a stringlist called Subtitles which items would also keep objects similar to this one
type tSubtitle = class
num : integer;
prevTime, fromTime : tdatetime;
toTime, nextTime: tdatetime;
text: string;
end;
You might want to extend the object to hold some highlighting attributes as well.
Then you just need to display those objects synchronized with a timer.
procedure TForm1.Timer1Timer(Sender: TObject);
var rt : TDateTime;
done:boolean;
si,st,sb:integer;
s:string;
begin
rt:=now-startTime;
st:=0;
sb:=subtitles.Count; // binary search the subtitle for the current time
repeat
si:=(st+sb) div 2;
s:=TSubtitle(subtitles.Objects[si-1]);
done:= ((t>=s.prevTime) and (t<=s.nextTime));
if not done then
begin
if t>s.prevTime then st:=si
else if t<s.nextTime then sb:=si;
if st=sb then done:=true;
end;
until done;
// do what you want with s
end;
Another option would be to create your own markup that you parse for that contains both the text and the delay timing. While a timer would work, the problem is that its not going to be accurate enough over time to give you reliable results since its fired based on messaging. Instead, I would perform triggers based on how far from the beginning of the music file you want the event to occur. This also allows the system to catch-up if some other app blocking process gets in the way and should help keep things in sync.
Something as simple as:
00:00:15;LYRIC;This is lyric line 1
00:00:18;FADEOUT
you then can parse this into list of appropriate objects which take the appropriate actions.
You should create a new class based on TGraphicControl/TCustomControl(anything with a canvas) and add a string property, now you have to create a timer as a private variable with it's interval value published through your class, something like so
...
type TLyricViewer = class(TGraphicControl)
private
FTimer : TTimer;
FLyric : string;
FBitmap : TBitmap;// offset bitmap on which you draw
// some more variables to store paint information
private
procedure OnNextWord(Sender: TObject);// assign this to FTimer.OnTimer event
public
constructor Create(AOwner: TComponent);
destructor Destroy; override;
public
procedure StartLyric;
procedure StopLyric;
procedure Paint; override;
published
property WordInterval : Integer|Cardinal
read GetWordInterval write SetWordInterval;
end;
...
procedure TLyricViewer.Paint;
begin
// here is where the magic happends
end;
constructor TLyricViewer.Create(AOwner: TComponent);
begin
// create timer, bitmap and set default properties
end;
destructor TLyricViewer.Destroy;
begin
// free and nil the timer and bitmap
inherited Destroy;
end;
The rest is up to you, after all your the one getting paid, work for it :)