From 6ac4dc34685f02626e95d7317c1582eff9ad4d11 Mon Sep 17 00:00:00 2001 From: "Crom (Thibaut CHARLES)" Date: Thu, 12 Mar 2015 23:56:27 +0100 Subject: [PATCH] Organized code --- source/app.d | 525 +----------------------------------------------- source/twoda.d | 54 +++++ source/window.d | 508 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 564 insertions(+), 523 deletions(-) create mode 100644 source/twoda.d create mode 100644 source/window.d diff --git a/source/app.d b/source/app.d index 6a77b8c..db49847 100755 --- a/source/app.d +++ b/source/app.d @@ -4,30 +4,11 @@ import std.string; import std.conv : to; import std.path; +import window; import gtk.Main; import gtk.Version; -import gtk.MainWindow; -import gtk.Widget; -import gtk.TreeView; -import gtk.ListStore; -import gtk.TreeViewColumn; -import gtk.TreeIter; -import gtk.Label; -import gtk.Entry; -import gtk.CellRenderer; -import gtk.CellRendererText; -import gtk.VBox; -import gtk.Statusbar; -import gtk.Button; -import gtk.ScrolledWindow; -import gtk.AccelGroup; -import gtk.HBox; - -__gshared MainWindow window; -__gshared HBox statusbar; -__gshared Object header; void main(string[] args) { @@ -37,516 +18,14 @@ void main(string[] args) Main.init(args); //Window - window = new MainWindow("2DA-Edit"); - window.getSettings.setLongProperty("gtk-application-prefer-dark-theme", 0, ""); - // - - auto accel = new AccelGroup(); - window.addAccelGroup(accel); - - auto cont = new VBox(false, 0); - window.add(cont); - cont.setSizeRequest(300, 200); - - 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); - auto buttonRenumber = new Button("view-list-symbolic", GtkIconSize.SMALL_TOOLBAR); - 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); - - //Header bar - import gtk.HeaderBar; - - header = new HeaderBar(); - window.setTitlebar(cast(HeaderBar)header); - (cast(HeaderBar)header).setTitle("2DAEdit"); - (cast(HeaderBar)header).setProperty("show-close-button", true); - - (cast(HeaderBar)header).packStart(buttonOpen); - (cast(HeaderBar)header).packEnd(buttonSaveAs); - (cast(HeaderBar)header).packEnd(buttonSave); - - //Status bar - statusbar = new HBox(false, 0); - cont.packEnd(statusbar, false, false, 5); - statusbar.packStart(buttonRenumber, false, false, 5); - statusbar.packStart(buttonInsert, false, false, 5); - statusbar.packStart(buttonDelete, false, false, 5); - statusbar.packEnd(buttonNewCol, false, false, 5); - - //TreeView to display database - auto scroll = new ScrolledWindow(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC); - cont.packEnd(scroll, true, true, 0); - - auto tree = new TreeView(); - scroll.add(tree); - - tree.setHeadersVisible(true); - tree.setEnableSearch(true); - tree.setProperty("enable-grid-lines", GtkTreeViewGridLines.VERTICAL); - tree.setProperty("tooltip-column", 0); - tree.setProperty("reorderable", true); - tree.setProperty("headers-clickable", true); - tree.addOnColumnsChanged((TreeView tree){ - auto store = cast(ListStore)tree.getModel(); - if(store is null) return; - - 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); - fc.setDoOverwriteConfirmation(true); - fc.setCreateFolders(true); - fc.setCurrentFolder(openedFile!=""? dirName(openedFile) : getcwd()); - auto res = fc.run(); - if(res==GtkResponseType.OK){ - string filename = fc.getFilename(); - Save(tree, filename); - } - fc.destroy(); - }); - - buttonOpen.addOnClicked((Button){ - import gtk.Dialog; - import gtk.FileChooserDialog; - - auto fc = new FileChooserDialog("Open 2DA", window, FileChooserAction.OPEN); - fc.setSelectMultiple(false); - fc.setCurrentFolder(openedFile!=""? dirName(openedFile) : getcwd()); - auto res = fc.run(); - if(res==GtkResponseType.OK){ - string filename = fc.getFilename(); - Open(filename, tree); - } - fc.destroy(); - }); - - buttonInsert.addOnClicked((Button){ - TreeIter it = tree.getSelectedIter(); - auto store = cast(ListStore)tree.getModel(); - - if(store !is null){ - store.insertAfter(it, it); - store.setValue(it, 0, 0); - foreach(i ; 1..store.getNColumns()){ - store.setValue(it, cast(int)i, "_"); - } - } - }); - - buttonDelete.addOnClicked((Button){ - TreeIter it = tree.getSelectedIter(); - auto store = cast(ListStore)tree.getModel(); - - if(store !is null) - store.remove(it); - }); - - buttonRenumber.addOnClicked((Button){ - auto store = cast(ListStore)tree.getModel(); - - if(store !is null){ - TreeIter it = new TreeIter(); - if(store.getIterFirst(it)){ - int id = 0; - do{ - store.setValue(it, 0, id++); - - }while(store.iterNext(it)); - } - } - }); - - buttonNewCol.addOnClicked((Button){ - auto oldstore = cast(ListStore)tree.getModel(); - if(oldstore is null) return; - int newColIndex = oldstore.getNColumns(); - - - foreach(i ; 0..tree.getNColumns){ - auto col = tree.getColumn(i); - writeln("col",i,"=",col, "(", col is null? "" : col.getTitle, ")"); - } - - - GType[] types; - string[] titles; - int[] storeIndex; - foreach(i ; 0..tree.getNColumns){ - writeln("Saving ",i); - if(i==0)types~= GType.INT; - else types~= GType.STRING; - - storeIndex~= GetColumnStoreIndex(tree, 0)>=0?GetColumnStoreIndex(tree, 0):0; - - auto col = tree.getColumn(0); - if(col !is null) titles~= tree.getColumn(0).getTitle; - else titles~= "ERR"; - tree.removeColumn(tree.getColumn(0)); - } - types~=GType.STRING; - titles~="new_col"; - - 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); - - foreach(i ; 0..newColIndex+1){ - if(i=2 && exists(args[1])){ - Open(args[1], tree); - } - - window.showAll(); + auto w = new Window((args.length>=2 && exists(args[1]))? args[1] : null); Main.run(); } -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); - } - - //Detect column sizes - size_t colSize[]; - colSize.length = tree.getNColumns; - foreach(i ; 0..tree.getNColumns) - colSize[i] = tree.getColumn(i).getTitle.length +1;//+1 space - - TreeIter it = new TreeIter(); - if(store.getIterFirst(it)){ - do{ - import std.math : log10; - int size0 = log10(store.getValueInt(it, 0)+1).to!int +1;//+1 space - if(size0>colSize[0]) - colSize[0] = size0; - - foreach(i ; 1..store.getNColumns()){ - int size = store.getValueString(it, GetColumnStoreIndex(tree, i)).length.to!int +3;//+2 double quotes added, +1 space - if(size>colSize[i]) - colSize[i] = size; - } - }while(store.iterNext(it)); - } - - - //Write file - auto file = File(openedFile, "w"); - - foreach(i ; 0..tree.getNColumns) - file.write(leftJustify(tree.getColumn(i).getTitle, colSize[i])); - file.write("\n"); - - it = new TreeIter(); - if(store.getIterFirst(it)){ - do{ - file.write(leftJustify(store.getValueInt(it, 0).to!string, colSize[0])); - foreach(i ; 1..store.getNColumns()){ - file.write(leftJustify("\""~store.getValueString(it, GetColumnStoreIndex(tree, i))~"\"", colSize[i])); - } - file.write("\n"); - }while(store.iterNext(it)); - } - - file.flush(); - file.close(); - - SaySomething("Saved to "~openedFile); - } - else - SaySomething("Nothing to save !"); -} - -void SetTitle(string title){ - import gtk.HeaderBar; - (cast(HeaderBar)header).setSubtitle(title); -} - -__gshared string openedFile; -void Open(string file, ref TreeView tree){ - - auto twoda = new TwoDA(file); - - openedFile = file; - SetTitle(openedFile); - - //Delete old store - auto oldstore = cast(ListStore)tree.getModel(); - if(oldstore !is null) - oldstore.destroy(); - - //Remove columns from TreeView - foreach(i ; 0..tree.getNColumns) - tree.removeColumn(tree.getColumn(0)); - - //Set store types - GType type[]; - type~=GType.INT; - foreach(i;1..twoda.header.length)type~=GType.STRING; - - //Create new store - auto store = new ListStore(type); - tree.setModel(store); - - - //Setup TreeView columns - foreach(index, s ; twoda.header){ - - auto col = SetupColumn(tree, s, index); - tree.appendColumn(col); - } - - //Fill database - TreeIter iter = new TreeIter(); - - for(int i=0 ; i<=twoda.lastLine ; i++){ - store.append(iter); - store.setValue(iter, 0, i); - - if(i in twoda.values){ - foreach(index, v ; twoda.values[i]){ - store.setValue(iter, cast(int)index+1, v); - } - } - else{ - foreach(index ; 1..twoda.header.length){ - store.setValue(iter, cast(int)index+1, "_"); - } - } - } - - //Autosize columns - tree.columnsAutosize(); - - tree.setSizeRequest(50, 50); -} - - -//Note: tree is only used on events (click) -auto ref SetupColumn(TreeView tree, string sName, size_t index){ - auto store = cast(ListStore)tree.getModel(); - - 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, 0.5))); - cr.setProperty("background-set", true); - - cr.addOnEdited((string path, string newval, CellRendererText crt){ - try{ - int n = newval.to!int; - TreeIter t = new TreeIter(tree.getModel(), path); - store.setValue(t, cast(int)crt.getData("colnumber"), n); - } - catch(Exception e){ - SaySomething("Not a number !"); - } - - }); - } - else{ - cr.addOnEdited((string path, string newval, CellRendererText crt){ - TreeIter t = new TreeIter(tree.getModel(), path); - if(newval.countchars("\"")!=0){ - SaySomething("Double quotes are forbidden !"); - } - else - store.setValue(t, cast(int)crt.getData("colnumber"), newval); - }); - } - - cr.setData("colnumber", cast(void*)cast(int)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("Column options", window, GtkDialogFlags.MODAL, ["Close"], [ResponseType.CANCEL]); - - //Rename - dlg.getContentArea.packStart(new Label("Rename:"), false, false, 0); - - auto renamebox = new HBox(false, 0); - dlg.getContentArea.packStart(renamebox, false, false, 0); - - auto renameentry = new Entry(col.getTitle); - auto renamebutton = new Button("object-select-symbolic", GtkIconSize.SMALL_TOOLBAR); - renamebutton.addOnClicked((Button){ - auto newname = renameentry.getText.strip; - if(newname.countchars(" \t\n\r")==0) - col.setTitle(newname); - else - SaySomething("Spaces are forbidden in column name"); - }); - renamebox.packStart(renameentry, true, true, 0); - renamebox.packEnd(renamebutton, false, false, 0); - - //Delete - auto deletebox = new HBox(false, 0); - dlg.getContentArea.packStart(deletebox, false, false, 0); - - auto deletebutton = new Button("user-trash-symbolic", GtkIconSize.SMALL_TOOLBAR); - deletebutton.addOnClicked((Button){ - tree.removeColumn(col); - dlg.destroy(); - }); - deletebox.packStart(new Label("Delete column"), true, true, 0); - deletebox.packEnd(deletebutton, false, false, 0); - - - - dlg.showAll(); - dlg.run(); - dlg.destroy(); - - }); - - return col; -} -class TwoDA{ - import std.regex; - import std.file; - - this(string filepath){ - lastLine = 0; - foreach(index, line ; readText(filepath).splitLines()){ - - string data[]; - - auto results = matchAll(line, rgxField); - foreach(res ; results){ - string s; - if(res[0][0]=='"') data~= res[2]; - else data~= res[1]; - } - - if(index==0){ - header = data; - //writeln(header); - } - else{ - int nLine = data[0].to!int; - values[nLine] = data[1..$]; - //writeln(values[nLine]); - - if(nLine > lastLine) - lastLine = nLine; - } - - } - - } - - - string[] header; - string[][uint] values; - uint lastLine; - enum rgxField = ctRegex!"(?:\\b([^\\s]+?)\\b|\"([^\"]+?)\")"; -} diff --git a/source/twoda.d b/source/twoda.d new file mode 100644 index 0000000..1da78af --- /dev/null +++ b/source/twoda.d @@ -0,0 +1,54 @@ +module twoda; + +import std.string; +import std.conv : to; +import std.regex; + + +class TwoDA{ + import std.regex; + import std.file; + + class ParseException : Exception{ + this(in string s){super(s);} + } + + this(string filepath){ + lastLine = 0; + foreach(index, line ; readText(filepath).splitLines){ + + string data[]; + + auto results = matchAll(line, rgxField); + if(results.hit is null) + throw new ParseException(filepath~":"~index.to!string~" Incorrect 2da line (not parseable)"); + + foreach(res ; results){ + string s; + if(res[0][0]=='"') data~= res[2]; + else data~= res[1]; + } + + if(index==0){ + header = data; + //writeln(header); + } + else{ + int nLine = data[0].to!int; + values[nLine] = data[1..$]; + //writeln(values[nLine]); + + if(nLine > lastLine) + lastLine = nLine; + } + + } + + } + + + string[] header; + string[][uint] values; + uint lastLine; + enum rgxField = ctRegex!"(?:\\b([^\\s]+?)\\b|\"([^\"]+?)\")"; +} \ No newline at end of file diff --git a/source/window.d b/source/window.d new file mode 100644 index 0000000..d22b53c --- /dev/null +++ b/source/window.d @@ -0,0 +1,508 @@ +module window; + +import std.path; +import std.stdio; +import std.string; +import std.conv : to; + +import gtk.MainWindow; +import gtk.HeaderBar; +import gtk.Widget; +import gtk.TreeView; +import gtk.ListStore; +import gtk.TreeViewColumn; +import gtk.TreeIter; +import gtk.Label; +import gtk.Entry; +import gtk.CellRenderer; +import gtk.CellRendererText; +import gtk.VBox; +import gtk.Statusbar; +import gtk.Button; +import gtk.ScrolledWindow; +import gtk.AccelGroup; +import gtk.HBox; + +import twoda; + +class Window : MainWindow{ + + this(in string file=null){ + super("2DA-Edit"); + getSettings.setLongProperty("gtk-application-prefer-dark-theme", 0, ""); + + + auto accel = new AccelGroup(); + addAccelGroup(accel); + + auto cont = new VBox(false, 0); + add(cont); + cont.setSizeRequest(300, 200); + + 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); + auto buttonRenumber = new Button("view-list-symbolic", GtkIconSize.SMALL_TOOLBAR); + 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); + + //Header bar + import gtk.HeaderBar; + + header = new HeaderBar(); + setTitlebar(header); + header.setTitle("2DAEdit"); + header.setProperty("show-close-button", true); + + header.packStart(buttonOpen); + header.packEnd(buttonSaveAs); + header.packEnd(buttonSave); + + //Status bar + statusbar = new HBox(false, 0); + cont.packEnd(statusbar, false, false, 5); + statusbar.packStart(buttonRenumber, false, false, 5); + statusbar.packStart(buttonInsert, false, false, 5); + statusbar.packStart(buttonDelete, false, false, 5); + statusbar.packEnd(buttonNewCol, false, false, 5); + + //TreeView to display database + auto scroll = new ScrolledWindow(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC); + cont.packEnd(scroll, true, true, 0); + + auto tree = new TreeView(); + scroll.add(tree); + + tree.setHeadersVisible(true); + tree.setEnableSearch(true); + tree.setProperty("enable-grid-lines", GtkTreeViewGridLines.VERTICAL); + tree.setProperty("tooltip-column", 0); + tree.setProperty("reorderable", true); + tree.setProperty("headers-clickable", true); + tree.addOnColumnsChanged((TreeView tree){ + auto store = cast(ListStore)tree.getModel(); + if(store is null) return; + + 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); + fc.setDoOverwriteConfirmation(true); + fc.setCreateFolders(true); + fc.setCurrentFolder(openedFile!=""? dirName(openedFile) : getcwd()); + auto res = fc.run(); + if(res==GtkResponseType.OK){ + string filename = fc.getFilename(); + save(tree, filename); + } + fc.destroy(); + }); + + buttonOpen.addOnClicked((Button){ + import gtk.Dialog; + import gtk.FileChooserDialog; + + auto fc = new FileChooserDialog("Open 2DA", window, FileChooserAction.OPEN); + fc.setSelectMultiple(false); + fc.setCurrentFolder(openedFile!=""? dirName(openedFile) : getcwd()); + auto res = fc.run(); + if(res==GtkResponseType.OK){ + string filename = fc.getFilename(); + open(filename, tree); + } + fc.destroy(); + }); + + buttonInsert.addOnClicked((Button){ + TreeIter it = tree.getSelectedIter(); + auto store = cast(ListStore)tree.getModel(); + + if(store !is null){ + store.insertAfter(it, it); + store.setValue(it, 0, 0); + foreach(i ; 1..store.getNColumns()){ + store.setValue(it, cast(int)i, "_"); + } + } + }); + + buttonDelete.addOnClicked((Button){ + TreeIter it = tree.getSelectedIter(); + auto store = cast(ListStore)tree.getModel(); + + if(store !is null) + store.remove(it); + }); + + buttonRenumber.addOnClicked((Button){ + auto store = cast(ListStore)tree.getModel(); + + if(store !is null){ + TreeIter it = new TreeIter(); + if(store.getIterFirst(it)){ + int id = 0; + do{ + store.setValue(it, 0, id++); + + }while(store.iterNext(it)); + } + } + }); + + buttonNewCol.addOnClicked((Button){ + auto oldstore = cast(ListStore)tree.getModel(); + if(oldstore is null) return; + int newColIndex = oldstore.getNColumns(); + + + foreach(i ; 0..tree.getNColumns){ + auto col = tree.getColumn(i); + writeln("col",i,"=",col, "(", col is null? "" : col.getTitle, ")"); + } + + + GType[] types; + string[] titles; + int[] storeIndex; + foreach(i ; 0..tree.getNColumns){ + writeln("Saving ",i); + if(i==0)types~= GType.INT; + else types~= GType.STRING; + + storeIndex~= getColumnStoreIndex(tree, 0)>=0?getColumnStoreIndex(tree, 0):0; + + auto col = tree.getColumn(0); + if(col !is null) titles~= tree.getColumn(0).getTitle; + else titles~= "ERR"; + tree.removeColumn(tree.getColumn(0)); + } + types~=GType.STRING; + titles~="new_col"; + + 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); + + foreach(i ; 0..newColIndex+1){ + if(i"~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; + header.setSubtitle(openedFile); + } + + //Detect column sizes + size_t colSize[]; + colSize.length = tree.getNColumns; + foreach(i ; 0..tree.getNColumns) + colSize[i] = tree.getColumn(i).getTitle.length +1;//+1 space + + TreeIter it = new TreeIter(); + if(store.getIterFirst(it)){ + do{ + import std.math : log10; + int size0 = log10(store.getValueInt(it, 0)+1).to!int +1;//+1 space + if(size0>colSize[0]) + colSize[0] = size0; + + foreach(i ; 1..store.getNColumns()){ + int size = store.getValueString(it, getColumnStoreIndex(tree, i)).length.to!int +3;//+2 double quotes added, +1 space + if(size>colSize[i]) + colSize[i] = size; + } + }while(store.iterNext(it)); + } + + + //Write file + auto file = File(openedFile, "w"); + + foreach(i ; 0..tree.getNColumns) + file.write(leftJustify(tree.getColumn(i).getTitle, colSize[i])); + file.write("\n"); + + it = new TreeIter(); + if(store.getIterFirst(it)){ + do{ + file.write(leftJustify(store.getValueInt(it, 0).to!string, colSize[0])); + foreach(i ; 1..store.getNColumns()){ + file.write(leftJustify("\""~store.getValueString(it, getColumnStoreIndex(tree, i))~"\"", colSize[i])); + } + file.write("\n"); + }while(store.iterNext(it)); + } + + file.flush(); + file.close(); + + displayMessage("Saved to "~openedFile); + } + else + displayMessage("Nothing to save !"); + } + + + void open(string file, ref TreeView tree){ + + auto twoda = new TwoDA(file); + + openedFile = file; + header.setSubtitle(openedFile); + + //Delete old store + auto oldstore = cast(ListStore)tree.getModel(); + if(oldstore !is null) + oldstore.destroy(); + + //Remove columns from TreeView + foreach(i ; 0..tree.getNColumns) + tree.removeColumn(tree.getColumn(0)); + + //Set store types + GType type[]; + type~=GType.INT; + foreach(i;1..twoda.header.length)type~=GType.STRING; + + //Create new store + auto store = new ListStore(type); + tree.setModel(store); + + + //Setup TreeView columns + foreach(index, s ; twoda.header){ + + auto col = SetupColumn(tree, s, index); + tree.appendColumn(col); + } + + //Fill database + TreeIter iter = new TreeIter(); + + for(int i=0 ; i<=twoda.lastLine ; i++){ + store.append(iter); + store.setValue(iter, 0, i); + + if(i in twoda.values){ + foreach(index, v ; twoda.values[i]){ + store.setValue(iter, cast(int)index+1, v); + } + } + else{ + foreach(index ; 1..twoda.header.length){ + store.setValue(iter, cast(int)index+1, "_"); + } + } + } + + //Autosize columns + tree.columnsAutosize(); + + tree.setSizeRequest(50, 50); + } + + + +private: + MainWindow window; + HBox statusbar; + HeaderBar header; + + string openedFile; + + + + + + + + + int getColumnStoreIndex(TreeView tree, int colindex){ + auto col = tree.getColumn(colindex); + if(col !is null) + return cast(int)(col.getData("colnumber")); + return -1; + } + + auto ref SetupColumn(TreeView tree, string sName, size_t index){ + auto store = cast(ListStore)tree.getModel(); + + 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, 0.5))); + cr.setProperty("background-set", true); + + cr.addOnEdited((string path, string newval, CellRendererText crt){ + try{ + int n = newval.to!int; + TreeIter t = new TreeIter(tree.getModel(), path); + store.setValue(t, cast(int)crt.getData("colnumber"), n); + } + catch(Exception e){ + displayMessage("Not a number !"); + } + + }); + } + else{ + cr.addOnEdited((string path, string newval, CellRendererText crt){ + TreeIter t = new TreeIter(tree.getModel(), path); + if(newval.countchars("\"")!=0){ + displayMessage("Double quotes are forbidden !"); + } + else + store.setValue(t, cast(int)crt.getData("colnumber"), newval); + }); + } + + cr.setData("colnumber", cast(void*)cast(int)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("Column options", window, GtkDialogFlags.MODAL, ["Close"], [ResponseType.CANCEL]); + + //Rename + dlg.getContentArea.packStart(new Label("Rename:"), false, false, 0); + + auto renamebox = new HBox(false, 0); + dlg.getContentArea.packStart(renamebox, false, false, 0); + + auto renameentry = new Entry(col.getTitle); + auto renamebutton = new Button("object-select-symbolic", GtkIconSize.SMALL_TOOLBAR); + renamebutton.addOnClicked((Button){ + auto newname = renameentry.getText.strip; + if(newname.countchars(" \t\n\r")==0) + col.setTitle(newname); + else + displayMessage("Spaces are forbidden in column name"); + }); + renamebox.packStart(renameentry, true, true, 0); + renamebox.packEnd(renamebutton, false, false, 0); + + //Delete + auto deletebox = new HBox(false, 0); + dlg.getContentArea.packStart(deletebox, false, false, 0); + + auto deletebutton = new Button("user-trash-symbolic", GtkIconSize.SMALL_TOOLBAR); + deletebutton.addOnClicked((Button){ + tree.removeColumn(col); + dlg.destroy(); + }); + deletebox.packStart(new Label("Delete column"), true, true, 0); + deletebox.packEnd(deletebutton, false, false, 0); + + + + dlg.showAll(); + dlg.run(); + dlg.destroy(); + + }); + + return col; + } + +} \ No newline at end of file