1 module lem1802_fontview; 2 3 import core.stdc.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 immutable 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 immutable 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 immutable 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 : format; 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 Main.init(args); 193 194 auto builder = new Builder (); 195 196 if (! builder.addFromString (import("fview.ui"))) { 197 writefln("Oops, could not create Builder object, check your builder file ;)"); 198 exit(1); 199 } 200 201 // Get reference to Objects 202 mainwin = cast(Window) builder.getObject ("win_fontview"); 203 if (mainwin is null) { 204 writefln("Can't find win_fontview widget"); 205 exit(1); 206 } 207 auto accelgroup = cast(AccelGroup) builder.getObject ("accelgroup1"); 208 if (accelgroup is null) { 209 writefln("Can't find accelgroup1 widget"); 210 exit(1); 211 } 212 mainwin.addAccelGroup(accelgroup); 213 214 win_about = cast(AboutDialog) builder.getObject ("win_about"); 215 if (mainwin is null) { 216 writefln("Can't find win_about widget"); 217 exit(1); 218 } 219 220 221 dwa = cast(DrawingArea) builder.getObject("dwa_general"); 222 if (dwa is null) { 223 writefln("Can't find dwa_general widget"); 224 exit(1); 225 } 226 227 lbl_pos = cast(Label) builder.getObject("lbl_pos"); 228 if (lbl_pos is null) { 229 writefln("Can't find lbl_pos widget"); 230 exit(1); 231 } 232 lbl_bin = cast(Label) builder.getObject ("lbl_bin"); 233 if (lbl_bin is null) { 234 writefln("Can't find lbl_bin widget"); 235 exit(1); 236 } 237 lbl_hex = cast(Label) builder.getObject ("lbl_hex"); 238 if (lbl_hex is null) { 239 writefln("Can't find lbl_hex widget"); 240 exit(1); 241 } 242 lbl_dec = cast(Label) builder.getObject ("lbl_dec"); 243 if (lbl_dec is null) { 244 writefln("Can't find lbl_dec widget"); 245 exit(1); 246 } 247 248 glyph_editor = cast(DrawingArea) builder.getObject ("glyph_editor"); 249 if (glyph_editor is null) { 250 writefln("Can't find glyph_editor widget"); 251 exit(1); 252 } 253 254 dwa.overrideBackgroundColor( GtkStateFlags.NORMAL, new RGBA(0, 0, 0)); 255 glyph_editor.overrideBackgroundColor( GtkStateFlags.NORMAL, new RGBA(0, 0, 0)); 256 // Here we assing event handlers -------------------------------------------- 257 258 // Closing the window ends the program 259 mainwin.addOnDestroy ( (Widget w) { 260 Main.quit(); 261 }); 262 263 // Select a Glyph 264 dwa.addOnButtonPress ( (Event event, Widget widget) { 265 if (event !is null) { 266 GtkAllocation size; 267 widget.getAllocation(size); 268 269 double x = event.button().x *(min_width / size.width); // Scales coords to be the same 270 double y = event.button().y *(min_height / size.height); // always with diferent geometry 271 272 x = floor(x / (G_WIDTH*RECT_SIZE +1)); 273 y = floor(y / (G_HEIGHT*RECT_SIZE +1)); 274 selected = (to!size_t(x+ y*MATRIX_WIDTH) % FONT_GLYPHS); 275 276 lbl_pos.setLabel(to!string(selected)); 277 dwa.queueDraw(); 278 update_editor(); 279 280 return true; 281 } 282 return false; 283 }); 284 285 // Draws Glyphs viewer 286 dwa.addOnDraw( (Scoped!Context cr, Widget widget) { 287 GtkAllocation size; 288 widget.getAllocation(size); 289 290 // Calcs factor scale 291 immutable double scale_x = size.width / min_width; 292 immutable double scale_y = size.height / min_height; 293 cr.scale(scale_x, scale_y); 294 295 // Draw font on a 32x4 matrix. Each font[i] is half glyph 296 cr.save(); 297 for (size_t i; i< font.length; i++) { 298 auto hcell_x = i % (2*MATRIX_WIDTH); 299 auto cell_y = i / (2*MATRIX_WIDTH); 300 auto x_org = hcell_x*CELL_WIDTH/2 + floor(hcell_x/2.0); 301 auto y_org = cell_y * (CELL_HEIGHT+1); 302 303 for (ushort p; p < 16; p++) { // And loops each pixel of a half glyph 304 if(( font[i] & (1<<p)) != 0) { 305 immutable int l_oct = 1 - (p / 8); 306 double x = l_oct * RECT_SIZE; 307 x += x_org; 308 309 double y = (p % 8) * RECT_SIZE; 310 y += y_org; 311 312 cr.rectangle(x, y, RECT_SIZE, RECT_SIZE); 313 cr.setSourceRgb(1.0, 1.0, 1.0); 314 cr.fill(); 315 } 316 } 317 } 318 cr.restore(); 319 320 // Draw lines around gryphs 321 cr.save(); 322 cr.setSourceRgb(1.0, 0, 0); 323 cr.setLineWidth(1.0); 324 for (auto y = CELL_HEIGHT +1 ; y< (CELL_HEIGHT+1)*MATRIX_HEIGHT; y+=CELL_HEIGHT+1) { 325 cr.moveTo(0, y); 326 cr.lineTo(min_width, y); 327 } 328 for (auto x = CELL_WIDTH+1; x< (CELL_WIDTH+1)*MATRIX_WIDTH; x+=CELL_WIDTH+1) { 329 cr.moveTo(x, 0); 330 cr.lineTo(x, min_height); 331 } 332 cr.stroke(); 333 334 cr.restore(); 335 336 // Draw rectangle around selected glyph 337 cr.save(); 338 cr.setSourceRgb(0, 1.0, 0); 339 cr.setLineWidth(1.5); 340 immutable double x = (selected%MATRIX_WIDTH)*G_WIDTH*RECT_SIZE + (selected%MATRIX_WIDTH); 341 immutable double y = (selected / MATRIX_WIDTH)*(CELL_HEIGHT+1); 342 cr.rectangle(x, y, RECT_SIZE*G_WIDTH, RECT_SIZE*G_HEIGHT); 343 cr.stroke(); 344 345 cr.restore(); 346 return false; 347 }); 348 349 // Draws Glyph editor 350 glyph_editor.addOnDraw( (Scoped!Context cr, Widget widget) { 351 GtkAllocation size; 352 widget.getAllocation(size); 353 /+ 354 355 //cr.scale(scale_x, scale_y); 356 cr.translate(0, 0); 357 358 // Calcs factor scale 359 double scale_x = size.width / min_width; 360 double scale_y = size.height / min_height; 361 +/ 362 363 // Draw lines around gryphs 364 cr.save(); 365 cr.setSourceRgb(0.5, 0.5, 0.5); 366 cr.setLineWidth(1.0); 367 for (auto y = 20.0; y< 21*8; y+=21) { 368 cr.moveTo(0, y); 369 cr.lineTo(size.width, y); 370 } 371 for (auto x = 20.0; x< 21*4; x+=21) { 372 cr.moveTo(x, 0); 373 cr.lineTo(x, size.height); 374 } 375 cr.stroke(); 376 cr.restore(); 377 378 // Paints selected Glyph 379 cr.save(); 380 for (int x; x < 2; x++) { 381 for (int y; y < 16; y++) { 382 if (( font[selected*2+x] & (1<<y)) != 0) { 383 auto pos_x = (x*2 + 1 - floor(cast(double)(y/8)) ) * 21; 384 cr.rectangle(pos_x, (y%8)*21, 20, 20); 385 cr.setSourceRgb(1.0, 1.0, 1.0); 386 cr.fill(); 387 } 388 } 389 } 390 cr.restore(); 391 392 return false; 393 }); 394 395 // Update the changes of edited glyph 396 glyph_editor.addOnButtonPress( (Event event, Widget widget) { 397 if (event !is null) { 398 auto x = cast(ushort) floor(event.button().x / (20.0 +1)); 399 auto y = cast(ushort) floor(event.button().y / (20.0 +1)); 400 401 if ( x < 1) { // First column 402 font[selected*2] = font[selected*2] ^ cast(ushort)(1<<(y+8)); 403 } else if ( x < 2) { // Second column 404 font[selected*2] = font[selected*2] ^ cast(ushort)(1<<y); 405 } else if ( x < 3) { // Third column 406 font[selected*2 +1] = font[selected*2 +1] ^ cast(ushort)(1<<(y+8)); 407 } else if ( x < 4) { // Fourth column 408 font[selected*2 +1] = font[selected*2 +1] ^ cast(ushort)(1<<y); 409 } 410 411 dwa.queueDraw(); 412 update_editor(); 413 414 return true; 415 } 416 return false; 417 /* 418 for (int x; x < 2; x++) { 419 for (int y; y <16; y++) { 420 auto pos = 1<<y; 421 r ~= "editor["~to!string(x)~"]["~to!string(y)~"]"; 422 r ~= ".addOnClicked( (Button b) {"; 423 r ~= " if (!updating) {"; 424 if (x != 1) { 425 r ~= " font[selected*2] = font[selected*2] ^ "~to!string(pos)~";"; 426 } else { 427 r ~= " font[selected*2 +1] = font[selected*2 +1] ^ "~to!string(pos)~";"; 428 } 429 r ~= " dwa.queueDraw(); update_glyph_lbl;"; 430 r ~= " }"; 431 r ~= "});"; 432 } 433 } 434 */ 435 }); 436 437 builder.connectSignals (null); // This connect signals defiend on the builder with "extern (C) export" edifned functions 438 mainwin.show (); 439 440 Main.run(); 441 }