1namespace gui;
2
3import "Window"
4
5public struct AnchorValue
6{
7   AnchorValueType type;
8
9   union
10   {
11      int distance;
12      float percent;
13   };
14   property int
15   {
16      set { distance = value; type = offset; }
17      get { return distance; }
18   }
19   property double
20   {
21      set { percent = (float) value; type = relative; }
22      get { return (double) percent; }
23   }
24
25   char * OnGetString(char * stringOutput, void * fieldData, bool * needClass)
26   {
27      if(type == offset)
28      {
29         sprintf(stringOutput, "%d", distance);
30      }
31      else if(type == relative)
32      {
33         int c;
34         int last = 0;
35         sprintf(stringOutput, "%f", percent);
36         c = strlen(stringOutput)-1;
37         for( ; c >= 0; c--)
38         {
39            if(stringOutput[c] != '0')
40               last = Max(last, c);
41            if(stringOutput[c] == '.')
42            {
43               if(last == c)
44               {
45                  stringOutput[c+1] = '0';
46                  stringOutput[c+2] = 0;
47               }
48               else
49                  stringOutput[last+1] = 0;
50               break;
51            }
52         }
53      }
54      if(needClass) *needClass = false;
55      return stringOutput;
56   }
57
58   bool OnGetDataFromString(char * stringOutput)
59   {
60      char * end;
61      if(strchr(stringOutput, '.'))
62      {
63         float percent = (float)strtod(stringOutput, &end);
64
65         if(end != stringOutput)
66         {
67            this.percent = percent;
68            type = relative;
69            return true;
70         }
71      }
72      else if(stringOutput[0])
73      {
74         int distance = strtol(stringOutput, &end, 0);
75         if(end != stringOutput)
76         {
77            this.distance = distance;
78            type = offset;
79            return true;
80         }
81      }
82      else
83      {
84         distance = 0;
85         type = 0;
86      }
87      return false;
88   }
89};
90
91public struct MiddleAnchorValue
92{
93   AnchorValueType type;
94
95   union
96   {
97      int distance;
98      float percent;
99   };
100   property int
101   {
102      set { distance = value; type = none; }
103      get { return distance; }
104   }
105   property double
106   {
107      set { percent = (float) value; type = middleRelative; }
108      get { return (double) percent; }
109   }
110
111   char * OnGetString(char * stringOutput, void * fieldData, bool * needClass)
112   {
113      if(type == middleRelative)
114      {
115         int c;
116         int last = 0;
117         sprintf(stringOutput, "%f", percent);
118         c = strlen(stringOutput)-1;
119         for( ; c >= 0; c--)
120         {
121            if(stringOutput[c] != '0')
122               last = Max(last, c);
123            if(stringOutput[c] == '.')
124            {
125               if(last == c)
126               {
127                  stringOutput[c+1] = '0';
128                  stringOutput[c+2] = 0;
129               }
130               else
131                  stringOutput[last+1] = 0;
132               break;
133            }
134         }
135      }
136      else if(type == none && distance)
137      {
138         sprintf(stringOutput, "%d", distance);
139      }
140      if(needClass) *needClass = false;
141      return stringOutput;
142   }
143
144   bool OnGetDataFromString(char * stringOutput)
145   {
146      if(strchr(stringOutput, '.'))
147      {
148         percent = (float)strtod(stringOutput, null);
149         type = middleRelative;
150      }
151      else
152      {
153         distance = strtol(stringOutput, null, 0);
154         type = none;
155      }
156      return true;
157   }
158};
159
160public enum AnchorValueType { none, offset, relative, middleRelative, cascade, vTiled, hTiled };
161
162public struct Anchor
163{
164   union { AnchorValue left; MiddleAnchorValue horz; };
165   union { AnchorValue top; MiddleAnchorValue vert; };
166   AnchorValue right, bottom;
167
168   char * OnGetString(char * stringOutput, void * fieldData, bool * needClass)
169   {
170      char tempString[256];
171      char * anchorValue;
172      bool subNeedClass;
173
174      tempString[0] = '\0';
175      anchorValue = left.OnGetString(tempString, null, &subNeedClass);
176      if(anchorValue[0]) { if(stringOutput[0]) strcat(stringOutput, ", "); strcat(stringOutput, "left = "); strcat(stringOutput, anchorValue); }
177
178      //if(((!left.type && !right.type) && horz.distance) || horz.type == middleRelative)
179      if(!right.type && ((!left.type && horz.distance) || horz.type == middleRelative))
180      {
181         tempString[0] = '\0';
182         anchorValue = horz.OnGetString(tempString, null, &subNeedClass);
183         if(anchorValue[0]) { if(stringOutput[0]) strcat(stringOutput, ", "); strcat(stringOutput, "horz = "); strcat(stringOutput, anchorValue); }
184      }
185
186      tempString[0] = '\0';
187      anchorValue = top.OnGetString(tempString, null, &subNeedClass);
188      if(anchorValue[0]) { if(stringOutput[0]) strcat(stringOutput, ", "); strcat(stringOutput, "top = "); strcat(stringOutput, anchorValue); }
189
190      tempString[0] = '\0';
191      anchorValue = right.OnGetString(tempString, null, &subNeedClass);
192      if(anchorValue[0]) { if(stringOutput[0]) strcat(stringOutput, ", "); strcat(stringOutput, "right = "); strcat(stringOutput, anchorValue); }
193
194      // if(((!top.type && !bottom.type) && vert.distance) || vert.type == middleRelative)
195      if(!bottom.type && ((!top.type && vert.distance) || vert.type == middleRelative))
196      {
197         tempString[0] = '\0';
198         anchorValue = vert.OnGetString(tempString, null, &subNeedClass);
199         if(anchorValue[0]) { if(stringOutput[0]) strcat(stringOutput, ", "); strcat(stringOutput, "vert = "); strcat(stringOutput, anchorValue); }
200      }
201
202      tempString[0] = '\0';
203      anchorValue = bottom.OnGetString(tempString, null, &subNeedClass);
204      if(anchorValue[0]) { if(stringOutput[0]) strcat(stringOutput, ", "); strcat(stringOutput, "bottom = "); strcat(stringOutput, anchorValue); }
205
206      return stringOutput;
207   }
208
209   bool OnGetDataFromString(char * string)
210   {
211      this = Anchor {};
212      return class::OnGetDataFromString(string);
213   }
214
215   bool OnSaveEdit(DropBox dropBox, void * object)
216   {
217      return dropBox.Save();
218   }
219
220   Window OnEdit(Window listBox, Window master, int x, int y, int w, int h, Window control)
221   {
222      char * string = "";
223      AnchorDropBox comboBox
224      {
225         editText = true;
226         parent = listBox;
227         master = master;
228         position = Point { x, y };
229         //clientSize = Size { h = h };
230         //size.w = w;
231         size = { w, h };
232         anchorValue = this;
233         control = control;
234         borderStyle = 0;
235      };
236
237      comboBox.Create();
238
239      {
240         char tempString[MAX_F_STRING] = "";
241         bool needClass = false;
242         char * result = OnGetString(tempString, null, &needClass);
243         if(result) string = result;
244      }
245      comboBox.contents = string;
246      return comboBox;
247   }
248};
249
250private class AnchorButton : Button
251{
252   toggle = true, bevel = false;
253
254   void OnRedraw(Surface surface)
255   {
256      int cw = clientSize.w;
257      int ch = clientSize.h;
258
259      surface.SetForeground(black);
260      if(checked)
261      {
262         surface.SetBackground(Color { 85,85,85 });
263         surface.Area(0,0, cw-1, ch-1);
264      }
265      else
266         surface.LineStipple(0xAAAA);
267
268      surface.Rectangle(0,0,cw-1,ch-1);
269
270      if(active)
271      {
272         surface.LineStipple(0xAAAA);
273         surface.Rectangle(2,2,cw-3,ch-3);
274      }
275   }
276
277   bool AnchorEditor::NotifyClicked(Button button, int x, int y, Modifiers mods)
278   {
279      AnchorDropBox anchorDropBox = (AnchorDropBox)master;
280      Anchor anchor = anchorDropBox.anchorValue;
281      Window control = anchorDropBox.control;
282      DataBox dropMaster = (DataBox)anchorDropBox.master;
283      int id = button.id;
284
285      switch(id)
286      {
287         case 0: anchor.left.type   = button.checked ? offset : none; break;
288         case 1: anchor.top.type    = button.checked ? offset : none; break;
289         case 2: anchor.right.type  = button.checked ? offset : none; break;
290         case 3: anchor.bottom.type = button.checked ? offset : none; break;
291      }
292
293      if(anchor.horz.type == middleRelative && (id == 0 || id == 2))
294      {
295         anchorDropBox.relButtons[0].checked = false;
296         anchorDropBox.relButtons[2].checked = false;
297      }
298      if(anchor.vert.type == middleRelative && (id == 1 || id == 3))
299      {
300         anchorDropBox.relButtons[1].checked = false;
301         anchorDropBox.relButtons[3].checked = false;
302      }
303      anchorDropBox.relButtons[id].checked = false;
304
305      //anchor.horz.type = none;
306      //anchor.vert.type = none;
307
308      {
309         int vpw, vph;
310         int x,y,w,h;
311         Window parent = control.parent;
312
313         // Fix Anchor
314         x = control.position.x;
315         y = control.position.y;
316         w = control.size.w;
317         h = control.size.h;
318
319         vpw = parent.clientSize.w;
320         vph = parent.clientSize.h;
321         if(control.nonClient)
322         {
323            vpw = parent.size.w;
324            vph = parent.size.h;
325         }
326         else if(((BorderBits)control.borderStyle).fixed)
327         {
328            if(!control.dontScrollHorz && parent.scrollArea.w) vpw = parent.scrollArea.w;
329            if(!control.dontScrollVert && parent.scrollArea.h) vph = parent.scrollArea.h;
330         }
331
332         if(anchor.left.type == offset) anchor.left.distance = x;
333         else if(anchor.left.type == relative) anchor.left.percent = (float)x / vpw;
334         if(anchor.top.type == offset) anchor.top.distance = y;
335         else if(anchor.top.type == relative) anchor.top.percent = (float)y / vph;
336         if(anchor.right.type == offset) anchor.right.distance = vpw - (x + w);
337         //else if(anchor.right.type == relative) anchor.right.percent = (float) (x + w) / vpw;
338         else if(anchor.right.type == relative) anchor.right.percent = (float) (vpw - (x + w)) / vpw;
339         if(anchor.bottom.type == offset) anchor.bottom.distance = vph - (y + h);
340         //else if(anchor.bottom.type == relative) anchor.bottom.percent = (float) (y + h) / vph;
341         else if(anchor.bottom.type == relative) anchor.bottom.percent = (float) (vph - (y + h)) / vph;
342
343         if(!anchor.left.type && !anchor.right.type)
344         {
345            anchor.horz.distance = (x + w / 2) - (vpw / 2);
346            //anchor.horz.type = anchor.horz.distance ? offset : 0;
347         }
348         else if(anchor.horz.type == middleRelative) anchor.horz.percent = (float) ((x + w / 2) - (vpw / 2)) / vpw;
349         if(!anchor.top.type && !anchor.bottom.type)
350         {
351            anchor.vert.distance = (y + h / 2) - (vph / 2);
352            //anchor.vert.type = anchor.vert.distance ? offset : 0;
353         }
354         else if(anchor.vert.type == middleRelative) anchor.vert.percent = (float)((y + h / 2) - (vph / 2)) / vph;
355      }
356
357      {
358         char tempString[1024] = "";
359         bool needClass = false;
360         char * string = anchor.OnGetString(tempString, null, &needClass);
361         anchorDropBox.contents = string;
362      }
363
364      dropMaster.SetData(&anchor, false);
365      anchorDropBox.anchorValue = anchor;
366      return true;
367   }
368}
369
370private class AnchorRelButton : Button
371{
372   toggle = true;
373   bevel = false;
374   text = "%";
375   //bevelOver = true;
376
377   void OnRedraw(Surface surface)
378   {
379      int cw = clientSize.w;
380      int ch = clientSize.h;
381
382      if(checked)
383      {
384         surface.SetForeground(black);
385      }
386      else
387      {
388         surface.SetForeground(Color{170,170,170});
389      }
390      surface.WriteText(5,2, "%", 1);
391
392      if(active)
393      {
394         surface.LineStipple(0xAAAA);
395         surface.Rectangle(3,3,cw-4,ch-4);
396      }
397   }
398
399   bool AnchorEditor::NotifyClicked(Button button, int x, int y, Modifiers mods)
400   {
401      AnchorDropBox anchorDropBox = (AnchorDropBox)master;
402      Anchor anchor = anchorDropBox.anchorValue;
403      Window control = anchorDropBox.control;
404      DataBox dropMaster = (DataBox)anchorDropBox.master;
405      int id = button.id;
406
407      if((id == 0 || id == 2) && ((!anchor.left.type && !anchor.right.type) || anchor.left.type == middleRelative))
408      {
409         if(button.checked) anchor.horz.type = middleRelative; else anchor.horz.type = none;
410         anchorDropBox.relButtons[(id + 2)%4].checked = button.checked;
411      }
412      else if((id == 1 || id == 3) && ((!anchor.top.type && !anchor.bottom.type) || anchor.top.type == middleRelative))
413      {
414         if(button.checked) anchor.vert.type = middleRelative; else anchor.vert.type = none;
415         anchorDropBox.relButtons[(id + 2)%4].checked = button.checked;
416      }
417      else
418      {
419         switch(id)
420         {
421            case 0: anchor.left.type   = button.checked ? relative : (anchor.left.type   ? offset : none); break;
422            case 1: anchor.top.type    = button.checked ? relative : (anchor.top.type    ? offset : none); break;
423            case 2: anchor.right.type  = button.checked ? relative : (anchor.right.type  ? offset : none); break;
424            case 3: anchor.bottom.type = button.checked ? relative : (anchor.bottom.type ? offset : none); break;
425         }
426         anchorDropBox.buttons[id].checked = true;
427         if(anchor.horz.type == middleRelative) anchor.horz.type = none;
428         if(anchor.vert.type == middleRelative) anchor.vert.type = none;
429      }
430
431      {
432         int vpw, vph;
433         int x,y,w,h;
434         Window parent = control.parent;
435
436         // Fix Anchor
437         x = control.position.x;
438         y = control.position.y;
439         w = control.size.w;
440         h = control.size.h;
441
442         vpw = parent.clientSize.w;
443         vph = parent.clientSize.h;
444         if(control.nonClient)
445         {
446            vpw = parent.size.w;
447            vph = parent.size.h;
448         }
449         else if(((BorderBits)control.borderStyle).fixed)
450         {
451            if(!control.dontScrollHorz && parent.scrollArea.w)  vpw = parent.scrollArea.w;
452            if(!control.dontScrollVert && parent.scrollArea.h) vph = parent.scrollArea.h;
453         }
454
455         if(anchor.left.type == offset) anchor.left.distance = x;
456         else if(anchor.left.type == relative) anchor.left.percent = (float)x / vpw;
457         if(anchor.top.type == offset) anchor.top.distance = y;
458         else if(anchor.top.type == relative) anchor.top.percent = (float)y / vph;
459         if(anchor.right.type == offset) anchor.right.distance = vpw - (x + w);
460         //else if(anchor.right.type == relative) anchor.right.percent = (float) (x + w) / vpw;
461         else if(anchor.right.type == relative) anchor.right.percent = (float) (vpw - (x + w)) / vpw;
462         if(anchor.bottom.type == offset) anchor.bottom.distance = vph - (y + h);
463         //else if(anchor.bottom.type == relative) anchor.bottom.percent = (float) (y + h) / vph;
464         else if(anchor.bottom.type == relative) anchor.bottom.percent = (float) (vph - (y + h)) / vph;
465
466         if(!anchor.left.type && !anchor.right.type)
467         {
468            anchor.horz.distance = (x + w / 2) - (vpw / 2);
469            //anchor.horz.type = anchor.horz.distance ? offset : none;
470         }
471         else if(anchor.horz.type == middleRelative) anchor.horz.percent = (float) ((x + w / 2) - (vpw / 2)) / vpw;
472         if(!anchor.top.type && !anchor.bottom.type)
473         {
474            anchor.vert.distance = (y + h / 2) - (vph / 2);
475            //anchor.vert.type = anchor.vert.distance ? offset : none;
476         }
477         else if(anchor.vert.type == middleRelative) anchor.vert.percent = (float)((y + h / 2) - (vph / 2)) / vph;
478      }
479
480      {
481         char tempString[1024] = "";
482         bool needClass = false;
483         char * string = anchor.OnGetString(tempString, null, &needClass);
484         anchorDropBox.contents = string;
485      }
486
487      dropMaster.SetData(&anchor, false);
488      anchorDropBox.anchorValue = anchor;
489      return true;
490   }
491}
492
493private class AnchorEditor : Window
494{
495   interim = true;
496   borderStyle = deepContour;
497   size.h = 92;
498
499   bool OnKeyDown(Key key, unichar ch)
500   {
501      if(key == escape)
502         return master.OnKeyDown(key, ch);
503      return true;
504   }
505}
506
507private class AnchorDropBox : DropBox
508{
509   Anchor anchorValue;
510   Window control;
511   Button relButtons[4], buttons[4];
512
513   AnchorEditor anchorEditor
514   {
515      master = this;
516      autoCreate = false;
517   };
518
519   Window OnDropDown()
520   {
521      int c;
522      Button
523      {
524         anchorEditor,
525         anchor = Anchor { left = 28, top = 28, right = 28, bottom = 28 },
526         inactive = true, disabled = true
527      };
528      for(c = 0; c<4; c++)
529      {
530         Button button = buttons[c] = AnchorButton
531         {
532            anchorEditor, id = c,
533            size = Size { (c%2)?10:28, (c%2)?28:10 }
534         };
535         Button relButton = relButtons[c] = AnchorRelButton
536         {
537            anchorEditor, id = c;
538         };
539
540         switch(c)
541         {
542            case 0:
543               if(anchorValue.left.type && anchorValue.left.type != middleRelative) button.checked = true;
544               if(anchorValue.left.type == relative || anchorValue.horz.type == middleRelative) relButton.checked = true;
545
546               button.anchor = Anchor { left = 0 };
547               relButton.anchor = Anchor { left = 5, vert = 16 };
548               break;
549            case 1:
550               if(anchorValue.top.type && anchorValue.top.type != middleRelative) button.checked = true;
551               if(anchorValue.top.type == relative || anchorValue.vert.type == middleRelative) relButton.checked = true;
552
553               button.anchor = Anchor { top = 0 };
554               relButton.anchor = Anchor { top = 5, horz = 16 };
555               break;
556            case 2:
557               if(anchorValue.right.type && anchorValue.right.type != middleRelative) button.checked = true;
558               if(anchorValue.right.type == relative || anchorValue.horz.type == middleRelative) relButton.checked = true;
559
560               button.anchor = Anchor { right = 0 };
561               relButton.anchor = Anchor { right = 5, vert = 16 };
562               break;
563            case 3:
564               if(anchorValue.bottom.type && anchorValue.bottom.type != middleRelative) button.checked = true;
565               if(anchorValue.bottom.type == relative || anchorValue.vert.type == middleRelative) relButton.checked = true;
566
567               button.anchor = Anchor { bottom = 0 };
568               relButton.anchor = Anchor { bottom = 5, horz = 16 };
569               break;
570         }
571      }
572      anchorEditor.Create();
573      return anchorEditor;
574   }
575
576   void OnCloseDropDown(Window anchorEditor)
577   {
578      // TOFIX: Patch for update bug
579      master.Update(null);
580      anchorEditor.Destroy(0);
581   }
582
583   bool DataBox::NotifyTextEntry(AnchorDropBox dropBox, char * string, bool save)
584   {
585      Anchor anchor = dropBox.anchorValue;
586      Window control = dropBox.control;
587
588      if(save)
589      {
590         if(anchor.OnGetDataFromString(string))
591         {
592            SetData(&anchor, false);
593            dropBox.anchorValue = anchor;
594         }
595      }
596      else
597      {
598         char tempString[1024] = "";
599         bool needClass = false;
600         char * string = anchor.OnGetString(tempString, null, &needClass);
601         dropBox.contents = string;
602      }
603      return true;
604   }
605}
606