Click here to Skip to main content
Click here to Skip to main content

DHTML Editor with Table Support and Source Editor

By , 14 Feb 2011
 
DHtmlEditor.gif

Introduction

When you add the class CHtmlEditor to your project, the users of your software will be able to enter HTML content into an HTML Editor GUI control. Using this class, you get several weeks of coding and testing for free!

ClassView.gif

Features

  • WYSIWYG Editor using the DHTML Editor of Internet Explorer
  • Edit mode / Browse mode / Source code editor
  • Support of tables (add and delete rows and columns, split cells, combine cells, edit table properties)
  • Default functionality as in Outlook Express (images, links, HR, undo, redo...) and more
  • Complex support of text formatting (font size can be set very precisely in pixels) and definition of default font
  • Enabling / disabling of XP desktop themes
  • Complex library of classes and functions which wrap the access to Internet Explorer's COM interface like IHTMLElement, IHTMLDocument2, etc
  • Programmatic access to style sheet definitions inside the HTML page
  • Complex clean-up function which removes invalid HTML tags
  • Automatic update of the state of the GUI elements (combo boxes, buttons) depending on the current cursor position
  • Custom context menus for Internet Explorer in editor and browse mode possible
  • Browsing to embedded HTML resources
  • Dialog-based GUI control
  • MFC project
  • All functionality is encapsulated in one class with multiple embedded classes, so you add only one new item to the class view of your project (see image above)
  • New in version 5.0: Generation of indented HTML 5 (W3C conform) HTML code

Quality

This project is very versatile and very thoroughly tested:

  • Tested on Visual Studio 6.0 - 2010
  • Tested on Internet Explorer 5.0 - 8.0
  • Tested on Windows 95 - Windows 7
  • Can be integrated into a UNICODE or Multibyte (MBCS) project
  • Even if compiled as Multibyte version, this project ALWAYS supports Greek, Russian, Japanese letters
  • Cleanly and thoroughly written code
  • Very well-documented code; if you are not one of those too lazy to read, you will understand
  • Tested by thousands of users worldwide, as this class is part of the program ElmüSoft Desktop Organizer

Requirements

To understand this project, you:

  • Need profound HTML and Style Sheet knowledge
  • Must be firm in C++ programming (deriving classes, etc)
  • Have to study the MSDN if you want to expand the existing functionality

Why Such a Comlpex Class?

You might ask, "With MFC7, Microsoft introduced the CHtmlEditView class. Isn't that all I need?"

No, definitely not!

CHtmlEditView is only the very basic base of all. It does not offer any functionality to work with tables. It does not allow you to modify the background color of the document or to modify its STYLE definitions. It does not offer a source editor and it does not allow you to directly access any HTML element inside the page, etc...

Tables

I don't know of any open source HTML editor that supports tables. So I wrote the stuff on my own, which is more complex than you might think,especially if you want to support adding new columns or splitting one cell into two, etc. To keep the work in a reasonable limit, this editor only supports cells that span over multiple columns. Cells spanning over multiple rows are not needed that often, and implementing this would still be a lot of additional work if you do it thoroughly!

Fonts

The Internet Explorer Editor only supports seven font sizes, as you can see in Outlook Express. The command IDM_FONTSIZE sets <FONT size=1...7>. I decided that this is too gross and found a way to let the user set unlimited and precise font sizes using <FONT style="font-size:17px"> (using px is much more precise than using pt).

Internet Explorer COM Interface Relationship

Internet Explorer offers a really huge bunch of COM interfaces, which allow the control of ANYthing. Your application can let Internet Explorer browse to any URL, access any HTML element in the document and read or modify its content. Everything you can do with JavaScript you can also do directly from your application via COM. Also, you cannot only do that if your application hosts the Internet Explorer control, but also if Internet Explorer runs as an independent application! However, remote automation is not a subject of this project.

There is a very long list of COM interfaces described in the MSDN. I will explain here only the basics. The following image tries to illustrate the relationship of the most important interfaces.

ObjectHierarchy.gif

The IWebBrowser interface is the browser / editor itself. It contains, for example the command Navigate(), which browses to a URL. You can ask the IWebBrowser interface for the IHTMLDocument interface that represents all the visible content of the HTML document. If the document contains a frameset, then each frame again contains its own IHTMLDocument interface.

You can ask the IHTMLDocument interface for the IHTMLBodyElement which, for example, contains a command to set the background color (<BODY bgcolor=red>). The document can also retrieve ANY other IHTMLElement interface. For example, it can do this by using IHTMLDocument3.getElementById() or by using IHTMLDocument.get_all(), which retrieves a collection of ALL elements in the document that can later be filtered.

You will notice that some interfaces exist with a number. They represent the same interface and you can cast them into each other using CComQIPtr<...>. However, the commands they provide are different. The reason is that these interfaces are implemented at different times:

  • IHTMLDocument requires Internet Explorer 4.0
  • IHTMLDocument2 requires Internet Explorer 4.0
  • IHTMLDocument3 requires Internet Explorer 5.0
  • IHTMLDocument4 requires Internet Explorer 5.5
  • IHTMLDocument5 requires Internet Explorer 6.0

With CHtmlEditor::GetMsieVersion(), you can retrieve the version of Internet Explorer on the current machine. If you should need any of the newer functionality, you must check the MSIE version. Otherwise, the result is a crash of your application on older Internet Explorer versions! Currently the project does not use any commands that require more than Microsoft Internet Explorer 5.0.

CHtmlEditor Class Hierarchy

The following image illustrates the class hierarchy:

ClassHierarchy.gif

The hierachy of classes in CHtmlEditor is equivalent to this tree. cHtmlDomNode is the base of all. It contains commands like those in the JavaScript DOM model: you can navigate from one element to its parent or sibling (cHtmlDomNode::NextSibling()); you can remove an element from the document (cHtmlDomNode::Remove()) or you can even create a new element.

Derived from cHtmlDomNode are cHtmlDocument and cHtmlElement. This means that these inherit the functionality of cHtmlDomNode. cHtmlElement allows, for example, retrieval of the inner HTML code of an element or the modification of its attributes. For example, cHtmlElement::SetAttribute("Align", "Right") would result in <SPAN align=right> when executed on a SPAN element.

