245 lines
6.7 KiB
D
245 lines
6.7 KiB
D
import model;
|
|
|
|
import std.stdio;
|
|
import std.functional : toDelegate;
|
|
import dsh : getHomeDir;
|
|
import std.path;
|
|
import std.file;
|
|
|
|
import gtk.MainWindow;
|
|
import gtk.Main;
|
|
import gtk.Builder;
|
|
import gtk.Application;
|
|
import gtk.ApplicationWindow;
|
|
import gtk.Entry;
|
|
import gtk.Box;
|
|
import gtk.ListBox;
|
|
import gtk.ListBoxRow;
|
|
import gtk.Label;
|
|
import gtk.CheckButton;
|
|
import gtk.ToggleButton;
|
|
import gtk.Button;
|
|
import gtk.Widget;
|
|
|
|
import gdk.Keymap;
|
|
import gdk.Keysyms : GdkKeysyms;
|
|
import gio.Resource;
|
|
import glib.Bytes;
|
|
|
|
class ToDoItemWidget : Box {
|
|
this(ToDoItem item, ToDoModel todoModel) {
|
|
super(GtkOrientation.HORIZONTAL, 5);
|
|
CheckButton button = new CheckButton();
|
|
button.setActive(item.checked);
|
|
button.addOnToggled(delegate(ToggleButton b) {
|
|
item.checked = b.getActive();
|
|
});
|
|
Label label = new Label(item.text);
|
|
label.setLineWrap(true);
|
|
label.setHalign(GtkAlign.START);
|
|
label.setValign(GtkAlign.CENTER);
|
|
this.packStart(button, false, false, 0);
|
|
this.packEnd(label, true, true, 0);
|
|
}
|
|
}
|
|
|
|
Entry taskEntry;
|
|
ListBox taskList;
|
|
ApplicationWindow window;
|
|
|
|
ToDoModel todoModel;
|
|
|
|
void main(string[] args) {
|
|
Main.init(args);
|
|
|
|
auto bytes = new Bytes(cast(ubyte[]) import("resources.gresource"));
|
|
Resource.register(new Resource(bytes));
|
|
|
|
Builder builder = new Builder();
|
|
builder.addFromResource("/ui/todo-ui.glade");
|
|
|
|
builder.addCallbackSymbol("onAddTask", &addTask);
|
|
builder.addCallbackSymbol("onWindowDestroy", &onWindowDestroy);
|
|
|
|
builder.addCallbackSymbol("onNewMenuActivated", &onNewMenuActivated);
|
|
builder.addCallbackSymbol("onSaveMenuActivated", &onSaveMenuActivated);
|
|
builder.addCallbackSymbol("onSaveAsMenuActivated", &onSaveAsMenuActivated);
|
|
builder.addCallbackSymbol("onOpenMenuActivated", &onOpenMenuActivated);
|
|
builder.addCallbackSymbol("onQuitMenuActivated", &onWindowDestroy);
|
|
builder.addCallbackSymbol("onAboutMenuActivated", &onAboutMenuActivated);
|
|
builder.connectSignals(null);
|
|
|
|
taskList = cast(ListBox) builder.getObject("taskList");
|
|
Widget listWidget = cast(Widget) taskList;
|
|
listWidget.addOnKeyPress(toDelegate(&taskListKeyPressed));
|
|
taskEntry = cast(Entry) builder.getObject("addTaskEntry");
|
|
|
|
todoModel = new ToDoModel();
|
|
string lastOpenPath = buildPath(getHomeDir(), ".config/todo-d/last-open.txt");
|
|
if (exists(lastOpenPath)) {
|
|
import std.string : strip;
|
|
string lastOpenFile = readText(lastOpenPath).strip;
|
|
todoModel.openFromJson(lastOpenFile);
|
|
}
|
|
|
|
window = cast(ApplicationWindow) builder.getObject("window");
|
|
auto listener = new UIModelUpdateListener(window);
|
|
todoModel.addListener(listener);
|
|
|
|
// Trigger UI updates once before rendering the window.
|
|
todoModel.notifyListeners();
|
|
listener.fileUpdated(todoModel.getOpenFilename());
|
|
|
|
window.showAll();
|
|
Main.run();
|
|
}
|
|
|
|
class UIModelUpdateListener : ModelUpdateListener {
|
|
private ApplicationWindow window;
|
|
|
|
this(ApplicationWindow window) {
|
|
this.window = window;
|
|
}
|
|
|
|
void itemsUpdated(ToDoItem[] items) {
|
|
taskList.removeAll();
|
|
foreach (item; items) {
|
|
auto widget = new ToDoItemWidget(item, todoModel);
|
|
auto row = new ListBoxRow();
|
|
row.setSelectable(true);
|
|
row.setActivatable(false);
|
|
row.add(widget);
|
|
taskList.add(row);
|
|
}
|
|
taskList.showAll();
|
|
}
|
|
|
|
void fileUpdated(string filename) {
|
|
if (filename is null) {
|
|
window.setTitle("todo-d");
|
|
} else {
|
|
window.setTitle("todo-d - " ~ filename);
|
|
}
|
|
window.showAll();
|
|
}
|
|
}
|
|
|
|
bool taskListKeyPressed(GdkEventKey* event, Widget w) {
|
|
int idx = taskList.getSelectedRow().getIndex();
|
|
int selectedPrio = idx + 1;
|
|
ToDoItem selectedItem = todoModel.getItemAt(selectedPrio);
|
|
if (selectedItem is null) return true;
|
|
bool ctrlDown = (event.state & ModifierType.CONTROL_MASK) > 0;
|
|
if (ctrlDown && event.keyval == GdkKeysyms.GDK_Up && todoModel.canIncrement(selectedItem)) {
|
|
todoModel.incrementPriority(selectedItem);
|
|
auto row = taskList.getRowAtIndex(idx - 1);
|
|
taskList.selectRow(row);
|
|
Widget rowWidget = cast(Widget) row;
|
|
rowWidget.grabFocus();
|
|
}
|
|
if (ctrlDown && event.keyval == GdkKeysyms.GDK_Down && todoModel.canDecrement(selectedItem)) {
|
|
todoModel.decrementPriority(selectedItem);
|
|
auto row = taskList.getRowAtIndex(idx + 1);
|
|
taskList.selectRow(row);
|
|
Widget rowWidget = cast(Widget) row;
|
|
rowWidget.grabFocus();
|
|
}
|
|
if (!ctrlDown && event.keyval == GdkKeysyms.GDK_Up && idx > 0) {
|
|
auto row = taskList.getRowAtIndex(idx - 1);
|
|
taskList.selectRow(row);
|
|
Widget rowWidget = cast(Widget) row;
|
|
rowWidget.grabFocus();
|
|
}
|
|
if (!ctrlDown && event.keyval == GdkKeysyms.GDK_Down && idx + 1 < todoModel.itemCount) {
|
|
auto row = taskList.getRowAtIndex(idx + 1);
|
|
taskList.selectRow(row);
|
|
Widget rowWidget = cast(Widget) row;
|
|
rowWidget.grabFocus();
|
|
}
|
|
if (event.keyval == GdkKeysyms.GDK_Delete) {
|
|
todoModel.removeItem(selectedPrio);
|
|
}
|
|
if (event.keyval == GdkKeysyms.GDK_Return) {
|
|
selectedItem.checked = !selectedItem.checked;
|
|
todoModel.notifyListeners();
|
|
auto row = taskList.getRowAtIndex(idx);
|
|
taskList.selectRow(row);
|
|
Widget rowWidget = cast(Widget) row;
|
|
rowWidget.grabFocus();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
extern (C) void addTask() {
|
|
todoModel.addItem(taskEntry.getText());
|
|
taskEntry.setText("");
|
|
}
|
|
|
|
extern (C) void onWindowDestroy() {
|
|
Main.quit();
|
|
if (todoModel.getOpenFilename() !is null) {
|
|
todoModel.saveToJson(todoModel.getOpenFilename());
|
|
string configPath = buildPath(getHomeDir(), ".config/todo-d/");
|
|
if (!exists(configPath)) {
|
|
mkdirRecurse(configPath);
|
|
}
|
|
string lastOpenFile = buildPath(configPath, "last-open.txt");
|
|
std.file.write(lastOpenFile, todoModel.getOpenFilename());
|
|
}
|
|
}
|
|
|
|
extern (C) void onNewMenuActivated() {
|
|
todoModel.clear();
|
|
}
|
|
|
|
extern (C) void onSaveMenuActivated() {
|
|
if (todoModel.getOpenFilename() is null) {
|
|
onSaveAsMenuActivated();
|
|
} else {
|
|
todoModel.saveToJson(todoModel.getOpenFilename());
|
|
}
|
|
}
|
|
|
|
extern (C) void onSaveAsMenuActivated() {
|
|
import gtk.FileChooserDialog;
|
|
auto dialog = new FileChooserDialog(
|
|
"Save As",
|
|
window,
|
|
FileChooserAction.SAVE
|
|
);
|
|
if (todoModel.getOpenFilename() !is null) {
|
|
dialog.setFilename(todoModel.getOpenFilename());
|
|
}
|
|
int result = dialog.run();
|
|
if (result == -5) {
|
|
todoModel.saveToJson(dialog.getFilename());
|
|
}
|
|
dialog.close();
|
|
}
|
|
|
|
extern (C) void onOpenMenuActivated() {
|
|
import gtk.FileChooserDialog;
|
|
auto dialog = new FileChooserDialog(
|
|
"Open",
|
|
window,
|
|
FileChooserAction.OPEN
|
|
);
|
|
int result = dialog.run();
|
|
if (result == -5) {
|
|
todoModel.openFromJson(dialog.getFilename());
|
|
}
|
|
dialog.close();
|
|
}
|
|
|
|
extern (C) void onAboutMenuActivated() {
|
|
import gtk.AboutDialog;
|
|
AboutDialog dialog = new AboutDialog();
|
|
dialog.setProgramName("Todo-D");
|
|
dialog.setLicenseType(GtkLicense.MIT_X11);
|
|
dialog.setAuthors(["Andrew Lalis"]);
|
|
dialog.setComments("A simple To-Do app written in D using GTK.");
|
|
dialog.setWebsite("https://github.com/andrewlalis/todo-d");
|
|
dialog.run();
|
|
dialog.destroy();
|
|
}
|