diff --git a/CMakeLists.txt b/CMakeLists.txt index 5403a72..f203a07 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,6 +12,8 @@ add_executable(yellownotes main.cpp yellownotes.h yellownotes.cpp tr.h tr.cpp win32gtkdownloader.h win32gtkdownloader.cpp) + utils/whereami.c utils/whereami.h + exe_path.h exe_path.cpp) include(GNUInstallDirs) install(TARGETS yellownotes diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f64453d --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ + + +all: release + @echo make install + +install: release + mkdir -p /opt/yellownotes + cp build/release/yellownotes *.svg *.png /opt/yellownotes + +release: build/release/yellownotes + +build/release/yellownotes: exe_path.cpp gtk-imports.c gtkloader.cpp main.cpp tr.cpp yellownotes.cpp utils/whereami.c \ + exe_path.h gtk-imports.h gtkloader.h tr.h yellownotes.h utils/whereami.h + cmake -S . -B build/release + cmake --build build/release --target all + + +clean: + rm -rf build/release diff --git a/exe_path.cpp b/exe_path.cpp new file mode 100644 index 0000000..ecbd4c6 --- /dev/null +++ b/exe_path.cpp @@ -0,0 +1,20 @@ +#include "exe_path.h" + +#include "utils/whereami.h" + +#include + +std::string WhereAmI::containing_folder() +{ + int len = wai_getExecutablePath(NULL, 0, NULL); + + char *path = static_cast(malloc(len + 1)); + wai_getExecutablePath(path, len, NULL); + path[len] = '\0'; + + std::string p(path); + std::cout << p << std::endl; + return p; +} + +WhereAmI::WhereAmI() {} diff --git a/exe_path.h b/exe_path.h new file mode 100644 index 0000000..e532334 --- /dev/null +++ b/exe_path.h @@ -0,0 +1,14 @@ +#ifndef EXE_PATH_H +#define EXE_PATH_H + +#include + +class WhereAmI +{ +public: + std::string containing_folder(); +public: + WhereAmI(); +}; + +#endif // EXE_PATH_H diff --git a/gtk-imports.h b/gtk-imports.h index 0282eb8..20c3eaf 100644 --- a/gtk-imports.h +++ b/gtk-imports.h @@ -1,6 +1,8 @@ #ifndef GTK_IMPORTS_H #define GTK_IMPORTS_H +#include + /************************************************************************************* * boiler plate code to initialize Gtk functions etc. *************************************************************************************/ @@ -68,13 +70,17 @@ typedef void GtkDialog; typedef void GTimer; typedef void GtkComboBoxText; typedef void GtkComboBox; +typedef void GtkGrid; +typedef void GtkColorButton; +typedef void GtkColorChooser; +typedef void GtkSpinButton; typedef int gboolean; typedef int gint; typedef char gchar; typedef long glong; typedef unsigned long gulong; -typedef unsigned long guint32; +typedef unsigned int guint32; typedef unsigned int guint; typedef long long gint64; typedef unsigned long long guint64; @@ -274,7 +280,7 @@ typedef struct _GdkEventMotion gint16 is_hint; GdkDevice *device; gdouble x_root, y_root; -} GdkEventMotion; +} GdkEventMotion;; typedef struct _GdkEventKey { @@ -649,11 +655,24 @@ DECL(void, gtk_widget_set_vexpand, (GtkWidget* widget, gboolean expand )) DECL(void, gtk_widget_set_hexpand, (GtkWidget* widget, gboolean expand )) DECL(void, gtk_widget_set_halign, (GtkWidget* widget, GtkAlign align )) DECL(void, gtk_widget_set_valign, (GtkWidget* widget, GtkAlign align )) +DECL(GtkWidget*,gtk_grid_new, (void)) +DECL(void, gtk_grid_attach, (GtkGrid* grid, GtkWidget* child, gint left, gint top, gint width, gint height )) + +DECL(GtkWidget*, gtk_color_button_new, (void )) +DECL(void, gtk_color_chooser_set_rgba, (GtkColorChooser* chooser, const GdkRGBA* color )) +DECL(void, gtk_color_chooser_get_rgba, (GtkColorChooser* chooser, GdkRGBA* color )) DECL(gboolean, gtk_widget_translate_coordinates, ( GtkWidget* src_widget, GtkWidget* dest_widget, gint src_x, gint src_y, gint* dest_x, gint* dest_y)) DECL(void, gtk_main, (void)) DECL(void, gtk_main_quit, (void)) +DECL(GdkEvent*, gtk_get_current_event, (void )) +DECL(void, gtk_main_do_event, (GdkEvent* event)) +DECL(void, gdk_event_free, (GdkEvent* event)) + +DECL(GtkWidget*, gtk_spin_button_new_with_range, (gdouble min, gdouble max, gdouble step)) +DECL(void, gtk_spin_button_set_value, (GtkSpinButton* spin_button, gdouble value )) +DECL(gint, gtk_spin_button_get_value_as_int, (GtkSpinButton* spin_button )) DECL(unsigned long, g_signal_connect_data, (GObject *obj, const char *signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags)) diff --git a/gtkloader.cpp b/gtkloader.cpp index 21edad6..6685ef7 100644 --- a/gtkloader.cpp +++ b/gtkloader.cpp @@ -123,6 +123,7 @@ void GtkLoader::dlopen() "libgio-2.0.so", "libglib-2.0.so", "libgdk-3.so", + "libgdk_pixbuf-2.0.so", NULL }; int i; diff --git a/main.cpp b/main.cpp index 9e88434..e9906c9 100644 --- a/main.cpp +++ b/main.cpp @@ -6,6 +6,7 @@ extern "C" { } #include "yellownotes.h" +#include "exe_path.h" SIGNAL(YellowNotes, on_tray_activate, popupTrayMenu) @@ -22,9 +23,12 @@ static void activate (GtkApplication* app, gpointer user_data) int main(int argc, char **argv) { GtkLoader l; + WhereAmI w; srand(time(NULL)); // seed with current time + std::string my_path = w.containing_folder(); + try { l.loadGtk(); } catch(std::string msg) { diff --git a/title_only.png b/title_only.png new file mode 100644 index 0000000..c60b627 Binary files /dev/null and b/title_only.png differ diff --git a/title_only.svg b/title_only.svg new file mode 100644 index 0000000..400b1aa --- /dev/null +++ b/title_only.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/to_desktop.png b/to_desktop.png new file mode 100644 index 0000000..35b5a1e Binary files /dev/null and b/to_desktop.png differ diff --git a/to_desktop.svg b/to_desktop.svg new file mode 100644 index 0000000..c65ee3c --- /dev/null +++ b/to_desktop.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/tr.cpp b/tr.cpp index e217f8b..9db430b 100644 --- a/tr.cpp +++ b/tr.cpp @@ -1,5 +1,6 @@ #include "tr.h" #include +#include void tr::add(const char *sentence, ...) { @@ -39,9 +40,12 @@ const char *tr::translate(const char *str) { std::unordered_map::iterator it; + if (_lang == "en") { return str; } + it = _translations.find(_lang); if (it == _translations.end()) { + std::cerr << "No translation for: " << str << std::endl; return str; } else { Translations_t *m = it->second; @@ -49,6 +53,7 @@ const char *tr::translate(const char *str) std::string s(str); n_it = m->find(s); if (n_it == m->end()) { + std::cerr << "No translation for: " << str << std::endl; return str; } else { return n_it->second.c_str(); @@ -61,14 +66,36 @@ void tr::setLang(const std::string &l) _lang = l; } +#define nl(a, b) add(a, "nl:" b, nullptr) +#define en(a, b) add(a, "en:" b, nullptr) + tr::tr() { - add("Quit", "nl:Beëindigen", nullptr); - add("New Note", "nl:Nieuwe Notitie", nullptr); - add("Show Notes", "nl:Notities Presenteren", nullptr); - add("Hide Notes", "nl:Notities Verbergen", nullptr); - add("Reload Notes", "nl:Notities opnieuw laden", nullptr); - add("Setup", "nl:Instellingen", nullptr); + + nl("Quit", "Beëindigen"); + nl("New Note", "Nieuwe Notitie"); + nl("Show Notes", "Notities Presenteren"); + nl("Hide Notes", "Notities Verbergen"); + nl("Reload Notes", "Notities opnieuw laden"); + nl("Setup", "Instellingen"); + nl("Notes on desktop", "Noties naar achteren"); + nl("Language:", "Taalinstelling:"); + nl("Language:", "Taalinstelling:"); + nl("Ok", "Ok"); + nl("Yellownotes Setup", "Yellownotes Instellingen"); + nl("Foreground Color", "Voorgrond kleur"); + nl("Background Color", "Achtergrond kleur"); + nl("Dark", "Donker"); + nl("Yellow", "Geel"); + nl("Orange", "Oranje"); + nl("Blue", "Blauw"); + nl("Cyan", "Cyaan"); + nl("Green", "Groen"); + nl("Red", "Rood"); + nl("Grey", "Grijs"); + nl("Font size:", "Lettertype Grootte:"); + nl("Actual Font Size in Note", "Werkelijke lettertype grootte in de notitie"); + _lang = "en"; } diff --git a/utils/whereami.c b/utils/whereami.c new file mode 100644 index 0000000..37397d4 --- /dev/null +++ b/utils/whereami.c @@ -0,0 +1,815 @@ +// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses +// without any warranty. +// by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +// in case you want to #include "whereami.c" in a larger compilation unit +#if !defined(WHEREAMI_H) +#include "whereami.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(__linux__) || defined(__CYGWIN__) +#undef _DEFAULT_SOURCE +#define _DEFAULT_SOURCE +#elif defined(__APPLE__) +#undef _DARWIN_C_SOURCE +#define _DARWIN_C_SOURCE +#define _DARWIN_BETTER_REALPATH +#endif + +#if !defined(WAI_MALLOC) || !defined(WAI_FREE) || !defined(WAI_REALLOC) +#include +#endif + +#if !defined(WAI_MALLOC) +#define WAI_MALLOC(size) malloc(size) +#endif + +#if !defined(WAI_FREE) +#define WAI_FREE(p) free(p) +#endif + +#if !defined(WAI_REALLOC) +#define WAI_REALLOC(p, size) realloc(p, size) +#endif + +#ifndef WAI_NOINLINE +#if defined(_MSC_VER) +#define WAI_NOINLINE __declspec(noinline) +#elif defined(__GNUC__) +#define WAI_NOINLINE __attribute__((noinline)) +#else +#error unsupported compiler +#endif +#endif + +#if defined(_MSC_VER) +#define WAI_RETURN_ADDRESS() _ReturnAddress() +#elif defined(__GNUC__) +#define WAI_RETURN_ADDRESS() __builtin_extract_return_addr(__builtin_return_address(0)) +#else +#error unsupported compiler +#endif + +#if defined(_WIN32) + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#if defined(_MSC_VER) +#pragma warning(push, 3) +#endif +#include +#include +#if defined(_MSC_VER) +#pragma warning(pop) +#endif +#if (_MSC_VER >= 1900) +#include +#else +#define bool int +#define false 0 +#define true 1 +#endif + +static int WAI_PREFIX(getModulePath_)(HMODULE module, char* out, int capacity, int* dirname_length) +{ + wchar_t buffer1[MAX_PATH]; + wchar_t buffer2[MAX_PATH]; + wchar_t* path = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + DWORD size; + int length_, length__; + + size = GetModuleFileNameW(module, buffer1, sizeof(buffer1) / sizeof(buffer1[0])); + + if (size == 0) + break; + else if (size == (DWORD)(sizeof(buffer1) / sizeof(buffer1[0]))) + { + DWORD size_ = size; + do + { + wchar_t* path_; + + path_ = (wchar_t*)WAI_REALLOC(path, sizeof(wchar_t) * size_ * 2); + if (!path_) + break; + size_ *= 2; + path = path_; + size = GetModuleFileNameW(module, path, size_); + } + while (size == size_); + + if (size == size_) + break; + } + else + path = buffer1; + + if (!_wfullpath(buffer2, path, MAX_PATH)) + break; + length_ = (int)wcslen(buffer2); + length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_ , out, capacity, NULL, NULL); + + if (length__ == 0) + length__ = WideCharToMultiByte(CP_UTF8, 0, buffer2, length_, NULL, 0, NULL, NULL); + if (length__ == 0) + break; + + if (length__ <= capacity && dirname_length) + { + int i; + + for (i = length__ - 1; i >= 0; --i) + { + if (out[i] == '\\') + { + *dirname_length = i; + break; + } + } + } + + length = length__; + } + + if (path != buffer1) + WAI_FREE(path); + + return ok ? length : -1; +} + +WAI_NOINLINE WAI_FUNCSPEC + int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + return WAI_PREFIX(getModulePath_)(NULL, out, capacity, dirname_length); +} + +WAI_NOINLINE WAI_FUNCSPEC + int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + HMODULE module; + int length = -1; + +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable: 4054) +#endif + if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)WAI_RETURN_ADDRESS(), &module)) +#if defined(_MSC_VER) +#pragma warning(pop) +#endif + { + length = WAI_PREFIX(getModulePath_)(module, out, capacity, dirname_length); + } + + return length; +} + +#elif defined(__linux__) || defined(__CYGWIN__) || defined(__sun) || defined(WAI_USE_PROC_SELF_EXE) + +#include +#include +#include +#if defined(__linux__) +#include +#else +#include +#endif +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include +#include + +#if !defined(WAI_PROC_SELF_EXE) +#if defined(__sun) +#define WAI_PROC_SELF_EXE "/proc/self/path/a.out" +#else +#define WAI_PROC_SELF_EXE "/proc/self/exe" +#endif +#endif + +WAI_FUNCSPEC + int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + resolved = realpath(WAI_PROC_SELF_EXE, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + return ok ? length : -1; +} + +#if !defined(WAI_PROC_SELF_MAPS_RETRY) +#define WAI_PROC_SELF_MAPS_RETRY 5 +#endif + +#if !defined(WAI_PROC_SELF_MAPS) +#if defined(__sun) +#define WAI_PROC_SELF_MAPS "/proc/self/map" +#else +#define WAI_PROC_SELF_MAPS "/proc/self/maps" +#endif +#endif + +#if !defined(WAI_STRINGIZE) +#define WAI_STRINGIZE(s) +#define WAI_STRINGIZE_(s) #s +#endif + +#if defined(__ANDROID__) || defined(ANDROID) +#include +#include +#include +#endif +#include + +WAI_NOINLINE WAI_FUNCSPEC + int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + int length = -1; + FILE* maps = NULL; + + for (int r = 0; r < WAI_PROC_SELF_MAPS_RETRY; ++r) + { + maps = fopen(WAI_PROC_SELF_MAPS, "r"); + if (!maps) + break; + + for (;;) + { + char buffer[128 + PATH_MAX]; + uintptr_t low, high; + char perms[5]; + uint64_t offset; + uint32_t major, minor, inode; + char path[PATH_MAX + 1]; + + if (!fgets(buffer, sizeof(buffer), maps)) + break; + + if (sscanf(buffer, "%" SCNxPTR "-%" SCNxPTR " %s %" SCNx64 " %x:%x %u %" WAI_STRINGIZE(PATH_MAX) "[^\n]\n", &low, &high, perms, &offset, &major, &minor, &inode, path) == 8) + { + void* _addr = WAI_RETURN_ADDRESS(); + uintptr_t addr = (uintptr_t)_addr; + if (low <= addr && addr <= high) + { + char* resolved; + + resolved = realpath(path, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); +#if defined(__ANDROID__) || defined(ANDROID) + if (length > 4 + &&buffer[length - 1] == 'k' + &&buffer[length - 2] == 'p' + &&buffer[length - 3] == 'a' + &&buffer[length - 4] == '.') + { + int fd = open(path, O_RDONLY); + if (fd == -1) + { + length = -1; // retry + break; + } + + char* begin = (char*)mmap(0, offset, PROT_READ, MAP_SHARED, fd, 0); + if (begin == MAP_FAILED) + { + close(fd); + length = -1; // retry + break; + } + + char* p = begin + offset - 30; // minimum size of local file header + while (p >= begin) // scan backwards + { + if (*((uint32_t*)p) == 0x04034b50UL) // local file header signature found + { + uint16_t length_ = *((uint16_t*)(p + 26)); + + if (length + 2 + length_ < (int)sizeof(buffer)) + { + memcpy(&buffer[length], "!/", 2); + memcpy(&buffer[length + 2], p + 30, length_); + length += 2 + length_; + } + + break; + } + + --p; + } + + munmap(begin, offset); + close(fd); + } +#endif + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + + break; + } + } + } + + fclose(maps); + maps = NULL; + + if (length != -1) + break; + } + + return length; +} + +#elif defined(__APPLE__) + +#include +#include +#include +#include +#include +#include + +WAI_FUNCSPEC + int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char* path = buffer1; + char* resolved = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + uint32_t size = (uint32_t)sizeof(buffer1); + if (_NSGetExecutablePath(path, &size) == -1) + { + path = (char*)WAI_MALLOC(size); + if (!_NSGetExecutablePath(path, &size)) + break; + } + + resolved = realpath(path, buffer2); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + if (path != buffer1) + WAI_FREE(path); + + return ok ? length : -1; +} + +WAI_NOINLINE WAI_FUNCSPEC + int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for(;;) + { + Dl_info info; + + if (dladdr(WAI_RETURN_ADDRESS(), &info)) + { + resolved = realpath(info.dli_fname, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + break; + } + + return length; +} + +#elif defined(__QNXNTO__) + +#include +#include +#include +#include +#include +#include + +#if !defined(WAI_PROC_SELF_EXE) +#define WAI_PROC_SELF_EXE "/proc/self/exefile" +#endif + +WAI_FUNCSPEC + int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char* resolved = NULL; + FILE* self_exe = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + self_exe = fopen(WAI_PROC_SELF_EXE, "r"); + if (!self_exe) + break; + + if (!fgets(buffer1, sizeof(buffer1), self_exe)) + break; + + resolved = realpath(buffer1, buffer2); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + fclose(self_exe); + + return ok ? length : -1; +} + +WAI_FUNCSPEC + int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for(;;) + { + Dl_info info; + + if (dladdr(WAI_RETURN_ADDRESS(), &info)) + { + resolved = realpath(info.dli_fname, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + break; + } + + return length; +} + +#elif defined(__DragonFly__) || defined(__FreeBSD__) || \ +defined(__FreeBSD_kernel__) || defined(__NetBSD__) || defined(__OpenBSD__) + +#include +#include +#include +#include +#include +#include +#include + +#if defined(__OpenBSD__) + +#include + + WAI_FUNCSPEC + int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[4096]; + char buffer2[PATH_MAX]; + char buffer3[PATH_MAX]; + char** argv = (char**)buffer1; + char* resolved = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, getpid(), KERN_PROC_ARGV }; + size_t size; + + if (sysctl(mib, 4, NULL, &size, NULL, 0) != 0) + break; + + if (size > sizeof(buffer1)) + { + argv = (char**)WAI_MALLOC(size); + if (!argv) + break; + } + + if (sysctl(mib, 4, argv, &size, NULL, 0) != 0) + break; + + if (strchr(argv[0], '/')) + { + resolved = realpath(argv[0], buffer2); + if (!resolved) + break; + } + else + { + const char* PATH = getenv("PATH"); + if (!PATH) + break; + + size_t argv0_length = strlen(argv[0]); + + const char* begin = PATH; + while (1) + { + const char* separator = strchr(begin, ':'); + const char* end = separator ? separator : begin + strlen(begin); + + if (end - begin > 0) + { + if (*(end -1) == '/') + --end; + + if (((end - begin) + 1 + argv0_length + 1) <= sizeof(buffer2)) + { + memcpy(buffer2, begin, end - begin); + buffer2[end - begin] = '/'; + memcpy(buffer2 + (end - begin) + 1, argv[0], argv0_length + 1); + + resolved = realpath(buffer2, buffer3); + if (resolved) + break; + } + } + + if (!separator) + break; + + begin = ++separator; + } + + if (!resolved) + break; + } + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + if (argv != (char**)buffer1) + WAI_FREE(argv); + + return ok ? length : -1; +} + +#else + +WAI_FUNCSPEC + int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length) +{ + char buffer1[PATH_MAX]; + char buffer2[PATH_MAX]; + char* path = buffer1; + char* resolved = NULL; + int length = -1; + bool ok; + + for (ok = false; !ok; ok = true) + { +#if defined(__NetBSD__) + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; +#else + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; +#endif + size_t size = sizeof(buffer1); + + if (sysctl(mib, 4, path, &size, NULL, 0) != 0) + break; + + resolved = realpath(path, buffer2); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + return ok ? length : -1; +} + +#endif + +WAI_NOINLINE WAI_FUNCSPEC + int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length) +{ + char buffer[PATH_MAX]; + char* resolved = NULL; + int length = -1; + + for(;;) + { + Dl_info info; + + if (dladdr(WAI_RETURN_ADDRESS(), &info)) + { + resolved = realpath(info.dli_fname, buffer); + if (!resolved) + break; + + length = (int)strlen(resolved); + if (length <= capacity) + { + memcpy(out, resolved, length); + + if (dirname_length) + { + int i; + + for (i = length - 1; i >= 0; --i) + { + if (out[i] == '/') + { + *dirname_length = i; + break; + } + } + } + } + } + + break; + } + + return length; +} + +#else + +#error unsupported platform + +#endif + +#ifdef __cplusplus +} +#endif diff --git a/utils/whereami.h b/utils/whereami.h new file mode 100644 index 0000000..2380aaf --- /dev/null +++ b/utils/whereami.h @@ -0,0 +1,69 @@ +// (‑●‑●)> dual licensed under the WTFPL v2 and MIT licenses +// without any warranty. +// by Gregory Pakosz (@gpakosz) +// https://github.com/gpakosz/whereami + +#ifndef WHEREAMI_H +#define WHEREAMI_H + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WAI_FUNCSPEC +#define WAI_FUNCSPEC +#endif +#ifndef WAI_PREFIX +#define WAI_PREFIX(function) wai_##function +#endif + +/** + * Returns the path to the current executable. + * + * Usage: + * - first call `int length = wai_getExecutablePath(NULL, 0, NULL);` to + * retrieve the length of the path + * - allocate the destination buffer with `path = (char*)malloc(length + 1);` + * - call `wai_getExecutablePath(path, length, NULL)` again to retrieve the + * path + * - add a terminal NUL character with `path[length] = '\0';` + * + * @param out destination buffer, optional + * @param capacity destination buffer capacity + * @param dirname_length optional recipient for the length of the dirname part + * of the path. Available only when `capacity` is large enough to retrieve the + * path. + * + * @return the length of the executable path on success (without a terminal NUL + * character), otherwise `-1` + */ +WAI_FUNCSPEC + int WAI_PREFIX(getExecutablePath)(char* out, int capacity, int* dirname_length); + +/** + * Returns the path to the current module + * + * Usage: + * - first call `int length = wai_getModulePath(NULL, 0, NULL);` to retrieve + * the length of the path + * - allocate the destination buffer with `path = (char*)malloc(length + 1);` + * - call `wai_getModulePath(path, length, NULL)` again to retrieve the path + * - add a terminal NUL character with `path[length] = '\0';` + * + * @param out destination buffer, optional + * @param capacity destination buffer capacity + * @param dirname_length optional recipient for the length of the dirname part + * of the path. Available only when `capacity` is large enough to retrieve the + * path. + * + * @return the length of the module path on success (without a terminal NUL + * character), otherwise `-1` + */ +WAI_FUNCSPEC + int WAI_PREFIX(getModulePath)(char* out, int capacity, int* dirname_length); + +#ifdef __cplusplus +} +#endif + +#endif // #ifndef WHEREAMI_H diff --git a/yellownotes.cpp b/yellownotes.cpp index e055c33..1a3d9c0 100644 --- a/yellownotes.cpp +++ b/yellownotes.cpp @@ -22,6 +22,8 @@ extern "C" { #include "tr.h" +#include + static void trim(std::string &s) { size_t start = s.find_first_not_of(" \t\n\r"); size_t end = s.find_last_not_of(" \t\n\r"); @@ -49,6 +51,8 @@ private: GtkImage *_delete_image; GtkImage *_plus_image; GtkImage *_hide_image; + GtkImage *_to_desktop_image; + GtkImage *_title_only_image; GtkWidget *_title_label; GtkWidget *_title_entry; GtkWidget *_title_separator; @@ -62,16 +66,27 @@ private: std::string _title; bool _hidden; + bool _hidden_loaded; + + bool _title_only; + bool _title_only_loaded; + int _x; int _y; + bool _pos_loaded; + int _width; int _height; + bool _size_loaded; + bool _in_transaction; bool _editing_title; int _save_counter; int _save_id; + bool _double_clicked; + ColorType_t _color; bool _color_changed; @@ -91,7 +106,7 @@ private: void updatePosition(); void updateHidden(); void updateSize(); - void updateColor(); + void updateTitleOnly(); void adjustTitle(bool mutate); @@ -106,11 +121,16 @@ private: void updateWidgetColors(GtkWidget *w); public: - void changed(GtkWidget *sender); - void size_allocated(GtkWidget *sender, GtkAllocation *a); + void resized(int width, int height); + void size_allocated(GtkWidget *sender, GtkAllocation *alloc); + void positioned(GtkWidget *sender, int x, int y); public: void toFront(); + void toDesktop(); + void fromDesktop(); + void updateColor(); + void toggleTitleOnly(); public: bool move_begin(GtkWidget *sender, GdkEventButton *evt); @@ -127,16 +147,67 @@ public: public: std::string title(); bool isHidden(); + void doubleClicked(); + YellowNotes *notes(); public: void load(); void save(); + void setLoaded(); public: YellowNote(YellowNotes *notes, const std::string &filename); ~YellowNote(); }; +class SettingContainer +{ +public: + ColorType_t color; + GtkLabel *font_label; + GtkSpinButton *font_size_btn; + YellowNotes *notes; + YellowNote *note; + bool bg; + +public: + void colorSet(GtkColorButton *sender) { + GdkRGBA rgba; + gtk_color_chooser_get_rgba(sender, &rgba); + std::string c = notes->fromRGBA(rgba); + if (bg) { + notes->setBgColor(color, c); + } else { + notes->setFgColor(color, c); + } + } + + void fontSizeSet(GtkWidget *Sender) { + int size = gtk_spin_button_get_value_as_int(font_size_btn); + notes->updateWidgetCss(font_label, ColorType_t::YELLOW); + notes->setFontSize(size); + } + + SettingContainer(ColorType_t t, bool _bg, YellowNotes *n) { + color = t; + bg = _bg; + notes = n; + font_label = nullptr; + font_size_btn = nullptr; + } + + SettingContainer(GtkSpinButton *btn, GtkLabel *fl, YellowNotes *n) { + color = FIRST; + font_label = fl; + font_size_btn = btn; + notes = n; + bg = false; + } +}; + +SIGNAL(SettingContainer, on_color_set, colorSet); +SIGNAL(SettingContainer, on_font_size_set, fontSizeSet); + SIGNAL2(YellowNote, on_size_allocated, size_allocated, GtkAllocation) BSIGNAL2(YellowNote, on_button_press, move_begin, GdkEventButton); BSIGNAL2(YellowNote, on_button_release, move_end, GdkEventButton); @@ -156,7 +227,8 @@ static gboolean on_text_save_timeout(gpointer data) } SIGNAL(YellowNotes, on_new_yellow, newNote) -SIGNAL(YellowNotes, on_show, showNotes); +SIGNAL(YellowNotes, on_show, notesFromDesktop); +SIGNAL(YellowNotes, on_to_back, notesToDesktop); SIGNAL(YellowNotes, on_reload, reloadNotes); SIGNAL(YellowNotes, on_setup, setup); SIGNAL(YellowNotes, on_quit, quit) @@ -210,28 +282,122 @@ std::string YellowNotes::notesDir() return notes_dir; } -std::string YellowNotes::css(ColorType_t type) +std::string YellowNotes::fromRGBA(const GdkRGBA &rgba) { - const char *bgs[] = { "#404040", // dark - "#faf32a", // yellow - "#fcbf56", // orange, - "#5df0f5", // blue - "#fc77f4", // Cyaan - "#74fc94", // greeen - "#f7bcbc", // red - "#cdcfd1" // grey + char buf[100]; + + auto to255 = [](double c) { + return static_cast(floor((c * 255) + 0.5)); }; - const char *fgs[] = { "white", - "black", - "black", - "black", - "black", - "black", - "black", - "black" + sprintf(buf, "#%02x%02x%02x", to255(rgba.red), to255(rgba.green), to255(rgba.blue)); + std::string col = buf; + return col; +} + +void YellowNotes::toRGBA(const std::string &col, GdkRGBA &rgba) +{ + auto from_hex = [](std::string hex_num) { + return strtol(hex_num.c_str(), NULL, 16); }; + double red = from_hex(col.substr(1, 2)) / 255.0; + double green = from_hex(col.substr(3, 2)) / 255.0; + double blue = from_hex(col.substr(5, 2)) / 255.0; + double alpha = 1.0; + + rgba.alpha = alpha; + rgba.blue = blue; + rgba.green = green; + rgba.red = red; +} + +void YellowNotes::updateWidgetCss(GtkWidget *w, ColorType_t col) +{ + auto set_style = [this, col](GtkWidget *widget) { + GtkStyleContext *c = gtk_widget_get_style_context(widget); + const char *style = gtk_style_context_to_string(c, GTK_STYLE_CONTEXT_PRINT_RECURSE); + GtkCssProvider *css_p = gtk_css_provider_new(); + gtk_style_context_add_provider(c, css_p, GTK_STYLE_PROVIDER_PRIORITY_USER); + std::string widget_css = css(col); + gtk_css_provider_load_from_data(css_p, widget_css.c_str(), widget_css.size(), nullptr); + }; + + set_style(w); +} + +std::string YellowNotes::getFgColor(ColorType_t type) +{ + char buf[100]; + sprintf(buf, "fg_color_%d", type); + std::string key = buf; + std::unordered_map::iterator it = _cfg.find(key); + if (it != _cfg.end()) { + return _cfg[key]; + } else { + const char *fgs[] = { "#ffffff", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000", + "#000000" + }; + std::string c(fgs[type]); + return c; + } +} + +std::string YellowNotes::getBgColor(ColorType_t type) +{ + char buf[100]; + sprintf(buf, "bg_color_%d", type); + std::string key = buf; + std::unordered_map::iterator it = _cfg.find(key); + if (it != _cfg.end()) { + return _cfg[key]; + } else { + const char *bgs[] = { + "#404040", // dark + "#faf32a", // yellow + "#fcbf56", // orange, + "#5df0f5", // blue + "#fc77f4", // Cyaan + "#74fc94", // greeen + "#f7bcbc", // red + "#cdcfd1" // grey + }; + std::string c(bgs[type]); + return c; + } +} + +void YellowNotes::setFgColor(ColorType_t type, const std::string &col) +{ + char buf[100]; + sprintf(buf, "fg_color_%d", type); + std::string key = buf; + _cfg.erase(key); + _cfg.insert(std::pair(key, col)); + saveConfig(); +} + +void YellowNotes::setBgColor(ColorType_t type, const std::string &col) +{ + char buf[100]; + sprintf(buf, "bg_color_%d", type); + std::string key = buf; + _cfg.erase(key); + _cfg.insert(std::pair(key, col)); + saveConfig(); +} + +std::string YellowNotes::css(ColorType_t type) +{ + + if (type > LAST) { type = YELLOW; } + auto from_hex = [](std::string hex_num) { return strtol(hex_num.c_str(), NULL, 16); }; @@ -255,14 +421,14 @@ std::string YellowNotes::css(ColorType_t type) std::string css = std::string() + "label, box.horizontal, textview.view, textview.view text, frame, messagedialog.background {\n" - " background-color: " + bgs[type] + ";\n" - " color: " + fgs[type] + ";\n" + " background-color: " + getBgColor(type) + ";\n" + " color: " + getFgColor(type) + ";\n" " font-family: sans;\n" " font-size: " + font_size + ";\n" "}\n" "frame border {\n" " border: none;\n" - " box-shadow: 5px 5px 5px " + darker(bgs[type]) + ";\n" + " box-shadow: 5px 5px 5px " + darker(getBgColor(type)) + ";\n" " margin: 5px;\n" "}\n"; @@ -271,9 +437,28 @@ std::string YellowNotes::css(ColorType_t type) int YellowNotes::fontSize() { + std::string key("font_size"); + std::unordered_map::iterator it; + it = _cfg.find(key); + if (it != _cfg.end()) { + _font_size = atoi(it->second.c_str()); + if (_font_size < 6 || _font_size > 30) { _font_size = 15; } + } return _font_size; } +void YellowNotes::setFontSize(int size) +{ + _font_size = size; + char buf[100]; + sprintf(buf, "%d", size); + std::string v = buf; + std::string key("font_size"); + _cfg.erase(key); + _cfg.insert(std::pair(key, v)); + saveConfig(); +} + int YellowNotes::iconSize() { return _font_size * 1.5; @@ -291,6 +476,7 @@ void YellowNotes::popupTrayMenu(void *sender) GtkMenuItem *new_yellow = gtk_menu_item_new_with_label(_("New Note")); GtkMenuItem *show_notes = gtk_menu_item_new_with_label(_("Show Notes")); + GtkMenuItem *notes_to_back = gtk_menu_item_new_with_label(_("Notes on desktop")); GtkMenuItem *reload_notes = gtk_menu_item_new_with_label(_("Reload Notes")); GtkWidget *sep = gtk_separator_menu_item_new(); @@ -314,6 +500,7 @@ void YellowNotes::popupTrayMenu(void *sender) gtk_menu_shell_append(tray_menu, new_yellow); gtk_menu_shell_append(tray_menu, show_notes); + gtk_menu_shell_append(tray_menu, notes_to_back); gtk_menu_shell_append(tray_menu, reload_notes); gtk_menu_shell_append(tray_menu, sep); @@ -330,6 +517,7 @@ void YellowNotes::popupTrayMenu(void *sender) g_signal_connect(new_yellow, "activate", on_new_yellow, this); g_signal_connect(show_notes, "activate", on_show, this); + g_signal_connect(notes_to_back, "activate", on_to_back, this); g_signal_connect(reload_notes, "activate", on_reload, this); g_signal_connect(setup, "activate", on_setup, this); g_signal_connect(quit, "activate", on_quit, this); @@ -412,6 +600,13 @@ void YellowNotes::saveConfig() } } +gboolean load_notes_timeout(void *_notes) +{ + YellowNotes *notes = static_cast(_notes); + notes->notesToDesktop(nullptr); + return false; +} + void YellowNotes::loadNotes() { gtk_widget_show_all(topLevel()); @@ -437,12 +632,17 @@ void YellowNotes::loadNotes() const auto base_name = entry.path().filename().string(); if (base_name.rfind(".note") > 0) { YellowNote *note = new YellowNote(this, full_name); + //note->fromDesktop(); _notes.push_back(note); } } } gtk_widget_hide(topLevel()); + + if (cfgOnDesktop()) { + g_timeout_add(500, load_notes_timeout, this); + } } void YellowNotes::showNotes(void *sender) @@ -453,7 +653,27 @@ void YellowNotes::showNotes(void *sender) note->toFront(); it++; } +} +void YellowNotes::notesToDesktop(void *sender) +{ + std::list::const_iterator it = _notes.begin(); + while (it != _notes.end()) { + YellowNote *note = *it; + note->toDesktop(); + it++; + } + setCfgOnDesktop(true); +} + +void YellowNotes::notesFromDesktop(void *sender) +{ std::list::const_iterator it = _notes.begin(); + while (it != _notes.end()) { + YellowNote *note = *it; + note->fromDesktop(); + it++; + } + setCfgOnDesktop(false); } void YellowNotes::setupCancel(GtkWidget *sender) @@ -474,17 +694,30 @@ bool YellowNotes::setupDel(GtkWidget *sender, void *evt) void YellowNotes::setupClose(GtkWidget *sender) { if (_dlg != nullptr) { - std::cout << "hey" << std::endl; gtk_dialog_response(_dlg, GTK_RESPONSE_OK); std::string lang = std::string(gtk_combo_box_get_active_id(_langs)); - std::cout << "lang " << lang << std::endl; setCurrentLang(lang); gtk_widget_destroy(_dlg); + + std::list::iterator it; + for(it = _settings_containers.begin() ; it != _settings_containers.end(); it++) { + SettingContainer *s = *it; + delete s; + } + _settings_containers.clear(); + _dlg = nullptr; _langs = nullptr; + saveConfig(); + + std::list::iterator n_it; + for(n_it = _notes.begin(); n_it != _notes.end(); n_it++) { + YellowNote *n = *n_it; + n->updateColor(); + } + } else { - std::cout << "close button" << std::endl; } } @@ -504,13 +737,72 @@ void YellowNotes::setup(void *sender) gtk_combo_box_text_append(langs, "en", "English"); gtk_combo_box_text_append(langs, "nl", "Nederlands"); + GtkWidget *lbl_langs = gtk_label_new(_("Language:")); + gtk_widget_set_size_request(lbl_langs, 150, -1); + std::string current_lang = currentLang(); gtk_combo_box_set_active_id(langs, current_lang.c_str()); + gtk_widget_set_size_request(langs, 300, -1); - GtkWidget *hbox = gtk_box_new(GtkOrientation::GTK_ORIENTATION_HORIZONTAL, 5); + GtkGrid *grid = gtk_grid_new(); - gtk_container_add(hbox, langs); - gtk_container_add(content, hbox); + gtk_grid_attach(grid, lbl_langs, 0, 0, 1, 1); + gtk_grid_attach(grid, langs, 1, 0, 2, 1); + + GtkLabel *lbl_fg = gtk_label_new(_("Foreground Color")); + GtkLabel *lbl_bg = gtk_label_new(_("Background Color")); + gtk_grid_attach(grid, lbl_fg, 1, 1, 1, 1); + gtk_grid_attach(grid, lbl_bg, 2, 1, 1, 1); + + + int i = ColorType_t::FIRST; + std::string colors[] = { _("Dark"), _("Yellow"), _("Orange"), + _("Blue"), _("Cyan"), _("Green"), _("Red"), _("Grey") }; + + int offset = 2; + + for(;i <= ColorType_t::LAST; i++) { + GtkWidget *lbl = gtk_label_new(colors[i].c_str()); + gtk_grid_attach(grid, lbl, 0, i + offset, 1, 1); + + GdkRGBA bg_rgba, fg_rgba; + std::string fg_color = getFgColor(static_cast(i)); + std::string bg_color = getBgColor(static_cast(i)); + toRGBA(fg_color, fg_rgba); + toRGBA(bg_color, bg_rgba); + + GtkColorButton *fg_btn = gtk_color_button_new(); + gtk_color_chooser_set_rgba(fg_btn, &fg_rgba); + SettingContainer *fg = new SettingContainer(static_cast(i), false, this); + _settings_containers.push_back(fg); + g_signal_connect(fg_btn, "color_set", on_color_set, fg); + gtk_grid_attach(grid, fg_btn, 1, i + offset, 1, 1); + + GtkColorButton *bg_btn = gtk_color_button_new(); + gtk_color_chooser_set_rgba(bg_btn, &bg_rgba); + SettingContainer *bg = new SettingContainer(static_cast(i), true, this); + _settings_containers.push_back(bg); + g_signal_connect(bg_btn, "color_set", on_color_set, bg); + gtk_grid_attach(grid, bg_btn, 2, i + offset, 1, 1); + } + + offset = i + offset; + + gtk_grid_attach(grid, gtk_label_new(_("Font size:")), 0, offset, 1, 1); + GtkSpinButton *fs = gtk_spin_button_new_with_range(6.0, 20.0, 1.0); + gtk_spin_button_set_value(fs, fontSize()); + gtk_grid_attach(grid, fs, 1, offset, 1, 1); + + GtkLabel *font_label = gtk_label_new(_("Actual Font Size in Note")); + gtk_widget_set_size_request(font_label, 400, -1); + gtk_grid_attach(grid, font_label, 2, offset, 1, 1); + + SettingContainer *fl = new SettingContainer(fs, font_label, this); + _settings_containers.push_back(fl); + g_signal_connect(fs, "value_changed", on_font_size_set, fl); + fl->fontSizeSet(fs); + + gtk_container_add(content, grid); g_signal_connect(ok_btn, "clicked", on_setup_ok, this); g_signal_connect(dlg, "close", on_setup_close, this); @@ -527,8 +819,10 @@ void YellowNotes::reloadNotes(void *sender) delete note; it++; } + _notes.clear(); loadNotes(); + showNotes(sender); } @@ -541,7 +835,7 @@ void YellowNotes::newNote(void *sender) char buf[200]; int r = rand() % 1000; - sprintf(buf, "%llu-%d", milliseconds_since_epoch, r); + sprintf(buf, "%d-%llu", r, milliseconds_since_epoch); std::string new_note_file = buf; std::string notes_dir = notesDir(); @@ -562,6 +856,7 @@ void YellowNotes::newNote(void *sender) YellowNote *note = new YellowNote(this, p.string()); _notes.push_back(note); + note->setLoaded(); note->show(); note->save(); } @@ -582,6 +877,21 @@ std::string YellowNotes::currentLang() return _cfg[key]; } +bool YellowNotes::cfgOnDesktop() +{ + std::string key("on_desktop"); + return (_cfg[key] == "true") ? true : false; +} + +void YellowNotes::setCfgOnDesktop(bool y) +{ + std::string key("on_desktop"); + _cfg.erase(key); + std::string v = (y) ? "true" : "false"; + _cfg.insert(std::pair(key, v)); + saveConfig(); +} + void YellowNotes::setCurrentLang(const std::string &l) { std::string key("lang"); @@ -642,6 +952,7 @@ YellowNote::YellowNote(YellowNotes *notes, const std::string &filename) _width = 300; _height = 200; _hidden = false; + _title_only = false; _editing_title = false; _save_counter = 0; _save_id = -1; @@ -654,11 +965,22 @@ YellowNote::YellowNote(YellowNotes *notes, const std::string &filename) _color = ColorType_t::YELLOW; _color_changed = false; + _in_transaction = true; + _hidden_loaded = false; + _pos_loaded = false; + _size_loaded = false; + _double_clicked = false; + _note_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_widget_set_can_focus(_note_widget, true); gtk_window_set_decorated(_note_widget, false); +#ifdef _WIN32 gtk_window_set_type_hint(_note_widget, GDK_WINDOW_TYPE_HINT_POPUP_MENU); gtk_window_set_transient_for(_note_widget, _notes->topLevel()); +#endif +#ifdef __linux + gtk_window_set_type_hint(_note_widget, GdkWindowTypeHint::GDK_WINDOW_TYPE_HINT_UTILITY); +#endif _scroll_widget = gtk_scrolled_window_new(nullptr, nullptr); gtk_widget_set_vexpand(_scroll_widget, true); @@ -712,14 +1034,31 @@ YellowNote::YellowNote(YellowNotes *notes, const std::string &filename) width, height, nullptr ); _hide_image = gtk_image_new_from_pixbuf(hide_pixbuf); - gtk_widget_set_halign(_plus_image, GtkAlign::GTK_ALIGN_END); + gtk_widget_set_halign(_hide_image, GtkAlign::GTK_ALIGN_END); g_object_unref(hide_pixbuf); + GdkPixbuf *to_desktop_pixbuf = gdk_pixbuf_new_from_file_at_size(notes->imageFile("to_desktop").c_str(), + width, height, nullptr + ); + _to_desktop_image = gtk_image_new_from_pixbuf(to_desktop_pixbuf); + gtk_widget_set_halign(_to_desktop_image, GtkAlign::GTK_ALIGN_END); + g_object_unref(to_desktop_pixbuf); + + GdkPixbuf *title_only_pixbuf = gdk_pixbuf_new_from_file_at_size(notes->imageFile("title_only").c_str(), + width, height, nullptr + ); + _title_only_image = gtk_image_new_from_pixbuf(title_only_pixbuf); + gtk_widget_set_halign(_title_only_image, GtkAlign::GTK_ALIGN_END); + g_object_unref(title_only_pixbuf); + + gtk_container_add(_note_header, _color_image); gtk_container_add(_note_header, _title_label); gtk_container_add(_note_header, _plus_image); gtk_container_add(_note_header, _delete_image); gtk_container_add(_note_header, _hide_image); + gtk_container_add(_note_header, _to_desktop_image); + gtk_container_add(_note_header, _title_only_image); gtk_container_add(_scroll_widget, _text_widget); @@ -733,6 +1072,14 @@ YellowNote::YellowNote(YellowNotes *notes, const std::string &filename) gtk_widget_show_all(_note_widget); + /*while (!gtk_widget_is_visible(_note_widget)) { + GdkEvent *e = gtk_get_current_event(); + if (e != nullptr) { + gtk_main_do_event(e); + gdk_event_free(e); + } + }*/ + //GdkWindow *window = gdk_window_get_effective_toplevel(gtk_widget_get_window(_note_widget)); //gdk_window_set_events(window, static_cast(gdk_window_get_events(window) | GDK_VISIBILITY_NOTIFY_MASK)); //gdk_window_set_override_redirect(window, true); @@ -753,6 +1100,8 @@ YellowNote::YellowNote(YellowNotes *notes, const std::string &filename) g_object_ref(_delete_image); g_object_ref(_plus_image); g_object_ref(_hide_image); + g_object_ref(_to_desktop_image); + g_object_ref(_title_only_image); load(); @@ -766,6 +1115,8 @@ YellowNote::~YellowNote() g_object_unref(_delete_image); g_object_unref(_plus_image); g_object_unref(_hide_image); + g_object_unref(_to_desktop_image); + g_object_unref(_title_only_image); gtk_widget_destroy(_note_widget); _note_widget = nullptr; } @@ -782,6 +1133,7 @@ void YellowNote::updateWidgetColors(GtkWidget *w) }; set_style(w); + _notes->updateWidgetCss(w, _color); } void YellowNote::updateColor() @@ -841,6 +1193,19 @@ void YellowNote::updateTitle() gtk_label_set_label(_title_label, _title.c_str()); } +void YellowNote::updateTitleOnly() +{ + if (_title_only) { + gtk_widget_hide(_scroll_widget); + GtkAllocation alloc; + gtk_widget_get_allocation(_note_header, &alloc); + gtk_window_resize(_note_widget, _width, alloc.height); + } else { + gtk_widget_show_all(_scroll_widget); + gtk_window_resize(_note_widget, _width, _height); + } +} + void YellowNote::updateHidden() { if (_hidden) { hide(); } @@ -854,7 +1219,8 @@ void YellowNote::updatePosition() void YellowNote::updateSize() { - gtk_window_resize(_note_widget, _width, _height); + updateTitleOnly(); + //gtk_window_resize(_note_widget, _width, _height); } void YellowNote::showNote(GtkWidget *sender) @@ -862,6 +1228,15 @@ void YellowNote::showNote(GtkWidget *sender) show(); } +void YellowNote::setLoaded() +{ + _pos_loaded = true; + _hidden_loaded = true; + _title_only_loaded = true; + _size_loaded = true; + _in_transaction = false; +} + void YellowNote::show() { gtk_widget_show_all(_note_widget); @@ -876,63 +1251,39 @@ void YellowNote::hide() save(); } -void YellowNote::changed(GtkWidget *sender) +void YellowNote::toggleTitleOnly() { - bool sv = false; - - if (_color_changed) { - _color_changed = true; - sv = true; - } - - bool hidden = !gtk_widget_is_visible(_note_widget); - if (hidden != _hidden) { - _hidden = hidden; - sv = true; - } - - int w, h; - gtk_window_get_size(_note_widget, &w, &h); - if (w < 50) { w = 50; } - if (_width != w) { - _width = w; - sv = true; - } - - if (h < 50) { h = 50; } - if (_height != h) { - _height = h; - sv = true; - } - - int x, y; - gtk_window_get_position(_note_widget, &x, &y); - if (x <= 0) { x = 1; } - if (y <= 0) { y = 1; } - if (_x != x) { - _x = x; - sv = true; - } - if (_y != y) { - _y = y; - sv = true; - } - - const char *c_t = gtk_label_get_text (_title_label); - if (c_t != nullptr) { - std::string t(c_t); - if (t != _title) { - _title = t; - sv = true; - } - } - - if (sv) { save(); } + _title_only = !_title_only; + updateTitleOnly(); + save(); } -void YellowNote::size_allocated(GtkWidget *sender, GtkAllocation *a) +void YellowNote::resized(int width, int height) { - changed(sender); + _width = width; + _height = height; + save(); +} + +void YellowNote::size_allocated(GtkWidget *sender, GtkAllocation *alloc) +{ + int width = alloc->width; + int height = alloc->height; + if (!_moving) { + if (width != _width || height != _height) { + updateSize(); + //gtk_window_resize(_note_widget, _width, _height); + } + } +} + +void YellowNote::positioned(GtkWidget *sender, int x, int y) +{ + if (_pos_loaded) { + _x = x; + _y = y; + save(); + } } void YellowNote::toFront() @@ -941,11 +1292,29 @@ void YellowNote::toFront() gtk_window_move(_note_widget, _x, _y); gtk_widget_hide(_note_widget); } else { + int x = _x; + int y = _y; gtk_window_present(_note_widget); - gtk_window_move(_note_widget, _x, _y); + gtk_window_move(_note_widget, x, y); } } + +void YellowNote::toDesktop() +{ +#ifdef __linux + gtk_window_set_type_hint(_note_widget, GdkWindowTypeHint::GDK_WINDOW_TYPE_HINT_DESKTOP); +#endif +} + +void YellowNote::fromDesktop() +{ +#ifdef __linux + gtk_window_set_type_hint(_note_widget, GdkWindowTypeHint::GDK_WINDOW_TYPE_HINT_UTILITY); +#endif + toFront(); +} + void YellowNote::get_header_screen_coords(int &header_top, int &header_bottom) { int left, top; @@ -1000,6 +1369,11 @@ void YellowNote::adjustTitle(bool mutate) gtk_container_add(_note_header, _delete_image); gtk_container_remove(_note_header, _hide_image); gtk_container_add(_note_header, _hide_image); + gtk_container_remove(_note_header, _to_desktop_image); + gtk_container_add(_note_header, _to_desktop_image); + gtk_container_remove(_note_header, _title_only_image); + gtk_container_add(_note_header, _title_only_image); + } bool YellowNote::windowPresented(void *sender, GdkEventVisibility *evt) // TODO according to docs this must not be a pointer @@ -1021,10 +1395,8 @@ void YellowNote::textChanged(void *sender) { if (_in_transaction) return; - std::cout << "Changed " << _save_id << std::endl; _save_counter++; if (_save_id == -1) { - std::cout << "Starting save timer" << std::endl; _save_id = _save_counter; g_timeout_add(1000, on_text_save_timeout, this); } @@ -1033,13 +1405,11 @@ void YellowNote::textChanged(void *sender) bool YellowNote::textSaveTimeout() { if (_save_counter != _save_id) { - std::cout << "Something changed" << std::endl; _save_id = _save_counter; g_timeout_add(1000, on_text_save_timeout, this); return false; } else { _save_id = -1; - std::cout << "Saving" << std::endl; save(); return false; } @@ -1070,6 +1440,26 @@ bool YellowNote::titleFocusOut(GtkWidget *sender, GdkEventFocus *evt) #define AROUND(c, n) ((c >= (n - threshold)) && (c <= (n + threshold))) +gboolean is_dblclk(gpointer d) +{ + YellowNote *n = static_cast(d); + n->doubleClicked(); + return false; + +} + +void YellowNote::doubleClicked() +{ + if (!_double_clicked) { + _notes->notesFromDesktop(nullptr); + } +} + +YellowNotes *YellowNote::notes() +{ + return _notes; +} + bool YellowNote::move_begin(GtkWidget *sender, GdkEventButton *evt) { int x = evt->x_root; @@ -1093,6 +1483,12 @@ bool YellowNote::move_begin(GtkWidget *sender, GdkEventButton *evt) int hide_left, hide_right; get_screen_left_right(_hide_image, hide_left, hide_right); + int to_desktop_left, to_desktop_right; + get_screen_left_right(_to_desktop_image, to_desktop_left, to_desktop_right); + + int title_only_left, title_only_right; + get_screen_left_right(_title_only_image, title_only_left, title_only_right); + if (y >= header_top && y <= header_bottom) { if (x >= color_left && x <= color_right) { nextColor(); @@ -1106,6 +1502,14 @@ bool YellowNote::move_begin(GtkWidget *sender, GdkEventButton *evt) hide(); return true; } + if (x >= to_desktop_left && x <= to_desktop_right) { + _notes->notesToDesktop(this); + return true; + } + if (x >= title_only_left && x <= title_only_right) { + toggleTitleOnly(); + return true; + } if (x >= delete_left && x <= delete_right) { deleteMe(); return true; @@ -1121,17 +1525,24 @@ bool YellowNote::move_begin(GtkWidget *sender, GdkEventButton *evt) gtk_container_add(_note_header, _delete_image); gtk_container_remove(_note_header, _hide_image); gtk_container_add(_note_header, _hide_image); + gtk_container_remove(_note_header, _to_desktop_image); + gtk_container_add(_note_header, _to_desktop_image); + gtk_container_remove(_note_header, _title_only_image); + gtk_container_add(_note_header, _title_only_image); gtk_widget_show(_title_entry); gtk_entry_set_text(_title_entry, _title.c_str()); gtk_widget_grab_focus(_title_entry); _editing_title = true; + _double_clicked = true; return true; } + _double_clicked = false; if (y >= header_top && y <= header_bottom) { _moving = true; _x_orig = evt->x; _y_orig = evt->y; + g_timeout_add(250, is_dblclk, this); return true; } @@ -1173,10 +1584,11 @@ bool YellowNote::moving(GtkWidget *sender, GdkEventMotion *evt) int the_x = x - _x_orig; int the_y = y - _y_orig; gtk_window_move(_note_widget, the_x, the_y); + positioned(_note_widget, the_x, the_y); return true; } - if (_resize_bottom || _resize_right || _resize_edge) { + if (_resize_bottom || _resize_right || _resize_edge) { if (_resize_edge) { int left, top; gtk_window_get_position(_note_widget, &left, &top); @@ -1185,6 +1597,7 @@ bool YellowNote::moving(GtkWidget *sender, GdkEventMotion *evt) if (width < 100) { width = 100; } if (height < 60) { height = 60; } gtk_window_resize(_note_widget, width, height); + resized(width, height); return true; } @@ -1196,6 +1609,7 @@ bool YellowNote::moving(GtkWidget *sender, GdkEventMotion *evt) int width = x - left; if (width < 100) { width = 100; } gtk_window_resize(_note_widget, width, h); + resized(width, h); return true; } @@ -1207,12 +1621,13 @@ bool YellowNote::moving(GtkWidget *sender, GdkEventMotion *evt) int height = y - top; if (height < 60) { height = 60; } gtk_window_resize(_note_widget, w, height); + resized(w, height); return true; } } - int frame_bottom, frame_right; - get_frame_screen_coords(frame_bottom, frame_right); + int frame_bottom, frame_right; + get_frame_screen_coords(frame_bottom, frame_right); int header_top, header_bottom; get_header_screen_coords(header_top, header_bottom); @@ -1240,7 +1655,7 @@ bool YellowNote::moving(GtkWidget *sender, GdkEventMotion *evt) return false; } -#define YELLOWNOTE_VERSION 1 +#define YELLOWNOTE_VERSION 2 void YellowNote::load() { @@ -1258,47 +1673,74 @@ void YellowNote::load() std::filesystem::path p(_filename); + int hidden, x, y, width, height; + ColorType_t c; + std::string title; + bool title_only; + size_t s = 0; if (std::filesystem::is_regular_file(p)) { s = std::filesystem::file_size(p); + + FILE *f = fopen(_filename.c_str(), "rt"); + + if (f) { + int version = readInt(f, -1); + + hidden = readInt(f, -1); + x = readInt(f, 200); + y = readInt(f, 200); + width = readInt(f, 300); + height = readInt(f, 200); + + int color; + color = readInt(f, ColorType_t::YELLOW); + c = static_cast(color); + + if (version >= 2) { + int t = readInt(f, 0); + title_only = (t) ? true : false; + } + + char *buf = static_cast(malloc(s)); + memset(buf, 0, s); + + fgets(buf, s, f); + title = buf; + trim(title); + + memset(buf, 0, s); + int pos = ftell(f); + int bytes = s - pos; + fread(buf, bytes, 1, f); + + fclose(f); + gtk_text_buffer_set_text(_buffer, buf, strlen(buf)); + free(buf); + + _title = title; + updateTitle(); + + _color = c; + updateColor(); + + _x = x; _y = y; + _pos_loaded = true; + updatePosition(); + + _hidden = hidden; + _hidden_loaded = true; + updateHidden(); + + _title_only = title_only; + _title_only_loaded = true; + updateTitleOnly(); + + _width = width; _height = height; + _size_loaded = true; + updateSize(); + } } - FILE *f = fopen(_filename.c_str(), "rt"); - - if (f) { - int version = readInt(f, -1); - - _hidden = readInt(f, -1); - _x = readInt(f, 200); - _y = readInt(f, 200); - _width = readInt(f, 300); - _height = readInt(f, 200); - - int color; - color = readInt(f, ColorType_t::YELLOW); - _color = static_cast(color); - - char *buf = static_cast(malloc(s)); - memset(buf, 0, s); - - fgets(buf, s, f); - _title = buf; - trim(_title); - - memset(buf, 0, s); - int pos = ftell(f); - int bytes = s - pos; - fread(buf, bytes, 1, f); - - fclose(f); - gtk_text_buffer_set_text(_buffer, buf, strlen(buf)); - free(buf); - } - - updateTitle(); - updateColor(); - updatePosition(); - updateSize(); - updateHidden(); _in_transaction = false; } @@ -1319,6 +1761,7 @@ void YellowNote::save() fprintf(f, "%d\n", _width); fprintf(f, "%d\n", _height); fprintf(f, "%d\n", _color); + fprintf(f, "%d\n", _title_only); fprintf(f, "%s\n", _title.c_str()); GtkTextIter start, end; diff --git a/yellownotes.h b/yellownotes.h index 11d02d5..2ec54e1 100644 --- a/yellownotes.h +++ b/yellownotes.h @@ -10,6 +10,7 @@ extern "C" { } class YellowNote; +class SettingContainer; typedef enum { DARK = 0, @@ -32,6 +33,8 @@ private: void *_dlg; void *_langs; + std::list _settings_containers; + std::list _notes; int _font_size; GtkWindow *_toplevel; @@ -53,11 +56,15 @@ public: int fontSize(); int iconSize(); GtkWindow *topLevel(); + void colorSet(void *sender); + void setFontSize(int size); public: void popupTrayMenu(void *sender); void newNote(void *sender); void showNotes(void *sender); + void notesToDesktop(void *sender); + void notesFromDesktop(void *sender); void reloadNotes(void *sender); void quit(void *sender); void topLevelHidden(GtkWidget *sender); @@ -70,6 +77,15 @@ public: public: std::string currentLang(); void setCurrentLang(const std::string &l); + bool cfgOnDesktop(); + void setCfgOnDesktop(bool y); + std::string getFgColor(ColorType_t type); + std::string getBgColor(ColorType_t type); + void setFgColor(ColorType_t type, const std::string &col); + void setBgColor(ColorType_t type, const std::string &col); + std::string fromRGBA(const GdkRGBA &rgba); + void toRGBA(const std::string &col, GdkRGBA &rgba); + void updateWidgetCss(GtkWidget *w, ColorType_t col); public: YellowNotes(void *app);