475 lines
14 KiB
C
475 lines
14 KiB
C
/*
|
|
* $Id: wmstickynotes.c 11 2009-02-21 04:11:47Z hnc $
|
|
*
|
|
* Copyright (C) 2009 Heath Caldwell <hncaldwell@gmail.com>
|
|
*
|
|
*/
|
|
|
|
#include <gtk/gtk.h>
|
|
#include <gdk/gdkx.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <errno.h>
|
|
#include <dirent.h>
|
|
#include <getopt.h>
|
|
|
|
#include "wmstickynotes.h"
|
|
#include "wmstickynotes.xpm"
|
|
#include "delete_button.xpm"
|
|
#include "resize_button.xpm"
|
|
#include "config.h"
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/extensions/shape.h>
|
|
|
|
GdkColormap *colormap;
|
|
|
|
/* The highest note id used so far (this is used when making a new note so
|
|
* that no ids are clobbered */
|
|
long int highest_note_id = 0;
|
|
|
|
/* The current note that the popup menu was shown for */
|
|
Note *current_note;
|
|
|
|
|
|
|
|
void usage()
|
|
{
|
|
printf("Usage: wmstickynotes [options]\n");
|
|
printf("\toptions:\n");
|
|
printf("\t-d [dir], --directory=[dir]\tSet directory in which to store notes\n");
|
|
printf("\t\t\t\t\tDefaults to $HOME/%s\n", default_wmstickynotes_dir);
|
|
printf("\t-v, --version\tPrint version information\n");
|
|
printf("\t-h, --help\tPrint usage\n");
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
GtkWidget *window;
|
|
GtkWidget *box;
|
|
GdkColor color;
|
|
XWMHints mywmhints;
|
|
GtkWidget *main_button;
|
|
GdkPixmap *main_button_pixmap;
|
|
GdkBitmap *main_button_mask;
|
|
GtkWidget *main_button_box;
|
|
GtkWidget *color_menu;
|
|
GtkWidget *item;
|
|
GtkWidget *label;
|
|
GtkWidget *color_box;
|
|
GtkWidget *hbox;
|
|
GdkColor gcolor;
|
|
char *wmstickynotes_dir = NULL;
|
|
gboolean use_default_dir = TRUE;
|
|
int option_index = 0;
|
|
int i = 0;
|
|
|
|
struct option long_options[] = {
|
|
{"directory", required_argument, 0, 'd'},
|
|
{"version", no_argument, 0, 'v'},
|
|
{"help", no_argument, 0, 'h'},
|
|
{0, 0, 0, 0}};
|
|
|
|
for(
|
|
i = getopt_long(argc, argv, "d:vh", long_options, &option_index);
|
|
i >= 0;
|
|
i = getopt_long(argc, argv, "d:vh", long_options, &option_index)
|
|
) {
|
|
switch(i) {
|
|
case 'd':
|
|
wmstickynotes_dir = optarg;
|
|
use_default_dir = FALSE;
|
|
break;
|
|
case 'v':
|
|
printf("%s\n", PACKAGE_STRING);
|
|
printf("Copyright (C) 2009 %s\n", PACKAGE_BUGREPORT);
|
|
return 0;
|
|
case 'h':
|
|
usage();
|
|
return 0;
|
|
default:
|
|
usage();
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
umask(077);
|
|
|
|
if(use_default_dir) {
|
|
wmstickynotes_dir = calloc(
|
|
strlen(default_wmstickynotes_dir) +
|
|
strlen(getenv("HOME")) + 2, sizeof(char));
|
|
strcpy(wmstickynotes_dir, getenv("HOME"));
|
|
strcat(wmstickynotes_dir, "/");
|
|
strcat(wmstickynotes_dir, default_wmstickynotes_dir);
|
|
}
|
|
|
|
if(chdir(wmstickynotes_dir)) {
|
|
if(errno == ENOENT) {
|
|
if(mkdir(wmstickynotes_dir, 0777)) {
|
|
fprintf(stderr, "Couldn't make directory: %s\n", wmstickynotes_dir);
|
|
exit(1);
|
|
}
|
|
if(chdir(wmstickynotes_dir)) {
|
|
fprintf(stderr, "Couldn't change to directory: %s\n", wmstickynotes_dir);
|
|
exit(1);
|
|
}
|
|
} else {
|
|
fprintf(stderr, "Couldn't change to directory: %s\n", wmstickynotes_dir);
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
if(use_default_dir) free(wmstickynotes_dir);
|
|
|
|
gtk_init(&argc, &argv);
|
|
|
|
colormap = gdk_colormap_new(gdk_visual_get_system(), TRUE);
|
|
|
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_default_size(GTK_WINDOW(window), 64, 64);
|
|
|
|
box = gtk_event_box_new();
|
|
gtk_container_add(GTK_CONTAINER (window), box);
|
|
|
|
gdk_color_parse ("#fafafa", &color);
|
|
gtk_widget_modify_bg(box, GTK_STATE_NORMAL, &color);
|
|
|
|
main_button_pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL, colormap, &main_button_mask, NULL, wmstickynotes_xpm);
|
|
main_button = gtk_image_new_from_pixmap(main_button_pixmap, main_button_mask);
|
|
main_button_box = gtk_event_box_new();
|
|
gtk_container_add(GTK_CONTAINER(main_button_box), main_button);
|
|
gtk_container_add(GTK_CONTAINER(box), main_button_box);
|
|
|
|
color_menu = gtk_menu_new();
|
|
|
|
for(i=0; i < num_color_schemes; i++) {
|
|
item = gtk_menu_item_new();
|
|
label = gtk_label_new(color_schemes[i].name);
|
|
color_box = gtk_event_box_new();
|
|
gtk_widget_set_size_request(color_box, 15, -1);
|
|
hbox = gtk_hbox_new(FALSE, 4);
|
|
|
|
gdk_color_parse(color_schemes[i].top, &gcolor);
|
|
gtk_widget_modify_bg(color_box, GTK_STATE_NORMAL, &gcolor);
|
|
gtk_widget_modify_bg(color_box, GTK_STATE_PRELIGHT, &gcolor);
|
|
|
|
gtk_container_add(GTK_CONTAINER(item), hbox);
|
|
gtk_box_pack_start(GTK_BOX(hbox), color_box, FALSE, FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
|
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(color_menu), item);
|
|
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(new_note_from_menu), &color_schemes[i]);
|
|
}
|
|
|
|
gtk_widget_show_all(GTK_WIDGET(color_menu));
|
|
gtk_widget_show_all(window);
|
|
|
|
mywmhints.initial_state = WithdrawnState;
|
|
mywmhints.icon_window = GDK_WINDOW_XWINDOW(box->window);
|
|
mywmhints.icon_x = 0;
|
|
mywmhints.icon_y = 0;
|
|
mywmhints.window_group = GDK_WINDOW_XWINDOW(window->window);
|
|
mywmhints.flags = StateHint | IconWindowHint | IconPositionHint | WindowGroupHint;
|
|
|
|
XSetWMHints(GDK_DISPLAY(), GDK_WINDOW_XWINDOW(window->window), &mywmhints);
|
|
|
|
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);
|
|
g_signal_connect(G_OBJECT(main_button_box), "button-press-event", G_CALLBACK(main_button_pressed), color_menu);
|
|
|
|
read_old_notes();
|
|
gtk_main();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void delete_note(GtkWidget *widget, Note *note)
|
|
{
|
|
char *filename;
|
|
asprintf(&filename, "%d", note->id);
|
|
unlink(filename);
|
|
free(note);
|
|
}
|
|
|
|
void save_note(GtkWidget *widget, Note *note)
|
|
{
|
|
FILE *file;
|
|
char *filename;
|
|
GtkTextBuffer *text_buffer;
|
|
GtkTextIter start;
|
|
GtkTextIter end;
|
|
gchar *text;
|
|
|
|
text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(note->text_widget));
|
|
gtk_text_buffer_get_start_iter(text_buffer, &start);
|
|
gtk_text_buffer_get_end_iter(text_buffer, &end);
|
|
|
|
text = gtk_text_buffer_get_text(text_buffer, &start, &end, FALSE);
|
|
|
|
asprintf(&filename, "%d", note->id);
|
|
file = fopen(filename, "w");
|
|
free(filename);
|
|
|
|
fprintf(
|
|
file, "%d,%d,%d,%d,%d,%d,%s\n%s",
|
|
note->x, note->y, note->width, note->height, 0, 0, note->scheme->name, text);
|
|
fclose(file);
|
|
|
|
g_free(text);
|
|
}
|
|
|
|
gboolean note_configure_event(GtkWidget *window, GdkEventConfigure *event, Note *note)
|
|
{
|
|
note->x = event->x;
|
|
note->y = event->y;
|
|
note->width = event->width;
|
|
note->height = event->height;
|
|
save_note(window, note);
|
|
return FALSE;
|
|
}
|
|
|
|
void bar_pressed(GtkWidget *widget, GdkEventButton *event, Note *note)
|
|
{
|
|
gtk_window_begin_move_drag(GTK_WINDOW(note->window), event->button, event->x_root, event->y_root, event->time);
|
|
}
|
|
|
|
void resize_button_pressed(GtkWidget *widget, GdkEventButton *event, Note *note)
|
|
{
|
|
gtk_window_begin_resize_drag(GTK_WINDOW(note->window), GDK_WINDOW_EDGE_SOUTH_EAST, event->button, event->x_root, event->y_root, event->time);
|
|
}
|
|
|
|
void delete_button_pressed(GtkWidget *widget, GdkEventButton *event, GtkWidget *window)
|
|
{
|
|
if(event->button != 1) return;
|
|
|
|
gtk_widget_destroy(window);
|
|
}
|
|
|
|
void main_button_pressed(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
|
|
{
|
|
if(event->button == 1) {
|
|
create_note(NULL, &color_schemes[0]);
|
|
} else if(event->button == 3) {
|
|
gtk_menu_popup(GTK_MENU(user_data), NULL, NULL, NULL, NULL, event->button, event->time);
|
|
}
|
|
}
|
|
|
|
void create_note(Note *old_note, ColorScheme *scheme)
|
|
{
|
|
GtkWidget *window;
|
|
GtkWidget *vbox;
|
|
GtkWidget *top_hbox;
|
|
GtkWidget *mid_hbox;
|
|
GtkWidget *bottom_bar;
|
|
GtkWidget *bottom_hbox;
|
|
GtkWidget *top_bar;
|
|
GtkWidget *delete_button;
|
|
GdkPixmap *delete_button_pixmap;
|
|
GdkBitmap *delete_button_mask;
|
|
GtkWidget *resize_button;
|
|
GdkPixmap *resize_button_pixmap;
|
|
GdkBitmap *resize_button_mask;
|
|
GtkTextBuffer *text_buffer;
|
|
|
|
Note *note;
|
|
|
|
note = old_note ? old_note : malloc(sizeof(Note));
|
|
|
|
if(!old_note) {
|
|
highest_note_id++;
|
|
note->id = highest_note_id;
|
|
note->scheme = scheme;
|
|
}
|
|
|
|
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
|
gtk_window_set_decorated(GTK_WINDOW(window), FALSE);
|
|
gtk_window_set_default_size(GTK_WINDOW(window), 150, 150);
|
|
|
|
if(!old_note) {
|
|
note->text_widget = gtk_text_view_new_with_buffer(NULL);
|
|
}
|
|
text_buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(note->text_widget));
|
|
|
|
note->window = window;
|
|
|
|
vbox = gtk_vbox_new(FALSE, 0);
|
|
top_hbox = gtk_hbox_new(FALSE, 0);
|
|
mid_hbox = gtk_hbox_new(FALSE, 0);
|
|
bottom_hbox = gtk_hbox_new(FALSE, 0);
|
|
top_bar = gtk_label_new("");
|
|
note->top_bar_box = gtk_event_box_new();
|
|
gtk_widget_set_size_request(top_bar, -1, 10);
|
|
bottom_bar = gtk_label_new("");
|
|
gtk_widget_set_size_request(bottom_bar, -1, 8);
|
|
|
|
delete_button_pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL, colormap, &delete_button_mask, NULL, delete_button_xpm);
|
|
delete_button = gtk_image_new_from_pixmap(delete_button_pixmap, delete_button_mask);
|
|
note->delete_button_box = gtk_event_box_new();
|
|
gtk_widget_set_size_request(note->delete_button_box, 10, 10);
|
|
|
|
resize_button_pixmap = gdk_pixmap_colormap_create_from_xpm_d(NULL, colormap, &resize_button_mask, NULL, resize_button_xpm);
|
|
resize_button = gtk_image_new_from_pixmap(resize_button_pixmap, resize_button_mask);
|
|
note->resize_button_box = gtk_event_box_new();
|
|
|
|
set_note_color(note, note->scheme);
|
|
|
|
gtk_container_add(GTK_CONTAINER(window), vbox);
|
|
gtk_container_add(GTK_CONTAINER(note->top_bar_box), top_bar);
|
|
gtk_container_add(GTK_CONTAINER(note->delete_button_box), delete_button);
|
|
gtk_container_add(GTK_CONTAINER(note->resize_button_box), resize_button);
|
|
gtk_box_pack_start(GTK_BOX(top_hbox), note->top_bar_box, TRUE, TRUE, 0);
|
|
gtk_box_pack_start(GTK_BOX(top_hbox), note->delete_button_box, FALSE, FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(mid_hbox), note->text_widget, TRUE, TRUE, 2);
|
|
gtk_box_pack_start(GTK_BOX(bottom_hbox), bottom_bar, TRUE, TRUE, 0);
|
|
gtk_box_pack_start(GTK_BOX(bottom_hbox), note->resize_button_box, FALSE, FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), top_hbox, FALSE, FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), mid_hbox, TRUE, TRUE, 0);
|
|
gtk_box_pack_start(GTK_BOX(vbox), bottom_hbox, FALSE, FALSE, 0);
|
|
|
|
gtk_widget_show_all(window);
|
|
|
|
if(old_note) {
|
|
gtk_window_resize(GTK_WINDOW(window), old_note->width, old_note->height);
|
|
gtk_window_move(GTK_WINDOW(window), old_note->x, old_note->y);
|
|
} else {
|
|
gtk_window_get_position(GTK_WINDOW(window), &(note->x), &(note->y));
|
|
gtk_window_get_size(GTK_WINDOW(window), &(note->width), &(note->height));
|
|
}
|
|
|
|
g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(delete_note), note);
|
|
g_signal_connect(G_OBJECT(window), "configure-event", G_CALLBACK(note_configure_event), note);
|
|
g_signal_connect(G_OBJECT(note->delete_button_box), "button-press-event", G_CALLBACK(delete_button_pressed), window);
|
|
g_signal_connect(G_OBJECT(note->resize_button_box), "button-press-event", G_CALLBACK(resize_button_pressed), note);
|
|
g_signal_connect(G_OBJECT(text_buffer), "changed", G_CALLBACK(save_note), note);
|
|
g_signal_connect(G_OBJECT(note->top_bar_box), "button-press-event", G_CALLBACK(bar_pressed), note);
|
|
g_signal_connect(G_OBJECT(note->text_widget), "populate-popup", G_CALLBACK(populate_note_popup), note);
|
|
}
|
|
|
|
void read_old_notes()
|
|
{
|
|
Note *note;
|
|
GtkTextBuffer *text_buffer;
|
|
GtkTextIter iter;
|
|
DIR *dir = opendir(".");
|
|
FILE *file;
|
|
struct dirent *entry;
|
|
int reserved1;
|
|
int reserved2;
|
|
int i;
|
|
char buffer[256];
|
|
|
|
rewinddir(dir);
|
|
while((entry = readdir(dir)) != NULL) {
|
|
/* Check if it is a valid note name */
|
|
for(i=0; entry->d_name[i]; i++) {
|
|
if(entry->d_name[i] < '0' || entry->d_name[i] > '9') break;
|
|
}
|
|
if(i < strlen(entry->d_name)) continue;
|
|
|
|
file = fopen(entry->d_name, "r");
|
|
note = malloc(sizeof(Note));
|
|
|
|
note->id = atoi(entry->d_name);
|
|
if(note->id > highest_note_id) highest_note_id = note->id;
|
|
|
|
fscanf(file, "%d,%d,%d,%d,%d,%d,",
|
|
&(note->x), &(note->y), &(note->width), &(note->height),
|
|
&reserved1, &reserved2);
|
|
|
|
/* Get color name */
|
|
fgets(buffer, 256, file);
|
|
/* Replace the newline with a null char */
|
|
buffer[strlen(buffer) - 1] = '\0';
|
|
|
|
for(i=num_color_schemes; i > 0; i--) {
|
|
if(!strcmp(color_schemes[i].name, buffer)) break;
|
|
}
|
|
note->scheme = &color_schemes[i];
|
|
|
|
text_buffer = gtk_text_buffer_new(NULL);
|
|
while(fgets(buffer, 256, file)) {
|
|
gtk_text_buffer_get_end_iter(text_buffer, &iter);
|
|
gtk_text_buffer_insert(text_buffer, &iter, buffer, -1);
|
|
}
|
|
|
|
note->text_widget = gtk_text_view_new_with_buffer(text_buffer);
|
|
|
|
create_note(note, note->scheme);
|
|
|
|
fclose(file);
|
|
}
|
|
|
|
closedir(dir);
|
|
}
|
|
|
|
void populate_note_popup(GtkTextView *entry, GtkMenu *menu, Note *note)
|
|
{
|
|
GtkWidget *color_menu;
|
|
GtkWidget *color_item;
|
|
GtkWidget *item;
|
|
GtkWidget *label;
|
|
GtkWidget *color_box;
|
|
GtkWidget *hbox;
|
|
GdkColor gcolor;
|
|
int i;
|
|
|
|
color_menu = gtk_menu_new();
|
|
color_item = gtk_menu_item_new_with_label("Color");
|
|
|
|
gtk_menu_item_set_submenu(GTK_MENU_ITEM(color_item), color_menu);
|
|
gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), color_item);
|
|
|
|
current_note = note;
|
|
for(i=0; i < num_color_schemes; i++) {
|
|
item = gtk_menu_item_new();
|
|
label = gtk_label_new(color_schemes[i].name);
|
|
color_box = gtk_event_box_new();
|
|
gtk_widget_set_size_request(color_box, 15, -1);
|
|
hbox = gtk_hbox_new(FALSE, 4);
|
|
|
|
gdk_color_parse(color_schemes[i].top, &gcolor);
|
|
gtk_widget_modify_bg(color_box, GTK_STATE_NORMAL, &gcolor);
|
|
gtk_widget_modify_bg(color_box, GTK_STATE_PRELIGHT, &gcolor);
|
|
|
|
gtk_container_add(GTK_CONTAINER(item), hbox);
|
|
gtk_box_pack_start(GTK_BOX(hbox), color_box, FALSE, FALSE, 0);
|
|
gtk_box_pack_start(GTK_BOX(hbox), label, TRUE, TRUE, 0);
|
|
|
|
gtk_menu_shell_append(GTK_MENU_SHELL(color_menu), item);
|
|
g_signal_connect(G_OBJECT(item), "activate", G_CALLBACK(set_current_note_color), &color_schemes[i]);
|
|
}
|
|
|
|
gtk_widget_show_all(GTK_WIDGET(menu));
|
|
}
|
|
|
|
void set_current_note_color(GtkMenuItem *menuitem, ColorScheme *scheme)
|
|
{
|
|
set_note_color(current_note, scheme);
|
|
save_note(NULL, current_note);
|
|
}
|
|
|
|
void new_note_from_menu(GtkMenuItem *menuitem, ColorScheme *scheme)
|
|
{
|
|
create_note(NULL, scheme);
|
|
}
|
|
|
|
void set_note_color(Note *note, ColorScheme *scheme)
|
|
{
|
|
GdkColor gcolor;
|
|
|
|
note->scheme = scheme;
|
|
|
|
gdk_color_parse(scheme->top, &gcolor);
|
|
gtk_widget_modify_bg(note->top_bar_box, GTK_STATE_NORMAL, &gcolor);
|
|
gtk_widget_modify_bg(note->delete_button_box, GTK_STATE_NORMAL, &gcolor);
|
|
|
|
gdk_color_parse(scheme->background, &gcolor);
|
|
gtk_widget_modify_base(note->text_widget, GTK_STATE_NORMAL, &gcolor);
|
|
gtk_widget_modify_bg(note->window, GTK_STATE_NORMAL, &gcolor);
|
|
gtk_widget_modify_bg(note->resize_button_box, GTK_STATE_NORMAL, &gcolor);
|
|
}
|