[DUG] D2006 menus and buttons

Stacey Verner stacey at cjntech.co.nz
Wed May 17 07:54:13 NZST 2006


We use Toolbar 2000 from Jordan Russel
(http://www.jrsoftware.org/tb2k.php) with the TBX addon from Alex
Denisov  (http://www.g32.org/tbx/index.html).
 
Toolbar 200 gives you clean Windows 2000 style menus, but with the TBX
addon you get nice theming capabilities. Downoad the TBX demo to see
what it can do.
 
http://www.g32.org/files/tbx/tbx-demo-2_2-alpha.zip
 
 
Note: TBX makes some modificaitons to Toolbar 2000 (make methods virtual
etc.) so you need to use Toolbar 2000 2.1.6 for the latest TBX which is
2.2 alpha (don't worry, its not alpha quality, its solid as a rock.).
 
Stacey
 
________________________________

From: delphi-bounces at ns3.123.co.nz [mailto:delphi-bounces at ns3.123.co.nz]
On Behalf Of John Bird
Sent: Tuesday, 16 May 2006 16:47
To: 'NZ Borland Developers Group - Delphi List'
Subject: [DUG] D2006 menus and buttons


I am using the XP Themes component, which is great.  This gives rounded
buttons with a faint orange mouse over effect - its a standard XP
feature.  However I have also see other effects which I like which I
haven't yet got:
 
I am wondering about the buttons and menu items widely available in
Windows XP, for instance in Outlook 2002 and in the Delphi 2006 IDE -
buttons that are invisible or flat with a mouse over blue highlight and
similar blue highlights on the main menu items current item.
 
Are these available in D2006?
 
(Up to now I have been using my own modified tjbclColorbutton which has
full control over foreground and background colour, and also mouse-over
foreground and background colour and optional bolding of text with mouse
over.....looks ok if I do say so myself.   Available as giveaway if
anyone interested).  The XP buttons I have seen in the IDE and Outlook I
do prefer though - as they add border effects.

John Bird

email

 john at jbcl.co.nz

 johnkbird at paradise.net.nz

Expert in Beyond Software

Ph land (03)384-4527  mobile (027)484-4528

92 Soleares Ave, Mt Pleasant

Christchurch

Web www.jbcl.co.nz <http://www.jbcl.co.nz/> 

 

 

	-----Original Message-----
	From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Stefan Mueller
	Sent: Tuesday, 16 May 2006 2:15 p.m.
	To: 'NZ Borland Developers Group - Delphi List'
	Subject: RE: [DUG] Discussion & Donation
	
	

	Nhmm .. surprising. The "for" loop should evaluate lower/upper
bounds only once .. while the "while" statement always has to evaluate
the expression for each iteration. This doesn't make sense ;-/

	 

	Anyway .. a bit busy with other things rather than exploring the
"why". A russian guy cracked my licenseprotection (not that hard) last
weekend and is offering the crack to my customers on my own support
forums (
http://www.orcl-toolbox.com/forum_new2/forum_posts.asp?TID=341&PN=1 ).
Still trying to get on the good site with him(talking on email) but
patience is starting to run out.

	 

	Kind Regards,
	Stefan Mueller 
	
	

	
________________________________


	From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Todd Martin
	Sent: Tuesday, 16 May 2006 1:42 p.m.
	To: NZ Borland Developers Group - Delphi List
	Subject: Re: [DUG] Discussion & Donation

	 

	Kylie 

	I re-ran your timed loop with a minor change to IndexOfTodd(). I
removed the CurrentName variable.

	 

	function IndexOf_todd(AName : String; AStringArray : array of
string) : Integer;

	var

	  NameIndex, FoundIndex : Integer;

	//  CurrentName : String;

	begin

	  NameIndex := 0;

	  FoundIndex := 0;

	  while (NameIndex < length(AStringArray)) and (FoundIndex = 0)
do

	  begin

	//    CurrentName := AStringArray[NameIndex];

	//    if SameText(AName,CurrentName) then

	   if SameText(AName,AStringArray[NameIndex]) then

	    begin

	      FoundIndex := NameIndex;

	    end;

	 

	 

	 

	    inc(NameIndex);

	  end;

	 

	 

	 

	  Result := FoundIndex;

	end;

	 

	Now compare the results

	 

	Button 1

	Test Todd 1101ms

	Test Stef 1142ms

	Test StringList 921ms

	 

	Button 2

	Test Todd 2553ms

	Test Stef 2534ms

	Test StringList 230ms

	 

	So Stefan's break statement is slower in the first case and adds
a negligible time saving in the second case. Personally I hate break and
continue statements, so I never use them.

	The string list still looks impressive though.

	 

	But look what happens when the list size is increased from 200
to 2000 items in button 2 click.

	 

	--using TStringList

	 

	Test Todd 23023ms

	Test Stef 23554ms

	Test StringList 361ms

	 

	--using THashedStringList

	 

	Test Todd 22703ms

	Test Stef 22632ms

	Test StringList 110ms

	 

	----- Original Message ----- 

		From: Kyley Harris <mailto:kyley at harrissoftware.com>  

		To: NZ Borland Developers Group - Delphi List
<mailto:delphi at ns3.123.co.nz>  

		Sent: Monday, May 15, 2006 2:44 PM

		Subject: RE: [DUG] Discussion & Donation

		 

		Hmm your comment that doing it that way intrigued me
enough to test it. Seeing as I like to make things quicker. You were
right in certain cases. 

		 

		Given the following code.

		 

		TEST 1. High iterations on a small set 5 elements

		 

		Todds loop took 150ms, 

		Stefans loop took 100ms

		StringList took 108ms

		 

		But given test 2 high itereations on a larger list. 200
elements Which is more likely when dealing with messaging

		 

		Todds took 4125 ms

		Stefans took 2641ms

		Stringlist took 297ms

		 

		So I think I will stick with sorted stringlists. Because
then you can also attach a data object to it for retrieving the value,
rather than relying on the index position being the value, which I find
to be a very bad idea

		 

		unit Unit1;

		 

		interface

		 

		uses

		  Windows, Messages, SysUtils, Variants, Classes,
Graphics, Controls, Forms,

		  Dialogs, StdCtrls;

		 

		type

		  TForm1 = class(TForm)

		    Button1: TButton;

		    Button2: TButton;

		    procedure Button1Click(Sender: TObject);

		    procedure Button2Click(Sender: TObject);

		  private

		    { Private declarations }

		  public

		    { Public declarations }

		  end;

		 

		var

		  Form1: TForm1;

		 

		implementation

		 

		uses

		  uHssStopWatch;

		 

		{$R *.dfm}

		 

		function IndexOf_stef(AName : String; AStringArray :
array of string) : Integer;

		var

		  i : Integer;

		begin

		 

		 

		 

		  result := -1;

		 

		 

		 

		  for i := 0 to high(AStringArray) do

		  begin

		 

		    if SameText(AName, AStringArray[i]) then

		    begin

		 

		       result := i;

		 

		       break;

		 

		    end;

		 

		  end;

		 

		end;

		 

		 

		function IndexOf_todd(AName : String; AStringArray :
array of string) : Integer;

		var

		  NameIndex, FoundIndex : Integer;

		  CurrentName : String;

		begin

		  NameIndex := 0;

		  FoundIndex := 0;

		  while (NameIndex < length(AStringArray)) and
(FoundIndex = 0) do

		  begin

		    CurrentName := AStringArray[NameIndex];

		    if SameText(AName,CurrentName) then

		    begin

		      FoundIndex := NameIndex;

		    end;

		 

		 

		 

		    inc(NameIndex);

		  end;

		 

		 

		 

		  Result := FoundIndex;

		end;

		 

		 

		 

		 

		 

		 

		procedure TForm1.Button1Click(Sender: TObject);

		var

		  s:TStringList;

		  data:TStringList;

		  a1:array of string;

		  i:integer;

		begin

		  initialize(a1);

		  s := TStringList.Create;

		  setlength(a1,5);

		  a1[0] := 'test';

		  a1[1] := 'testa';

		  a1[2] := 'testb';

		  a1[3] := 'testc';

		  a1[4] := 'testd';

		 

		  s.CaseSensitive := false;

		  s.Sorted := true;

		 

		  s.add(a1[0]);

		  s.add(a1[1]);

		  s.add(a1[2]);

		  s.add(a1[3]);

		  s.add(a1[4]);

		 

		  with THssStopWatch.Start('Test Todd') do

		  try

		    for i := 0 to 1000000 do

		      case i mod 5 of

		        0: Assert(SameText('test',a1[
IndexOf_todd('TEST',a1)]));

		        1:
Assert(SameText('testa',a1[IndexOf_todd('TESTa',a1)]));

		        2:
Assert(SameText('testb',a1[IndexOf_todd('TESTb',a1)]));

		        3:
Assert(SameText('testc',a1[IndexOf_todd('TESTc',a1)]));

		        4:
Assert(SameText('testd',a1[IndexOf_todd('TESTd',a1)]));

		      end;

		  finally

		    Free;

		  end;

		 

		  with THssStopWatch.Start('Test Stef') do

		  try

		    for i := 0 to 1000000 do

		      case i mod 5 of

		        0: Assert(SameText('test',a1[
IndexOf_stef('TEST',a1)]));

		        1:
Assert(SameText('testa',a1[IndexOf_stef('TESTa',a1)]));

		        2:
Assert(SameText('testb',a1[IndexOf_stef('TESTb',a1)]));

		        3:
Assert(SameText('testc',a1[IndexOf_stef('TESTc',a1)]));

		        4:
Assert(SameText('testd',a1[IndexOf_stef('TESTd',a1)]));

		      end;

		  finally

		    Free;

		  end;

		 

		 

		  with THssStopWatch.Start('Test StringList') do

		  try

		    for i := 0 to 1000000 do

		      case i mod 5 of

		        0:
Assert(SameText('test',s[s.IndexOf('test')]));

		        1:
Assert(SameText('testa',s[s.IndexOf('testa')]));

		        2:
Assert(SameText('testb',s[s.IndexOf('testb')]));

		        3:
Assert(SameText('testc',s[s.IndexOf('testc')]));

		        4:
Assert(SameText('testd',s[s.IndexOf('testd')]));

		      end;

		  finally

		    Free;

		  end;

		 

		 

		end;

		 

		procedure TForm1.Button2Click(Sender: TObject);

		var

		  s:TStringList;

		  data:TStringList;

		  a1:array of string;

		  i,x:integer;

		begin

		  initialize(a1);

		  s := TStringList.Create;

		  setlength(a1,200);

		  s.CaseSensitive := false;

		  s.Sorted := true;

		  for i := 0 to 199 do

		  begin

		    a1[i] := 'test'+inttostr(i);

		    s.add(a1[i]);

		  end;

		 

		 

		  with THssStopWatch.Start('Test Todd') do

		  try

		    for i := 0 to 100000 do

		      x := IndexOf_Todd(a1[i mod 200],a1);

		  finally

		    Free;

		  end;

		 

		  with THssStopWatch.Start('Test Stef') do

		  try

		    for i := 0 to 100000 do

		      x := IndexOf_Stef(a1[i mod 200],a1);

		  finally

		    Free;

		  end;

		 

		 

		  with THssStopWatch.Start('Test StringList') do

		  try

		    for i := 0 to 100000 do

		      x := s.IndexOf(a1[i mod 200]);

		  finally

		    Free;

		  end;

		 

		 

		end;

		 

		end..

		 

		 

		 

		
________________________________


		From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Stefan Mueller
		Sent: Monday, 15 May 2006 1:36 p.m.
		To: 'NZ Borland Developers Group - Delphi List'
		Subject: RE: [DUG] Discussion & Donation

		 

		Hi Todd,

		 

		Here is how I usually write such scanning loops. Should
be a bit faster than yours and less confusing (less juggling around with
variables) ... and less error prone (raises exceptions if AName can't be
found):

		 

		function IndexOf(AName : String; AStringArray : array of
string) : Integer;
		var
		  i : Integer;
		begin

		 

		  result := -1;

		 

		  for i := 0 to high(AStringArray) do
		  begin

		    if SameText(AName, AStringArray[i]) then
		    begin

		       result := i;

		       break;

		    end;

		  end;

		 

		  if result := -1 then 

		    raise exception.create('String '+ AName +' not found
in array!');

		end; 

		 

		 

		Regards,
		Stefan

		
________________________________


		From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Todd Martin
		Sent: Monday, 15 May 2006 12:55 p.m.
		To: NZ Borland Developers Group - Delphi List
		Subject: Re: [DUG] Discussion & Donation

		 

		3) Okay. Good point. I didn't pick up on this, as you
hadn't marked the procedures as virtual, but I guess that can always be
added later without breaking existing code.

		 

		4) Have you considered RegisterIntegerConsts() for
converting message integers to text?

		Or failing that I often use something like this

		 

		type

		TMessageType = (mtUnknown,mtChange,mtSave,mtLoad)

		 

		const

		   MessageTypeToText : array[TMessageType ] of string =
('Unknown','Change','Save','Load');  

		 

		That way, whenever I add an enumeration, I have to also
add an equivalent string description.

		To convert the text back to an enumerated type, I just
use a standard procedure to find the index of the text in the array. By
making the first enumeration unknown/default I always get a meaningful
result when casting the integer back to a type.

		 

		Type :=
TMessageType(IndexOf(MessageText,MessageTypeToText);

		 

		function IndexOf(AName : String; AStringArray : array of
string) : Integer;
		var
		  NameIndex, FoundIndex : Integer;
		  CurrentName : String;
		begin
		  NameIndex := 0;
		  FoundIndex := 0;
		  while (NameIndex < length(AStringArray)) and
(FoundIndex = 0) do
		  begin
		    CurrentName := AStringArray[NameIndex];
		    if SameText(AName,CurrentName) then
		    begin
		      FoundIndex := NameIndex;
		    end;

		 

		    inc(NameIndex);
		  end;

		 

		  Result := FoundIndex;
		end;

		 

		 

		However, I see your point about streaming numbers to XML

		     

		Todd.

		 

			----- Original Message ----- 

			From: Kyley Harris
<mailto:kyley at harrissoftware.com>  

			To: NZ Borland Developers Group - Delphi List
<mailto:delphi at ns3.123.co.nz>  

			Sent: Monday, May 15, 2006 11:05 AM

			Subject: RE: [DUG] Discussion & Donation

			 

			Thank.

			Point 1/ Fair enough

			Point 2/ correct . I had written that bit early
on before I realized I could break it down correctly with TMethdo.code
and TMethod.Data and I haven't fully refactored it yet. I've removed it
now.

			Point 3/ Because you cant do anything with a
bunch of procedures. Ie it is not portable. Class functions can be
overridden. You don't need to use TNotifier.AddListener,

			 You could have for example add a meta class and
a classvariable to the global, Var NotifierClass:TNotiferClass =
TNotifer. I then use this for accessing the functions and down the track
in some cases or applications you may wish to modify how it works,
without effecting All the other apps by simply changing the class
pointer. static procedures do not lend themselves to upgrading software
without breaking old software and this is one of the most powerful uses
of static classes as libraries that many people in Delphi don't take
advantage of. 

			Point 4. I use constants, so I don't have typos
in message names (except for demos). And I use SameText, so they are not
case-sensitve. I do this purely because I prefer debugging meaningful
strings rather than integers. It also makes It easier when dealing with
storage of types etc in human readable XML. So that's why I do it. Many
books use integers. I just don't like it. Also. A lot of my
infrastructor permits streaming of the entire message, and the data
object over TCP as part of the notification. I prefer unknown apps
receiving this to get a meaningful 'CLIENT_OBJECT_UPDATED' rather than 3
;)

			 

			The StringList is indexed, and its very fast,
I've tested with thousands of binds with no real time effect. The work
done by listeners is the time issue. Not the lookup.

			 

			 

			I used to have an observer and observed class
system too. And it was really annoying when you had to make changes. I
even wrote a plugin for the IDE to convert an object into a listener etc
by inserting all the code for me. So that took the hassle out. But I
prefer the decoupled method. 

			 

			Thanks for the feedback. 

			 

			
________________________________


			From: delphi-bounces at ns3.123.co.nz
[mailto:delphi-bounces at ns3.123.co.nz] On Behalf Of Todd Martin
			Sent: Monday, 15 May 2006 10:38 a.m.
			To: NZ Borland Developers Group - Delphi List
			Subject: Re: [DUG] Discussion & Donation

			 

			Well you said you wanted feedback, so I hope
this isn't too picky.

			 

			1) I find the inconsistent use of variable
prefixes a bit annoying. I tend to stick with

			F- class field

			A - method parameter

			P - pointer

			and no prefixes for class properties or local
variables.

			 

			2) TListenerItem.FListener is redundant, since
the information it contains is already held in TListenerItem.FBindMethod

			It could have been implemented as a function,
but I don't see why it is needed.

			As usual, redundancy leads to complication
elsewhere

			 

			eg.  if
(MethodEquals(TMethod(Items[i].FBindMethod) ,TMethod(ABinder))) and
(Items[i].FListener = AListener) then

			 

			the second condition is automatically satisfied
by the first condition.

			 

			3) Why define a TNotifier class when all its
methods are static? They could be defined as simple procedures/functions
in the unit.

			 

			4) I'm not keen on the idea of passing a string
parameter to identify the message type, for the simple reason that
spelling mistakes can arise and you're never really sure at compile time
what "messages" are being broadcast/handled. This problem can of course
be ameliorated through the use of constants, but I prefer the use of an
enumerated type.

			 

			Having said all that. I like the loose coupling
of your solution. I have implemented the observer pattern previously
with the lists maintained internally by the objects involved, so
removing the need to inherit from a particular class is a great
improvement. I hadn't considered that option before. Of course you do
pay a miniscule performance penalty in having to find the correct list
for the object. Perhaps the use of THashedstringlist could mitigate this
further.

			 

			Todd.

			 

			 

				----- Original Message ----- 

				From: Kyley Harris
<mailto:kyley at harrissoftware.com>  

				To: NZ Borland Developers Group - Delphi
List <mailto:delphi at ns3.123.co.nz>  

				Sent: Wednesday, May 10, 2006 3:36 PM

				Subject: [DUG] Discussion & Donation

				 

				In an attempt to create some more
interesting discussion, other than talking about the crud IDE issues, I
am donating some useful code for discussion, rather than for help. Feel
free to keep/use the code if you want to. Be mean if you feel the need
(but if your personal then watch out ;) Hopefully this may introduce a
discussion where people learn something, or are able to contribute some
interesting ideas/information about the topic (which is object
notification and observation).

				 

				In the attached zip is a sample