Derived from cHtmlElement are the other elements like cHtmlTable, cHtmlImg, etc that again derive the functionality of cHtmlElement and add their own specialized functionality. For example, cHtmlTableCell::Split() would split a table cell into two cells.

Using CHtmlEditor

The fine thing is that, using CHtmlEditor, you don't have to care about all of the COM interfaces. They are all nicely wrapped in C++ classes. First of all, to integrate the editor into your project, simply place a static control into the dialog where you want the editor to be. The following code creates the Internet Explorer editor and the Richedit Source editor:

pi_Editor = new CHtmlEditor();
BOOL b_OK = pi_Editor->CreateEditor(&mCtrl_Static, this, FALSE, TRUE);

Here are some examples that demonstrate the usage:

Example 1

You have a table <TABLE ID="MainTable"> inside the document and you want to set its border color:

cHtmlTable i_Table = pi_Editor->GetDocument()->GetElementByID("MainTable");
if (i_Table.Valid()) i_Table.SetBorderColor("#5544FF");

Example 2

You have a table and want to set the background color of the third cell in the first row:

cHtmlTableCell i_Cell = i_Table.GetCell(0, 2, TRUE);
if (i_Cell.Valid()) i_Cell.SetBgColor("blue");

Example 3

You want to set the color of all <HR> tags that exist in the document:

CComQIPtr<IHTMLElementCollection> i_Collect;
UINT Count = pi_Editor->GetDocument()->GetElementCollection("HR", i_Collect);
for (UINT i=0; i<Count; i++)
{
   cHtmlHR i_HR = cHtmlElement::GetElementFromCollection(i, i_Collect);
   i_HR.SetProperty(E_Color, "#FF8800");
}

Working with Styles

CHtmlEditor allows access (read and write) to any style attribute of any HTML element in the document. It is as easy as writing:

i_TableCell.GetStyle().SetProperty(E_FontSize, "18px");

...which would set the font size of a table cell to <TD style="font-size:18px">...</TD>. You can also modify the general style definitions for the whole document. The following sets <HEAD><STYLE> Body { FONT-SIZE: 18px; } </STYLE></HEAD>.

pi_Editor->GetDocument()->GetStyleSheet().SetProperty("Body", E_FontSize, "18px");

Working with the Selection / Cursor Position

For example, the user has selected text and clicks a toolbar button to execute any action on this text. There are several default functions provided by Internet Explorer that work with the current selection. For example, if you call:

pi_Editor->ExecSetCommand(IDM_FORECOLOR, "red")

...the foreground color of the selected text will be set to red. You can find all the IDM_XYZ commands in the file MsHtmcid.h of Visual Studio 7, but most of them are not implemented. It seems that Microsoft initially had many more plans with Internet Explorer than they finally realized.

But what if you want to implement your own not-yet-existing functionality? You can call cHtmlDocument::GetSelection(), which will return the HTML element containing the cursor or the selection.

Example 1

You want to retrieve the URL of the image which is currently selected by the user:

cHtmlImg i_Img = pi_Editor->GetDocument()->GetSelection();
if (i_Img.Valid()) s_Url = i_Img.GetURL();

Example 2

You want to add a <SUB> tag around the text that is currently selected (<SUB>Text</SUB>):

BOOL b_OK = pi_Editor->GetDocument()->AddToSelection("<SUB>", "</SUB>");

See source code comments for more info!

Visual Studio 6 versus 7

There are several reasons why I prefer working with Visual Studio 6 instead of upgrading to Visual Studio 7, but I don't want to explain this here. The problem is that MFC 6 does not yet support CHtmlEditView, the MFC wrapper for the HTML Editor. However, MFC 6 already supports CHtmlView, the Internet Explorer Browser.

If you look into the source code of CHtmlEditView (VisualStudio7\Vc7\AtlMfc\Include\AfxHtml.h), you will notice that it would be extremely awkward to convert all that stuff to make it run on Visual Studio 6. There are several classes required, like CHtmlEditCtrlBase, etc and you cannot simply take Microsoft's Visual Studio 7 code and put it into your Visual Studio 6 project. This is because there are several dependencies on classes which do not yet exist in Visual Studio 6 (CStringA, CStringW) or which have less functionality. You would end up rewriting all of it.

I found an easier way of expanding CHtmlView to get all the functionality I need by adding only a very few lines of code. For Visual Studio 6, it is required to #include some Visual Studio 7 header files and a *.Lib file, which you find in the folder Vs7 of the Visual Studio 6 project. This is the reason why the Visual Studio 6 project download is much bigger. The Visual Studio 7 project obviously uses CHtmlEditView and is much smaller, as all the includes are not required.

Why MFC?

If you are not familiar with MFC and if you are wondering what _T("Red") is good for or what the compiler options UNICODE and MBCS mean, I recommend the VERY good book "Professional MFC," which you can download for free from my homepage. There are some people who don't like MFC, mostly beginners who never understood it. However, this project is a very good example that demonstrates how MFC makes your life much easier! Let's say you have the HTML Editor and want to retrieve the title of the document:

<HTML><HEAD><TITLE>Title of document</TITLE></HEAD><BODY></BODY></HTML>

Version 1

Programming the COM interface of Internet Explorer without MFC would result in this code:

GetDocTitle(IWebBrowser2* pWebBrowser, WCHAR *pu16Title)
{
   IDispatch      *pHtmlDocDisp;
   IHTMLDocument2 *pHtmlDoc;
   
   // get the IDispatch interface of the document
   HREULT hr = pWebBrowser->get_Document (&pHtmlDocDisp);
   if (SUCCEEDED (hr))
   {
      // Query interface for IHTMLDocument2
      hr = pHtmlDocDisp->QueryInterface (IID_IHTMLDocument2, (void**)&pHtmlDoc);
      if (SUCCEEDED (hr))
      {
         BSTR bsTitle;
         hr = pHtmlDoc->get_title (&bsTitle);
         if (SUCCEEDED (hr))
         {
            wcscpy(pu16Title, bsTitle);
            SysFreeString (bsTitle);
         }
         pHtmlDoc->Release();
      }
      pHtmlDocDisp->Release();
   }
}

Version 2

With MFC, the same code becomes much shorter and less error-prone:

GetDocTitle(CHtmlEditView *pEditor, CString *ps_Title)
{
   CComQIPtr<IHTMLDocument2> i_Doc2 = pEditor->GetHtmlDocument();
   CComBSTR bs_Title;
   i_Doc2->get_title (&bsTitle);
   *ps_Title = bs_Title;
}

Version 3

Using the class CHtmlEditor of this project, it cannot be easier anymore:

GetDocTitle(CHtmlEditor *p_Editor, CString *ps_Title)
{
   *ps_Title = p_Editor->GetDocument()->GetTitle();
}

Internet Explorer / MFC Bugs

After working for nearly 2 years with the Internet Explorer COM interface, I have to say that this is very good quality: it is free of bugs! This is very unusual for Microsoft products, but the only Internet Explorer bug I found is IHTMLDocument2.get_readyState. Do not use this command! This function worked fine until Microsoft destroyed it with Windows XP SP2. However, this does not matter, as you can easily replace it with CHtmlEditor::GetBusy().

There is also a bug in the MFC function CHtml(Edit)View::GetDocumentHTML() that uses CStreamOnCString, which is buggy. I wrote my own class, cStreamReader, which replaces the buggy function.

Security

Everybody knows that Internet Explorer is full of security holes. However, if you use it in your application just as an editor and do NOT browse with it to any malicious webpage, you don't have to worry about anything!

IMPORTANT: I recommend NOT allowing the user to switch into browse mode. This sample project allows everything.

If you don't want to be that strict, you can use the function CHtmlEditor::OnBeforeNavigate2() to forbid browsing to the internet. There you can put a filter which allows only files on the local hard disk and embedded resources. Each time the user clicks a link, he gets an error message.

<P> versus <DIV>

If you switch Internet Explorer into Design Mode, you will find that by default hitting the Enter key inserts TWO new lines. This is because Internet Explorer inserts a <P> tag. If you want a <BR> (a single new line), you have to press Shift + Enter.

This is quite stupid because you will have to tell all the users of your software to change their habits and to always hold the Shift key down when they want to go to the next line. There is no way to change this behaviour in Internet Explorer. However, there is a work-around. After loading a clean document, you have to insert an empty <DIV> tag like this: <BODY><DIV></DIV></BODY>. Now each Enter inserts ONE new line, which will look like this: <DIV>Text of line 1</DIV><DIV>Text of line 2</DIV>. Also, each empty table cell has to be filled: <TD><DIV></DIV></TD>. CHtmlEditor takes care to do all this automatically.

GUI

This sample project uses a very primitive GUI consisting of buttons and button-style check boxes to keep it simple. You will have to create your own nice toolbar with tooltips. The advantage of a toolbar over check boxes and buttons is that a toolbar cannot steal the focus from the HTML editor when the user clicks a toolbar button. The result could look like this in my program ElmüSoft Desktop Organizer:

PTBSync.png

I used the top row of toolbar buttons for table editing, the bottom row for text editing and the middle row for all the rest of the functionality.

Automatic GUI Update

Every time you move the cursor in the HTML document, CHtmlEditor posts a notification to its parent so the GUI can be updated. This means that if the cursor moved from a bold text with font size 11 to an underlined text with font size 15, the combo boxes and toolbars are updated. In MFC7, CHtmlEditor gets this event from CHtmlEditView::OnUpdateUI(), but this is not yet available in MFC6. However, after studying the MFC source code, I found that this event is triggered by a timer. I subclass the "Internet Explorer_Server" window, catch this timer and so get the event also using MFC6.

After CHtmlEditor receives this event, it posts a message to its parent. I use WM_IDLEUPDATECMDUI for that, but you can use any other message that is not currently used for other purposes.

MSIE Context Menu

Internet Explorer displays a context menu when you right click into it. This is different in Browse mode and in Design mode. Here on The Code Project, you can find an article about how to modify or turn off this context menu, but this way is extremely complicated.

As I wrote above, I subclass the "Internet Explorer_Server" window and there I catch WM_CONTEXTMENU. There you can easily implement your own context menu via TrackPopupMenuEx() or turn it completely off.

Keyboard Shortcuts

In the same class (CMsieWnd), you can also adapt the way Internet Explorer reacts to keyboard shortcuts. You can either modify the default behaviour (e.g. CTRL-P for printing, CTRL-N to open a new window) or add your own additional shortcuts (e.g. CTRL-R to insert a new table row).

The Cleanup Function

DesktopNotes.gif

CHtmlEditor contains a very complex HTML cleanup functionality. Why is this? As I already wrote, I made this editor for my program ElmüSoft Desktop Organizer. There the user can enter any HTML content which will later be displayed on the desktop in a note. These desktop notes are displayed by an Internet Explorer Window that is positioned invisibly over the desktop, as you see above.

As this desktop organizer uses Java Script to move and open/close these notes, I cannot allow the user to enter his own JavaScript that might disturb the correct functioning. So there is a cleanup function in CHtmlEditor that removes any <SCRIPT>, <IFRAME>, <OBJECT>, etc blocks the user might have entered.

Additionally there are HTML tags which are not supported by the Internet Explorer editor. For example, <CENTER> centers a whole block of HTML code. However, the editor normally centers only line-by-line by using <DIV align=center>Text</DIV>. If you enter a <CENTER> tag, the editor will display the HTML content correctly, but the GUI button for the centered text will not be pressed and your users will be wondering. There are ONLY two places where illegal code may derive from:

  1. The user entered it in the source editor.
  2. The user copied it from a web page and pasted it into the HTML editor.

However, even if you do NOT paste or source edit, the cleanup function will remove useless empty tags like <u></u>. These may appear after typing, copying & pasting and deleting around for a while.

IMPORTANT: you will have to adapt the function cHtmlDocument::RecursiveCleanUpChilds() to your needs, but do NOT modify anything before you COMPLETELY understand how it works!!!

You could also completely prohibit source editing and pasting, but this is not a good idea. The user will become unable to copy HTML content that he has written on his own and paste it to another location in his document or duplicate it.

Finally

There are lots of other things I should explain here but, you will find all you need to know by studying the source code and its plentiful comments!

IMPORTANT: before you start, study the class DHtmlEditDemoDlg thoroughly to understand how to integrate CHtmlEditor into your project !!!!!!!!!

If you should find a bug, write me an email!

