summaryrefslogtreecommitdiffabout
path: root/src/napkin.cc
Side-by-side diff
Diffstat (limited to 'src/napkin.cc') (more/less context) (ignore whitespace changes)
-rw-r--r--src/napkin.cc367
1 files changed, 367 insertions, 0 deletions
diff --git a/src/napkin.cc b/src/napkin.cc
new file mode 100644
index 0000000..d9ba0c9
--- a/dev/null
+++ b/src/napkin.cc
@@ -0,0 +1,367 @@
+#include <fcntl.h>
+#include <iostream>
+using std::cerr;
+using std::endl;
+#include <fstream>
+using std::ofstream;
+#include <cstdlib>
+using std::min;
+#include <stdexcept>
+using std::runtime_error;
+#include <list>
+using std::list;
+#include <vector>
+using std::vector;
+#include <string>
+using std::string;
+#include <gtkmm/main.h>
+#include <gtkmm/window.h>
+#include <gtkmm/box.h>
+#include <gtkmm/statusbar.h>
+#include <gtkmm/uimanager.h>
+#include <gtkmm/stock.h>
+#include <gtkmm/toolbar.h>
+#include <gtkmm/filechooserdialog.h>
+#include <gtkmm/messagedialog.h>
+#include <gtkmm/aboutdialog.h>
+#include <napkin/exception.h>
+#include <napkin/util.h>
+#include <napkin/st/decode.h>
+#include <napkin/st/download.h>
+
+#include "db.h"
+#include "sleep_timeline.h"
+#include "dialogs.h"
+#include "sleep_history.h"
+
+#include "config.h"
+
+class napkin_ui : public Gtk::Window {
+ public:
+ Gtk::VBox w_outer_box;
+ Gtk::Statusbar w_status_bar;
+ napkin::gtk::sleep_history_t w_history;
+ Glib::RefPtr<Gtk::UIManager> uiman;
+ Glib::RefPtr<Gtk::ActionGroup> agroup;
+ napkin::db_t db;
+ Glib::RefPtr<Gtk::Action> a_remove;
+
+ napkin_ui()
+ : w_history(db)
+ {
+ static char *ui_info =
+ "<ui>"
+ "<menubar name='menu_bar'>"
+ "<menu action='menu_sleep'>"
+#ifndef NDEBUG
+ "<menu action='menu_sleep_add'>"
+#endif
+ "<menuitem action='sleep_add_from_sleeptracker'/>"
+#ifndef NDEBUG
+ "<menuitem action='sleep_add_from_datafile'/>"
+ "</menu>"
+#endif
+ "<menuitem action='sleep_remove'/>"
+ "<menuitem action='exit'/>"
+ "</menu>"
+ "<menu action='menu_help'>"
+ "<menuitem action='help_about'/>"
+ "</menu>"
+ "</menubar>"
+ "<toolbar action='tool_bar'>"
+ "<toolitem action='sleep_add_from_sleeptracker'/>"
+ "<toolitem action='sleep_remove'/>"
+ "<separator expand='true'/>"
+#ifndef NDEBUG
+ "<toolitem action='debug'/>"
+ "<separator/>"
+#endif
+ "<toolitem action='exit'/>"
+ "</toolbar>"
+ "</ui>";
+ agroup = Gtk::ActionGroup::create();
+ agroup->add(Gtk::Action::create("menu_sleep","Sleep"));
+ agroup->add(Gtk::Action::create("menu_sleep_add","Add"));
+ agroup->add(Gtk::Action::create("sleep_add_from_sleeptracker",Gtk::Stock::CONNECT,
+ "from sleeptracker","import sleeptracker data from watch"),
+ Gtk::AccelKey("<Ctrl>d"),
+ sigc::mem_fun(*this,&napkin_ui::on_sleep_add_from_sleeptracker));
+#ifndef NDEBUG
+ agroup->add(Gtk::Action::create("sleep_add_from_datafile",Gtk::Stock::CONVERT,
+ "from data file","import sleeptracker data stored in a file"),
+ sigc::mem_fun(*this,&napkin_ui::on_sleep_add_from_datafile));
+#endif
+ agroup->add(a_remove=Gtk::Action::create("sleep_remove",Gtk::Stock::REMOVE,
+ "Remove","remove highlighted sleep event from the database"),
+ Gtk::AccelKey("delete"),
+ sigc::mem_fun(*this,&napkin_ui::on_remove));
+ agroup->add(Gtk::Action::create("exit",Gtk::Stock::QUIT,"Exit","Exit "PACKAGE_NAME),
+ Gtk::AccelKey("<control>w"),
+ sigc::mem_fun(*this,&napkin_ui::on_quit));
+ agroup->add(Gtk::Action::create("menu_help","Help"));
+ agroup->add(Gtk::Action::create("help_about",Gtk::Stock::ABOUT,
+ "About","About this program"),
+ sigc::mem_fun(*this,&napkin_ui::on_help_about));
+#ifndef NDEBUG
+ agroup->add(Gtk::Action::create("debug",Gtk::Stock::INFO,"Debug","debug action"),
+ sigc::mem_fun(*this,&napkin_ui::on_debug));
+#endif
+ uiman = Gtk::UIManager::create();
+ uiman->insert_action_group(agroup);
+ add_accel_group(uiman->get_accel_group());
+ uiman->add_ui_from_string(ui_info);
+ Gtk::Widget * mb = uiman->get_widget("/menu_bar");
+ if(mb)
+ w_outer_box.pack_start(*mb,Gtk::PACK_SHRINK);
+ Gtk::Widget * tb = uiman->get_widget("/tool_bar");
+ if(tb) {
+ static_cast<Gtk::Toolbar*>(tb)->set_toolbar_style(Gtk::TOOLBAR_ICONS);
+ w_outer_box.pack_start(*tb,Gtk::PACK_SHRINK);
+ }
+ w_outer_box.pack_start(w_history,true/*expand*/,true/*fill*/);
+ w_outer_box.pack_end(w_status_bar,false/*expand*/,false/*fill*/);
+ add(w_outer_box);
+ set_title(PACKAGE_STRING);
+ set_default_size(800,600);
+ show_all();
+ w_status_bar.push(" "PACKAGE_STRING);
+
+ refresh_data();
+
+ w_history.signal_cursor_changed().connect(
+ sigc::mem_fun(*this,&napkin_ui::on_history_cursor_changed));
+ on_history_cursor_changed();
+ w_history.signal_double_click().connect(
+ sigc::mem_fun(*this,&napkin_ui::on_history_double_click));
+ }
+
+ void on_help_about() {
+ Gtk::AboutDialog about;
+ about.set_authors(vector<string>(1,"Michael Krelin <hacker@klever.net>"));
+ about.set_copyright("© 2008 Klever Group");
+ extern const char *COPYING;
+ about.set_license(COPYING);
+ about.set_program_name(PACKAGE_NAME);
+ about.set_version(VERSION);
+ about.set_website("http://kin.klever.net/");
+ about.set_website_label("Klever Internet Nothings");
+ about.set_comments("The Sleeptracker PRO watch support program");
+ about.run();
+ }
+
+ void on_history_double_click() {
+ napkin::hypnodata_ptr_t hd = w_history.get_current();
+ if(!hd) return;
+ napkin::gtk::hypnoinfo_dialog_t hid(*this);
+ hid.update_data(hd);
+ hid.add_button(Gtk::Stock::OK,Gtk::RESPONSE_OK);
+ hid.run();
+ }
+
+ void refresh_data() {
+ load_data("ORDER BY s_alarm DESC");
+ }
+
+ void load_data(const string& sql) {
+ list<napkin::hypnodata_ptr_t> hds;
+ db.load(hds,sql);
+ w_history.set_data(hds);
+ }
+
+ void on_history_cursor_changed() {
+ a_remove->set_sensitive(w_history.get_current());
+ }
+
+ void on_remove() {
+ napkin::hypnodata_ptr_t hd = w_history.get_current();
+ if(!hd) return;
+ napkin::gtk::hypnoinfo_dialog_t hid(*this);
+ hid.update_data(hd);
+ hid.add_button("Remove from the database",Gtk::RESPONSE_OK);
+ hid.add_button(Gtk::Stock::CANCEL,Gtk::RESPONSE_CANCEL);
+ if(hid.run() == Gtk::RESPONSE_OK) {
+ db.remove(*hd); // TODO: handle error
+ refresh_data();
+ }
+ }
+
+ void on_quit() {
+ hide();
+ }
+
+ void import_data(const napkin::hypnodata_ptr_t& hd) {
+ napkin::gtk::hypnoinfo_dialog_t hid(*this);
+ hid.update_data(hd);
+ hid.add_button("Add to the database",Gtk::RESPONSE_OK);
+ hid.add_button(Gtk::Stock::CANCEL,Gtk::RESPONSE_CANCEL);
+ if(hid.run() == Gtk::RESPONSE_OK) {
+ try {
+ db.store(*hd);
+ refresh_data();
+ }catch(napkin::exception_db& nedb) {
+ Gtk::MessageDialog md(*this,
+ string("Failed to add data to the database... ")+nedb.what(),
+ false/*use_markup*/,Gtk::MESSAGE_ERROR,Gtk::BUTTONS_OK,
+ true/*modal*/);
+ md.run();
+ }
+ }
+ }
+
+ class st_download_t : public Gtk::Dialog {
+ public:
+ Gtk::Label hint, attempt, error;
+ int nattempt;
+ napkin::hypnodata_ptr_t rv;
+
+ st_download_t(Gtk::Window& w)
+ : Gtk::Dialog("Importing data from watch",w,true/*modal*/,false/*use separator*/),
+ hint("\nImporting data from the sleeptracker...\n\n"
+ "Set your watch to the 'data' screen "
+ " and connect to the compuer, if you haven't yet.",0.5,0.5),
+ attempt("",1,0.5), error("",0,0.5),
+ nattempt(1), fd(-1)
+ {
+ Gtk::VBox *vb = get_vbox();
+ vb->set_spacing(10);
+ hint.set_justify(Gtk::JUSTIFY_CENTER);
+ vb->pack_start(hint,Gtk::PACK_SHRINK,5);
+ vb->pack_start(attempt);
+ vb->pack_start(error);
+ add_button("Cancel",Gtk::RESPONSE_CANCEL);
+ vb->show_all();
+ }
+ ~st_download_t() {
+ if(!(fd<0)) close(fd);
+ }
+
+ void on_map() {
+ Gtk::Dialog::on_map();
+ initiate_attempt();
+ }
+
+ void initiate_attempt() {
+ Glib::signal_timeout().connect_seconds(
+ sigc::mem_fun(*this,&st_download_t::try_watch),
+ 1);
+ }
+ void show_error(const napkin::exception& e) {
+ error.set_use_markup(true);
+ error.set_markup(string()+
+ "<span color='red'>"+
+ e.what()+"</span>");
+ }
+ void next_attempt() {
+ char tmp[128];
+ snprintf(tmp,sizeof(tmp),"Trying again, attempt #%d",++nattempt);
+ attempt.set_text(tmp);
+ }
+
+ int fd;
+ char buffer[512];
+ size_t rb;
+
+ bool try_watch() {
+ try {
+ fd = napkin::sleeptracker::download_initiate(getenv("SLEEPTRACKER_PORT"));
+ Glib::signal_timeout().connect_seconds(
+ sigc::mem_fun(*this,&st_download_t::try_data),
+ 1);
+ return false;
+ }catch(napkin::exception_sleeptracker& nest) {
+ show_error(nest);
+ }
+ next_attempt();
+ return true;
+ }
+
+ bool try_data() {
+ try {
+ try {
+ rb = napkin::sleeptracker::download_finish(fd,buffer,sizeof(buffer));
+ }catch(napkin::exception_st_port& nestp) {
+ fd = -1;
+ show_error(nestp);
+ next_attempt();
+ initiate_attempt();
+ return false;
+ }
+ rv = napkin::sleeptracker::decode(buffer,rb);
+ response(Gtk::RESPONSE_OK);
+ }catch(napkin::exception_st_data_envelope& neste) {
+ show_error(neste);
+ next_attempt();
+ initiate_attempt();
+ }catch(napkin::exception_sleeptracker& nest) {
+ show_error(nest);
+ }
+ return false;
+ }
+ };
+
+ void on_sleep_add_from_sleeptracker() {
+ st_download_t sd(*this);
+ if(sd.run()==Gtk::RESPONSE_OK && sd.rv ) {
+ sd.hide();
+#ifndef NDEBUG
+ {
+ ofstream dfile(
+ (db.datadir+"/raw-"+napkin::strftime("%Y-%m-%d.st",time(0))).c_str(),
+ std::ios::binary|std::ios::out|std::ios::trunc);
+ if(dfile)
+ dfile.write(sd.buffer,sd.rb);
+ dfile.close();
+ }
+#endif
+ import_data(sd.rv);
+ }
+ }
+
+#ifndef NDEBUG
+ void on_sleep_add_from_datafile() {
+ Gtk::FileChooserDialog d("Please select a file",
+ Gtk::FILE_CHOOSER_ACTION_OPEN);
+ d.set_transient_for(*this);
+ d.add_button(Gtk::Stock::CANCEL,Gtk::RESPONSE_CANCEL);
+ d.add_button(Gtk::Stock::OPEN,Gtk::RESPONSE_OK);
+ Gtk::FileFilter stfiles;
+ stfiles.set_name("Sleeptracker files");
+ stfiles.add_pattern("*.st");
+ d.add_filter(stfiles);
+ Gtk::FileFilter allfiles;
+ allfiles.set_name("All files");
+ allfiles.add_pattern("*");
+ d.add_filter(allfiles);
+ if(d.run()==Gtk::RESPONSE_OK) {
+ d.hide();
+
+ int fd = open(d.get_filename().c_str(),O_RDONLY);
+ if(fd<0)
+ throw napkin::exception("failed to open() data");
+ unsigned char buffer[512];
+ size_t rb = read(fd,buffer,sizeof(buffer));
+ close(fd);
+ if( (rb==(size_t)-1) || rb==sizeof(buffer))
+ throw napkin::exception("error reading datafile");
+ napkin::hypnodata_ptr_t hd = napkin::sleeptracker::decode(buffer,rb);
+ import_data(hd);
+ }
+ }
+#endif
+
+#ifndef NDEBUG
+ void on_debug() {
+ }
+#endif
+};
+
+int main(int argc,char**argv) {
+ try {
+ Gtk::Main m(argc,argv);
+ napkin_ui hui;
+ m.run(hui);
+ return 0;
+ }catch(std::exception& e) {
+ cerr << "oops: " << e.what() << endl;
+ }
+}