ecere/gui/EditBox: (#670) Yet another number syntax highlighting fix
[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<uintptr, CheckListBoxButton> buttonMaps { };
35    AVLTree<DataRow> rowChecks { };
36    AVLTree<DataRow> rowDisabled { };
37    int checkIndent;
38
39    checkIndent = 20;
40
41    fullRowSelect = false, collapseControl = true, treeBranches = true, rootCollapseButton = true, 
42    noDragging = true;
43    // rowHeight = 18;
44
45    void OnDestroy()
46    {
47       buttonMaps.RemoveAll();
48    }
49    
50    bool NotifyCollapse(CheckListBox listBox, DataRow row, bool collapsed)
51    {
52       DataRow r;
53       for(r = row.firstRow; r && r != row; )
54       {
55          if(collapsed)
56          {
57             MapIterator<uintptr, Button> it { map = listBox.buttonMaps };
58             if(it.Index((uintptr)r, false))
59             {
60                Button checkBox = it.data;
61                if(checkBox)
62                {
63                   checkBox.Destroy(0);
64                   it.Remove();
65                }
66             }
67          }
68          else
69          {
70             listBox.SetupButtons(r, false);
71          }
72          if(r.firstRow && !r.collapsed) 
73             r = r.firstRow;
74          else 
75             for(; r != row; r = r.parent)
76                if(r.next) { r = r.next; break; }
77       }
78       for(r = row.GetNextRow(); r; r = r.GetNextRow())
79       {
80          Button checkBox = listBox.buttonMaps[(uintptr)r];
81          if(checkBox)
82             checkBox.position.y = 1 + (r.index + listBox.hasHeader) * listBox.rowHeight;
83       }
84       return true;
85    }
86
87    bool CheckPartialChecks(DataRow row)
88    {
89       DataRow r;
90       for(r = row.firstRow; r; r = r.next)
91       {
92          if(rowChecks.Find(r) || CheckPartialChecks(r))
93             return true;
94       }
95       return false;
96    }
97    
98    void SetupButtons(DataRow row, bool recurse)
99    {
100       DataRow parent;
101       CheckListBoxButton button;
102       int indent = checkIndent;
103
104       for(parent = row.parent; parent; parent = parent.parent) indent += 20;
105       button = buttonMaps[(uintptr)row];
106       if(!button) button = CheckListBoxButton { this };
107       button.position = { 2 + indent, 1+(row.index + hasHeader) * rowHeight };
108       button.id = (uintptr)row;
109
110       for(parent = row; parent; parent = parent.parent) if(rowChecks.Find(parent)) break;
111       if(parent)
112       {
113          button.checked = true;
114          button.buttonState = up;
115       }
116       else
117       {
118          button.checked = CheckPartialChecks(row);
119          button.buttonState = button.checked ? down : up;
120       }
121       if(rowDisabled.Find(row))
122          button.disabled = true;
123       button.Create();
124       buttonMaps[(uintptr)row] = button;
125       if(recurse && !row.collapsed)
126       {
127          DataRow r;
128          for(r = row.firstRow; r; r = r.next)
129             SetupButtons(r, recurse);
130       }
131    }
132
133    void UpdateButtons()
134    {
135       DataRow row;
136       for(row = firstRow; row; row = row.next)
137          SetupButtons(row, true);
138    }
139
140    bool OnCreate()
141    {
142       if(ListBox::OnCreate())
143       {
144          DataRow row;
145          
146          buttonMaps.RemoveAll();
147
148          for(row = firstRow; row; row = row.next)
149             SetupButtons(row, true);
150          return true;
151       }
152       return false;
153    }
154
155    void ToggleCheck(DataRow row)
156    {
157       CheckListBoxButton checkBox = buttonMaps[(uintptr)row];
158       if(checkBox && !checkBox.disabled)
159       {
160          bool checked = false;
161          DataRow r;
162          for(r = row; r; r = r.parent)
163             if(rowChecks.Find(r))
164             { 
165                checked = true;
166                break;
167             }         
168          SetCheck(row, !checked);
169       }
170    }
171
172    void UncheckBoxes(DataRow row)
173    {
174       if(!row.parent || !row.parent.collapsed)
175       {
176          CheckListBoxButton button = buttonMaps[(uintptr)row];
177          if(button)
178          {
179             bool wasChecked = button.checked;
180             button.checked = false;
181             button.buttonState = up;
182          }
183       }
184       // if(!row.collapsed)
185       {
186          DataRow r;
187          for(r = row.firstRow; r; r = r.next)
188             UncheckBoxes(r);
189       }
190       NotifyChecked(master, this, row);
191    }
192    
193    void UnsetChildren(DataRow row)
194    {
195       DataRow r;
196       CheckListBoxButton button = buttonMaps[(uintptr)row];
197       if(button)
198       {
199          button.checked = true;
200          button.buttonState = up;
201       }
202
203       for(r = row.firstRow; r; r = r.next)
204       {
205          Iterator<DataRow> it { rowChecks };
206
207          if(it.Find(r))
208             it.Remove();
209          UnsetChildren(r);
210          NotifyChecked(master, this, r);
211       }      
212    }
213    
214    void SetCheck(DataRow row, bool checked)
215    {
216       DataRow parent;
217       bool wasChecked = false;
218
219       for(parent = row; parent; parent = parent.parent)
220       {
221          if(rowChecks.Find(parent)) { wasChecked = true; break; }
222       }
223       if(checked != wasChecked)
224       {
225          modifiedDocument = true;
226          // NotifyChecked(master, this, row);
227          if(checked)
228          {
229             DataRow rr = row;
230             // Check if all siblings are checked, if so go up until we reach a row not fully checked
231             while(rr)
232             {
233                DataRow r;
234                for(r = rr.parent.firstRow; r; r = r.next)
235                {
236                   if(r != rr && !rowChecks.Find(r))
237                      break;
238                }
239                if(r || !rr.parent) break;
240                rr = rr.parent;
241             }
242
243             rowChecks.Add(rr);
244
245             // Take out all children from rowChecks, checking them all
246             UnsetChildren(rr);
247
248             NotifyChecked(master, this, row);
249
250             for(parent = rr.parent; parent; parent = parent.parent)
251             {
252                CheckListBoxButton button = buttonMaps[(uintptr)parent];
253                if(button)
254                {
255                   button.checked = true;
256                   button.buttonState = down;
257
258                   NotifyChecked(master, this, parent);
259                }
260             }
261          }
262          else
263          {
264             DataRow rr = row;
265
266             parent = rr.parent;
267             while(rr)
268             {
269                Iterator<DataRow> it { rowChecks };
270                if(it.Find(rr))
271                {
272                   it.Remove();
273                   break;
274                }
275                else
276                {
277                   DataRow r;
278                   for(r = rr.parent.firstRow; r; r = r.next)
279                   {
280                      if(r != rr)
281                         rowChecks.Add(r);
282                   }
283                   rr = rr.parent;
284                }
285             }
286
287             UncheckBoxes(row);
288
289             for(; parent; parent = parent.parent)
290             {
291                CheckListBoxButton button = buttonMaps[(uintptr)parent];
292                if(button)
293                {
294                   if(CheckPartialChecks(parent))
295                   {
296                      button.checked = true;
297                      button.buttonState = down;
298                   }
299                   else
300                   {
301                      button.checked = false;
302                      button.buttonState = up;
303                   }
304
305                   NotifyChecked(master, this, parent);
306                }
307             }
308          }
309       }
310    }
311    
312    bool NotifyKeyDown(CheckListBox listBox, DataRow row, Key key, unichar ch)
313    {
314       if(key == space)
315       {
316          listBox.ToggleCheck(row);
317          return false;
318       }
319       return true;
320    }
321
322    bool OnKeyHit(Key key, unichar ch)
323    {
324       if(key == space)
325          return false;
326       return ListBox::OnKeyHit(key, ch);
327    }
328
329    bool NotifyDoubleClick(CheckListBox listBox, int x, int y, Modifiers mods)
330    {
331       listBox.OnLeftButtonDown(x, y, mods);
332       return false;
333    }
334
335    bool NotifyReclick(CheckListBox listBox, DataRow row, Modifiers mods)
336    {
337       if(row == listBox.currentRow)
338          listBox.ToggleCheck(row);
339       return true;
340    }
341
342 public:
343    bool IsChecked(DataRow row)
344    {
345       CheckListBoxButton button = buttonMaps[(uintptr)row];
346       DataRow parent;
347       for(parent = row; parent; parent = parent.parent) if(rowChecks.Find(parent)) return true;
348       // For partially checked because of children:
349       if(button && button.checked)
350          return true;
351       return false;
352    }
353
354    virtual void Window::NotifyChecked(CheckListBox listBox, DataRow row);
355
356    void SetDisabled(DataRow row, bool disabled)
357    {
358       CheckListBoxButton checkBox = buttonMaps[(uintptr)row];
359       if(checkBox)
360          checkBox.disabled = disabled;
361
362       if(rowDisabled.Find(row))
363       {
364          if(!disabled)
365             rowDisabled.TakeOut(row);
366       }
367       else
368       {
369          if(disabled)
370             rowDisabled.Add(row);
371       }
372    }
373 };