472
yellownotes.cpp
Normal file
472
yellownotes.cpp
Normal file
@@ -0,0 +1,472 @@
|
||||
#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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
yellownotes.h
Normal file
45
yellownotes.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#ifndef YELLOWNOTES_H
|
||||
#define YELLOWNOTES_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
class YellowNote;
|
||||
|
||||
class YellowNotes
|
||||
{
|
||||
private:
|
||||
void *_tray_menu;
|
||||
void *_app;
|
||||
std::list<YellowNote *> _notes;
|
||||
|
||||
public:
|
||||
void loadNotes();
|
||||
void clearNotes();
|
||||
|
||||
public:
|
||||
std::string imageFile(const char *name);
|
||||
std::string appDir();
|
||||
std::string notesDir();
|
||||
|
||||
public:
|
||||
void popupTrayMenu();
|
||||
void newNote();
|
||||
void quit();
|
||||
|
||||
public:
|
||||
YellowNotes(void *app);
|
||||
~YellowNotes();
|
||||
};
|
||||
|
||||
#define YELLOWNOTES(obj) reinterpret_cast<YellowNotes *>(obj)
|
||||
#define SIGNAL(type, func, member) \
|
||||
static void func(GObject *obj, gpointer user_data) { type *o = reinterpret_cast<type *>(user_data); o->member(); }
|
||||
|
||||
#define SIGNAL2(type, func, member, argtype) \
|
||||
static void func(GObject *obj, void *arg, gpointer user_data) { type *o = reinterpret_cast<type *>(user_data); o->member(reinterpret_cast<argtype *>(arg)); }
|
||||
|
||||
#define BSIGNAL2(type, func, member, argtype) \
|
||||
static gboolean func(GObject *obj, void *arg, gpointer user_data) { type *o = reinterpret_cast<type *>(user_data); return o->member(reinterpret_cast<argtype *>(arg)); }
|
||||
|
||||
#endif // YELLOWNOTES_H
|
||||
Reference in New Issue
Block a user