summaryrefslogtreecommitdiff
path: root/main.cpp
blob: e0b78e001d50cf08969496ed76cde26a4e9f67e9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include <iostream>
#include <string>
#include <filesystem>
#include <cstring>
#include <X11/Xutil.h>
#include <xkbcommon/xkbcommon-keysyms.h>
#include "seqmatcher.h"
#include "got.h"
#include "gui.h"
#include "xutil.h"


namespace Config {
	constexpr const char *const TRIGGER_KEY = "<F18>";
	constexpr const char *const TRIGGER_KEY_DESCR = "F18 (XF86Launch9)";
	constexpr const KeySym TRIGGER_KEY_X = XKB_KEY_XF86Launch9;

	const std::string USAGE_TEXT = std::string{}
		+ "  " + TRIGGER_KEY + " H        -- Get brief help in a notification\n"
		+ "  " + TRIGGER_KEY + " I        -- Check into a sheet\n"
		+ "  " + TRIGGER_KEY + " E        -- Edit the note for the currently running activity\n"
		+ "  " + TRIGGER_KEY + " O        -- Check out of the current activity\n"
		+ "  " + TRIGGER_KEY + " <space>  -- Switch and check into the 'misc' sheet\n"
		+ "  " + TRIGGER_KEY + " " + TRIGGER_KEY + "    -- Show notification with current status\n"
		+ "  " + TRIGGER_KEY + " Q        -- Quit this program (killing it also works of course)\n";

	const std::string USAGE_SUMMARY =
		std::string{TRIGGER_KEY} + " + (H)elp (I)ncheck (O)utcheck (E)dit (Q)uit; "
		+ TRIGGER_KEY + " " + TRIGGER_KEY + " = status; "
		+ TRIGGER_KEY + " <space> = into misc";
}


int main(int argc, char **argv) {
	if (argc >= 2 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0)) {
		std::cout <<
			"GUI for GOT (https://github.com/lieuwex/got), which is a rewrite of Timetrap.\n"
			"This thing runs as a daemon; it gets activated with the " << Config::TRIGGER_KEY_DESCR << " key on\n"
			"your keyboard. The following sequences trigger actions:\n"
			<< Config::USAGE_TEXT
			<< std::flush;
		return 0;
	}

	if (!std::filesystem::exists(got::getDBpath())) {
		std::cerr << "The GOT database (" << got::getDBpath() << ") doesn't exist!" << std::endl;
		std::cerr << "Please run 'got' at least once before using this tool." << std::endl;
		return 1;
	}

	auto dpy_pair = x::XOpenDisplayRAII(nullptr);
	Display *dpy = dpy_pair.first;

	bool quitRequested = false;

	SeqMatcher matcher{dpy};

	matcher.addSequence({XK_H}, []() {
		gui::showNotification(Config::USAGE_SUMMARY);
	});

	matcher.addSequence({XK_E}, []() {
		if (got::getRunning()) {
			if (auto descr = gui::promptText("Edit currently running entry's text:")) {
				got::editRunning(*descr);
			}
		} else {
			gui::showNotification("Cannot edit if not checked in");
		}
	});

	matcher.addSequence({XK_I}, []() {
		std::vector<std::string> sheets = got::getSheets();
		auto choice = gui::chooseList("Check in", "Sheet", sheets);
		if (choice) {
			auto current = got::getRunning();
			if (current) {
				got::checkOut();
				gui::showNotification("Checked out of sheet '" + current->first + "'");
			}
			got::checkIn(*choice);
			gui::showNotification("Checked in to sheet '" + *choice + "'");
			if (auto descr = gui::promptText("Set text for this '" + *choice + "' entry:")) {
				got::editRunning(*descr);
			}
		}
	});

	matcher.addSequence({XK_O}, []() {
		auto current = got::getRunning();
		if (current) {
			got::checkOut();
			gui::showNotification("Checked out of sheet '" + current->first + "'");
		} else {
			gui::showNotification("Cannot check out if not checked in");
		}
	});

	matcher.addSequence({XK_space}, []() {
		if (got::getRunning()) got::checkOut();
		got::checkIn("misc");
		gui::showNotification("Switched to 'misc'");
		if (auto descr = gui::promptText("Set text for this 'misc' entry:")) {
			got::editRunning(*descr);
		}
	});

	matcher.addSequence({Config::TRIGGER_KEY_X}, []() {
		auto current = got::getRunning();
		if (!current) {
			gui::showNotification("Currently checked out");
		} else {
			gui::showNotification(
				"Checked into '" + current->first + "'" +
				(current->second.empty() ? "" : " (" + current->second + ")")
			);
		}
	});

	matcher.addSequence({XK_Q}, [&quitRequested]() {
		gui::showNotification("GOT GUI is quitting");
		quitRequested = true;
	});

	x::globalKeyWatch(
		dpy, Config::TRIGGER_KEY_X,
		[dpy, &matcher, &quitRequested](const XKeyEvent&) -> bool {
			matcher.reset();

			SeqMatcher::Callback cb;

			x::globalKeyboardGrab(dpy, [&matcher, &cb](const XKeyEvent &ev) -> bool {
				auto opt_cb = matcher.observe(ev);
				if (opt_cb) {
					cb = *opt_cb;
					return true;
				} else {
					return false;
				}
			});

			if (cb) cb();

			return quitRequested;
		}
	);
}