diff --git a/source/app.d b/source/app.d index 1ece442..1b78fa1 100644 --- a/source/app.d +++ b/source/app.d @@ -18,10 +18,13 @@ import gtk.VBox; import gtk.Statusbar; import gtk.Button; import gtk.ScrolledWindow; +import gtk.AccelGroup; import gtk.HBox; -MainWindow window; +__gshared MainWindow window; +__gshared HBox statusbar; +__gshared Object header; void main(string[] args) { @@ -30,12 +33,16 @@ void main(string[] args) //Window window = new MainWindow("2DA-Edit"); + auto accel = new AccelGroup(); + window.addAccelGroup(accel); + auto cont = new VBox(false, 0); window.add(cont); cont.setSizeRequest(300, 200); version(Windows){ auto buttonSave = new Button(StockID.SAVE, true); + auto buttonSaveAs = new Button(StockID.SAVE_AS, true); auto buttonOpen = new Button(StockID.OPEN, true); auto buttonInsert = new Button(StockID.JUMP_TO, true); auto buttonDelete = new Button(StockID.DELETE, true); @@ -44,6 +51,7 @@ void main(string[] args) } else{ auto buttonSave = new Button("document-save-symbolic", GtkIconSize.MENU); + auto buttonSaveAs = new Button("document-save-as-symbolic", GtkIconSize.MENU); auto buttonOpen = new Button("document-open-symbolic", GtkIconSize.MENU); auto buttonInsert = new Button("format-text-direction-ltr-symbolic", GtkIconSize.SMALL_TOOLBAR); auto buttonDelete = new Button("user-trash-symbolic", GtkIconSize.SMALL_TOOLBAR); @@ -51,37 +59,44 @@ void main(string[] args) auto buttonNewCol = new Button("tab-new-symbolic", GtkIconSize.SMALL_TOOLBAR); } buttonSave.setTooltipText("Save"); + buttonSaveAs.setTooltipText("Save as"); buttonOpen.setTooltipText("Open 2DA"); buttonInsert.setTooltipText("Insert row after"); buttonDelete.setTooltipText("Delete row"); buttonRenumber.setTooltipText("Renumber all rows"); buttonNewCol.setTooltipText("Add new column"); + enum GDK_KEY_S = 0x053; + buttonSave.addAccelerator("clicked", accel, GDK_KEY_S, GdkModifierType.CONTROL_MASK, GtkAccelFlags.VISIBLE); + buttonSaveAs.addAccelerator("clicked", accel, GDK_KEY_S, GdkModifierType.CONTROL_MASK|GdkModifierType.SHIFT_MASK, GtkAccelFlags.VISIBLE); + version(Windows){ //Menu bar import gtk.HBox; - auto cont2 = new HBox(false, 0); - cont.packStart(cont2, false, false, 0); + header = new HBox(false, 0); + cont.packStart(header, false, false, 0); - cont2.packStart(buttonOpen, false, false, 0); - cont2.packEnd(buttonSave, false, false, 0); + header.packStart(buttonOpen, false, false, 0); + header.packEnd(buttonSaveAs, false, false, 0); + header.packEnd(buttonSave, false, false, 0); } else{ //Header bar import gtk.HeaderBar; - auto header = new HeaderBar(); - window.setTitlebar(header); - header.setTitle("2DAEdit"); - header.setProperty("show-close-button", true); + header = new HeaderBar(); + window.setTitlebar(cast(HeaderBar)header); + (cast(HeaderBar)header).setTitle("2DAEdit"); + (cast(HeaderBar)header).setProperty("show-close-button", true); - header.packStart(buttonOpen); - header.packEnd(buttonSave); + (cast(HeaderBar)header).packStart(buttonOpen); + (cast(HeaderBar)header).packEnd(buttonSaveAs); + (cast(HeaderBar)header).packEnd(buttonSave); } //Status bar - auto statusbar = new HBox(false, 0); + statusbar = new HBox(false, 0); cont.packEnd(statusbar, false, false, 5); statusbar.packStart(buttonRenumber, false, false, 5); statusbar.packStart(buttonInsert, false, false, 5); @@ -101,12 +116,38 @@ void main(string[] args) tree.setProperty("tooltip-column", 0); tree.setProperty("reorderable", true); tree.setProperty("headers-clickable", true); + tree.addOnColumnsChanged((TreeView tree){ + auto store = cast(ListStore)tree.getModel(); + + int si = GetColumnStoreIndex(tree, 0); + if(si>=0 && store.getColumnType(si) != GType.INT){ + foreach(i ; 0..store.getNColumns()){ + si = GetColumnStoreIndex(tree, i); + if(si>=0 && store.getColumnType(si)==GType.INT){ + tree.moveColumnAfter(tree.getColumn(i), null); + break; + } + } + } + }); //Configure button callbacks buttonSave.addOnClicked((Button){ Save(tree); }); + buttonSaveAs.addOnClicked((Button){ + import gtk.Dialog; + import gtk.FileChooserDialog; + + auto fc = new FileChooserDialog("Save 2DA as", window, FileChooserAction.SAVE); + auto res = fc.run(); + if(res==GtkResponseType.OK){ + string filename = fc.getFilename(); + Save(tree, filename); + } + fc.destroy(); + }); buttonOpen.addOnClicked((Button){ import gtk.Dialog; @@ -116,8 +157,7 @@ void main(string[] args) auto res = fc.run(); if(res==GtkResponseType.OK){ string filename = fc.getFilename(); - version(Windows) Open(filename, tree, cast(Object)window); - else Open(filename, tree, cast(Object)header); + Open(filename, tree); } fc.destroy(); }); @@ -159,37 +199,57 @@ void main(string[] args) }); buttonNewCol.addOnClicked((Button){ - auto store = cast(ListStore)tree.getModel(); - int newColIndex = store.getNColumns(); + auto oldstore = cast(ListStore)tree.getModel(); + int newColIndex = oldstore.getNColumns(); GType[] types; - foreach(i ; 0..newColIndex+1) - types~= store.getColumnType(i); - store.setColumnTypes(types); + string[] titles; + foreach(i ; 0..newColIndex){ + if(i==0)types~= GType.INT; + else types~= GType.STRING; + titles~= tree.getColumn(0).getTitle; + tree.removeColumn(tree.getColumn(0)); + } + types~=GType.STRING; + titles~="new_col"; - writeln(types.length); + auto store = new ListStore(types); + tree.setModel(store); + //Fill them + TreeIter oldit = new TreeIter(); + TreeIter newit = new TreeIter(); + if(oldstore.getIterFirst(oldit)){ + do{ + store.append(newit); - //auto col = SetupColumn(tree, "new_col", newColIndex); + foreach(i ; 0..newColIndex+1){ + if(i=2 && exists(args[1])){ - version(Windows) Open(args[1], tree, cast(Object)window); - else Open(args[1], tree, cast(Object)header); + Open(args[1], tree); } window.showAll(); @@ -198,9 +258,46 @@ void main(string[] args) } -void Save(ref TreeView tree){ +int GetColumnStoreIndex(TreeView tree, int colindex){ + auto col = tree.getColumn(colindex); + if(col !is null) + return cast(int)(col.getData("colnumber")); + return -1; +} + +void SaySomething(string msg){ + import core.thread; + new Thread({ + Thread.getThis.sleep(dur!"msecs"(100)); + + auto lbl = new Label(""); + lbl.setMarkup(""~msg~""); + statusbar.packEnd(lbl, false, false, 5); + + //Wow, much animation, very badass + lbl.setOpacity(0.0); + lbl.show(); + foreach(i ; 0..20){ + lbl.setOpacity(i/20.0); + Thread.getThis.sleep(dur!"msecs"(10)); + } + Thread.getThis.sleep(dur!"msecs"(1500)); + foreach(i ; 1..20){ + lbl.setOpacity(1.0-i/20.0); + Thread.getThis.sleep(dur!"msecs"(10)); + } + //Destroy + lbl.destroy(); + }).start(); +} + +void Save(ref TreeView tree, string newpath=""){ auto store = cast(ListStore)tree.getModel(); if(store !is null){ + if(newpath!=""){ + openedFile = newpath; + SetTitle(openedFile); + } auto file = File(openedFile, "w"); @@ -213,33 +310,36 @@ void Save(ref TreeView tree){ do{ file.write(store.getValueInt(it, 0)); foreach(i ; 1..store.getNColumns()){ - file.write("\t\"",store.getValueString(it, i),"\""); + file.write("\t\"", store.getValueString(it, GetColumnStoreIndex(tree, i)), "\""); } file.write("\n"); }while(store.iterNext(it)); - - file.flush(); - file.close(); - writeln("File written: ",openedFile); } + + file.flush(); + file.close(); + + SaySomething("Saved to "~openedFile); } else - writeln("Nothing to save !"); + SaySomething("Nothing to save !"); +} +void SetTitle(string title){ + version(Windows) (cast(MainWindow)header).setTitle(title); + else{ + import gtk.HeaderBar; + (cast(HeaderBar)header).setSubtitle(title); + } } string openedFile; -void Open(string file, ref TreeView tree, Object header){ +void Open(string file, ref TreeView tree){ auto twoda = new TwoDA(file); openedFile = file; - - version(Windows) (cast(MainWindow)header).setTitle(file); - else{ - import gtk.HeaderBar; - (cast(HeaderBar)header).setSubtitle(file); - } + SetTitle(openedFile); //Delete old store auto oldstore = cast(ListStore)tree.getModel(); @@ -300,7 +400,7 @@ auto ref SetupColumn(TreeView tree, string sName, size_t index){ CellRendererText cr = new CellRendererText(); cr.setProperty("editable", true); if(index==0){ - cr.setProperty("background-rgba", cast(ulong)(new GdkRGBA(0.36, 0.13, 0.4, 1.0))); + cr.setProperty("background-rgba", cast(ulong)(new GdkRGBA(0.36, 0.13, 0.4, 0.5))); cr.setProperty("background-set", true); cr.addOnEdited((string path, string newval, CellRendererText crt){ @@ -310,7 +410,7 @@ auto ref SetupColumn(TreeView tree, string sName, size_t index){ store.setValue(t, cast(int)crt.getData("colnumber"), n); } catch(Exception e){ - writeln("Not a number"); + SaySomething("Not a number !"); } }); @@ -318,7 +418,11 @@ auto ref SetupColumn(TreeView tree, string sName, size_t index){ else{ cr.addOnEdited((string path, string newval, CellRendererText crt){ TreeIter t = new TreeIter(tree.getModel(), path); - store.setValue(t, cast(int)crt.getData("colnumber"), newval); + if(newval.countchars("\"")!=0){ + SaySomething("Double quotes are forbidden !"); + } + else + store.setValue(t, cast(int)crt.getData("colnumber"), newval); }); } @@ -326,9 +430,11 @@ auto ref SetupColumn(TreeView tree, string sName, size_t index){ auto col = new TreeViewColumn(sName, cr, "text", cast(int)index); + col.setData("colnumber", cast(void*)cast(int)index); col.setResizable(true); col.setMinWidth(10); col.setClickable(true); + col.setReorderable(true); col.addOnClicked((TreeViewColumn col){ import gtk.Dialog; auto dlg = new Dialog("Rename column", window, GtkDialogFlags.MODAL, ["Cancel","Rename"], [ResponseType.CANCEL, ResponseType.OK]); @@ -341,7 +447,7 @@ auto ref SetupColumn(TreeView tree, string sName, size_t index){ if(newname.countchars(" \t\n\r")==0) col.setTitle(newname); else - writeln("Unauthorized caracters in column name"); + SaySomething("Spaces are forbidden in column name"); } dlg.destroy();