Agile methods teach that software should be released as early as possible and updated as often as possible. This allows users to work with the software so they can provide meaningful feedback that guides future development. I recently tried this with a plugin that I’m working on and it was quite effective. The following six responses provided me with several ideas for new features (and one bug) that I would not have come up with on my own.
You could extend this to include replace, find next etc, and then it would become full regex support within N++ (which is wanted anyway, and will probably happen sometime soon, so not sure how much effort should be put into that).
I like the idea though, I use something similar to test regex’s, doing it in N++ is even better. Not sure what you get access to, but it’d be really nice to show the replacement groups – I often need to see not only that a particular regex matches, but also what \1 and \2 (and maybe \17!!) relate to.
Good job – I’ll try to add it to the plugin manager as soon as I can.
Yea, nice one! And I wanna second davegb3 regarding the replacement groups.. that would be useful to see them. Also can’t you just name the buttons "&Match" and "&Clear" for having shortcuts ? Or wouldn’t they work in your dialog anyway ? What’s the regex engine behind ? The one of scintilla ?
Your work is really appreciating one.
I found that Notepad++ is going "Out of Memory" when "*" or "?" is applied.
But it works fine in case of "+".
A further suggestion: Make the Matches in the Details-Area clickable (go to them)
And also scroll-bars, that appears in the dialog if necessary? 🙂
Is there a chance for the ANSI version?. I use my own private plugin AutomationPHP only the ANSI version because I only have Delphi compiler in this version. Therefore, I would be happy to try your plugin but the ANSI version. regards AK
Looks very nice to me now! Regarding N++ eating shortcuts, I think you could simply catch WM_KEYDOWN in your dialog, but your CTRL shortcuts sound even better imho. Also maybe add a helping link or something…
I have been a professional software developer for close to five years now and have been able to code for close to ten. In all this time I haven’t bothered to contribute a single line of code to any open source project. That changed Monday December 21st 2009 at 03:43:29 UTC when I committed the code for a Notepad++ plugin that I wrote. The plugin is called RegEx Helper and it provides you with a convenient way to test regular expression against open documents. I created a project for the plugin on sourceforge. Lastly, if you have not tried Notepad++ go download a copy and try it out. I use it whenever I work with code outside of an IDE.
This post will explain how to display messages using the TBalloonHint component that was added in Delphi 2009. This approach is much less intrusive than traditional modal dialog boxes that steal focus and require user interaction. Balloon hints simply display a message then disappear after a short period of time.
TBalloonHint is easy to use but there are a few subtleties that I will go over. Controls, like TButton, have a Hint and a ShowHint property which can be used to popup text near the cursor when it is over a control; TBalloonHint is a just a prettier and more flexible hint. Hints are automatically displayed when the cursor is over a control so it is hard to find documentation that explains how to display a balloon hint programmatically. That is what I will do here.
Create and Configure
TBalloonHint is found in the ‘Additional’ section of the tool panel so it can be added to a form at design time. Since the balloon hint is not being linked to any controls, I create and configure it in the OnCreate method instead. The HideAfter property allows you to specify, in ms, how long the balloon will be shown before it is automatically hidden. The Delay property allows you to specify, in ms, how long the hint should wait before appearing; the default this value is 500.
procedure TForm1.FormCreate(Sender: TObject); begin FBalloonHint := TBalloonHint.Create(Self); FBalloonHint.HideAfter := 5000; FBalloonHint.Delay := 0; end;
Show and Position
Before showing a balloon hint, the Title and Description properties can be used to specify its contents. Once the contents are set, the ShowHint method can be used to display the hint. Since the hint is not attached to a specific control, we need to pass some position information, using screen coordinates, to ShowHint.
The orientation of the balloon depends on its screen location. If the balloon is displayed in the bottom-half of the screen it looks like the image at the beginning and if it is displayed in the top-half of the screen it will look like the image below. Thus telling a balloon to pop-up at the TopLeft or BottomRight corner of a control does not work well because one of the balloon orientations will cause it to cover the control .This problem can be solved by passing a rectangle to ShowHint instead of a point. When a rectangle is passed to ShowHint it will make sure that the region inside the rectangle remains visible when the hint is shown.
procedure TForm1.Button1Click(Sender: TObject); var R: TRect; begin FBalloonHint.Description := 'You pressed ' + Button1.Caption; R := Button1.BoundsRect; R.TopLeft := ClientToScreen(R.TopLeft); R.BottomRight := ClientToScreen(R.BottomRight); FBalloonHint.ShowHint(R); end;
When to Hide
Balloon hints are created in their own window so it is important to hide them when your application is moved, minimized, resized or covered by another application. The image below shows what happens when you move the form without hiding the hint.You only have to process two Windows messages (WM_ACTIVATEAPP and WM_WINDOWPOSCHANGED) to cover all of these situations. To process these messages, define the following message handling functions in the protected section of the form.
protected procedure WMWindowPosChanged(var AMessage:TMessage); message WM_WINDOWPOSCHANGED; procedure WMActivateApp(var AMessage: TMessage); message WM_ACTIVATEAPP;
When you implement the functions make sure to call inherited to run the form’s message handlers. It is possible for these message to come before the TBalloonHint object has been created so do not forget to check if it has been assigned.
procedure TForm1.WMActivateApp(var AMessage: TMessage); begin if Assigned(FBalloonHint) then FBalloonHint.HideHint; inherited; end; procedure TForm1.WMWindowPosChanged(var AMessage: TMessage); begin if Assigned(FBalloonHint) then FBalloonHint.HideHint; inherited; end;
I recently bought an HP laptop that came loaded with its fair share of bloatware. Over the years, I have managed to shake my OCD dreams of a ‘clean’ system so when I brought my shiny new laptop home I just started using it. I started installing all my development tools, some handy apps (see clipx, notepad++), ect…
Once my system was setup the way I like it, I sat down to write some code. I fired up Delphi, typed in a new function definition, then used one of my favourite shortcuts, Shift+Ctrl+C, to generate corresponding code in the implementation section. Hmmm… this is taking longer than usual… HP Health Check… WTF? Maybe it’s just a coincidence? Shift+Ctrl+C …. HP Health Check … Dammit!
That right, someone at HP thought that HP Health Check was important enough to warrant the use of a system wide hot key that supersedes any application hot keys (accelerator keys). News Flash… it isn’t.
In their defence, they probably have a semi-legitimate reason for doings this (make life easier for tech support?) and they did pick a lesser used combination. Unfortunately, this seemingly harmless convenience actually breaks any application which uses that accelerator key combination. A quick search for Shift+Ctrl+C revealed several post requesting help because Shift+Ctrl+C is not working in their version of:
- The Sims
Before you plan on adding any global hooks to your application please consider the following: It is unlikely that a user intends to do anything with your application unless they are actually using it.
Are you the victim of Accelerator Key theft?
To get rid of unwanted system wide hotkeys you can use this approach:
- delete the executable that is launched by the hotkey
- use the hotkey sequence
- a window will pop up stating that there is a problem with the shortcut
- choose to delete the shortcut
- restore the executable
Threads are typically viewed as a very powerful but dangerous programming concept. They are powerful concept because they allow code to be broken up into several executable chunks that can be scheduled by the operating system and run concurrently on multi-core/multi-processor systems. Unfortunately, by doing this, your program become susceptible to a whole new breed of problems that are hard to reproduce and debug. Because of this, I feel it is necessary (if only to prove to myself) to defend my usage of threads in a project that I am working on.
I work for a small company that makes custom ultrasonic equipment to monitor manufacturing processes and assess the quality of the manufactured goods. On top of your normal CRUD application needs, this also involves communicating with manufacturing equipment, communication with ultrasonic hardware, and running complex signal processing algorithms in a time sensitive environment (order of ms) . One of my main tasks is/was to combine all the pieces of this puzzle into a monitoring application that can run on a dedicated Windows based computer that sits on the factory floor.
A typical manufacturing process will have a robot capable of making a few similar but different products. It goes something like this:
- Robot sits idle waiting for materials to be loaded
- Materials for one of the products are loaded
- Robot is told to build the product
- Robot moves to a position
- Robot performs an action
- Repeats steps 4 and 5 until product is built
At this stage in our product’s development, the manufacturing environment is asynchronous meaning we have no control over its processes; things happen when they happen. However, our ultrasonic equipment must collect data in real-time while actions are being performed (i.e. during step 5). So if the software cannot activate the ultrasonic equipment at the right time, maybe it busy with a length signal processing algorithm, the data is lost forever.
I separated the application’s functionality into two threads. The acquisition thread has complete control of the hardware for collecting ultrasonic data and the hardware for communicating with the manufacturing equipment. The main thread is responsible for everything else which includes user interaction, CRUD operations, and processing the ultrasonic data. The acquisition thread sends the main thread messages using a thread-safe message queue.
Why it Works
The acquisition thread is assigned the highest priority possible when it is created and its main routine looks something like this.
while not Terminated do waitForStartActionSignal; // sleep acquireUltrasonicData; sendUltrasonicDataToMainThread; end;
The acquisition thread spends the vast majority of its time waiting for signals from the manufacturing environment, so even though it has the highest priority possible, it does not starve the main thread or any other system processes for that matter. However, as soon a signal is received from the manufacturing equipment, the acquisition thread instantly springs to life an runs uninterrupted until it next sleep. Thus, the most important task, collecting ultrasonic data, will be completed on time no matter what the main thread or other system processes are doing. Since, the acquisition thread sends information to the main thread through a queue, it can easily handle short periods of time where data is collected faster than it can be processed.
It has been a little over a year and a few hundred-thousand products since our first installation and I have to say that the approach described here has worked very well. However, I am not immune to the problems that plague developers who try to harness the power of threads. For the first month or so, the software would arbitrarily freeze and I could not figure out why. Eventually, with a little help, I was able to find my one and only deadlock.
Exceptions are a useful programming concept because they1:
- separate error handling code from regular logic
- propagate errors up the stack automatically
- provide a convenient way to group and handle different types of errors
I have been thinking about exceptions a lot lately and I would have to agree with the commonly help opinion that exceptions are a useful programming concept. However, for all the good they provide, I do think that there is one major disadvantage and that it does not get much attention.
it is hard to use exceptions and exception handling correctly
By this I mean it is hard to know when an exception is the right way to communicate an error condition. I have read several blogs recently and it seems that most people subscribe to one of the following philosophies:
- exceptions should be used whenever a function cannot perform its job
- exceptions should only be used for unpredictable events
To help clarify the difference between the two trains of thought, lets consider a function that converts an ascii string to an integer. These two groups disagree on what should happed when the function is passed the string ‘abc’. People in group 1 want an exception while people in group 2 do not. However, both groups would agree that an exception should be thrown if the function is passed a null pointer or something along those lines.
Both camps have some good arguments explaining why their approach is better [here are a few 1, 2, 3, 4]. Since I do most of my coding in Delphi, I decided to look to the choices made by its designers for guidance. Here’s what I found2:
//throws an exception if S cannot be converted to an integer function StrToInt(S: String): integer; // returns false if S cannot be converted to an integer function TryStrToInt(S: String; out Value: Integer): boolean; //returns Default if S cannot be converted to an integer function StrToIntDef(S: String; Default: Integer): integer;
Hmmm… why didn’t they pick a side? I believe that the designers realized that there is no best approach because both approaches can be useful depending on the situation. So, if you are writing a really generic function, like one that converts a string to an integer, it is probably best to provide both options but if your writing a really specific function, like one that reads an applications settings, just do what is best for the code that will be calling your functions.
- dig a little deeper and you will discovered that these functions are just wrappers for the Val function which does not use exceptions
Once and a while I run into code like this.
if SomeObject.SomeCounter() > 0 then // do something else // do something else
Under normal circumstances the exact value of SomeCounter does not matter but when your are trying to track down a nasty bug, the exact value could be really useful. If your lucky, you can just mouse over SomeCounter or add a watch, and the debugger will tell you the value. However, there are many cases where this does not work so you have to stop debugging and change the code to look something like this.
N := SomeObject.SomeCounter(); if N > 0 then // do something else // do something else
Here is a trick that saves me a lot of time. The return value of a function is stored in the EAX register. So to get value of SomeCounter, you can just at the watch Integer(EAX) and look at it immediately after the function returns. What if the return type is a string or some sort of complex object? The EAX register will hold a pointer to the object which can be casted and examined (i.e. PString(EAX)^ or TMyObject(EAX)). So far the only type I haven’t figured out are floating point values. Please post a comment if you know how to do this.