History

  • 9 November, 2005 -- Original version posted
  • 20 November, 2006 -- First update
  • 12 February, 2008 -- Second update
    • Version 4.2 released
    • Article content and downloads updated

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Elmue
Software Developer (Senior) ElmüSoft
Chile Chile
Member
Software Engineer since 27 years.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralMy vote of 5memberOAlpha4 Mar '13 - 14:46 
Good job~!~
QuestionThe RichEdit or Static control can not displaymembercoral952724 Sep '12 - 17:40 
In my dialog-based app, there's a button. When the button is clicked,the editor should display.
void CXXXDlg::OnButtonHtml() 
{
	CDHtmlEditDemoDlg dlg;
	int nResponse = dlg.DoModal();
	if (nResponse == IDOK)
	{
	}
	else if (nResponse == IDCANCEL)
	{
	}
}
Then the editor displays,but there's NO the Static or RichEdit control.The other controls in it are display normally.
I trace it in CDHtmlEditDemoDlg::OnInitDialog(),CHtmlEditor::CreateEditor(),and CHtmlEditor::CUniRichEdit::CreateEx(), They all return TRUE normally.
I try to add ::AfxInitRichEdit() in the CxxxApp::InitInstance(), the problem still exists.
Should i do something else? Could anyone help me?
AnswerRe: The RichEdit or Static control can not displaymemberElmue25 Sep '12 - 5:21 
Hello
 
Did you try the demo project ?
Do you see that it works fine there ?
 
Sadly your code snippet is !completely! useless.
So it is difficult to guess what is your error.
 
In CHtmlEditor::CreateEditor() the size of the Static control is used to resize the Html editor.
Then the static is hidden.
 
> but there's NO the Static or RichEdit control
 
So it is obvious that you don't see the static control.
It is always hidden.
 
The RichEdit control appears after calling pi_Editor->SetDisplayMode()
 
Please study the demo project THOROUGHLY and you will find your error.
 
Elmü
GeneralMy vote of 5membermaplewang16 Jul '12 - 22:43 
Thanks it is a good tool.
GeneralMy vote of 5memberMihai MOGA22 Jun '12 - 4:30 
This is a great inspiring article. I am pretty much pleased with your good work. You put really very helpful information. Keep it up once again.
GeneralMy vote of 5membermanoj kumar choubey23 Feb '12 - 19:34 
Nice
QuestionFix for IE 9.0memberBadJerry5 Jul '11 - 1:31 
Hi Elmue!
 
I suspect I am not on your Xmas card list because of the lack of feed back on the changes you have made... I apologise again!
 
In the mean time, accept a small contribution: your control stops working once you install IE9... there is an easy fix which I found online... I tested it on your code and it works (note also that you have a minor compilation error in Unicode in CHtmlEditor::cMisc::EncodeHtml)
 
Anyway for your control to work with IE9, simply add the virtual function
 
void CHtmlEditor::OnDocumentComplete(LPCTSTR lpszURL)
{
	mi_HtmDoc.SetDesignMode(true);
}
 

With all my gratitude,
Jerry
AnswerNo fix for IE 9.0 requiredmemberElmue6 Jul '11 - 16:39 
Hello
 
There is no problem with IE9.
It works seamlessly.
 

Your "fix" is completely wrong.
Normally the control allows the user to chose between browser mode and display mode.
If you switch always to design mode you break the functionality.
 
Elmü
AnswerNo fix for IE 9.0 requiredmemberElmue6 Jul '11 - 16:41 
Hello
 
There is no problem with IE9.
It works seamlessly.
 

Your "fix" is completely wrong.
Normally the control allows the user to chose between browser mode and design mode.
If you switch always to design mode you break the functionality.
 
Elmü
Generalhow to programmatically set the position of the caret in the text?membereu.gar13 May '11 - 3:03 
Hi!
 
I need a function that works like CEdit::SetSel(). What's the easiest way to do this?
GeneralRe: how to programmatically set the position of the caret in the text?memberElmue13 May '11 - 11:09 
Hello
 
I never did that but I suppose that you use IHTMLTxtRange for that.
Study the Sourcecode of cHtmlDocument::PasteIntoSelection(), cHtmlDocument::GetSelection(), cHtmlDocument::AddToSelection()
 
Elmü
GeneralRe: how to programmatically set the position of the caret in the text?membereu.gar16 May '11 - 19:42 
thanks for the reply! But cHtmlDocument::PasteIntoSelection(), cHtmlDocument::GetSelection(), cHtmlDocument::AddToSelection() suggest the presence of selected text in editor.. And I have such a situation: there is a cHtmlElement (for example <DIV id='selection'>some text</DIV>). I found it and now I need programmatically move the caret in the beginning of the text of this element. How to create a IHTMLSelectionObject with this cHtmlElement?
GeneralRe: how to programmatically set the position of the caret in the text?memberElmue17 May '11 - 6:24 
Hello
 
As I already wrote:
I don't know because I never did that!
 
With IHTMLTxtRange you can select text.
Try if setting start position == end position moves the cursor to where you want!
 
Elmü
GeneralRe: how to programmatically set the position of the caret in the text?membereu.gar18 May '11 - 6:12 
Thank you very much for your advice! I can't think of anything better than using IDM_SELECTALL and working with IHTMLTxtRange as you advised. Thanks again for a great class!
GeneralFind a problemmemberMember 9934045 Apr '11 - 16:20 
Thanks your class. I'm sorry for my english is poor. But i find a problem:
//I add a new dialog and class CAbout in project, and modify code so:
BOOL CDHtmlEditDemoApp::InitInstance()
{
#if _MSC_VER <= 1200 // Visual Studio 6
#error This project is a special version for Visual Studio 7 ONLY! Please download the Visual Studio 6 version from CodeProject.com!!
#endif
InitCommonControls();
CWinApp::InitInstance();
 
CAbout aboutDlg;
m_pMainWnd = &aboutDlg;
int nResponse = aboutDlg.DoModal();
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
}
/////////////
//And add code in the CAbout class:
void CAbout::OnBnClickedOk()
{
CDHtmlEditDemoDlg dlg;
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
}
else if (nResponse == IDCANCEL)
{
}
}

//Last HtmlView is not display in CDHtmlEditDemoDlg dlg. HtmlView is display in the about dialog. he,he!!!
// please tell me, how do i do?
// thanks!!!
AnswerRe: Find a problemmemberElmue6 Apr '11 - 5:33 
Hello
 
