PageRenderTime 57ms CodeModel.GetById 17ms RepoModel.GetById 0ms 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
Possible License(s): CPL-1.0, GPL-2.0, CC-BY-SA-3.0, MPL-2.0-no-copyleft-exception, Apache-2.0

Large files files are truncated, but you can click here to view the full 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. namespace RegEditPHP{
  7. ///<summary>Main application form</summary>
  8. partial class frmMain extends System:::Windows:::Forms:::Form{
  9. //This class represents main form of this application. It contains the most of the code.
  10. //Initialization
  11. ///<summary>This name is used as name and text of placeholder child key</summary>
  12. 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.
  13. ///<summary>Gives access to localized resources</summary>
  14. private $Resources;//See ResourceAccessor.php
  15. ///<summary>Internal initialization method</summary>
  16. private function Init(){
  17. //Call to this method wa manually added to __construct() in frmMain.php
  18. //Following commented lines are no longer needed because pictures now can be added form designer
  19. //$this->tmiNew->Image = Program::$Resources->new;
  20. //$this->tmiNewKey->Image = Program::$Resources->folder->ToBitmap();
  21. //$this->tmiNewDWord->Image =
  22. //$this->tmiNewQWord->Image = Program::$Resources->numeric->ToBitmap();
  23. //$this->tmiNewString->Image =
  24. //$this->tmiNewExpandable->Image =
  25. //$this->tmiNewMulti->Image = Program::$Resources->string->ToBitmap();
  26. //$this->tmiNewBinary->Image = Program::$Resources->binary->ToBitmap();
  27. //$this->tmiRename->Image = Program::$Resources->rename;
  28. //$this->tmiEdit->Image = Program::$Resources->edit;
  29. //$this->tmiDelete->Image = Program::$Resources->delete;
  30. //$this->tmiRefreshNode->Image = Program::$Resources->refresh;
  31. //Initialize sorter to sort list of values
  32. $this->Sorter = new Sorter($this->lvwRegistry);
  33. //Fill nodes added in designer with placeholder subnodes
  34. //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)
  35. foreach($this->tvwRegistry->Nodes as $node):
  36. $node->Nodes->Add(self::dummy)->Name=self::dummy;
  37. endforeach;
  38. }
  39. //Main menu
  40. //Handles click to Exit menu item
  41. private function tmiExit_Click(System:::Object $sender, System:::EventArgs $e) {
  42. $this->Close();//Close the main form (it terminates the application)
  43. }
  44. //Handles click to Language menu item
  45. private function tmiLanguage_Click(System:::Object $sender, System:::EventArgs $e) {
  46. //Create instance of form for language sleection
  47. $lSel = new LanguageSelector();
  48. //Show it and test if user clicked OK
  49. if($lSel->ShowDialog($this) == DialogResult::OK):
  50. //If so, the application must be restarted (changing UI strings without restart is too much work)
  51. //To restart the application we need know path of it - location of assembly
  52. //So get the assembly from type contained in it
  53. $MyType = CLRTypeOf frmMain;
  54. $MyPath = $MyType->Assembly->Location;
  55. try{ //Launch it
  56. System:::Diagnostics:::Process::Start($MyPath);
  57. }catch(System:::Exception $ex){ //This probably will not happen
  58. self::OnError(Program::$Resources->e_Restart."\r\n$ex->Message");
  59. return;
  60. }
  61. //If application successfully started, this can be closed
  62. $this->Close();
  63. endif;
  64. }
  65. //Handles click on the Jump menu item
  66. private function tmiJump_Click(System:::Object $sender, System:::EventArgs $e) {
  67. //get path to jump to using InputBox (see InputBox.user.php)
  68. $Path = InputBox::GetInput(Program::$Resources->JumpToKey,Program::$Resources->JumpToKey_t);
  69. if(!is_null($Path)) //InputBox::GetInput returns null on cancel
  70. $this->JumpToKey($Path);//Do the jump
  71. }
  72. //Handles click on the Refresh all menu item
  73. private function tmiRefreshAll_Click(System:::Object $sender, System:::EventArgs $e) {
  74. //Remember currently selected key
  75. $OldPath = $this->tvwRegistry->SelectedNode->FullPath;
  76. //Clear content of all top-level nodes
  77. foreach($this->tvwRegistry->Nodes as $node):
  78. $node->Nodes->Clear;
  79. //And add placeholder into each of them to appear expandable
  80. $node->Nodes->Add(self::dummy)->Name=self::dummy;
  81. endforeach;
  82. //For user convenience, navigate to node that was selected when reloading (I hate when reloading changes my status)
  83. $this->JumpToKey($OldPath);
  84. }
  85. ///<summary>Navigates to key with given path</summary>
  86. ///<param name="Path">Path full to navigate too</param>
  87. ///<returns type="bool">success</returns>
  88. private function JumpToKey(string $Path){
  89. //Firts trim trailing backslashes
  90. //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)
  91. while($Path->ToString()->EndsWith("\\"))
  92. $Path = $Path->ToString()->Substring(0,strlen($Path) - 1);
  93. //You can use many of PHP built-in function you are familiar with. Sometimes they are nicer/better tna .NET ones - as explode()
  94. $PathParts = explode("\\",$Path);
  95. $CurrentNodes = $this->tvwRegistry->Nodes;//Starting with top-level nodes
  96. foreach($PathParts as $Part)://For each path part
  97. foreach($CurrentNodes as $Node)://Wee look for node with same at current level
  98. 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
  99. $this->NoExpandQuestion=true;//Turn of expand question ofr HKCR
  100. $Node->Expand();//Expand node
  101. $this->NoExpandQuestion=false;//Turn the question back on
  102. $CurrentNodes = $Node->Nodes;//Dive one more level
  103. $LastNode = $Node;//Remember last succesfully found node (in case of failure, we will navigate to last-match level)
  104. continue 2;//Next (deeper) level
  105. endif;
  106. endforeach;
  107. self::OnError(Program::$Resources->e_KeyDoesNotExist($Path));//continue 2 above skips this line if node is found
  108. break;
  109. endforeach;
  110. if(!is_null($LastNode))://If there was matc at least at one level
  111. $this->tvwRegistry->SelectedNode = $LastNode;//select it
  112. $LastNode->EnsureVisible();//And scroll it to view
  113. return $LastNode->FullPath->ToLower() == $Path->ToString()->ToLower();//Only when all levels succeeded we consider jump successfull
  114. else:
  115. return false;//The root name was unknown
  116. endif;
  117. }
  118. //Hanndles click on the Jump to same key under ... menu item
  119. //It navigates to node with same addres but sapped first part HKLM<->HKCU (if possible)
  120. private function tmiJumpToSame_Click(System:::Object $sender, System:::EventArgs $e) {
  121. if($this->tvwRegistry->SelectedNode->FullPath->StartsWith("HKEY_CURRENT_USER\\")):
  122. $start=HKEY_CURRENT_USER;//current 1st part
  123. $new=HKEY_LOCAL_MACHINE;//new 1st part
  124. else:
  125. $new=HKEY_CURRENT_USER;//new 1st part
  126. $start=HKEY_LOCAL_MACHINE;//current 1st part
  127. endif;
  128. $new= $new . $this->tvwRegistry->SelectedNode->FullPath->Substring(strlen($start));///replace 1st part
  129. $this->JumpToKey($new);//Navigate to new path (if navigation is not possible, navigates as near as can)
  130. }
  131. //Handles dropdown opening of the Tools top-level menu item
  132. private function tmiTools_DropDownOpening(System:::Object $sender, System:::EventArgs $e) {
  133. //We enable/disable Jump to same menu item depending on if user is under HKCU or HKLM or not
  134. $this->tmiJumpToSame->Enabled = (
  135. $this->tvwRegistry->SelectedNode->FullPath->StartsWith("HKEY_CURRENT_USER\\") or
  136. $this->tvwRegistry->SelectedNode->FullPath->StartsWith("HKEY_LOCAL_MACHINE\\"));
  137. //And adjust text to show name of target hive (top-level node)
  138. if($this->tmiJumpToSame->Enabled and $this->tvwRegistry->SelectedNode->FullPath->StartsWith("HKEY_CURRENT_USER\\")):
  139. $this->tmiJumpToSame->Text = Program::$Resources->JumToSameKey("HKLM");
  140. elseif($this->tmiJumpToSame->Enabled):
  141. $this->tmiJumpToSame->Text = Program::$Resources->JumToSameKey("HKCU");
  142. endif;
  143. }
  144. //Handles click on About menu item
  145. private function tmiAbout_Click(System:::Object $sender, System:::EventArgs $e) {
  146. $ad=new dlgAbout();//Create about dialog
  147. $ad->ShowDialog($this);//And show it
  148. }
  149. //Tree
  150. ///<summary>If false, user is askt if (s)he really wants to expand HKCR</summary>
  151. private $NoExpandQuestion = false;
  152. //Called before tree node is expanded
  153. //Dummy sub keys are deleted in this event and actual node content is loaded
  154. private function tvwRegistry_BeforeExpand(System:::Object $sender, System:::Windows:::Forms:::TreeViewCancelEventArgs $e) {
  155. if(!$this->NoExpandQuestion and $e->Node->FullPath==HKEY_CLASSES_ROOT and $e->Node->Nodes->Count == 1):
  156. //PHPBUG:
  157. //We use Sorter::Iterate because indexing and default properties does not work well in current version of Phalanger
  158. if(Sorter::Iterate($e->Node->Nodes->GetEnumerator(),0)->Name == self::dummy):
  159. //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)
  160. if(MessageBox::Show(Program::$Resources->ExpandHKCR,Program::$Resources->ExpandHKCR_t,MessageBoxButtons::YesNo,MessageBoxIcon::Information) != DialogResult::Yes):
  161. //Cancels expanding (node will remain unexpanded)
  162. $e->Cancel = true;
  163. return;
  164. endif;
  165. endif;
  166. endif;
  167. //Test if node hals only one child - the Dummy
  168. if($e->Node->Nodes->Count==1):
  169. //Following 2 lines are same as Sorter::Iterate($e->Node->Nodes->GetEnumerator(),0) (legacy)
  170. $nem=$e->Node->Nodes->GetEnumerator();
  171. $nem->MoveNext();
  172. if($nem->Current->Name=self::dummy):
  173. //Need load childs from registry
  174. $this->Cursor = Cursors::$WaitCursor;//Set cursor to horglass (it may take a while)
  175. try{
  176. self::FillNode($e->Node);//Load subkeys
  177. }catch(System:::Exception $ex){
  178. $e->Node->Nodes->Clear();//When there is error, treat key as empty
  179. self::OnError($ex->Message);//Show error message
  180. }
  181. $this->Cursor = Cursors::$Default;//Revert cursor icon
  182. endif;
  183. endif;
  184. }
  185. //Caled after tree node is expanded
  186. private function tvwRegistry_AfterExpand(System:::Object $sender, System:::Windows:::Forms:::TreeViewEventArgs $e) {
  187. //We handle change of icon here
  188. //Note: Tree node has 2 icons - normal and selected
  189. // Both icons are taken from associated image list (set in designer)
  190. // and we want them to be the same, thus same key
  191. $e->Node->ImageKey="open";
  192. $e->Node->SelectedImageKey="open";
  193. }
  194. //Called after tree node is collapsed
  195. private function tvwRegistry_AfterCollapse(System:::Object $sender, System:::Windows:::Forms:::TreeViewEventArgs $e) {
  196. //Same as above, but change icon to closed
  197. $e->Node->ImageKey="closed";
  198. $e->Node->SelectedImageKey="closed";
  199. }
  200. ///<summary>Fills node with subnodes representing corresponding key subkeys</summary>
  201. ///<param name="Node">Node to fill</param>
  202. ///<returns type="void"/>
  203. private static function FillNode(TreeNode $Node){
  204. //1st clear any current content
  205. //It's typically dummy node, but for refresh it may be anything
  206. $Node->Nodes->Clear();
  207. //Get key for node path
  208. //Nice with TreeViewIs that it gives path of node which is immediatelly usable for registry lookup
  209. $Key=self::getRegistryKey($Node->FullPath);
  210. $SubKeys=$Key->GetSubKeyNames();//Enumerate sub keys (this may throw an exception and it must be handles by caller)
  211. foreach($SubKeys as $SubKeyName)://Add the keys to current node
  212. $SubKey = $Node->Nodes->Add($SubKeyName);//Add it (this creates node with given text, adds it and returns it)
  213. $SubKey->Name = $SubKeyName;//Set its name (it is used by Fullpath)
  214. $SubKey->ImageKey = "closed";//Set its image
  215. $SubKey->SelectedImageKey = "closed";
  216. $SubKey->Nodes->Add(self::dummy)->Name=self::dummy;//And fill it with dummy child (to seem to be axpandable)
  217. endforeach;
  218. //Note: Every node seems to be expandable if it has been never expanded
  219. // so user may experience following:
  220. // (S)he clicks the + and instead of node expanding, the + vanishes
  221. // it's not 100% good behavior, but is quite common and saves us some pre-testing
  222. }
  223. ///<summary>Gets registry key for its path</summary>
  224. ///<param name="Path">Full path of key to open</param>
  225. ///<param name="writeable">True to open key for writing, false to open it as readonly</param>
  226. ///<returns type="registryKey">Opened key</param>
  227. private static function getRegistryKey(string $Path,bool $writeable = false){
  228. //We must firts parse the path
  229. $parts=explode("\\",$Path);//Explode it by \
  230. //The bad thisng with .NET registry API is that there is no general-purpose function for opening registry key
  231. // The top-level keys (so-called hives) must be treated separatelly
  232. switch($parts[0])://So switch for them
  233. case HKCR://Shortcut names are not currently utilized by this program
  234. case HKEY_CLASSES_ROOT: $root=Registry::$ClassesRoot;
  235. break;
  236. case HKCU:
  237. case HKEY_CURRENT_USER: $root=Registry::$CurrentUser;
  238. break;
  239. case HKLM:
  240. case HKEY_LOCAL_MACHINE: $root=Registry::$LocalMachine;
  241. break;
  242. case HKU:
  243. case HKEY_USERS: $root=Registry::$Users;
  244. break;
  245. case HKCC:
  246. case HKEY_CURRENT_CONFIG: $root=Registry::$CurrentConfig;
  247. break;
  248. case HKDD:
  249. case HKEY_DYN_DATA: $root=Registry::$DynData;
  250. break;
  251. case HKPD:
  252. case HKEY_PERFORMANCE_DATA: $root=Registry::$PerformanceData;
  253. break;
  254. default: throw new CLRException(new ArgumentException(Program::$Resources->e_UnknownRoot));
  255. endswitch;
  256. if(count($parts)==1) return $root;//Return root for single-part path
  257. //Otherwise open root's sub key (now ve have general-purpose function to open multiplůe levels at once)
  258. return $root->OpenSubKey(i'String'::Join("\\",$parts,1,count($parts)-1),$writeable);
  259. //Note: Two uncommon things at line above:
  260. //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
  261. //2) I pass $parts (PHP array) where .NET String[] array is expected
  262. //PHPBUG:
  263. // It is strange, but Phalanger currently has not silent implicit conversion from string to System::String but has conversion of array to String[]
  264. }
  265. //Called after node in tree is selected
  266. private function tvwRegistry_AfterSelect(System:::Object $sender, System:::Windows:::Forms:::TreeViewEventArgs $e) {
  267. //We need show values in this key
  268. try{ //get key and show values for it
  269. $this->LoadValues(self::GetRegistryKey($e->Node->FullPath));
  270. }catch(System:::Exception $ex){
  271. self::OnError($ex->Message);
  272. }
  273. $this->tslKey->Text=$e->Node->FullPath;
  274. }
  275. ///<summary>Loads values for given key rto ListView>/summary>
  276. ///<param name="key">Key to load values from</param>
  277. ///<returns type="void"/>
  278. private function LoadValues(RegistryKey $key){
  279. //Gate value names (may cause Exception, must be caught by caller)
  280. $ValueNames=$key->GetValueNames();
  281. //Clear list
  282. $this->lvwRegistry->Items->Clear();
  283. foreach($ValueNames as $Name)://For each name
  284. $item=$this->lvwRegistry->Items->Add($Name);//Create and add item
  285. $item->Name=$Name;//Set its name
  286. switch($key->GetValueKind($Name))://We must support regtlue types separately
  287. case RegistryValueKind::Binary://Binary (got as array of bytes)
  288. $item->ImageKey="binary";
  289. $kind=REG_BINARY;//You know - undefined constant's
  290. $value=$key->GetValue($Name);
  291. if(!is_null($value))$value=self::GetBytesString($value,&$alt);//Call helper method to display byte array
  292. break;
  293. case RegistryValueKind::DWord://DWord (got as int32)
  294. $item->ImageKey="numeric";
  295. $kind=REG_DWORD;
  296. $value=$key->GetValue($Name);
  297. $alt="0x".$value->ToString("X");//Hexa
  298. $value=$value->ToString();//Decadic
  299. break;
  300. case RegistryValueKind::ExpandString://Expandable string (%something% is replaced by value of system variable something)
  301. $item->ImageKey="string";
  302. $kind=REG_EXPAND_SZ;
  303. $value=$key->GetValue($Name,null,RegistryValueOptions::DoNotExpandEnvironmentNames);//Unexpanded value
  304. $alt=$key->GetValue($Name);//Expanded value
  305. break;
  306. case RegistryValueKind::MultiString://MultistringgotasString
  307. $item->ImageKey="string";
  308. $kind=REG_MULTI_SZ;
  309. $value=$key->GetValue($Name);
  310. $value=System:::String::Join("; ",$value);//Just join it using semicolon
  311. $alt=$value;//No alternative sight
  312. break;
  313. case RegistryValueKind::QWord://QWord (got as int64) XP regedit does not support it, but we DO!
  314. $item->ImageKey="numeric";
  315. $kind=REG_QWORD;
  316. $value=$key->GetValue($Name);
  317. $alt="0x".$value->ToString("X");//Hexa
  318. $value=$value->ToString();//decadic
  319. break;
  320. case RegistryValueKind::i'String'://Normal string
  321. //PHPBUG:
  322. //This is littele inconsistent behavior:
  323. // While RegistryValueKind::String is invalid
  324. // System:::String is valid
  325. $item->ImageKey="string";
  326. $kind=REG_SZ;
  327. $value=$key->GetValue($Name);
  328. $alt=$value;//There is no alternative sight of plain text
  329. break;
  330. default://Unknown kind (tehre are more kinds of registry values - uncommon and I dunno what good for)
  331. //Just show that there is sth
  332. $item->ImageKey="unknown";
  333. $kind="unknown";
  334. $value=Program::$Resources->UnknownValue;
  335. $alt=Program::$Resources->UnknownValueAlt;
  336. endswitch;
  337. //Fill subitems (2nd, 3rd, 4th columns in list view)
  338. $item->SubItems->Add($kind);
  339. $item->SubItems->Add($value);
  340. $item->SubItems->Add($alt);
  341. endforeach;
  342. $this->lvwRegistry->Sort();//Sort list (sorting is actually provided by Sorter.php)
  343. $this->AfterSort();//Some after-sort settings
  344. }
  345. ///<summary>Gets display string for byte array</summary>
  346. ///<param name="bytes">Byte array to get values from</param>
  347. ///<param name="alt" type=´"string">Output parameter filled with alternative (decimal) representation of <paramref name="bytes"/></param>
  348. ///<returns type="string">String representation of byte array (2-digits hexadecimal numbers separated by spaces)</returns>
  349. private static function GetBytesString(i'Array' $bytes,&$alt){
  350. //Note: i'Array' means that we acceps System:::array, not PHP array
  351. //Output parameters passed by reference are possible in PHP as well, but not commonly used. This uis the rare case when it is useful
  352. // In phalanger you can use [out] attribute, whoch I'm not using here
  353. //StringBuolder is efficient way of concatenating strings in .NET
  354. $ret=new System:::Text:::StringBuilder(count($bytes)*3);//Initial capacity count*3 is not compulsory but is god for efficiency
  355. $ret2=new System:::Text:::StringBuilder(count($bytes)*3);//For decimal capacity as approximate
  356. foreach($bytes as $byte):
  357. if($ret->Length>0) $ret->Append(" ");//Add space, if non empty
  358. if($ret2->Length>0) $ret2->Append(" ");
  359. $ret->Append($byte->ToString("X2"));//Hexadecimal, 2 didigts
  360. $ret2->Append($byte->ToString());//Decimal
  361. endforeach;
  362. $alt=$ret2->ToString(); //StringBuilder must be converted to string explicitly
  363. return $ret->ToString();
  364. }
  365. //Handles click on list view collumn
  366. private function lvwRegistry_ColumnClick(System:::Object $sender, System:::Windows:::Forms:::ColumnClickEventArgs $e) {
  367. //We do sorting here
  368. if($this->Sorter->Column == $e->Column):
  369. //If user click same column 2nd or more times
  370. switch($this->Sorter->Order)://reverse order
  371. case SortOrder::Ascending:
  372. $this->Sorter->Order=SortOrder::Descending;
  373. break;
  374. default:$this->Sorter->Order=SortOrder::Ascending;
  375. endswitch;
  376. else://CHange sort column and set order to ASC
  377. $this->Sorter->Order=SortOrder::Ascending;
  378. $this->Sorter->Column = $e->Column;
  379. endif;
  380. $this->lvwRegistry->Sort();//Force apply sorting
  381. $this->AfterSort();//After sort operations
  382. }
  383. ///<summary>Does common after ListView sort operations</summary>
  384. ///<returns type="void"/>
  385. private function AfterSort(){
  386. foreach($this->lvwRegistry->Columns as $col)
  387. $col->ImageKey=null;//Remove sort image from all columns
  388. //And set it for the used one using direction-aware image
  389. Sorter::Iterate($this->lvwRegistry->Columns->GetEnumerator(),$this->Sorter->Column)->ImageKey = $this->Sorter->Order == SortOrder::Ascending ? 'asc' : 'desc';
  390. }
  391. //Context menu and actions
  392. //Called befor context menu is opened
  393. private function cmsContext_Opening(System:::Object $sender, System:::ComponentModel:::CancelEventArgs $e) {
  394. //Note: ContextMenuStrip is associated with TreeView and ListView in designer
  395. //We hide/show and enable/disable certain menu items here based on conditions
  396. //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.
  397. if($this->cmsContext->SourceControl === $this->tvwRegistry and is_null($this->tvwRegistry->SelectedNode)):
  398. //No TreeNode selected - this should not happedn
  399. $e->Cancel=true;//Do not show the menu
  400. return;
  401. endif;
  402. //Edit only for values
  403. $this->tmiEdit->Visible = (
  404. $this->cmsContext->SourceControl === $this->lvwRegistry and
  405. $this->lvwRegistry->SelectedItems->Count == 1);
  406. //No rename for no value selected or more values selected
  407. $this->tmiRename->Visible =
  408. !($this->cmsContext->SourceControl === $this->lvwRegistry and $this->lvwRegistry->SelectedItems->Count <> 1);
  409. //No delete for no values selected
  410. $this->tmiDelete->Visible =
  411. !($this->cmsContext->SourceControl === $this->lvwRegistry and $this->lvwRegistry->SelectedItems->Count == 0);
  412. //No delete for root
  413. $this->tmiDelete->Enabled =
  414. !($this->cmsContext->SourceControl === $this->tvwRegistry and is_null($this->tvwRegistry->SelectedNode->Parent));
  415. //Select all only for values
  416. $this->tmiSelectAll->Visible = $this->cmsContext->SourceControl === $this->lvwRegistry;
  417. //If there is nothig to select ...
  418. $this->tmiSelectAll->Enabled = $this->lvwRegistry->Items->Count > 0;
  419. //Refres only for nodes
  420. $this->tmiRefreshNode->Visible = $this->cmsContext->SourceControl === $this->tvwRegistry;
  421. //Copy only for single key or current values
  422. //Note: LIst veiw recognizec selected values and focused vakues
  423. $this->tmiCopyFullPath->Enabled = (
  424. $this->cmsContext->SourceControl === $this->tvwRegistry or
  425. ($this->cmsContext->SourceControl === $this->lvwRegistry and !is_null($this->lvwRegistry->FocusedItem)));
  426. }
  427. //Handles attempt to rename tree node
  428. private function tvwRegistry_BeforeLabelEdit(System:::Object $sender, System:::Windows:::Forms:::NodeLabelEditEventArgs $e) {
  429. //Top-level cannot be renamed
  430. $e->CancelEdit = $e->CancelEdit || is_null($e->Node->Parent);
  431. }
  432. //Hanldes confirmed rename of tree node
  433. private function tvwRegistry_AfterLabelEdit(System:::Object $sender, System:::Windows:::Forms:::NodeLabelEditEventArgs $e) {
  434. //Reaname can be cancled or it is not rename at all (user haven't changed text - indinated by null Label)
  435. if(is_null($e->Label) or $e->Label == $e->Node->Text) $e->CancelEdit=true;//No change
  436. if($e->CancelEdit) return;
  437. $e->CancelEdit = !$this->RenameKey($e->Node, $e->Label);//Try to rename
  438. }
  439. //Handles confirned rename of values
  440. private function lvwRegistry_AfterLabelEdit(System:::Object $sender, System:::Windows:::Forms:::LabelEditEventArgs $e) {
  441. //Same as rename of key - the event can indicate no rename at all (null Label)
  442. if(is_null($e->Label)) $e->CancelEdit = true;
  443. $node = Sorter::Iterate($this->lvwRegistry->Items->GetEnumerator(),$e->Item);
  444. if($node->Text == $e->Label) $e->CancelEdit = true;//No change
  445. if($e->CancelEdit) return;
  446. $e->CancelEdit = $this->RenameValue($node, $e->Label);//Try to rename
  447. }
  448. //Handles click on New key context menu item
  449. private function tmiNewKey_Click(System:::Object $sender, System:::EventArgs $e) {
  450. $this->AddKey();//Add the key
  451. }
  452. //Handles click on Rename context menu item
  453. private function tmiRename_Click(System:::Object $sender, System:::EventArgs $e) {
  454. //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)
  455. if($this->cmsContext->SourceControl === $this->tvwRegistry):
  456. $this->tvwRegistry->SelectedNode->BeginEdit();//Tree
  457. else:
  458. Sorter::Iterate($this->lvwRegistry->SelectedItems,0)->BeginEdit();//List
  459. endif;
  460. }
  461. //Handles click on Edit context menu item
  462. private function tmiEdit_Click(System:::Object $sender, System:::EventArgs $e) {
  463. $this->EditValue();//Do the edit
  464. }
  465. //Handles doble-click on list view item
  466. //Note: Some lama-users like to instruct system to activate list view item on single click instead of on double click (link behavior)
  467. // If your ListView follows this user decision can be set in designer
  468. private function lvwRegistry_ItemActivate(System:::Object $sender, System:::EventArgs $e) {
  469. $this->EditValue();//Edit value value
  470. }
  471. //Handles click on delete on context menu item
  472. private function tmiDelete_Click(System:::Object $sender, System:::EventArgs $e) {
  473. //We just detect control contet menu is shown on and call appropriate method
  474. if($this->cmsContext->SourceControl === $this->tvwRegistry):
  475. $this->DeleteKey();//Delete key
  476. else:
  477. $this->DeleteValues();//Delete value
  478. endif;
  479. }
  480. //Handles key down when tree view is active
  481. private function tvwRegistry_KeyDown(System:::Object $sender, System:::Windows:::Forms:::KeyEventArgs $e) {
  482. //We are intrested only in some keys
  483. //KeyData contains or combination of KeyCode and Ctrl+Alt+Shift state
  484. switch($e->KeyData):
  485. case Keys::Delete: $this->DeleteKey();//Delete key (w/o any Ctrl+Shift+Alt)
  486. break;
  487. case Keys::F2://F2 key (w/o ...)
  488. if(!is_null($this->tvwRegistry->SelectedNode) and !is_null($this->tvwRegistry->SelectedNode->Parent)):
  489. $this->tvwRegistry->SelectedNode->BeginEdit();
  490. endif;
  491. break;
  492. case Keys::F7: $this->AddKey();//F7 - I love Total Commander :-)
  493. endswitch;
  494. }
  495. //Handles key down on list view
  496. private function lvwRegistry_KeyDown(System:::Object $sender, System:::Windows:::Forms:::KeyEventArgs $e) {
  497. //Same note as abowe but we're interested in more keys
  498. switch($e->KeyData):
  499. case Keys::Delete: $this->DeleteValues();//delete
  500. break;
  501. case Keys::F2://rename
  502. if(!is_null($this->lvwRegistry->FocusedItem))
  503. $this->lvwRegistry->FocusedItem->BeginEdit();
  504. break;
  505. case Keys::Control | Keys::A://This is how to uttilize the key combination
  506. foreach($this->lvwRegistry->Items as $item)//Select all items
  507. $item->Selected=true;
  508. break;
  509. case Keys::F4://Edit (like in TC)
  510. case Keys::Enter: $this->EditValue();//Edit
  511. break;
  512. case Keys::Shift | Keys::F4://New string (like in TC)
  513. $this->Addvalue(RegistryValueKind::i'String');
  514. break;
  515. endswitch;
  516. }
  517. //Handles click on any of New value menu items
  518. //Note: More events can have sam handler and more hahndlers can be attached to single event in .NET
  519. private function tmiNewValue_Click(System:::Object $sender, System:::EventArgs $e) {
  520. //Detect kind from item being clicked
  521. if($sender === $this->tmiNewBinary)
  522. $kind = RegistryValueKind::Binary;
  523. elseif($sender === $this->tmiNewDWord)
  524. $kind = RegistryValueKind::DWord;
  525. elseif($sender === $this->tmiNewExpandable)
  526. $kind = RegistryValueKind::ExpandString;
  527. elseif($sender === $this->tmiNewMulti)
  528. $kind = RegistryValueKind::MultiString;
  529. elseif($sender === $this->tmiNewQWord)
  530. $kind = RegistryValueKind::QWord;
  531. elseif($sender === $this->tmiNewString)
  532. $kind = RegistryValueKind::i'String';
  533. $this->Addvalue($kind);//Do add
  534. }
  535. ///<summary>deletes currently selected registry key</summary>
  536. ///<returns type="bool">success</returns>
  537. private function DeleteKey(){
  538. $KeyName=$this->tvwRegistry->SelectedNode->Text;
  539. //Ask user
  540. if(MessageBox::Show(Program::$Resources->DeleteKey($KeyName),Program::$Resources->DeleteKey_t,MessageBoxButtons::YesNo,MessageBoxIcon::Question) == DialogResult::Yes):
  541. //We must open parent key and ask it to delete its child
  542. $parts=explode("\\",$this->tvwRegistry->SelectedNode->FullPath);
  543. $ParentPath=i'String'::Join("\\",$parts,0,count($parts)-1);//Parent path
  544. try{
  545. $ParentKey=$this->getRegistryKey($ParentPath,true);//Open parent
  546. }catch(System:::Exception $ex){
  547. self::OnError($ex->Message);
  548. return false;
  549. }
  550. try{
  551. $ParentKey->DeleteSubKeyTree($parts[count($parts)-1]);//Delete child
  552. }catch(System:::Exception $ex){
  553. self::OnError($ex->Message);
  554. return false;
  555. }
  556. $this->tvwRegistry->SelectedNode->Remove();//On success delete the node visualy as vell
  557. return true;
  558. else:
  559. return false;
  560. endif;
  561. }
  562. ///<summary>Deletes selected values</param>
  563. ///<returns type="bool">success</returns>
  564. private function DeleteValues(){
  565. //First chose message
  566. if($this->lvwRegistry->SelectedItems->Count == 1):
  567. //For one-value deletion as with value name
  568. $item1=Sorter::Iterate($this->lvwRegistry->SelectedItems->GetEnumerator(),0)->Text;
  569. $message= Program::$Resources->DeleteValue($item1);
  570. else://Otherwise simly ask on values
  571. $message=Program::$Resources->DeleteValues;
  572. endif;
  573. //Ask user
  574. if(MessageBox::Show($message,Program::$Resources->DeleteValues_t,MessageBoxButtons::YesNo,MessageBoxIcon::Question)==DialogResult::Yes):
  575. try{//Open key
  576. $key=$this->getRegistryKey($this->tvwRegistry->SelectedNode->FullPath,true);
  577. }catch(System:::Exception $ex){
  578. self::OnError($ex->Message);
  579. return false;
  580. }
  581. //Index of st selected item (to preselect something after deletion, users like it)
  582. $fsi=Sorter::Iterate($this->lvwRegistry->SelectedIndices->GetEnumerator(),0);
  583. //Number of items to delete (to detect skip)
  584. $todelcnt=$this->lvwRegistry->SelectedItems->Count;
  585. foreach($this->lvwRegistry->SelectedItems as $item):
  586. //Label is target for goto statement (Phalanger PHP extension)
  587. DeleteValue: try{
  588. $key->DeleteValue($item->Name);//Try to delete
  589. }catch(System:::Exception $ex){//On error ask user what to do
  590. switch(self::OnError(Program::$Resources->e_DeleteValue($item->Name)."\r\n$ex->Message",MessageBoxButtons::AbortRetryIgnore)):
  591. case DialogResult::Ignore: continue 2;//Ignore, just procede to next selected value
  592. case DialogResult::Retry: goto DeleteValue;//Retry (this is where goto is usefully).
  593. default: break 2;//Stop deleteing more values (exit foreach loop)
  594. endswitch;
  595. }
  596. $deleted[]=$item;//Remeber deleted items (to delete them visually as well)
  597. endforeach;
  598. foreach($deleted as $deleteditem)//Delete items visually
  599. $this->lvwRegistry->Items->Remove($deleteditem);
  600. //If all selected items was deleted
  601. // there is no selected item now
  602. // if there is somethign to select
  603. if(count($deleted) == $todelcnt and $this->lvwRegistry->Items->Count > 0):
  604. //We will select item near the deleted one
  605. $newIndex = max(min($this->lvwRegistry->Items->Count-1,$fsi-1),0);
  606. Sorter::Iterate($this->lvwRegistry->Items->GetEnumerator(),$newIndex)->Selected = true;
  607. endif;
  608. return count($deleted) > 0;//It may indicate success or semi-success
  609. else:
  610. return false;
  611. endif;
  612. }
  613. ///<summary>Adds key under curent key</summary>
  614. ///<param name="parent">Node (not key) to add key into</param>
  615. ///<param name="newname">Name of key to add. If empty user is asked.</param>
  616. ///<param name="norefresh">True to not add the key to TreeView and return it instead</param>
  617. ///<returns>Bool success; created key on success when norefresh was true</returns>
  618. private function AddKey($parent=null, string $newname="", bool $norefresh=false){
  619. //This is somewhat complicated because it ca operate in ineractive and non-interactive mode
  620. if(is_null($parent)):
  621. //Get parent node if we haven't got it
  622. if(is_null($this->tvwRegistry->SelectedNode)) return false;
  623. $parent = $this->tvwRegistry->SelectedNode;
  624. endif;
  625. $Name=$newname;
  626. if($newname<>"") goto testBS;//Do not ask for name
  627. //Ask for name
  628. EnterName: if(!is_null($Name=InputBox::GetInput(Program::$Resources->NameOfNewKey,Program::$Resources->CreateKey_t,$Name))):
  629. testBS: if($Name->Contains("\\"))://Name cannot contain \
  630. self::OnError(Program::$Resources->e_InvalidName($Name));
  631. if($newname=="")
  632. goto EnterName;//Interactive mode (repeat enter)
  633. else return false;
  634. endif;
  635. try{ //Open paren registry key for writing
  636. $parentkey=$this->getRegistryKey($parent->FullPath,true);
  637. }catch(System:::Exception $ex){
  638. $this->OnError($ex->Message);
  639. return false;
  640. }
  641. //Check if key to be created already exists
  642. try{
  643. $existing = $parentkey->OpenSubKey($Name,false);
  644. }catch(System:::Exception $ex){
  645. $this->OnError($ex->Message);
  646. return false;
  647. }
  648. if(!is_null($existing)):
  649. self::OnError(Program::$Resources->e_KeyExists($Name));
  650. return false;
  651. endif;
  652. try{//Create it
  653. $newKey = $parentkey->CreateSubKey($Name);
  654. }catch(System:::Exception $ex){
  655. $this->OnError($ex->Message);
  656. return false;
  657. }
  658. if($norefresh) return $newKey;//Not interactive, return created key to caller
  659. //Otherwise navigate to the new key
  660. $wasdummy =
  661. ($parent->Nodes->Count == 1 and Sorter::Iterate($parent->Nodes->GetEnumerator(),0)->Name=self::dummy);
  662. $parent->Expand();//Expand prent
  663. if($wasdummy)://If parent was not loaded, it is now loaded and contain newly created key
  664. foreach($parent->Nodes as $node)://search for it
  665. if($node->Name == $Name):
  666. $newNode=$node;//found
  667. break;
  668. endif;
  669. endforeach;
  670. else://Otherwise add it visually
  671. $newNode = $parent->Nodes->Add($Name);
  672. $newNode->Name = $Name;
  673. $newNode->ImageKey = "closed";
  674. $newNode->SelectedImageKey = "closed";
  675. endif;
  676. if (!is_null($newNode)):
  677. $this->tvwRegistry->SelectedNode = $newNode;//Select new key
  678. $newNode->EnsureVisible();//and scrollit to view
  679. endif;
  680. return true;
  681. else:
  682. return false;
  683. endif;
  684. }
  685. ///<summary>Adds value of given type</summary>
  686. ///<param name="type">Type of value to add</param>
  687. ///<returns type="bool">success</returns>
  688. private function Addvalue(int $type){
  689. //PHPBUG:
  690. //Note: We cannot declare $type as RegistryValueKind because it then will not assept values like RegistryValueKind::Binary (being it treated as int)
  691. $editor = new ValueEditor();//Create value editor
  692. $editor->SetValue(null,$type);//Initialize it
  693. $editor->NameReadOnly=false;//Allow enter of name
  694. if($editor->ShowDialog($this)==DialogResult::OK)://Show it
  695. try{
  696. //Open registry key to add value into
  697. $key=$this->getRegistryKey($this->tvwRegistry->SelectedNode->FullPath,true);
  698. try{//Check if value with same name exists
  699. $key->GetValueKind($editor->ValueName);
  700. self::OnError(Program::$Resources->e_ValueExists($editor->ValueName));
  701. return false;
  702. }catch(System:::IO:::IOException $ex){}//Non existent value is indicated by System:::IO:::IOExcpetion - and its what we want
  703. $key->SetValue($editor->ValueName,$editor->Value,$type);//Create it (create and change value are same operations)
  704. }catch(System:::Exception $ex){
  705. self::OnError($ex->Message);
  706. return false;
  707. }
  708. $this->LoadValues($key);//refresh list of values
  709. foreach($this->lvwRegistry->Items as $item)://Search for newly added
  710. if($item->Name==$editor->ValueName):
  711. $item->Selected=true;//select it
  712. $item->EnsureVisible();//and scroll it into view
  713. break;
  714. endif;
  715. endforeach;
  716. return true;
  717. else:
  718. return false;
  719. endif;
  720. }
  721. ///<summary>Attempts to rename registry key</summary>
  722. ///<param name="node">Tree node representing the key to reanem</param>
  723. ///<param name="newname>New name of key</param>
  724. ///<returns type="bool">success</returns>
  725. private function RenameKey(TreeNode $node, string $newname){
  726. //This is tricky operation because neither .NET registry API nor Win32 ASPI supports key rename
  727. // we must clone the key and then delete original
  728. //Create target for copy
  729. $newKey = $this->AddKey($node->Parent,$newname,true);
  730. if(is_bool($newKey)) return false;
  731. $copyed=false;
  732. $this->Cursor = Cursors::$WaitCursor;//Set cursor to hourglass
  733. try{
  734. $OldKey=$this->getRegistryKey($node->FullPath,false);//Open source key for reading
  735. $copyed = self::CopyKey($OldKey,$newKey,false);//Copy old key to new key
  736. }catch(System:::Exception $ex){
  737. self::OnError($ex->Message);
  738. }
  739. $this->Cursor = Cursors::$Default;//Reset cursor back to normal
  740. $OldKey->Close();//Closing old key is necessary to be done explicitly (PHP closes IDisposable for you somewhen), but open key cannot be deleted
  741. unset($OldKey);
  742. if($copyed):
  743. try{
  744. $pathparts = explode("\\",$node->FullPath);
  745. //Open old key's parent
  746. $parent = $this->getRegistryKey(System:::String::Join("\\",$pathparts,0,count($pathparts)-1),true);
  747. $parent->DeleteSubKeyTree($node->Name);//delete old key
  748. }catch(System:::Exception $ex){
  749. //Delete failed - now we have two identical keys, super :-(
  750. $TwoKeys=true;
  751. if(isset($parent)):
  752. //When we're puzzled, puzzle user as well - let him decide
  753. 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):
  754. try{//Delete old key
  755. $parent->DeleteSubKeyTree($newname);

Large files files are truncated, but you can click here to view the full file