|
I am facing the same problem where the application does not care whether the operating system (Win10) is in dark or light mode.
Member 15775931 wrote: Could you point out any files that might have code that would allow me to manually change the color?
From what I read on the forum, the application is built so that the OS actually colours the background based on the system's dark/bright setting, so there is no way to change the background colour directly.
This weekend I'll try to find the code that should make the app behave as expected and see if I can find the problem. Any tips are welcome!
|
|
|
|
|
You are welcome to try, but there is no way that TDL can be made to support Dark/Light modes on Windows 10+. It's just too old an application.
|
|
|
|
|
Hello
Does there exist a schema definition, such as an .xsd file?
Thanks
|
|
|
|
|
Yes, but I don't know how correct it is because I don't have a lot of knowledge in that domain.
.\Resources\Misc\TdlSchema.xsd
|
|
|
|
|
I had a look into TdlSchema.xsd: it has some formal errors, an XSD validator shows them.
So I fixed these errors - some attributes got lost and appeared like a value of an element - and created a structure diagram with my XML editor (XMLspy).
You can download both inside this zip file.
But the TdlSchema.xsd has some secrets. E.g. some dates have a native element (STARTDATE) and a string-value element (STARTDATESTRING). But despite its string-value twin the data type of STARTDATE is also xs:string. On the other hand the values of this element in .tdl files are obviously decimal numeric values - what kind of format is used for the date in STARTDATE?
|
|
|
|
|
ToDoList keeps abbreviating task names and adding ellipsis to the end even if there is plenty of space to show the full task name. The only way to see the full task name in the task list is to edit the task name (yes I see full task name is in the status bar but it's too small and I'd like to see it in the task list) I've just spent 15 minutes and couldn't find the option to display full task name. Could anyone point in the right direction?
modified 27-Aug-22 17:31pm.
|
|
|
|
|
This is a bug, but unfortunately I've never been able to reproduce it.
Perhaps you could report it with supporting information on our dedicated forum[^]...
|
|
|
|
|
had clicked max on somehow
Cant update the other item as it is held for moderation
Regards
Michael Hawksworth
|
|
|
|
|
|
Hiya Dan, hope things are going well (is this 10 years I have been using this!).
I seem to have lost the edit boxes, doesn't matter if I move them around they don't seem to come back.
Michael.
Regards
Michael Hawksworth
|
|
|
|
|
Hello, I have been improving the export to MyLifeOrganized.
Now I can export all my important information but cannot export the formatted text of the comments...
I can get the plain text comments with the following code:
LPCTSTR szComments = pSrcTaskFile->GetTaskComments(hTask);
But, how could I get the formatted version of it? Or, at least, to know if the text has been formatted or not?
Ideally, I would like to convert that formatted text to a markdown text or a html text, so MyLifeOrganized can display it...
Any ideas or help would be greatly appreciated!!!
In case you also are interested to export your tasks to MyLifeOrganized, here it is my improved version of the exporter. Now I export:
- Task colors
- Flag state
- Completion state
- Comments
- Link or multiple links
- Task dependency or multiple dependencies
- First level task exported in bold, like in ToDoList
- Folders and projects: These are not available in ToDoList, but I set a criteria for setting those in MyLifeOrganized
- Fixed importance scale
- Fixed dates
#include "stdafx.h"
#include "MLOImport.h"
#include "MLOExporter.h"
#include "..\shared\xmlfileex.h"
#include "..\shared\datehelper.h"
#include "..\shared\timehelper.h"
#include "..\shared\misc.h"
#include "..\3rdParty\T64Utils.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CMLOExporter::CMLOExporter()
{
m_icon.Load(IDI_MYLIFEORGANISED);
}
CMLOExporter::~CMLOExporter()
{
}
void CMLOExporter::SetLocalizer(ITransText* )
{
}
IIMPORTEXPORT_RESULT CMLOExporter::Export(const ITaskList* pSrcTaskFile, LPCTSTR szDestFilePath, DWORD , IPreferences* , LPCTSTR )
{
const ITASKLISTBASE* pTasks = GetITLInterface<ITASKLISTBASE>(pSrcTaskFile, IID_TASKLISTBASE);
if (pTasks == NULL)
{
ASSERT(0);
return IIER_BADINTERFACE;
}
CXmlFile fileDest(_T("MyLifeOrganized-xml"));
fileDest.SetXmlHeader(DEFAULT_UTF8_HEADER);
fileDest.AddItem(_T("ver"), _T("1.2"));
CXmlItem* pXITasks = fileDest.AddItem(_T("TaskTree"));
if (!ExportTask(pTasks, pSrcTaskFile->GetFirstTask(), pXITasks, TRUE))
return IIER_SOMEFAILED;
ExportPlaces(pTasks, fileDest.Root());
if (!fileDest.Save(szDestFilePath, SFEF_UTF8WITHOUTBOM))
return IIER_BADFILE;
return IIER_SUCCESS;
}
IIMPORTEXPORT_RESULT CMLOExporter::Export(const IMultiTaskList* pSrcTaskFile, LPCTSTR szDestFilePath, DWORD , IPreferences* , LPCTSTR )
{
CXmlFile fileDest(_T("MyLifeOrganized-xml"));
fileDest.SetXmlHeader(DEFAULT_UTF8_HEADER);
fileDest.AddItem(_T("ver"), _T("1.2"));
CXmlItem* pXITasks = fileDest.AddItem(_T("TaskTree"));
for (int nTaskList = 0; nTaskList < pSrcTaskFile->GetTaskListCount(); nTaskList++)
{
const ITASKLISTBASE* pTasks = GetITLInterface<ITASKLISTBASE>(pSrcTaskFile->GetTaskList(nTaskList), IID_TASKLISTBASE);
if (pTasks == NULL)
{
ASSERT(0);
return IIER_BADINTERFACE;
}
if (!ExportTask(pTasks, pTasks->GetFirstTask(), pXITasks, TRUE))
return IIER_SOMEFAILED;
ExportPlaces(pTasks, fileDest.Root());
}
if (!fileDest.Save(szDestFilePath, SFEF_UTF8WITHOUTBOM))
return IIER_BADFILE;
return IIER_SUCCESS;
}
bool CMLOExporter::ExportTask(const ITASKLISTBASE* pSrcTaskFile, HTASKITEM hTask,
CXmlItem* pXIDestParent, BOOL bAndSiblings)
{
if (!hTask)
return true;
CXmlItem* pXIDestItem = pXIDestParent->AddItem(_T("TaskNode"));
if (!pXIDestItem)
return false;
pXIDestItem->AddItem(_T("Caption"), pSrcTaskFile->GetTaskTitle(hTask));
int nPriority = pSrcTaskFile->GetTaskPriority(hTask, FALSE);
int nImportance = max(nPriority, 0) * 20;
pXIDestItem->AddItem(_T("Importance"), nImportance, XIT_ELEMENT);
CString strIDD;
unsigned long nIDD = pSrcTaskFile->GetTaskID(hTask);
strIDD.Format(_T("{00000000-0000-1111-0000-%012lu}"), nIDD);
pXIDestItem->AddItem(_T("IDD"), strIDD, XIT_ELEMENT);
CString strNote;
int nDependencies = pSrcTaskFile->GetTaskDependencyCount(hTask);
if (nDependencies > 0)
{
CXmlItem* pXIDestDependenciesItem = pXIDestItem->AddItem(_T("Dependency"), _T(""), XIT_ELEMENT);
if (!pXIDestDependenciesItem)
return false;
CString strUID;
CString strDependencyCount;
for (int nDependency = 0; nDependency < nDependencies; ++nDependency)
{
LPCWSTR pStrDependency = pSrcTaskFile->GetTaskDependency(hTask, nDependency);
strDependencyCount.Format(_T("%d"), nDependency + 1);
strNote += _T("ToDoList dependency (") + strDependencyCount + _T("): [ID " + CString(pStrDependency) + _T("] "));
unsigned long nID = _wtoi(pStrDependency);
HTASKITEM hTaskDependency = pSrcTaskFile->FindTask(nID);
if (hTaskDependency)
{
unsigned long nUID = pSrcTaskFile->GetTaskID(hTaskDependency);
strUID.Format(_T("{00000000-0000-1111-0000-%012lu}"), nUID);
pXIDestDependenciesItem->AddItem(_T("UID"), strUID, XIT_ELEMENT);
strNote += pSrcTaskFile->GetTaskTitle(hTaskDependency);
}
strNote += _T("\n");
}
}
time64_t tDate = T64Utils::T64_NULL;
if (pSrcTaskFile->GetTaskCreationDate64(hTask, tDate))
pXIDestItem->AddItem(_T("Created"), FormatDate(tDate), XIT_ELEMENT);
if (pSrcTaskFile->GetTaskDoneDate64(hTask, tDate))
pXIDestItem->AddItem(_T("CompletionDateTime"), FormatDate(tDate), XIT_ELEMENT);
if (pSrcTaskFile->GetTaskDueDate64(hTask, false, tDate))
pXIDestItem->AddItem(_T("DueDateTime"), FormatDate(tDate), XIT_ELEMENT);
TDC_UNITS nUnits;
double dTimeEst = pSrcTaskFile->GetTaskTimeEstimate(hTask, nUnits, FALSE);
if (dTimeEst > 0.0)
{
TH_UNITS nTHUnits = MapUnitsToTHUnits(nUnits);
pXIDestItem->AddItem(_T("EstimateMax"), CTimeHelper().Convert(dTimeEst, nTHUnits, THU_DAYS), XIT_ELEMENT);
}
CString strLink;
static const CString urlProtocol = _T("://");
static const CString fileProtocol = _T("file://");
int nLinks = pSrcTaskFile->GetTaskFileLinkCount(hTask);
if (nLinks > 1)
{
CString strLinkCount;
for (int nLinkCount = 0; nLinkCount < nLinks; ++nLinkCount)
{
strLink = pSrcTaskFile->GetTaskFileLink(hTask, nLinkCount);
strLinkCount.Format(_T("%d"), nLinkCount+1);
strNote += _T("ToDoList link (") + strLinkCount + _T("): <");
if (strLink.Find(urlProtocol) < 0)
strNote += fileProtocol;
strNote += CString(strLink) + _T(">\n");
}
}
else
{
strLink = pSrcTaskFile->GetTaskFileLinkPath(hTask);
if (!Misc::IsEmpty(strLink))
{
strNote += _T("ToDoList link: <");
if (strLink.Find(urlProtocol) < 0)
strNote += fileProtocol;
strNote += CString(strLink) + _T(">\n");
}
}
LPCTSTR szComments = pSrcTaskFile->GetTaskComments(hTask);
bool bHasFormattedComments = pSrcTaskFile->GetTaskComments(hTask);
if (bHasFormattedComments)
strNote += _T("ToDoList Comments: ***LOST THE FORMATTED TEXT!!!***\n");
if ((!Misc::IsEmpty(strNote)) && (!Misc::IsEmpty(szComments)))
strNote += _T("\n");
if (!Misc::IsEmpty(szComments))
strNote += szComments;
if (!strNote.IsEmpty())
pXIDestItem->AddItem(_T("Note"), strNote, XIT_ELEMENT);
if ((pSrcTaskFile->GetFirstTask(hTask) != nullptr)) {
if (strNote.IsEmpty())
pXIDestItem->AddItem(_T("HideInToDoThisTask"), -1, XIT_ELEMENT); else
pXIDestItem->AddItem(_T("IsProject"), -1, XIT_ELEMENT); }
bool bTaskFlagged = pSrcTaskFile->IsTaskFlagged(hTask, false);
if (bTaskFlagged)
pXIDestItem->AddItem(_T("Flag"), _T("Red Flag"), XIT_ELEMENT);
CXmlItem* pXIDestCustomFormatItem = pXIDestItem->AddItem(_T("CustomFormat"), _T(""), XIT_ELEMENT);
if (!pXIDestCustomFormatItem)
return false;
bool bIsParentTask = pSrcTaskFile->IsTaskParent(hTask);
if (bIsParentTask)
pXIDestCustomFormatItem->AddItem(_T("Bold"), _T("1"), XIT_ELEMENT);
unsigned long unTaskColor = pSrcTaskFile->GetTaskColor(hTask);
CString strColor;
strColor.Format(_T("%lu"), unTaskColor);
pXIDestCustomFormatItem->AddItem(_T("FontColor"), strColor, XIT_ELEMENT);
pXIDestCustomFormatItem->AddItem(_T("SideBarColor"), strColor, XIT_ELEMENT);
if (!ExportTask(pSrcTaskFile, pSrcTaskFile->GetFirstTask(hTask), pXIDestItem, TRUE))
return false;
if (bAndSiblings)
{
HTASKITEM hSibling = pSrcTaskFile->GetNextTask(hTask);
while (hSibling)
{
if (!ExportTask(pSrcTaskFile, hSibling, pXIDestParent, FALSE))
return false;
hSibling = pSrcTaskFile->GetNextTask(hSibling);
}
}
return true;
}
CString CMLOExporter::FormatDate(time64_t tDate)
{
COleDateTime date = CDateHelper::GetDate(tDate);
return CDateHelper::FormatDate(date, DHFD_ISO | DHFD_TIME, 'T');
}
void CMLOExporter::BuildPlacesMap(const ITASKLISTBASE* pSrcTaskFile, HTASKITEM hTask,
CMapStringToString& mapPlaces, BOOL bAndSiblings)
{
if (!hTask)
return;
int nCat = pSrcTaskFile->GetTaskCategoryCount(hTask);
while (nCat--)
{
CString sCat = pSrcTaskFile->GetTaskCategory(hTask, nCat);
CString sCatUpper(sCat);
sCat.MakeUpper();
mapPlaces[sCatUpper] = sCat;
}
BuildPlacesMap(pSrcTaskFile, pSrcTaskFile->GetFirstTask(hTask), mapPlaces, TRUE);
if (bAndSiblings)
{
HTASKITEM hSibling = pSrcTaskFile->GetNextTask(hTask);
while (hSibling)
{
BuildPlacesMap(pSrcTaskFile, hSibling, mapPlaces, FALSE);
hSibling = pSrcTaskFile->GetNextTask(hSibling);
}
}
}
void CMLOExporter::ExportPlaces(const ITASKLISTBASE* pSrcTaskFile, CXmlItem* pDestPrj)
{
CMapStringToString mapPlaces;
BuildPlacesMap(pSrcTaskFile, pSrcTaskFile->GetFirstTask(), mapPlaces, TRUE);
if (mapPlaces.GetCount())
{
CXmlItem* pXIResources = pDestPrj->AddItem(_T("PlacesList"));
CString sPlace, sPlaceUpper;
POSITION pos = mapPlaces.GetStartPosition();
while (pos)
{
mapPlaces.GetNextAssoc(pos, sPlaceUpper, sPlace);
CXmlItem* pXIRes = pXIResources->AddItem(_T("TaskPlace"));
if (pXIRes)
pXIRes->AddItem(_T("Caption"), sPlace);
}
}
}
TH_UNITS CMLOExporter::MapUnitsToTHUnits(TDC_UNITS nUnits)
{
switch (nUnits)
{
case TDCU_MINS: return THU_MINS;
case TDCU_HOURS: return THU_HOURS;
case TDCU_DAYS: return THU_DAYS;
case TDCU_WEEKDAYS: return THU_WEEKDAYS;
case TDCU_WEEKS: return THU_WEEKS;
case TDCU_MONTHS: return THU_MONTHS;
case TDCU_YEARS: return THU_YEARS;
}
ASSERT(0);
return THU_NULL;
}
Hope it is useful for someone!
|
|
|
|
|
Many thanks Hugo, I will definitely integrate your changes into the GitHub repo.
Quote: How can I get the formatted text of the comments of a task?
This is tricky because the rich text comments is also a plugin of its own, so the app knows nothing about the format.
However, for your personal build, the following should work:
1. Get the non-text task comments using:
CString sComments = pTasks->GetTaskAttribute(hTask, L"CUSTOMCOMMENTS");
CString sType = pTasks->GetTaskAttribute(hTask, L"COMMENTSTYPE");
2. Check 'sType' against 'L"849CF988-79FE-418A-A40D-01FE3AFCAB2C"' which is the Rich Text plugin ID (RTFContentCtrl\RTFContentCtrl.h)
3. Base64 decode the text into a binary blob, using the method in 'CTaskFile::GetTaskCustomComments()'.
4. Decompress and extract the RTF like so (ConvertRTFToHTML\ConvertRTFToHTMLDlg.cpp):
CRtfHtmlConverter rtfHtml;
CBinaryData content(blob from 3.);
const unsigned char* pContent = content.Get();
int nLength = content.GetLength();
unsigned char* pDecompressed = NULL;
if (nLength && !rtfHtml.IsRTF((const char*)pTDI->customComments.Get()))
{
int nLenDecompressed = 0;
if (Compression::Decompress(pContent, nLength, pDecompressed, nLenDecompressed))
{
pContent = pDecompressed;
nLength = nLenDecompressed;
if (!rtfHtml.IsRTF((const char*)pContent))
{
nLength = 0;
}
}
else
{
nLength = 0;
}
}
The only other thing to be aware of is that RTF is always in ANSI format even if the surrounding code is UNICODE.
Also, if you want it to be a two-way process then you would also need to modify the MLOImporter similarly.
modified 10-Aug-22 2:44am.
|
|
|
|
|
Thank you very much, Dan!
I really appreciate your help, it was key to fix my issue. I finally was able to export the formatted text in HTML, I finally used the HTML approach from your other comment. The methods on this thread seems not to be exported in the interface, and it was much easier with the other approach. I share with you the code on the other answer!
About the two-way process, and importing back to ToDoList, it will be very interesting to implement... but I will wait until working a bit more with MyLifeOrganized. For now, I needed to access the information on my mobile, but MLO has issues with the formatted text in general. It has a basic support of HTML, so I finally export both version: a section with the plain text and a section with the formatted text. I will see while working how is that, but it would complicate a lot importing it back. These ones will be for read-only on mobile...
Apart from that, I found a couple of issues on ToDoList you might be interested:
The AddItem function seems to have an issue when creating nodes without information. It dumps a "0" where it shouldn't. It might be due to an overload of the function, instead of taking the empty string it migh be taking null instead as an integer and dumping a zero. Here it is a sample extract from one of my TDL files:
</COMMENTS>0</TASK>0</TASK>
So I fixed that in the MLOExport by calling with an empty string _T("") :
CXmlItem* pXIDestCustomFormatItem = pXIDestItem->AddItem(_T("CustomFormat"), _T(""), XIT_ELEMENT);
The other issue I detected is that, when changing the format in the combo-box, from RTF to HTML, the text is removed instead of converted from one format to the other... although that might be intentional...
Thank you very much for your support!
|
|
|
|
|
Quote: The AddItem function seems to have an issue when creating nodes without information. It dumps a "0" where it shouldn't
Thx, I'll take a look.
Quote: The other issue I detected is that, when changing the format in the combo-box, from RTF to HTML, the text is removed instead of converted from one format to the other... although that might be intentional...
Yes, this is a weakness of any plugin system because neither the app nor the plugin has any awareness of any other comments formats.
I could possibly experiment with passing the custom content of one plugin to another plugin and allow the plugin to either return FALSE or to do its own conversion but that would create interdependencies which the plugin system is trying to avoid.
|
|
|
|
|
Alternatively, if mlo supports HTML notes you could:
1. Return 'true' from CMLOExporter::SupportsHtmlComments()
2. Get the html comments using
CString sHtml = pTasks->GetTaskAttribute(hTask, L"HTMLCOMMENTS")
3. Base64 decode as before
PS. Not sure why the ITaskList interface doesn't provide this more easily. If you have any trouble getting this to work, I'm happy to help out.
|
|
|
|
|
This was the key to solve my problem, I finally used this approach! I really appreciate your help, thank you very much indeed!!!
I also improved a bit the implementation of the dependencies, making sure the GUIDs that I generate for MLO are unique, even if I mix several TDL files. Well, it is not the best of my code, I needed to implement it as quickly as I could... So, feel free to change anything if you finally want to use it! I also might have overused the notes section in MLO, as I added there the links, dependencies (actually unnecessary because they are also implemented natively for MLO, but just wanted a doble check for my personal build), and also I finally dumped the plain text and HTML versions of the comments (due to some limitations of the HTML support in MLO). So, you might want to change the criteria I finally used for my build...
Here it is the final code:
bool SupportsHtmlComments() const { return true; }
#include "stdafx.h"
#include "MLOImport.h"
#include "MLOExporter.h"
#include "..\shared\xmlfileex.h"
#include "..\shared\datehelper.h"
#include "..\shared\timehelper.h"
#include "..\shared\misc.h"
#include "..\3rdParty\T64Utils.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
CMLOExporter::CMLOExporter()
{
m_icon.Load(IDI_MYLIFEORGANISED);
}
CMLOExporter::~CMLOExporter()
{
}
void CMLOExporter::SetLocalizer(ITransText* )
{
}
IIMPORTEXPORT_RESULT CMLOExporter::Export(const ITaskList* pSrcTaskFile, LPCTSTR szDestFilePath, DWORD , IPreferences* , LPCTSTR )
{
const ITASKLISTBASE* pTasks = GetITLInterface<ITASKLISTBASE>(pSrcTaskFile, IID_TASKLISTBASE);
if (pTasks == NULL)
{
ASSERT(0);
return IIER_BADINTERFACE;
}
CXmlFile fileDest(_T("MyLifeOrganized-xml"));
fileDest.SetXmlHeader(DEFAULT_UTF8_HEADER);
fileDest.AddItem(_T("ver"), _T("1.2"));
CXmlItem* pXITasks = fileDest.AddItem(_T("TaskTree"));
if (!ExportTask(pTasks, pSrcTaskFile->GetFirstTask(), pXITasks, TRUE))
return IIER_SOMEFAILED;
ExportPlaces(pTasks, fileDest.Root());
if (!fileDest.Save(szDestFilePath, SFEF_UTF8WITHOUTBOM))
return IIER_BADFILE;
return IIER_SUCCESS;
}
IIMPORTEXPORT_RESULT CMLOExporter::Export(const IMultiTaskList* pSrcTaskFile, LPCTSTR szDestFilePath, DWORD , IPreferences* , LPCTSTR )
{
CXmlFile fileDest(_T("MyLifeOrganized-xml"));
fileDest.SetXmlHeader(DEFAULT_UTF8_HEADER);
fileDest.AddItem(_T("ver"), _T("1.2"));
CXmlItem* pXITasks = fileDest.AddItem(_T("TaskTree"));
for (int nTaskList = 0; nTaskList < pSrcTaskFile->GetTaskListCount(); nTaskList++)
{
const ITASKLISTBASE* pTasks = GetITLInterface<ITASKLISTBASE>(pSrcTaskFile->GetTaskList(nTaskList), IID_TASKLISTBASE);
if (pTasks == NULL)
{
ASSERT(0);
return IIER_BADINTERFACE;
}
if (!ExportTask(pTasks, pTasks->GetFirstTask(), pXITasks, TRUE))
return IIER_SOMEFAILED;
ExportPlaces(pTasks, fileDest.Root());
}
if (!fileDest.Save(szDestFilePath, SFEF_UTF8WITHOUTBOM))
return IIER_BADFILE;
return IIER_SUCCESS;
}
bool CMLOExporter::ExportTask(const ITASKLISTBASE* pSrcTaskFile, HTASKITEM hTask,
CXmlItem* pXIDestParent, BOOL bAndSiblings)
{
if (!hTask)
return true;
CXmlItem* pXIDestItem = pXIDestParent->AddItem(_T("TaskNode"));
if (!pXIDestItem)
return false;
pXIDestItem->AddItem(_T("Caption"), pSrcTaskFile->GetTaskTitle(hTask));
int nPriority = pSrcTaskFile->GetTaskPriority(hTask, FALSE);
int nImportance = max(nPriority, 0) * 20;
pXIDestItem->AddItem(_T("Importance"), nImportance, XIT_ELEMENT);
CString strIDD;
unsigned long nIDD = pSrcTaskFile->GetTaskID(hTask);
CString strTitle = pSrcTaskFile->GetTaskTitle(hTask);
unsigned long nTaskNameCode = strTitle.GetLength();
if (nTaskNameCode > 1)
nTaskNameCode *= int(strTitle[1]);
strIDD.Format(_T("{%08lu-0000-1111-0000-%012lu}"), nIDD, nTaskNameCode);
pXIDestItem->AddItem(_T("IDD"), strIDD, XIT_ELEMENT);
CString strNote;
int nDependencies = pSrcTaskFile->GetTaskDependencyCount(hTask);
if (nDependencies > 0)
{
CXmlItem* pXIDestDependenciesItem = pXIDestItem->AddItem(_T("Dependency"), _T(""), XIT_ELEMENT);
if (!pXIDestDependenciesItem)
return false;
CString strUID;
CString strDependencyCount;
for (int nDependency = 0; nDependency < nDependencies; ++nDependency)
{
LPCWSTR pStrDependency = pSrcTaskFile->GetTaskDependency(hTask, nDependency);
strDependencyCount.Format(_T("%d"), nDependency + 1);
strNote += _T("ToDoList Dependency (") + strDependencyCount + _T("): [ID " + CString(pStrDependency) + _T("] "));
unsigned long nID = _wtoi(pStrDependency);
HTASKITEM hTaskDependency = pSrcTaskFile->FindTask(nID);
if (hTaskDependency)
{
unsigned long nUID = pSrcTaskFile->GetTaskID(hTaskDependency);
CString strDependencyTitle = pSrcTaskFile->GetTaskTitle(hTaskDependency);
unsigned long nTaskDependencyNameCode = strDependencyTitle.GetLength();
if (nTaskDependencyNameCode > 1)
nTaskDependencyNameCode *= int(strDependencyTitle[1]);
strUID.Format(_T("{%08lu-0000-1111-0000-%012lu}"), nUID, nTaskDependencyNameCode);
pXIDestDependenciesItem->AddItem(_T("UID"), strUID, XIT_ELEMENT);
strNote += strDependencyTitle;
}
strNote += _T("\r\n\r\n");
}
}
time64_t tDate = T64Utils::T64_NULL;
if (pSrcTaskFile->GetTaskCreationDate64(hTask, tDate))
pXIDestItem->AddItem(_T("Created"), FormatDate(tDate), XIT_ELEMENT);
if (pSrcTaskFile->GetTaskDoneDate64(hTask, tDate))
pXIDestItem->AddItem(_T("CompletionDateTime"), FormatDate(tDate), XIT_ELEMENT);
if (pSrcTaskFile->GetTaskDueDate64(hTask, false, tDate))
pXIDestItem->AddItem(_T("DueDateTime"), FormatDate(tDate), XIT_ELEMENT);
TDC_UNITS nUnits;
double dTimeEst = pSrcTaskFile->GetTaskTimeEstimate(hTask, nUnits, FALSE);
if (dTimeEst > 0.0)
{
TH_UNITS nTHUnits = MapUnitsToTHUnits(nUnits);
pXIDestItem->AddItem(_T("EstimateMax"), CTimeHelper().Convert(dTimeEst, nTHUnits, THU_DAYS), XIT_ELEMENT);
}
CString strLink;
static const CString urlProtocol = _T("://");
static const CString fileProtocol = _T("file://");
int nLinks = pSrcTaskFile->GetTaskFileLinkCount(hTask);
if (nLinks > 1)
{
CString strLinkCount;
for (int nLinkCount = 0; nLinkCount < nLinks; ++nLinkCount)
{
strLink = pSrcTaskFile->GetTaskFileLink(hTask, nLinkCount);
strLinkCount.Format(_T("%d"), nLinkCount+1);
strNote += _T("ToDoList Link (") + strLinkCount + _T("): <");
if (strLink.Find(urlProtocol) < 0)
strNote += fileProtocol;
strNote += CString(strLink) + _T(">\r\n\r\n");
}
}
else
{
strLink = pSrcTaskFile->GetTaskFileLinkPath(hTask);
if (!Misc::IsEmpty(strLink))
{
strNote += _T("ToDoList Link: <");
if (strLink.Find(urlProtocol) < 0)
strNote += fileProtocol;
strNote += CString(strLink) + _T(">\r\n\r\n");
}
}
LPCTSTR szComments = pSrcTaskFile->GetTaskComments(hTask);
CString strHtmlComments = pSrcTaskFile->GetTaskAttribute(hTask, TDCA_HTMLCOMMENTS);
CString strCommentsType;
strHtmlComments.Trim();
bool bHasFormattedComments = !strHtmlComments.IsEmpty();
if (bHasFormattedComments)
{
bool bIsRTFContent = (strHtmlComments.Find(_T("<div class=WordSection1>")) >= 0);
if (bIsRTFContent)
{
strHtmlComments.Replace(_T("<div class=WordSection1>"), _T(""));
strHtmlComments.Replace(_T("</div>"), _T(""));
strHtmlComments.Replace(_T(" style='font-size:8.0pt;font-family:"), _T(""));
strHtmlComments.Replace(_T("\"Arial\",\"sans-serif\"'"), _T(""));
strHtmlComments.Replace(_T(" lang=EN-GB"), _T(""));
strHtmlComments.Replace(_T(" class=MsoNormal style='margin-top:0cm;margin-bottom:.0001pt;line-height:"), _T(""));
strHtmlComments.Replace(_T("normal;text-autospace:none'"), _T(""));
strHtmlComments.Replace(_T("<p\r\n>"), _T("<p>"));
strHtmlComments.Replace(_T("<p\n>"), _T("<p>"));
strHtmlComments.Replace(_T("<span\r\n>"), _T("<span>"));
strHtmlComments.Replace(_T("<span\n>"), _T("<span>"));
strCommentsType = _T("RTF"); }
else
strCommentsType = _T("HTML");
strNote += _T("ToDoList Comments (from ") + strCommentsType + _T("): *** To see format in notes, activate \"Use Markdown format in notes\" in the MyLifeOrganized options ***\r\n\r\n");
}
if ((!Misc::IsEmpty(strNote)) && (!Misc::IsEmpty(szComments)))
strNote += _T("\r\n\r\n");
if (!Misc::IsEmpty(szComments))
{
if (!strHtmlComments.IsEmpty())
strNote += _T("----- *** ToDoList Plain Text (HTML below) *** -----\r\n\r\n");
strNote += szComments;
}
if (!strHtmlComments.IsEmpty())
strNote += _T("\r\n\r\n\r\n----- *** ToDoList HTML (from ") + strCommentsType + _T(") *** (MyLifeOrganized supports only basic HTML formatting and Markdown) -----\r\n\r\n") + strHtmlComments;
if (!strNote.IsEmpty())
pXIDestItem->AddItem(_T("Note"), strNote, XIT_ELEMENT);
if ((pSrcTaskFile->GetFirstTask(hTask) != nullptr)) {
if (strNote.IsEmpty())
pXIDestItem->AddItem(_T("HideInToDoThisTask"), -1, XIT_ELEMENT); else
pXIDestItem->AddItem(_T("IsProject"), -1, XIT_ELEMENT); }
bool bTaskFlagged = pSrcTaskFile->IsTaskFlagged(hTask, false);
if (bTaskFlagged)
pXIDestItem->AddItem(_T("Flag"), _T("Red Flag"), XIT_ELEMENT);
CXmlItem* pXIDestCustomFormatItem = pXIDestItem->AddItem(_T("CustomFormat"), _T(""), XIT_ELEMENT);
if (!pXIDestCustomFormatItem)
return false;
bool bIsParentTask = pSrcTaskFile->IsTaskParent(hTask);
if (bIsParentTask)
pXIDestCustomFormatItem->AddItem(_T("Bold"), _T("1"), XIT_ELEMENT);
unsigned long unTaskColor = pSrcTaskFile->GetTaskColor(hTask);
CString strColor;
strColor.Format(_T("%lu"), unTaskColor);
pXIDestCustomFormatItem->AddItem(_T("FontColor"), strColor, XIT_ELEMENT);
pXIDestCustomFormatItem->AddItem(_T("SideBarColor"), strColor, XIT_ELEMENT);
if (!ExportTask(pSrcTaskFile, pSrcTaskFile->GetFirstTask(hTask), pXIDestItem, TRUE))
return false;
if (bAndSiblings)
{
HTASKITEM hSibling = pSrcTaskFile->GetNextTask(hTask);
while (hSibling)
{
if (!ExportTask(pSrcTaskFile, hSibling, pXIDestParent, FALSE))
return false;
hSibling = pSrcTaskFile->GetNextTask(hSibling);
}
}
return true;
}
CString CMLOExporter::FormatDate(time64_t tDate)
{
COleDateTime date = CDateHelper::GetDate(tDate);
return CDateHelper::FormatDate(date, DHFD_ISO | DHFD_TIME, 'T');
}
void CMLOExporter::BuildPlacesMap(const ITASKLISTBASE* pSrcTaskFile, HTASKITEM hTask,
CMapStringToString& mapPlaces, BOOL bAndSiblings)
{
if (!hTask)
return;
int nCat = pSrcTaskFile->GetTaskCategoryCount(hTask);
while (nCat--)
{
CString sCat = pSrcTaskFile->GetTaskCategory(hTask, nCat);
CString sCatUpper(sCat);
sCat.MakeUpper();
mapPlaces[sCatUpper] = sCat;
}
BuildPlacesMap(pSrcTaskFile, pSrcTaskFile->GetFirstTask(hTask), mapPlaces, TRUE);
if (bAndSiblings)
{
HTASKITEM hSibling = pSrcTaskFile->GetNextTask(hTask);
while (hSibling)
{
BuildPlacesMap(pSrcTaskFile, hSibling, mapPlaces, FALSE);
hSibling = pSrcTaskFile->GetNextTask(hSibling);
}
}
}
void CMLOExporter::ExportPlaces(const ITASKLISTBASE* pSrcTaskFile, CXmlItem* pDestPrj)
{
CMapStringToString mapPlaces;
BuildPlacesMap(pSrcTaskFile, pSrcTaskFile->GetFirstTask(), mapPlaces, TRUE);
if (mapPlaces.GetCount())
{
CXmlItem* pXIResources = pDestPrj->AddItem(_T("PlacesList"));
CString sPlace, sPlaceUpper;
POSITION pos = mapPlaces.GetStartPosition();
while (pos)
{
mapPlaces.GetNextAssoc(pos, sPlaceUpper, sPlace);
CXmlItem* pXIRes = pXIResources->AddItem(_T("TaskPlace"));
if (pXIRes)
pXIRes->AddItem(_T("Caption"), sPlace);
}
}
}
TH_UNITS CMLOExporter::MapUnitsToTHUnits(TDC_UNITS nUnits)
{
switch (nUnits)
{
case TDCU_MINS: return THU_MINS;
case TDCU_HOURS: return THU_HOURS;
case TDCU_DAYS: return THU_DAYS;
case TDCU_WEEKDAYS: return THU_WEEKDAYS;
case TDCU_WEEKS: return THU_WEEKS;
case TDCU_MONTHS: return THU_MONTHS;
case TDCU_YEARS: return THU_YEARS;
}
ASSERT(0);
return THU_NULL;
}
It was extremelly useful for me, hope it is useful some someone else too!
Thank you for your support!
|
|
|
|
|
These HTML artifacts are a consequence of the app using MS Word to convert the RTF to HTML, something that can be changed via the 'Rich Text' comments 'Preferences' dialog. Might be worth a try...
|
|
|
|
|
That is interesting... For now, I have already migrated my 12 ToDoList projects into MyLifeOrganized, and everything is going smooth and fine... even the links open, because they keep the same path on the disk! I keep the TDL files because ToDoList has more options, like Gantt, Kanban, graphs,... that MyLifeOrganized doesn't have... I migrated just to be able to see the task on my mobile smoothly...
|
|
|
|
|
ps. I don't know if you have used GitHub before, but if you are interested you could fork the main repo[^] and add your code there which would allow me to more easily review the changes you have made and decide which ones are appropriate for incorporating back into the main branch.
This would also make it simpler for you to retrieve any fixes I make in the main repo.
No pressure.
|
|
|
|
|
I use a different repository system at work, but have already used Git and Bitbucket... It is time to open an account on GitHub, no problem!
|
|
|
|
|
Alternatively, you could just email the .h/.cpp files to:
todolist.forums@abstractspoon.com Thanks.
|
|
|
|
|
Thank you very much. No problem, I will do the fork. I have been a bit busy these days...
|
|
|
|
|
Sorry for the delay, I have done the fork but will have to push the changes after my holidays. I think I also will implement the import from MLO, as I have found a bug in the MLO markdown... I contacted their support, which is very kind, but I am not sure how much will it take for them to fix. So I think it will be a good idea to keep my projects in sync from the very beginning...
|
|
|
|
|
That's great. Thanks for the update Hugo.
|
|
|
|
|
Hello! After exporting my ToDoList projects to MyLifeOrganized... I imported them into MLO and found that a lot of information was totally missing, for example all the notes/comments on the tasks... Although the notes/comments were exported into the .ML/.XML file from TDL, that information was missing in MLO. I contacted the MLO support and they told me that the format should exactly match what they are currently exporting. It seems they broke the compatibility with older versions of their own files! So MLO import is broken and can no longer properly import the older MLO format. So TDL export is no longer working fine for MLO...
It seems they changed the attribute
<TaskNode Note="..."> for another format:
<TaskNode><Note>...</Note></TaskNode>
Another thing I found as a very serious issue for me was loosing the TDL links. MLO has no similar field for the TDL links... And I have a lot of those links in my TDL projects, so: I was not accepting loosing the links nor the notes/comments of my tasks... and decided to fix the source code (thanks Dan for opensourcing!) myself! I decided to get the link information and put it at the beginning of the notes/comments. It would be nice if this can be improved also exporting the text format, the text markdown that MLO supports, task colors, flags, completion, and other info... maybe I will update the fix, but, in case you are interested, here it is my customization to the CMLOExporter::ExportTask funtion:
Instead of:
LPCTSTR szComments = pSrcTaskFile->GetTaskComments(hTask);
if (!Misc::IsEmpty(szComments))
pXIDestItem->AddItem(_T("Note"), szComments);
I wrote the following, in case you are interested:
LPCTSTR szLink = pSrcTaskFile->GetTaskFileLinkPath(hTask);
LPCTSTR szComments = pSrcTaskFile->GetTaskComments(hTask);
CString strNote;
if (!Misc::IsEmpty(szLink))
strNote += _T("ToDoList link: ") + CString(szLink);
if ((!Misc::IsEmpty(szLink)) && (!Misc::IsEmpty(szComments)))
strNote += _T("\n\n");
if (!Misc::IsEmpty(szComments))
strNote += szComments;
if (!strNote.IsEmpty())
pXIDestItem->AddItem(_T("Note"), strNote, XIT_ELEMENT);
Ideally, I would like to keep using both software, so it would be really nice to keep this exporting up to date, and also the other way around, importing from MLO into TDL...
Also, recorded a quick video about this issue: My experience migrating from ToDoList to MyLifeOrganized - YouTube[^]
Thank you for your interest!
EDIT: Check my own reply below for a very improved version of the code, exporting a lot more data...
modified 9-Aug-22 19:22pm.
|
|
|
|
|