It seems that you are a MFC beginner.
I doubt that a HTML editor is the correct project for you to start with.
You should start with a simple project!
First learn how to program dialogs in MFC!
 
I recommend you to read the very good book "Professional MFC" that you can download from my homepage in the section eBooks.
 
What sense does this line make?
m_pMainWnd = &aboutDlg;
 
Elmü
GeneralHTML 4 and 5memberBadJerry4 Jan '11 - 7:27 
The World Wide Web Consortium (W3C) recommends lowercase in HTML 4, and demands lowercase tags in future versions of (X)HTML.
There is also the problems of </br> transformed into <br>
Is there any way to make your (great) editor compatible?
GeneralRe: HTML 4 and 5memberElmue4 Jan '11 - 10:57 
Hello
 
The recommendations of the W3C are for HTML that is published on websites.
 
But you will probably not use this simple HTML editor for creating websites that you will then upload to a server and publish them in the internet.
 
It is designed for internal use in applications that show simple HTML content in a nice manner and that allow the user to enter formatted text.
 
For web publishing use Dreamweaver or a simliar software!
 
Apart from that even in the far future webbrowsers will still understand <br> and uppercase HTML.
If they would not be downwards compatible, a 90% of the web would not work anymore.
For example the <b> tag has been deprecated since many years and it is recomended to use <strong> instead.
But still all browsers understand <b>.
 

Elmü
GeneralRe: HTML 4 and 5memberBadJerry4 Jan '11 - 23:27 
Hi Elmu,
 
Thanks for your reply - but I do use your (modified) control for letting users enter their own content on a published website - and it needs to be W3C compliant.. because most of the HTML generated code comes from the IHTMLDocumentN I was hoping that there would be some update soon from MS.
Alternatively, I could always parse the resulting HTML and deal with it but I was hoping there was an easier way - maybe a COM property hidden somewhere Wink | ;)
If you find anything in the future, do keep me posted!... and thanks for the good work!
 
Kind regards,
Jerry
GeneralRe: HTML 4 and 5memberElmue6 Jan '11 - 5:17 
Hello
 
Parsing the resulting HTML would be the most complicated way I can imagine.
It would result in a slow, clumsy and huge code that would be very error prone due to the complexity of HTML.
 
The work that must be done is replacing the outerHTML functionality of Internet Explorer that does not give you the result that you want.
 
The only way to do this correctly would be to take the tree of COM objects that are children of the <HTML> COM object and recursivly convert each of the COM objects into HTML code.
 
This must be done manually because outerHTML does not give the required result.
I think it should not be so difficult to add a function
CString cHtmlDomNode::ToHtmlW3C()
that recursivley converts the current tag and all child tags into W3C compliant HTML code.
 
I can investigate how complex such a code would be.
But as I did not yet invest much time into W3C requirements, could you send me a list of HTML examples how they come out of my code and how you need them to be ?
 
Do you know a tool that checks HTML code for W3C compliance and shows a list of errors ?
 
Elmü
GeneralRe: HTML 4 and 5memberBadJerry7 Jan '11 - 1:29 
I use http://validator.w3.org/[^] to validate the HTML...
The main problem comes from the fact that the control generates the tag <font> which is deprecated - I use <span> with the attribute style instead... and all tags should be lowercase.
 
I have written some code that does some of the parsing - let me know if you're interested... it's far from being robust at this stage!
GeneralRe: HTML 4 and 5memberElmue7 Jan '11 - 15:34 
Hello
 
I created a simple HTML code with DHTML Editor, made some modifications and now the W3C validator does not complain neither about the FONT tag nor about Uppercase.
 
If you put the code below into the W3C validator you dont get any error.
 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>TestPage</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<STYLE type="text/css">
BODY  { FONT-SIZE:14px; FONT-FAMILY:Arial; }
TABLE { FONT-SIZE:14px; FONT-FAMILY:Arial; }
</STYLE>
</head>
<BODY>
<DIV>Hello Test</DIV>
<DIV> </DIV>
<DIV><STRONG><EM><U><FONT style="BACKGROUND-COLOR: #aaffbb" color=red>sfgssdfsdfsdfsdfsdfsdfsd</FONT></U></EM></STRONG></DIV>
<UL>
<LI>
<DIV align=center><FONT style="BACKGROUND-COLOR: #aaffbb" color=red>fghfghfghfg</FONT></DIV></LI>
<LI>
<DIV align=center>
<HR>
</DIV></LI></UL>
<DIV>fghfghgfhfg</DIV>
</BODY></html>
 
Congratulations: The uploaded document was successfully checked as HTML 4.0 Transitional.
 
So could you please explain with more details what you exactly want ?
____________________________
 
Do you really think that you should care about these W3C recommendations?
Some years ago the Font tag was standard. (I have SelfHtml version 7 where nobody was seeing any bad in using FONT)
Today it is deprecated.
You use SPAN instead.
Tomorrow SPAN will be deprecated, too.
You will use any weird XML tag instead.
Then the W3C will change his opinion again and you keep on changing your code all day long?
Is this really worth the time that you waste with it ?
____________________________
 
It is correct that defining Styles in an external Style Sheet file has advantages over using inline definitions for colors or fonts.
 
But what should be better in
<SPAN style="color:red;">
than in:
<FONT color="red">
 
It is exactly the same !
There is no advantage or disadvantage.
 
And what should be better in
<style type="text/css">
than in:
<STYLE type="text/css">
 
I don't see any advantage or disadvantage.
For me this is bureaucracy.
 
Elmü
GeneralRe: HTML 4 and 5memberBadJerry10 Jan '11 - 1:54 
Elmu,
 
Yes it's ok for HTML 4.0.... not for the transitional html 5.0 and especially xhtml...
This is bureaucracy indeed... the ideas is is to separate meaning and style... and the philosophy is not so much which tag use... what they 're pushing is the use of style sheets instead of local formatting... and the tag font has been shot in the resulting cross-fire. But we are architects - software architects - but as real architects we have a duty to make our constructions accessible to anyone with a handicap...

There is a series of article quite interesting on web design... it's worth a read...
Starting at http://dev.opera.com/articles/view/1-introduction-to-the-web-standards-cur/[^]
 
Then skip to http://dev.opera.com/articles/view/4-the-web-standards-model-html-css-a/[^]
 
