1 module ui.dialog_slice;
2 
3 import gtk.Builder, gtk.Widget, gtk.Window, gdk.Event;
4 import gtk.Container, gtk.Grid;
5 import gtk.Label, gtk.Adjustment, gtk.SpinButton;
6 
7 import std.conv, std.algorithm, std.math;
8 
9 public import gtkc.gtktypes, gtk.Dialog;
10 
11 /**
12  * Custom FileChooserDialog that select a file for open or save data for FCPU-16
13  */
14 class dialog_slice : Dialog{
15 private:
16   Builder builder;            /// Builder used to load from xml file the file options widget
17 
18   Label lbl_text;             /// Label showing informative text
19 
20   Label lbl_maxsize;          /// Max size (max diference) of the slice
21   Label lbl_size;             /// Actual size (diference) of the slice
22   SpinButton spin_begin;      /// Spin button selecting the begin address of a slice
23   SpinButton spin_end;        /// Spin button selecting the end address of a slice
24 
25   size_t max_dif;             /// Max diference between adresses
26   size_t top;                 /// Top address
27   bool sbyte;                 /// Show number of bytes or number of glyphs
28   Adjustment adjust_beg_addr; /// Controls begin address spin button
29   Adjustment adjust_end_addr; /// Controls end address spin button
30 
31 public:
32 
33   /**
34    * See: gtk.Dialog;
35    * Params:
36    *  text      = Texto to be showed over the controls describing what the user are to do
37    *  size      = Top address
38    *  max_size  = Max size of the slice selection
39    *  sbyte     = If it's TRUE, show number of bytes, else show number of glyphs
40    */
41   this (string title, Window parent, GtkDialogFlags flags, string text, size_t top = 0xFFFF , size_t max_size = 255, bool sbyte = true) {
42     super(title, parent, flags, [StockID.OK, StockID.CANCEL], [ResponseType.ACCEPT, ResponseType.CANCEL]);
43 
44     this.top = top;
45     this.max_dif = max_size;
46     this.sbyte = sbyte;
47     // Adds the text label
48     lbl_text = new Label(text);
49     this.getContentArea().packStart(lbl_text, false, true, 10);
50     lbl_text.setPadding(5, 0);
51     lbl_text.setAlignment(0, 0.5);
52     lbl_text.show();
53 
54     // Load widgets from file
55     builder = new Builder ();
56     if (! builder.addFromString (import("slice.ui"))) {
57       throw new Exception("Can't find file slice.ui");
58     }
59 
60     // Gets every object
61     auto table= cast(Grid) builder.getObject ("table");
62     if (table is null) {
63       throw new Exception("Can't find table widget");
64     }
65     Label lbl_maxsize= cast(Label) builder.getObject ("lbl_maxsize");
66     if (lbl_maxsize is null) {
67       throw new Exception("Can't find lbl_maxsize widget");
68     }
69 
70     Label lbl_size= cast(Label) builder.getObject ("lbl_size");
71     if (lbl_size is null) {
72       throw new Exception("Can't find lbl_size widget");
73     }
74 
75     SpinButton spin_begin= cast(SpinButton) builder.getObject ("spin_begin");
76     if (spin_begin is null) {
77       throw new Exception("Can't find spin_begin widget");
78     }
79 
80     SpinButton spin_end= cast(SpinButton) builder.getObject ("spin_end");
81     if (spin_end is null) {
82       throw new Exception("Can't find spin_end widget");
83     }
84     // Set and assigns attributes of each widget *******************************
85     // Attach table container to the Dialog
86     table.reparent(this);
87     this.getContentArea().packEnd(table, false, true, 10);
88 
89     // Create the adjusment objects and assign it to the spin buttons
90     adjust_beg_addr = new Adjustment(0, 0, max_dif -1, 1 , 10, 0);
91     adjust_end_addr = new Adjustment(max_dif, 1, max_dif , 1 , 10, 0);
92 
93     spin_begin.configure(adjust_beg_addr, 0, 0);
94     spin_end.configure(adjust_end_addr, 0, 0);
95 
96     // Show max size and actual size
97     lbl_maxsize.setLabel(to!string(top));
98     auto s = adjust_end_addr.getValue() - adjust_beg_addr.getValue() +1;
99     if (!sbyte) {
100       lbl_size.setText(to!string(s) ~  " words" ~ " - "
101                       ~ to!string(floor(cast(double)(max_dif/2))) ~ " glyphs ");
102     } else {
103       lbl_size.setText(to!string(s) ~  " words" ~ " - "
104                       ~ to!string(s*2) ~ " bytes ");
105     }
106 
107     // Signal handlers *********************************************************
108     // Begin of slice being edited
109     adjust_beg_addr.addOnValueChanged( (Adjustment ad) {
110       adjust_end_addr.setLower(min(ad.getValue() +1, top));
111       adjust_end_addr.setUpper(min(ad.getValue() +255, top));
112       if (adjust_end_addr.getValue() < (ad.getValue() +1)) {
113         adjust_end_addr.setValue(ad.getValue() +1);
114       }
115       auto s = adjust_end_addr.getValue() - adjust_beg_addr.getValue() +1;
116       if (!sbyte) {
117         lbl_size.setText(to!string(s) ~  " words" ~ " - "
118                         ~ to!string(floor(s/2)) ~ " glyphs ");
119       } else {
120         lbl_size.setText(to!string(s) ~  " words" ~ " - "
121                         ~ to!string(s*2) ~ " bytes ");
122       }
123     });
124 
125     // End of slice being edited
126     adjust_end_addr.addOnValueChanged( (Adjustment ad) {
127       adjust_end_addr.setLower(adjust_beg_addr.getValue() +1);
128       adjust_beg_addr.setUpper(max(ad.getValue() -1, 0));
129       if (adjust_beg_addr.getValue() > (ad.getValue() -1)) {
130         adjust_beg_addr.setValue(ad.getValue() -1);
131       }
132       auto s = adjust_end_addr.getValue() - adjust_beg_addr.getValue() +1;
133       if (!sbyte) {
134         lbl_size.setText(to!string(s) ~  " words" ~ " - "
135                         ~ to!string(floor(s/2)) ~ " glyphs ");
136       } else {
137         lbl_size.setText(to!string(s) ~  " words" ~ " - "
138                         ~ to!string(s*2) ~ " bytes ");
139       }
140     });
141   }
142 
143   /// Return the selected bottom address
144   auto bottom_address() @property {
145     return adjust_beg_addr.getValue();
146   }
147 
148   /// Return the selected top address
149   auto top_address() @property {
150     return adjust_end_addr.getValue();
151   }
152 
153   /// Return the size of the selected slice (including the last address)
154   auto size() @property {
155     return adjust_end_addr.getValue() - adjust_beg_addr.getValue() +1;
156   }
157 }
158 
159 unittest {
160   import std.stdio, gtk.Main;
161 
162   Main.init([""]);
163 
164   auto d = new dialog_slice("test", null, GtkDialogFlags.MODAL, "This is a test", 1024);
165 
166   writeln(d.run());
167   writeln("Begin: ", d.bottom_address);
168   writeln("End: ", d.top_address);
169   writeln("Size (including end address): ", d.size);
170 
171 }