pax_global_header00006660000000000000000000000064140131740320014505gustar00rootroot0000000000000052 comment=f00b80d86300397b52c3cf7d8dfc8de5cdee921d gsimplecal-2.2/000077500000000000000000000000001401317403200134705ustar00rootroot00000000000000gsimplecal-2.2/.gitignore000066400000000000000000000004331401317403200154600ustar00rootroot00000000000000*.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/config.h.in~ src/stamp-h1 src/gsimplecal gsimplecal-2.2/AUTHORS000066400000000000000000000014051401317403200145400ustar00rootroot00000000000000Code and stuff: * Dmitry Medvinsky * Kim Hempel * Paul Vint * Ryan Quinlan * Johan Sjöblom * Unit 193 * yauhen-l * dionisiovj Bugs and ideas: * Sergei Sarbash * Julien Valroff * Iffan Arzanul Haq * Thomas Koch * Rick Nekus * Robert Orzanna Packages: - Arch Linux: * Victor Feight * jsteel - Gentoo Linux: * Joshua Saddler * maelnor - Fedora Linux: * Filip Pytloun - Debian: * Julien Valroff gsimplecal-2.2/COPYING000066400000000000000000000027311401317403200145260ustar00rootroot00000000000000Copyright (c) 2009-2021, 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. gsimplecal-2.2/ChangeLog000066400000000000000000000034431401317403200152460ustar00rootroot000000000000002021-02-17: v2.2: - Fix building when sysctl.h does not exist * Update clock each second if seconds are rendered 2014-12-12: v2.1: - Fix jumping to today when mark_today option is false + Vi-style dates navigation 2013-11-11: v2.0: + Use GTK3 by default (pass `--enable-gtk2` to `./configure` to use GTK2) + Try system-wide config if user config not present * Do not necessarily require procfs to be mounted 2012-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 gsimplecal-2.2/Makefile.am000066400000000000000000000000221401317403200155160ustar00rootroot00000000000000SUBDIRS = src doc gsimplecal-2.2/NEWS000066400000000000000000000000001401317403200141550ustar00rootroot00000000000000gsimplecal-2.2/README000066400000000000000000000000551401317403200143500ustar00rootroot00000000000000gsimplecal - lightweight gui calendar applet gsimplecal-2.2/README.rst000066400000000000000000000020721401317403200151600ustar00rootroot00000000000000============ Gsimplecal ============ Gsimplecal is a lightweight calendar applet written in C++ using GTK. 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 gsimplecal-2.2/autogen.sh000077500000000000000000000000421401317403200154650ustar00rootroot00000000000000#!/bin/sh autoreconf -i -s -f -v gsimplecal-2.2/configure.ac000066400000000000000000000044771401317403200157720ustar00rootroot00000000000000AC_PREREQ([2.65]) AC_INIT([gsimplecal], [2.2], [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]) # Support silent build rules. # Disable by either passing `--disable-silent-rules` to `configure` # or passing V=1 to `make`. AM_SILENT_RULES([yes]) # Checks for programs. AC_LANG([C++]) AC_PROG_CXX AC_PROG_INSTALL PKG_PROG_PKG_CONFIG # Checks for libraries. # GTK AC_ARG_ENABLE(gtk2, AS_HELP_STRING([--enable-gtk2], [Build against GTK2. By default gsimplecal uses GTK3, but if you set this flag, it will use GTK2 instead.]), [enable_gtk2=$enableval], [enable_gtk2=no] ) AM_CONDITIONAL([ENABLE_GTK2], [test x"$enable_gtk2" = x"yes"]) AM_COND_IF([ENABLE_GTK2], [GTK_VERSION=gtk+-2.0], [GTK_VERSION=gtk+-3.0]) AC_DEFINE([HAVE_GTK_BOX_NEW], [], [Have gtk_box_new function]) AM_COND_IF([ENABLE_GTK2], [AC_DEFINE([HAVE_GTK_BOX_NEW], [0], [])], [AC_DEFINE([HAVE_GTK_BOX_NEW], [1], [])]) PKG_CHECK_MODULES(GTK, [$GTK_VERSION]) AC_SUBST(GTK_CFLAGS) AC_SUBST(GTK_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 \ sys/sysctl.h \ signal.h \ limits.h]) # Checks for typedefs, structures, and compiler characteristics. # Checks for library functions. AC_CHECK_FUNCS([atexit \ execl \ fork \ ftok \ getexecname \ gettimeofday \ kill \ readlink \ semctl \ semget \ setenv \ signal \ strlcpy \ sysctl]) # Flags AM_CPPFLAGS="-Wall" AM_LDFLAGS="-Wl,--as-needed" AC_SUBST(AM_CPPFLAGS) AC_SUBST(AM_LDFLAGS) AC_CONFIG_FILES([Makefile doc/Makefile src/Makefile]) AC_OUTPUT gsimplecal-2.2/doc/000077500000000000000000000000001401317403200142355ustar00rootroot00000000000000gsimplecal-2.2/doc/Makefile.am000066400000000000000000000000711401317403200162670ustar00rootroot00000000000000man1_MANS = $(PACKAGE_NAME).1 EXTRA_DIST = $(man1_MANS) gsimplecal-2.2/doc/gsimplecal.1000066400000000000000000000163061401317403200164450ustar00rootroot00000000000000.TH GSIMPLECAL 1 "2014\-12\-12" .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, thus making it very easy to integrate anywhere without the need to make some wrapper scripts. .PP It was intentionally made for use with tint2 panel to be launched upon clock click, but of course it will work with any other panel, or no panel at all. For example you can bind it to some hotkey in you window manager config. .PP You may also configure .B gsimplecal to display different world time zones clocks. See the \fICONFIGURATION\fP section for details. .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 configuration file. The file is first searched in .nh $XDG_CONFIG_HOME/gsimplecal/config. Usually that will be .nh \fB~/.config/gsimplecal/config\fP. If found, it is used. If not found, system-wide configuration is searched in all the .nh \fB$XDG_CONFIG_DIRS/gsimplecal/config\fP locations. .SS Sample config file .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 = .br clock_label = BST .br clock_tz = :Europe/London .br clock_label = EDT .br clock_tz = :US/Eastern .br clock_label = CDT .br clock_tz = :US/Central .SS Config options description .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 time zone 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 time zone. .br If you omit the value for clock_tz, local time will be shown. .br For how to specify different time zone see \fIman timezone\fP. Usually it is a string in the format ":{Area}/{Location}", but really is a colon followed by the relative path to file that stores timezone information. Usually the directory containing the files is \fI/usr/share/zoneinfo\fP, so just look at the directory listing and pick the files you need. .SH KEYBOARD ACCELERATORS .PP You may use the following keyboard accelerators while gsimplecal window has a focus (not yet configurable): .TP 5 \fBEscape\fP, \fBCtrl+w\fP, \fBCtrl+q\fP Close the window. .TP 5 \fBn\fP Switch to the next month. .TP 5 \fBp\fP Switch to the previous month. .TP 5 \fBN\fP Jump one year forward. .TP 5 \fBP\fP Jump one year backward. .TP 5 \fBhjkl\fP Vi-style dates navigation: \fBh\fP -> left \fBj\fP -> down \fBk\fP -> up \fBl\fP -> right .TP 5 \fBg\fP, \fBHome\fP Jump to the current date. .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) gsimplecal-2.2/src/000077500000000000000000000000001401317403200142575ustar00rootroot00000000000000gsimplecal-2.2/src/Boxable.cpp000066400000000000000000000002651401317403200163420ustar00rootroot00000000000000#include #include "Boxable.hpp" void Boxable::addToBox(GtkWidget* box) { if (widget) { gtk_box_pack_start(GTK_BOX(box), widget, false, false, 0); } } gsimplecal-2.2/src/Boxable.hpp000066400000000000000000000003051401317403200163420ustar00rootroot00000000000000#ifndef BOXABLE_HPP #define BOXABLE_HPP #include class Boxable { public: virtual void addToBox(GtkWidget* box); protected: Boxable() {}; GtkWidget* widget; }; #endif gsimplecal-2.2/src/Calendar.cpp000066400000000000000000000104721401317403200165000ustar00rootroot00000000000000#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); // Store today date to be able to jump to it. gtk_calendar_get_date((GtkCalendar*)widget, &today_year, &today_month, &today_day); if (config->mark_today) { markToday(); g_signal_connect(widget, "month-changed", GCallback(monthChangedCb), (gpointer)this); } g_signal_connect(widget, "day-selected-double-click", GCallback(dayDoubleClickCb), (gpointer)this); gtk_widget_show(widget); } Calendar::~Calendar() { gtk_widget_destroy(widget); } void Calendar::nextYear() { _change(1, 0, 0); } void Calendar::prevYear() { _change(-1, 0, 0); } void Calendar::nextMonth() { _change(0, 1, 0); } void Calendar::prevMonth() { _change(0, -1, 0); } void Calendar::goLeft() { _change(0, 0, -1); } void Calendar::goDown() { _change(0, 0, 7); } void Calendar::goUp() { _change(0, 0, -7); } void Calendar::goRight() { _change(0, 0, 1); } void Calendar::_change(int year_offset, int month_offset, int day_offset) { int year, month, day; gtk_calendar_get_date((GtkCalendar*)widget, (guint*)&year, (guint*)&month, (guint*)&day); unsigned int n_days = _n_days(year, month); day += day_offset; if (day < 1) { day += _n_days(year, month - 1); month_offset--; } else if (day > n_days) { day -= n_days; month_offset++; } 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); gtk_calendar_select_day((GtkCalendar*)widget, day); } unsigned int Calendar::_n_days(unsigned int year, unsigned int month) { if (month == 3 || month == 5 || month == 8 || month == 10) { return 30; } else if (month == 1) { bool leap = (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); if (leap) { return 29; } else { return 28; } } else { return 31; } } 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; } } gsimplecal-2.2/src/Calendar.hpp000066400000000000000000000011711401317403200165010ustar00rootroot00000000000000#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(); void goLeft(); void goDown(); void goUp(); void goRight(); bool markToday(); bool runExternalViewer(); protected: void _change(int year_offset, int month_offset, int day_offset); unsigned int _n_days(unsigned int year, unsigned int month); private: guint today_year; guint today_month; guint today_day; }; #endif gsimplecal-2.2/src/Clock.cpp000066400000000000000000000031431401317403200160170ustar00rootroot00000000000000#include #include #include #include #include "Clock.hpp" #include "Config.hpp" #include "config.h" using namespace std; Clock::Clock(const string& label, const string& timezone) { this->timezone = timezone; #if HAVE_GTK_BOX_NEW widget = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); #else widget = gtk_hbox_new(false, 0); #endif 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); } gsimplecal-2.2/src/Clock.hpp000066400000000000000000000007221401317403200160240ustar00rootroot00000000000000#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 gsimplecal-2.2/src/Config.cpp000066400000000000000000000126331401317403200161750ustar00rootroot00000000000000#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() { path = NULL; setDefaults(); if (findPath()) { readFile(); } } Config::~Config() { if (path) { g_free(path); } for (unsigned int i = 0; i < clocks.size(); i++) { delete clocks[i]; } } const gchar *const Config::getPath() { return path; } void Config::setDefaults() { 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::findPath() { // First try XDG_CONFIG_HOME (usually `~/.config`). const gchar *const user_config_dir = g_get_user_config_dir(); path = g_build_filename(user_config_dir, "gsimplecal", "config", NULL); if (g_file_test(path, G_FILE_TEST_EXISTS)) { return true; } g_free(path); path = NULL; // Then try XDG_CONFIG_DIRS (or `/etc/xdg`). const gchar *const *const system_config_dirs = g_get_system_config_dirs(); for (const gchar *const *dir = system_config_dirs; dir && *dir; dir++) { path = g_build_filename(*dir, "gsimplecal", "config", NULL); if (g_file_test(path, G_FILE_TEST_EXISTS)) { return true; } g_free(path); path = NULL; } // Otherwise, bail. return false; } void Config::readFile() { std::ifstream file(path); if (!file.is_open()) { return; } string line; while (!file.eof()) { getline(file, line); parseLine(line); } } 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); } gsimplecal-2.2/src/Config.hpp000066400000000000000000000022451401317403200162000ustar00rootroot00000000000000#ifndef CONFIG_HPP #define CONFIG_HPP #include #include #include using namespace std; typedef struct _ClockInfo { string label; string timezone; } ClockInfo; class Config { public: static Config* getInstance(); const gchar *const getPath(); // 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); gchar* path; protected: explicit Config(); ~Config(); void setDefaults(); bool findPath(); void readFile(); void parseLine(string line); void addOption(string var, string val); }; #endif gsimplecal-2.2/src/MainWindow.cpp000066400000000000000000000142331401317403200170420ustar00rootroot00000000000000#include #include #include "MainWindow.hpp" #include "Config.hpp" #include "Calendar.hpp" #include "Timezones.hpp" #include "config.h" 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; } bool goLeftCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->goLeft(); } return true; } bool goDownCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->goDown(); } return true; } bool goUpCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->goUp(); } return true; } bool goRightCallback(GtkAccelGroup *group, GObject *obj, guint keyval, GdkModifierType mod, gpointer user_data) { if (user_data) { ((MainWindow*)user_data)->goRight(); } 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 #if HAVE_GTK_BOX_NEW children_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10); #else children_box = gtk_vbox_new(false, 10); #endif 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_KEY_Escape, 0, closeCallback}, {GDK_KEY_q, GDK_CONTROL_MASK, closeCallback}, {GDK_KEY_w, GDK_CONTROL_MASK, closeCallback}, {GDK_KEY_n, GDK_SHIFT_MASK, nextYearCallback}, {GDK_KEY_p, GDK_SHIFT_MASK, prevYearCallback}, {GDK_KEY_n, 0, nextMonthCallback}, {GDK_KEY_p, 0, prevMonthCallback}, {GDK_KEY_h, 0, goLeftCallback}, {GDK_KEY_j, 0, goDownCallback}, {GDK_KEY_k, 0, goUpCallback}, {GDK_KEY_l, 0, goRightCallback}, {GDK_KEY_g, 0, goTodayCallback}, {GDK_KEY_Home, 0, goTodayCallback}}; for (int key = 0; key < sizeof(keys) / sizeof(Shortcut); 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() { g_signal_emit_by_name(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(); } void MainWindow::goLeft() { calendar->goLeft(); } void MainWindow::goDown() { calendar->goDown(); } void MainWindow::goUp() { calendar->goUp(); } void MainWindow::goRight() { calendar->goRight(); } gsimplecal-2.2/src/MainWindow.hpp000066400000000000000000000012641401317403200170470ustar00rootroot00000000000000#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(); void goLeft(); void goDown(); void goUp(); void goRight(); Calendar* calendar; protected: GtkWidget* widget; GtkWidget* children_box; Timezones* timezones; }; struct Shortcut { int key; int modifier; bool (*func)(GtkAccelGroup*, GObject*, guint, GdkModifierType, void*); }; #endif gsimplecal-2.2/src/Makefile.am000066400000000000000000000005051401317403200163130ustar00rootroot00000000000000AM_CPPFLAGS = @GTK_CFLAGS@ LIBS = @GTK_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 gsimplecal-2.2/src/Timezones.cpp000066400000000000000000000020621401317403200167400ustar00rootroot00000000000000#include #include #include "Config.hpp" #include "Timezones.hpp" #include "Clock.hpp" #include "config.h" Timezones::Timezones() { #if HAVE_GTK_BOX_NEW widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); #else widget = gtk_vbox_new(false, 0); #endif 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); } } gsimplecal-2.2/src/Timezones.hpp000066400000000000000000000004351401317403200167470ustar00rootroot00000000000000#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 gsimplecal-2.2/src/Unique.cpp000066400000000000000000000066601401317403200162410ustar00rootroot00000000000000#include #include #include #include #include #include #if HAVE_SYSCTL #include #endif #include #include "Unique.hpp" #include "config.h" Unique::Unique(const char *const path) { char pathname[PATH_MAX + 1]; if (path) { getPathnameFromFile(path, pathname); } else { getPathnameFromExe(pathname); } // Get unique key for semaphore. semaphore_key = ftok(pathname, 1); if (semaphore_key == -1) { throw new UniqueException("ftok failed"); } } 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); } } void Unique::getPathnameFromFile(const char *const path, char* pathname) { #if HAVE_STRLCPY strlcpy(pathname, path, PATH_MAX + 1); #else strncpy(pathname, path, PATH_MAX); pathname[PATH_MAX] = '\0'; #endif } void Unique::getPathnameFromExe(char* pathname) { #if HAVE_GETEXECNAME && HAVE_STRLCPY // Try getexecname (Solaris). const char* execname = getexecname(); strlcpy(pathname, execname, PATH_MAX + 1); #elif HAVE_SYSCTL && defined(CTL_KERN) && defined(KERN_PROC) && defined(KERN_PROC_PATHNAME) // Try sysctl call (FreeBSD). int mib[4]; mib[0] = CTL_KERN; mib[1] = KERN_PROC; mib[2] = KERN_PROC_PATHNAME; mib[3] = -1; size_t cb = PATH_MAX; sysctl(mib, 4, pathname, &cb, NULL, 0); #else // Try reading procfs links. const char *const paths[] = { "/proc/self/exe", // Linux "/proc/curproc/exe", // NetBSD "/proc/curproc/file", // FreeBSD with procfs NULL }; for (const char *const *path = paths; *path; path++) { pathname[0] = 0; int bytes = readlink(*path, pathname, sizeof(*pathname) * PATH_MAX); if (bytes > 0) { if (bytes > PATH_MAX - 1) { bytes = PATH_MAX; } pathname[bytes] = '\0'; break; } } #endif } gsimplecal-2.2/src/Unique.hpp000066400000000000000000000013551401317403200162420ustar00rootroot00000000000000#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(const char* const path); ~Unique(); bool isRunning(); void kill(); void kill(int signal_id); void signal(int signal_id); void start(); void stop(); protected: key_t semaphore_key; private: void getPathnameFromFile(const char *const path, char* pathname); void getPathnameFromExe(char* pathname); }; #endif gsimplecal-2.2/src/gsimplecal.cpp000066400000000000000000000066151401317403200171130ustar00rootroot00000000000000#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; } } Config* config = Config::getInstance(); Unique* unique = new Unique(config->getPath()); 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); 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(); g_signal_connect(main_window->getWindow(), "destroy", GCallback(destroy), NULL); if (config->show_timezones) { // Only update as often if seconds are rendered. unsigned int interval = 1000; // msec if (config->clock_format.find("%S") == std::string::npos) { interval = 30 * 1000; } g_timeout_add(interval, (GSourceFunc)time_handler, NULL); } if (config->close_on_unfocus) { g_signal_connect(main_window->getWindow(), "focus-out-event", GCallback(gtk_widget_destroy), main_window->getWindow()); } gtk_main(); unique->stop(); return 0; }