There are some info on validating here: http://dev.opera.com/articles/view/24-validating-your-html/[^]
 
And this is about accessibility
http://dev.opera.com/articles/view/25-accessibility-basics/[^]
 
I am not trying to preach here... I just have clients demanding standards... and I am being the devil's advocate!
 
Speak soon!
 
Jerry
GeneralRe: HTML 4 and 5memberElmue11 Jan '11 - 4:08 
Hello
 
This is a very detailed tutorial that you cite.
But it does not tell me many new things.
 
The question is: How do you want to realize a separation of Html and Styles in my HTML Editor?
 
If the user selects text and choses it to be red and bold, even in a HTML Editor that outputs XHTML code you will get all style information as inline tags and attributes.
But you will never get the Style information separated from the HTML information in an extra file.
And this would not even make sense in an editor where the user defines the style of his text at his own will.
 
How do you want to realize this ?
 
What I could do is add code to my class that replaces the FONT tag with a SPAN tag and outputs something like:
<span style="color:#FF0000;"><strong>Some bold red text</strong></span>
 
If all tags are lowercase and all single tags closed with a slash and FONT replaced with SPAN, would this be usefull for you?
 
Elmü
GeneralRe: HTML 4 and 5memberBadJerry12 Jan '11 - 3:59 
The short answer is.... yes... please! Wink | ;)
News! DHTML Editor version 5.0 released !memberElmue15 Jan '11 - 15:36 
Hello
 
Uff... The work is done.
It was much more work than I originally thought.
 
The new version 5.0 has a new button in the GUI which is labled "HTML 5".
It shows the HTML 5 source code which is recursively generated from the DOM tree.
 
The HTML code is generated completely by my class because Internet Explorer's outerHTML delivers quite ugly code that is not W3C conform -- it is not even indented.
 
You can even use the HTML 5 code generator to check for errors in HTML code.
If you have for example an erroneous code like
<table><tr><td></td></td></tr></table>
You will see an error message in the HTML 5 when loading a page with this error (not when entering this into the source editor).
 
You can also use the HTML 5 code generator to tidy up ugly code like for example the horrible HTML that you find in the Google page. What comes out is clean indented HTML code no matter what you put in.
 
Please download the new version, check it and tell me your results!
 
The new version is now available on Codeproject.
 
Elmü

modified on Tuesday, February 15, 2011 4:02 AM

GeneralRe: HTML 4 and 5memberElmue28 Jan '11 - 9:12 
Hello
 
This is really strange!
Two weeks passed now.
I invest my time to program a HTML 5 generator and you don't even answer!
 
Elmü
GeneralRe: HTML 4 and 5memberBadJerry28 Jan '11 - 10:52 
Elmu,
I sincerely apologise - I have been pulled on another "urgent" project which means I have no life. I *really* appreciate you took the time to do this... I have downloaded your code but I have not had the time to test it. I will do.
I am sorry again- I should have replied - that was disrespectful!
Will keep you posted! Hopefully soon!
 
Jerry
GeneralFont size problem.memberflyfish198627 Aug '10 - 16:35 
when i change the font size and write text into the editor ,font size will auto change 48.
GeneralRe: Font size problem. [modified]member0mni8 Oct '10 - 2:10 
+1 about IDM_FONTSIZESTYLE..
When I select something and then change fontsize (via IDM_FONTSIZESTYLE) - it changes size of selected block, but without changing sizes of inner blocks, for which i previously set another size. Also i get a very-very dirty code.
For example we print: text1 text2
Then change text2 to size 10 and then all this words together continously searching for nicer size (11,12,16,20)
<FONT style="FONT-SIZE: 11px"><FONT style="FONT-SIZE: 12px"><FONT style="FONT-SIZE: 16px"><FONT style="FONT-SIZE: 20px">text1 <FONT style="FONT-SIZE: 10px">text2</FONT></FONT></FONT></FONT></FONT>
 
Also if cursor is at the end of the line, or on new line and I call IDM_FONTSIZESTYLE with any desired size it successfully sets ExecSetCommand(IDM_FONTSIZE, _T("7"), but then dont changes it.
Trying to solve it by myself, but yet without any great success. May be there is a simple way?... Frown | :(
 
Now I just using IDM_FONTSIZE in similar way as http://tinymce.moxiecode.com/examples/full.php[^]
 
Btw, my users wanted "scaling of page" function to work with small fonts and here it is... may be this will help someone)
int size = 150;//percent
VARIANT vaZoomFactor; // Input argument
VariantInit(&vaZoomFactor);
V_VT(&vaZoomFactor) = VT_I4;
V_I4(&vaZoomFactor) = size;
 
ExecWB(OLECMDID_OPTICAL_ZOOM,OLECMDEXECOPT_DONTPROMPTUSER,&vaZoomFactor,NULL);
The game is over! Thanks for playing!
modified on Friday, October 8, 2010 9:26 AM

AnswerRe: Font size problem.mvpElmue8 Oct '10 - 16:53 
Hello
 
> it changes size of selected block, but without changing sizes of inner blocks
 
That is correct.
You can easily change that behaviour by eliminating the line
if (i_Parent.GetAttribute(s_Attr).GetLength())
in the funtcion cHtmlDocument::RecursiveSetFontSize()
(BUT without removing the content of this if block!!)
Then also the inner <FONT> will be changed.
 
> Also i get a very-very dirty code.
 
When you do a dirty work (like changing font size several times) you will get a dirty code.
This is normal. Even with professional tools like Dreamweaver you will have the same effect.
 
You could write a CleanUp function that searches for <FONT> inside of other <FONT> and eliminates them. But this is not that easy. When the inner font is <FONT color=green> then it must not be removed, while a <FONT style="font-size:15px"> or <FONT size=5> must be removed.
And a <FONT style="font-size:15px; color:red"> must only partially be removed!
 
When you start coding that you will notice that it is so extremely complicated that you will only lose your time with that effort. You will never finish that work. There will always stay cases where the code is not optimal. If you need perfect code the !ONLY! option is writing HTML by hand.
 
> Also if cursor is at the end of the line....
 
You change font size at the end of the line ?
Before changing font size you must always select the text that you want to change.
Otherwise it will obviously have no effect.
 
> int size = 150;//percent
> VARIANT vaZoomFactor; // Input argument
> VariantInit(&vaZoomFactor);
> V_VT(&vaZoomFactor) = VT_I4;
> V_I4(&vaZoomFactor) = size;
 
Why do you write such an awkward stuff?
Why don't you use the smart CComVariant as you can see in my code?
 
Instead of writing 5 lines of code you can put that in one line:
CComVariant v_Zoomfactor(150);
 
Elmü
GeneralRe: Font size problem.member0mni15 Oct '10 - 5:13 
Thank you for reply about inner blocks, but I decided not to fight with MS HTML in this case. Seven predefined sizes enough for me and also setting size before inputting text is also important. Imagine that someone dictates: "With a new line in a large font type...".
 
About CComVariant I 100% agree, but in my project VARIANT was more handy, so I just added int size to explain copy-paste from my code and that parameter is VT_I4 Smile | :) Btw, OLECMDID_OPTICAL_ZOOM works fine for me in VStudio 2010, but in VC 2003 only exists OLECMDID_ZOOM.
 
