Organized code
This commit is contained in:
parent
2e6bf663ac
commit
6ac4dc3468
3 changed files with 564 additions and 523 deletions
525
source/app.d
525
source/app.d
|
@ -4,30 +4,11 @@ import std.string;
|
||||||
import std.conv : to;
|
import std.conv : to;
|
||||||
import std.path;
|
import std.path;
|
||||||
|
|
||||||
|
import window;
|
||||||
|
|
||||||
import gtk.Main;
|
import gtk.Main;
|
||||||
import gtk.Version;
|
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)
|
void main(string[] args)
|
||||||
{
|
{
|
||||||
|
@ -37,516 +18,14 @@ void main(string[] args)
|
||||||
Main.init(args);
|
Main.init(args);
|
||||||
|
|
||||||
//Window
|
//Window
|
||||||
window = new MainWindow("2DA-Edit");
|
auto w = new Window((args.length>=2 && exists(args[1]))? args[1] : null);
|
||||||
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<newColIndex){
|
|
||||||
if(types[i]==GType.INT) store.setValue(newit, i, oldstore.getValueInt(oldit, storeIndex[i]));
|
|
||||||
else store.setValue(newit, i, oldstore.getValueString(oldit, storeIndex[i]));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
store.setValue(newit, cast(int)i, "_");
|
|
||||||
}
|
|
||||||
|
|
||||||
}while(oldstore.iterNext(oldit));
|
|
||||||
}
|
|
||||||
|
|
||||||
//setup cols
|
|
||||||
foreach(i ; 0..newColIndex+1){
|
|
||||||
tree.appendColumn(SetupColumn(tree, titles[i], i));
|
|
||||||
}
|
|
||||||
|
|
||||||
oldstore.destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//Open if exists
|
|
||||||
if(args.length>=2 && exists(args[1])){
|
|
||||||
Open(args[1], tree);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.showAll();
|
|
||||||
|
|
||||||
Main.run();
|
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("<i>"~msg~"</i>");
|
|
||||||
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|\"([^\"]+?)\")";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
54
source/twoda.d
Normal file
54
source/twoda.d
Normal file
|
@ -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|\"([^\"]+?)\")";
|
||||||
|
}
|
508
source/window.d
Normal file
508
source/window.d
Normal file
|
@ -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<newColIndex){
|
||||||
|
if(types[i]==GType.INT) store.setValue(newit, i, oldstore.getValueInt(oldit, storeIndex[i]));
|
||||||
|
else store.setValue(newit, i, oldstore.getValueString(oldit, storeIndex[i]));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
store.setValue(newit, cast(int)i, "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
}while(oldstore.iterNext(oldit));
|
||||||
|
}
|
||||||
|
|
||||||
|
//setup cols
|
||||||
|
foreach(i ; 0..newColIndex+1){
|
||||||
|
tree.appendColumn(SetupColumn(tree, titles[i], i));
|
||||||
|
}
|
||||||
|
|
||||||
|
oldstore.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//Open if exists
|
||||||
|
if(file !is null)
|
||||||
|
open(file, tree);
|
||||||
|
|
||||||
|
showAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void displayMessage(string msg){
|
||||||
|
import core.thread;
|
||||||
|
new Thread({
|
||||||
|
Thread.getThis.sleep(dur!"msecs"(100));
|
||||||
|
|
||||||
|
auto lbl = new Label("");
|
||||||
|
lbl.setMarkup("<i>"~msg~"</i>");
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue