2120 lines
59 KiB
C++
2120 lines
59 KiB
C++
#include "yellownotes.h"
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <filesystem>
|
|
#include <vector>
|
|
|
|
#if defined(__linux) || defined(TARGET_OS_OSX)
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#include <shobjidl.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
extern "C" {
|
|
#include "gtk-imports.h"
|
|
}
|
|
|
|
#include "tr.h"
|
|
#include "info_over_me.h"
|
|
#include <functional>
|
|
#include <sstream>
|
|
#include <math.h>
|
|
|
|
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");
|
|
|
|
if (start == std::string::npos)
|
|
s.clear(); // String contains only whitespace
|
|
else
|
|
s = s.substr(start, end - start + 1);
|
|
}
|
|
|
|
class StoredCoords
|
|
{
|
|
public:
|
|
bool hidden;
|
|
int x;
|
|
int y;
|
|
int width;
|
|
int height;
|
|
std::string os_host;
|
|
};
|
|
|
|
typedef enum {
|
|
NONE,
|
|
BOTTOM_RIGHT,
|
|
BOTTOM,
|
|
RIGHT
|
|
} YellowNoteResize_t;
|
|
|
|
|
|
|
|
class YellowNote
|
|
{
|
|
private:
|
|
YellowNotes *_notes;
|
|
|
|
GtkWindow *_note_widget;
|
|
|
|
GtkWidget *_evt_box;
|
|
GtkWidget *_note_box;
|
|
GtkWidget *_frame;
|
|
|
|
GtkWidget *_note_header;
|
|
GtkImage *_color_image;
|
|
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;
|
|
|
|
GtkWidget *_scroll_widget;
|
|
GtkWidget *_text_widget;
|
|
GtkTextBuffer *_buffer;
|
|
|
|
std::string _filename;
|
|
std::string _note;
|
|
std::string _title;
|
|
|
|
std::list<StoredCoords> _stored_coords;
|
|
|
|
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;
|
|
|
|
bool _moving;
|
|
bool _resize_right;
|
|
bool _resize_bottom;
|
|
bool _resize_edge;
|
|
YellowNoteResize_t _resize_type;
|
|
|
|
int _x_orig;
|
|
int _y_orig;
|
|
|
|
public:
|
|
void show();
|
|
void hide();
|
|
|
|
private:
|
|
void updateTitle();
|
|
void updatePosition();
|
|
void updateHidden();
|
|
void updateSize();
|
|
void updateTitleOnly();
|
|
|
|
void adjustTitle(bool mutate);
|
|
|
|
void get_header_screen_coords(int &header_top, int &header_bottom);
|
|
void get_frame_screen_coords(int &frame_bottom, int &frame_right);
|
|
void get_screen_left_right(GtkWidget *widget, int &left, int &right);
|
|
|
|
void nextColor();
|
|
void deleteMe();
|
|
void addNew();
|
|
|
|
void updateWidgetColors(GtkWidget *w);
|
|
|
|
public:
|
|
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);
|
|
bool move_end(GtkWidget *sender, GdkEventButton *evt);
|
|
bool moving(GtkWidget *sender, GdkEventMotion *evt);
|
|
bool titleEscape(GtkWidget *sender, GdkEventKey *key);
|
|
void titleEnter(GtkWidget *sender);
|
|
bool titleFocusOut(GtkWidget *sender, GdkEventFocus *evt);
|
|
void textChanged(void *sender);
|
|
bool textSaveTimeout();
|
|
bool windowPresented(void *sender, GdkEventVisibility *evt);
|
|
void showNote(GtkWidget *sender);
|
|
void moveTo(int x, int y);
|
|
|
|
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);
|
|
BSIGNAL2(YellowNote, on_mouse_move, moving, GdkEventMotion);
|
|
SIGNAL(YellowNote, on_title_activate, titleEnter);
|
|
BSIGNAL2(YellowNote, on_title_escape, titleEscape, GdkEventKey);
|
|
BSIGNAL2(YellowNote, on_title_focus_out, titleFocusOut, GdkEventFocus);
|
|
BSIGNAL2(YellowNote, on_window_present, windowPresented, GdkEventVisibility);
|
|
SIGNAL(YellowNote, on_text_change, textChanged);
|
|
SIGNAL(YellowNote, on_show_note, showNote);
|
|
|
|
|
|
static gboolean on_text_save_timeout(gpointer data)
|
|
{
|
|
YellowNote *n = reinterpret_cast<YellowNote *>(data);
|
|
return n->textSaveTimeout();
|
|
}
|
|
|
|
SIGNAL(YellowNotes, on_new_yellow, newNote)
|
|
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)
|
|
SIGNAL(YellowNotes, on_reorder, reOrder);
|
|
SIGNAL(YellowNotes, on_about, about);
|
|
SIGNAL(YellowNotes, on_hide_toplevel, topLevelHidden)
|
|
SIGNAL(YellowNotes, on_setup_ok, setupClose);
|
|
SIGNAL(YellowNotes, on_setup_close, setupCancel);
|
|
BSIGNAL2(YellowNotes, on_setup_del, setupDel, void);
|
|
SIGNAL(YellowNotes, on_monitors_changed, monitorsChanged);
|
|
|
|
|
|
std::string YellowNotes::imageFile(const char *name)
|
|
{
|
|
#if defined(_WIN32) || defined(TARGET_OS_OSX)
|
|
std::string ext = ".png";
|
|
#else
|
|
std::string ext = ".svg";
|
|
#endif
|
|
|
|
std::string file = name + ext;
|
|
std::string path1 = appDir() + "/images/" + file;
|
|
if (std::filesystem::is_regular_file(path1)) {
|
|
return path1;
|
|
} else {
|
|
std::string path2 = appDir() + "/../../" + file;
|
|
if (std::filesystem::is_regular_file(path2)) {
|
|
return path2;
|
|
} else {
|
|
std::cerr << "No such image file: " << path2 << std::endl;
|
|
throw std::string("No such image file: " + file);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string YellowNotes::appDir()
|
|
{
|
|
InfoOverMe info;
|
|
std::string base = info.containingFolder();
|
|
return base;
|
|
}
|
|
|
|
std::string YellowNotes::notesDir()
|
|
{
|
|
#ifdef __linux
|
|
struct passwd *pw = getpwuid(getuid());
|
|
const char *homedir = pw->pw_dir;
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
char homedir[10240];
|
|
snprintf(homedir, 10240, "%s%s", getenv("HOMEDRIVE"), getenv("HOMEPATH"));
|
|
#endif
|
|
|
|
#ifdef TARGET_OS_OSX
|
|
struct passwd *pw = getpwuid(getuid());
|
|
const char *homedir = pw->pw_dir;
|
|
#endif
|
|
|
|
std::string home_dir = homedir;
|
|
std::string notes_dir = home_dir + "/yellownotes";
|
|
|
|
std::filesystem::path nd(notes_dir);
|
|
if (!std::filesystem::is_directory(nd)) {
|
|
std::filesystem::create_directory(nd);
|
|
}
|
|
|
|
return notes_dir;
|
|
}
|
|
|
|
std::string YellowNotes::fromRGBA(const GdkRGBA &rgba)
|
|
{
|
|
char buf[100];
|
|
|
|
auto to255 = [](double c) {
|
|
return static_cast<unsigned int>(floor((c * 255) + 0.5));
|
|
};
|
|
|
|
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);
|
|
}
|
|
|
|
GtkWidget *YellowNotes::getWindow()
|
|
{
|
|
return topLevel();
|
|
}
|
|
|
|
std::string YellowNotes::getFgColor(ColorType_t type)
|
|
{
|
|
char buf[100];
|
|
sprintf(buf, "fg_color_%d", type);
|
|
std::string key = buf;
|
|
std::unordered_map<std::string, std::string>::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<std::string, std::string>::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<std::string, std::string>(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<std::string, std::string>(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);
|
|
};
|
|
|
|
auto darker = [from_hex](std::string color) {
|
|
int red = from_hex(color.substr(1, 2));
|
|
int green = from_hex(color.substr(3, 2));
|
|
int blue = from_hex(color.substr(5, 2));
|
|
float factor = 0.9;
|
|
red *= factor;
|
|
green *= factor;
|
|
blue *= factor;
|
|
char buf[20];
|
|
sprintf(buf, "#%02x%02x%02x", red, green, blue);
|
|
std::string s = buf;
|
|
return s;
|
|
};
|
|
|
|
char font_size[20];
|
|
sprintf(font_size, "%dpx", _font_size);
|
|
|
|
std::string css = std::string() +
|
|
"label, box.horizontal, textview.view, textview.view text, frame, messagedialog.background {\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(getBgColor(type)) + ";\n"
|
|
" margin: 5px;\n"
|
|
"}\n";
|
|
|
|
return css;
|
|
}
|
|
|
|
int YellowNotes::fontSize()
|
|
{
|
|
std::string key("font_size");
|
|
std::unordered_map<std::string, std::string>::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<std::string, std::string>(key, v));
|
|
saveConfig();
|
|
}
|
|
|
|
int YellowNotes::iconSize()
|
|
{
|
|
return _font_size * 1.5;
|
|
}
|
|
|
|
bool YellowNotes::popupTrayMenuBtn(void *sender, GdkEventButton *evt)
|
|
{
|
|
popupTrayMenu(sender);
|
|
return true;
|
|
}
|
|
|
|
void YellowNotes::popupTrayMenu(void *sender)
|
|
{
|
|
GtkMenu *tray_menu = reinterpret_cast<GtkMenu *>(_tray_menu);
|
|
|
|
if (tray_menu) { gtk_widget_destroy(tray_menu); }
|
|
|
|
tray_menu = gtk_menu_new();
|
|
_tray_menu = reinterpret_cast<void *>(tray_menu);
|
|
|
|
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();
|
|
|
|
std::list<GtkWidget *> hidden;
|
|
std::list<YellowNote *> h_notes;
|
|
std::list<YellowNote *>::iterator it = _notes.begin();
|
|
while (it != _notes.end()) {
|
|
YellowNote *n = *it;
|
|
GtkWidget *m = gtk_menu_item_new_with_label(n->title().c_str());
|
|
if (n->isHidden()) {
|
|
hidden.push_back(m);
|
|
h_notes.push_back(n);
|
|
}
|
|
it++;
|
|
}
|
|
|
|
GtkWidget *sep1 = gtk_separator_menu_item_new();
|
|
|
|
GtkMenuItem *setup = gtk_menu_item_new_with_label(_("Setup"));
|
|
GtkMenuItem *reorder = gtk_menu_item_new_with_label(_("Reorder Notes"));
|
|
GtkMenuItem *about = gtk_menu_item_new_with_label(_("About"));
|
|
GtkMenuItem *quit = gtk_menu_item_new_with_label(_("Quit"));
|
|
|
|
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);
|
|
|
|
std::list<GtkWidget *>::iterator w_it = hidden.begin();
|
|
while (w_it != hidden.end()) {
|
|
gtk_menu_shell_append(tray_menu, *w_it);
|
|
w_it++;
|
|
}
|
|
|
|
gtk_menu_shell_append(tray_menu, sep1);
|
|
gtk_menu_shell_append(tray_menu, setup);
|
|
gtk_menu_shell_append(tray_menu, reorder);
|
|
gtk_menu_shell_append(tray_menu, about);
|
|
gtk_menu_shell_append(tray_menu, quit);
|
|
gtk_widget_show_all(tray_menu);
|
|
|
|
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(reorder, "activate", on_reorder, this);
|
|
g_signal_connect(about, "activate", on_about, this);
|
|
g_signal_connect(quit, "activate", on_quit, this);
|
|
w_it = hidden.begin();
|
|
it = h_notes.begin();
|
|
while(w_it != hidden.end()) {
|
|
g_signal_connect(*w_it, "activate", on_show_note, *it);
|
|
w_it++;
|
|
it++;
|
|
}
|
|
|
|
#ifdef __linux
|
|
gtk_menu_popup_at_pointer(tray_menu, nullptr);
|
|
#endif
|
|
#ifdef _WIN32
|
|
gtk_menu_popup(tray_menu, nullptr, nullptr, nullptr, nullptr, 0, 0);
|
|
#endif
|
|
}
|
|
|
|
void YellowNotes::clearNotes()
|
|
{
|
|
while (!_notes.empty()) {
|
|
YellowNote *note = _notes.front();
|
|
_notes.pop_front();
|
|
delete note;
|
|
}
|
|
}
|
|
|
|
void YellowNotes::loadConfig()
|
|
{
|
|
std::string notes_dir = notesDir();
|
|
std::string cfg_file = notes_dir + "/yellownotes.cfg";
|
|
std::filesystem::path p(cfg_file);
|
|
|
|
auto add = [this](const char *k, const char *v) {
|
|
_cfg.insert(std::pair<std::string, std::string>(std::string(k), std::string(v)));
|
|
};
|
|
|
|
_cfg.clear();
|
|
add("lang", "en");
|
|
|
|
if (std::filesystem::is_regular_file(p)) {
|
|
FILE *f = fopen(cfg_file.c_str(), "rt");
|
|
char buf[10240];
|
|
char *b = fgets(buf, 10240, f);
|
|
while (b != nullptr) {
|
|
std::string e = std::string(b);
|
|
trim(e);
|
|
if (e != "") {
|
|
size_t pos = e.find("=");
|
|
std::string k = e.substr(0, pos);
|
|
std::string v = e.substr(pos + 1);
|
|
trim(k);
|
|
trim(v);
|
|
_cfg.erase(k);
|
|
_cfg.insert(std::pair<std::string, std::string>(k, v));
|
|
}
|
|
b = fgets(buf, 10240, f);
|
|
}
|
|
fclose (f);
|
|
}
|
|
|
|
setLang(currentLang());
|
|
}
|
|
|
|
void YellowNotes::saveConfig()
|
|
{
|
|
std::string notes_dir = notesDir();
|
|
std::string cfg_file = notes_dir + "/yellownotes.cfg";
|
|
|
|
FILE *f = fopen(cfg_file.c_str(), "wt");
|
|
if (f) {
|
|
std::unordered_map<std::string, std::string>::iterator it;
|
|
it = _cfg.begin();
|
|
while (it != _cfg.end()) {
|
|
fprintf(f, "%s=%s\n", it->first.c_str(), it->second.c_str());
|
|
it++;
|
|
}
|
|
fclose(f);
|
|
}
|
|
}
|
|
|
|
gboolean load_notes_timeout(void *_notes)
|
|
{
|
|
YellowNotes *notes = static_cast<YellowNotes *>(_notes);
|
|
notes->notesToDesktop(nullptr);
|
|
return false;
|
|
}
|
|
|
|
void YellowNotes::loadNotes()
|
|
{
|
|
gtk_widget_show_all(topLevel());
|
|
|
|
clearNotes();
|
|
|
|
std::string notes_dir = notesDir();
|
|
|
|
std::filesystem::path folder(notes_dir);
|
|
|
|
if(!std::filesystem::is_directory(folder))
|
|
{
|
|
throw std::runtime_error(folder.string() + " is not a folder");
|
|
}
|
|
|
|
std::vector<std::string> file_list;
|
|
for (const auto& entry : std::filesystem::directory_iterator(folder))
|
|
{
|
|
const auto full_name = entry.path().string();
|
|
|
|
if (entry.is_regular_file() && full_name.rfind(".note") != std::string::npos)
|
|
{
|
|
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)
|
|
{
|
|
std::list<YellowNote *>::const_iterator it = _notes.begin();
|
|
while (it != _notes.end()) {
|
|
YellowNote *note = *it;
|
|
note->toFront();
|
|
it++;
|
|
}
|
|
}
|
|
|
|
void YellowNotes::notesToDesktop(void *sender)
|
|
{
|
|
std::list<YellowNote *>::const_iterator it = _notes.begin();
|
|
while (it != _notes.end()) {
|
|
YellowNote *note = *it;
|
|
note->toDesktop();
|
|
it++;
|
|
}
|
|
setCfgOnDesktop(true);
|
|
}
|
|
|
|
void YellowNotes::notesFromDesktop(void *sender)
|
|
{ std::list<YellowNote *>::const_iterator it = _notes.begin();
|
|
while (it != _notes.end()) {
|
|
YellowNote *note = *it;
|
|
note->fromDesktop();
|
|
it++;
|
|
}
|
|
setCfgOnDesktop(false);
|
|
}
|
|
|
|
void YellowNotes::setupCancel(GtkWidget *sender)
|
|
{
|
|
if (_dlg) {
|
|
gtk_widget_destroy(_dlg);
|
|
_dlg = nullptr;
|
|
_langs = nullptr;
|
|
}
|
|
}
|
|
|
|
bool YellowNotes::setupDel(GtkWidget *sender, void *evt)
|
|
{
|
|
setupCancel(sender);
|
|
return true;
|
|
}
|
|
|
|
void YellowNotes::setupClose(GtkWidget *sender)
|
|
{
|
|
if (_dlg != nullptr) {
|
|
gtk_dialog_response(_dlg, GTK_RESPONSE_OK);
|
|
std::string lang = std::string(gtk_combo_box_get_active_id(_langs));
|
|
setCurrentLang(lang);
|
|
gtk_widget_destroy(_dlg);
|
|
|
|
std::list<SettingContainer *>::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<YellowNote *>::iterator n_it;
|
|
for(n_it = _notes.begin(); n_it != _notes.end(); n_it++) {
|
|
YellowNote *n = *n_it;
|
|
n->updateColor();
|
|
}
|
|
|
|
} else {
|
|
}
|
|
}
|
|
|
|
void YellowNotes::setup(void *sender)
|
|
{
|
|
loadConfig();
|
|
|
|
GtkWidget *dlg = gtk_dialog_new();
|
|
gtk_window_set_title(dlg, _("Yellownotes Setup"));
|
|
_dlg = dlg;
|
|
|
|
GtkWidget *ok_btn = gtk_dialog_add_button(dlg, _("Ok"), GTK_RESPONSE_OK);
|
|
GtkWidget *content = gtk_dialog_get_content_area(dlg);
|
|
|
|
GtkWidget *langs = gtk_combo_box_text_new();
|
|
_langs = langs;
|
|
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);
|
|
|
|
GtkGrid *grid = gtk_grid_new();
|
|
|
|
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<ColorType_t>(i));
|
|
std::string bg_color = getBgColor(static_cast<ColorType_t>(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<ColorType_t>(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<ColorType_t>(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);
|
|
g_signal_connect(dlg, "delete-event", on_setup_del, this);
|
|
|
|
gtk_widget_show_all(dlg);
|
|
}
|
|
|
|
void YellowNotes::about(void *sender)
|
|
{
|
|
const char *authors[] = { "Hans Dijkema", nullptr };
|
|
|
|
GtkWidget *dlg = gtk_about_dialog_new();
|
|
gtk_about_dialog_set_authors(dlg, authors);
|
|
gtk_about_dialog_set_copyright(dlg, _("(c) 2025"));
|
|
gtk_about_dialog_set_license_type(dlg, GTK_LICENSE_GPL_2_0);
|
|
|
|
char buf[200];
|
|
sprintf(buf, _("Version %s, file format version: %d"),
|
|
YELLOWNOTE_VERSION,
|
|
YELLOWNOTE_FILE_VERSION
|
|
);
|
|
gtk_about_dialog_set_version(dlg, buf);
|
|
|
|
int width = 100;
|
|
int height = 100;
|
|
GdkPixbuf *logo_pixbuf = gdk_pixbuf_new_from_file_at_size(imageFile("yellownotes").c_str(),
|
|
width, height, nullptr
|
|
);
|
|
gtk_about_dialog_set_logo(dlg, logo_pixbuf);
|
|
g_object_unref(logo_pixbuf);
|
|
|
|
gtk_about_dialog_set_program_name(dlg, _("Yellow Notes"));
|
|
|
|
gtk_dialog_run(dlg);
|
|
|
|
gtk_widget_destroy(dlg);
|
|
}
|
|
|
|
void YellowNotes::reOrder(void *sender)
|
|
{
|
|
int x = 50;
|
|
int y = 50;
|
|
|
|
std::list<YellowNote *>::iterator it;
|
|
for(it = _notes.begin(); it != _notes.end(); it++, x += 25, y += 25) {
|
|
(*it)->moveTo(x, y);
|
|
}
|
|
}
|
|
|
|
void YellowNotes::monitorsChanged(void *sender)
|
|
{
|
|
this->reloadNotes(sender);
|
|
}
|
|
|
|
void YellowNotes::reloadNotes(void *sender)
|
|
{
|
|
std::list<YellowNote *>::const_iterator it = _notes.begin();
|
|
while (it != _notes.end()) {
|
|
YellowNote *note = *it;
|
|
delete note;
|
|
it++;
|
|
}
|
|
|
|
_notes.clear();
|
|
loadNotes();
|
|
|
|
showNotes(sender);
|
|
}
|
|
|
|
|
|
void YellowNotes::newNote(void *sender)
|
|
{
|
|
unsigned long long milliseconds_since_epoch =
|
|
std::chrono::system_clock::now().time_since_epoch() /
|
|
std::chrono::milliseconds(1);
|
|
|
|
char buf[200];
|
|
int r = rand() % 1000;
|
|
sprintf(buf, "%d-%llu", r, milliseconds_since_epoch);
|
|
|
|
std::string new_note_file = buf;
|
|
std::string notes_dir = notesDir();
|
|
|
|
auto filename = [notes_dir](const std::string & fname, int i) {
|
|
char s[20];
|
|
sprintf(s, "%d", i);
|
|
std::string fn = notes_dir + "/" + fname + "-" + s + ".note";
|
|
return fn;
|
|
};
|
|
|
|
int i = 1;
|
|
std::filesystem::path p(filename(new_note_file, i));
|
|
while(std::filesystem::is_regular_file(p)) {
|
|
i += 1;
|
|
p = std::filesystem::path(filename(new_note_file, i));
|
|
}
|
|
|
|
YellowNote *note = new YellowNote(this, p.string());
|
|
_notes.push_back(note);
|
|
note->setLoaded();
|
|
note->show();
|
|
note->save();
|
|
}
|
|
|
|
void YellowNotes::quit(void *sender)
|
|
{
|
|
gtk_main_quit();
|
|
}
|
|
|
|
void YellowNotes::remove(YellowNote *n)
|
|
{
|
|
_notes.remove(n);
|
|
}
|
|
|
|
std::string YellowNotes::currentLang()
|
|
{
|
|
std::string key("lang");
|
|
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<std::string, std::string>(key, v));
|
|
saveConfig();
|
|
}
|
|
|
|
void YellowNotes::setCurrentLang(const std::string &l)
|
|
{
|
|
std::string key("lang");
|
|
_cfg.erase(key);
|
|
_cfg.insert(std::pair<std::string, std::string>(key, l));
|
|
}
|
|
|
|
static int show_notes_timed(void *user_data)
|
|
{
|
|
YellowNotes *notes = reinterpret_cast<YellowNotes *>(user_data);
|
|
notes->showNotes(nullptr);
|
|
return false;
|
|
}
|
|
|
|
void YellowNotes::topLevelHidden(GtkWidget *sender)
|
|
{
|
|
g_timeout_add(500, show_notes_timed, this);
|
|
}
|
|
|
|
void YellowNotes::checkMonitors()
|
|
{
|
|
auto compare = [](const Geom_t &a, const Geom_t &b) {
|
|
if (a.x < b.x) {
|
|
return true;
|
|
} else if (a.x > b. x) {
|
|
return false;
|
|
} else if (a.y < b.y) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
GdkDisplay *d = gdk_display_get_default();
|
|
int n = gdk_display_get_n_monitors(d);
|
|
|
|
if (_monitors.empty()) {
|
|
int i;
|
|
for(i = 0; i < n; i++) {
|
|
GdkMonitor *m = gdk_display_get_monitor(d, i);
|
|
GdkRectangle r;
|
|
gdk_monitor_get_geometry(m, &r);
|
|
Geom_t g;
|
|
g.x = r.x;
|
|
g.y = r.y;
|
|
g.width = r.width;
|
|
g.height = r.height;
|
|
_monitors.push_back(g);
|
|
}
|
|
_monitors.sort(compare);
|
|
} else {
|
|
if (n != _monitors.size()) {
|
|
_monitors.clear();
|
|
checkMonitors();
|
|
monitorsChanged(nullptr);
|
|
} else {
|
|
std::list<Geom_t> l;
|
|
int i;
|
|
for(i = 0; i < n; i++) {
|
|
GdkMonitor *m = gdk_display_get_monitor(d, i);
|
|
GdkRectangle r;
|
|
gdk_monitor_get_geometry(m, &r);
|
|
Geom_t g;
|
|
g.x = r.x;
|
|
g.y = r.y;
|
|
g.width = r.width;
|
|
g.height = r.height;
|
|
l.push_back(g);
|
|
}
|
|
l.sort(compare);
|
|
std::list<Geom_t>::iterator m_it = _monitors.begin();
|
|
std::list<Geom_t>::iterator l_it = l.begin();
|
|
while(l_it != l.end()) {
|
|
Geom_t g_l = *l_it;
|
|
Geom_t g_m = *m_it;
|
|
if (g_l.x != g_m.x || g_l.y != g_m.y || g_l.width != g_m.width || g_l.height != g_m.height) {
|
|
_monitors.clear();
|
|
checkMonitors();
|
|
monitorsChanged(nullptr);
|
|
}
|
|
l_it++;
|
|
m_it++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static YellowNotes *_the_notes = nullptr;
|
|
|
|
gboolean monitor_monitors(void *data)
|
|
{
|
|
YellowNotes *n = reinterpret_cast<YellowNotes *>(data);
|
|
if (_the_notes == nullptr) {
|
|
return false;
|
|
} else {
|
|
n->checkMonitors();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
GtkWindow *YellowNotes::topLevel()
|
|
{
|
|
if (_toplevel == nullptr) {
|
|
_toplevel = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_widget_show_all(_toplevel);
|
|
g_signal_connect(_toplevel, "hide", on_hide_toplevel, this);
|
|
}
|
|
return _toplevel;
|
|
}
|
|
|
|
YellowNotes::YellowNotes(void *app)
|
|
{
|
|
_app = app;
|
|
_tray_menu = nullptr;
|
|
_font_size = 15;
|
|
_toplevel = nullptr;
|
|
_the_notes = this;
|
|
g_timeout_add(1000, monitor_monitors, this); // Beware monitor_monitors checks _the_notes.
|
|
loadConfig();
|
|
}
|
|
|
|
YellowNotes::~YellowNotes()
|
|
{
|
|
_the_notes = nullptr;
|
|
if (_tray_menu) { gtk_widget_destroy(reinterpret_cast<GtkMenu *>(_tray_menu)); }
|
|
clearNotes();
|
|
gtk_widget_destroy(_toplevel);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
/// YellowNote implementation
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
YellowNote::YellowNote(YellowNotes *notes, const std::string &filename)
|
|
{
|
|
_notes = notes;
|
|
_filename = filename;
|
|
|
|
_title = _("New Note");
|
|
_x = 200;
|
|
_y = 300;
|
|
_width = 300;
|
|
_height = 200;
|
|
_hidden = false;
|
|
_title_only = false;
|
|
_editing_title = false;
|
|
_save_counter = 0;
|
|
_save_id = -1;
|
|
|
|
_moving = false;
|
|
_resize_bottom = false;
|
|
_resize_edge = false;
|
|
_resize_right = false;
|
|
_resize_type = NONE;
|
|
|
|
_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);
|
|
|
|
_buffer = gtk_text_buffer_new(nullptr);
|
|
_text_widget = gtk_text_view_new_with_buffer(_buffer);
|
|
gtk_text_view_set_wrap_mode(_text_widget, GTK_WRAP_WORD);
|
|
|
|
_evt_box = gtk_event_box_new();
|
|
|
|
_note_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
|
|
|
|
_note_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
|
|
|
|
_title_label = gtk_label_new(_title.c_str());
|
|
gtk_label_set_ellipsize(_title_label, PANGO_ELLIPSIZE_END);
|
|
gtk_widget_set_hexpand(_title_label, true);
|
|
|
|
_title_entry = gtk_entry_new();
|
|
gtk_widget_set_hexpand(_title_entry, true);
|
|
|
|
_title_separator = gtk_separator_new(GTK_ORIENTATION_HORIZONTAL);
|
|
|
|
_frame = gtk_frame_new(nullptr);
|
|
gtk_frame_set_shadow_type(_frame, GtkShadowType::GTK_SHADOW_OUT);
|
|
|
|
int height = _notes->iconSize();
|
|
int width = height;
|
|
|
|
GdkPixbuf *color_pixbuf = gdk_pixbuf_new_from_file_at_size(notes->imageFile("color").c_str(),
|
|
width, height, nullptr
|
|
);
|
|
_color_image = gtk_image_new_from_pixbuf(color_pixbuf);
|
|
g_object_unref(color_pixbuf);
|
|
|
|
GdkPixbuf *delete_pixbuf = gdk_pixbuf_new_from_file_at_size(notes->imageFile("delete").c_str(),
|
|
width, height, nullptr
|
|
);
|
|
_delete_image = gtk_image_new_from_pixbuf(delete_pixbuf);
|
|
gtk_widget_set_halign(_delete_image, GtkAlign::GTK_ALIGN_END);
|
|
g_object_unref(delete_pixbuf);
|
|
|
|
GdkPixbuf *plus_pixbuf = gdk_pixbuf_new_from_file_at_size(notes->imageFile("plus").c_str(),
|
|
width, height, nullptr
|
|
);
|
|
_plus_image = gtk_image_new_from_pixbuf(plus_pixbuf);
|
|
gtk_widget_set_halign(_plus_image, GtkAlign::GTK_ALIGN_END);
|
|
g_object_unref(plus_pixbuf);
|
|
|
|
GdkPixbuf *hide_pixbuf = gdk_pixbuf_new_from_file_at_size(notes->imageFile("hide").c_str(),
|
|
width, height, nullptr
|
|
);
|
|
_hide_image = gtk_image_new_from_pixbuf(hide_pixbuf);
|
|
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);
|
|
|
|
gtk_container_add(_note_box, _note_header);
|
|
gtk_container_add(_note_box, _title_separator);
|
|
gtk_container_add(_note_box, _scroll_widget);
|
|
|
|
gtk_container_add(_frame, _note_box);
|
|
gtk_container_add(_evt_box, _frame);
|
|
gtk_container_add(_note_widget, _evt_box);
|
|
|
|
gtk_container_set_border_width(_evt_box, 0);
|
|
|
|
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<GdkEventMask>(gdk_window_get_events(window) | GDK_VISIBILITY_NOTIFY_MASK));
|
|
//gdk_window_set_override_redirect(window, true);
|
|
|
|
g_signal_connect(_note_widget, "size_allocate", on_size_allocated, this);
|
|
g_signal_connect(_evt_box, "button_press_event", on_button_press, this);
|
|
g_signal_connect(_evt_box, "button_release_event", on_button_release, this);
|
|
g_signal_connect(_evt_box, "motion_notify_event", on_mouse_move, this);
|
|
g_signal_connect(_title_entry, "activate", on_title_activate, this);
|
|
g_signal_connect(_title_entry, "key_press_event", on_title_escape, this);
|
|
g_signal_connect(_title_entry, "focus_out_event", on_title_focus_out, this);
|
|
g_signal_connect(_buffer, "changed", on_text_change, this);
|
|
g_signal_connect(_note_widget, "visibility_notify_event", on_window_present, this);
|
|
|
|
// Keep label and entry of title alive
|
|
g_object_ref(_title_label);
|
|
g_object_ref(_title_entry);
|
|
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();
|
|
|
|
_in_transaction = false;
|
|
}
|
|
|
|
YellowNote::~YellowNote()
|
|
{
|
|
g_object_unref(_title_label);
|
|
g_object_unref(_title_entry);
|
|
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;
|
|
}
|
|
|
|
void YellowNote::updateWidgetColors(GtkWidget *w)
|
|
{
|
|
auto set_style = [this](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 = gtk_css_provider_new();
|
|
gtk_style_context_add_provider(c, css, GTK_STYLE_PROVIDER_PRIORITY_USER);
|
|
std::string widget_css = _notes->css(_color);
|
|
gtk_css_provider_load_from_data(css, widget_css.c_str(), widget_css.size(), nullptr);
|
|
};
|
|
|
|
set_style(w);
|
|
_notes->updateWidgetCss(w, _color);
|
|
}
|
|
|
|
void YellowNote::updateColor()
|
|
{
|
|
|
|
auto set_style = [this](GtkWidget *w) {
|
|
this->updateWidgetColors(w);
|
|
};
|
|
|
|
set_style(_title_label);
|
|
set_style(_note_header);
|
|
set_style(_text_widget);
|
|
set_style(_note_widget);
|
|
set_style(_frame);
|
|
}
|
|
|
|
void YellowNote::nextColor()
|
|
{
|
|
int c = _color;
|
|
c += 1;
|
|
_color = static_cast<ColorType_t>(c);
|
|
if (_color > ColorType_t::LAST) {
|
|
_color = ColorType_t::FIRST;
|
|
}
|
|
save();
|
|
updateColor();
|
|
}
|
|
|
|
void YellowNote::addNew()
|
|
{
|
|
_notes->newNote(this);
|
|
}
|
|
|
|
void YellowNote::deleteMe()
|
|
{
|
|
GtkWidget *msg = gtk_message_dialog_new(_note_widget,
|
|
GtkDialogFlags::GTK_DIALOG_MODAL,
|
|
GtkMessageType::GTK_MESSAGE_QUESTION,
|
|
GtkButtonsType::GTK_BUTTONS_YES_NO,
|
|
"%s", "Are you sure you want to delete this note?"
|
|
);
|
|
updateWidgetColors(msg);
|
|
|
|
int response = gtk_dialog_run(msg);
|
|
gtk_widget_destroy (msg);
|
|
|
|
if (response == GTK_RESPONSE_YES) {
|
|
_notes->remove(this);
|
|
std::filesystem::path p(_filename);
|
|
std::filesystem::remove(p);
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
void YellowNote::updateTitle()
|
|
{
|
|
gtk_label_set_label(_title_label, _title.c_str());
|
|
gtk_window_set_title(_note_widget, _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(); }
|
|
else { show(); }
|
|
}
|
|
|
|
void YellowNote::updatePosition()
|
|
{
|
|
gtk_window_move(_note_widget, _x, _y);
|
|
}
|
|
|
|
void YellowNote::updateSize()
|
|
{
|
|
updateTitleOnly();
|
|
//gtk_window_resize(_note_widget, _width, _height);
|
|
}
|
|
|
|
void YellowNote::showNote(GtkWidget *sender)
|
|
{
|
|
show();
|
|
}
|
|
|
|
void YellowNote::moveTo(int x, int y)
|
|
{
|
|
_x = x;
|
|
_y = y;
|
|
updatePosition();
|
|
}
|
|
|
|
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);
|
|
_hidden = false;
|
|
save();
|
|
}
|
|
|
|
void YellowNote::hide()
|
|
{
|
|
gtk_widget_hide(_note_widget);
|
|
_hidden = true;
|
|
save();
|
|
}
|
|
|
|
void YellowNote::toggleTitleOnly()
|
|
{
|
|
_title_only = !_title_only;
|
|
updateTitleOnly();
|
|
save();
|
|
}
|
|
|
|
void YellowNote::resized(int width, int height)
|
|
{
|
|
_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()
|
|
{
|
|
if (_hidden) {
|
|
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);
|
|
}
|
|
}
|
|
|
|
void YellowNote::toDesktop()
|
|
{
|
|
#ifdef __linux
|
|
gtk_window_set_type_hint(_note_widget, GdkWindowTypeHint::GDK_WINDOW_TYPE_HINT_DESKTOP);
|
|
#endif
|
|
#ifdef _WIN32
|
|
GdkWindow *notes_win = gtk_widget_get_window(_notes->getWindow());
|
|
HWND nw = gdk_win32_window_get_handle(notes_win);
|
|
SetWindowPos(nw, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
|
|
GdkWindow *win = gtk_widget_get_window(_note_widget);
|
|
HWND w = gdk_win32_window_get_handle(win);
|
|
SetWindowPos(w, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
|
|
#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;
|
|
gtk_window_get_position(_note_widget, &left, &top);
|
|
header_top = top;
|
|
|
|
GtkAllocation alloc;
|
|
gtk_widget_get_allocation(_note_header, &alloc);
|
|
header_bottom = alloc.y + alloc.height + top;
|
|
}
|
|
|
|
void YellowNote::get_frame_screen_coords(int &frame_bottom, int &frame_right)
|
|
{
|
|
int left, top;
|
|
gtk_window_get_position(_note_widget, &left, &top);
|
|
GtkAllocation alloc;
|
|
|
|
gtk_widget_get_allocation(_scroll_widget, &alloc);
|
|
frame_bottom = alloc.y + alloc.height + top;
|
|
frame_right = alloc.x + alloc.width + left;
|
|
}
|
|
|
|
void YellowNote::get_screen_left_right(GtkWidget *widget, int &left, int &right)
|
|
{
|
|
int wleft, wtop;
|
|
gtk_window_get_position(_note_widget, &wleft, &wtop);
|
|
GtkAllocation alloc;
|
|
|
|
gtk_widget_get_allocation(widget, &alloc);
|
|
left = wleft + alloc.x;
|
|
right = left + alloc.width;
|
|
}
|
|
|
|
void YellowNote::adjustTitle(bool mutate)
|
|
{
|
|
if (!_editing_title) { return; }
|
|
_editing_title = false;
|
|
|
|
if (mutate) {
|
|
std::string _old_title = _title;
|
|
_title = gtk_entry_get_text(_title_entry);
|
|
trim(_title);
|
|
gtk_label_set_label(_title_label, _title.c_str());
|
|
save();
|
|
}
|
|
|
|
gtk_container_remove(_note_header, _title_entry);
|
|
gtk_container_add(_note_header, _title_label);
|
|
gtk_container_remove(_note_header, _plus_image);
|
|
gtk_container_add(_note_header, _plus_image);
|
|
gtk_container_remove(_note_header, _delete_image);
|
|
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
|
|
{
|
|
return false;
|
|
}
|
|
|
|
std::string YellowNote::title()
|
|
{
|
|
return _title;
|
|
}
|
|
|
|
bool YellowNote::isHidden()
|
|
{
|
|
return _hidden;
|
|
}
|
|
|
|
void YellowNote::textChanged(void *sender)
|
|
{
|
|
if (_in_transaction) return;
|
|
|
|
_save_counter++;
|
|
if (_save_id == -1) {
|
|
_save_id = _save_counter;
|
|
g_timeout_add(1000, on_text_save_timeout, this);
|
|
}
|
|
}
|
|
|
|
bool YellowNote::textSaveTimeout()
|
|
{
|
|
if (_save_counter != _save_id) {
|
|
_save_id = _save_counter;
|
|
g_timeout_add(1000, on_text_save_timeout, this);
|
|
return false;
|
|
} else {
|
|
_save_id = -1;
|
|
save();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void YellowNote::titleEnter(GtkWidget *sender)
|
|
{
|
|
adjustTitle(true);
|
|
}
|
|
|
|
bool YellowNote::titleEscape(GtkWidget *sender, GdkEventKey *key)
|
|
{
|
|
if (key->keyval == GDK_KEY_Escape && _editing_title) {
|
|
adjustTitle(false);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool YellowNote::titleFocusOut(GtkWidget *sender, GdkEventFocus *evt)
|
|
{
|
|
if (_editing_title) {
|
|
adjustTitle(false);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
#define AROUND(c, n) ((c >= (n - threshold)) && (c <= (n + threshold)))
|
|
|
|
gboolean is_dblclk(gpointer d)
|
|
{
|
|
YellowNote *n = static_cast<YellowNote *>(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;
|
|
int y = evt->y_root;
|
|
|
|
int header_top, header_bottom;
|
|
get_header_screen_coords(header_top, header_bottom);
|
|
|
|
int frame_bottom, frame_right;
|
|
get_frame_screen_coords(frame_bottom, frame_right);
|
|
|
|
int color_left, color_right;
|
|
get_screen_left_right(_color_image, color_left, color_right);
|
|
|
|
int delete_left, delete_right;
|
|
get_screen_left_right(_delete_image, delete_left, delete_right);
|
|
|
|
int plus_left, plus_right;
|
|
get_screen_left_right(_plus_image, plus_left, plus_right);
|
|
|
|
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();
|
|
return true;
|
|
}
|
|
if (x >= plus_left && x <= plus_right) {
|
|
addNew();
|
|
return true;
|
|
}
|
|
if (x >= hide_left && x <= hide_right) {
|
|
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;
|
|
}
|
|
}
|
|
|
|
if (evt->type == GDK_2BUTTON_PRESS) {
|
|
gtk_container_remove(_note_header, _title_label);
|
|
gtk_container_add(_note_header, _title_entry);
|
|
gtk_container_remove(_note_header, _plus_image);
|
|
gtk_container_add(_note_header, _plus_image);
|
|
gtk_container_remove(_note_header, _delete_image);
|
|
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;
|
|
}
|
|
|
|
int threshold = 8;
|
|
|
|
/*
|
|
if (AROUND(y, frame_bottom) && AROUND(x, frame_right)) {
|
|
_resize_edge = true;
|
|
} else if (AROUND(y, frame_bottom)) {
|
|
_resize_bottom = true;
|
|
} else if (AROUND(x, frame_right)) {
|
|
_resize_right = true;
|
|
}
|
|
*/
|
|
if (_resize_type == BOTTOM_RIGHT) {
|
|
_resize_edge = true;
|
|
} else if (_resize_type == BOTTOM) {
|
|
_resize_bottom = true;
|
|
} else if (_resize_type == RIGHT) {
|
|
_resize_right = true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool YellowNote::move_end(GtkWidget *sender, GdkEventButton *evt)
|
|
{
|
|
if (_moving) {
|
|
_moving = false;
|
|
return true;
|
|
}
|
|
|
|
if (_resize_edge || _resize_bottom || _resize_right) {
|
|
_resize_edge = false;
|
|
_resize_bottom = false;
|
|
_resize_right = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool YellowNote::moving(GtkWidget *sender, GdkEventMotion *evt)
|
|
{
|
|
int x = evt->x_root;
|
|
int y = evt->y_root;
|
|
|
|
if (_moving) {
|
|
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_edge) {
|
|
int left, top;
|
|
gtk_window_get_position(_note_widget, &left, &top);
|
|
int width = x - left;
|
|
int height = y - top;
|
|
if (width < 100) { width = 100; }
|
|
if (height < 60) { height = 60; }
|
|
gtk_window_resize(_note_widget, width, height);
|
|
resized(width, height);
|
|
return true;
|
|
}
|
|
|
|
if (_resize_right) {
|
|
int left, top;
|
|
gtk_window_get_position(_note_widget, &left, &top);
|
|
int w, h;
|
|
gtk_window_get_size(_note_widget, &w, &h);
|
|
int width = x - left;
|
|
if (width < 100) { width = 100; }
|
|
gtk_window_resize(_note_widget, width, h);
|
|
resized(width, h);
|
|
return true;
|
|
}
|
|
|
|
if (_resize_bottom) {
|
|
int left, top;
|
|
gtk_window_get_position(_note_widget, &left, &top);
|
|
int w, h;
|
|
gtk_window_get_size(_note_widget, &w, &h);
|
|
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 header_top, header_bottom;
|
|
get_header_screen_coords(header_top, header_bottom);
|
|
|
|
GdkWindow *window = gtk_widget_get_window(_frame);
|
|
GdkCursor *c;
|
|
|
|
int threshold = 16;
|
|
|
|
if (y >= header_top && y <= header_bottom) {
|
|
c = gdk_cursor_new(GDK_LEFT_PTR);
|
|
_resize_type = NONE;
|
|
} else if (AROUND(x, frame_right) && AROUND(y, frame_bottom)) {
|
|
c = gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER);
|
|
_resize_type = BOTTOM_RIGHT;
|
|
} else if (AROUND(x, frame_right)) {
|
|
c = gdk_cursor_new(GDK_RIGHT_SIDE);
|
|
_resize_type = RIGHT;
|
|
} else if (AROUND(y, frame_bottom)) {
|
|
c = gdk_cursor_new(GDK_BOTTOM_SIDE);
|
|
_resize_type = BOTTOM;
|
|
} else {
|
|
c = nullptr;
|
|
_resize_type = NONE;
|
|
}
|
|
|
|
gdk_window_set_cursor(window, c);
|
|
if (c) g_object_unref(c);
|
|
|
|
return false;
|
|
}
|
|
|
|
void YellowNote::load()
|
|
{
|
|
auto readInt = [](FILE *f, int default_value) {
|
|
char buffer[100];
|
|
fgets(buffer, 100, f);
|
|
std::string s = buffer;
|
|
trim(s);
|
|
std::string::iterator sit = s.begin();
|
|
while(sit != s.end() && (*sit >= '0' && *sit <= '9')) { sit++; }
|
|
if (sit != s.end()) {
|
|
return default_value;
|
|
} else {
|
|
int v = atoi(buffer);
|
|
return v;
|
|
}
|
|
};
|
|
|
|
_in_transaction = true;
|
|
|
|
std::filesystem::path p(_filename);
|
|
|
|
int hidden, x, y, width, height;
|
|
ColorType_t c;
|
|
std::string title;
|
|
bool title_only;
|
|
|
|
InfoOverMe info;
|
|
std::string my_name = info.myId();
|
|
|
|
auto readKind = [my_name, this](std::string line, std::function<void (const std::string &os_host, int val)> f) {
|
|
// Split line in parts.
|
|
std::istringstream inp(line);
|
|
std::string part;
|
|
bool found = false;
|
|
while(std::getline(inp, part, ',')) {
|
|
std::string os_host;
|
|
int val;
|
|
int idx;
|
|
idx = part.find(":");
|
|
if (idx >= 0) {
|
|
os_host = part.substr(0, idx);
|
|
std::string s = part.substr(idx + 1);
|
|
val = atoi(s.c_str());
|
|
f(os_host,val);
|
|
if (os_host == my_name) { found = true; }
|
|
}
|
|
}
|
|
return found;
|
|
};
|
|
|
|
auto getStoredCoord = [this](const std::string &os_host, bool &created) {
|
|
std::list<StoredCoords>::iterator it = _stored_coords.begin();
|
|
while(it != _stored_coords.end() && it->os_host != os_host) {
|
|
it++;
|
|
}
|
|
if (it == _stored_coords.end()) {
|
|
StoredCoords c;
|
|
c.os_host = os_host;
|
|
_stored_coords.push_back(c);
|
|
StoredCoords *r = &(_stored_coords.back());
|
|
created = true;
|
|
return r;
|
|
} else {
|
|
StoredCoords *r = &(*it);
|
|
created = false;
|
|
return r;
|
|
}
|
|
};
|
|
|
|
auto setStoredCoordVar = [this, getStoredCoord, readKind](std::string line, std::function<void(StoredCoords *c, int v)> f) {
|
|
auto setter = [f, this, getStoredCoord](const std::string &os_name, int val) {
|
|
bool created;
|
|
StoredCoords *r = getStoredCoord(os_name, created);
|
|
f(r, val);
|
|
};
|
|
readKind(line, setter);
|
|
};
|
|
|
|
auto readLine = [](FILE *f) {
|
|
char buf[10240];
|
|
fgets(buf, 10240, f);
|
|
std::string l(buf);
|
|
trim(l);
|
|
return l;
|
|
};
|
|
|
|
_stored_coords.clear();
|
|
|
|
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);
|
|
|
|
if (version <= 2) {
|
|
hidden = readInt(f, -1);
|
|
x = readInt(f, 200);
|
|
y = readInt(f, 200);
|
|
width = readInt(f, 300);
|
|
height = readInt(f, 200);
|
|
} else {
|
|
// fill in the dots.
|
|
setStoredCoordVar(readLine(f), [](StoredCoords *c, int val) { c->hidden = val; });
|
|
setStoredCoordVar(readLine(f), [](StoredCoords *c, int val) { c->x = val; });
|
|
setStoredCoordVar(readLine(f), [](StoredCoords *c, int val) { c->y = val; });
|
|
setStoredCoordVar(readLine(f), [](StoredCoords *c, int val) { c->width = val; });
|
|
setStoredCoordVar(readLine(f), [](StoredCoords *c, int val) { c->height = val; });
|
|
|
|
bool created;
|
|
StoredCoords *r = getStoredCoord(my_name, created);
|
|
if (created) {
|
|
r->hidden = false;
|
|
r->x = 100;
|
|
r->y = 100;
|
|
r->width = 300;
|
|
r->height = 200;
|
|
}
|
|
|
|
hidden = r->hidden;
|
|
x = r->x;
|
|
y = r->y;
|
|
width = r->width;
|
|
height = r->height;
|
|
}
|
|
|
|
int color;
|
|
color = readInt(f, ColorType_t::YELLOW);
|
|
c = static_cast<ColorType_t>(color);
|
|
|
|
if (version >= 2) {
|
|
int t = readInt(f, 0);
|
|
title_only = (t) ? true : false;
|
|
}
|
|
|
|
char *buf = static_cast<char *>(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();
|
|
}
|
|
}
|
|
|
|
_in_transaction = false;
|
|
}
|
|
|
|
void YellowNote::save()
|
|
{
|
|
if (_in_transaction) {
|
|
return;
|
|
}
|
|
|
|
InfoOverMe info;
|
|
std::string my_name = info.myId();
|
|
|
|
auto storeKind = [my_name, this](int val, std::function<int (const StoredCoords &)> f) {
|
|
std::string line("");
|
|
std::list<StoredCoords>::iterator it;
|
|
std::string comma("");
|
|
bool stored = false;
|
|
for(it = _stored_coords.begin(); it != _stored_coords.end(); it++) {
|
|
const StoredCoords &c = *it;
|
|
int num = (my_name == c.os_host) ? val : f(c);
|
|
if (my_name == c.os_host) { stored = true; }
|
|
char buf[100];
|
|
sprintf(buf, "%d", num);
|
|
line += comma + c.os_host + ":" + buf;
|
|
comma = ",";
|
|
}
|
|
if (!stored) {
|
|
char buf[100];
|
|
sprintf(buf, "%d", val);
|
|
line += comma + my_name + ":" + buf;
|
|
}
|
|
return line;
|
|
};
|
|
|
|
std::filesystem::path p(_filename);
|
|
FILE *f = fopen(_filename.c_str(), "wt");
|
|
if (f) {
|
|
fprintf(f, "%d\n", YELLOWNOTE_FILE_VERSION);
|
|
|
|
|
|
fprintf(f, "%s\n", storeKind(_hidden, [](const StoredCoords &c) { return c.hidden; }).c_str());
|
|
fprintf(f, "%s\n", storeKind(_x, [](const StoredCoords &c) { return c.x; }).c_str());
|
|
fprintf(f, "%s\n", storeKind(_y, [](const StoredCoords &c) { return c.y; }).c_str());
|
|
fprintf(f, "%s\n", storeKind(_width, [](const StoredCoords &c) { return c.width; }).c_str());
|
|
fprintf(f, "%s\n", storeKind(_height, [](const StoredCoords &c) { return c.height; }).c_str());
|
|
|
|
fprintf(f, "%d\n", _color);
|
|
fprintf(f, "%d\n", _title_only);
|
|
fprintf(f, "%s\n", _title.c_str());
|
|
|
|
GtkTextIter start, end;
|
|
gtk_text_buffer_get_start_iter(_buffer, &start);
|
|
gtk_text_buffer_get_end_iter(_buffer, &end);
|
|
gchar *text = gtk_text_buffer_get_text(_buffer, &start, &end, false);
|
|
fprintf(f, "%s", text);
|
|
fclose(f);
|
|
g_free(text);
|
|
}
|
|
}
|
|
|
|
|