Alse I continue to fight and explore) Someone here asked how to make some content uneditable, and thats pretty easy: just add to block tag like div parameter contentEditable="false" and also useful thing unselectable="on"
 
Anyway big thanks for sharing this class - it was a great starting point!
The game is over! Thanks for playing!

QuestionHow to without any changes on open a html or other type file?memberguguqiaqiachina22 Jun '10 - 16:44 
How to without any changes on open a html or other type file when i switch view mode or source code mode?
 
I found your modify some tag when open a html file . for example: added <body>,<head> tags.
GeneralA image as a link is not work,can not open a filememberMember 263478317 Jun '10 - 16:53 
<A href="file:///d:/Src/video/myvideo.avi"><IMG border=0 alt=video\myvideo.avi src="file:///d:/Src/video.jpg" width=225 height=155></A>
 
In view mode ,click this image link ,editor can not open file "myvideo.avi" .
GeneralSwitch source mode, some html code lose....memberMember 26347831 Jun '10 - 22:56 
hi,
 
I have a html file. I opened it in this editor,but some content is deleted and modified.
following is html source code :
"
<div class="sectionMenuWrapper">
<span class="sectionMenuLeft">
<a href="4_3.htm">Previous Article</a>
</span>
<span class="sectionMenuRight">
<a href="11_1.htm">Next Article</a>
</span>
</div>
"
 
after,following is deleted and modified html source code:
"
<DIV><SPAN><A href="4_3.htm">Previous Article</A> </SPAN><SPAN><A href="11_1.htm">Next Article</A> </SPAN></DIV>
"
How can i not delete or modify html source code when i switch source mode in editor ?
 
thanks.
GeneralRe: Switch source mode, some html code lose....mvpElmue2 Jun '10 - 3:09 
Hello
 
You are filling the discussion area with your questions since some weeks.
Im sorry that I dont have the time to give you gratis programming lessons.
Please study the code!
 
I have the impression that the HTML editor is too complex for your current programming skills.
May be you start with something more simple ?
 
Sorry I have very few time and cannot answer another question every day!
 
Elmü
GeneralRe: Switch source mode, some html code lose....memberMember 26347832 Jun '10 - 5:57 
ok,stop talk about this topic.
QuestionHow to open or edit XML file?memberMember 263478330 May '10 - 17:25 
^_^
 
I use code "pi_Editor->Navigate(_T("d:\\test.xml"));" to open a xml file.
But editor notice me following message:
"The XML page cannot be displayed
Cannot view XML input using style sheet. Please correct the error and then click the Refresh button, or try again later. "
 
Need i to remove the style css structure?
 
thanks.
AnswerRe: How to open or edit XML file?mvpElmue31 May '10 - 2:51 
It does not make much sense to open a XML file in a HTML editor !
You must turn off ALL the code that manipulates the document.
I never tested that because it is nonsense.
GeneralRe: How to open or edit XML file?memberMember 26347831 Jun '10 - 22:58 
OK, thanks .
GeneralAdd message "WM_DROPFILES" for drag image to editor,but can not paste html at the cursor position in function PasteIntoSelectionmemberMember 263478323 May '10 - 6:48 
Hello,
 
I add a message "WM_DROPFILES" for drag image to my editor.but i can not paste html at the cursor position in function PasteIntoSelection.(The currently not selected text/controls) .
 
