Added history combo to 'Title' field of transform, print/preview dialogs

Added drag'n'drop support for re-ordering tabs

Added optional confirmation dialog to time tracking

Added close buttons to tasklist tabs

Added blank line to single-selection droplists

And much, much more.

Introduction

You know how it is - you start work on one project and halfway through, you find one or two side-projects crop up that have to be solved before you can continue on the original project.

This is one such project with the added twist that it too started its life as a side-project. Here's what happened:

<Cue wavy screen effect>

I can only imagine that the planets must have been in (mis-)alignment or something, because at one point a few months ago, I was suddenly fielding emails on four or five separate articles I had previously submitted to CodeProject, some asking for features and others for bug fixes.

Foolishly or otherwise, I largely agreed with all the points raised, and subsequently found myself with fourteen or fifteen separate issues to resolve.

The situation was also made worse because I was trying to use CodeProject to keep track of all the things I had agreed to do, meaning that I had to continuously trawl the comments section of each article to remind myself of what I was supposed to be working on.

It even got to the stage where I was worrying that I'd fail to deliver on something - silly I know, but there you are!

Keeping a list on paper was a definite step in the right direction, but since I do all my coding on the same machine, it seemed somewhat inelegant, and anyway, we all know what happens to crucial bits of paper left lying around on desks and such.

The next step was to hunt around on the web for a tool to meet the following requirements:

Simple interface

Support for hierarchical data

Numbered items/subitems

Open file format

Freeware

Simple, huh! not!

I will admit that I did not spend weeks searching, but I am still surprised at the general lack of software matching my needs.

On reflection, I think that the reason may be simple: people are so used to commercial software being 'feature-rich' that when they come to design software themselves, they (not unreasonably) think they too need to cram as much in as possible, often leading to software where a lot of essential functionality is hidden away in the menu bar.

So, surprise, surprise, I decided to write something myself.

However, it's fair to say that I did not originally intend to post it on CodeProject and am only really doing so because I had a heap of fun solving some very interesting problems and these are what I think make it worth it.

Using the Software

There's really very little I need to say here since every feature/function is explicitly visible in the interface.

Nevertheless, the following list of basic capabilities and omissions may go someway to answering any questions that arise:

Files are stored in XML format with .xml file extension.

Trying to load a non-tasklist file will generally fail (unless you read the code to see how to circumvent it).

The number of items/subitems is limited only by memory (although performance may be the deciding factor before you exhaust memory).

Marking a parent item as 'done' will also gray-out child items, but they are not disabled or automatically marked as 'done'.

An ellipsis (...) indicates that an item has sub-items.

All items can be expanded or collapsed (by double-clicking).

Top-level items and sub-items are created using different toolbar buttons.

There are task-specific context-menus.

The previously open tasklists are re-opened on startup.

The tasklist is automatically saved when closing the software or minimizing it to the system tray.

The priority of a task is shown as a grayscale box to the left of the item.

Points of Interest

Here's where we come to the side-projects I was talking about, the first two of which I intend to work up into follow-up articles.

They are:

The 'ordered' tree control, which incorporates a non-client gutter for displaying the item numbers.

The idea stemmed from research I did into alternative designs for a tree-list control, which did not solve it by creating a hybrid control incorporating a tree and a list.

The hybrid control seems such an obvious solution that I suspect few people have stopped to question it, but it has still always struck me as looking far too much like hard work to be truly elegant ('square pegs' and 'round holes' spring to mind).

One possible idea is to implement the 'list' portion entirely in the non-client area of the tree. I.e., shift the right hand client edge to the left and then render the list portion in the resulting non-client area.

Whilst I've yet to get round to building a proof of concept, it was nevertheless this ongoing mental debate which prompted me to try to solve the requirement for numbered items and subitems by rendering the item/subitem numbers in the non-client area.

Without going into too much detail (as this will subsequently be an article of its own), this is how I got it to work:

Handle TVM_INSERTITEM and TVM_DELETEITEM to know exactly when items are added and removed.

In these handlers recalculate the width of the gutter required to display the widest 'dotted' item/subitem number. (Note: this is not necessarily simply the deepest subitem.)

Handle WM_NCCALCSIZE when it does, and offset the left border by the required gutter width.

Handle WM_NCPAINT for painting the numbers.

This is necessarily an over-simplification, but it captures the essence of the solution, and all that essentially remains is lots of fiddling about to ensure the non-client area gets redrawn at the the right times to stay synchronized with the client area.

