diff --git a/CMakeLists.txt b/CMakeLists.txt index 74da535..fa0b7bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,8 @@ add_executable(yellownotes main.cpp gtk-imports.h gtkloader.h gtkloader.cpp gtk-imports.c - yellownotes.h yellownotes.cpp) + yellownotes.h yellownotes.cpp + tr.h tr.cpp) include(GNUInstallDirs) install(TARGETS yellownotes diff --git a/color.png b/color.png new file mode 100644 index 0000000..734c728 Binary files /dev/null and b/color.png differ diff --git a/color.svg b/color.svg new file mode 100644 index 0000000..ef6c734 --- /dev/null +++ b/color.svg @@ -0,0 +1,17 @@ + + + Font-Color + + + + + + + + + + + + + + \ No newline at end of file diff --git a/delete.png b/delete.png new file mode 100644 index 0000000..d9105a6 Binary files /dev/null and b/delete.png differ diff --git a/delete.svg b/delete.svg new file mode 100644 index 0000000..fc2ebd9 --- /dev/null +++ b/delete.svg @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/gtk-imports.h b/gtk-imports.h index cbe3a26..0282eb8 100644 --- a/gtk-imports.h +++ b/gtk-imports.h @@ -44,12 +44,30 @@ typedef GtkMenuShell GtkMenu; typedef void GtkMenuItem; typedef void GdkEvent; typedef void GtkTextBuffer; +typedef void GtkTextView; typedef void GtkTextTagTable; typedef void GtkContainer; typedef void GtkLabel; typedef void GtkHeaderBar; typedef void GdkWindow; typedef void GdkDevice; +typedef void GtkImage; +typedef void GdkPixbuf; +typedef void GError; +typedef void GtkStyleContext; +typedef void GtkCssProvider; +typedef void GtkStyleProvider; +typedef void GtkWidgetPath; +typedef void GtkAdjustment; +typedef void GtkFrame; +typedef void GtkEventBox; +typedef void GdkCursor; +typedef void GdkDisplay; +typedef void GtkEntry; +typedef void GtkDialog; +typedef void GTimer; +typedef void GtkComboBoxText; +typedef void GtkComboBox; typedef int gboolean; typedef int gint; @@ -63,11 +81,14 @@ typedef unsigned long long guint64; typedef float gfloat; typedef double gdouble; typedef char gint8; +typedef unsigned char guint8; typedef short gint16; - +typedef unsigned short guint16; +typedef size_t gssize; typedef enum { - GTK_WINDOW_TOPLEVEL = 0 + GTK_WINDOW_TOPLEVEL = 0, + GTK_WINDOW_POPUP = 1 } GtkWindowType; typedef enum { @@ -172,36 +193,56 @@ typedef struct _GtkTextIter { typedef enum { - GDK_DELETE, - GDK_MOTION_NOTIFY, - GDK_BUTTON_PRESS, - GDK_BUTTON_RELEASE, - GDK_KEY_PRESS, - GDK_KEY_RELEASE, - GDK_ENTER_NOTIFY, - GDK_LEAVE_NOTIFY, - GDK_FOCUS_CHANGE, - GDK_PROXIMITY_IN, - GDK_PROXIMITY_OUT, - GDK_DRAG_ENTER, - GDK_DRAG_LEAVE, - GDK_DRAG_MOTION, - GDK_DROP_START, - GDK_SCROLL, - GDK_GRAB_BROKEN, - GDK_TOUCH_BEGIN, - GDK_TOUCH_UPDATE, - GDK_TOUCH_END, - GDK_TOUCH_CANCEL, - GDK_TOUCHPAD_SWIPE, - GDK_TOUCHPAD_PINCH, - GDK_PAD_BUTTON_PRESS, - GDK_PAD_BUTTON_RELEASE, - GDK_PAD_RING, - GDK_PAD_STRIP, - GDK_PAD_GROUP_MODE, - GDK_TOUCHPAD_HOLD, - GDK_PAD_DIAL, + GDK_NOTHING = -1, + GDK_DELETE = 0, + GDK_DESTROY = 1, + GDK_EXPOSE = 2, + GDK_MOTION_NOTIFY = 3, + GDK_BUTTON_PRESS = 4, + GDK_2BUTTON_PRESS = 5, + GDK_DOUBLE_BUTTON_PRESS = GDK_2BUTTON_PRESS, + GDK_3BUTTON_PRESS = 6, + GDK_TRIPLE_BUTTON_PRESS = GDK_3BUTTON_PRESS, + GDK_BUTTON_RELEASE = 7, + GDK_KEY_PRESS = 8, + GDK_KEY_RELEASE = 9, + GDK_ENTER_NOTIFY = 10, + GDK_LEAVE_NOTIFY = 11, + GDK_FOCUS_CHANGE = 12, + GDK_CONFIGURE = 13, + GDK_MAP = 14, + GDK_UNMAP = 15, + GDK_PROPERTY_NOTIFY = 16, + GDK_SELECTION_CLEAR = 17, + GDK_SELECTION_REQUEST = 18, + GDK_SELECTION_NOTIFY = 19, + GDK_PROXIMITY_IN = 20, + GDK_PROXIMITY_OUT = 21, + GDK_DRAG_ENTER = 22, + GDK_DRAG_LEAVE = 23, + GDK_DRAG_MOTION = 24, + GDK_DRAG_STATUS = 25, + GDK_DROP_START = 26, + GDK_DROP_FINISHED = 27, + GDK_CLIENT_EVENT = 28, + GDK_VISIBILITY_NOTIFY = 29, + GDK_SCROLL = 31, + GDK_WINDOW_STATE = 32, + GDK_SETTING = 33, + GDK_OWNER_CHANGE = 34, + GDK_GRAB_BROKEN = 35, + GDK_DAMAGE = 36, + GDK_TOUCH_BEGIN = 37, + GDK_TOUCH_UPDATE = 38, + GDK_TOUCH_END = 39, + GDK_TOUCH_CANCEL = 40, + GDK_TOUCHPAD_SWIPE = 41, + GDK_TOUCHPAD_PINCH = 42, + GDK_PAD_BUTTON_PRESS = 43, + GDK_PAD_BUTTON_RELEASE = 44, + GDK_PAD_RING = 45, + GDK_PAD_STRIP = 46, + GDK_PAD_GROUP_MODE = 47, GDK_EVENT_LAST /* helper variable for decls */ } GdkEventType; @@ -235,8 +276,22 @@ typedef struct _GdkEventMotion gdouble x_root, y_root; } GdkEventMotion; +typedef struct _GdkEventKey +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + guint32 time; + guint state; + guint keyval; + gint length; + gchar *string; + guint16 hardware_keycode; + guint8 group; + guint is_modifier : 1; +} GdkEventKey; - +#define GDK_KEY_Escape 65307 typedef enum { @@ -268,14 +323,260 @@ typedef enum GDK_ALL_EVENTS_MASK = 0x3FFFFFE } GdkEventMask; +typedef enum +{ + GTK_STATE_FLAG_NORMAL = 0, + GTK_STATE_FLAG_ACTIVE = 1 << 0, + GTK_STATE_FLAG_PRELIGHT = 1 << 1, + GTK_STATE_FLAG_SELECTED = 1 << 2, + GTK_STATE_FLAG_INSENSITIVE = 1 << 3, + GTK_STATE_FLAG_INCONSISTENT = 1 << 4, + GTK_STATE_FLAG_FOCUSED = 1 << 5, + GTK_STATE_FLAG_BACKDROP = 1 << 6, + GTK_STATE_FLAG_DIR_LTR = 1 << 7, + GTK_STATE_FLAG_DIR_RTL = 1 << 8, + GTK_STATE_FLAG_LINK = 1 << 9, + GTK_STATE_FLAG_VISITED = 1 << 10, + GTK_STATE_FLAG_CHECKED = 1 << 11, + GTK_STATE_FLAG_DROP_ACTIVE = 1 << 12 +} GtkStateFlags; + + +typedef void (* GtkMenuPositionFunc) ( + GtkMenu* menu, + gint* x, + gint* y, + gboolean* push_in, + gpointer user_data + ); + +typedef struct _GdkRGBA +{ + gdouble red; + gdouble green; + gdouble blue; + gdouble alpha; +} GdkRGBA; + +typedef struct _GtkRequisition +{ + gint width; + gint height; +} GtkRequisition; + +typedef enum { + GTK_STYLE_CONTEXT_PRINT_NONE = 0, + GTK_STYLE_CONTEXT_PRINT_RECURSE = 1 << 0, + GTK_STYLE_CONTEXT_PRINT_SHOW_STYLE = 1 << 1 +} GtkStyleContextPrintFlags; + +typedef enum +{ + GTK_WRAP_NONE, + GTK_WRAP_CHAR, + GTK_WRAP_WORD, + GTK_WRAP_WORD_CHAR +} GtkWrapMode; + +typedef enum +{ + GTK_SHADOW_NONE, + GTK_SHADOW_IN, + GTK_SHADOW_OUT, + GTK_SHADOW_ETCHED_IN, + GTK_SHADOW_ETCHED_OUT +} GtkShadowType; + +typedef enum +{ + GDK_X_CURSOR = 0, + GDK_ARROW = 2, + GDK_BASED_ARROW_DOWN = 4, + GDK_BASED_ARROW_UP = 6, + GDK_BOAT = 8, + GDK_BOGOSITY = 10, + GDK_BOTTOM_LEFT_CORNER = 12, + GDK_BOTTOM_RIGHT_CORNER = 14, + GDK_BOTTOM_SIDE = 16, + GDK_BOTTOM_TEE = 18, + GDK_BOX_SPIRAL = 20, + GDK_CENTER_PTR = 22, + GDK_CIRCLE = 24, + GDK_CLOCK = 26, + GDK_COFFEE_MUG = 28, + GDK_CROSS = 30, + GDK_CROSS_REVERSE = 32, + GDK_CROSSHAIR = 34, + GDK_DIAMOND_CROSS = 36, + GDK_DOT = 38, + GDK_DOTBOX = 40, + GDK_DOUBLE_ARROW = 42, + GDK_DRAFT_LARGE = 44, + GDK_DRAFT_SMALL = 46, + GDK_DRAPED_BOX = 48, + GDK_EXCHANGE = 50, + GDK_FLEUR = 52, + GDK_GOBBLER = 54, + GDK_GUMBY = 56, + GDK_HAND1 = 58, + GDK_HAND2 = 60, + GDK_HEART = 62, + GDK_ICON = 64, + GDK_IRON_CROSS = 66, + GDK_LEFT_PTR = 68, + GDK_LEFT_SIDE = 70, + GDK_LEFT_TEE = 72, + GDK_LEFTBUTTON = 74, + GDK_LL_ANGLE = 76, + GDK_LR_ANGLE = 78, + GDK_MAN = 80, + GDK_MIDDLEBUTTON = 82, + GDK_MOUSE = 84, + GDK_PENCIL = 86, + GDK_PIRATE = 88, + GDK_PLUS = 90, + GDK_QUESTION_ARROW = 92, + GDK_RIGHT_PTR = 94, + GDK_RIGHT_SIDE = 96, + GDK_RIGHT_TEE = 98, + GDK_RIGHTBUTTON = 100, + GDK_RTL_LOGO = 102, + GDK_SAILBOAT = 104, + GDK_SB_DOWN_ARROW = 106, + GDK_SB_H_DOUBLE_ARROW = 108, + GDK_SB_LEFT_ARROW = 110, + GDK_SB_RIGHT_ARROW = 112, + GDK_SB_UP_ARROW = 114, + GDK_SB_V_DOUBLE_ARROW = 116, + GDK_SHUTTLE = 118, + GDK_SIZING = 120, + GDK_SPIDER = 122, + GDK_SPRAYCAN = 124, + GDK_STAR = 126, + GDK_TARGET = 128, + GDK_TCROSS = 130, + GDK_TOP_LEFT_ARROW = 132, + GDK_TOP_LEFT_CORNER = 134, + GDK_TOP_RIGHT_CORNER = 136, + GDK_TOP_SIDE = 138, + GDK_TOP_TEE = 140, + GDK_TREK = 142, + GDK_UL_ANGLE = 144, + GDK_UMBRELLA = 146, + GDK_UR_ANGLE = 148, + GDK_WATCH = 150, + GDK_XTERM = 152, + GDK_LAST_CURSOR, + GDK_BLANK_CURSOR = -2, + GDK_CURSOR_IS_PIXMAP = -1 +} GdkCursorType; + +typedef enum +{ + GTK_ALIGN_FILL, + GTK_ALIGN_START, + GTK_ALIGN_END, + GTK_ALIGN_CENTER, + GTK_ALIGN_BASELINE +} GtkAlign; + +typedef enum +{ + GTK_DIALOG_MODAL = 1 << 0, + GTK_DIALOG_DESTROY_WITH_PARENT = 1 << 1, + GTK_DIALOG_USE_HEADER_BAR = 1 << 2 +} GtkDialogFlags; + +typedef enum +{ + GTK_MESSAGE_INFO, + GTK_MESSAGE_WARNING, + GTK_MESSAGE_QUESTION, + GTK_MESSAGE_ERROR, + GTK_MESSAGE_OTHER +} GtkMessageType; + +typedef enum +{ + GTK_BUTTONS_NONE, + GTK_BUTTONS_OK, + GTK_BUTTONS_CLOSE, + GTK_BUTTONS_CANCEL, + GTK_BUTTONS_YES_NO, + GTK_BUTTONS_OK_CANCEL +} GtkButtonsType; + +typedef enum +{ + GTK_RESPONSE_NONE = -1, + GTK_RESPONSE_REJECT = -2, + GTK_RESPONSE_ACCEPT = -3, + GTK_RESPONSE_DELETE_EVENT = -4, + GTK_RESPONSE_OK = -5, + GTK_RESPONSE_CANCEL = -6, + GTK_RESPONSE_CLOSE = -7, + GTK_RESPONSE_YES = -8, + GTK_RESPONSE_NO = -9, + GTK_RESPONSE_APPLY = -10, + GTK_RESPONSE_HELP = -11 +} GtkResponseType; + +typedef struct _GdkEventFocus +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + gint16 in; +} GdkEventFocus; + +typedef gboolean (*GSourceFunc) (gpointer user_data); + +typedef enum +{ + GDK_WINDOW_TYPE_HINT_NORMAL, + GDK_WINDOW_TYPE_HINT_DIALOG, + GDK_WINDOW_TYPE_HINT_MENU, /* Torn off menu */ + GDK_WINDOW_TYPE_HINT_TOOLBAR, + GDK_WINDOW_TYPE_HINT_SPLASHSCREEN, + GDK_WINDOW_TYPE_HINT_UTILITY, + GDK_WINDOW_TYPE_HINT_DOCK, + GDK_WINDOW_TYPE_HINT_DESKTOP, + GDK_WINDOW_TYPE_HINT_DROPDOWN_MENU, /* A drop down menu (from a menubar) */ + GDK_WINDOW_TYPE_HINT_POPUP_MENU, /* A popup menu (from right-click) */ + GDK_WINDOW_TYPE_HINT_TOOLTIP, + GDK_WINDOW_TYPE_HINT_NOTIFICATION, + GDK_WINDOW_TYPE_HINT_COMBO, + GDK_WINDOW_TYPE_HINT_DND +} GdkWindowTypeHint; + +typedef enum +{ + GDK_VISIBILITY_UNOBSCURED, + GDK_VISIBILITY_PARTIAL, + GDK_VISIBILITY_FULLY_OBSCURED +} GdkVisibilityState; + +typedef struct _GdkEventVisibility +{ + GdkEventType type; + GdkWindow *window; + gint8 send_event; + GdkVisibilityState state; +} GdkEventVisibility; #define G_TYPE_FUNDAMENTAL_SHIFT (2) #define G_TYPE_MAKE_FUNDAMENTAL(x) ((GType) ((x) << G_TYPE_FUNDAMENTAL_SHIFT)) #define G_TYPE_INT G_TYPE_MAKE_FUNDAMENTAL (6) +#define GTK_STYLE_PROVIDER_PRIORITY_USER 800 #endif +// GLib +DECL(GTimer*, g_timeout_add, (guint interval, GSourceFunc func, gpointer data)); + +// Gtk Application / Window / Widget + DECL(GtkApplication *, gtk_application_new, (const char *name, GApplicationFlags flags)) DECL(int, g_application_run, (GApplication* application, int argc, char** argv)) @@ -287,47 +588,105 @@ DECL(void, gtk_window_set_decorated, (GtkWindow* window, gboolean setting )) DECL(void, gtk_widget_show_all, (GtkWindow *window)) DECL(void, gtk_widget_hide, (GtkWidget* widget )) +DECL(void, gtk_widget_show, (GtkWidget* widget)) +DECL(gboolean, gtk_widget_grab_focus, ( GtkWidget* widget )) DECL(GtkWidget*, gtk_event_box_new, (void)) +DECL(void, gtk_event_box_set_visible_window, (GtkEventBox* event_box, gboolean visible_window )) DECL(void, gtk_window_get_size, (GtkWindow *win, int *width, int *height)) DECL(void, gtk_window_resize, (GtkWindow* window, gint width, gint height)) DECL(void, gtk_window_set_default_size, (GtkWindow *window, gint width, gint height)) DECL(void, gtk_window_get_position, (GtkWindow* window, gint* root_x, gint* root_y)) DECL(void, gtk_window_move, (GtkWindow* window, gint x, gint y)) +DECL(void, gtk_window_present, (GtkWindow* window)) +DECL(void, gtk_window_set_type_hint, (GtkWindow* window, GdkWindowTypeHint hint)) +DECL(void, gtk_window_set_skip_taskbar_hint, (GtkWindow* window, gboolean setting)) +DECL(void, gtk_window_set_skip_pager_hint, (GtkWindow* window, gboolean setting)) +DECL(void, gtk_widget_set_can_focus, (GtkWidget* widget, gboolean can_focus )) +DECL(void, gtk_window_set_transient_for, (GtkWindow* window, GtkWindow* parent )) +DECL(GType, gtk_window_get_type, (void)) +DECL(GObject*, g_object_new, (GType object_type, const gchar* first_property_name, ...)) + +DECL(void, gtk_widget_set_size_request, (GtkWidget* widget, int width, int height )) +DECL(void, gtk_widget_get_preferred_size, (GtkWidget* widget, GtkRequisition* minimum_size, GtkRequisition* natural_size)) +DECL(void, gtk_widget_get_allocation, ( GtkWidget* widget, GtkAllocation* allocation )) DECL(void, gtk_window_set_resizable, (GtkWindow* window, gboolean resizable)) DECL(GdkWindow *, gtk_widget_get_window, (GtkWidget *w)); +DECL(GdkWindow*, gdk_window_get_effective_toplevel, (GdkWindow* window)) +DECL(void, gdk_window_set_override_redirect, (GdkWindow* window, gboolean override_redirect)) +DECL(GtkWidget*, gtk_scrolled_window_new, (GtkAdjustment* hadjustment, GtkAdjustment* vadjustment)) DECL(gboolean, gtk_window_get_decorated, (GtkWindow* window)) DECL(void, gdk_window_set_events, (GdkWindow* window, GdkEventMask event_mask )) +DECL(GdkEventMask, gdk_window_get_events, (GdkWindow* window )) DECL(void, gtk_window_set_title, (GtkWindow* window, const gchar* title)) DECL(const gchar*, gtk_window_get_title, (GtkWindow* window)) DECL(void, gtk_widget_destroy, (GtkWidget* widget)) DECL(void, gtk_container_add, (GtkContainer* container, GtkWidget* widget)) +DECL(void, gtk_container_remove, (GtkContainer* container, GtkWidget* widget)) + DECL(gboolean, gtk_widget_is_visible, (GtkWidget* widget)) DECL(GtkWidget*, gtk_label_new, (const char* str)) DECL(void, gtk_label_set_ellipsize, (GtkLabel* label, PangoEllipsizeMode mode)) DECL(void, gtk_label_set_label, (GtkLabel* label, const gchar* str)) DECL(const gchar*, gtk_label_get_text, (GtkLabel* label)) +DECL(GtkWidget*, gtk_entry_new, (void)) +DECL(void, gtk_entry_set_text, (GtkEntry* entry, const gchar* text )) +DECL(const gchar*, gtk_entry_get_text, (GtkEntry* entry)) + +DECL(GtkWidget*, gtk_separator_new, ( GtkOrientation orientation )) + +DECL(GtkWidget*, gtk_frame_new, ( const gchar* label )) +DECL(void, gtk_frame_set_shadow_type, (GtkFrame* frame, GtkShadowType type )) DECL(GtkWidget*, gtk_box_new, (GtkOrientation orientation, gint spacing )) +DECL(void, gtk_widget_set_vexpand, (GtkWidget* widget, gboolean expand )) +DECL(void, gtk_widget_set_hexpand, (GtkWidget* widget, gboolean expand )) +DECL(void, gtk_widget_set_halign, (GtkWidget* widget, GtkAlign align )) +DECL(void, gtk_widget_set_valign, (GtkWidget* widget, GtkAlign align )) + +DECL(gboolean, gtk_widget_translate_coordinates, ( GtkWidget* src_widget, GtkWidget* dest_widget, gint src_x, gint src_y, gint* dest_x, gint* dest_y)) DECL(void, gtk_main, (void)) DECL(void, gtk_main_quit, (void)) DECL(unsigned long, g_signal_connect_data, (GObject *obj, const char *signal, GCallback c_handler, gpointer data, GClosureNotify destroy_data, GConnectFlags connect_flags)) +// Messages +DECL(GtkWidget*, gtk_message_dialog_new, (GtkWindow* parent, GtkDialogFlags flags, GtkMessageType type, GtkButtonsType buttons, const gchar* message_format, ...)) +DECL(gint, gtk_dialog_run, (GtkDialog* dialog)) + +// Dialogs +DECL(GtkWidget*, gtk_dialog_new, (void)) +DECL(GtkWidget*, gtk_dialog_add_button, (GtkDialog* dialog, const gchar* button_text, gint response_id )) +DECL(void, gtk_dialog_response, (GtkDialog* dialog, gint response_id )) +//DECL(GtkWidget*, gtk_dialog_new_with_buttons, (const gchar* title, GtkWindow* parent, GtkDialogFlags flags, const gchar* first_button_text, ...)) +//DECL(GtkWidget*, gtk_drop_down_new_from_strings, (const char* const* strings )) +DECL(GtkWidget*, gtk_combo_box_new, (void)); +DECL(GtkWidget*, gtk_combo_box_text_new, (void)) +DECL(void, gtk_combo_box_text_append, ( GtkComboBoxText* combo_box, const gchar* id, const gchar* text )) +DECL(GtkWidget*, gtk_dialog_get_content_area, (GtkDialog* dialog )) +DECL(gboolean, gtk_combo_box_set_active_id, ( GtkComboBox* combo_box, const gchar* active_id )) +DECL(const gchar*, gtk_combo_box_get_active_id, (GtkComboBox* combo_box)) + // GObject / GValue DECL(void, g_object_unref, (GObject* object)) +DECL(void, g_object_ref, (GObject* object)) DECL(void, g_object_set_property, (GObject* object, const gchar* property_name, const GValue* value)) DECL(GValue*, g_value_init, (GValue* value, GType g_type)) DECL(void, g_value_unset, (GValue* value)) DECL(void, g_value_set_int, (GValue* value, gint v_int)) +DECL(void, g_free, (gpointer mem)) +// Cursors +DECL(GdkCursor*, gdk_cursor_new_from_name, (GdkDisplay* display, const gchar* name )) +DECL(GdkCursor*, gdk_cursor_new, ( GdkCursorType cursor_type )) +DECL(void, gdk_window_set_cursor, (GdkWindow* window, GdkCursor* cursor )) // Menus @@ -338,6 +697,8 @@ DECL(GtkMenuItem *, gtk_menu_item_new, (void)); DECL(GtkMenuItem *, gtk_menu_item_new_with_label, (const gchar *label)); DECL(void, gtk_menu_shell_append, (GtkMenuShell* menu_shell, GtkWidget* child)) DECL(GtkWidget *, gtk_separator_menu_item_new, (void)) +DECL(void, gtk_menu_popup, (GtkMenu* menu, GtkWidget* parent_menu_shell, GtkWidget* parent_menu_item, + GtkMenuPositionFunc func, gpointer data, guint button, guint32 activate_time)) // Text Buffer / Widget DECL(GtkTextBuffer*, gtk_text_buffer_new, (GtkTextTagTable* table)) @@ -347,17 +708,42 @@ DECL(void, gtk_text_buffer_get_end_iter, (GtkTextBuffer* buffer, GtkTextIter* it DECL(GtkWidget*, gtk_text_view_new, (void)) DECL(GtkWidget*, gtk_text_view_new_with_buffer, ( GtkTextBuffer* buffer )) DECL(void, gtk_text_buffer_set_text, (GtkTextBuffer* buffer, const gchar* text, gint len )) +DECL(void, gtk_text_view_set_wrap_mode, (GtkTextView* text_view, GtkWrapMode wrap_mode )) // Titlebar DECL(GtkWidget*, gtk_header_bar_new, (void)) DECL(void, gtk_header_bar_set_custom_title, (GtkHeaderBar* bar, GtkWidget* title_widget)) -// Deprecated in Gtk3 +// Image +DECL(GtkWidget*, gtk_image_new_from_file, (const char* filename )) +DECL(GtkWidget*, gtk_image_new_from_pixbuf, (GdkPixbuf* pixbuf )) +DECL(void, gtk_image_set_from_pixbuf, ( GtkImage* image, GdkPixbuf* pixbuf )) +DECL(GdkPixbuf*, gdk_pixbuf_new_from_file, (const char* filename, GError** error)) +DECL(GdkPixbuf*, gdk_pixbuf_new_from_file_at_size, (const char* filename, int width, int height, GError** error)) +// Styles +DECL(GtkStyleContext*, gtk_widget_get_style_context, (GtkWidget* widget)) +DECL(char*, gtk_style_context_to_string, (GtkStyleContext* context, GtkStyleContextPrintFlags flags)) +DECL(GtkCssProvider*, gtk_css_provider_new, (void)) +DECL(gboolean, gtk_css_provider_load_from_data, (GtkCssProvider* css_provider, const gchar* data, gssize length, GError** error)) +DECL(void, gtk_style_context_add_provider, (GtkStyleContext* context, GtkStyleProvider* provider, guint priority)) +DECL(const GtkWidgetPath*, gtk_style_context_get_path, (GtkStyleContext* context)) +DECL(char*, gtk_widget_path_to_string, (const GtkWidgetPath* path)) + +// Deprecated in Gtk3 DECL(GtkStatusIcon*, gtk_status_icon_new, (void )) DECL(GtkStatusIcon*, gtk_status_icon_new_from_file, (const gchar* filename)) +DECL(void, gtk_widget_override_background_color, (GtkWidget* widget, GtkStateFlags state, const GdkRGBA* color)) +DECL(void, gtk_window_set_has_resize_grip, (GtkWindow* window, gboolean value )) #define g_signal_connect(instance, signal, c_handler, data) \ g_signal_connect_data(instance, signal, reinterpret_cast(c_handler), data, NULL, G_CONNECT_DEFAULT) +// WIN32 + +#ifdef _WIN32 +#include +DECL(HWND, gdk_win32_window_get_handle, (GdkWindow *window)); +#endif + #endif // GTK_IMPORTS_H diff --git a/gtkloader.cpp b/gtkloader.cpp index cfd62e2..21edad6 100644 --- a/gtkloader.cpp +++ b/gtkloader.cpp @@ -105,6 +105,8 @@ void GtkLoader::dlopen() "gtk-3-vs17", "glib-2.0-0", "gio-2.0-0", + "gdk-3-vs17", + "gdk_pixbuf-2.0-0", NULL }; int i; diff --git a/hide.png b/hide.png new file mode 100644 index 0000000..e58aa47 Binary files /dev/null and b/hide.png differ diff --git a/hide.svg b/hide.svg new file mode 100644 index 0000000..a6ae70c --- /dev/null +++ b/hide.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/main.cpp b/main.cpp index 0a68b54..9e88434 100644 --- a/main.cpp +++ b/main.cpp @@ -14,7 +14,8 @@ static void activate (GtkApplication* app, gpointer user_data) GtkWidget *window; YellowNotes *notes = YELLOWNOTES(user_data); - GtkStatusIcon *tray = gtk_status_icon_new_from_file(notes->imageFile("yellownotes.svg").c_str()); + std::string img_file = notes->imageFile("yellownotes"); + GtkStatusIcon *tray = gtk_status_icon_new_from_file(img_file.c_str()); g_signal_connect(tray, "activate", on_tray_activate, notes); } @@ -22,6 +23,8 @@ int main(int argc, char **argv) { GtkLoader l; + srand(time(NULL)); // seed with current time + try { l.loadGtk(); } catch(std::string msg) { diff --git a/plus.png b/plus.png new file mode 100644 index 0000000..7e0eb9a Binary files /dev/null and b/plus.png differ diff --git a/plus.svg b/plus.svg new file mode 100644 index 0000000..2dfdc78 --- /dev/null +++ b/plus.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/tr.cpp b/tr.cpp new file mode 100644 index 0000000..e217f8b --- /dev/null +++ b/tr.cpp @@ -0,0 +1,94 @@ +#include "tr.h" +#include + +void tr::add(const char *sentence, ...) +{ + va_list args; + va_start(args, sentence); + + std::string sent(sentence); + + const char *tr = va_arg(args, char *); + while (tr != nullptr) { + std::string t(tr); + size_t pos = t.find(":"); + std::string lang = t.substr(0, pos); + std::string str = t.substr(pos + 1); + + std::unordered_map::iterator it; + it = _translations.find(lang); + if (it != _translations.end()) { + Translations_t *m = it->second; + std::pair p = { sent, str }; + m->insert(p); + } else { + Translations_t *m = new Translations_t(); + std::pair p = { sent, str }; + m->insert(p); + std::pair lp = { lang, m }; + _translations.insert(lp); + } + + tr = va_arg(args, char *); + } + + va_end(args); +} + +const char *tr::translate(const char *str) +{ + std::unordered_map::iterator it; + + it = _translations.find(_lang); + + if (it == _translations.end()) { + return str; + } else { + Translations_t *m = it->second; + Translations_t::iterator n_it; + std::string s(str); + n_it = m->find(s); + if (n_it == m->end()) { + return str; + } else { + return n_it->second.c_str(); + } + } +} + +void tr::setLang(const std::string &l) +{ + _lang = l; +} + +tr::tr() +{ + add("Quit", "nl:Beƫindigen", nullptr); + add("New Note", "nl:Nieuwe Notitie", nullptr); + add("Show Notes", "nl:Notities Presenteren", nullptr); + add("Hide Notes", "nl:Notities Verbergen", nullptr); + add("Reload Notes", "nl:Notities opnieuw laden", nullptr); + add("Setup", "nl:Instellingen", nullptr); + _lang = "en"; +} + +static tr *_t = nullptr; + +static void init() +{ + if (_t == nullptr) { + _t = new tr(); + } +} + +const char *_(const char *str) +{ + init(); + return _t->translate(str); +} + +void setLang(const std::string &lang) +{ + init(); + _t->setLang(lang); +} diff --git a/tr.h b/tr.h new file mode 100644 index 0000000..cf041ae --- /dev/null +++ b/tr.h @@ -0,0 +1,31 @@ +#ifndef TR_H +#define TR_H + +#include +#include + +typedef std::unordered_map Translations_t; + +class tr +{ +private: + std::unordered_map _translations; + std::string _lang; + +private: + void add(const char *sentence, ...); + +public: + const char *translate(const char *str); + +public: + void setLang(const std::string &l); + +public: + tr(); +}; + +const char *_(const char *str); +void setLang(const std::string &lang); + +#endif // TR_H diff --git a/yellownotes.cpp b/yellownotes.cpp index 57aeb22..e055c33 100644 --- a/yellownotes.cpp +++ b/yellownotes.cpp @@ -11,31 +11,74 @@ #include #endif +#ifdef _WIN32 +#include +#endif + #include +extern "C" { #include "gtk-imports.h" +} + +#include "tr.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 YellowNote { private: + YellowNotes *_notes; + GtkWindow *_note_widget; + + GtkWidget *_evt_box; GtkWidget *_note_box; - GtkTextBuffer *_buffer; - GtkWidget *_text_widget; - GtkWidget *_title_box; + GtkWidget *_frame; + + GtkWidget *_note_header; + GtkImage *_color_image; + GtkImage *_delete_image; + GtkImage *_plus_image; + GtkImage *_hide_image; GtkWidget *_title_label; - GtkHeaderBar *_note_header; - GtkWidget *_header_evt_box; + GtkWidget *_title_entry; + GtkWidget *_title_separator; + + GtkWidget *_scroll_widget; + GtkWidget *_text_widget; + GtkTextBuffer *_buffer; + std::string _filename; std::string _note; std::string _title; + bool _hidden; int _x; int _y; int _width; int _height; bool _in_transaction; + bool _editing_title; + + int _save_counter; + int _save_id; + + ColorType_t _color; + bool _color_changed; bool _moving; + bool _resize_right; + bool _resize_bottom; + bool _resize_edge; int _x_orig; int _y_orig; @@ -48,22 +91,49 @@ private: void updatePosition(); void updateHidden(); void updateSize(); + void updateColor(); + + 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 changed(); - void size_allocated(GtkAllocation *a); + void changed(GtkWidget *sender); + void size_allocated(GtkWidget *sender, GtkAllocation *a); public: - bool move_begin(GdkEventButton *evt); - bool move_end(GdkEventButton *evt); - bool moving(GdkEventMotion *evt); + void toFront(); + +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); + +public: + std::string title(); + bool isHidden(); public: void load(); void save(); public: - YellowNote(const std::string &filename);; + YellowNote(YellowNotes *notes, const std::string &filename); ~YellowNote(); }; @@ -71,19 +141,49 @@ 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(data); + return n->textSaveTimeout(); +} SIGNAL(YellowNotes, on_new_yellow, newNote) +SIGNAL(YellowNotes, on_show, showNotes); +SIGNAL(YellowNotes, on_reload, reloadNotes); +SIGNAL(YellowNotes, on_setup, setup); SIGNAL(YellowNotes, on_quit, quit) +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); + std::string YellowNotes::imageFile(const char *name) { - - return appDir() + "/" + name; +#ifdef _WIN32 + std::string ext = ".png"; +#else + std::string ext = ".svg"; +#endif + return appDir() + "/" + name + ext; } std::string YellowNotes::appDir() { +#ifdef __linux std::string base = "/home/hans/src/yellownotes"; +#endif +#ifdef _WIN32 + std::string base = "c:/devel/yellownotes"; +#endif return base; } @@ -94,6 +194,11 @@ std::string YellowNotes::notesDir() const char *homedir = pw->pw_dir; #endif +#ifdef _WIN32 + char homedir[10240]; + snprintf(homedir, 10240, "%s%s", getenv("HOMEDRIVE"), getenv("HOMEPATH")); +#endif + std::string home_dir = homedir; std::string notes_dir = home_dir + "/yellownotes"; @@ -105,7 +210,77 @@ std::string YellowNotes::notesDir() return notes_dir; } -void YellowNotes::popupTrayMenu() +std::string YellowNotes::css(ColorType_t type) +{ + const char *bgs[] = { "#404040", // dark + "#faf32a", // yellow + "#fcbf56", // orange, + "#5df0f5", // blue + "#fc77f4", // Cyaan + "#74fc94", // greeen + "#f7bcbc", // red + "#cdcfd1" // grey + }; + + const char *fgs[] = { "white", + "black", + "black", + "black", + "black", + "black", + "black", + "black" + }; + + 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: " + bgs[type] + ";\n" + " color: " + fgs[type] + ";\n" + " font-family: sans;\n" + " font-size: " + font_size + ";\n" + "}\n" + "frame border {\n" + " border: none;\n" + " box-shadow: 5px 5px 5px " + darker(bgs[type]) + ";\n" + " margin: 5px;\n" + "}\n"; + + return css; +} + +int YellowNotes::fontSize() +{ + return _font_size; +} + +int YellowNotes::iconSize() +{ + return _font_size * 1.5; +} + + +void YellowNotes::popupTrayMenu(void *sender) { GtkMenu *tray_menu = reinterpret_cast(_tray_menu); @@ -114,22 +289,64 @@ void YellowNotes::popupTrayMenu() 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"); + GtkMenuItem *new_yellow = gtk_menu_item_new_with_label(_("New Note")); + GtkMenuItem *show_notes = gtk_menu_item_new_with_label(_("Show Notes")); + GtkMenuItem *reload_notes = gtk_menu_item_new_with_label(_("Reload Notes")); GtkWidget *sep = gtk_separator_menu_item_new(); - GtkMenuItem *quit = gtk_menu_item_new_with_label("Quit"); + std::list hidden; + std::list h_notes; + std::list::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 *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, show_notes); + gtk_menu_shell_append(tray_menu, reload_notes); gtk_menu_shell_append(tray_menu, sep); + + std::list::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, 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(reload_notes, "activate", on_reload, this); + g_signal_connect(setup, "activate", on_setup, 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() @@ -141,8 +358,64 @@ void YellowNotes::clearNotes() } } +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(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(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::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); + } +} + void YellowNotes::loadNotes() { + gtk_widget_show_all(topLevel()); + clearNotes(); std::string notes_dir = notesDir(); @@ -159,110 +432,327 @@ void YellowNotes::loadNotes() { const auto full_name = entry.path().string(); - if (entry.is_regular_file()) + 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(full_name); + YellowNote *note = new YellowNote(this, full_name); _notes.push_back(note); } } } + + gtk_widget_hide(topLevel()); } -void YellowNotes::newNote() +void YellowNotes::showNotes(void *sender) { - std::string new_note_title = "New Note"; + std::list::const_iterator it = _notes.begin(); + while (it != _notes.end()) { + YellowNote *note = *it; + note->toFront(); + it++; + } + +} + +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) { + std::cout << "hey" << std::endl; + gtk_dialog_response(_dlg, GTK_RESPONSE_OK); + std::string lang = std::string(gtk_combo_box_get_active_id(_langs)); + std::cout << "lang " << lang << std::endl; + setCurrentLang(lang); + gtk_widget_destroy(_dlg); + _dlg = nullptr; + _langs = nullptr; + saveConfig(); + } else { + std::cout << "close button" << std::endl; + } +} + +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"); + + std::string current_lang = currentLang(); + gtk_combo_box_set_active_id(langs, current_lang.c_str()); + + GtkWidget *hbox = gtk_box_new(GtkOrientation::GTK_ORIENTATION_HORIZONTAL, 5); + + gtk_container_add(hbox, langs); + gtk_container_add(content, hbox); + + 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::reloadNotes(void *sender) +{ + std::list::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, "%llu-%d", milliseconds_since_epoch, r); + + std::string new_note_file = buf; std::string notes_dir = notesDir(); - auto filename = [notes_dir](const std::string & title, int i) { + auto filename = [notes_dir](const std::string & fname, int i) { char s[20]; - sprintf(s, " %d", i); - std::string fn = notes_dir + "/" + title + s + ".note"; + sprintf(s, "%d", i); + std::string fn = notes_dir + "/" + fname + "-" + s + ".note"; return fn; }; - int i = 1; - std::filesystem::path p(filename(new_note_title, i)); + 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_title, i)); + p = std::filesystem::path(filename(new_note_file, i)); } - YellowNote *note = new YellowNote(p); + YellowNote *note = new YellowNote(this, p.string()); _notes.push_back(note); note->show(); note->save(); } -void YellowNotes::quit() +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]; +} + +void YellowNotes::setCurrentLang(const std::string &l) +{ + std::string key("lang"); + _cfg.erase(key); + _cfg.insert(std::pair(key, l)); +} + +static int show_notes_timed(void *user_data) +{ + YellowNotes *notes = reinterpret_cast(user_data); + notes->showNotes(nullptr); + return false; +} + +void YellowNotes::topLevelHidden(GtkWidget *sender) +{ + g_timeout_add(500, show_notes_timed, this); +} + +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; + loadConfig(); } YellowNotes::~YellowNotes() { if (_tray_menu) { gtk_widget_destroy(reinterpret_cast(_tray_menu)); } clearNotes(); + gtk_widget_destroy(_toplevel); } //////////////////////////////////////////////////////////////////////////////////////////////////// /// YellowNote implementation //////////////////////////////////////////////////////////////////////////////////////////////////// -YellowNote::YellowNote(const std::string &filename) { +YellowNote::YellowNote(YellowNotes *notes, const std::string &filename) +{ + _notes = notes; _filename = filename; - _title = ""; - _x = -1; - _y = -1; - _width = -1; - _height = -1; + _title = _("New Note"); + _x = 200; + _y = 300; + _width = 300; + _height = 200; _hidden = false; + _editing_title = false; + _save_counter = 0; + _save_id = -1; _moving = false; + _resize_bottom = false; + _resize_edge = false; + _resize_right = false; + + _color = ColorType_t::YELLOW; + _color_changed = false; + + _note_widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_widget_set_can_focus(_note_widget, true); + gtk_window_set_decorated(_note_widget, false); + gtk_window_set_type_hint(_note_widget, GDK_WINDOW_TYPE_HINT_POPUP_MENU); + gtk_window_set_transient_for(_note_widget, _notes->topLevel()); + + _scroll_widget = gtk_scrolled_window_new(nullptr, nullptr); + gtk_widget_set_vexpand(_scroll_widget, true); - _note_widget = gtk_window_new(GtkWindowType::GTK_WINDOW_TOPLEVEL); _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); - _header_evt_box = gtk_event_box_new(); - _note_header = gtk_header_bar_new(); + _evt_box = gtk_event_box_new(); + + _note_header = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5); - 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_widget_set_hexpand(_title_label, true); - //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); + _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(_plus_image, GtkAlign::GTK_ALIGN_END); + g_object_unref(hide_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(_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_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)); - } - + //GdkWindow *window = gdk_window_get_effective_toplevel(gtk_widget_get_window(_note_widget)); + //gdk_window_set_events(window, static_cast(gdk_window_get_events(window) | GDK_VISIBILITY_NOTIFY_MASK)); + //gdk_window_set_override_redirect(window, true); 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); + 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); load(); @@ -271,10 +761,81 @@ YellowNote::YellowNote(const std::string &filename) { 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); 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); +} + +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(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()); @@ -296,21 +857,34 @@ void YellowNote::updateSize() gtk_window_resize(_note_widget, _width, _height); } +void YellowNote::showNote(GtkWidget *sender) +{ + show(); +} + 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::changed() +void YellowNote::changed(GtkWidget *sender) { bool sv = false; + if (_color_changed) { + _color_changed = true; + sv = true; + } + bool hidden = !gtk_widget_is_visible(_note_widget); if (hidden != _hidden) { _hidden = hidden; @@ -344,7 +918,6 @@ void YellowNote::changed() sv = true; } - const char *c_t = gtk_label_get_text (_title_label); if (c_t != nullptr) { std::string t(c_t); @@ -357,42 +930,317 @@ void YellowNote::changed() if (sv) { save(); } } -void YellowNote::size_allocated(GtkAllocation *a) +void YellowNote::size_allocated(GtkWidget *sender, GtkAllocation *a) { - changed(); + changed(sender); } -bool YellowNote::move_begin(GdkEventButton *evt) +void YellowNote::toFront() { - std::cout << "move_begin" << std::endl; - _moving = true; - _x_orig = evt->x_root; - _y_orig = evt->y_root; - return true; + if (_hidden) { + gtk_window_move(_note_widget, _x, _y); + gtk_widget_hide(_note_widget); + } else { + gtk_window_present(_note_widget); + gtk_window_move(_note_widget, _x, _y); + } } -bool YellowNote::move_end(GdkEventButton *evt) +void YellowNote::get_header_screen_coords(int &header_top, int &header_bottom) { - std::cout << "move_end" << std::endl; - _moving = false; - return true; + 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; } -bool YellowNote::moving(GdkEventMotion *evt) +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); +} + +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; + + std::cout << "Changed " << _save_id << std::endl; + _save_counter++; + if (_save_id == -1) { + std::cout << "Starting save timer" << std::endl; + _save_id = _save_counter; + g_timeout_add(1000, on_text_save_timeout, this); + } +} + +bool YellowNote::textSaveTimeout() +{ + if (_save_counter != _save_id) { + std::cout << "Something changed" << std::endl; + _save_id = _save_counter; + g_timeout_add(1000, on_text_save_timeout, this); + return false; + } else { + _save_id = -1; + std::cout << "Saving" << std::endl; + save(); + return false; + } +} + +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))) + +bool YellowNote::move_begin(GtkWidget *sender, GdkEventButton *evt) { int x = evt->x_root; int y = evt->y_root; - int dx = x - _x_orig; - int dy = y - _y_orig; + int header_top, header_bottom; + get_header_screen_coords(header_top, header_bottom); - std::cout << "moving " << x << ", " << y << ", dx = " << dx << ", dy = " << dy << ", _x = " << _x << ", _y = " << y << std::endl; + int frame_bottom, frame_right; + get_frame_screen_coords(frame_bottom, frame_right); - //gtk_window_move(_note_widget, _x + dx, _y + dy); + 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); + + 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 >= 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_widget_show(_title_entry); + gtk_entry_set_text(_title_entry, _title.c_str()); + gtk_widget_grab_focus(_title_entry); + _editing_title = true; + return true; + } + + if (y >= header_top && y <= header_bottom) { + _moving = true; + _x_orig = evt->x; + _y_orig = evt->y; + 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; + } 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); + 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); + 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); + 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); + 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 = 8; + + if (y >= header_top && y <= header_bottom) { + c = gdk_cursor_new(GDK_LEFT_PTR); + } else if (AROUND(x, frame_right) && AROUND(y, frame_bottom)) { + c = gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER); + } else if (AROUND(x, frame_right)) { + c = gdk_cursor_new(GDK_RIGHT_SIDE); + } else if (AROUND(y, frame_bottom)) { + c = gdk_cursor_new(GDK_BOTTOM_SIDE); + } else { + c = nullptr; + } + + gdk_window_set_cursor(window, c); + if (c) g_object_unref(c); + + return false; +} + +#define YELLOWNOTE_VERSION 1 void YellowNote::load() { @@ -409,8 +1257,6 @@ void YellowNote::load() _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)) { @@ -419,15 +1265,27 @@ void YellowNote::load() FILE *f = fopen(_filename.c_str(), "rt"); if (f) { + int version = readInt(f, -1); + _hidden = readInt(f, -1); _x = readInt(f, 200); _y = readInt(f, 200); _width = readInt(f, 300); _height = readInt(f, 200); - int pos = ftell(f); + int color; + color = readInt(f, ColorType_t::YELLOW); + _color = static_cast(color); + char *buf = static_cast(malloc(s)); memset(buf, 0, s); + + fgets(buf, s, f); + _title = buf; + trim(_title); + + memset(buf, 0, s); + int pos = ftell(f); int bytes = s - pos; fread(buf, bytes, 1, f); @@ -437,9 +1295,10 @@ void YellowNote::load() } updateTitle(); - updateHidden(); + updateColor(); updatePosition(); updateSize(); + updateHidden(); _in_transaction = false; } @@ -453,11 +1312,14 @@ void YellowNote::save() std::filesystem::path p(_filename); FILE *f = fopen(_filename.c_str(), "wt"); if (f) { + fprintf(f, "%d\n", YELLOWNOTE_VERSION); 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); + fprintf(f, "%d\n", _color); + fprintf(f, "%s\n", _title.c_str()); GtkTextIter start, end; gtk_text_buffer_get_start_iter(_buffer, &start); @@ -465,7 +1327,7 @@ void YellowNote::save() gchar *text = gtk_text_buffer_get_text(_buffer, &start, &end, false); fprintf(f, "%s", text); fclose(f); - free(text); + g_free(text); } } diff --git a/yellownotes.h b/yellownotes.h index 91ee34f..11d02d5 100644 --- a/yellownotes.h +++ b/yellownotes.h @@ -3,15 +3,43 @@ #include #include +#include + +extern "C" { +#include "gtk-imports.h" +} class YellowNote; +typedef enum { + DARK = 0, + FIRST= DARK, + YELLOW, + ORANGE, + BLUE, + CYAAN, + GREEN, + RED, + GREY, + LAST = GREY +} ColorType_t; + class YellowNotes { private: void *_tray_menu; void *_app; + void *_dlg; + void *_langs; + std::list _notes; + int _font_size; + GtkWindow *_toplevel; + std::unordered_map _cfg; + +private: + void loadConfig(); + void saveConfig(); public: void loadNotes(); @@ -21,11 +49,27 @@ public: std::string imageFile(const char *name); std::string appDir(); std::string notesDir(); + std::string css(ColorType_t type); + int fontSize(); + int iconSize(); + GtkWindow *topLevel(); public: - void popupTrayMenu(); - void newNote(); - void quit(); + void popupTrayMenu(void *sender); + void newNote(void *sender); + void showNotes(void *sender); + void reloadNotes(void *sender); + void quit(void *sender); + void topLevelHidden(GtkWidget *sender); + void setup(void *sender); + void setupClose(GtkWidget *sender); + void setupCancel(GtkWidget *sender); + bool setupDel(GtkWidget *sender, void *evt); + void remove(YellowNote *n); + +public: + std::string currentLang(); + void setCurrentLang(const std::string &l); public: YellowNotes(void *app); @@ -34,12 +78,15 @@ public: #define YELLOWNOTES(obj) reinterpret_cast(obj) #define SIGNAL(type, func, member) \ - static void func(GObject *obj, gpointer user_data) { type *o = reinterpret_cast(user_data); o->member(); } + static void func(GObject *obj, gpointer user_data) { type *o = reinterpret_cast(user_data); o->member(obj); } + +#define BSIGNAL(type, func, member) \ + static gboolean func(GObject *obj, gpointer user_data) { std::cout << user_data << std::endl; type *o = reinterpret_cast(user_data); return o->member(obj); } #define SIGNAL2(type, func, member, argtype) \ - static void func(GObject *obj, void *arg, gpointer user_data) { type *o = reinterpret_cast(user_data); o->member(reinterpret_cast(arg)); } + static void func(GObject *obj, void *arg, gpointer user_data) { type *o = reinterpret_cast(user_data); o->member(obj, reinterpret_cast(arg)); } #define BSIGNAL2(type, func, member, argtype) \ - static gboolean func(GObject *obj, void *arg, gpointer user_data) { type *o = reinterpret_cast(user_data); return o->member(reinterpret_cast(arg)); } + static gboolean func(GObject *obj, void *arg, gpointer user_data) { type *o = reinterpret_cast(user_data); return o->member(obj, reinterpret_cast(arg)); } #endif // YELLOWNOTES_H