1 module lem1802_fontview; 2 3 import std.c.process, std.stdio, std.conv, std.math; 4 5 import gtkc.gtktypes; 6 import gtk.Main, gtk.Builder; 7 import gtk.Widget, gtk.Window, gtk.MainWindow, gtk.Dialog, gtk.AboutDialog; 8 import gtk.Button, gtk.Label, gtk.MenuBar, gtk.MenuItem, gtk.ToggleButton; 9 import gtk.SpinButton, gtk.Adjustment, gtk.AccelGroup; 10 import gtk.DrawingArea; 11 import gdk.Event, gtk.Container, gdk.RGBA; 12 13 import cairo.Context, cairo.Surface; 14 15 import ui.file_chooser, ui.dialog_slice; 16 import dcpu.ram_io; 17 18 string filename; // Open file 19 TypeHexFile type; // Type of file 20 Window mainwin; // Main window 21 22 ushort[256] font; // Font data 23 size_t selected; // Selected gryph 24 25 DrawingArea dwa; // Drawing widget 26 enum FONT_GLYPHS = 128; 27 enum G_WIDTH = 4; 28 enum G_HEIGHT = 8; 29 enum uint MATRIX_WIDTH = 32; 30 enum uint MATRIX_HEIGHT = 4; 31 enum RECT_SIZE = 4; 32 enum CELL_HEIGHT = RECT_SIZE*G_HEIGHT; 33 enum CELL_WIDTH = RECT_SIZE*G_WIDTH; 34 enum double min_width = G_WIDTH*RECT_SIZE*MATRIX_WIDTH +30; // Min width of drawing widget 35 enum double min_height = G_HEIGHT*RECT_SIZE*MATRIX_HEIGHT +3; // Min height of drawing widget 36 37 Label lbl_pos; // Label with selected glyph position 38 Label lbl_bin; // Label with binary representation of selected glyph 39 Label lbl_hex; // Label with hex representation of selected glyph 40 Label lbl_dec; // Label with decimal representation of selected glyph 41 42 //bool updating; // Updating data form out to the editor ? 43 DrawingArea glyph_editor; // Glyph editor 44 //ToggleButton[16][2] editor; // Editor toggle buttons 45 46 AboutDialog win_about; // About dialog 47 48 size_t file_size; // Original File size 49 50 /** 51 * Close the App when it's clicked menu exit option 52 */ 53 extern (C) export void on_mnu_exit_activate (Event event, Widget widget) { 54 Main.quit(); 55 } 56 57 /** 58 * Show About dialog 59 */ 60 extern (C) export void on_mnu_about_activate (Event event, Widget widget) { 61 win_about.run(); 62 win_about.hide(); 63 } 64 65 /** 66 * Click over Previus button 67 */ 68 extern (C) export void on_but_prev_clicked (Event event, Widget widget) { 69 selected = (selected -1) % 128; 70 lbl_pos.setLabel(to!string(selected)); 71 update_editor(); 72 dwa.queueDraw(); 73 } 74 75 /** 76 * Click over Previus button 77 */ 78 extern (C) export void on_but_next_clicked (Event event, Widget widget) { 79 selected = (selected +1) % 128; 80 lbl_pos.setLabel(to!string(selected)); 81 update_editor(); 82 dwa.queueDraw(); 83 } 84 85 /** 86 * Reset all data (new font) 87 */ 88 extern (C) export void on_mnu_new_activate (Event event, Widget widget) { 89 filename = ""; 90 selected = 0; 91 font[] = 0; 92 update_editor(); 93 dwa.queueDraw(); 94 } 95 96 97 /** 98 * Show the Open file Dialog and try to load it 99 */ 100 extern (C) export void on_mnu_open_activate (Event event, Widget widget) { 101 auto opener = new FileOpener(mainwin); 102 auto response = opener.run(); 103 if (response == ResponseType.ACCEPT) { 104 filename = opener.getFilename(); 105 type = opener.type; 106 107 ushort[] tmp; 108 if (filename !is null && filename.length > 0){ 109 try { 110 tmp = load_ram(type, filename); 111 } catch (Exception e) { 112 stderr.writeln("Error: Couldn't open file\n", e.msg); 113 } 114 115 if (tmp.length > 256) { // Contains something more that a LEM1802 font 116 file_size = tmp.length; 117 auto d = new dialog_slice("LEM1802 Font View", mainwin, GtkDialogFlags.MODAL, 118 "The file contains more data that a font for the LEM1802. 119 You must select a range of data that you desire to load like a font.", file_size, 255, false); 120 d.show(); 121 auto r = d.run(); 122 d.hide(); 123 if ( r == ResponseType.ACCEPT) { 124 size_t slice = cast(size_t)(d.size); 125 size_t b = cast(size_t)d.bottom_address; 126 size_t e = cast(size_t)d.top_address; 127 e++; 128 129 if (((e-b+1)%2) != 0 && (e-b > 2)) { 130 e--; // Clamp the last half glyph selected 131 slice--; 132 } 133 134 font[0..slice] = tmp[b..e]; 135 font[slice..$] = 0; 136 } 137 } else { 138 font[0..tmp.length] = tmp[0..tmp.length]; 139 font[tmp.length..$] = 0; 140 } 141 // Updates GUI 142 selected = 0; 143 update_editor(); 144 dwa.queueDraw(); 145 } 146 } 147 opener.hide(); 148 opener.destroy(); 149 } 150 151 /** 152 * Show the Save file Dialog and try to save it 153 */ 154 extern (C) export void on_mnu_saveas_activate (Event event, Widget widget) { 155 auto opener = new FileOpener(mainwin, false); 156 auto response = opener.run(); 157 if (response == ResponseType.ACCEPT) { 158 filename = opener.getFilename(); 159 type = opener.type; 160 stderr.writeln("Type :", type); 161 // Save data 162 try { 163 save_ram(type, filename, font); 164 } catch (Exception e) { 165 stderr.writeln("Error: Couldn't save data\n", e.msg); 166 } 167 } 168 169 opener.hide(); 170 opener.destroy(); 171 } 172 173 /** 174 * Update the state of the editor buttons 175 */ 176 void update_editor() { 177 glyph_editor.queueDraw(); 178 update_glyph_lbl(); // Update labels at same time 179 } 180 181 /** 182 * Updates binary, hex and decimal representation of selected glyph 183 */ 184 void update_glyph_lbl() { 185 import std..string; 186 lbl_bin.setLabel("0b"~format("%016b",font[selected*2])~"\n"~ "0b"~format("%016b",font[selected*2+1])); 187 lbl_hex.setLabel("0x"~format("%04X",font[selected*2])~"\n"~ "0x"~format("%04X",font[selected*2+1])); 188 lbl_dec.setLabel(to!string(font[selected*2])~"\n"~to!string(font[selected*2+1])); 189 } 190 191 void main(string[] args) { 192 int old_w, old_h; 193 Main.init(args); 194 195 auto builder = new Builder (); 196 197 if (! builder.addFromString (import("fview.ui"))) { 198 writefln("Oops, could not create Builder object, check your builder file ;)"); 199 exit(1); 200 } 201 202 // Get reference to Objects 203 mainwin = cast(Window) builder.getObject ("win_fontview"); 204 if (mainwin is null) { 205 writefln("Can't find win_fontview widget"); 206 exit(1); 207 } 208 auto accelgroup = cast(AccelGroup) builder.getObject ("accelgroup1"); 209 if (accelgroup is null) { 210 writefln("Can't find accelgroup1 widget"); 211 exit(1); 212 } 213 mainwin.addAccelGroup(accelgroup); 214 215 win_about = cast(AboutDialog) builder.getObject ("win_about"); 216 if (mainwin is null) { 217 writefln("Can't find win_about widget"); 218 exit(1); 219 } 220 221 222 dwa = cast(DrawingArea) builder.getObject("dwa_general"); 223 if (dwa is null) { 224 writefln("Can't find dwa_general widget"); 225 exit(1); 226 } 227 228 lbl_pos = cast(Label) builder.getObject("lbl_pos"); 229 if (lbl_pos is null) { 230 writefln("Can't find lbl_pos widget"); 231 exit(1); 232 } 233 lbl_bin = cast(Label) builder.getObject ("lbl_bin"); 234 if (lbl_bin is null) { 235 writefln("Can't find lbl_bin widget"); 236 exit(1); 237 } 238 lbl_hex = cast(Label) builder.getObject ("lbl_hex"); 239 if (lbl_hex is null) { 240 writefln("Can't find lbl_hex widget"); 241 exit(1); 242 } 243 lbl_dec = cast(Label) builder.getObject ("lbl_dec"); 244 if (lbl_dec is null) { 245 writefln("Can't find lbl_dec widget"); 246 exit(1); 247 } 248 249 glyph_editor = cast(DrawingArea) builder.getObject ("glyph_editor"); 250 if (glyph_editor is null) { 251 writefln("Can't find glyph_editor widget"); 252 exit(1); 253 } 254 255 dwa.overrideBackgroundColor( GtkStateFlags.NORMAL, new RGBA(0, 0, 0)); 256 glyph_editor.overrideBackgroundColor( GtkStateFlags.NORMAL, new RGBA(0, 0, 0)); 257 // Here we assing event handlers -------------------------------------------- 258 259 // Closing the window ends the program 260 mainwin.addOnDestroy ( (Widget w) { 261 Main.quit(); 262 }); 263 264 // Select a Glyph 265 dwa.addOnButtonPress ( (Event event, Widget widget) { 266 if (event !is null) { 267 GtkAllocation size; 268 widget.getAllocation(size); 269 270 double x = event.button().x *(min_width / size.width); // Scales coords to be the same 271 double y = event.button().y *(min_height / size.height); // always with diferent geometry 272 273 x = floor(x / (G_WIDTH*RECT_SIZE +1)); 274 y = floor(y / (G_HEIGHT*RECT_SIZE +1)); 275 selected = (to!size_t(x+ y*MATRIX_WIDTH) % FONT_GLYPHS); 276 277 lbl_pos.setLabel(to!string(selected)); 278 dwa.queueDraw(); 279 update_editor(); 280 281 return true; 282 } 283 return false; 284 }); 285 286 // Draws Glyphs viewer 287 dwa.addOnDraw( (Scoped!Context cr, Widget widget) { 288 GtkAllocation size; 289 widget.getAllocation(size); 290 291 // Calcs factor scale 292 double scale_x = size.width / min_width; 293 double scale_y = size.height / min_height; 294 cr.scale(scale_x, scale_y); 295 296 // Draw font on a 32x4 matrix. Each font[i] is half glyph 297 cr.save(); 298 for (size_t i; i< font.length; i++) { 299 auto hcell_x = i % (2*MATRIX_WIDTH); 300 auto cell_y = i / (2*MATRIX_WIDTH); 301 auto x_org = hcell_x*CELL_WIDTH/2 + floor(hcell_x/2.0); 302 auto y_org = cell_y * (CELL_HEIGHT+1); 303 304 for (ushort p; p < 16; p++) { // And loops each pixel of a half glyph 305 if(( font[i] & (1<<p)) != 0) { 306 int l_oct = 1 - (p / 8); 307 double x = l_oct * RECT_SIZE; 308 x += x_org; 309 310 double y = (p % 8) * RECT_SIZE; 311 y += y_org; 312 313 cr.rectangle(x, y, RECT_SIZE, RECT_SIZE); 314 cr.setSourceRgb(1.0, 1.0, 1.0); 315 cr.fill(); 316 } 317 } 318 } 319 cr.restore(); 320 321 // Draw lines around gryphs 322 cr.save(); 323 cr.setSourceRgb(1.0, 0, 0); 324 cr.setLineWidth(1.0); 325 for (auto y = CELL_HEIGHT +1 ; y< (CELL_HEIGHT+1)*MATRIX_HEIGHT; y+=CELL_HEIGHT+1) { 326 cr.moveTo(0, y); 327 cr.lineTo(min_width, y); 328 } 329 for (auto x = CELL_WIDTH+1; x< (CELL_WIDTH+1)*MATRIX_WIDTH; x+=CELL_WIDTH+1) { 330 cr.moveTo(x, 0); 331 cr.lineTo(x, min_height); 332 } 333 cr.stroke(); 334 335 cr.restore(); 336 337 // Draw rectangle around selected glyph 338 cr.save(); 339 cr.setSourceRgb(0, 1.0, 0); 340 cr.setLineWidth(1.5); 341 double x = (selected%MATRIX_WIDTH)*G_WIDTH*RECT_SIZE + (selected%MATRIX_WIDTH); 342 double y = (selected / MATRIX_WIDTH)*(CELL_HEIGHT+1); 343 cr.rectangle(x, y, RECT_SIZE*G_WIDTH, RECT_SIZE*G_HEIGHT); 344 cr.stroke(); 345 346 cr.restore(); 347 return false; 348 }); 349 350 // Draws Glyph editor 351 glyph_editor.addOnDraw( (Scoped!Context cr, Widget widget) { 352 GtkAllocation size; 353 widget.getAllocation(size); 354 /+ 355 356 //cr.scale(scale_x, scale_y); 357 cr.translate(0, 0); 358 359 // Calcs factor scale 360 double scale_x = size.width / min_width; 361 double scale_y = size.height / min_height; 362 +/ 363 364 // Draw lines around gryphs 365 cr.save(); 366 cr.setSourceRgb(0.5, 0.5, 0.5); 367 cr.setLineWidth(1.0); 368 for (auto y = 20.0; y< 21*8; y+=21) { 369 cr.moveTo(0, y); 370 cr.lineTo(size.width, y); 371 } 372 for (auto x = 20.0; x< 21*4; x+=21) { 373 cr.moveTo(x, 0); 374 cr.lineTo(x, size.height); 375 } 376 cr.stroke(); 377 cr.restore(); 378 379 // Paints selected Glyph 380 cr.save(); 381 for (int x; x < 2; x++) { 382 for (int y; y < 16; y++) { 383 if (( font[selected*2+x] & (1<<y)) != 0) { 384 auto pos_x = (x*2 + 1 - floor(cast(double)(y/8)) ) * 21; 385 cr.rectangle(pos_x, (y%8)*21, 20, 20); 386 cr.setSourceRgb(1.0, 1.0, 1.0); 387 cr.fill(); 388 } 389 } 390 } 391 cr.restore(); 392 393 return false; 394 }); 395 396 // Update the changes of edited glyph 397 glyph_editor.addOnButtonPress( (Event event, Widget widget) { 398 if (event !is null) { 399 auto x = cast(ushort) floor(event.button().x / (20.0 +1)); 400 auto y = cast(ushort) floor(event.button().y / (20.0 +1)); 401 402 if ( x < 1) { // First column 403 font[selected*2] = font[selected*2] ^ cast(ushort)(1<<(y+8)); 404 } else if ( x < 2) { // Second column 405 font[selected*2] = font[selected*2] ^ cast(ushort)(1<<y); 406 } else if ( x < 3) { // Third column 407 font[selected*2 +1] = font[selected*2 +1] ^ cast(ushort)(1<<(y+8)); 408 } else if ( x < 4) { // Fourth column 409 font[selected*2 +1] = font[selected*2 +1] ^ cast(ushort)(1<<y); 410 } 411 412 dwa.queueDraw(); 413 update_editor(); 414 415 return true; 416 } 417 return false; 418 /* 419 for (int x; x < 2; x++) { 420 for (int y; y <16; y++) { 421 auto pos = 1<<y; 422 r ~= "editor["~to!string(x)~"]["~to!string(y)~"]"; 423 r ~= ".addOnClicked( (Button b) {"; 424 r ~= " if (!updating) {"; 425 if (x != 1) { 426 r ~= " font[selected*2] = font[selected*2] ^ "~to!string(pos)~";"; 427 } else { 428 r ~= " font[selected*2 +1] = font[selected*2 +1] ^ "~to!string(pos)~";"; 429 } 430 r ~= " dwa.queueDraw(); update_glyph_lbl;"; 431 r ~= " }"; 432 r ~= "});"; 433 } 434 } 435 */ 436 }); 437 438 builder.connectSignals (null); // This connect signals defiend on the builder with "extern (C) export" edifned functions 439 mainwin.show (); 440 441 Main.run(); 442 }