documentor: Fixed editing status on failed write
[sdk] / documentor / src / Documentor.ec
index 337d117..2cc1a7c 100644 (file)
@@ -67,8 +67,8 @@ static void _PrintType(Type type, char * string, bool printName, bool printFunct
                {
                   if(type._class.registered)
                   {
-                     char hex[10];
-                     sprintf(hex, "%08x", type._class.registered);
+                     char hex[20];
+                     sprintf(hex, "%p", type._class.registered);
                      strcat(string, "<a href=\"api://");
                      strcat(string, hex);
                      strcat(string, "\" style=\"text-decoration: none;\">");
@@ -76,7 +76,7 @@ static void _PrintType(Type type, char * string, bool printName, bool printFunct
                      strcat(string, "</a>");
                   }
                   else
-                     strcat(string, type._class.string);                     
+                     strcat(string, type._class.string);
                }
             }
             break;
@@ -106,7 +106,7 @@ static void _PrintType(Type type, char * string, bool printName, bool printFunct
                   DocPrintType(param, string, false, fullName);
                   if(param.next) strcat(string, ", ");
                }
-               strcat(string, ")");               
+               strcat(string, ")");
             }
             else*/
             {
@@ -251,7 +251,7 @@ static void _PrintType(Type type, char * string, bool printName, bool printFunct
                   DocPrintType(param, string, false, fullName);
                   if(param.next) strcat(string, ", ");
                }
-               strcat(string, ")");               
+               strcat(string, ")");
             }
             else*/
             {
@@ -302,7 +302,7 @@ static void _PrintType(Type type, char * string, bool printName, bool printFunct
          case subClassType:
             strcat(string, "subclass(");
             strcat(string, type._class ? type._class.string : "int");
-            strcat(string, ")");                  
+            strcat(string, ")");
             break;
          default:
             printf("");
@@ -539,7 +539,7 @@ static char * ReadDoc(Module module, DocumentationType type, void * object, Docu
       {
          contents = new char[len+1];
          file.Read(contents, 1, len);
-         contents[len] = '\0';      
+         contents[len] = '\0';
       }
       delete file;
    }
@@ -549,7 +549,7 @@ static char * ReadDoc(Module module, DocumentationType type, void * object, Docu
       for(c = 0; contents[c]; c++)
          if(!isspace(contents[c])) break;
       if(!contents[c])
-         delete contents;      
+         delete contents;
    }
    if(editing && !contents)
       contents = CopyString($"[Add Text]");
@@ -585,7 +585,7 @@ class APIPageNameSpace : APIPage
       {
          f.Printf("<FONT FACE=\"Arial\" SIZE=\"6\">%s</FONT><br><br>\n", nsName );
          tag = (uint)nameSpace;
-         f.Printf($"Module: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
+         f.Printf($"Module: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
       }
       else
       {
@@ -604,7 +604,7 @@ class APIPageNameSpace : APIPage
          ns = ns->parent;
       }
       if(nsName[0]) 
-         f.Printf($"Parent namespace: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", nameSpace->parent, nsName);
+         f.Printf($"Parent namespace: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", nameSpace->parent, nsName);
 
       f.Printf("<br>");
       {
@@ -639,7 +639,7 @@ class APIPageNameSpace : APIPage
                first = false;
             }
             f.Printf("<TR>");
-            f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", iconNames[typeNameSpace], ns, ns->name);
+            f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%p\" style=\"text-decoration: none;\">%s</a></TD>", iconNames[typeNameSpace], ns, ns->name);
             if(desc)
             {
                if(editing)
@@ -679,7 +679,7 @@ class APIPageNameSpace : APIPage
 
                f.Printf("<TR>");
 
-               f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", (cl.type == enumClass || cl.type == unitClass || cl.type == systemClass) ? iconNames[typeDataType] : iconNames[typeClass], cl, cl.name);
+               f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%p\" style=\"text-decoration: none;\">%s</a></TD>", (cl.type == enumClass || cl.type == unitClass || cl.type == systemClass) ? iconNames[typeDataType] : iconNames[typeClass], cl, cl.name);
                if(desc)
                {
                   if(editing)
@@ -693,7 +693,7 @@ class APIPageNameSpace : APIPage
                   else
                      f.Printf("<TD valign=top height=22>%s</TD>", desc);
                   delete desc;
-               }            
+               }
                f.Printf("</TR>\n");
             }
          }
@@ -717,7 +717,7 @@ class APIPageNameSpace : APIPage
                first = false;
             }
             f.Printf("<TR>");
-            f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", iconNames[typeMethod], function, name);
+            f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%p\" style=\"text-decoration: none;\">%s</a></TD>", iconNames[typeMethod], function, name);
             if(desc)
             {
                if(editing)
@@ -752,7 +752,7 @@ class APIPageNameSpace : APIPage
                first = false;
             }
             f.Printf("<TR>");
-            f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", def, iconNames[typeData], def.name);
+            f.Printf("<TD valign=top height=22 nowrap=1><a name=%p></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", def, iconNames[typeData], def.name);
             f.Printf("<TD valign=top height=22>%s</TD>", def.value);
             if(desc)
             {
@@ -775,7 +775,7 @@ class APIPageNameSpace : APIPage
       }
 
       f.Printf("</FONT></BODY></HTML>\n");
-   }   
+   }
 }
 
 class APIPageClass : APIPage
@@ -805,9 +805,9 @@ class APIPageClass : APIPage
       f.Printf($"<HTML><HEAD><TITLE>API Reference</TITLE></HEAD>\n<BODY><FONT SIZE=\"3\">\n");
       f.Printf("<FONT FACE=\"Arial\" SIZE=\"6\">%s</FONT><br><br>\n", name);
 
