PageRenderTime 9ms CodeModel.GetById 1ms app.highlight 3ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/CTreeViewControl.ahk

http://github.com/Skiouros/Macro
AutoHotKey | 590 lines | 375 code | 17 blank | 198 comment | 104 complexity | 139c9f701bff50daf8b5f95dc0b1833f MD5 | raw file
  1/*
  2Class: CTreeViewControl
  3A TreeView control.
  4
  5This control extends <CControl>. All basic properties and functions are implemented and documented in this class.
  6*/
  7Class CTreeViewControl Extends CControl
  8{
  9	__New(Name, ByRef Options, Text, GUINum)
 10	{
 11		;~ global CGUI
 12		Events := ["_Click", "_RightClick", "_EditingStart", "_FocusReceived", "_FocusLost", "_KeyPress", "_ItemExpanded", "_ItemCollapsed"]
 13		if(!InStr(Options, "AltSubmit")) ;Automagically add AltSubmit when necessary
 14		{
 15			for index, function in Events
 16			{
 17				if(IsFunc(CGUI.GUIList[GUINum][Name Function]))
 18				{
 19					Options .= " AltSubmit"
 20					break
 21				}
 22			}
 23		}
 24		base.__New(Name, Options, Text, GUINum)
 25		this._.Insert("ControlStyles", {Checked : 0x100, ReadOnly : -0x8, FullRowSelect : 0x1000, Buttons : 0x1, Lines : 0x2, HScroll : -0x8000, AlwaysShowSelection : 0x20, SingleExpand : 0x400, HotTrack : 0x200})
 26		this._.Insert("Events", ["DoubleClick", "EditingEnd", "ItemSelected", "Click", "RightClick", "EditingStart", "KeyPress", "ItemExpanded", "ItemCollapsed", "FocusReceived", "FocusLost"])
 27		this._.Insert("Messages", {0x004E : "Notify"}) ;This control uses WM_NOTIFY with NM_SETFOCUS and NM_KILLFOCUS
 28		this.Type := "TreeView"
 29	}
 30	
 31	PostCreate()
 32	{
 33		Base.PostCreate()
 34		this._.ImageListManager := new this.CImageListManager(this.GUINum, this.hwnd)
 35		this._.Items := new this.CItem(0, this.GUINum, this.hwnd)
 36		this._.PreviouslySelectedItem := this._.Items
 37	}
 38	/*
 39	Function: FindItem
 40	Finds an item by its ID.
 41	
 42	Parameters:
 43		ID - The ID of the item.
 44	*/
 45	FindItem(ID, Root = "")
 46	{
 47		if(!ID) ;Root node
 48			return this.Items
 49		if(!IsObject(Root))
 50			Root := this.Items
 51		if(ID = Root.ID)
 52			return Root
 53		Loop % Root.MaxIndex()
 54			if(result := this.FindItem(ID, Root[A_Index]))
 55				return result
 56		return 0
 57		
 58	}
 59	/*
 60	Variable: Items
 61	Contains the nodes of the tree. Each level can be iterated and indexed. A node is of type <CTreeViewControl.CItem>
 62	
 63	Variable: SelectedItem
 64	Contains the node of type <CItem> that is currently selected.
 65	
 66	Variable: PreviouslySelectedItem
 67	Contains the node of type <CItem> that was previously selected.
 68	*/
 69	__Get(Name, Params*)
 70	{
 71		;~ global CGUI		
 72		if(Name = "Items")
 73			Value := this._.Items
 74		else if(Name = "SelectedItem")
 75		{			
 76			GUI := CGUI.GUIList[this.GUINum]
 77			if(GUI.IsDestroyed)
 78				return
 79			Gui, % this.GUINum ":Default"
 80			Gui, TreeView, % this.ClassNN
 81			Value := this.FindItem(TV_GetSelection())
 82		}
 83		Loop % Params.MaxIndex()
 84			if(IsObject(Value)) ;Fix unlucky multi parameter __GET
 85				Value := Value[Params[A_Index]]
 86		if(Value)
 87			return Value
 88	}
 89	
 90	__Set(Name, Value)
 91	{
 92		;~ global CGUI
 93		if(!CGUI.GUIList[this.GUINum].IsDestroyed)
 94		{
 95			DetectHidden := A_DetectHiddenWindows
 96			DetectHiddenWindows, On
 97			Handled := true
 98			if(Name = "SelectedItem")
 99			{
100				GUI := CGUI.GUIList[this.GUINum]
101				Gui, % this.GUINum ":Default"
102				Gui, TreeView, % this.ClassNN
103				TV_Modify(Value._.ID)
104				this.ProcessSubControlState(this._.PreviouslySelectedItem, this.SelectedItem)
105				this._.PreviouslySelectedItem := this.SelectedItem
106			}
107			else
108				Handled := false
109			if(!DetectHidden)
110				DetectHiddenWindows, Off
111			if(Handled)
112				return Value
113		}
114	}
115	/*
116	Event: Introduction
117	To handle control events you need to create a function with this naming scheme in your window class: ControlName_EventName(params)
118	The parameters depend on the event and there may not be params at all in some cases.
119	Additionally it is required to create a label with this naming scheme: GUIName_ControlName
120	GUIName is the name of the window class that extends CGUI. The label simply needs to call CGUI.HandleEvent(). 
121	For better readability labels may be chained since they all execute the same code.
122	Instead of using ControlName_EventName() you may also call <CControl.RegisterEvent> on a control instance to register a different event function name.
123	
124	Event: Click(Item)
125	Invoked when the user clicked on the control.
126	
127	Event: DoubleClick(Item)
128	Invoked when the user double-clicked on the control.
129	
130	Event: RightClick(Item)
131	Invoked when the user right-clicked on the control.
132	
133	Event: EditingStart(Item)
134	Invoked when the user started editing a node.
135	
136	Event: EditingEnd(Item)
137	Invoked when the user finished editing a node.
138	
139	Event: ItemSelected(Item)
140	Invoked when the user selected a node.
141	
142	Event: ItemExpanded(Item)
143	Invoked when the user expanded a node.
144	
145	Event: ItemCollapsed(Item)
146	Invoked when the user collapsed a node.
147	
148	Event: KeyPress(KeyCode)
149	Invoked when the user pressed a key while the control was focused.
150	*/
151	HandleEvent(Event)
152	{
153		;~ global CGUI
154		if(CGUI.GUIList[this.GUINum].IsDestroyed)
155			return
156		;Handle visibility of controls associated with tree nodees
157		if(Event.GUIEvent = "S")
158		{
159			this.ProcessSubControlState(this.PreviouslySelectedItem, this.SelectedItem)
160		}
161		if(Event.GUIEvent == "E")
162			this.CallEvent("EditingStart", this.Items.ItemByID(Event.EventInfo))
163		else if(EventName := {DoubleClick : "DoubleClick", e : "EditingEnd", S : "ItemSelected", Normal : "Click", RightClick : "RightClick", "+" : "ItemExpanded", "-" : "ItemCollapsed"}[Event.GUIEvent])
164			this.CallEvent(EventName, this.Items.ItemByID(Event.EventInfo))
165		else if(EventName = "K")
166			this.CallEvent(EventName, Event.EventInfo)
167		else if(Event.GUIEvent == "F")
168			this.CallEvent("FocusReceived")
169		else if(Event.GUIEvent == "f")
170			this.CallEvent("FocusLost")
171		if(Event.GUIEvent = "S")			
172			this.PreviouslySelectedItem := this.SelectedItem
173	}
174	
175	/*
176	Class: CTreeViewControl.CItem
177	A tree node.
178	*/
179	Class CItem
180	{
181		__New(ID, GUINum, hwnd)
182		{
183			this.Insert("_", {})
184			this._.Insert("GUINum", GUINum)
185			this._.Insert("hwnd", hwnd)
186			this._.Insert("ID", ID)
187			this._.Insert("Controls", {})
188		}
189		/*
190			Function: Add
191			Adds a new item to the TreeView.
192			
193			Parameters:
194				Text - The text of the item.
195				Options - Various options, see Autohotkey TreeView documentation
196			
197			Returns:
198			An object of type CItem representing the newly added item.
199		*/
200		Add(Text, Options = "")
201		{
202			;~ global CGUI, CTreeViewControl
203			GUI := CGUI.GUIList[this._.GUINum]
204			if(GUI.IsDestroyed)
205				return
206			Control := GUI.Controls[this._.hwnd]
207			Gui, % this._.GUINum ":Default"
208			Gui, TreeView, % Control.ClassNN
209			ID := TV_Add(Text, this.ID, Options)
210			Item := new CTreeViewControl.CItem(ID, this._.GUINum, this._.hwnd)
211			;~ Item.Icon := ""
212			this.Insert(Item)
213			return Item
214		}
215		
216		/*
217		Function: AddControl
218		Adds a control to this tree node that will be visible only when this node is selected. The parameters correspond to the Add() function of CGUI.
219		
220		Parameters:
221			Type - The type of the control.
222			Name - The name of the control.
223			Options - Options used for creating the control.
224			Text - The text of the control.
225			UseEnabledState - If true, the control will be enabled/disabled instead of visible/hidden.
226		*/
227		AddControl(type, Name, Options, Text, UseEnabledState = 0)
228		{
229			;~ global CGUI
230			GUI := CGUI.GUIList[this._.GUINum]
231			if(!this.Selected)
232				Options .= UseEnabledState ? " Disabled" : " Hidden"
233			Control := GUI.AddControl(type, Name, Options, Text, this._.Controls)
234			Control._.UseEnabledState := UseEnabledState
235			Control.hParentControl := this._.hwnd
236			return Control
237		}
238		
239		/*
240			Function: Remove
241			Removes an item.
242			
243			Parameters:
244				ObjectOrIndex - The item object or the index of the child item of this.
245		*/
246		Remove(ObjectOrIndex)
247		{
248			;~ global CGUI
249			GUI := CGUI.GUIList[this._.GUINum]
250			if(GUI.IsDestroyed)
251				return
252			Control := GUI.Controls[this._.hwnd]
253			Gui, % this._.GUINum ":Default"
254			Gui, TreeView, % Control.ClassNN
255			if(!IsObject(ObjectOrIndex)) ;If index, get object and then handle
256				ObjectOrIndex := this[ObjectOrIndex]
257			if(ObjectOrIndex.ID = 0) ;Don't delete root node
258				return
259			if(ObjectOrIndex.Selected)
260				WasSelected := true
261			p := ObjectOrIndex.Parent
262			for Index, Item in ObjectOrIndex.Parent
263				if(Item = ObjectOrIndex)
264				{
265					ObjectOrIndex.Parent._Remove(A_Index)
266					break
267				}
268			TV_Delete(ObjectOrIndex.ID)
269			if(WasSelected)
270			{
271				Control.ProcessSubControlState(ObjectOrIndex, this.SelectedItem)
272				Control.PreviouslySelectedItem := ObjectOrIndex ;The node is accessible here even though it does not exist anymore because the user might have stored data in it that might need to be used in _ItemSelected handler.
273			}
274			if(TV_GetCount() = 0) ;If all TreeView items are deleted, fire a selection changed event
275				if(IsFunc(GUI[Control.Name "_ItemSelected"]))
276				{
277					ErrorLevel := ErrLevel
278					GUI[Control.Name "_ItemSelected"](Control.Items)
279					if(!Critical)
280						Critical, Off
281					return
282				}
283		}
284		/*
285		Function: Move
286		Moves an Item to another position.
287		
288		Parameters:
289			Position - The new (one-based) - position in the child items of Parent.
290			Parent - The item will be inserted as child of the Parent item. Leave empty to use its current parent.
291		*/
292		Move(Position=1, Parent = "")
293		{
294			;~ global CGUI
295			GUI := CGUI.GUIList[this._.GUINum]
296			if(GUI.IsDestroyed)
297				return
298			Control := GUI.Controls[this._.hwnd]
299			Gui, % this._.GUINum ":Default"
300			Gui, TreeView, % Control.ClassNN
301			
302			;Backup properties which are stored in the TreeList itself
303			Text := this.Text
304			Bold := this.bold
305			Expanded := this.Expanded
306			Checked := this.Checked
307			Selected := this.Selected
308			OldID := this.ID
309			
310			;If no parent is specified, the item will be moved on the current level
311			if(!Parent)
312				Parent := this.Parent
313			OldParent := this.Parent
314			
315			;Add new node. At this point there are two nodes.
316			NewID := TV_Add(Text, Parent.ID, (Position = 1 ? "First" : Parent[Position-1].ID) " " (Bold ? "+Bold" : "") (Expanded ?  "Expand" : "") (Checked ? "Check" : "") (Selected ? "Select" : ""))
317			
318			;Collect all child items
319			Childs := []
320			for index, Item in this
321				Childs.Insert(Item)
322			
323			this._.ID := NewID
324			
325			;Remove old parent node link and set the new one
326			if(OldParent != Parent)
327			{
328				for Index, Item in OldParent
329					if(Item = this)
330					{
331						OldParent.Remove(A_Index)
332						break
333					}
334				Parent.Insert(Position, this)
335			}
336			
337			if(this.Icon)
338				Control._.ImageListManager.SetIcon(this._.ID, this.Icon, this.IconNumber)
339			
340			;Move child items
341			for index, Item in Childs
342				Item.Move(index, this)
343			
344			;Delete old tree node
345			TV_Delete(OldID)
346		}
347		/*
348		Function: SetIcon
349		Sets the icon of a tree node
350		
351		Parameters:
352			Filename - The filename of the file containing the icon.
353			IconNumberOrTransparencyColor - The icon number or the transparency color if the used file has no transparency support.
354		*/
355		SetIcon(Filename, IconNumberOrTransparencyColor = 1)
356		{
357			;~ global CGUI
358			GUI := CGUI.GUIList[this._.GUINum]
359			if(GUI.IsDestroyed)
360				return
361			Control := GUI.Controls[this._.hwnd]
362			Control._.ImageListManager.SetIcon(this._.ID, Filename, IconNumberOrTransparencyColor)
363			this._.Icon := Filename
364			this._.IconNumber := IconNumberOrTransparencyColor
365		}
366		/*
367		Function: MaxIndex
368		Returns the number of child nodes.		
369		*/
370		MaxIndex()
371		{
372			;~ global CGUI
373			GUI := CGUI.GUIList[this._.GUINum]
374			if(GUI.IsDestroyed)
375				return
376			Control := GUI.Controls[this._.hwnd]
377			Gui, % this._.GUINum ":Default"
378			Gui, TreeView, % Control.ClassNN
379			current := this._.ID ? TV_GetChild(this._.ID) : TV_GetNext() ;Get first child or first top node
380			if(!current)
381				return 0 ;No children
382			count := 0
383			while(current && current := TV_GetNext(current))
384				count++
385			return count + 1
386		}
387		
388		/*
389		Function: ItemByID
390		Access a child item by its ID.
391		
392		Parameters:
393			ID - The ID of the child item
394		*/
395		;Access a child item by its ID
396		ItemByID(ID)
397		{
398			Loop % this.MaxIndex()
399			{
400				if(this[A_Index]._.ID = ID)
401					return this[A_Index]
402				else if(Item := this[A_Index].ItemByID(ID))
403					return Item
404			}
405		}
406		_NewEnum()
407		{
408			;~ global CEnumerator
409			return new CEnumerator(this)
410		}
411		
412		/*
413		Variable: 1,2,3,4,...
414		The child nodes of a tree node may be accessed by their index, e.g. this.TreeView1.Items[1][2][3].Text := "AHK"
415		
416		Variable: CheckedItems
417		An array containing all checked child nodes of type <CTreeViewControl.CItem>.
418		
419		Variable: CheckedIndices
420		An array containing all checked child indices.
421		
422		Variable: Parent
423		The parent node of this node.
424		
425		Variable: ID
426		The ID used internally in the TreeView control.
427		
428		Variable: Icon
429		The path of an icon assigned to this node.
430		
431		Variable: IconNumber
432		The icon number used when an icon file contains more than one icon.
433		
434		Variable: Count
435		The number of child nodes.
436		
437		Variable: HasChildren
438		True if there is at least one child node.
439		
440		Variable: Text
441		The text of this tree node.
442		
443		Variable: Checked
444		True if the tree node is checked.
445		
446		Variable: Selected
447		True if the tree node is selected.
448		
449		Variable: Expanded
450		True if the tree node is expanded.
451		
452		Variable: Bold
453		If true, the text of this node is bold.
454		*/
455		__Get(Name, Params*)
456		{
457			;~ global CTreeViewControl, CGUI
458			if(Name != "_")
459			{
460				GUI := CGUI.GUIList[this._.GUINum]
461				if(!GUI.IsDestroyed)
462				{					
463					;~ if Name is Integer ;get a child node
464					;~ {
465						;~ if(Name <= this.MaxIndex())
466						;~ {
467							;~ Control := GUI.Controls[this._.hwnd]
468							;~ Gui, % this._.GUINum ":Default"
469							;~ Gui, TreeView, % Control.ClassNN
470							;~ child := TV_GetChild(this._.ID) ;Find child node id
471							;~ Loop % Name - 1
472								;~ child := TV_GetNext(child)
473							;~ Value := new CTreeViewControl.CItem(child, this._.GUINum, this._.hwnd)
474						;~ }
475					;~ }
476					if(Name = "CheckedItems")
477					{
478						Value := []
479						for index, Item in this
480							if(Item.Checked)
481								Value.Insert(Item)				
482					}
483					else if(Name = "CheckedIndices")
484					{
485						Value := []
486						for index, Item in this
487							if(Item.Checked)
488								Value.Insert(index)				
489					}
490					else if(Name = "Parent")
491					{
492						Control := GUI.Controls[this._.hwnd]
493						Gui, % this._.GUINum ":Default"
494						Gui, TreeView, % Control.ClassNN
495						VaLue := Control.FindItem(TV_GetParent(this._.ID))
496					}
497					else if(Name = "ID" || Name = "Icon" || Name = "IconNumber")
498						Value := this._[Name]
499					else if(Name = "Count")
500						Value := this.MaxIndex()
501					else if(Name = "HasChildren")
502					{
503						Control := GUI.Controls[this._.hwnd]
504						Gui, % this._.GUINum ":Default"
505						Gui, TreeView, % Control.ClassNN
506						Value := TV_GetChild(this._.ID) > 0
507					}
508					else if(Name = "Text")
509					{
510						Control := GUI.Controls[this._.hwnd]
511						Gui, % this._.GUINum ":Default"
512						Gui, TreeView, % Control.ClassNN
513						TV_GetText(Value, this._.ID)
514					}
515					else if(Name = "Checked" || Name = "Expanded" || Name = "Bold")
516					{
517						Control := GUI.Controls[this._.hwnd]
518						Gui, % this._.GUINum ":Default"
519						Gui, TreeView, % Control.ClassNN
520						Value := TV_Get(this._.ID, Name) > 0
521					}
522					else if(Name = "Selected")
523					{
524						Control := GUI.Controls[this._.hwnd]
525						Gui, % this._.GUINum ":Default"
526						Gui, TreeView, % Control.ClassNN
527						Value := TV_GetSelection() = this._.ID
528					}
529					else if(Name = "Controls")
530						Value := this._.Controls
531					Loop % Params.MaxIndex()
532						if(IsObject(Value)) ;Fix unlucky multi parameter __GET
533							Value := Value[Params[A_Index]]
534					if(Value)
535						return Value
536				}
537			}
538		}
539		__Set(Name, Params*)
540		{
541			;~ global CGUI
542			Value := Params[Params.MaxIndex()]
543			Params.Remove(Params.MaxIndex())
544			GUI := CGUI.GUIList[this._.GUINum]
545			if(!GUI.IsDestroyed)
546			{
547				if(Name = "Text")
548				{
549					Control := GUI.Controls[this._.hwnd]
550					Gui, % this._.GUINum ":Default"
551					Gui, TreeView, % Control.ClassNN
552					TV_Modify(this._.ID, "", Value)
553					return Value
554				}
555				else if(Name = "Selected") ;Deselecting is not possible it seems
556				{
557					if(Value = 1)
558					{
559						Control := GUI.Controls[this._.hwnd]
560						Gui, % this._.GUINum ":Default"
561						Gui, TreeView, % Control.ClassNN
562						TV_Modify(this._.ID)
563						Control.ProcessSubControlState(Control._.PreviouslySelectedItem, Control.SelectedItem)
564						Control._.PreviouslySelectedItem := Control.SelectedItem
565					}
566					return Value
567				}
568				else if(Option := {Checked : "Check", Expanded : "Expand", Bold : "Bold"}[Name]) ;Wee, check and remapping in one step
569				{
570					Control := GUI.Controls[this._.hwnd]
571					Gui, % this._.GUINum ":Default"
572					Gui, TreeView, % Control.ClassNN				
573					TV_Modify(this._.ID, (Value = 1 ? "+" : "-") Option)
574				}
575				else if(Name = "Icon")
576				{
577					this.SetIcon(Value, this._.HasKey("IconNumber") ? this._.IconNumber : 1)
578					return Value
579				}
580				else if(Name = "IconNumber")
581				{
582					this._.IconNumber := Value
583					if(this._.Icon)
584						this.SetIcon(this.Icon, Value)
585					return Value
586				}
587			}
588		}
589	}
590}