pax_global_header00006660000000000000000000000064117414720430014515gustar00rootroot0000000000000052 comment=6aa09661275d8dd6bed471789072b30ec1af2a34 dmedvinsky-gsimplecal-6aa0966/000077500000000000000000000000001174147204300163465ustar00rootroot00000000000000dmedvinsky-gsimplecal-6aa0966/.gitignore000066400000000000000000000004121174147204300203330ustar00rootroot00000000000000*.o aclocal.m4 autom4te.cache/ config.log config.status config.guess config.sub configure depcomp install-sh missing src/.deps/ Makefile Makefile.in doc/Makefile doc/Makefile.in src/Makefile src/Makefile.in src/config.h src/config.h.in src/stamp-h1 src/gsimplecal dmedvinsky-gsimplecal-6aa0966/AUTHORS000066400000000000000000000012171174147204300174170ustar00rootroot00000000000000Code and stuff: * Dmitry Medvinsky * Kim Hempel * Paul Vint * Ryan Quinlan Bugs and ideas: * Sergei Sarbash * Julien Valroff * Iffan Arzanul Haq * Thomas Koch * Rick Nekus Packages: - Arch Linux: * Victor Feight * jsteel - Gentoo Linux: * Joshua Saddler * maelnor - Fedora Linux: * Filip Pytloun - Debian: * Julien Valroff dmedvinsky-gsimplecal-6aa0966/COPYING000066400000000000000000000027311174147204300174040ustar00rootroot00000000000000Copyright (c) 2009-2012, Dmitry Medvinsky Some rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The names of the contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. dmedvinsky-gsimplecal-6aa0966/ChangeLog000066400000000000000000000026101174147204300201170ustar00rootroot000000000000002012-04-12: v1.6: + Add option to force LANG for changing weekdays order 2012-03-26: v1.5: - Fix compilation on BSD - Fix current executable path on BSD - Fix exception when gsimplecal gets a PID > SEMVMX 2012-02-18: v1.4: + Add option to close gsimplecal when it loses focus 2012-01-23: v1.3: + Add options for mainwindow position fine-tuning 2011-12-06: v1.2: + Add `--version` and `--help` flags 2011-09-19: v1.1: + Add new option: mainwindow_resizable 2011-09-05: v1.0: - Fix bug with external viewer when mark_today is false + Add ability to show week numbers — show_week_numbers option 2011-08-24: v0.9: - Fix license text not included in distribution - Fix last `=` was treated as separator of key/value in config, not the first + Add ability to execute external viewer upon doubleclick on a date 2011-04-03: v0.8: - Fix segmentation fault in the clock + Add keyboard bindings to go to the current date 2011-03-26: v0.7: + Mark today even when selecting some other day 2010-10-18: v0.6: + Add next_month and prev_month command line arguments + Add more config options (see man page) 2010-07-13: v0.5: + Add keyboard shortcuts to close the gsimplecal * Add program crashes handling + Add keyboard shortcuts to switch months and years 2010-05-29: v0.4: + Use semaphores instead of libunique 2010-03-27: v0.3: + Migrate to autotools 2010-03-22: v0.2: + World clocks 2009-07-04: v0.1: + Calendar dmedvinsky-gsimplecal-6aa0966/Makefile.am000066400000000000000000000000221174147204300203740ustar00rootroot00000000000000SUBDIRS = src doc dmedvinsky-gsimplecal-6aa0966/NEWS000066400000000000000000000000001174147204300170330ustar00rootroot00000000000000dmedvinsky-gsimplecal-6aa0966/README000066400000000000000000000000551174147204300172260ustar00rootroot00000000000000gsimplecal - lightweight gui calendar applet dmedvinsky-gsimplecal-6aa0966/README.rst000066400000000000000000000020721174147204300200360ustar00rootroot00000000000000============ Gsimplecal ============ Gsimplecal is a lightweight calendar applet written in C++ using GTK2. It was intentionally made for use with tint2_ panel in the openbox_ environment to be launched upon clock click, but of course it will work without it. In fact, binding the gsimplecal to some hotkey in you window manager will probably make you happy. The thing is that when it is started it first shows up, when you run it again it closes the running instance. In that way it is very easy to integrate anywhere. No need to write some wrapper scripts or whatever. Also, you can configure it to not only show the calendar, but also display multiple clocks for different world timezones. Read the manual page for the details (there is info about keyboard controls as well!). Feel free to ask me anything and do not hesitate to post an issue. I don't get a lot of questions and I feel kind of lonely. ;) .. _tint2: http://code.google.com/p/tint2/ .. _openbox: http://openbox.org/wiki/Main_Page .. image:: https://github.com/dmedvinsky/gsimplecal/raw/gh-pages/g/scrot2.png dmedvinsky-gsimplecal-6aa0966/autogen.sh000077500000000000000000000000421174147204300203430ustar00rootroot00000000000000#!/bin/sh autoreconf -i -s -f -v dmedvinsky-gsimplecal-6aa0966/autoreconf.sh000077500000000000000000000000471174147204300210530ustar00rootroot00000000000000#!/bin/sh autoreconf --force --install dmedvinsky-gsimplecal-6aa0966/configure.ac000066400000000000000000000037131174147204300206400ustar00rootroot00000000000000AC_PREREQ([2.65]) AC_INIT([gsimplecal], [1.6], [https://github.com/dmedvinsky/gsimplecal/issues], [gsimplecal], [https://github.com/dmedvinsky/gsimplecal]) AM_INIT_AUTOMAKE([-Wall -Werror foreign]) AC_CONFIG_SRCDIR([src/gsimplecal.cpp]) AC_CONFIG_HEADERS([src/config.h]) # Checks for programs. AC_LANG([C++]) AC_PROG_CXX AC_PROG_INSTALL PKG_PROG_PKG_CONFIG # Checks for libraries. PKG_CHECK_MODULES([GTK2], [gtk+-2.0]) AC_SUBST(GTK2_CFLAGS) AC_SUBST(GTK2_LIBS) # Checks for header files. AC_CHECK_HEADERS([iostream \ string \ sstream \ fstream \ vector \ cstdlib \ stdlib.h \ sys/time.h \ sys/types.h \ sys/ipc.h \ sys/sem.h \ signal.h \ limits.h]) # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions. AC_CHECK_FUNCS([atexit \ gettimeofday \ setenv \ signal \ kill \ semget \ semctl \ ftok \ readlink \ fork \ execl]) # Checks for host AC_CANONICAL_HOST case $host_os in *linux* | *Linux*) AC_DEFINE([PROC_SELF_PATH], ["/proc/self/exe"], [Path to symlink in proc that leads to current executable.]) ;; *bsd* | *BSD*) AC_DEFINE([PROC_SELF_PATH], ["/proc/curproc/file"], [Path to symlink in proc that leads to current executable.]) ;; *) AC_MSG_ERROR([Your platform $host_os is not currently supported. Please, report this to the issue tracker.]) ;; esac # Flags AM_CPPFLAGS="-Wall" AM_LDFLAGS="-Wl,--as-needed" AC_SUBST(AM_CFLAGS) AC_SUBST(AM_LDFLAGS) AC_CONFIG_FILES([Makefile doc/Makefile src/Makefile]) AC_OUTPUT dmedvinsky-gsimplecal-6aa0966/doc/000077500000000000000000000000001174147204300171135ustar00rootroot00000000000000dmedvinsky-gsimplecal-6aa0966/doc/Makefile.am000066400000000000000000000000721174147204300211460ustar00rootroot00000000000000man1_MANS = $(PACKAGE_NAME).1 EXTRA_DIST = $(man1_MANS) dmedvinsky-gsimplecal-6aa0966/doc/gsimplecal.1000066400000000000000000000144521174147204300213230ustar00rootroot00000000000000.TH GSIMPLECAL 1 "2012\-03\-26" .SH NAME gsimplecal \- lightweight calendar applet .SH SYNOPSIS .B gsimplecal [\-h|\-\-help|\-v|\-\-version|next_month|prev_month] .SH DESCRIPTION This manual page documents the usage of the .B gsimplecal command. .PP .B gsimplecal is a lightweight calendar applet. When it is started, it first shows up, when you run it again, it closes the running instance. It was intentionally made for use with tint2 panel to be launched upon clock click, but of course it will work without it, you can bind it to some hotkey in you window manager, for example. .PP Also, you may configure gsimplecal to display different world timezones clocks. See the \fICONFIGURATION\fP section to get to know how to. .SH COMMANDS AND OPTIONS .TP 5 \fB\-v, \-\-version\fP Print the program name and version to stdout, then exit with code 0. .TP 5 \fB\-h, \-\-help\fP Print the short usage help to stderr, then exit with error code 2. .TP 5 \fBprev_month, next_month\fP If the program is not running, simply run it. If the program is running, change currently displayed month. .PP If no options and commands are given, the program is toggled, i.e. if it is running it stops, otherwise it starts. .SH CONFIGURATION .PP To configure the application you should manually create the file .nh \fB$XDG_CONFIG_HOME/gsimplecal/config\fP (usually it will be .nh ~/.config/gsimplecal/config) with contents like this: .IP show_calendar = 1 .br show_timezones = 1 .br mark_today = 1 .br show_week_numbers = 0 .br close_on_unfocus = 0 .br external_viewer = sunbird \-showdate "%Y\-%m\-%d" .br clock_format = %a %d %b %H:%M .br force_lang = en_US.utf8 .br mainwindow_decorated = 0 .br mainwindow_keep_above = 1 .br mainwindow_sticky = 0 .br mainwindow_skip_taskbar = 1 .br mainwindow_resizable = 0 .br mainwindow_position = none .br mainwindow_xoffset = 0 .br mainwindow_yoffset = 0 .br clock_label = UTC .br clock_tz = :UTC .br clock_label = Local .br clock_tz = .PP The options are pretty self explanatory, but here is detailed description: .TP 5 \fBshow_calendar\fP: 1 or 0, defaults to 1. Sets whether the calendar should be shown. Most users want this option to be 1. .TP 5 \fBshow_timezones\fP: 1 or 0, defaults to 0. Sets whether the different timezone clocks should be shown. .TP 5 \fBmark_today\fP: 1 or 0, defaults to 1. Sets whether today's date will be marked in the calendar (besides the default selection, i.e. when you click on the other day, today will remain marked somehow, e.g. in bold print). .TP 5 \fBshow_week_numbers\fP: 1 or 0, defaults to 0. Sets whether week numbers are shown in the calendar. .TP 5 \fBclose_on_unfocus\fP: 1 or 0, defaults to 0. Sets whether the calendar will close if the window loses focus. Note that if mainwindow_skip_taskbar is set to 1 then the calendar window may not be given focus upon creation .TP 5 \fBexternal_viewer\fP: string, defaults to empty string. Command line to run when doubleclicking a date. This string is strftime'd (see \fIman strftime\fP for the possible substitutions) and passed to the shell. Thus you can use pipes, redirections, and whatever, I hope. .br Currently the shell is hardcoded to .nh /bin/sh though. I hope that will do for all the users, but if you've got a trouble, please file a ticket (see \fIREPORTING BUGS\fP). .TP 5 \fBclock_format\fP: string Sets the clocks format. Look \fIman strftime\fP for the possible formats. .TP 5 \fBforce_lang\fP: string Overrides the \fILANG\fP environment variable, thus making it possible to change the first day of week, i.e. choose if Monday or Sunday goes first. Basically it's the same as running gsimplecal as LANG=en_GB.utf8 gsimplecal Must be one of \fIlocale -a\fP output. .TP 5 \fBmainwindow_decorated\fP: 1 or 0, defaults to 0. Tells your window manager to decorate or not to decorate the main window. .TP 5 \fBmainwindow_keep_above\fP: 1 or 0, defaults to 1. Sets whether the main window should be placed on top of other windows by your window manager. .TP 5 \fBmainwindow_sticky\fP: 1 or 0, defaults to 0. Tells your window manager to show gsimplecal on all desktops. .TP 5 \fBmainwindow_skip_taskbar\fP: 1 or 0, defaults to 1. Sets whether the main window should be shown in the task list by your panel or window manager. .TP 5 \fBmainwindow_resizable\fP: 1 or 0, defaults to 1. Sets whether your window manager should allow the main window to be resized. If you are using a tiling window manager which supports floating windows, setting this options to 0 will most likely tell your WM not to tile the window. (Tested with XMonad and Awesome). .TP 5 \fBmainwindow_position\fP: mouse|center|none, defaults to mouse. Tells your window manager where to place the gsimplecal window: .TP 10 \fBmouse\fP .br close to the mouse cursor position (this one is useful when you bind gsimplecal on some mouse click command); .TP 10 \fBcenter\fP .br in the center of the screen; .TP 10 \fBnone\fP .br it's up to your window manager to decide, where to place the window (this one is useful when you bind gsimplecal invocation on some hotkey, so you can configure your window manager to place gsimplecal in some predefined position). .TP 5 \fBmainwindow_xoffset\fP and \fBmainwindow_yoffset\fP: integer, default to 0. Allow for main window position fine tuning. Throw an integer at these, and it'll move the window by that number of pixels. .TP 5 \fBclock_label\fP and \fBclock_tz\fP: string These two options should go in pairs and \fBmust\fP be in the order given. .br Each pair creates new clock. The clock_label variable sets the string to be displayed near the clock, the clock_tz sets the timezone. .br If you omit the value for clock_tz, local time will be shown. .br For a list of timezones see \fIman timezone\fP, or \fIls /usr/share/zoneinfo\fP .SH KEYBOARD ACCELERATORS .PP You may use the following keyboard accelerators while gsimplecal window has a focus: .IP Escape, Ctrl+w, Ctrl+q: close the window .br j: switch to the next month .br k: switch to the previous month .br J: jump one year forward .br K: jump one year backward .br g, Home: jump to the current date .PP These are not yet configurable, but I'm working on it. .SH REPORTING BUGS .PP Please, report any issues to the gsimplecal issue tracker, available at: .nh https://github.com/dmedvinsky/gsimplecal/issues .SH AUTHOR Created by Dmitry Medvinsky et al. .SH SEE ALSO tzset(3), strftime(3) dmedvinsky-gsimplecal-6aa0966/src/000077500000000000000000000000001174147204300171355ustar00rootroot00000000000000dmedvinsky-gsimplecal-6aa0966/src/Boxable.cpp000066400000000000000000000002651174147204300212200ustar00rootroot00000000000000#include #include "Boxable.hpp" void Boxable::addToBox(GtkWidget* box) { if (widget) { gtk_box_pack_start(GTK_BOX(box), widget, false, false, 0); } } dmedvinsky-gsimplecal-6aa0966/src/Boxable.hpp000066400000000000000000000003051174147204300212200ustar00rootroot00000000000000#ifndef BOXABLE_HPP #define BOXABLE_HPP #include class Boxable { public: virtual void addToBox(GtkWidget* box); protected: Boxable() {}; GtkWidget* widget; }; #endif dmedvinsky-gsimplecal-6aa0966/src/Calendar.cpp000066400000000000000000000067221174147204300213610ustar00rootroot00000000000000#include #include #include #include #include "Calendar.hpp" #include "Config.hpp" void fork_and_run(char* cmdline) { if (strlen(cmdline)) { if (fork() == 0) { execl("/bin/sh", "/bin/sh", "-c", cmdline, NULL); _exit(0); } } } void monthChangedCb(GtkCalendar *calendar, gpointer cls) { if (cls) { ((Calendar*)cls)->markToday(); } } void dayDoubleClickCb(GtkCalendar *calendar, gpointer cls) { if (cls) { ((Calendar*)cls)->runExternalViewer(); } } Calendar::Calendar() { widget = gtk_calendar_new(); Config* config = Config::getInstance(); g_object_set(widget, "show-heading", true, "show-day-names", true, "show-details", false, "show-week-numbers", config->show_week_numbers, NULL); if (config->mark_today) { // Store today date to be able to mark it after month changes. gtk_calendar_get_date((GtkCalendar*)widget, &today_year, &today_month, &today_day); markToday(); gtk_signal_connect(GTK_OBJECT(widget), "month-changed", GTK_SIGNAL_FUNC(monthChangedCb), (gpointer)this); } gtk_signal_connect(GTK_OBJECT(widget), "day-selected-double-click", GTK_SIGNAL_FUNC(dayDoubleClickCb), (gpointer)this); gtk_widget_show(widget); } Calendar::~Calendar() { gtk_widget_destroy(widget); } void Calendar::nextYear() { _change(1, 0); } void Calendar::prevYear() { _change(-1, 0); } void Calendar::nextMonth() { _change(0, 1); } void Calendar::prevMonth() { _change(0, -1); } void Calendar::_change(int year_offset, int month_offset) { int year, month; gtk_calendar_get_date((GtkCalendar*)widget, (guint*)&year, (guint*)&month, NULL); month += month_offset; if (month > 11) { month = 0; year_offset++; } else if (month < 0) { month = 11; year_offset--; } year += year_offset; gtk_calendar_select_month((GtkCalendar*)widget, (guint)month, (guint)year); } bool Calendar::markToday() { guint year, month; gtk_calendar_get_date((GtkCalendar*)widget, &year, &month, NULL); if (year == today_year && month == today_month) { gtk_calendar_mark_day((GtkCalendar*)widget, today_day); return true; } else { gtk_calendar_unmark_day((GtkCalendar*)widget, today_day); return false; } } void Calendar::goToday() { gtk_calendar_select_month((GtkCalendar*)widget, today_month, today_year); gtk_calendar_select_day((GtkCalendar*)widget, today_day); } bool Calendar::runExternalViewer() { Config* config = Config::getInstance(); size_t len = config->external_viewer.length(); if (len > 0) { int year, month, day; gtk_calendar_get_date((GtkCalendar*)widget, (guint*)&year, (guint*)&month, (guint*)&day); struct tm date; date.tm_year = year - 1900; date.tm_mon = month; date.tm_mday = day; date.tm_sec = date.tm_min = date.tm_hour = date.tm_wday = date.tm_yday = date.tm_isdst = 0; size_t buf_size = len + 64; char* cmd = new char[buf_size]; strftime(cmd, buf_size, config->external_viewer.c_str(), &date); fork_and_run(cmd); delete[] cmd; return true; } else { return false; } } dmedvinsky-gsimplecal-6aa0966/src/Calendar.hpp000066400000000000000000000007351174147204300213640ustar00rootroot00000000000000#ifndef CALENDAR_HPP #define CALENDAR_HPP #include #include "Boxable.hpp" class Calendar : public Boxable { public: Calendar(); ~Calendar(); void nextYear(); void prevYear(); void nextMonth(); void prevMonth(); void goToday(); bool markToday(); bool runExternalViewer(); protected: void _change(int year_offset, int month_offset); private: guint today_year; guint today_month; guint today_day; }; #endif dmedvinsky-gsimplecal-6aa0966/src/Clock.cpp000066400000000000000000000027641174147204300207050ustar00rootroot00000000000000#include #include #include #include #include "Clock.hpp" #include "Config.hpp" using namespace std; Clock::Clock(const string& label, const string& timezone) { this->timezone = timezone; widget = gtk_hbox_new(false, 0); label_label = gtk_label_new(label.c_str()); time_label = gtk_label_new(timezone.c_str()); gtk_box_pack_start(GTK_BOX(widget), label_label, false, false, 10); gtk_box_pack_end(GTK_BOX(widget), time_label, false, false, 10); gtk_widget_show(label_label); gtk_widget_show(time_label); gtk_widget_show(widget); } Clock::~Clock() { gtk_widget_destroy(time_label); gtk_widget_destroy(label_label); gtk_widget_destroy(widget); } void Clock::updateTime(const timeval& time) { string time_text = getTimeForTZ(time, timezone); gtk_label_set_text(GTK_LABEL(time_label), time_text.c_str()); } string Clock::getTimeForTZ(const timeval& time, const string& zone) { struct tm* result; if (zone.length()) { const char* old_tz = getenv("TZ"); setenv("TZ", zone.c_str(), 1); result = localtime(&time.tv_sec); if (old_tz) { setenv("TZ", old_tz, 1); } else { unsetenv("TZ"); } } else { result = localtime(&time.tv_sec); } // format time Config* config = Config::getInstance(); char buffer[64]; strftime(buffer, sizeof(buffer), config->clock_format.c_str(), result); return string(buffer); } dmedvinsky-gsimplecal-6aa0966/src/Clock.hpp000066400000000000000000000007221174147204300207020ustar00rootroot00000000000000#ifndef CLOCK_HPP #define CLOCK_HPP #include #include #include #include "Boxable.hpp" using namespace std; class Clock : public Boxable { public: Clock(const string& label, const string& timezone); ~Clock(); void updateTime(const timeval& time); protected: string timezone; GtkWidget* label_label; GtkWidget* time_label; string getTimeForTZ(const timeval& time, const string& zone); }; #endif dmedvinsky-gsimplecal-6aa0966/src/Config.cpp000066400000000000000000000115561174147204300210560ustar00rootroot00000000000000#include #include #include #include #include #include #include #include "Config.hpp" using namespace std; Config* Config::_instance = NULL; Config* Config::getInstance() { if (_instance == NULL) { _instance = new Config(); atexit(Destroy); } return _instance; } void Config::Destroy() { delete _instance; _instance = NULL; } Config::Config() { getDefaults(); if (getFile()) { readFile(); } } Config::~Config() { for (unsigned int i = 0; i < clocks.size(); i++) { delete clocks[i]; } } void Config::getDefaults() { show_calendar = true; show_timezones = false; clock_format = string("%H:%M"); mainwindow_decorated = false; mainwindow_keep_above = true; mainwindow_sticky = false; mainwindow_skip_taskbar = true; mainwindow_position = GTK_WIN_POS_MOUSE; mainwindow_xoffset = 0; mainwindow_yoffset = 0; mark_today = true; show_week_numbers = false; close_on_unfocus = false; } bool Config::getFile() { // use XDG config dir (~/.config/ usually) char *path_to_config; path_to_config = g_build_filename(g_get_user_config_dir(), "gsimplecal", "config", NULL); if (g_file_test(path_to_config, G_FILE_TEST_EXISTS)) { file.open(path_to_config, ios::in); } g_free(path_to_config); return file.is_open(); } void Config::readFile() { string line; while (!file.eof()) { getline(file, line); parseLine(line); } file.close(); } void Config::parseLine(string line) { if (!line.length() || line[0] == '#') { return; } size_t pos = line.find_first_of('='); if (pos == string::npos) { return; } string var = strip(line.substr(0, pos)); string val = strip(line.substr(pos + 1)); addOption(var, val); } void Config::addOption(string var, string val) { if (var == "show_calendar") { if (!fromString(show_calendar, val)) { show_calendar = true; } } else if (var == "show_timezones") { if (!fromString(show_timezones, val)) { show_timezones = false; } } else if (var == "clock_format") { clock_format = val; } else if (var == "clock_label") { ClockInfo* clockinfo = new ClockInfo; clockinfo->label = val; clocks.push_back(clockinfo); } else if (var == "clock_tz") { clocks[clocks.size() - 1]->timezone = val; } else if (var == "mainwindow_decorated") { if (!fromString(mainwindow_decorated, val)) { mainwindow_decorated = false; } } else if (var == "mainwindow_keep_above") { if (!fromString(mainwindow_keep_above, val)) { mainwindow_keep_above = true; } } else if (var == "mainwindow_sticky") { if (!fromString(mainwindow_sticky, val)) { mainwindow_sticky = false; } } else if (var == "mainwindow_skip_taskbar") { if (!fromString(mainwindow_skip_taskbar, val)) { mainwindow_skip_taskbar = true; } } else if (var == "mainwindow_position") { if (val == "center") { mainwindow_position = GTK_WIN_POS_CENTER; } else if (val == "mouse") { mainwindow_position = GTK_WIN_POS_MOUSE; } else { mainwindow_position = GTK_WIN_POS_NONE; } } else if (var == "mainwindow_xoffset") { stringstream convert(val); if (!(convert >> mainwindow_xoffset)) { mainwindow_xoffset = 0; } } else if (var == "mainwindow_yoffset") { stringstream convert(val); if (!(convert >> mainwindow_yoffset)) { mainwindow_yoffset = 0; } } else if (var == "mainwindow_resizable") { if (!fromString(mainwindow_resizable, val)) { mainwindow_resizable = true; } } else if (var == "mark_today") { if (!fromString(mark_today, val)) { mark_today = true; } } else if (var == "external_viewer") { external_viewer = val; } else if (var == "show_week_numbers") { if (!fromString(show_week_numbers, val)) { show_week_numbers = false; } } else if (var == "close_on_unfocus") { if (!fromString(close_on_unfocus, val)) { close_on_unfocus = false; } } else if (var == "force_lang") { force_lang = val; } } template bool Config::fromString(T& t, const string& s) { istringstream iss(s); return !(iss >> t).fail(); } string Config::strip(const string& s) { string::size_type const first = s.find_first_not_of(' '); if (first == string::npos) { return string(); } return s.substr(first, s.find_last_not_of(' ') - first + 1); } dmedvinsky-gsimplecal-6aa0966/src/Config.hpp000066400000000000000000000022571174147204300210610ustar00rootroot00000000000000#ifndef CONFIG_HPP #define CONFIG_HPP #include #include #include #include #include using namespace std; typedef struct _ClockInfo { string label; string timezone; } ClockInfo; class Config { public: static Config* getInstance(); // options bool show_calendar; bool show_timezones; string clock_format; vector clocks; bool mark_today; string external_viewer; bool show_week_numbers; bool close_on_unfocus; string force_lang; bool mainwindow_decorated; bool mainwindow_keep_above; bool mainwindow_sticky; bool mainwindow_skip_taskbar; bool mainwindow_resizable; GtkWindowPosition mainwindow_position; int mainwindow_xoffset; int mainwindow_yoffset; private: static Config* _instance; static void Destroy(); template bool fromString(T& t, const string& s); string strip(string const& str); std::ifstream file; protected: explicit Config(); ~Config(); void getDefaults(); bool getFile(); void readFile(); void parseLine(string line); void addOption(string var, string val); }; #endif dmedvinsky-gsimplecal-6aa0966/src/MainWindow.cpp000066400000000000000000000113171174147204300217200ustar00rootroot00000000000000#include #include #include "MainWindow.hpp" #include "Config.hpp" #include "Calendar.hpp" #include "Timezones.hpp" bool closeCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->close(); } return true; } bool nextYearCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->nextYear(); } return true; } bool prevYearCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->prevYear(); } return true; } bool nextMonthCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->nextMonth(); } return true; } bool prevMonthCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->prevMonth(); } return true; } bool goTodayCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->goToday(); } return true; } MainWindow::MainWindow() { Config* config = Config::getInstance(); gint xpos, ypos; widget = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_title(GTK_WINDOW(widget), "gsimplecal"); gtk_window_set_decorated(GTK_WINDOW(widget), config->mainwindow_decorated); gtk_window_set_position(GTK_WINDOW(widget), config->mainwindow_position); gtk_window_get_position(GTK_WINDOW(widget), &xpos, &ypos); gtk_window_move(GTK_WINDOW(widget), config->mainwindow_xoffset + xpos, config->mainwindow_yoffset + ypos); gtk_window_set_resizable(GTK_WINDOW(widget), config->mainwindow_resizable); gtk_window_set_keep_above(GTK_WINDOW(widget), config->mainwindow_keep_above); gtk_window_set_skip_taskbar_hint(GTK_WINDOW(widget), config->mainwindow_skip_taskbar); if (config->mainwindow_sticky) { gtk_window_stick(GTK_WINDOW(widget)); } // Create box for child items children_box = gtk_vbox_new(false, 10); calendar = NULL; if (config->show_calendar) { calendar = new Calendar(); calendar->addToBox(children_box); } timezones = NULL; if (config->show_timezones) { timezones = new Timezones(); timezones->updateTime(); timezones->addToBox(children_box); } gtk_container_add(GTK_CONTAINER(widget), children_box); gtk_widget_show(children_box); gtk_widget_show(widget); // Connect keyboard accelerators GtkAccelGroup *accelerators = gtk_accel_group_new(); GClosure *closure; Shortcut keys[] = {{GDK_Escape, 0, closeCallback}, {GDK_q, GDK_CONTROL_MASK, closeCallback}, {GDK_w, GDK_CONTROL_MASK, closeCallback}, {GDK_j, GDK_SHIFT_MASK, nextYearCallback}, {GDK_k, GDK_SHIFT_MASK, prevYearCallback}, {GDK_j, 0, nextMonthCallback}, {GDK_k, 0, prevMonthCallback}, {GDK_g, 0, goTodayCallback}, {GDK_Home, 0, goTodayCallback}}; for (int key = 0; key < 9; key++) { closure = g_cclosure_new(G_CALLBACK(keys[key].func), (gpointer)this, NULL); gtk_accel_group_connect(accelerators, keys[key].key, (GdkModifierType)keys[key].modifier, (GtkAccelFlags)NULL, closure); g_closure_unref(closure); } gtk_window_add_accel_group(GTK_WINDOW(widget), accelerators); } MainWindow::~MainWindow() { if (calendar) { delete calendar; } if (timezones) { delete timezones; } gtk_widget_destroy(children_box); gtk_widget_destroy(widget); } GtkWindow* MainWindow::getWindow() { return GTK_WINDOW(widget); } void MainWindow::updateTime() { if (timezones) { timezones->updateTime(); } } void MainWindow::close() { gtk_signal_emit_by_name(GTK_OBJECT(widget), "destroy"); } void MainWindow::goToday() { calendar->goToday(); } void MainWindow::nextMonth() { calendar->nextMonth(); } void MainWindow::prevMonth() { calendar->prevMonth(); } void MainWindow::nextYear() { calendar->nextYear(); } void MainWindow::prevYear() { calendar->prevYear(); } dmedvinsky-gsimplecal-6aa0966/src/MainWindow.hpp000066400000000000000000000011511174147204300217200ustar00rootroot00000000000000#ifndef MAINWINDOW_HPP #define MAINWINDOW_HPP #include #include "Calendar.hpp" #include "Timezones.hpp" class MainWindow { public: MainWindow(); ~MainWindow(); GtkWindow* getWindow(); void updateTime(); void close(); void nextMonth(); void prevMonth(); void nextYear(); void prevYear(); void goToday(); Calendar* calendar; protected: GtkWidget* widget; GtkWidget* children_box; Timezones* timezones; }; struct Shortcut { int key; int modifier; bool (*func)(GtkAccelGroup*, GObject*, guint, GdkModifierType, void*); }; #endif dmedvinsky-gsimplecal-6aa0966/src/Makefile.am000066400000000000000000000005071174147204300211730ustar00rootroot00000000000000AM_CPPFLAGS = @GTK2_CFLAGS@ LIBS = @GTK2_LIBS@ bin_PROGRAMS = gsimplecal gsimplecal_SOURCES = \ Boxable.cpp \ Boxable.hpp \ Calendar.cpp \ Calendar.hpp \ Clock.cpp \ Clock.hpp \ Config.cpp \ Config.hpp \ gsimplecal.cpp \ MainWindow.cpp \ MainWindow.hpp \ Timezones.cpp \ Timezones.hpp \ Unique.cpp \ Unique.hpp dmedvinsky-gsimplecal-6aa0966/src/Timezones.cpp000066400000000000000000000017051174147204300216210ustar00rootroot00000000000000#include #include #include "Config.hpp" #include "Timezones.hpp" #include "Clock.hpp" Timezones::Timezones() { widget = gtk_vbox_new(false, 0); Config* config = Config::getInstance(); for (unsigned int clock_num = 0; clock_num < config->clocks.size(); clock_num++) { Clock* clock = new Clock(config->clocks[clock_num]->label, config->clocks[clock_num]->timezone); clock->addToBox(widget); clocks.push_back(clock); } gtk_widget_show(widget); } Timezones::~Timezones() { for (unsigned int clock_num = 0; clock_num < clocks.size(); clock_num++) { delete clocks[clock_num]; } gtk_widget_destroy(widget); } void Timezones::updateTime() { struct timeval clock_time; gettimeofday(&clock_time, 0); for (unsigned int clock_num = 0; clock_num < clocks.size(); clock_num++) { clocks[clock_num]->updateTime(clock_time); } } dmedvinsky-gsimplecal-6aa0966/src/Timezones.hpp000066400000000000000000000004351174147204300216250ustar00rootroot00000000000000#ifndef TIMEZONES_HPP #define TIMEZONES_HPP #include #include #include "Boxable.hpp" #include "Clock.hpp" class Timezones : public Boxable { public: Timezones(); ~Timezones(); void updateTime(); protected: vector clocks; }; #endif dmedvinsky-gsimplecal-6aa0966/src/Unique.cpp000066400000000000000000000046671174147204300211240ustar00rootroot00000000000000#include #include #include #include #include #include #include #include #include "Unique.hpp" #include "config.h" Unique::Unique() { // Get path to the current binary. // It's a bit ugly, I guess, to rely on /proc, but it'll do for now. // Also, /proc has different layout on Linux and BSD, so there is ugly // conditional compilation in configure script. char* filename = new char[PATH_MAX + 1]; int bytes = readlink(PROC_SELF_PATH, filename, sizeof(*filename) * PATH_MAX); if (bytes > PATH_MAX - 1) { bytes = PATH_MAX; } filename[bytes] = '\0'; // Get unique key for semaphore. semaphore_key = ftok(filename, 1); if (semaphore_key == -1) { throw new UniqueException("ftok failed"); } delete[] filename; } Unique::~Unique() { } bool Unique::isRunning() { int semid = semget(semaphore_key, 0, 0); if (semid == -1) { return false; } return true; } void Unique::start() { if (!isRunning()) { // Create semaphore; fail if already exists. int semid = semget(semaphore_key, 1, IPC_CREAT | IPC_EXCL | 0660); if (semid == -1) { throw UniqueException("semget failed while creating semaphore"); } // Perform an operation on semaphore so that semctl(GETPID) returns // current PID. struct sembuf ops = {0, 1, 0}; if (semop(semid, &ops, 1) == -1) { throw UniqueException("semop failed"); } } } void Unique::signal(int signal_id) { kill(signal_id); } void Unique::kill() { kill(SIGTERM); } void Unique::kill(int signal_id) { if (isRunning()) { // Get semaphore; fail if not present. int semid = semget(semaphore_key, 1, 0660); if (semid == -1) { throw UniqueException("semget failed while trying to kill"); } // Get the pid from semaphore value (stored before) to kill the process. int pid = semctl(semid, 0, GETPID, 0); if (pid <= 0) { throw UniqueException("semctl(GETPID) failed"); } if (::kill(pid, signal_id)) { throw UniqueException("kill failed"); } } } void Unique::stop() { // Clean up semaphore, if exists. int semid = semget(semaphore_key, 1, 0660); if (semid != -1) { semctl(semid, 0, IPC_RMID, 0); } } dmedvinsky-gsimplecal-6aa0966/src/Unique.hpp000066400000000000000000000011331174147204300211120ustar00rootroot00000000000000#ifndef UNIQUE_HPP #define UNIQUE_HPP #include #include #include using namespace std; class UniqueException : public exception { public: UniqueException(string m="Unique Exception") : msg(m) {} ~UniqueException() throw() {} const char* what() const throw() { return msg.c_str(); } private: string msg; }; class Unique { public: Unique(); ~Unique(); bool isRunning(); void kill(); void kill(int signal_id); void signal(int signal_id); void start(); void stop(); protected: key_t semaphore_key; }; #endif dmedvinsky-gsimplecal-6aa0966/src/gsimplecal.cpp000066400000000000000000000063441174147204300217700ustar00rootroot00000000000000#include #include #include #include #include #include "config.h" #include "MainWindow.hpp" #include "Config.hpp" #include "Unique.hpp" MainWindow* main_window; static void signal_handler(int signal_id) { if (signal_id == SIGTERM) { gtk_main_quit(); } else if (signal_id == SIGUSR1) { main_window->nextMonth(); } else if (signal_id == SIGUSR2) { main_window->prevMonth(); } } static void destroy() { delete main_window; gtk_main_quit(); } static bool time_handler(GtkWidget *widget) { main_window->updateTime(); return true; } static void version() { std::cout << PACKAGE_STRING << std::endl; } static void usage(const char* const name) { std::cerr << "usage: " << name << " [options] [commands]\n" << "\noptions:\n" << "\t-h, --help\n\t\tprint this help text and quit\n" << "\t-v, --version\n\t\tprint version info and quit\n" << "\ncommands:\n" << "\tnext_month, prev_month\n" << "\t\tif program is running, switch the displayed month\n" << "\t\totherwise simply run the program\n" << "\tif no command is given\n" << "\t\tif program is not running, start it\n" << "\t\totherwise stop it\n" ; } int main(int argc, char *argv[]) { if (argc >= 2) { if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) { usage(argv[0]); return 2; } else if (strcmp(argv[1], "-v") == 0 || strcmp(argv[1], "--version") == 0) { version(); return 0; } } Unique* unique = new Unique(); if (unique->isRunning()) { try { if (argc >= 2 && strcmp(argv[1], "next_month") == 0) { unique->signal(SIGUSR1); } else if (argc >= 2 && strcmp(argv[1], "prev_month") == 0) { unique->signal(SIGUSR2); } else { unique->kill(); unique->stop(); } return 0; } catch (UniqueException e) { std::cerr << "Looks like gsimplecal crashed last time." << " Exception message is: " << e.what() << ". Cleaning up." << std::endl; unique->stop(); } } unique->start(); signal(SIGTERM, &signal_handler); signal(SIGUSR1, &signal_handler); signal(SIGUSR2, &signal_handler); signal(SIGCHLD, SIG_IGN); Config* config = Config::getInstance(); if (config->force_lang.length()) { // Must be done before gtk_init call. setenv("LANG", config->force_lang.c_str(), 1); } gtk_init(&argc, &argv); main_window = new MainWindow(); gtk_signal_connect(GTK_OBJECT(main_window->getWindow()), "destroy", GTK_SIGNAL_FUNC(destroy), NULL); if (config->show_timezones) { g_timeout_add(30000, (GSourceFunc)time_handler, NULL); } if (config->close_on_unfocus) { g_signal_connect(G_OBJECT(main_window->getWindow()), "focus-out-event", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(main_window->getWindow())); } gtk_main(); unique->stop(); return 0; }