extrras/gui/CheckListBox: Fixed NotifyChanged to give proper 'row' for hierarchy
[sdk] / extras / gui / controls / CheckListBox.ec
1 import "ecere"
2
3 class CheckListBoxButton : Button
4 {
5    isCheckbox = true, inactive = true, size = { 12, 12 };
6
7    bool CheckListBox::NotifyPushed(Button button, int x, int y, Modifiers mods)
8    {
9       currentRow = (DataRow)button.id;
10       ToggleCheck(currentRow);
11       return false;
12    }
13
14    bool CheckListBox::NotifyReleased(Button button, int x, int y, Modifiers mods)
15    {
16       return false;
17    }
18
19    bool OnMouseOver(int x, int y, Modifiers mods)
20    {
21
22       return true;
23    }
24
25    bool OnMouseLeave(Modifiers mods)
26    {
27
28       return true;
29    }
30 }
31
32 class CheckListBox : ListBox
33 {
34    Map<int, CheckListBoxButton> buttonMaps { };
35    AVLTree<DataRow> rowChecks { };
36    int checkIndent;
37
38    checkIndent = 20;
39
40    fullRowSelect = false, collapseControl = true, treeBranches = true, rootCollapseButton = true, 
41    noDragging = true;
42    // rowHeight = 18;
43
44    void OnDestroy()
45    {
46       buttonMaps.RemoveAll();
47    }
48    
49    bool NotifyCollapse(CheckListBox listBox, DataRow row, bool collapsed)
50    {
51       DataRow r;
52       for(r = row.firstRow; r && r != row; )
53       {
54          if(collapsed)
55          {
56             MapIterator<int, Button> it { map = listBox.buttonMaps };
57             if(it.Index((int)r, false))
58             {
59                Button checkBox = it.data;
60                if(checkBox)
61                {
62                   checkBox.Destroy(0);
63                   it.Remove();
64                }
65             }
66          }
67          else
68          {
69             listBox.SetupButtons(r, false);
70          }
71          if(r.firstRow && !r.collapsed) 
72             r = r.firstRow;
73          else 
74             for(; r != row; r = r.parent)
75                if(r.next) { r = r.next; break; }
76       }
77       for(r = row.GetNextRow(); r; r = r.GetNextRow())
78       {
79          Button checkBox = listBox.buttonMaps[(int)r];
80          checkBox.position.y = 1 + (r.index + listBox.hasHeader) * listBox.rowHeight;
81       }
82       return true;
83    }
84
85    bool CheckPartialChecks(DataRow row)
86    {
87       DataRow r;
88       for(r = row.firstRow; r; r = r.next)
89       {
90          if(rowChecks.Find(r) || CheckPartialChecks(r))
91             return true;
92       }
93       return false;
94    }
95    
96    void SetupButtons(DataRow row, bool recurse)
97    {
98       DataRow parent;
99       CheckListBoxButton button;
100       int indent = checkIndent;
101
102       for(parent = row.parent; parent; parent = parent.parent) indent += 20;
103       button = buttonMaps[(int)row];
104       if(!button) button = CheckListBoxButton { this };
105       button.position = { 2 + indent, 1+(row.index + hasHeader) * rowHeight };
106       button.id = (int)row;
107
108       for(parent = row; parent; parent = parent.parent) if(rowChecks.Find(parent)) break;
109       if(parent)
110       {
111          button.checked = true;
112          button.buttonState = up;
113       }
114       else
115       {
116          button.checked = CheckPartialChecks(row);
117          button.buttonState = button.checked ? down : up;
118       }
119       button.Create();
120       buttonMaps[(int)row] = button;
121       if(recurse && !row.collapsed)
122       {
123          DataRow r;
124          for(r = row.firstRow; r; r = r.next)
125             SetupButtons(r, recurse);
126       }
127    }
128
129    void UpdateButtons()
130    {
131       DataRow row;
132       for(row = firstRow; row; row = row.next)
133          SetupButtons(row, true);
134    }
135
136    bool OnCreate()
137    {
138       if(ListBox::OnCreate())
139       {
140          DataRow row;
141          
142          buttonMaps.RemoveAll();
143
144          for(row = firstRow; row; row = row.next)
145             SetupButtons(row, true);
146          return true;
147       }
148       return false;
149    }
150
151    void ToggleCheck(DataRow row)
152    {
153       CheckListBoxButton checkBox = buttonMaps[(int)row];
154       if(checkBox)
155       {
156          bool checked = false;
157          DataRow r;
158          for(r = row; r; r = r.parent)
159             if(rowChecks.Find(r))
160             { 
161                checked = true;
162                break;
163             }         
164          SetCheck(row, !checked);
165       }
166    }
167
168    void UncheckBoxes(DataRow row)
169    {
170       CheckListBoxButton button = buttonMaps[(int)row];
171       if(button) { button.checked = false; button.buttonState = up; }
172       if(!row.collapsed)
173       {
174          DataRow r;
175          for(r = row.firstRow; r; r = r.next)
176             UncheckBoxes(r);
177       }
178    }
179    
180    void UnsetChildren(DataRow row)
181    {
182       DataRow r;
183       CheckListBoxButton button = buttonMaps[(int)row];
184       if(button) { button.checked = true; button.buttonState = up; }
185
186       for(r = row.firstRow; r; r = r.next)
187       {
188          Iterator<DataRow> it { rowChecks };
189          if(it.Find(r))
190             it.Remove();
191          UnsetChildren(r);
192       }      
193    }
194    
195    void SetCheck(DataRow row, bool checked)
196    {
197       DataRow parent;
198       bool wasChecked = false;
199
200       for(parent = row; parent; parent = parent.parent)
201       {
202          if(rowChecks.Find(parent)) { wasChecked = true; break; }
203       }
204       if(checked != wasChecked)
205       {
206          modifiedDocument = true;
207          // NotifyChanged(master, this, row);
208          if(checked)
209          {
210             // Check if all siblings are checked, if so go up until we reach a row not fully checked
211             while(row)
212             {
213                DataRow r;
214                for(r = row.parent.firstRow; r; r = r.next)
215                {
216                   if(r != row && !rowChecks.Find(r))
217                      break;
218                }
219                if(r || !row.parent) break;
220                row = row.parent;
221             }
222
223             // Take out all children from rowChecks, checking them all
224             UnsetChildren(row);
225
226             rowChecks.Add(row);            
227
228             for(parent = row.parent; parent; parent = parent.parent)
229             {
230                DataRow r;
231                CheckListBoxButton button = buttonMaps[(int)parent];
232                if(button)
233                {
234                   button.checked = true;
235                   button.buttonState = down;
236                }
237             }
238          }
239          else
240          {
241             DataRow rr = row;
242             UncheckBoxes(row);
243             parent = row.parent;
244
245             while(rr)
246             {
247                Iterator<DataRow> it { rowChecks };
248                if(it.Find(rr))
249                {
250                   it.Remove();
251                   break;
252                }
253                else
254                {
255                   DataRow r;
256                   for(r = row.parent.firstRow; r; r = r.next)
257                   {
258                      if(r != rr)
259                         rowChecks.Add(r);
260                   }
261                   rr = rr.parent;
262                }
263             }
264
265             for(; parent; parent = parent.parent)
266             {
267                CheckListBoxButton button = buttonMaps[(int)parent];
268                if(button)
269                {
270                   if(CheckPartialChecks(parent))
271                   {
272                      button.checked = true;
273                      button.buttonState = down;
274                   }
275                   else
276                   {
277                      button.checked = false;
278                      button.buttonState = up;
279                   }
280                }
281             }
282          }
283
284          NotifyChanged(master, this, row);
285       }
286    }
287    
288    bool NotifyKeyDown(CheckListBox listBox, DataRow row, Key key, unichar ch)
289    {
290       if(key == space)
291       {
292          listBox.ToggleCheck(row);
293          return false;
294       }
295       return true;
296    }
297
298    bool OnKeyHit(Key key, unichar ch)
299    {
300       if(key == space)
301          return false;
302       return ListBox::OnKeyHit(key, ch);
303    }
304
305    bool NotifyDoubleClick(CheckListBox listBox, int x, int y, Modifiers mods)
306    {
307       listBox.OnLeftButtonDown(x, y, mods);
308       return false;
309    }
310
311    bool NotifyReclick(CheckListBox listBox, DataRow row, Modifiers mods)
312    {
313       if(row == listBox.currentRow)
314          listBox.ToggleCheck(row);
315       return true;
316    }
317
318    // virtual void Window::NotifyChecked(CheckListBox listBox, DataRow row);
319 };