extras/CheckListBox: Fixes for property NotifyChanged callbacks
[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)
172       {
173          bool wasChecked = button.checked;
174          button.checked = false;
175          button.buttonState = up;
176          if(wasChecked) NotifyChanged(master, this, row);
177       }
178       if(!row.collapsed)
179       {
180          DataRow r;
181          for(r = row.firstRow; r; r = r.next)
182             UncheckBoxes(r);
183       }
184    }
185    
186    void UnsetChildren(DataRow row)
187    {
188       DataRow r;
189       CheckListBoxButton button = buttonMaps[(int)row];
190       if(button)
191       {
192          bool wasChecked = button.checked;
193          button.checked = true;
194          button.buttonState = up;
195          if(!wasChecked) NotifyChanged(master, this, row);
196       }
197
198       for(r = row.firstRow; r; r = r.next)
199       {
200          Iterator<DataRow> it { rowChecks };
201          if(it.Find(r))
202             it.Remove();
203          UnsetChildren(r);
204       }      
205    }
206    
207    void SetCheck(DataRow row, bool checked)
208    {
209       DataRow parent;
210       bool wasChecked = false;
211
212       for(parent = row; parent; parent = parent.parent)
213       {
214          if(rowChecks.Find(parent)) { wasChecked = true; break; }
215       }
216       if(checked != wasChecked)
217       {
218          modifiedDocument = true;
219          // NotifyChanged(master, this, row);
220          if(checked)
221          {
222             DataRow rr = row;
223             // Check if all siblings are checked, if so go up until we reach a row not fully checked
224             while(rr)
225             {
226                DataRow r;
227                for(r = rr.parent.firstRow; r; r = r.next)
228                {
229                   if(r != rr && !rowChecks.Find(r))
230                      break;
231                }
232                if(r || !row.parent) break;
233                rr = rr.parent;
234             }
235
236             // Take out all children from rowChecks, checking them all
237             UnsetChildren(row);
238
239             rowChecks.Add(row);            
240
241             for(parent = row.parent; parent; parent = parent.parent)
242             {
243                DataRow r;
244                CheckListBoxButton button = buttonMaps[(int)parent];
245                if(button)
246                {
247                   button.checked = true;
248                   button.buttonState = down;
249                }
250             }
251          }
252          else
253          {
254             DataRow rr = row;
255             UncheckBoxes(row);
256             parent = row.parent;
257
258             while(rr)
259             {
260                Iterator<DataRow> it { rowChecks };
261                if(it.Find(rr))
262                {
263                   it.Remove();
264                   break;
265                }
266                else
267                {
268                   DataRow r;
269                   for(r = row.parent.firstRow; r; r = r.next)
270                   {
271                      if(r != rr)
272                         rowChecks.Add(r);
273                   }
274                   rr = rr.parent;
275                }
276             }
277
278             for(; parent; parent = parent.parent)
279             {
280                CheckListBoxButton button = buttonMaps[(int)parent];
281                if(button)
282                {
283                   if(CheckPartialChecks(parent))
284                   {
285                      button.checked = true;
286                      button.buttonState = down;
287                   }
288                   else
289                   {
290                      button.checked = false;
291                      button.buttonState = up;
292                   }
293                }
294             }
295          }
296
297          NotifyChanged(master, this, row);
298       }
299    }
300    
301    bool NotifyKeyDown(CheckListBox listBox, DataRow row, Key key, unichar ch)
302    {
303       if(key == space)
304       {
305          listBox.ToggleCheck(row);
306          return false;
307       }
308       return true;
309    }
310
311    bool OnKeyHit(Key key, unichar ch)
312    {
313       if(key == space)
314          return false;
315       return ListBox::OnKeyHit(key, ch);
316    }
317
318    bool NotifyDoubleClick(CheckListBox listBox, int x, int y, Modifiers mods)
319    {
320       listBox.OnLeftButtonDown(x, y, mods);
321       return false;
322    }
323
324    bool NotifyReclick(CheckListBox listBox, DataRow row, Modifiers mods)
325    {
326       if(row == listBox.currentRow)
327          listBox.ToggleCheck(row);
328       return true;
329    }
330
331    // virtual void Window::NotifyChecked(CheckListBox listBox, DataRow row);
332 };