-      f.Printf($"Module: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
+      f.Printf($"Module: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
       if(nsName[0]) 
-         f.Printf($"Namespace: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", cl.nameSpace, nsName);
+         f.Printf($"Namespace: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", cl.nameSpace, nsName);
 
       {
          char * classType = null;
@@ -848,7 +848,7 @@ class APIPageClass : APIPage
          else if(cl.type == enumClass && !strcmp(cl.base.name, "enum"))
             f.Printf("%s", cl.dataTypeString);
          else
-            f.Printf("<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a>", cl.base, cl.base.name);
+            f.Printf("<a href=\"api://%p\" style=\"text-decoration: none;\">%s</a>", cl.base.templateClass ? cl.base.templateClass : cl.base, cl.base.name);
          f.Printf("<br>\n");
       }
 
@@ -910,10 +910,10 @@ class APIPageClass : APIPage
                      dataClass = base.dataType._class ? base.dataType._class.registered : null;
                }
                else
-                  dataClass = base;                  
+                  dataClass = base;
                
                f.Printf("<TR>");
-               f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", item, iconNames[typeEnumValue], item.name);
+               f.Printf("<TD valign=top height=22 nowrap=1><a name=%p></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", item, iconNames[typeEnumValue], item.name);
                if(dataClass.type == systemClass)
                {
                   needClass = false;
@@ -965,7 +965,7 @@ class APIPageClass : APIPage
                string[0] = 0;
                DocPrintType(type, string, true, false);
                
-               f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", prop, iconNames[typeDataType], string);
+               f.Printf("<TD valign=top height=22 nowrap=1><a name=%p></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", prop, iconNames[typeDataType], string);
                if(desc)
                {
                   if(editing)
@@ -1013,7 +1013,7 @@ class APIPageClass : APIPage
                   string[0] = 0;
                   DocPrintType(prop.dataType, string, true, false);
 
-                  f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", prop, iconNames[typeProperty], prop.name);
+                  f.Printf("<TD valign=top height=22 nowrap=1><a name=%p></a><img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", prop, iconNames[typeProperty], prop.name);
                   f.Printf("<TD valign=top height=22 nowrap=1>%s</TD>", string);
                   if(desc)
                   {
@@ -1060,7 +1060,7 @@ class APIPageClass : APIPage
                   ProcessMethodType(method);
 
                f.Printf("<TR>");
-               f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", method.dataType.thisClass ? iconNames[typeEvent] : iconNames[typeMethod], method, method.name);
+               f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%p\" style=\"text-decoration: none;\">%s</a></TD>", method.dataType.thisClass ? iconNames[typeEvent] : iconNames[typeMethod], method, method.name);
                if(desc)
                {
                   if(editing)
@@ -1099,7 +1099,7 @@ class APIPageClass : APIPage
                   ProcessMethodType(method);
 
                f.Printf("<TR>");
-               f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a></TD>", iconNames[typeMethod], method, method.name);
+               f.Printf("<TD valign=top height=22 nowrap=1><img valign=center src=\"%s\">&nbsp;&nbsp;<a href=\"api://%p\" style=\"text-decoration: none;\">%s</a></TD>", iconNames[typeMethod], method, method.name);
                if(desc)
                {
                   if(editing)
@@ -1202,8 +1202,8 @@ class APIPageClass : APIPage
                }
                else
                   f.Printf(", ");
-               f.Printf("<a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a>", deriv, deriv.name);
-             }            
+               f.Printf("<a href=\"api://%p\" style=\"text-decoration: none;\">%s</a>", deriv, deriv.name);
+             }
          }
          if(!first)
             f.Printf("<br><br>\n");
@@ -1228,7 +1228,7 @@ class APIPageClass : APIPage
          }
       }
       f.Printf("</FONT></BODY></HTML>\n");
-   }   
+   }
 }
 
 class APIPageMethod : APIPage
@@ -1256,17 +1256,17 @@ class APIPageMethod : APIPage
       f.Printf($"<HTML><HEAD><TITLE>API Reference</TITLE></HEAD>\n<BODY><FONT SIZE=\"3\">\n");
       f.Printf("<FONT FACE=\"Arial\" SIZE=\"6\">%s</FONT><br><br>\n", name);
 
-      f.Printf($"Module: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
+      f.Printf($"Module: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
       if(nsName[0])
-         f.Printf($"Namespace: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", cl.nameSpace, nsName);
-      f.Printf("Class: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", cl, cl.name);
+         f.Printf($"Namespace: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", cl.nameSpace, nsName);
+      f.Printf("Class: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", cl, cl.name);
       if(method.dataType.staticMethod)
       {
          f.Printf($"this pointer class: None<br>\n");
       }
       else if(method.dataType.thisClass && method.dataType.thisClass.registered && (method.dataType.thisClass.registered != method._class || method.type == virtualMethod))
       {
-         f.Printf($"this pointer class: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", method.dataType.thisClass.registered, method.dataType.thisClass.registered.name);
+         f.Printf($"this pointer class: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", method.dataType.thisClass.registered, method.dataType.thisClass.registered.name);
       }
 
       // Generate Method Page
@@ -1451,7 +1451,7 @@ class APIPageMethod : APIPage
          }
       }
       f.Printf("</FONT></BODY></HTML>\n");
-   }   
+   }
 }
 
 class APIPageFunction : APIPage
@@ -1478,17 +1478,17 @@ class APIPageFunction : APIPage
       f.Printf($"<HTML><HEAD><TITLE>API Reference</TITLE></HEAD>\n<BODY><FONT SIZE=\"3\">\n");
       f.Printf("<FONT FACE=\"Arial\" SIZE=\"6\">%s</FONT><br><br>\n", name);
 
-      f.Printf($"Module: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
+      f.Printf($"Module: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", (module && module.name) ? module : null, (!module || !module.name || !strcmp(nsName, "ecere::com")) ? "ecereCOM" : module.name);
 
       if(nsName[0])
-         f.Printf($"Namespace: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", function.nameSpace, nsName);
+         f.Printf($"Namespace: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", function.nameSpace, nsName);
 
       if(!function.dataType)
          function.dataType = ProcessTypeString(function.dataTypeString, false);
 
       if(function.dataType.thisClass && function.dataType.thisClass.registered)
       {
-         f.Printf($"this pointer class: <a href=\"api://%08x\" style=\"text-decoration: none;\">%s</a><br>\n", function.dataType.thisClass.registered, function.dataType.thisClass.registered.name);
+         f.Printf($"this pointer class: <a href=\"api://%p\" style=\"text-decoration: none;\">%s</a><br>\n", function.dataType.thisClass.registered, function.dataType.thisClass.registered.name);
       }
 
       // Generate Method Page
@@ -1670,7 +1670,7 @@ class APIPageFunction : APIPage
          }
       }
       f.Printf("</FONT></BODY></HTML>\n");
-   }   
+   }
 }
 
 static void AddNameSpace(DataRow parentRow, Module module, NameSpace mainNameSpace, NameSpace comNameSpace, char * parentName, bool showPrivate)
@@ -1765,7 +1765,7 @@ static void AddNameSpace(DataRow parentRow, Module module, NameSpace mainNameSpa
                   if(!functionsRow) { functionsRow = row.AddRow(); functionsRow.SetData(null, APIPage { $"Functions", page = page }); functionsRow.collapsed = true; functionsRow.icon = mainForm.icons[typeMethod];  functionsRow.tag = 2; };
                   fnRow = functionsRow.AddRow(); fnRow.SetData(null, APIPageFunction { name, function = fn }); fnRow.icon = mainForm.icons[typeMethod]; fnRow.tag = (int)fn;
                }
-            }            
+            }
          }
       }
    }
@@ -1788,7 +1788,7 @@ static void AddNameSpace(DataRow parentRow, Module module, NameSpace mainNameSpa
                   if(!definesRow) { definesRow = row.AddRow(); definesRow.SetData(null, APIPage { $"Definitions", page = page }); definesRow.collapsed = true; definesRow.icon = mainForm.icons[typeData]; definesRow.tag = 3; };
                   defRow = definesRow.AddRow(); defRow.SetData(null, APIPage { name, page = page }); defRow.icon = mainForm.icons[typeData]; defRow.tag = (int)def;
                }
-            }            
+            }
          }
       }
    }
@@ -1805,7 +1805,7 @@ static void AddDataMemberToPage(File f, DataMember member, int indent, bool show
    string[0] = 0;
    DocPrintType(member.dataType, string, true, false);
 
-   f.Printf("<TD valign=top height=22 nowrap=1><a name=%08x></a>", member);
+   f.Printf("<TD valign=top height=22 nowrap=1><a name=%p></a>", member);
    for(c = 0; c<indent; c++)
       f.Printf("&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;");
    f.Printf("<img valign=center src=\"%s\">&nbsp;&nbsp;%s</TD>", iconNames[typeData], member.name ? member.name : ((member.type == structMember) ? "(struct)" : "(union)"));
@@ -1965,9 +1965,9 @@ static void AddClass(DataRow parentRow, Module module, Class cl, char * nsName,
       NamedLink item;
       for(item = enumeration.values.first; item; item = item.next)
       {
-         DataRow mRow;                                                                                                                                                                                      
+         DataRow mRow;
          if(!enumRow) { enumRow = row.AddRow(); enumRow.SetData(null, APIPage { $"Enumeration Values", page = page }); enumRow.collapsed = true; enumRow.icon = mainForm.icons[typeEnumValue]; enumRow.tag = 8; }
-         mRow = enumRow.AddRow(); mRow.SetData(null, APIPage { item.name, page = page }); mRow.icon = mainForm.icons[typeEnumValue];         
+         mRow = enumRow.AddRow(); mRow.SetData(null, APIPage { item.name, page = page }); mRow.icon = mainForm.icons[typeEnumValue];
          mRow.tag = (int)item;
       }
    }
@@ -2029,6 +2029,7 @@ class MainForm : Window
       bool NotifySelect(MenuItem selection, Modifiers mods)
       {
          SettingsDialog { master = this }.Modal(); // Open the settings dialog to allow the user to change the directory for the eCdoc files
+         return true;
       }
    };
    MenuDivider { fileMenu };
@@ -2127,8 +2128,8 @@ class MainForm : Window
                   case 8: view.GoToAnchor("EnumerationValues"); break;
                   default:
                   {
-                     char hex[10];
-                     sprintf(hex, "%08x", row.tag);
+                     char hex[20];
+                     sprintf(hex, "%p", row.tag);
                      view.GoToAnchor(hex);
                   }
                }
@@ -2239,10 +2240,10 @@ class HelpView : HTMLView
       GetLastDirectory(location, fileName);
       StripLastDirectory(location, directory);
       archive = ArchiveOpen(archiveFile, { true } );
-      if(archive)
+      // if(archive)
       {
          TempFile f { };
-         ArchiveDir dir = archive.OpenDirectory(directory, null, replace);
+         ArchiveDir dir = archive ? archive.OpenDirectory(directory, null, replace) : null;
          Block block;
          bool empty = true;
          for(block = textBlock.parent.subBlocks.first; block; block = block.next)
@@ -2264,7 +2265,8 @@ class HelpView : HTMLView
             }
          }
          f.Seek(0, start);
-         dir.AddFromFile(fileName, f, null, replace, 0, null, null);
+         if(dir)
+            dir.AddFromFile(fileName, f, null, replace, 0, null, null);
          delete dir;
          delete archive;
          delete f;
@@ -2282,7 +2284,7 @@ class HelpView : HTMLView
             parent.subBlocks.Add(textBlock);
          }
 
-         edit = false;            
+         edit = false;
          if(created)
          {
             ComputeMinSizes();
@@ -2295,356 +2297,118 @@ class HelpView : HTMLView
 
    bool OnLeftButtonDown(int x, int y, Modifiers mods)
    {
-      if(edit)
-      {
-         // Update overLink
-         HTMLView::OnMouseMove(x, y, mods);
-         if(textBlock && overLink == textBlock.parent)
-         {
-            selPosition = curPosition = TextPosFromPoint(x, y, &textBlock);
-            PositionCaret(true);            
-         }
-         else
-         {
-            SaveEdit();
-            HTMLView::OnLeftButtonDown(x, y, mods);
-         }
-         return true;
-      }
-      return HTMLView::OnLeftButtonDown(x, y, mods);
-   }
+      bool result = true;
 
-   bool OnLeftButtonUp(int x, int y, Modifiers mods)
-   {
-      if(!edit || !textBlock || clickedLink != textBlock.parent)
+      if(edit && (!textBlock || overLink != textBlock.parent))
       {
-         HTMLView::OnLeftButtonUp(x, y, mods);
-         if(edit)
-         {
-            selPosition = curPosition = TextPosFromPoint(x, y, &textBlock);
-            PositionCaret(true);
-         }
-      }
-      return true;
-   }
-
-   // Returns true if it needs scrolling
-   /*
-   bool FindMouse(int px, int py, int * tx, int * ty, EditLine * tline, bool half)
-   {
-      int w;
-      int c;
-      int x, y;
-      EditLine line;
-      bool needHScroll = false;
-
-      if(py < 0)
-      {
-         if(this.viewY > 0)
-         {
-            y = this.viewY-1;
-            line = this.viewLine ? (void *)this.viewLine.prev : null;
-         }
-         else
-         {
-            y = 0;
-            line = (void *)this.lines.first;
-         }
+         SaveEdit();
+         HTMLView::OnLeftButtonDown(x, y, mods);
+         selPosition = curPosition = 0;
+         selBlock = textBlock;
+         Update(null);
       }
       else
-      {
-         py = Min(py, clientSize.h);
-         py /= this.space.h;
-         py = Min(py, this.lineCount);
-         y = this.viewY;
-         for(c = 0, line = this.viewLine; (line != (void *)this.lines.last && c<py); line = line.next, c++)
-         {
-            y++;
-         }
-      }
-
-      if( (px >= clientSize.w || px < clientSize.w/2) && this.viewX)
-         needHScroll = true;
-      px = Max(px,0);
-      px = Min(px,clientSize.w+this.space.w);
-
-      if(tx && line)
-      {
-         *tx = AdjustXPosition(line, px + viewX, half, null, MAXINT, 0);
-      }
-
-      if(tline) *tline = line;
-      if(ty) *ty = y;
-
-      // Prevent divide by 0 from non valid this.font
-      if(!this.space.h)
-         return (y < this.viewY) || needHScroll;
-      else
-         return (y < this.viewY || y >= this.viewY + clientSize.h / this.space.h) || needHScroll;
-      return false;
-   }
-*/
-/*
-   bool OnLeftButtonDown(int mx, int my, Modifiers mods)
-   {
-      int x,y;
-      EditLine line;
-
-      if(style.noSelect) return true;
-
-      if(!mods.isActivate)
-      {
-         Capture();
-         mouseSelect = true;
-      }
-
-      mouseX = mx - XOFFSET;
-      mouseY = my;
-
-      FindMouse(mouseX, mouseY, &x, &y, &line, true);
+         result = HTMLView::OnLeftButtonDown(x, y, mods);
 
-      if(!style.readOnly)
+      if(!edit && clickedLink)
       {
-         if(wordSelect)
-            mouseMove = false;
-         else if(IsMouseOnSelection() && !mods.isActivate)
+         ReleaseCapture();
+         if(clickedLink == overLink && clickedLink.href)
          {
-            DirtyLine(this.y);
-            mouseMove = true;
-            dropX = x;
-            dropY = y;
-            dropLine = line;
+            if(OnOpen(clickedLink.href))
+               Update(null);
          }
       }
 
-      if(!mouseMove && !wordSelect && (!mods.isActivate || style.multiLine))
+      if(edit)
       {
-         if(mods.shift && !mods.isActivate)
-         {
-            this.x = x;
-            this.y = y;
-            this.line = line;
-            DirtyAll();
-         }
-         else
+         // Update overLink
+         if(textBlock && overLink == textBlock.parent)
          {
-            SelDirty();
-            DirtyLine(this.y);
-            this.x = x;
-            this.y = y;
-            this.line = line;
-            DirtyLine(this.y);
-            this.selLine = this.line;
-            this.selX = this.x;
-            this.selY = this.y;
-            //Deselect();
+            selPosition = curPosition = TextPosFromPoint(x, y, &textBlock, true);
+            selBlock = textBlock;
+            PositionCaret(true);
+            selecting = true;
+            Update(null);
          }
-         ComputeColumn();
       }
-      
-      UpdateDirty();
-      UpdateCaretPosition(true);
-      return true;
+      return result;
    }
-*/
-/*
+
    bool OnLeftButtonUp(int x, int y, Modifiers mods)
    {
-      timer.Stop();
-
-      mouseSelect = false;
-      wordSelect = false;
-      
-      x -= XOFFSET;
-      
-      ReleaseCapture();
-      if(!style.readOnly)
+      if(!edit || !textBlock || clickedLink != textBlock.parent)
       {
-         if(mouseMove)
-         {
-            EditLine line;
-            FindMouse(mouseX, mouseY, &x, &y, &line, true);
-    
-            dropX = x;
-            dropY = y;
-            dropLine = line;
-
-            mouseMove = IsMouseOnSelection();
-
-            if(!mouseMove)
-            {
-               int size = SelSize();
-               if(size)
-               {
-                  char * text = new char[size+1];
-                  if(text)
-                  {
-                     int moveX = 0;
-                     GetSel(text, false);
-
-                     if(Max(selY, this.y) == dropY)
-                     {
-                        if(this.x > selX)
-                        {
-                           if(this.dropX > this.selX)
-                              moveX = this.x - this.selX;
-                        }
-                        else
-                        {
-                           if(this.dropX > this.x)
-                              moveX = this.selX - this.x;
-                        }
-                     }
-                     DelSel(null);
-                     this.dropX -= moveX;
-                     this.selX = this.x = this.dropX;
-                     this.selY = this.y = this.dropY;
-                     this.selLine = this.line = this.dropLine;
-                     AddS(text);
-                     SetViewToCursor(true);
-                     delete text;
-                     Modified();
-                  }
-               }
-            }
-            else
-            {
-               SelDirty();
-               DirtyLine(this.y);
-               this.x = x;
-               this.y = y;
-               this.line = line;
-               ComputeColumn();
-               DirtyLine(this.y);
-               Deselect();
-               UpdateDirty();
-            }
-         }
-         else
+         HTMLView::OnLeftButtonUp(x, y, mods);
+         if(edit)
          {
-            EditLine line;
-            mouseX = x;
-            mouseY = y;
-
-            FindMouse(mouseX, mouseY, &x, &y, &line, true);
-
-            NotifyDropped(master, this, x, y);
+            selPosition = curPosition = TextPosFromPoint(x, y, &textBlock, true);
+            selBlock = textBlock;
+            PositionCaret(true);
+            Update(null);
          }
       }
-      mouseMove = false;
+      else
+         ReleaseCapture();
+      selecting = false;
       return true;
    }
+   bool selecting;
 
-   bool OnMouseMove(int mx, int my, Modifiers mods)
+   bool OnMouseMove(int x, int y, Modifiers mods)
    {
-      int x,y;
-      EditLine line;
-      bool needScroll;
-      
-      if(mods != -1 && mods.isSideEffect) 
-      { 
-         SetSelectCursor(); 
-         return true; 
-      }
-      if(style.noSelect) return true;
-      if(wordSelect) return true;
-      mouseX = mx - XOFFSET;
-      mouseY = my;
-
-      needScroll = FindMouse(this.mouseX, this.mouseY, &x, &y, &line, true);
-
-      if(this.mouseMove || this.mouseSelect)
+      if(edit && selecting)
       {
-         if(!needScroll)
-            timer.Stop();
-         else 
-         {
-            if(needScroll)
-               timer.Start();
-            if(mods != -1 && 
-               ((style.hScroll) || (style.vScroll)))
-               return true;
-         }
-      }
-
-      if(this.mouseMove)
-      {
-         DirtyLine(this.dropY);
-         this.dropX = x;
-         this.dropY = y;
-         DirtyLine(this.dropY);
-         this.dropLine = line;
-         SetViewToCursor(true);
-      }
-      else if(this.mouseSelect)
-      {
-         DirtyLine(this.selY);
-         DirtyLine(this.y);
-         this.x = x;
-         this.y = y;
-         ComputeColumn();
-         DirtyLine(this.y);
-         this.line = line;
-         SetViewToCursor(true);
-         UpdateDirty();
+         curPosition = TextPosFromPoint(x, y, &textBlock, true);
+         PositionCaret(true);
+         Update(null);
       }
-      SetSelectCursor();
-      return true;
+      return HTMLView::OnMouseMove(x, y, mods);
    }
 
    bool OnLeftDoubleClick(int mx, int my, Modifiers mods)
    {
-      int x,y;
-      EditLine line;
-      
-      mx -= XOFFSET;
-
-      if(style.noSelect) return true;
-      FindMouse(mx, my, &x, &y, &line, false);
-      if(!NotifyDoubleClick(master, this, line, mods))
-         return false;
-      if(x < line.count)
+      if(edit && textBlock)
       {
          int c;
          int start = -1;
          int numBytes;
-         for(c = x; c >= 0; c--)
+
+         selPosition = curPosition = TextPosFromPoint(mx, my, &textBlock, false);
+         selBlock = textBlock;
+         for(c = curPosition; c >= 0; c--)
          {
             unichar ch;
-            while(c > 0 && !UTF8_IS_FIRST(line.buffer[c])) c--;
-            ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
-            if(!IS_ALUNDER(ch))
+            while(c > 0 && !UTF8_IS_FIRST(textBlock.text[c])) c--;
+            ch = UTF8GetChar(textBlock.text + c, &numBytes);
+            if(!CharMatchCategories(ch, letters|numbers|marks|connector))
                break;
             start = c;
          }
          if(start != -1)
          {
-            for(c = start; c<line.count; c += numBytes)
+            for(c = start; c < textBlock.textLen; c += numBytes)
             {
-               unichar ch = UTF8_GET_CHAR(line.buffer + c, numBytes);
-               if(!IS_ALUNDER(ch))
+               unichar ch = UTF8GetChar(textBlock.text + c, &numBytes);
+               if(!CharMatchCategories(ch, letters|numbers|marks|connector))
                   break;
             }
-            SelDirty();
-            DirtyLine(this.y);
-            this.y = y;
-            DirtyLine(this.y);
-            this.selX = start;
-            this.x = c;
-            ComputeColumn();
-            this.line = this.selLine = line;
-            this.wordSelect = (c != start);
-            UpdateDirty();
+            selPosition = start;
+            curPosition = c;
+
+            PositionCaret(true);
+            Update(null);
+            return false;
          }
       }
       return true;
    }
-*/
+
    bool OnOpen(char * href)
    {
       if(!strncmp(href, "api://", 6))
       {
-         int tag = strtoul(href + 6, null, 16);
+         int tag = (uint)strtoul(href + 6, null, 16);
          DataRow row = mainForm.browser.FindSubRow(tag);
          if(row)
          {
@@ -2676,8 +2440,8 @@ class HelpView : HTMLView
                else
                {
                   block.parent.subBlocks.Insert(block, newBlock);
-                  startY += th;
-               }               
+                  startY += block.prev.height;
+               }
                newBlock.startX = startX;
                newBlock.startY = startY;
                newBlock.text = new0 char[1];
@@ -2688,21 +2452,137 @@ class HelpView : HTMLView
          if(!strcmp(textBlock.text, $"[Add Text]"))
          {
             textBlock.text[0] = 0;
-            textBlock.textLen = 0;               
+            textBlock.textLen = 0;
          }
 
          strcpy(editString, href + 7);
          selPosition = curPosition = 0;
+         selBlock = textBlock;
          // dialog.Create();
          edit = true;
-         PositionCaret(true);
+         // PositionCaret(true);
       }
       return true;
    }
 
-   Block textBlock;
    char * text;
-   int curPosition, selPosition;
+
+   void DeleteSelection()
+   {
+      if(textBlock != selBlock || curPosition != selPosition)
+      {
+         if(textBlock == selBlock)
+         {
+            // Within same block
+            int start = Min(curPosition, selPosition);
+            int end = Max(curPosition, selPosition);
+            memmove(textBlock.text + start, textBlock.text + end, textBlock.textLen - end);
+            textBlock.textLen -= end-start;
+            textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
+            curPosition = start;
+            selPosition = start;
+         }
+         else
+         {
+            int startSel, endSel;
+            Block startSelBlock = null, endSelBlock = null, b, next;
+
+            NormalizeSelection(&startSelBlock, &startSel, &endSelBlock, &endSel);
+
+            startSelBlock.text = renew startSelBlock.text char[startSel + endSelBlock.textLen - endSel + 1];
+            memcpy(startSelBlock.text + startSel, endSelBlock.text + endSel, endSelBlock.textLen - endSel + 1);
+
+            startSelBlock.textLen = startSel + endSelBlock.textLen - endSel;
+            for(b = startSelBlock.next; b; b = next)
+            {
+               bool isEnd = b == endSelBlock;
+               next = GetNextBlock(b);
+               b.parent.subBlocks.Remove(b);
+               delete b;
+               if(isEnd)
+                  break;
+            }
+            textBlock = startSelBlock;
+            selBlock = startSelBlock;
+            curPosition = startSel;
+            selPosition = startSel;
+         }
+         ComputeMinSizes();
+         ComputeSizes();
+         PositionCaret(true);
+         Update(null);
+      }
+   }
+
+   String GetSelectionString()
+   {
+      String selection = null;
+      if(textBlock == selBlock)
+      {
+         // Within same block
+         int start = Min(curPosition, selPosition);
+         int end = Max(curPosition, selPosition);
+         int len = end - start;
+         selection = new char[len + 1];
+         memcpy(selection, textBlock.text + start, len);
+         selection[len] = 0;
+      }
+      else
+      {
+         int startSel, endSel;
+         Block startSelBlock = null, endSelBlock = null, b;
+         int totalLen = 0;
+
+         NormalizeSelection(&startSelBlock, &startSel, &endSelBlock, &endSel);
+
+         // Compute length
+         for(b = startSelBlock; b; b = GetNextBlock(b))
+         {
+            int start = (b == startSelBlock) ? startSel : 0;
+            int end = (b == endSelBlock) ? endSel : b.textLen;
+            int len = end - start;
+            totalLen += len;
+            if(b == endSelBlock)
+               break;
+            else if(b.type == TEXT)
+               totalLen++;
+         }
+
+         selection = new char[totalLen + 1];
+         totalLen = 0;
+         for(b = startSelBlock; b; b = GetNextBlock(b))
+         {
+            int start = (b == startSelBlock) ? startSel : 0;
+            int end = (b == endSelBlock) ? endSel : b.textLen;
+            int len = end - start;
+            memcpy(selection + totalLen, b.text + start, len);
+            totalLen += len;
+            if(b == endSelBlock)
+               break;
+            else if(b.type == TEXT)
+               selection[totalLen++] = '\n';
+         }
+         selection[totalLen] = 0;
+      }
+      return selection;
+   }
+
+   void CopySelection()
+   {
+      String s = GetSelectionString();
+      if(s)
+      {
+         int len = strlen(s);
+         ClipBoard cb { };
+         if(cb.Allocate(len + 1))
+         {
+            memcpy(cb.text, s, len + 1);
+            cb.Save();
+         }
+         delete cb;
+         delete s;
+      }
+   }
 
    bool OnKeyDown(Key key, unichar ch)
    {
@@ -2713,27 +2593,51 @@ class HelpView : HTMLView
             case escape:
                OnLeftButtonDown(0,0,0);
                return false;
+            case Key { end, shift = true }:
             case end:
-               selPosition = curPosition = textBlock.textLen;
+               curPosition = textBlock.textLen;
+               if(!key.shift)
+               {
+                  selPosition = curPosition;
+                  selBlock = textBlock;
+               }
                PositionCaret(true);
                Update(null);
                break;
+            case Key { home, shift = true }:
             case home:
-               selPosition = curPosition = 0;
+               curPosition = 0;
+               if(!key.shift)
+               {
+                  selPosition = curPosition;
+                  selBlock = textBlock;
+               }
                PositionCaret(true);
                Update(null);
                break;
+            case Key { home, ctrl = true, shift = true }:
             case ctrlHome:
-               selPosition = curPosition = 0;
+               curPosition = 0;
                while(textBlock.prev)
                   textBlock = textBlock.prev.prev;
+               if(!key.shift)
+               {
+                  selPosition = curPosition;
+                  selBlock = textBlock;
+               }
                PositionCaret(true);
                Update(null);
                return false;
+            case Key { end, ctrl = true, shift = true }:
             case ctrlEnd:
                while(textBlock.next && textBlock.next.next)
                   textBlock = textBlock.next.next;
-               selPosition = curPosition = textBlock.textLen;
+               curPosition = textBlock.textLen;
+               if(!key.shift)
+               {
+                  selPosition = curPosition;
+                  selBlock = textBlock;
+               }
                PositionCaret(true);
                Update(null);
                return false;
@@ -2743,12 +2647,14 @@ class HelpView : HTMLView
          return HTMLView::OnKeyDown(key, ch);
       return true;
    }
+
    bool OnKeyHit(Key key, unichar ch)
    {
       if(edit)
       {
          switch(key)
          {
+            case Key { up, shift = true }:
             case up:
             {
                if(caretY == textBlock.startY)
@@ -2756,7 +2662,13 @@ class HelpView : HTMLView
                   if(textBlock.prev)
                   {
                      textBlock = textBlock.prev.prev;
-                     selPosition = curPosition = Min(curPosition, textBlock.textLen);
+                     curPosition = Min(curPosition, textBlock.textLen);
+                     if(!key.shift)
+                     {
+                        selPosition = curPosition;
+                        selBlock = textBlock;
+                     }
+                     Update(null);
                      PositionCaret(false);
                      caretY = MAXINT;
                   }
@@ -2827,7 +2739,13 @@ class HelpView : HTMLView
                               len = curPosition - startPos;
                               display.FontExtent(textBlock.font.font, text + startPos, len, &x, null);
                            }
-                           selPosition = curPosition;
+                           if(!key.shift)
+                           {
+                              selPosition = curPosition;
+                              selBlock = textBlock;
+                           }
+                           Update(null);
+
                            PositionCaret(false);
                            return false;
                         }
@@ -2838,10 +2756,24 @@ class HelpView : HTMLView
                         {
                            int c = textPos - 1;
                            while(c > 0 && text[c] == ' ') c--;
-                           selPosition = curPosition = c + 1;
+                           curPosition = c + 1;
+                           if(!key.shift)
+                           {
+                              selPosition = curPosition;
+                              selBlock = textBlock;
+                           }
+                           Update(null);
                         }
                         else
-                           selPosition = curPosition = textBlock.textLen;
+                        {
+                           curPosition = textBlock.textLen;
+                           if(!key.shift)
+                           {
+                              selPosition = curPosition;
+                              selBlock = textBlock;
+                           }
+                           Update(null);
+                        }
                         PositionCaret(false);
                         return false;
                      }
@@ -2852,6 +2784,7 @@ class HelpView : HTMLView
                }
                return false;
             }
+            case Key { down, shift = true }:
             case down:
             {
                int tw = 0, th = 0;
@@ -2916,14 +2849,25 @@ class HelpView : HTMLView
                            len = curPosition - startPos;
                            display.FontExtent(textBlock.font.font, text + startPos, len, &x, null);
                         }
-                        selPosition = curPosition;
+                        if(!key.shift)
+                        {
+                           selPosition = curPosition;
+                           selBlock = textBlock;
+                        }
+                        Update(null);
                         PositionCaret(false);
                         return false;
                      }
                   }
                   if(sy > caretY)
                   {
-                     selPosition = curPosition = textBlock.textLen;
+                     curPosition = textBlock.textLen;
+                     if(!key.shift)
+                     {
+                        selPosition = curPosition;
+                        selBlock = textBlock;
+                     }
+                     Update(null);
                      PositionCaret(false);
                      return false;
                   } 
@@ -2947,15 +2891,14 @@ class HelpView : HTMLView
                {
                   textBlock = textBlock.next.next;
                   selPosition = curPosition = Min(curPosition, textBlock.textLen);
+                  selBlock = textBlock;
                   PositionCaret(false);
                }*/
                break;
             }
-            #define IS_ALUNDER(ch) ((ch) == '_' || isalnum((ch)))
+            case Key { right, shift = true, ctrl = true }:
             case ctrlRight:
             {
-               // SELECTION CTRL-RIGHT
-               /*
                bool foundAlpha = false;
                bool found = false;
                Block line, lastLine;
@@ -2967,7 +2910,9 @@ class HelpView : HTMLView
                   int c;
                   for(c = start; c < line.textLen; c++)
                   {
-                     if(IS_ALUNDER(line.text[c]))
+                     char ch = line.text[c];
+                     bool isAlUnder = CharMatchCategories(ch, letters|numbers|marks|connector);
+                     if(key.shift ? isAlUnder : !isAlUnder)
                      {
                         foundAlpha = true;
                         lastC = c;
@@ -2976,58 +2921,56 @@ class HelpView : HTMLView
                      else if(foundAlpha)
                      {
                         found = true;
+                        if(!key.shift)
+                        {
+                           curPosition = c;
+                           if(!key.shift)
+                           {
+                              selPosition = curPosition;
+                              selBlock = textBlock;
+                           }
+                           Update(null);
+                           textBlock = line;
+                           PositionCaret(true);
+                        }
                         break;
                      }
                   }
+                  // No next word found, 
                   if(!found && (c != curPosition || line != textBlock))
                   {
                      found = true;
                      lastLine = line;
                      lastC = line.textLen-1;
-                     break;
-                  }
-               }  
-               if(found)
-               {
-                  selPosition = curPosition = lastC+1;
-                  textBlock = lastLine;
-                  PositionCaret(true);
-               }
-               */
-               
-               bool foundAlpha = false;
-               bool found = false;
-               Block line;
-
-               for(line = textBlock; (line && !found); line = line.next ? line.next.next : null)
-               {
-                  int start = (line == textBlock) ? curPosition : 0;
-                  int c;
-                  for(c = start; c < line.textLen; c++)
-                  {
-                     if(!IS_ALUNDER(line.text[c]))
-                        foundAlpha = true;
-                     else if(foundAlpha)
+                     if(key.shift)
+                        break;
+                     else
                      {
-                        found = true;
-                        selPosition = curPosition = c;
+                        curPosition = line.textLen;
+                        if(!key.shift)
+                        {
+                           selPosition = curPosition;
+                           selBlock = textBlock;
+                        }
+                        Update(null);
+
                         textBlock = line;
                         PositionCaret(true);
-                        break;
                      }
                   }
-                  // No next word found, 
-                  if(!found && (c != curPosition || line != textBlock))
-                  {
-                     found = true;
-                     selPosition = curPosition = line.textLen;
-                     textBlock = line;
-                     PositionCaret(true);
-                  }
-                  foundAlpha = true;
+                  if(!key.shift)
+                     foundAlpha = true;
+               }
+               if(key.shift && found)
+               {
+                  curPosition = lastC+1;
+                  textBlock = lastLine;
+                  PositionCaret(true);
+                  Update(null);
                }
                break;
             }
+            case Key { left, ctrl = true, shift = true }:
             case ctrlLeft:
             {
                bool foundAlpha = false;
@@ -3048,7 +2991,7 @@ class HelpView : HTMLView
                   if(line == textBlock) start = curPosition-1; else start = line.textLen-1;
                   for(c = start; c>=0; c--)
                   {
-                     if(IS_ALUNDER(line.text[c]))
+                     if(CharMatchCategories(line.text[c], letters|numbers|marks|connector))
                      {
                         foundAlpha = true;
                         lastC = c;
@@ -3075,99 +3018,136 @@ class HelpView : HTMLView
                if(foundAlpha)
                {
                   textBlock = lastLine;
-                  selPosition = curPosition = lastC;
+                  curPosition = lastC;
+                  if(!key.shift)
+                  {
+                     selPosition = curPosition;
+                     selBlock = textBlock;
+                  }
                   PositionCaret(true);
+                  Update(null);
                }
                break;
             }
+            case Key { right, shift = true }:
             case right:
                if(curPosition < textBlock.textLen)
                {
                   curPosition += UTF8_NUM_BYTES(textBlock.text[curPosition]);
+                  if(!key.shift)
+                  {
+                     selPosition = curPosition;
+                     selBlock = textBlock;
+                  }
                   PositionCaret(true);
-                  selPosition = curPosition;
+                  Update(null);
                }
                else if(textBlock.next && textBlock.next.next)
                {
                   textBlock = textBlock.next.next;
-                  selPosition = curPosition = 0;
+                  curPosition = 0;
+                  if(!key.shift)
+                  {
+                     selPosition = curPosition;
+                     selBlock = textBlock;
+                  }
                   PositionCaret(true);
+                  Update(null);
                }
                break;
+            case Key { left, shift = true }:
             case left:
                if(curPosition > 0)
                {
                   while(curPosition > 0 && !UTF8_IS_FIRST(textBlock.text[--curPosition]));
+                  if(!key.shift)
+                  {
+                     selPosition = curPosition;
+                     selBlock = textBlock;
+                  }
                   PositionCaret(true);
-                  selPosition = curPosition;
+                  Update(null);
                }
                else if(textBlock.prev)
                {
                   textBlock = textBlock.prev.prev;
-                  selPosition = curPosition = textBlock.textLen;
+                  curPosition = textBlock.textLen;
+                  if(!key.shift)
+                  {
+                     selPosition = curPosition;
+                     selBlock = textBlock;
+                  }
                   PositionCaret(true);
+                  Update(null);
                }
                break;
             case backSpace:
-               if(curPosition)
+               if(textBlock == selBlock && curPosition == selPosition)
                {
-                  int c = curPosition;
-                  int nb = 1;
-                  while(c > 0 && !UTF8_IS_FIRST(textBlock.text[--c])) nb++;
-                  memmove(textBlock.text + curPosition - nb, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
-                  textBlock.textLen -= nb;
-                  textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
-                  curPosition -= nb;
-                  selPosition = curPosition;
+                  if(curPosition)
                   {
-                     //Clear(html.block);
-                     //CreateForms(html.block);
+                     int c = curPosition;
+                     int nb = 1;
+                     while(c > 0 && !UTF8_IS_FIRST(textBlock.text[--c])) nb++;
+                     memmove(textBlock.text + curPosition - nb, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
+                     textBlock.textLen -= nb;
+                     textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
+                     curPosition -= nb;
+                     selPosition = curPosition;
+                     selBlock = textBlock;
+
                      ComputeMinSizes();
                      ComputeSizes();
-                     //PositionForms();
+                     PositionCaret(true);
+                     Update(null);
                   }
-                  PositionCaret(true);
-                  Update(null);
-               }
-               else if(textBlock.prev)
-               {
-                  Block prev = textBlock.prev, prevBlock = textBlock.prev.prev;
-                  prevBlock.text = renew prevBlock.text char[prevBlock.textLen + textBlock.textLen + 1];
-                  memcpy(prevBlock.text + prevBlock.textLen, textBlock.text, textBlock.textLen + 1);
-
-                  selPosition = curPosition = prevBlock.textLen;
-                  prevBlock.textLen += textBlock.textLen;
-                  textBlock.parent.subBlocks.Remove(prev);
-                  delete prev;
-                  textBlock.parent.subBlocks.Remove(textBlock);                  
-                  delete textBlock;
-                  textBlock = prevBlock;
-                  
+                  else if(textBlock.prev)
                   {
-                     //Clear(html.block);
-                     //CreateForms(html.block);
+                     Block prev = textBlock.prev, prevBlock = textBlock.prev.prev;
+                     prevBlock.text = renew prevBlock.text char[prevBlock.textLen + textBlock.textLen + 1];
+                     memcpy(prevBlock.text + prevBlock.textLen, textBlock.text, textBlock.textLen + 1);
+
+                     selPosition = curPosition = prevBlock.textLen;
+                     selBlock = textBlock;
+                     prevBlock.textLen += textBlock.textLen;
+                     textBlock.parent.subBlocks.Remove(prev);
+                     if(prev == selBlock)
+                     {
+                        selBlock = textBlock;
+                        selPosition = curPosition;
+                     }
+                     delete prev;
+                     textBlock.parent.subBlocks.Remove(textBlock);
+                     if(textBlock == selBlock)
+                     {
+                        selBlock = prevBlock;
+                        selPosition = curPosition;
+                     }
+                     delete textBlock;
+                     textBlock = prevBlock;
+
                      ComputeMinSizes();
                      ComputeSizes();
-                     //PositionForms();
+                     PositionCaret(true);
+                     Update(null);
                   }
-                  PositionCaret(true);
-                  Update(null);
                }
+               else
+                  DeleteSelection();
                break;
             case del:
-               if(textBlock.textLen > curPosition)
+               if(textBlock != selBlock || curPosition != selPosition)
+                  DeleteSelection();
+               else if(textBlock.textLen > curPosition)
                {
                   int nb = UTF8_NUM_BYTES(textBlock.text[curPosition]);
                   memmove(textBlock.text + curPosition, textBlock.text + curPosition + nb, textBlock.textLen - curPosition + 1 - nb + 1);
                   textBlock.textLen -= nb;
                   textBlock.text = renew textBlock.text char[textBlock.textLen + 1];
-                  {
-                     //Clear(html.block);
-                     //CreateForms(html.block);
-                     ComputeMinSizes();
-                     ComputeSizes();
-                     //PositionForms();
-                  }
+
+                  ComputeMinSizes();
+                  ComputeSizes();
+
                   PositionCaret(true);
                   Update(null);
                }
@@ -3179,27 +3159,39 @@ class HelpView : HTMLView
 
                   textBlock.textLen += nextBlock.textLen;
                   textBlock.parent.subBlocks.Remove(next);
+                  if(next == selBlock)
+                  {
+                     selBlock = textBlock;
+                     selPosition = curPosition;
+                  }
                   delete next;
-                  textBlock.parent.subBlocks.Remove(nextBlock);                  
-                  delete nextBlock;
-                  
+                  textBlock.parent.subBlocks.Remove(nextBlock);
+                  if(nextBlock == selBlock)
                   {
-                     //Clear(html.block);
-                     //CreateForms(html.block);
-                     ComputeMinSizes();
-                     ComputeSizes();
-                     //PositionForms();
+                     selBlock = textBlock;
+                     selPosition = curPosition;
                   }
+                  delete nextBlock;
+
+                  ComputeMinSizes();
+                  ComputeSizes();
                   PositionCaret(true);
                   Update(null);
                }
                break;
             case enter:
             {
-               Block block { type = BR, parent = textBlock.parent, font = textBlock.font };
-               Block newBlock { type = TEXT, parent = textBlock.parent, font = textBlock.font };
-               int startY = textBlock.startY, startX = textBlock.startX;
                int tw = 0, th = 0;
+               Block block;
+               Block newBlock;
+               int startY, startX;
+
+               DeleteSelection();
+
+               block = { type = BR, parent = textBlock.parent, font = textBlock.font };
+               newBlock = { type = TEXT, parent = textBlock.parent, font = textBlock.font };
+               startY = textBlock.startY;
+               startX = textBlock.startX;
 
                display.FontExtent(textBlock.font.font, " ", 1, null, &th);
                textBlock.parent.subBlocks.Insert(textBlock, block);
@@ -3216,18 +3208,27 @@ class HelpView : HTMLView
                newBlock.startY = startY;
                newBlock.startX = startX;
                selPosition = curPosition = 0;
-               {
-                  //Clear(html.block);
-                  //CreateForms(html.block);
-                  ComputeMinSizes();
-                  ComputeSizes();
-                  //PositionForms();
-               }
+
+               ComputeMinSizes();
+               ComputeSizes();
+
                textBlock = newBlock;
+               selBlock = textBlock;
                PositionCaret(true);
                Update(null);
                break;
             }
+            case ctrlX:
+            case Key { del, shift = true }:
+               // Cut
+               CopySelection();
+               DeleteSelection();
+               break;
+            case ctrlC:
+            case ctrlInsert:
+               // Copy
+               CopySelection();
+               break;
             case shiftInsert:
             case ctrlV:
             {
@@ -3238,8 +3239,14 @@ class HelpView : HTMLView
                   char * text = clipBoard.memory;
                   char ch;
                   int start = 0;
-                  Block parent = textBlock.parent;
-                  FontEntry font = textBlock.font;
+                  Block parent;
+                  FontEntry font;
+
+                  DeleteSelection();
+
+                  parent = textBlock.parent;
+                  font = textBlock.font;
+
                   for(c = 0; ; c++)
                   {
                      ch = text[c];
@@ -3252,6 +3259,7 @@ class HelpView : HTMLView
                         textBlock.textLen += len;
                         curPosition += len;
                         selPosition = curPosition;
+                        selBlock = textBlock;
                         if(!ch) break;
                         {
                            Block block { type = BR, parent = parent, font = font };
@@ -3274,6 +3282,7 @@ class HelpView : HTMLView
                            newBlock.startY = startY;
                            newBlock.startX = startX;
                            selPosition = curPosition = 0;
+                           selBlock = textBlock;
                            textBlock = newBlock;
                         }
                         if(ch == '\r' && text[c+1] == '\n') c++;
@@ -3297,6 +3306,8 @@ class HelpView : HTMLView
                   int len = UTF32toUTF8Len(&ch, 1, string, 5);
                   int c;
 
+                  DeleteSelection();
+
                   textBlock.text = renew textBlock.text char[textBlock.textLen + len + 1];
                   memmove(textBlock.text + curPosition + len, textBlock.text + curPosition, textBlock.textLen - curPosition + 1);
                   
@@ -3307,6 +3318,7 @@ class HelpView : HTMLView
                      curPosition++;
                   }
                   selPosition = curPosition;
+                  selBlock = textBlock;
                   
                   {
                      //Clear(html.block);
@@ -3396,7 +3408,7 @@ class HelpView : HTMLView
                int len = curPosition - startPos;
                display.FontExtent(textBlock.font.font, text + startPos, len, &tw, null);
                sx += tw;
-               break;            
+               break;
             }
             sy += th;
             sx = textBlock.startX;
@@ -3416,7 +3428,7 @@ class HelpView : HTMLView
             else if(sy - scroll.y < 0)
             {
                scrollPos.y = sy;
-               doScroll = true;            
+               doScroll = true;
             }
             if(sx - scroll.x + 10 > clientSize.w)
             {
@@ -3426,7 +3438,7 @@ class HelpView : HTMLView
             else if(sx - scroll.x < 10)
             {
                scrollPos.x = sx - 10;
-               doScroll = true;            
+               doScroll = true;
             }
             if(doScroll)
                scroll = scrollPos;
@@ -3437,7 +3449,7 @@ class HelpView : HTMLView
    }
 
    // Returns a character offset into the TextBlock from a window coordinate
-   int TextPosFromPoint(int px, int py, Block * block)
+   int TextPosFromPoint(int px, int py, Block * block, bool half)
    {
       Block parentBlock = this.textBlock.parent;
       Block textBlock;
@@ -3506,7 +3518,7 @@ class HelpView : HTMLView
                   {
                      numBytes = UTF8_NUM_BYTES(ch);
                      display.FontExtent(textBlock.font.font, text + c, numBytes, &w, &th);
-                     if(/*py >= sy && */py < sy + th && /*px >= sx-w/2-space && */px < sx + w -w/2-space)
+                     if(/*py >= sy && */py < sy + th && /*px >= sx-w/2-space && */px < sx + (half ? w/2 : w) -space)
                         break;
                      sx += w;
                   }