#include "yellownotes.h" #include #include #include #include #ifdef __linux #include #include #include #include #endif #include #include "gtk-imports.h" class YellowNote { private: GtkWindow *_note_widget; GtkWidget *_note_box; GtkTextBuffer *_buffer; GtkWidget *_text_widget; GtkWidget *_title_box; GtkWidget *_title_label; GtkHeaderBar *_note_header; GtkWidget *_header_evt_box; std::string _filename; std::string _note; std::string _title; bool _hidden; int _x; int _y; int _width; int _height; bool _in_transaction; bool _moving; int _x_orig; int _y_orig; public: void show(); void hide(); private: void updateTitle(); void updatePosition(); void updateHidden(); void updateSize(); public: void changed(); void size_allocated(GtkAllocation *a); public: bool move_begin(GdkEventButton *evt); bool move_end(GdkEventButton *evt); bool moving(GdkEventMotion *evt); public: void load(); void save(); public: YellowNote(const std::string &filename);; ~YellowNote(); }; 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(YellowNotes, on_new_yellow, newNote) SIGNAL(YellowNotes, on_quit, quit) std::string YellowNotes::imageFile(const char *name) { return appDir() + "/" + name; } std::string YellowNotes::appDir() { std::string base = "/home/hans/src/yellownotes"; return base; } std::string YellowNotes::notesDir() { #ifdef __linux 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; } void YellowNotes::popupTrayMenu() { GtkMenu *tray_menu = reinterpret_cast(_tray_menu); if (tray_menu) { gtk_widget_destroy(tray_menu); } tray_menu = gtk_menu_new(); _tray_menu = reinterpret_cast(tray_menu); GtkMenuItem *new_yellow = gtk_menu_item_new_with_label("New Note"); GtkMenuItem *handle_notes = gtk_menu_item_new_with_label("Organize Notes"); GtkWidget *sep = gtk_separator_menu_item_new(); GtkMenuItem *quit = gtk_menu_item_new_with_label("Quit"); gtk_menu_shell_append(tray_menu, new_yellow); gtk_menu_shell_append(tray_menu, handle_notes); gtk_menu_shell_append(tray_menu, sep); 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(quit, "activate", on_quit, this); gtk_menu_popup_at_pointer(tray_menu, nullptr); } void YellowNotes::clearNotes() { while (!_notes.empty()) { YellowNote *note = _notes.front(); _notes.pop_front(); delete note; } } void YellowNotes::loadNotes() { 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 file_list; for (const auto& entry : std::filesystem::directory_iterator(folder)) { const auto full_name = entry.path().string(); if (entry.is_regular_file()) { const auto base_name = entry.path().filename().string(); if (base_name.rfind(".note") > 0) { YellowNote *note = new YellowNote(full_name); _notes.push_back(note); } } } } void YellowNotes::newNote() { std::string new_note_title = "New Note"; std::string notes_dir = notesDir(); auto filename = [notes_dir](const std::string & title, int i) { char s[20]; sprintf(s, " %d", i); std::string fn = notes_dir + "/" + title + s + ".note"; return fn; }; int i = 1; std::filesystem::path p(filename(new_note_title, i)); while(std::filesystem::is_regular_file(p)) { i += 1; p = std::filesystem::path(filename(new_note_title, i)); } YellowNote *note = new YellowNote(p); _notes.push_back(note); note->show(); note->save(); } void YellowNotes::quit() { gtk_main_quit(); } YellowNotes::YellowNotes(void *app) { _app = app; _tray_menu = nullptr; } YellowNotes::~YellowNotes() { if (_tray_menu) { gtk_widget_destroy(reinterpret_cast(_tray_menu)); } clearNotes(); } //////////////////////////////////////////////////////////////////////////////////////////////////// /// YellowNote implementation //////////////////////////////////////////////////////////////////////////////////////////////////// YellowNote::YellowNote(const std::string &filename) { _filename = filename; _title = ""; _x = -1; _y = -1; _width = -1; _height = -1; _hidden = false; _moving = false; _note_widget = gtk_window_new(GtkWindowType::GTK_WINDOW_TOPLEVEL); _buffer = gtk_text_buffer_new(nullptr); _text_widget = gtk_text_view_new_with_buffer(_buffer); _header_evt_box = gtk_event_box_new(); _note_header = gtk_header_bar_new(); gtk_window_set_decorated(_note_widget, false); _note_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5); //_title_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); _title_label = gtk_label_new(_title.c_str()); gtk_label_set_ellipsize(_title_label, PANGO_ELLIPSIZE_END); gtk_header_bar_set_custom_title(_note_header, _title_label); //gtk_container_add(_title_box, _title_label); gtk_container_add(_note_box, _header_evt_box); gtk_container_add(_header_evt_box, _note_header); gtk_container_add(_note_box, _text_widget); gtk_container_add(_note_widget, _note_box); gtk_widget_show_all(_note_widget); { GdkWindow *w = gtk_widget_get_window(_title_label); int mask = GDK_BUTTON1_MOTION_MASK + GDK_BUTTON_PRESS_MASK + GDK_BUTTON_RELEASE_MASK; gdk_window_set_events(w, static_cast(mask)); } g_signal_connect(_note_widget, "size_allocate", on_size_allocated, this); g_signal_connect(_header_evt_box, "button_press_event", on_button_press, this); g_signal_connect(_header_evt_box, "button_release_event", on_button_release, this); g_signal_connect(_header_evt_box, "motion_notify_event", on_mouse_move, this); load(); _in_transaction = false; } YellowNote::~YellowNote() { gtk_widget_destroy(_note_widget); _note_widget = nullptr; } void YellowNote::updateTitle() { gtk_label_set_label(_title_label, _title.c_str()); } void YellowNote::updateHidden() { if (_hidden) { hide(); } else { show(); } } void YellowNote::updatePosition() { gtk_window_move(_note_widget, _x, _y); } void YellowNote::updateSize() { gtk_window_resize(_note_widget, _width, _height); } void YellowNote::show() { gtk_widget_show_all(_note_widget); } void YellowNote::hide() { gtk_widget_hide(_note_widget); } void YellowNote::changed() { bool sv = false; 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(); } } void YellowNote::size_allocated(GtkAllocation *a) { changed(); } bool YellowNote::move_begin(GdkEventButton *evt) { std::cout << "move_begin" << std::endl; _moving = true; _x_orig = evt->x_root; _y_orig = evt->y_root; return true; } bool YellowNote::move_end(GdkEventButton *evt) { std::cout << "move_end" << std::endl; _moving = false; return true; } bool YellowNote::moving(GdkEventMotion *evt) { int x = evt->x_root; int y = evt->y_root; int dx = x - _x_orig; int dy = y - _y_orig; std::cout << "moving " << x << ", " << y << ", dx = " << dx << ", dy = " << dy << ", _x = " << _x << ", _y = " << y << std::endl; //gtk_window_move(_note_widget, _x + dx, _y + dy); return false; } void YellowNote::load() { auto readInt = [](FILE *f, int default_value) { char buffer[100]; fgets(buffer, 100, f); int v = atoi(buffer); if (default_value >= 0 && v == 0) { return default_value; } return v; }; _in_transaction = true; std::filesystem::path p(_filename); const auto base_name = p.filename().string(); _title = base_name.substr(0, base_name.rfind(".note")); 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) { _hidden = readInt(f, -1); _x = readInt(f, 200); _y = readInt(f, 200); _width = readInt(f, 300); _height = readInt(f, 200); int pos = ftell(f); char *buf = static_cast(malloc(s)); memset(buf, 0, s); int bytes = s - pos; fread(buf, bytes, 1, f); fclose(f); gtk_text_buffer_set_text(_buffer, buf, strlen(buf)); free(buf); } updateTitle(); updateHidden(); updatePosition(); updateSize(); _in_transaction = false; } void YellowNote::save() { if (_in_transaction) { return; } std::filesystem::path p(_filename); FILE *f = fopen(_filename.c_str(), "wt"); if (f) { fprintf(f, "%d\n", _hidden); fprintf(f, "%d\n", _x); fprintf(f, "%d\n", _y); fprintf(f, "%d\n", _width); fprintf(f, "%d\n", _height); 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); free(text); } }