void CDHtmlEditDemoDlg::OnDropFiles(HDROP hDropInfo)
{
.......
if(s_FilePath.Right(3) == _T("jpg")
{
CString s_Html = _T("<img src=\"");
s_Html += szNextFile;
s_Html += _T("\">");
BOOL b_SelOK = pi_Editor->GetDocument()->PasteIntoSelection(s_Html); // (The currently not selected text/controls),in editor design mode,that can not paste this html to cursor,but paste it to the first line .
}
......
}
 

How can i paste html to cursor when i use WM_DROPFILES to drag image file?
thanks .
GeneralRe: Add message "WM_DROPFILES" for drag image to editor,but can not paste html at the cursor position in function PasteIntoSelectionmvpElmue24 May '10 - 2:38 
Hello
 
HTML does not store an image like you would do in Word or in a PDF document.
HTML only stores the URL or the relative path on disk of the image.
 
How do you get the path of the document ?
 
Elmü

 

GeneralRe: Add message "WM_DROPFILES" for drag image to editor,but can not paste html at the cursor position in function PasteIntoSelection [modified]memberMember 263478325 May '10 - 5:52 
Thanks your reply.
I think you do not know what I mean.
I can get image file path . I do not store the image in the editor.
Because i want to show a image in the editor when i drag a image file to editor. So i use windows message WM_DROPFILES for get image relative path and then make a html sentence.
 
first:
I set the dialog IDD_MAIN_DIALOG "Accept files" is True.
Added the WM_DROPFILES message function "void OnDropFiles(HDROP hDropInfo)".
And then write following code :

void CDHtmlEditDemoDlg::OnDropFiles(HDROP hDropInfo)
{
UINT uNumFiles;
TCHAR szNextFile [MAX_PATH];
SHFILEINFO rFileInfo;
HANDLE hFind;
WIN32_FIND_DATA rFind;
TCHAR szFileLen [64];

uNumFiles = DragQueryFile ( hDropInfo, -1, NULL, 0 );
for ( UINT uFile = 0; uFile < uNumFiles; uFile++ )
{
if ( DragQueryFile ( hDropInfo, uFile, szNextFile, MAX_PATH ) > 0 )
{
 
SHGetFileInfo ( szNextFile, 0, &rFileInfo, sizeof(rFileInfo),
SHGFI_SYSICONINDEX | SHGFI_ATTRIBUTES |
SHGFI_TYPENAME );//szNextFile store the drag image file path!!!.
 
hFind = FindFirstFile ( szNextFile, &rFind );
if ( INVALID_HANDLE_VALUE != hFind )
{
StrFormatByteSize ( rFind.nFileSizeLow, szFileLen, 64 );
FindClose ( hFind );
}
}
}
DragFinish ( hDropInfo );
 
CDialog::OnDropFiles(hDropInfo);
}

 
"szNextFile" is store the image file path .
so i write following code added to function "OnDropFiles":

......
CString s_Html = _T("<img src=\"");
s_Html += szNextFile;
s_Html += _T("\" alt=\"this is a image\">");
BOOL b_SelOK = pi_Editor->GetDocument()->PasteIntoSelection(s_Html);// can not paste this html at the cursor position in design mode ,but paste this html at first line in this editor.??????? ^_^.....
......

 
How can i paste this html(stored in variable s_Html) at the cursor position in editor when i drag a image file to editor ?
thanks.

modified on Tuesday, May 25, 2010 9:12 PM

GeneralRe: Add message "WM_DROPFILES" for drag image to editor,but can not paste html at the cursor position in function PasteIntoSelectionmvpElmue25 May '10 - 17:50 
Very weird code what you post here!
Additionally you should use the button "Code Block" when you post code here!
 

I tried the following:
 
void CDHtmlEditDemoDlg::OnWmCommand(WPARAM wParam)
{
    CString s_NewTable = _T("<img src=\"C:\\Temp\\Test.gif\">");
    .....
    // The rest unchanged.
 
When I click the button "New Table" the image is inserted perfectly at the cursor position.
Does this work for you, too?
 
I suppose that it does.
So the problem probably has to do with the fact that Internet Explorer already has a Drop Target registered. So you probably have to avoid that the WM_DROPFILES message gets to Internet Explorer.
 
See MSDN: RegisterAsDropTarget()
 
But is this worth the work ???
Why dont you simply use the editors "add image" button to insert images ?
 

Elmü

 

GeneralRe: Add message "WM_DROPFILES" for drag image to editor,but can not paste html at the cursor position in function PasteIntoSelectionmemberMember 263478330 May '10 - 5:11 
Oh, sorry my code .
 
Again thanks your reply .
 
Button click can add html to curror position.
 
Because my boss hope to drag target to editor then add code to curror positon. so i will search some knowledge for RegisterAsDropTarget() .
GeneralWhen use IDM_SAVE can not work .memberMember 263478313 May '10 - 23:58 
hi,
 
void CHtmlEditor::Save()
{
// You can make the path a variable on your own here
if (!ExecSetCommand(IDM_SAVE, L"d:\\DhtmlEdit.htm"))
{
gp_Parent->MessageBox(_T("Error saving the file"), _T("Error"), MB_ICONSTOP);
}
}
 

but, file "DhtmlEdit.htm" is not save .IDM_SAVEAS is correctly.
 
How can i get filename after this file saved when use IDM_SAVEAS?
 
thanks.
GeneralRe: When use IDM_SAVE can not work .mvpElmue14 May '10 - 14:34 
I have no idea what you are talking about ?
 
> but, file "DhtmlEdit.htm" is not save
 
What do you want to say with that ?
 
I think the problem is your english.
 
Elmü


GeneralRe: When use IDM_SAVE can not work .memberMember 263478314 May '10 - 16:24 
OK,thanks your reply.
 
But,in save function,i use IDM_SAVE replace IDM_SAVEAS,the save function looks like fine ,but it isn't save file .
void CHtmlEditor::Save()
{
if (!ExecSetCommand(IDM_SAVE,L"D:\\DhtmlEdit111.htm")) //SUCCESS,but not saved file.
{
gp_Parent->MessageBox(_T("Error saving the file"), _T("Error"), MB_ICONSTOP);//can not run.
}
}
 
Because i want to get saved file name. Use IDM_SAVE in save function ,i can set a know file name ,later i can use this file name for other functions.^_^.....
 
How can i get file name after saved file (use IDM_SAVEAS in save function)?
 
very thanks.
NewsHow to save a documentmvpElmue16 May '10 - 8:49 
Hello
 
IDM_SAVEAS:
normally will open a window (OpenFileDialog) where the user can select a folder and a file location where he wants to save the HTML file.
See MSDN - IDM_SAVEAS.
The MSDN says that this depends on the flag OLECMDEXECOPT_DODEFAULT or MSOCMDEXECOPT_DONTPROMPTUSER beeing set in cHtmlDocument::ExecSetCommand()
But I found that the flag is ignored. It seems that MSOCMDEXECOPT_DONTPROMPTUSER does not work.
 

IDM_SAVE:
will save the (modified) editor content to the SAME location from where the document has been opened.
 
Try the following: (after replacing IDM_SAVEAS with IDM_SAVE)
1.) Turn OFF Design Mode
2.) Into the URL textbox enter a local path (e.g. C:\Test.htm)
3.) Hit Enter or click "Load" (now the local file will open in the editor)
4.) Switch to Design Mode
5.) Modify the HTML content
6.) Click "Save" (wich executes IDM_SAVE)
 
Now you will find the modified file AT THE SAME LOCATION (C:\Test.htm) on your disk.
 
Note that IDM_SAVE does not take any second parameter.
Any path you pass to IDM_SAVE will be IGNORED!
 
IDM_SAVE is only usefull after opening a local file.
If you start the program directly in edit mode your current file location is "about:blank".
about:blank can NOT be saved with IDM_SAVE.
 
> How can i get file name after saved file (use IDM_SAVEAS in save function)?
 
This is very easy: After executing IDM_SAVEAS:
CString s_Path = mi_HtmDoc.GetUrl();
 
Elmü

 

GeneralRe: How to save a documentmemberMember 263478316 May '10 - 16:53 
Thanks so much !!! ^_^.....

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 14 Feb 2011
Article Copyright 2005 by Elmue
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid