PageRenderTime 236ms CodeModel.GetById 192ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/2.0/Samples/RegEditPHP/RegEditPHP/frmMain.user.php

#
PHP | 1215 lines | 919 code | 6 blank | 290 comment | 95 complexity | 918e03ea50ee3086e3fc623174759e5b MD5 | raw file
   1<? //Š Đonny 2009 - Part of Phalanger project
   2    import namespace RegEditPHP;
   3    import namespace System:::Windows:::Forms;
   4    import namespace Microsoft:::Win32;
   5    import namespace System;
   6    
   7    namespace RegEditPHP{
   8        ///<summary>Main application form</summary>
   9	    partial class frmMain extends System:::Windows:::Forms:::Form{
  10	        //This class represents main form of this application. It contains the most of the code.
  11//Initialization
  12             ///<summary>This name is used as name and text of placeholder child key</summary>
  13             const dummy = "dummy node"; //Each tree node in TreeView that have never been expanded is filled with one dummy node to have the + sign in front of itself (to appear expandable). This node is replaced with actual nodes before expanding.
  14             ///<summary>Gives access to localized resources</summary>
  15             private $Resources;//See ResourceAccessor.php
  16             ///<summary>Internal initialization method</summary>
  17             private function Init(){
  18                //Call to this method wa manually added to __construct() in frmMain.php
  19                //Following commented lines are no longer needed because pictures now can be added form designer
  20                //$this->tmiNew->Image = Program::$Resources->new;
  21                //$this->tmiNewKey->Image = Program::$Resources->folder->ToBitmap();
  22                //$this->tmiNewDWord->Image = 
  23                //$this->tmiNewQWord->Image = Program::$Resources->numeric->ToBitmap();
  24                //$this->tmiNewString->Image = 
  25                //$this->tmiNewExpandable->Image = 
  26                //$this->tmiNewMulti->Image = Program::$Resources->string->ToBitmap();
  27                //$this->tmiNewBinary->Image = Program::$Resources->binary->ToBitmap();
  28                //$this->tmiRename->Image = Program::$Resources->rename;
  29                //$this->tmiEdit->Image = Program::$Resources->edit;
  30                //$this->tmiDelete->Image = Program::$Resources->delete;
  31                //$this->tmiRefreshNode->Image = Program::$Resources->refresh;
  32                //Initialize sorter to sort list of values
  33                $this->Sorter = new Sorter($this->lvwRegistry);
  34                //Fill nodes added in designer with placeholder subnodes
  35                //This can be done in designer as well but it's so much clicking and use of self::dummy is better than use of "dummy key" (which'd be case of designer)
  36                foreach($this->tvwRegistry->Nodes as $node):
  37                    $node->Nodes->Add(self::dummy)->Name=self::dummy;
  38                endforeach;
  39             }
  40//Main menu  
  41             //Handles click to Exit menu item
  42             private function tmiExit_Click(System:::Object $sender, System:::EventArgs $e) {
  43                $this->Close();//Close the main form (it terminates the application)
  44             }
  45            //Handles click to Language menu item
  46            private function tmiLanguage_Click(System:::Object $sender, System:::EventArgs $e) {
  47                //Create instance of form for language sleection
  48                $lSel = new LanguageSelector();
  49                //Show it and test if user clicked OK
  50                if($lSel->ShowDialog($this) == DialogResult::OK):
  51                    //If so, the application must be restarted (changing UI strings without restart is too much work)
  52                    //To restart the application we need know path of it - location of assembly
  53                    //So get the assembly from type contained in it
  54                    $MyType = CLRTypeOf frmMain;
  55                    $MyPath = $MyType->Assembly->Location;
  56                    try{ //Launch it
  57                        System:::Diagnostics:::Process::Start($MyPath);
  58                    }catch(System:::Exception $ex){ //This probably will not happen
  59                        self::OnError(Program::$Resources->e_Restart."\r\n$ex->Message");
  60                        return;
  61                    }
  62                    //If application successfully started, this can be closed
  63                    $this->Close();
  64                endif;
  65            }
  66            //Handles click on the Jump menu item
  67            private function tmiJump_Click(System:::Object $sender, System:::EventArgs $e) {
  68                //get path to jump to using InputBox (see InputBox.user.php)
  69                $Path = InputBox::GetInput(Program::$Resources->JumpToKey,Program::$Resources->JumpToKey_t);
  70                if(!is_null($Path)) //InputBox::GetInput returns null on cancel
  71                    $this->JumpToKey($Path);//Do the jump
  72            }
  73            //Handles click on the Refresh all menu item
  74            private function tmiRefreshAll_Click(System:::Object $sender, System:::EventArgs $e) {
  75                //Remember currently selected key
  76                $OldPath = $this->tvwRegistry->SelectedNode->FullPath;
  77                //Clear content of all top-level nodes
  78                foreach($this->tvwRegistry->Nodes as $node):
  79                    $node->Nodes->Clear;
  80                    //And add placeholder into each of them to appear expandable
  81                    $node->Nodes->Add(self::dummy)->Name=self::dummy;
  82                endforeach; 
  83                //For user convenience, navigate to node that was selected when reloading (I hate when reloading changes my status)
  84                $this->JumpToKey($OldPath);    
  85            }
  86            ///<summary>Navigates to key with given path</summary>
  87            ///<param name="Path">Path full to navigate too</param>
  88            ///<returns type="bool">success</returns>
  89            private function JumpToKey(string $Path){
  90                //Firts trim trailing backslashes
  91                //We must use $Path->ToString() because we want to work with .NET string and .NET string is not same as PHP string (i think this is bug, teher should be only one string)
  92                while($Path->ToString()->EndsWith("\\"))
  93                    $Path = $Path->ToString()->Substring(0,strlen($Path) - 1);
  94                //You can use many of PHP built-in function you are familiar with. Sometimes they are nicer/better tna .NET ones - as explode()
  95                $PathParts = explode("\\",$Path);
  96                $CurrentNodes = $this->tvwRegistry->Nodes;//Starting with top-level nodes
  97                foreach($PathParts as $Part)://For each path part
  98                    foreach($CurrentNodes as $Node)://Wee look for node with same at current level
  99                        if($Node->Name->ToLower() == $Part->ToString()->ToLower())://Note: Usage of ToString() on PHP string is required to use ToLower(); $Node->Name is not PHP string but .NET string
 100                            $this->NoExpandQuestion=true;//Turn of expand question ofr HKCR
 101                            $Node->Expand();//Expand node
 102                            $this->NoExpandQuestion=false;//Turn the question back on
 103                            $CurrentNodes = $Node->Nodes;//Dive one more level
 104                            $LastNode = $Node;//Remember last succesfully found node (in case of failure, we will navigate to last-match level)
 105                            continue 2;//Next (deeper) level
 106                        endif;
 107                    endforeach;
 108                    self::OnError(Program::$Resources->e_KeyDoesNotExist($Path));//continue 2 above skips this line if node is found
 109                    break;
 110                endforeach;
 111                if(!is_null($LastNode))://If there was matc at least at one level
 112                    $this->tvwRegistry->SelectedNode = $LastNode;//select it
 113                    $LastNode->EnsureVisible();//And scroll it to view
 114                    return $LastNode->FullPath->ToLower() == $Path->ToString()->ToLower();//Only when all levels succeeded we consider jump successfull
 115                else:
 116                    return false;//The root name was unknown
 117                endif;
 118            }
 119            //Hanndles click on the Jump to same key under ... menu item
 120            //It navigates to node with same addres but sapped first part HKLM<->HKCU (if possible)
 121            private function tmiJumpToSame_Click(System:::Object $sender, System:::EventArgs $e) {
 122                if($this->tvwRegistry->SelectedNode->FullPath->StartsWith("HKEY_CURRENT_USER\\")):
 123                    $start=HKEY_CURRENT_USER;//current 1st part
 124                    $new=HKEY_LOCAL_MACHINE;//new 1st part
 125                else:
 126                    $new=HKEY_CURRENT_USER;//new 1st part
 127                    $start=HKEY_LOCAL_MACHINE;//current 1st part
 128                endif;
 129                $new= $new . $this->tvwRegistry->SelectedNode->FullPath->Substring(strlen($start));///replace 1st part
 130                $this->JumpToKey($new);//Navigate to new path (if navigation is not possible, navigates as near as can)
 131            }
 132            //Handles dropdown opening of the Tools top-level menu item
 133            private function tmiTools_DropDownOpening(System:::Object $sender, System:::EventArgs $e) {
 134                //We enable/disable Jump to same menu item depending on if user is under HKCU or HKLM or not
 135                $this->tmiJumpToSame->Enabled = (
 136                    $this->tvwRegistry->SelectedNode->FullPath->StartsWith("HKEY_CURRENT_USER\\") or
 137                    $this->tvwRegistry->SelectedNode->FullPath->StartsWith("HKEY_LOCAL_MACHINE\\"));
 138                //And adjust text to show name of target hive (top-level node)
 139                if($this->tmiJumpToSame->Enabled and $this->tvwRegistry->SelectedNode->FullPath->StartsWith("HKEY_CURRENT_USER\\")):
 140                    $this->tmiJumpToSame->Text = Program::$Resources->JumToSameKey("HKLM");
 141                elseif($this->tmiJumpToSame->Enabled):
 142                    $this->tmiJumpToSame->Text = Program::$Resources->JumToSameKey("HKCU");
 143                endif;
 144            }
 145            //Handles click on About menu item
 146            private function tmiAbout_Click(System:::Object $sender, System:::EventArgs $e) {
 147                $ad=new dlgAbout();//Create about dialog
 148                $ad->ShowDialog($this);//And show it
 149            }
 150
 151             
 152//Tree
 153             ///<summary>If false, user is askt if (s)he really wants to expand HKCR</summary>
 154             private $NoExpandQuestion = false;
 155             //Called before tree node is expanded
 156             //Dummy sub keys are deleted in this event and actual node content is loaded
 157             private function tvwRegistry_BeforeExpand(System:::Object $sender, System:::Windows:::Forms:::TreeViewCancelEventArgs $e) {
 158                if(!$this->NoExpandQuestion and $e->Node->FullPath==HKEY_CLASSES_ROOT and $e->Node->Nodes->Count == 1):
 159                    //PHPBUG:
 160                    //We use Sorter::Iterate because indexing and default properties does not work well in current version of Phalanger
 161                    if(Sorter::Iterate($e->Node->Nodes->GetEnumerator(),0)->Name == self::dummy):
 162                        //If node is HKCR and it was never expanded before, ask user if (s)he really wants to expand it (because it typically contains as many 2nd-level subkey as loading take a while (even in MS's regedit)
 163                        if(MessageBox::Show(Program::$Resources->ExpandHKCR,Program::$Resources->ExpandHKCR_t,MessageBoxButtons::YesNo,MessageBoxIcon::Information) != DialogResult::Yes):
 164                            //Cancels expanding (node will remain unexpanded)
 165                            $e->Cancel = true;
 166                            return;
 167                        endif;
 168                    endif;
 169                endif;
 170                //Test if node hals only one child - the Dummy
 171                if($e->Node->Nodes->Count==1):
 172                    //Following 2 lines are same as Sorter::Iterate($e->Node->Nodes->GetEnumerator(),0) (legacy)
 173                    $nem=$e->Node->Nodes->GetEnumerator();
 174                    $nem->MoveNext();
 175                    if($nem->Current->Name=self::dummy):
 176                        //Need load childs from registry
 177                        $this->Cursor = Cursors::$WaitCursor;//Set cursor to horglass (it may take a while)
 178                        try{
 179                            self::FillNode($e->Node);//Load subkeys
 180                        }catch(System:::Exception $ex){
 181                            $e->Node->Nodes->Clear();//When there is error, treat key as empty
 182                            self::OnError($ex->Message);//Show error message
 183                        }
 184                        $this->Cursor = Cursors::$Default;//Revert cursor icon
 185                    endif;
 186                endif;    
 187             }
 188             //Caled after tree node is expanded   
 189             private function tvwRegistry_AfterExpand(System:::Object $sender, System:::Windows:::Forms:::TreeViewEventArgs $e) {
 190                //We handle change of icon here
 191                //Note: Tree node has 2 icons - normal and selected
 192                // Both icons are taken from associated image list (set in designer)
 193                // and we want them to be the same, thus same key
 194                $e->Node->ImageKey="open";
 195                $e->Node->SelectedImageKey="open";
 196             }
 197            //Called after tree node is collapsed
 198             private function tvwRegistry_AfterCollapse(System:::Object $sender, System:::Windows:::Forms:::TreeViewEventArgs $e) {
 199                //Same as above, but change icon to closed
 200                $e->Node->ImageKey="closed";
 201                $e->Node->SelectedImageKey="closed";
 202             }
 203             ///<summary>Fills node with subnodes representing corresponding key subkeys</summary>
 204             ///<param name="Node">Node to fill</param>
 205             ///<returns type="void"/>
 206             private static function FillNode(TreeNode $Node){
 207                //1st clear any current content
 208                //It's typically dummy node, but for refresh it may be anything
 209                $Node->Nodes->Clear();
 210                //Get key for node path
 211                //Nice with TreeViewIs that it gives path of node which is immediatelly usable for registry lookup
 212                $Key=self::getRegistryKey($Node->FullPath);     
 213                $SubKeys=$Key->GetSubKeyNames();//Enumerate sub keys (this may throw an exception and it must be handles by caller)
 214                foreach($SubKeys as $SubKeyName)://Add the keys to current node
 215                    $SubKey = $Node->Nodes->Add($SubKeyName);//Add it (this creates node with given text, adds it and returns it)
 216                    $SubKey->Name = $SubKeyName;//Set its name (it is used by Fullpath)
 217                    $SubKey->ImageKey = "closed";//Set its image
 218                    $SubKey->SelectedImageKey = "closed";
 219                    $SubKey->Nodes->Add(self::dummy)->Name=self::dummy;//And fill it with dummy child (to seem to be axpandable)
 220                endforeach;
 221                //Note: Every node seems to be expandable if it has been never expanded
 222                // so user may experience following:
 223                // (S)he clicks the + and instead of node expanding, the + vanishes
 224                // it's not 100% good behavior, but is quite common and saves us some pre-testing
 225             }
 226             ///<summary>Gets registry key for its path</summary>
 227             ///<param name="Path">Full path of key to open</param>
 228             ///<param name="writeable">True to open key for writing, false to open it as readonly</param>
 229             ///<returns type="registryKey">Opened key</param>
 230             private static function getRegistryKey(string $Path,bool $writeable = false){
 231                //We must firts parse the path
 232                $parts=explode("\\",$Path);//Explode it by \
 233                //The bad thisng with .NET registry API is that there is no general-purpose function for opening registry key
 234                // The top-level keys (so-called hives) must be treated separatelly
 235                switch($parts[0])://So switch for them
 236                    case HKCR://Shortcut names are not currently utilized by this program
 237                    case HKEY_CLASSES_ROOT: $root=Registry::$ClassesRoot;
 238                    break;
 239                    case HKCU:
 240                    case HKEY_CURRENT_USER: $root=Registry::$CurrentUser;
 241                    break;
 242                    case HKLM:
 243                    case HKEY_LOCAL_MACHINE: $root=Registry::$LocalMachine;
 244                    break;
 245                    case HKU:
 246                    case HKEY_USERS: $root=Registry::$Users;
 247                    break;
 248                    case HKCC:
 249                    case HKEY_CURRENT_CONFIG: $root=Registry::$CurrentConfig;
 250                    break;
 251                    case HKDD:
 252                    case HKEY_DYN_DATA: $root=Registry::$DynData;
 253                    break;
 254                    case HKPD:
 255                    case HKEY_PERFORMANCE_DATA: $root=Registry::$PerformanceData;
 256                    break;
 257                    default: throw new CLRException(new ArgumentException(Program::$Resources->e_UnknownRoot));
 258                endswitch;
 259                if(count($parts)==1) return $root;//Return root for single-part path
 260                //Otherwise open root's sub key (now ve have general-purpose function to open multiplůe levels at once)
 261                return $root->OpenSubKey(i'String'::Join("\\",$parts,1,count($parts)-1),$writeable);
 262                //Note: Two uncommon things at line above:
 263                //1) i'String' is Phalanger way how to point to class or function that has same name as Phalanger keyword (in this case string). We use this i-preceded string in single quotes. i'String' means System:::String
 264                //2) I pass $parts (PHP array) where .NET String[] array is expected
 265                //PHPBUG:
 266                //   It is strange, but Phalanger currently has not silent implicit conversion from string to System::String but has conversion of array to String[]
 267             } 
 268            //Called after node in tree is selected
 269            private function tvwRegistry_AfterSelect(System:::Object $sender, System:::Windows:::Forms:::TreeViewEventArgs $e) {
 270                //We need show values in this key
 271                try{ //get key and show values for it
 272                    $this->LoadValues(self::GetRegistryKey($e->Node->FullPath));
 273                }catch(System:::Exception $ex){
 274                    self::OnError($ex->Message);     
 275                }
 276                $this->tslKey->Text=$e->Node->FullPath;
 277            }
 278            
 279            ///<summary>Loads values for given key rto ListView>/summary>
 280            ///<param name="key">Key to load values from</param>
 281            ///<returns type="void"/>
 282            private function LoadValues(RegistryKey $key){
 283                //Gate value names (may cause Exception, must be caught by caller)
 284                $ValueNames=$key->GetValueNames();
 285                //Clear list
 286                $this->lvwRegistry->Items->Clear();
 287                foreach($ValueNames as $Name)://For each name
 288                    $item=$this->lvwRegistry->Items->Add($Name);//Create and add item
 289                    $item->Name=$Name;//Set its name
 290                    switch($key->GetValueKind($Name))://We must support regtlue types separately
 291                        case RegistryValueKind::Binary://Binary (got as array of bytes)
 292                            $item->ImageKey="binary";
 293                            $kind=REG_BINARY;//You know - undefined constant's
 294                            $value=$key->GetValue($Name);
 295                            if(!is_null($value))$value=self::GetBytesString($value,&$alt);//Call helper method to display byte array
 296                        break;    
 297                        case RegistryValueKind::DWord://DWord (got as int32)
 298                            $item->ImageKey="numeric";
 299                            $kind=REG_DWORD;
 300                            $value=$key->GetValue($Name);
 301                            $alt="0x".$value->ToString("X");//Hexa
 302                            $value=$value->ToString();//Decadic                
 303                        break;    
 304                        case RegistryValueKind::ExpandString://Expandable string (%something% is replaced by value of system variable something)
 305                            $item->ImageKey="string";
 306                            $kind=REG_EXPAND_SZ;
 307                            $value=$key->GetValue($Name,null,RegistryValueOptions::DoNotExpandEnvironmentNames);//Unexpanded value
 308                            $alt=$key->GetValue($Name);//Expanded value
 309                        break;
 310                        case RegistryValueKind::MultiString://MultistringgotasString
 311                            $item->ImageKey="string";
 312                            $kind=REG_MULTI_SZ;
 313                            $value=$key->GetValue($Name);
 314                            $value=System:::String::Join("; ",$value);//Just join it using semicolon
 315                            $alt=$value;//No alternative sight
 316                        break;
 317                        case RegistryValueKind::QWord://QWord (got as int64) XP regedit does not support it, but we DO!
 318                            $item->ImageKey="numeric";
 319                            $kind=REG_QWORD;
 320                            $value=$key->GetValue($Name);
 321                            $alt="0x".$value->ToString("X");//Hexa
 322                            $value=$value->ToString();//decadic
 323                        break;
 324                        case RegistryValueKind::i'String'://Normal string
 325                            //PHPBUG:
 326                            //This is littele inconsistent behavior:
 327                            // While RegistryValueKind::String is invalid
 328                            // System:::String is valid
 329                            $item->ImageKey="string";
 330                            $kind=REG_SZ;
 331                            $value=$key->GetValue($Name);
 332                            $alt=$value;//There is no alternative sight of plain text
 333                        break;
 334                        default://Unknown kind (tehre are more kinds of registry values - uncommon and I dunno what good for)
 335                            //Just show that there is sth
 336                            $item->ImageKey="unknown";
 337                            $kind="unknown";    
 338                            $value=Program::$Resources->UnknownValue;
 339                            $alt=Program::$Resources->UnknownValueAlt;
 340                    endswitch;
 341                    //Fill subitems (2nd, 3rd, 4th columns in list view)
 342                    $item->SubItems->Add($kind);
 343                    $item->SubItems->Add($value);
 344                    $item->SubItems->Add($alt);
 345                endforeach;
 346                $this->lvwRegistry->Sort();//Sort list (sorting is actually provided by Sorter.php)
 347                $this->AfterSort();//Some after-sort settings
 348            }  
 349            ///<summary>Gets display string for byte array</summary>
 350            ///<param name="bytes">Byte array to get values from</param>
 351            ///<param name="alt" type=´"string">Output parameter filled with alternative (decimal) representation of <paramref name="bytes"/></param>
 352            ///<returns type="string">String representation of byte array (2-digits hexadecimal numbers separated by spaces)</returns>
 353            private static function GetBytesString(i'Array' $bytes,&$alt){
 354                //Note: i'Array' means that we acceps System:::array, not PHP array
 355                //Output parameters passed by reference are possible in PHP as well, but not commonly used. This uis the rare case when it is useful
 356                // In phalanger you can use [out] attribute, whoch I'm not using here
 357                //StringBuolder is efficient way of concatenating strings in .NET
 358                $ret=new System:::Text:::StringBuilder(count($bytes)*3);//Initial capacity count*3 is not compulsory but is god for efficiency
 359                $ret2=new System:::Text:::StringBuilder(count($bytes)*3);//For decimal capacity as approximate
 360                foreach($bytes as $byte):
 361                    if($ret->Length>0) $ret->Append(" ");//Add space, if non empty
 362                    if($ret2->Length>0) $ret2->Append(" ");
 363                    $ret->Append($byte->ToString("X2"));//Hexadecimal, 2 didigts    
 364                    $ret2->Append($byte->ToString());//Decimal
 365                endforeach;
 366                $alt=$ret2->ToString(); //StringBuilder must be converted to string explicitly
 367                return $ret->ToString(); 
 368            }             
 369            //Handles click on list view collumn
 370            private function lvwRegistry_ColumnClick(System:::Object $sender, System:::Windows:::Forms:::ColumnClickEventArgs $e) {
 371                //We do sorting here
 372                if($this->Sorter->Column == $e->Column):
 373                    //If user click same column 2nd or more times
 374                    switch($this->Sorter->Order)://reverse order
 375                        case SortOrder::Ascending:
 376                            $this->Sorter->Order=SortOrder::Descending;
 377                        break;
 378                        default:$this->Sorter->Order=SortOrder::Ascending;
 379                    endswitch;
 380                else://CHange sort column and set order to ASC
 381                    $this->Sorter->Order=SortOrder::Ascending;
 382                    $this->Sorter->Column = $e->Column;
 383                endif;
 384                $this->lvwRegistry->Sort();//Force apply sorting
 385                $this->AfterSort();//After sort operations
 386            }            
 387            ///<summary>Does common after ListView sort operations</summary>
 388            ///<returns type="void"/>
 389            private function AfterSort(){
 390                foreach($this->lvwRegistry->Columns as $col)
 391                    $col->ImageKey=null;//Remove sort image from all columns
 392                //And set it for the used one using direction-aware image
 393                Sorter::Iterate($this->lvwRegistry->Columns->GetEnumerator(),$this->Sorter->Column)->ImageKey = $this->Sorter->Order == SortOrder::Ascending ? 'asc' : 'desc';
 394            }
 395//Context menu and actions
 396            //Called befor context menu is opened
 397            private function cmsContext_Opening(System:::Object $sender, System:::ComponentModel:::CancelEventArgs $e) {
 398                //Note: ContextMenuStrip is associated with TreeView and ListView in designer
 399                //We hide/show and enable/disable certain menu items here based on conditions
 400                //Note: The === compares 2 instances for bering reference equal (the same instance). It's stronger than ==.Phalanger behavior with CLR classes is a little different form original PHP usage.
 401                if($this->cmsContext->SourceControl === $this->tvwRegistry and is_null($this->tvwRegistry->SelectedNode)):
 402                    //No TreeNode selected - this should not happedn
 403                    $e->Cancel=true;//Do not show the menu
 404                    return;
 405                endif;
 406                //Edit only for values
 407                $this->tmiEdit->Visible = (
 408                    $this->cmsContext->SourceControl === $this->lvwRegistry and
 409                    $this->lvwRegistry->SelectedItems->Count == 1);
 410                //No rename for no value selected or more values selected
 411                $this->tmiRename->Visible =
 412                    !($this->cmsContext->SourceControl === $this->lvwRegistry and $this->lvwRegistry->SelectedItems->Count <> 1);
 413                //No delete for no values selected
 414                $this->tmiDelete->Visible = 
 415                    !($this->cmsContext->SourceControl === $this->lvwRegistry and $this->lvwRegistry->SelectedItems->Count == 0);
 416                //No delete for root
 417                $this->tmiDelete->Enabled = 
 418                    !($this->cmsContext->SourceControl === $this->tvwRegistry and is_null($this->tvwRegistry->SelectedNode->Parent));
 419                //Select all only for values
 420                $this->tmiSelectAll->Visible = $this->cmsContext->SourceControl === $this->lvwRegistry;
 421                //If there is nothig to select ...
 422                $this->tmiSelectAll->Enabled = $this->lvwRegistry->Items->Count > 0;
 423                //Refres only for nodes
 424                $this->tmiRefreshNode->Visible = $this->cmsContext->SourceControl === $this->tvwRegistry;
 425                //Copy only for single key or current values
 426                //Note: LIst veiw recognizec selected values and focused vakues
 427                $this->tmiCopyFullPath->Enabled = (
 428                    $this->cmsContext->SourceControl === $this->tvwRegistry or
 429                    ($this->cmsContext->SourceControl === $this->lvwRegistry and !is_null($this->lvwRegistry->FocusedItem)));
 430            }
 431            //Handles attempt to rename tree node
 432            private function tvwRegistry_BeforeLabelEdit(System:::Object $sender, System:::Windows:::Forms:::NodeLabelEditEventArgs $e) {
 433                //Top-level cannot be renamed
 434                $e->CancelEdit = $e->CancelEdit || is_null($e->Node->Parent);
 435            }
 436            //Hanldes confirmed rename of tree node
 437            private function tvwRegistry_AfterLabelEdit(System:::Object $sender, System:::Windows:::Forms:::NodeLabelEditEventArgs $e) {
 438                //Reaname can be cancled or it is not rename at all (user haven't changed text - indinated by null Label)
 439                if(is_null($e->Label) or $e->Label == $e->Node->Text) $e->CancelEdit=true;//No change
 440                if($e->CancelEdit) return;
 441                $e->CancelEdit = !$this->RenameKey($e->Node, $e->Label);//Try to rename
 442            }
 443            //Handles confirned rename of values
 444            private function lvwRegistry_AfterLabelEdit(System:::Object $sender, System:::Windows:::Forms:::LabelEditEventArgs $e) {
 445                //Same as rename of key - the event can indicate no rename at all (null Label)
 446                if(is_null($e->Label)) $e->CancelEdit = true;
 447                $node = Sorter::Iterate($this->lvwRegistry->Items->GetEnumerator(),$e->Item);
 448                if($node->Text == $e->Label) $e->CancelEdit = true;//No change
 449                if($e->CancelEdit) return;
 450                $e->CancelEdit = $this->RenameValue($node, $e->Label);//Try to rename
 451            }
 452            //Handles click on New key context menu item
 453            private function tmiNewKey_Click(System:::Object $sender, System:::EventArgs $e) {
 454                $this->AddKey();//Add the key
 455            }
 456            //Handles click on Rename context menu item
 457            private function tmiRename_Click(System:::Object $sender, System:::EventArgs $e) {
 458                //We just detect which control context menu is show on and let the control to deal with rename on its own (in fact 2 functions above)
 459                if($this->cmsContext->SourceControl === $this->tvwRegistry):
 460                    $this->tvwRegistry->SelectedNode->BeginEdit();//Tree
 461                else:
 462                   Sorter::Iterate($this->lvwRegistry->SelectedItems,0)->BeginEdit();//List
 463                endif;
 464            }
 465            //Handles click on Edit context menu item
 466            private function tmiEdit_Click(System:::Object $sender, System:::EventArgs $e) {
 467                $this->EditValue();//Do the edit
 468            }
 469            //Handles doble-click on list view item
 470            //Note: Some lama-users like to instruct system to activate list view item on single click instead of on double click (link behavior)
 471            // If your ListView follows this user decision can be set in designer
 472            private function lvwRegistry_ItemActivate(System:::Object $sender, System:::EventArgs $e) {
 473                $this->EditValue();//Edit value value
 474            }
 475            //Handles click on delete on context menu item 
 476            private function tmiDelete_Click(System:::Object $sender, System:::EventArgs $e) {
 477                //We just detect control contet menu is shown on and call appropriate method
 478                if($this->cmsContext->SourceControl === $this->tvwRegistry):
 479                    $this->DeleteKey();//Delete key
 480                else:
 481                    $this->DeleteValues();//Delete value
 482                endif;
 483            }
 484            //Handles key down when tree view is active
 485            private function tvwRegistry_KeyDown(System:::Object $sender, System:::Windows:::Forms:::KeyEventArgs $e) {
 486                //We are intrested only in some keys
 487                //KeyData contains or combination of KeyCode and Ctrl+Alt+Shift state
 488                switch($e->KeyData):
 489                    case Keys::Delete: $this->DeleteKey();//Delete key (w/o any Ctrl+Shift+Alt)
 490                    break;
 491                    case Keys::F2://F2 key (w/o ...)
 492                        if(!is_null($this->tvwRegistry->SelectedNode) and !is_null($this->tvwRegistry->SelectedNode->Parent)):
 493                            $this->tvwRegistry->SelectedNode->BeginEdit();        
 494                        endif;
 495                    break;  
 496                    case Keys::F7: $this->AddKey();//F7 - I love Total Commander :-)
 497                endswitch;
 498            }
 499            //Handles key down on list view
 500            private function lvwRegistry_KeyDown(System:::Object $sender, System:::Windows:::Forms:::KeyEventArgs $e) {
 501                //Same note as abowe but we're interested in more keys
 502                switch($e->KeyData):
 503                    case Keys::Delete: $this->DeleteValues();//delete
 504                    break;
 505                    case Keys::F2://rename
 506                        if(!is_null($this->lvwRegistry->FocusedItem))
 507                            $this->lvwRegistry->FocusedItem->BeginEdit();
 508                    break;
 509                    case Keys::Control | Keys::A://This is how to uttilize the key combination
 510                        foreach($this->lvwRegistry->Items as $item)//Select all items
 511                            $item->Selected=true;
 512                    break;
 513                    case Keys::F4://Edit (like in TC)
 514                    case Keys::Enter: $this->EditValue();//Edit
 515                    break;
 516                    case Keys::Shift | Keys::F4://New string (like in TC)
 517                        $this->Addvalue(RegistryValueKind::i'String');
 518                    break;
 519                endswitch;
 520            }
 521            //Handles click on any of New value menu items
 522            //Note: More events can have sam handler and more hahndlers can be attached to single event in .NET
 523            private function tmiNewValue_Click(System:::Object $sender, System:::EventArgs $e) {
 524                //Detect kind from item being clicked
 525                if($sender === $this->tmiNewBinary)
 526                    $kind = RegistryValueKind::Binary;
 527                elseif($sender === $this->tmiNewDWord)
 528                    $kind = RegistryValueKind::DWord;
 529                elseif($sender === $this->tmiNewExpandable)
 530                    $kind = RegistryValueKind::ExpandString;    
 531                elseif($sender === $this->tmiNewMulti)
 532                    $kind = RegistryValueKind::MultiString;
 533                elseif($sender === $this->tmiNewQWord)
 534                    $kind = RegistryValueKind::QWord;
 535                elseif($sender === $this->tmiNewString)
 536                    $kind = RegistryValueKind::i'String';
 537                $this->Addvalue($kind);//Do add
 538            }
 539            ///<summary>deletes currently selected registry key</summary>
 540            ///<returns type="bool">success</returns>
 541            private function DeleteKey(){
 542                $KeyName=$this->tvwRegistry->SelectedNode->Text;
 543                //Ask user
 544                if(MessageBox::Show(Program::$Resources->DeleteKey($KeyName),Program::$Resources->DeleteKey_t,MessageBoxButtons::YesNo,MessageBoxIcon::Question) == DialogResult::Yes):
 545                    //We must open parent key and ask it to delete its child
 546                    $parts=explode("\\",$this->tvwRegistry->SelectedNode->FullPath);
 547                    $ParentPath=i'String'::Join("\\",$parts,0,count($parts)-1);//Parent path
 548                    try{
 549                        $ParentKey=$this->getRegistryKey($ParentPath,true);//Open parent
 550                    }catch(System:::Exception $ex){
 551                        self::OnError($ex->Message);
 552                        return false;
 553                    }
 554                    try{
 555                        $ParentKey->DeleteSubKeyTree($parts[count($parts)-1]);//Delete child
 556                    }catch(System:::Exception $ex){
 557                        self::OnError($ex->Message);
 558                        return false;
 559                    }
 560                    $this->tvwRegistry->SelectedNode->Remove();//On success delete the node visualy as vell
 561                    return true;
 562                else:
 563                    return false;
 564                endif;
 565            }
 566            ///<summary>Deletes selected values</param>
 567            ///<returns type="bool">success</returns>
 568            private function DeleteValues(){
 569                //First chose message
 570                if($this->lvwRegistry->SelectedItems->Count == 1):
 571                    //For one-value deletion as with value name
 572                    $item1=Sorter::Iterate($this->lvwRegistry->SelectedItems->GetEnumerator(),0)->Text;
 573                    $message= Program::$Resources->DeleteValue($item1);
 574                else://Otherwise simly ask on values
 575                    $message=Program::$Resources->DeleteValues;
 576                endif;
 577                //Ask user
 578                if(MessageBox::Show($message,Program::$Resources->DeleteValues_t,MessageBoxButtons::YesNo,MessageBoxIcon::Question)==DialogResult::Yes):
 579                    try{//Open key
 580                        $key=$this->getRegistryKey($this->tvwRegistry->SelectedNode->FullPath,true);
 581                    }catch(System:::Exception $ex){
 582                        self::OnError($ex->Message);
 583                        return false;
 584                    }
 585                    //Index of st selected item (to preselect something after deletion, users like it)
 586                    $fsi=Sorter::Iterate($this->lvwRegistry->SelectedIndices->GetEnumerator(),0);
 587                    //Number of items to delete (to detect skip)
 588                    $todelcnt=$this->lvwRegistry->SelectedItems->Count;
 589                    foreach($this->lvwRegistry->SelectedItems as $item):
 590                        //Label is target for goto statement (Phalanger PHP extension)
 591DeleteValue:            try{
 592                            $key->DeleteValue($item->Name);//Try to delete
 593                        }catch(System:::Exception $ex){//On error ask user what to do
 594                            switch(self::OnError(Program::$Resources->e_DeleteValue($item->Name)."\r\n$ex->Message",MessageBoxButtons::AbortRetryIgnore)):
 595                                case DialogResult::Ignore: continue 2;//Ignore, just procede to next selected value
 596                                case DialogResult::Retry: goto DeleteValue;//Retry (this is where goto is usefully).
 597                                default: break 2;//Stop deleteing more values (exit foreach loop)
 598                            endswitch;
 599                        }
 600                        $deleted[]=$item;//Remeber deleted items (to delete them visually as well)
 601                    endforeach;
 602                    foreach($deleted as $deleteditem)//Delete items visually
 603                        $this->lvwRegistry->Items->Remove($deleteditem);
 604                    //If all selected items was deleted
 605                    // there is no selected item now
 606                    // if there is somethign to select    
 607                    if(count($deleted) == $todelcnt and $this->lvwRegistry->Items->Count > 0):
 608                        //We will select item near the deleted one
 609                        $newIndex = max(min($this->lvwRegistry->Items->Count-1,$fsi-1),0);
 610                        Sorter::Iterate($this->lvwRegistry->Items->GetEnumerator(),$newIndex)->Selected = true;    
 611                    endif;
 612                    return count($deleted) > 0;//It may indicate success or semi-success
 613                else:
 614                    return false;
 615                endif;                
 616            }
 617            ///<summary>Adds key under curent key</summary>
 618            ///<param name="parent">Node (not key) to add key into</param>
 619            ///<param name="newname">Name of key to add. If empty user is asked.</param>
 620            ///<param name="norefresh">True to not add the key to TreeView and return it instead</param>
 621            ///<returns>Bool success; created key on success when norefresh was true</returns>
 622            private function AddKey($parent=null, string $newname="",  bool $norefresh=false){
 623                //This is somewhat complicated because it ca operate in ineractive and non-interactive mode
 624                if(is_null($parent)):
 625                    //Get parent node if we haven't got it
 626                    if(is_null($this->tvwRegistry->SelectedNode)) return false;
 627                    $parent = $this->tvwRegistry->SelectedNode;
 628                endif; 
 629                $Name=$newname;
 630                if($newname<>"") goto testBS;//Do not ask for name
 631                //Ask for name
 632EnterName:      if(!is_null($Name=InputBox::GetInput(Program::$Resources->NameOfNewKey,Program::$Resources->CreateKey_t,$Name))):
 633testBS:             if($Name->Contains("\\"))://Name cannot contain \
 634                        self::OnError(Program::$Resources->e_InvalidName($Name));
 635                        if($newname=="")
 636                            goto EnterName;//Interactive mode (repeat enter)
 637                        else return false;
 638                    endif;
 639                    try{ //Open paren registry key for writing
 640                        $parentkey=$this->getRegistryKey($parent->FullPath,true);
 641                    }catch(System:::Exception $ex){
 642                        $this->OnError($ex->Message);
 643                        return false;
 644                    }
 645                    //Check if key to be created already exists
 646                    try{
 647                        $existing = $parentkey->OpenSubKey($Name,false);
 648                    }catch(System:::Exception $ex){
 649                        $this->OnError($ex->Message);
 650                        return false;
 651                    }
 652                    if(!is_null($existing)):
 653                        self::OnError(Program::$Resources->e_KeyExists($Name));
 654                        return false;
 655                    endif;
 656                    try{//Create it
 657                       $newKey = $parentkey->CreateSubKey($Name);
 658                    }catch(System:::Exception $ex){
 659                        $this->OnError($ex->Message);
 660                        return false;
 661                    }
 662                    if($norefresh) return $newKey;//Not interactive, return created key to caller
 663                    //Otherwise navigate to the new key
 664                    $wasdummy = 
 665                        ($parent->Nodes->Count == 1 and Sorter::Iterate($parent->Nodes->GetEnumerator(),0)->Name=self::dummy);
 666                    $parent->Expand();//Expand prent
 667                    if($wasdummy)://If parent was not loaded, it is now loaded and contain newly created key
 668                        foreach($parent->Nodes as $node)://search for it
 669                            if($node->Name == $Name):
 670                                $newNode=$node;//found
 671                                break;
 672                            endif;
 673                        endforeach;
 674                    else://Otherwise add it visually
 675                        $newNode = $parent->Nodes->Add($Name);
 676                        $newNode->Name =  $Name;
 677                        $newNode->ImageKey = "closed";
 678                        $newNode->SelectedImageKey = "closed";
 679                    endif;
 680                    if (!is_null($newNode)):
 681                        $this->tvwRegistry->SelectedNode = $newNode;//Select new key
 682                        $newNode->EnsureVisible();//and scrollit to view
 683                    endif;
 684                    return true;
 685                else:
 686                    return false;
 687                endif;
 688            }
 689            ///<summary>Adds value of given type</summary>
 690            ///<param name="type">Type of value to add</param>
 691            ///<returns type="bool">success</returns>
 692            private function Addvalue(int $type){
 693                //PHPBUG:
 694                //Note: We cannot declare $type as RegistryValueKind because it then will not assept values like RegistryValueKind::Binary (being it treated as int)
 695                $editor = new ValueEditor();//Create value editor
 696                $editor->SetValue(null,$type);//Initialize it
 697                $editor->NameReadOnly=false;//Allow enter of name
 698                if($editor->ShowDialog($this)==DialogResult::OK)://Show it
 699                    try{
 700                        //Open registry key to add value into
 701                        $key=$this->getRegistryKey($this->tvwRegistry->SelectedNode->FullPath,true);
 702                        try{//Check if value with same name exists
 703                            $key->GetValueKind($editor->ValueName);
 704                            self::OnError(Program::$Resources->e_ValueExists($editor->ValueName));
 705                            return false;
 706                        }catch(System:::IO:::IOException $ex){}//Non existent value is indicated by System:::IO:::IOExcpetion - and its what we want
 707                        $key->SetValue($editor->ValueName,$editor->Value,$type);//Create it (create and change value are same operations)
 708                    }catch(System:::Exception $ex){
 709                        self::OnError($ex->Message);
 710                        return false;
 711                    }
 712                    $this->LoadValues($key);//refresh list of values
 713                    foreach($this->lvwRegistry->Items as $item)://Search for newly added
 714                        if($item->Name==$editor->ValueName):
 715                            $item->Selected=true;//select it
 716                            $item->EnsureVisible();//and scroll it into view
 717                            break;
 718                        endif;
 719                    endforeach; 
 720                    return true;
 721                else:
 722                    return false;
 723                endif;
 724            }
 725            ///<summary>Attempts to rename registry key</summary>
 726            ///<param name="node">Tree node representing the key to reanem</param>
 727            ///<param name="newname>New name of key</param>
 728            ///<returns type="bool">success</returns>
 729            private function RenameKey(TreeNode $node, string $newname){
 730                //This is tricky operation because neither .NET registry API nor Win32 ASPI supports key rename
 731                // we must clone the key and then delete original
 732                //Create target for copy
 733                $newKey = $this->AddKey($node->Parent,$newname,true);    
 734                if(is_bool($newKey)) return false;
 735                $copyed=false;
 736                $this->Cursor = Cursors::$WaitCursor;//Set cursor to hourglass
 737                try{
 738                    $OldKey=$this->getRegistryKey($node->FullPath,false);//Open source key for reading
 739                    $copyed = self::CopyKey($OldKey,$newKey,false);//Copy old key to new key
 740                }catch(System:::Exception $ex){
 741                    self::OnError($ex->Message);
 742                }
 743                $this->Cursor = Cursors::$Default;//Reset cursor back to normal
 744                $OldKey->Close();//Closing old key is necessary to be done explicitly (PHP closes IDisposable for you somewhen), but open key cannot be deleted
 745                unset($OldKey);
 746                if($copyed):
 747                    try{
 748                        $pathparts = explode("\\",$node->FullPath);
 749                        //Open old key's parent
 750                        $parent = $this->getRegistryKey(System:::String::Join("\\",$pathparts,0,count($pathparts)-1),true);
 751                        $parent->DeleteSubKeyTree($node->Name);//delete old key
 752                    }catch(System:::Exception $ex){
 753                        //Delete failed - now we have two identical keys, super :-(
 754                        $TwoKeys=true;
 755                        if(isset($parent)):
 756                            //When we're puzzled, puzzle user as well - let him decide
 757                            if(self::OnError(Program::$Resources->e_DeleteOriginalKey.":\r\n$ex->Message\r\n".Program::$Resources->KeepBoth."\r\n".Program::$Resources->KeepBoth_note,MessageBoxButtons::YesNo)<>DialogResult::Yes):
 758                                try{//Delete old key
 759                                    $parent->DeleteSubKeyTree($newname);
 760                                    $TwoKeys=false;
 761                                }catch(System:::Exception $ex){
 762                                    //Faild. It'sn't probable, but it's possible
 763                                    self::OnError(Program::$Resources->e_DeleteDuplicated."\r\n$ex->Message");
 764                                }
 765                            endif;    
 766                        endif;
 767                        if($TwoKeys)://Add doplicate key to tree to user see it
 768                            $newNode = $this->tvwRegistry->Nodes->Add($newKey->Name);
 769                            $newenameparts=explode("\\",$newKey->Name);
 770                            $newNode->Name = $newenameparts[count($newenameparts)-1];
 771                            $newNode->ImageKey="closed";
 772                            $newNode->SelectedImageKey="closed";
 773                            $newNode->Nodes->Add(self::dummy)->Name=self::dummy;
 774                        endif;
 775                        return false;
 776                    }
 777                    $node->Name=$newname;//Change of name (change of label is automatic)
 778                    return true;
 779                else:
 780                    return false;
 781                endif;
 782            }
 783            ///<summary>Renames registry value</summary>
 784            ///<param name="item"><see cref="ListViewItem"/> representing item to rename</prama>
 785            ///<param name="newname">Proposed new name</praram>
 786            private function RenameValue(ListViewItem $item, string $newname){
 787                //Technically the same situation as key rename, but copying is quite easy
 788                try{//Open key
 789                    $key=$this->getRegistryKey($this->tvwRegistry->SelectedNode->FullPath,true);
 790                }catch(System:::Exception $ex){
 791                    self::OnError($ex->Message);
 792                    return false;
 793                }
 794                try{//Chekck existence of newname-named value
 795                    $key->GetValueKind($newname);
 796                    self::OnError(Program::$Resources->e_Rename($item->Text,$newname));
 797                    return false;
 798                }catch(System:::IO:::IOException $ex){}//If thrown it does not exist
 799                try{//Set new value
 800                    $key->SetValue($newname,
 801                        $key->GetValue($item->Name,null,RegistryValueOptions::DoNotExpandEnvironmentNames),
 802                        $key->GetValueKind($item->Name));
 803                }catch(System:::Exception $ex){
 804                    self::OnError($ex->Message);
 805                    return false;
 806                }
 807                try{//Delete old value
 808                    $key->DeleteValue($item->Name);
 809                }catch(System:::Exception $ex){
 810                    //Failed of delete (inprobable when we were allowed to create one). Let user see both
 811                    $this->LoadValues($key);
 812                    foreach($this->lvwRegistry->Items as $item2):
 813                        $item2->Selected = ($item2->Name == $newname or $item2->Name == $item->Name);
 814                    endforeach;
 815                    return true;
 816                } 
 817                //Do the rename
 818                $item->Name=$newname;
 819                $item->Text=$newname;#is that necessary?
 820                return true;
 821            }
 822            ///<summary>Performs value editing</summary>
 823            ///<returns type="bool">success</returns>
 824            private function EditValue(){
 825                if ($this->lvwRegistry->SelectedItems->Count<>1) return false;
 826                $name = Sorter::Iterate($this->lvwRegistry->SelectedItems->GetEnumerator(),0)->Name;
 827                try{
 828                    //Open key
 829                    $key=$this->getRegistryKey($this->tvwRegistry->SelectedNode->FullPath,true);
 830                    $type = $key->GetValueKind($name);//get kind
 831                    if($type==RegistryValueKind::Unknown)://Cannot edit unknown
 832                        self::OnError(Program::$Resources->e_editUnknown);
 833                        return false;
 834                    endif;
 835                    //Get value
 836                    $value = $key->GetValue($name,null,RegistryValueOptions::DoNotExpandEnvironmentNames);
 837                }catch(System:::Exception $ex){
 838                    self::OnError($ex->Message);
 839                    return false;
 840                }
 841                $editor = new ValueEditor();//Create editor
 842                $editor->SetValue($value,$type);//Initialize it
 843                $editor->ValueName=$name;//Set value name
 844                if($editor->ShowDialog($this)==DialogResult::OK)://Show it
 845                    try{
 846                        $key->SetValue($name,$editor->Value,$type);//Write changed value    
 847                    }catch(System:::Exception $ex){
 848                        self::OnError($ex->Message);
 849                        return false;
 850                    }
 851                    $this->LoadValues($key);//referesh list
 852                    foreach($this->lvwRegistry->Items as $item):
 853                        if($item->Name == $name)://Search for edited value
 854                            $item->Selected=true;//Select it
 855                            $item->EnsureVisible();//Scroll it into view
 856                            break;
 857                        endif; 
 858                    endforeach;
 859                    return true;
 860                else:
 861                    return false;
 862                endif;    
 863            }
 864            ///<summary>Show error message box</summary>
 865            ///<param name="message">Message</param>
 866            ///<param name="buttons">Buttons to show</param>
 867            ///<returns type="int">Message box result (clicked button)</returns>
 868            public static function OnError(string $message, $buttons = MessageBoxButtons::OK){
 869                //This is the most common Windows API
 870                return MessageBox::Show($message, Program::$Resources->Error_t, $buttons, MessageBoxIcon::Error);
 871            } 
 872            //Handles mosued down on tree
 873            private function tvwRegistry_MouseDown(System:::Object $sender, System:::Windows:::Forms:::MouseEventArgs $e) {
 874                if ($e->Button == MouseButtons::Right and !is_null($node = $sender->GetNodeAt($e->X,$e->Y))):
 875                    //This little hack selects node befor context menu is shown for right click
 876                    $sender->SelectedNode = $node;
 877                endif;
 878            }
 879            //Handles clcick on tzhe Referesh cionetx menu item
 880            private function tmiRefreshNode_Click(System:::Object $sender, System:::EventArgs $e) {
 881                if($this->tvwRegistry->SelectedNode->FullPath == HKEY_CLASSES_ROOT)
 882                    //Ask for HKCR because it may take long    
 883                    if(MessageBox::Show(Program::$Resources->RefreshHKCR,Program::$Resources->RefreshHKCR_t,MessageBoxButtons::YesNo,MessageBoxIcon::Question)<>DialogResult::Yes)
 884                        return;
 885                $this->Cursor = Cursors::$WaitCursor;
 886                self::FillNode($this->tvwRegistry->SelectedNode);//Reload nodes
 887                $this->tvwRegistry->SelectedNode->Expand();//expand it
 888                $this->Cursor = Cursors::$Default;
 889            }
 890            //Hasndles clcick on Select all context menu item
 891            private function tmiSelectAll_Click(System:::Object $sender, System:::EventArgs $e) {
 892                foreach($this->lvwRegistry->Items as $item)
 893                    $item->Selected = true;
 894            }
 895            //Handkles click on Copy full path context menu item
 896            private function tmiCopyFullPath_Click(System:::Object $sender, System:::EventArgs $e) {
 897                //we can copy full poath of key as welkl as of value
 898                if($this->cmsContext->SourceControl === $this->tvwRegistry):
 899                    $str = $this->tvwRegistry->SelectedNode->FullPath;
 900                else://Just append value name
 901                    $str = $this->tvwRegistry->SelectedNode->FullPath . "\\" . $this->lvwRegistry->FocusedItem->Name;
 902                endif;
 903                //And place it into clipboard
 904                Clipboard::SetText($str,TextDataFormat::UnicodeText);
 905            }
 906
 907//Settings
 908            //Called when form loads
 909            private function frmMain_Load(System:::Object $sender, System:::EventArgs $e) {
 910                $this->LoadSettings();//Load settings
 911            }
 912            //Called after form closes
 913            private function frmMain_FormClosed(System:::Object $sender, System:::Windows:::Forms:::FormClosedEventArgs $e) {
 914                $this->SaveSettings();//Save settings
 915            }
 916            ///<summary>Loads form settings from settings class</summary>
 917            private function LoadSettings(){
 918                //Size
 919                if(!is_null($value=Program::$Settings->MainSize))
 920                    $this->ClientSize = $value;
 921                //State    
 922                if(!is_null($value=Program::$Settings->MainState))
 923                    $this->WindowState = $value;
 924                //Splitter (between tree view and listview) distance
 925                if(!is_null($value=Program::$Settings->SplitterDistance)) 
 926                    $this->splMain->SplitterDistance = $value;
 927                //Widths of columns    
 928                if(!is_null($value=Program::$Settings->ColumnWidths)):   
 929                    if(count($value) == $this->lvwRegistry->Columns->Count):
 930                        $i=0;
 931                        foreach($this->lvwRegistry->Columns as $col):
 932                            $col->Width = Program::$Settings->ColumnWidths[$i++];
 933                        endforeach;
 934                    endif;
 935                endif;
 936            }
 937            ///<summary>Saves settings to settings class</summary>
 938            private function SaveSettings(){
 939                Program::$Settings->MainSize = $this->ClientSize;//Size
 940                Program::$Settings->MainState = $this->WindowState;//State
 941                Program::$Settings->SplitterDistance = $this->splMain->SplitterDistance;//Splitter distance
 942                foreach($this->lvwRegistry->Columns as $col)://Column widths
 943                        $widths[] = $col->Width;
 944                endforeach;
 945                Program::$Settings->ColumnWidths = $widths;
 946                Program::$Settings->Save();//Write to disc
 947            }
 948//Copy
 949            ///<summary>Copies content of given registry key to another</summary>
 950            ///<param name="src">Source key</param>
 951            ///<param name="dest">Destination key</param>
 952            ///<param name="interactive">Allow user to recover form errors (if false only error message is displayed)</param>
 953            ///<returns>Success</param> 
 954            private static function CopyKey(RegistryKey $src, RegistryKey $dest,bool $interactive){
 955                //Copy values
 956                if (!self::CopyValues($src,$dest,$interactive)) return false;
 957                //and copy keys
 958                return self::CopySubKeys($src,$dest,$interactive);
 959            }
 960            ///<summary>Copies value ofrom one key to another</summary>
 961            ///<param name="src">Source key</param>
 962            ///<param name="dest">Destination key</param>
 963            ///<param name="interactive">Allow user to recover form errors (if false only error message is displayed)</param>
 964            ///<returns>Success</param> 
 965            private static function CopyValues(RegistryKey $src, RegistryKey $dest, bool $interactive){
 966                //Prepare message box buttons
 967                $mb=$interactive ? MessageBoxButtons::AbortRetryIgnore : MessageBoxButtons::OK;
 968InitCopy:       try{
 969                    foreach($src->GetValueNames() as $name):
 970CopyValue:              try{//Copy value
 971                            $dest->SetValue($name,
 972                                $src->GetValue($name,null,RegistryValueOptions::DoNotExpandEnvironmentNames),
 973                                $src->GetValueKind($name));
 974                        }catch(System:::Exception $ex){
 975                            switch(self::OnError(Program::$Resources->e_CopyValue($name,$src->Name,$dest->Name)."\r\n$ex->Message",$mb)):
 976                                case DialogResult::Retry: goto CopyValue;
 977                                case DialogResult::Ignore: break;
 978                                default:return false;
 979                            endswitch;
 980                        }
 981                    endforeach;
 982                }catch(System:::Exception $ex){
 983                    switch(self::OnError(Program::$Resources->e_EnlistValues($src->Name)."\r\n$ex->Message",$mb)):
 984                        case DialogResult::Retry: goto InitCopy;
 985                        case DialogResult::Ignore: return true;
 986                        default:return false;
 987                    endswitch;
 988                }
 989                return true;
 990            }
 991            ///<summary>Copies keys from one key to another</summary>
 992            ///<param name="src">Source key</param>
 993            ///<param name="dest">Destination key</param>
 994            ///<param name="interactive">Allow user to recover form errors (if false only error message is displayed)</param>
 995            ///<returns>Success</param> 
 996
 997            private static function CopySubKeys(RegistryKey $src, RegistryKey $dest, bool $interactive){
 998                //Prepare message box buttons
 999                $mb=$interactive ? MessageBoxButtons::AbortRetryIgnore : MessageBoxButtons::OK;
1000InitCopy:       try{
1001                    foreach($src->GetSubKeyNames() as $skName)://For each sub key
1002OpenSrc:                try{
1003                            $SrcSubKey=$src->OpenSubKey($skName);//Open it
1004                        }catch(System:::Exception $ex){
1005                            switch(self::OnError(Program::$Resources->e_OpenSubKey($src->Name,$skName)."\r\n$ex->Message",$mb)):
1006                                case DialogResult::Retry: goto OpenSrc;
1007                                case DialogResult::Ignore: continue 2;
1008                                default:return false;
1009                            endswitch;
1010                        }
1011OpenDest:               try{
1012                            $DestSubKey=$dest->CreateSubKey($skName);//Create it in target
1013                        }catch(System:::Exception $ex){
1014                            switch(self::OnError(Program::$Resources->e_CreateSubKey($dest->Name,$skName)."\r\n$ex->Message",$mb)):
1015                                case DialogResult::Retry: goto OpenDest;
1016                                case DialogResult::Ignore:
1017                                    $SrcSubKey->Close();
1018                                    continue 2;
1019                                default:return false;
1020                            endswitch;
1021                        }
1022                        //Recurse
1023                        if(!self::CopyKey($SrcSubKey,$DestSubKey,$interactive)) return false;
1024                        //PHP does this automatically for IDisposable 
1025                        // but for massive operations it's better to do it manually
1026                        $SrcSubKey->Close();
1027                        $DestSubKey->Close();       
1028                    endforeach;
1029                }catch(System:::Exception $ex){
1030                    switch (self::OnError(Program::$Resources->e_EnlistSubKeys($src->Name)."\r\n$ex->Message",$mb)):
1031                        case DialogResult::Retry: goto InitCopy;
1032                        case DialogResult::Ignore: return true;
1033                        default:return false;
1034                    endswitch;
1035                }
1036                return true;
1037            }
1038//Export            
1039            //Handles clcik on Export context menu item
1040            private function tmiExport_Click(System:::Object $sender, System:::EventArgs $e) {
1041                //Show save file dialog
1042                if($this->sfdExport->ShowDialog() == DialogResult::OK):
1043                    unset($kw);
1044                    //Depending on format selected by user
1045                    switch($this->sfdExport->FilterIndex):
1046                       case 1:
1047                            $kw="export";
1048                       case 3:
1049                           $kw = isset($kw) ? $kw : "save";
1050                           $Keypath = $this->tvwRegistry->SelectedNode->FullPath;
1051                           $file = $this->sfdExport->FileName;
1052                           if(System:::IO:::File::Exists($file)):
1053                                //If file exist delete is as reg does not delete it automatically
1054                                try{System:::IO:::File::Delete($file);}
1055                                catch(System:::Exception $ex){
1056                                    self::OnError($ex->Message);
1057                                    return;
1058                                }    
1059                           endif;
1060                           //Call reg to do the work
1061                           // because .NET does not have function for it
1062                           // doing it manually is too much work
1063                           //PHPBUG:
1064                           // using Win32 API cannot be done from Phalanger yet
1065                           $result = `reg $kw "$Keypath" "$file"`;//This curious feature of PHP requires at least 7 lines of code in C#/VB :-)
1066                           MessageBox::Show($result,Program::$Resources->ExportKey_t,MessageBoxButtons::OK,MessageBoxIcon::Information);
1067                       break;
1068                       case 2:
1069                            //To XML do it manually (it's not so hard)
1070                            //Create XML
1071                            $xml=self::KeysToXml(array(self::getRegistryKey($this->tvwRegistry->SelectedNode->FullPath)),true);
1072                            if(is_null($xml)) return;
1073                            try{
1074                                $xml->Save($this->sfdExport->FileName);//Save XML
1075                            }catch(System:::Exception $ex){
1076                                self::OnError($ex-Message);
1077                                return;
1078                            }
1079                            MessageBox::Show(Program::$Resources->ExportSuccessful,Program::$Resources->ExportKey_t,MessageBoxButtons::OK,MessageBoxIcon::Information);
1080                       break;
1081                       default: self::OnError(Program::$Resources->e_UnknownregistryFileType);
1082                    endswitch;
1083                endif;
1084            }
1085            //private static function GetKeyHandle(RegistryKey $key){
1086                //$KeyType = CLRTypeOf RegistryKey;
1087                //$hKey = $KeyType->GetField("hkey",System:::Reflection:::BindingFlags::NonPublic |  System:::Reflection:::BindingFlags::Instance);
1088                //$hKey = $hKey->GetValue($key);
1089                //return $hKey;
1090            //}
1091            ///<summary>XML namespace name for registry XML</summary>
1092            const XMLNS = "http://dzonny.cz/xml/registry";
1093            ///<summary>Converts registry keys to XML</summary>
1094            ///<param name="Keys">Keys tio convert>/param>
1095            ///<param name="Interactive">Allow user to recover from errors</param>
1096            ///<returns type="System:::Xml:::XmlDocument">XML document with exported nodes</returns>
1097            private static function KeysToXml(array $Keys,bool $Interactive){
1098                $doc = new System:::Xml:::XmlDocument();
1099                $ns=self::XMLNS;
1100                //Creating XML document this way is easier than using DOM
1101                $doc->LoadXml(<<<xmlstring
1102<?xml version="1.0"?>
1103<registry xmlns="$ns"/>
1104xmlstring
1105                );
1106                try{//Convert keys to XML
1107                    foreach($Keys as $key){//Ooops I've used C-syntax, I've must be drunken :-)
1108                        $keyXml = self::KeyToXml($key,$doc,$Interactive);//Get XML
1109                        $keyXml->SetAttribute("name",$key->Name);//Change name to be fully qualified
1110                        $doc->DocumentElement->AppendChild($keyXml,$Interactive);//Add to document
1111                    }
1112                }catch(System:::Exception $ex){return null;}
1113                catch(Exception $ex){return null;}
1114                return $doc;
1115            }
1116            ///<summary>COnverts single registry key to XML</summary>
1117            ///<param name="key">Key to export</param>
1118            ///<param name="doc">Document to create element for</param>
1119            ///<param name="Interactive">Allow user to recover form errros</prama>
1120            ///<returns type="System:::Xml:::XmlElement">Element with exported key</returns>
1121            private static function KeyToXml(RegistryKey $key,System:::Xml:::XmlDocument $doc,bool $Interactive){
1122                //Prepare message box buttons
1123                $mb=$interactive ? MessageBoxButtons::AbortRetryIgnore : MessageBoxButtons::OK;    
1124                //Create element
1125                $el = $doc->CreateElement("key",self::XMLNS);
1126                $parts = explode("\\",$key->Name);
1127                $el->SetAttribute("name",$parts[count($parts)-1]);//Name it
1128GetNames:       try{//Get value names
1129                    $names=$key->GetValueNames();
1130                }catch(System:::Exception $ex){
1131                    switch(self::OnError(Program::$Resources->e_EnlistValues($key->Name)."\r\n$ex->Message",$mb)):
1132                        case DialogResult::Retry: goto GetNames;
1133                        case DialogResult::Ignore: goto GetKNames;
1134                        default: throw new CLRException($ex);
1135                    endswitch;
1136                }
1137                foreach($names as $name)://Serialize values
1138GetValue:           unset($eln);
1139                    try{
1140                        switch($key->GetValueKind($name))://Depending on type
1141                            case RegistryValueKind::Binary://Base 64
1142                                $vel=$doc->CreateElement("binary",self::XMLNS);
1143                                $vel->InnerText = System:::Convert::ToBase64String($key->GetValue($name));
1144                            break;
1145                            case RegistryValueKind::QWord://This is easy
1146                                $eln="qword";
1147                            case RegistryValueKind::DWord:
1148                                $eln = isset($eln)?$eln:"dword";
1149                                $vel=$doc->CreateElement($eln,self::XMLNS);
1150                                //Just better use invariant culture to prevent arabicx or even georgian numerals in your XML
1151                                $vel->InnerText = $key->GetValue($name)->ToString("D",System:::Globalization:::CultureInfo::$InvariantCulture);
1152                            break;
1153                            case RegistryValueKind::ExpandString:
1154                                $eln="expand";
1155                            case RegistryValueKind::i'String':
1156                                $eln = isset($eln)?$eln:"string";
1157                                $vel=$doc->CreateElement($eln,self::XMLNS);
1158                                //String mkust be saved non-expanded
1159                                $vel->InnerText = $key->GetValue($name,null,RegistryValueOptions::DoNotExpandEnvironmentNames);
1160                            break;
1161                            case RegistryValueKind::MultiString:
1162                                //Store in multible subelements
1163                                $vel=$doc->CreateElement("multi",self::XMLNS);
1164                                foreach($key->GetValue($name) as $string):
1165                                    $subel=$doc->CreateElement("string",self::XMLNS);
1166                                    $subel->InnerText = $string;
1167                                    $vel->AppendChild($subel);
1168                                endforeach;
1169                            break;
1170                            default://Cannot store unknown
1171                                switch(self::OnError(Program::$Resources->e_GetValue($key->Name,$name)."\r\n$ex->Message",$mb)):
1172                                    case DialogResult::Retry: goto GetValue;
1173                                    case DialogResult::Ignore: continue 3;
1174                                    default: throw new CLRException($ex);
1175                                endswitch;
1176                        endswitch;
1177                    }catch(System:::Exception $ex){
1178                        switch(self::OnError(Program::$Resources->e_GetValue($key->Name,$name)."\r\n$ex->Message",$mb)):
1179                            case DialogResult::Retry: goto GetValue;
1180                            case DialogResult::Ignore: continue 2;
1181                            default: throw new CLRException($ex);
1182                        endswitch;
1183                    }
1184                    $el->AppendChild($vel);
1185                    $vel->SetAttribute("name",$name);
1186                endforeach; 
1187GetKNames:      try{//Get sub key names
1188                    $names=$key->GetSubKeyNames();
1189                }catch(System:::Exception $ex){
1190                    switch(self::OnError(Program::$Resources->e_EnlistSubKeys($key->Name)."\r\n$ex->Message",$mb)):
1191                        case DialogResult::Retry: goto GetKNames;
1192                        case DialogResult::Ignore: return $el;
1193                        default: throw new CLRException($ex);
1194                    endswitch;
1195                }
1196                foreach($names as $name)://Save sub keys
1197GetKey:             unset($SubKey);
1198                    try{
1199                        $SubKey = $key->OpenSubKey($name);//Open
1200                        $kel = self::KeyToXml($SubKey,$doc,$Interactive);//Recurse
1201                        $el->AppendChild($kel);
1202                    }catch(System:::Exception $ex){
1203                        switch(self::OnError(Program::$Resources->e_OpenSubKey($key->Name,$name)."\r\n$ex->Message",$mb)):
1204                            case DialogResult::Retry: goto GetKey;
1205                            case DialogResult::Ignore: break 2;
1206                            default: throw new CLRException($ex);
1207                        endswitch;
1208                    }
1209                    if(!is_null($SubKey))$SubKey->Close();
1210                endforeach;
1211                return $el;
1212            }
1213	    }    
1214    }
1215?>