473 lines
11 KiB
C++
473 lines
11 KiB
C++
#include "yellownotes.h"
|
|
#include <iostream>
|
|
#include <string>
|
|
#include <filesystem>
|
|
#include <vector>
|
|
|
|
#ifdef __linux
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <pwd.h>
|
|
#include <sys/stat.h>
|
|
#endif
|
|
|
|
#include <string.h>
|
|
#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<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 *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<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())
|
|
{
|
|
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<GtkMenu *>(_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<GdkEventMask>(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<char *>(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);
|
|
}
|
|
}
|
|
|
|
|