Embedding .RC control definition data directly in a .cpp file to break the dependency on binary resources (a.k.a. 'Runtime Dialogs').

This is an idea that has been floating about for quite some time and which has only recently gelled into a workable solution.

The problem, put simply, is that if you want to take advantage of the resource editor in Visual Studio (and who doesn't), then you very quickly find yourself stuck with having to load dialog templates from resources compiled into the binary file.

This further means that if you want to make use of a dialog across multiple projects, then either you need to copy and paste the dialog template between project .RC files, or you need to build the dialog into a DLL from which it can be accessed.

'Runtime Dialogs' (a snappy title I coined myself) is a solution that neatly sidesteps both the nuisance of copying dialog resources between resource files and the extra work (and maintenance) involved in packaging dialogs in DLLs.

And it works like this:

First, you design your dialog template in the resource editor, create a CDialog derived class using class wizard, and wire up all the controls just as you normally would.

Next, you #include "runtimedlg.h" and change all instances of CDialog to CRuntimeDlg.

Then, you cut and paste the control definition section from the appropriate section in the .RC file and embed it directly in the dialog's .cpp file as a static string (with a bit of tweaking to handle double quotes and such like).

Finally, in the constructor of your dialog, you simply call CRuntimeDlg::AddRCControls(...) passing the control definitions as a string.

And CRuntimeDlg takes care of the rest including, if required, auto-sizing the dialog to suit the control layout.

I'm certainly not suggesting that this is a 'win-win' solution for all situations but it certainly has merits in its closer coupling of dialog template to dialog code which makes sharing dialogs across multiple projects a breeze.

P.S.: In case it's not clear here, I used CRuntimeDlg to create CToDoCtrl which encapsulates the ordered tree together with the priority, date and comments controls as a single simple-to-instantiate control.

This is possibly the most satisfying aspect of the whole project because it was completely unexpected.

What I mean is that, until recently, my knowledge of DOM and XMLDOM was virtually non-existent, as it's only since I've become more interested in the presentation of AbstractSpoon that I've been forced to get to grips with the various implementations of DOM and XMLDOM out there.

I'm pleased to say that the code on my site works under IE 6.0, Netscape 7.1, and Mozilla, although custom code was required to achieve this.

Generic MFC Classes that may prove Useful to You

The following table lists a wide range of utility classes written for this project. They can all be included in any MFC project provided you include any class dependencies too. Feel free to ask any questions relating to these specific classes and how to use them.

Adds support for recognizing urls, clicking them and setting custom url callbacks

CWinClasses

Encapsulates the ::GetClassName Win32 functions

CXmlFile, CXmlItem

Non-Unicode class for reading and writing xml files

CXmlFileEx

Adds encryption capabilities to CXmlFile

CXmlFile, IEncryption

* CSubclassWnd was originally written by Paul DiLascia for MSJ magazine. The version I use has been heavily extended to suit my specific needs. The classes that depend on it here need this extended version.

Further Work

Whilst this tool was originally intended for my personal use only, it is now a 'community' project, so if you find it useful and want to make suggestions for enhancements or bug fixes, then post below.

1. Make the Enter Key do what your ctrl+n shortcut does. Helps us lousy typists add tasks quickly.
2. Add a Duration field to quickly enter an estimated task duration that changes the task duration accordingly. The Duration field is standard on all PM software I am familiar with. Maybe use your estimated time field to do this.
3. Make the creation of dependencies a drag-and-drop function.
4. If you are going to create more than the FS dependency; for example, FS, FF, SS, and SF as shown in the screencast, then implement these with a left-click drop-down menu, similar to the way it is done in the screencast. If all you want to do is the FS dependency...then forget this one.

When you have the task A which is dependent on tasks B, C and D and you move the task B (or its end) manually, the task A is moved too accordingly ONLY to their relationship with B. This behavior is insufficient: A-C and A-D relationships should not be ignored at the same time. The algorithm should be based on the idea that the start of the A task depends not only on the B end but on the latest of [B end, C end, D end].

I'm not sure if your answer is just on this bug report or on my code patch[^] too, but I support your version planning whatever you decide.

Concerning this bug, just if you already has an idea of the fix, I will be grateful if you can share it in several words. Saying, "I think ClassX::FuncY should look over all m_lstPredecessors list to find the latest end instead of taking *m_pSelectedPredecessor's end", something like that, to help me create a patch for my several co-workers (and myself). If you have not thought about the fix yet, don't bother.

I'm not sure if your answer is just on this bug report or on my code patch

It was a comment only on the bug, but I've since looked at your code patch and am happy with it in principle, though I (inevitably) may choose to code it a little differently.

In order to fix the bug I decided that some refactoring was required so I separated out the calculation of the dependent task's new start date (so that it could take into account all of its dependencies) from the setting of the new start date. I also corrected the name FixupTaskDependency to FixupTaskLocalDependentsDates.

As far as I see, ToDoList's Gantt chart shows the dependency as an arrow line from the end of the dependent task A to the end of the task B on which the task A depends.
In contrary, Wikipedia shows the same dependency in a different way[^], and as for me, Wikipedia's approach is more clear to understand the project workflow.

I never use the end to end relationship, but I think the arrow is going in the correct direction. However, when I compare TDL's "end to end" relationship, to the "Finish to Finish" relationship in ProjectLibre, it appears that TDL's "end to end" is not working correctly. See screencast.

I'm agree that "finish to finish" ("end to end") is rare thing in real project management and the most common thing is "finish to start". So maybe if Dan will just change the current behavior to FS, it will be a great progress for Gantt chart users.

It's a pity that both tasks in the screencast have the same name "task" so it's hard to guess which one of them depends on another in the "Task Dependency" dialog. So I'm still unsure about the arrow direction correctness in TDL. I think that the arrow should go to the dependent task from the task it depends on (regardless of FS, FF, SF, SS).

Because of this discussion, I took another look at the TDL Gantt chart feature and had an AHA! moment.

I have not used this feature because I thought it did not yet have a Finish to Start relationship between tasks, which is the default in most project management software. Now after taking a closer look and playing around with the feature, I’m pretty sure the feature is a Finish to Start relationship, and not a Finish to Finish relationship as I thought earlier. And, it works quite well, but the arrow direction and where it connects to the task bar is confusing. Let me explain.

Normally, in project management software a Finish to Start relationship is shown by an arrow that points from the predecessor task to the successor task and connects from the end of the predecessor task bar to the beginning of the successor task bar. However, in TDL the arrow points the opposite way and it connects to the end of the successor task bar instead of the beginning of it.

To prevent the confusion (IMHO) Dan would need to turn the arrow around, connect it to the beginning of the successor task bar, and change the instructions to say something like, “Step 1 – Select the predecessor task. Step 2 – Select the successor task.” Now people that are used to using the likes of MS Project, GanttProject, and ProjectLibre would be less likely to be confused, because the arrow would point the way that they are used to seeing it. What do you think Dan...would this work for you?

I think that the arrow should go to the dependent task from the task it depends on

There are various reasons why I chose the 'reverse' direction but I agree that the industry standard is 'forward' and this is sufficiently compelling for me to make the change. Thx for your suggestion.

I just looked at this function again. And I assure you that it already is a Finish to Start (FS) function and not a Finish to Finish (FF) function. If I change the end date of the dependent upon task, then the start date of the dependent task moves accordingly. This is the way a FS dependency is suppose to work. I compared it to ProjectLibre's FS function to be sure.

You are sooo busy and doing soo much for all of us, I think you may have forgotten that you built this as a FS dependency. And, I'd forgotten, that I thought the request did not get implemented because I was confused by the arrow and connection. It is only been during this discussion that I figured out that you really did create a FS dependency and not a FF dependency.

I think it will be great when you turn the arrow around and attach it to the other end of the dependent task to make it fit the standard...or defacto standard...whatever it is that Microsoft Project, GanttProject, ProjectLibre, and Wikipedia use. Then as Yuri says, "it will be perfect!"

Dan...thanks again for all that you do for us users. I appreciate it very much! Version 7 is going to be great!

Once this FS feature update has been completed, I will make a screencast tutorial for it, and put it on your ToDoList YouTube channel.

I have done a rather dirty hack to turn arrows around now by changing SetFrom to SetTo and vice versa in CGanttTreeListCtrl::CalcDependencyEndPos, CGanttTreeListCtrl::ClearDependencyPickLine, CGanttTreeListCtrl::DrawDependencyPickLine. It is dirty because the proper change should include renaming "From""To" in many places (I guess) to keep names clear.

Concerning drawing an arrow line to the start of the dependent task instead of the end, I'm afraid it needs adding nStartPos member to GANTTDISPLAY and setting it properly so it can be used in CGanttTreeListCtrl::CalcDependencyEndPos then...