application showing a basic, but horrible use of the class in
uNotifier.pas. TNotifer is a class I wrote a long while back which
loosely follows an observer pattern for allowing one-to-many
observations between an object, and a bunch of interested object. 

				 

				The main difference this class has
between many observer patterns is that you do not have to modify
existing classes, or sub-class anything, or instantiate anything in your
existing objects. The TNotifier handles all the bindings, you just need
to implement one listening event on each listening object.

				 

				The sample app is a corny app that makes
child forms (button click) update a memo based on changes to the memo on
the main form. God help me if I ever did anything as potentially
unthread safe as that in a real app.

				 

				Fun fun fun.... (I hope the app
compiles. You never know with these tricky little sample apps)

				
________________________________


	
_______________________________________________
				Delphi mailing list
				Delphi at ns3.123.co.nz
	
http://ns3.123.co.nz/mailman/listinfo/delphi

				
________________________________


				Internal Virus Database is out-of-date.
				Checked by AVG Free Edition.
				Version: 7.1.385 / Virus Database:
268.5.1/327 - Release Date: 28/04/2006

			
________________________________


			_______________________________________________
			Delphi mailing list
			Delphi at ns3.123.co.nz
			http://ns3.123.co.nz/mailman/listinfo/delphi

			
________________________________


			Internal Virus Database is out-of-date.
			Checked by AVG Free Edition.
			Version: 7.1.385 / Virus Database: 268.5.1/327 -
Release Date: 28/04/2006

		
________________________________


		_______________________________________________
		Delphi mailing list
		Delphi at ns3.123.co.nz
		http://ns3.123.co.nz/mailman/listinfo/delphi

		
________________________________


		Internal Virus Database is out-of-date.
		Checked by AVG Free Edition.
		Version: 7.1.385 / Virus Database: 268.5.1/327 - Release
Date: 28/04/2006

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://ns3.123.co.nz/pipermail/delphi/attachments/20060517/f189854d/attachment-0001.html


More information about the Delphi mailing list