Move plugins out of src/plugins/ into plugins/ (upper directory).
authorAndriy Grytsenko <andrej@rep.kiev.ua>
Sun, 17 Aug 2014 14:53:00 +0000 (17:53 +0300)
committerAndriy Grytsenko <andrej@rep.kiev.ua>
Sun, 17 Aug 2014 14:53:00 +0000 (17:53 +0300)
This movement makes rebuild of panel itself simpler when you change something.
This also makes sense because plugins are somehow distinct from panel.

681 files changed:
Makefile.am
configure.ac
plugins/Makefile.am [new file with mode: 0644]
plugins/batt/Makefile.am [new file with mode: 0644]
plugins/batt/batt.c [new file with mode: 0644]
plugins/batt/batt_sys.c [new file with mode: 0644]
plugins/batt/batt_sys.h [new file with mode: 0644]
plugins/cpu/Makefile.am [new file with mode: 0644]
plugins/cpu/cpu.c [new file with mode: 0644]
plugins/cpufreq/Makefile.am [new file with mode: 0644]
plugins/cpufreq/cpufreq.c [new file with mode: 0644]
plugins/dclock.c [new file with mode: 0644]
plugins/deskno/Makefile.am [new file with mode: 0644]
plugins/deskno/deskno.c [new file with mode: 0644]
plugins/dirmenu.c [new file with mode: 0644]
plugins/icon.xpm [new file with mode: 0644]
plugins/image.c [new file with mode: 0644]
plugins/indicator/Makefile.am [new file with mode: 0644]
plugins/indicator/indicator.c [new file with mode: 0644]
plugins/kbled/Makefile.am [new file with mode: 0644]
plugins/kbled/kbled.c [new file with mode: 0644]
plugins/launchtaskbar.c [new file with mode: 0644]
plugins/menu.c [new file with mode: 0644]
plugins/monitors/Makefile.am [new file with mode: 0644]
plugins/monitors/monitors.c [new file with mode: 0644]
plugins/netstat/Makefile.am [new file with mode: 0644]
plugins/netstat/devproc.c [new file with mode: 0644]
plugins/netstat/devproc.h [new file with mode: 0644]
plugins/netstat/lxnm_client.c [new file with mode: 0644]
plugins/netstat/lxnm_client.h [new file with mode: 0644]
plugins/netstat/netstat.c [new file with mode: 0644]
plugins/netstat/netstat.h [new file with mode: 0644]
plugins/netstat/nsconfig.h [new file with mode: 0644]
plugins/netstat/passwd_gui.c [new file with mode: 0644]
plugins/netstat/passwd_gui.h [new file with mode: 0644]
plugins/netstat/statusicon.c [new file with mode: 0644]
plugins/netstat/statusicon.h [new file with mode: 0644]
plugins/netstat/wireless.c [new file with mode: 0644]
plugins/netstat/wireless.h [new file with mode: 0644]
plugins/netstatus/COPYING [new file with mode: 0644]
plugins/netstatus/Makefile.am [new file with mode: 0644]
plugins/netstatus/netstatus-dialog.c [new file with mode: 0644]
plugins/netstatus/netstatus-dialog.h [new file with mode: 0644]
plugins/netstatus/netstatus-enums.c [new file with mode: 0644]
plugins/netstatus/netstatus-enums.h [new file with mode: 0644]
plugins/netstatus/netstatus-fallback-pixbuf.h [new file with mode: 0644]
plugins/netstatus/netstatus-icon.c [new file with mode: 0644]
plugins/netstatus/netstatus-icon.h [new file with mode: 0644]
plugins/netstatus/netstatus-iface.c [new file with mode: 0644]
plugins/netstatus/netstatus-iface.h [new file with mode: 0644]
plugins/netstatus/netstatus-sysdeps.c [new file with mode: 0644]
plugins/netstatus/netstatus-sysdeps.h [new file with mode: 0644]
plugins/netstatus/netstatus-util.c [new file with mode: 0644]
plugins/netstatus/netstatus-util.h [new file with mode: 0644]
plugins/netstatus/netstatus.c [new file with mode: 0644]
plugins/pager.c [new file with mode: 0644]
plugins/separator.c [new file with mode: 0644]
plugins/space.c [new file with mode: 0644]
plugins/test.c [new file with mode: 0644]
plugins/thermal/Makefile.am [new file with mode: 0644]
plugins/thermal/thermal.c [new file with mode: 0644]
plugins/tray.c [new file with mode: 0644]
plugins/volume/Makefile.am [new file with mode: 0644]
plugins/volume/volume-impl.c [new file with mode: 0644]
plugins/volume/volume-impl.h [new file with mode: 0644]
plugins/volume/volume.c [new file with mode: 0644]
plugins/volume/volume_xpm.h [new file with mode: 0644]
plugins/volumealsa/Makefile.am [new file with mode: 0644]
plugins/volumealsa/volumealsa.c [new file with mode: 0644]
plugins/weather/Makefile.am [new file with mode: 0644]
plugins/weather/forecast.c [new file with mode: 0644]
plugins/weather/forecast.h [new file with mode: 0644]
plugins/weather/httputil.c [new file with mode: 0644]
plugins/weather/httputil.h [new file with mode: 0644]
plugins/weather/location.c [new file with mode: 0644]
plugins/weather/location.h [new file with mode: 0644]
plugins/weather/logutil.c [new file with mode: 0644]
plugins/weather/logutil.h [new file with mode: 0644]
plugins/weather/weather.c [new file with mode: 0644]
plugins/weather/weatherwidget.c [new file with mode: 0644]
plugins/weather/weatherwidget.h [new file with mode: 0644]
plugins/weather/yahooutil.c [new file with mode: 0644]
plugins/weather/yahooutil.h [new file with mode: 0644]
plugins/wincmd.c [new file with mode: 0644]
plugins/xkb/Makefile.am [new file with mode: 0644]
plugins/xkb/flags/Makefile.am [new file with mode: 0644]
plugins/xkb/flags/ad.png [new file with mode: 0644]
plugins/xkb/flags/ae.png [new file with mode: 0644]
plugins/xkb/flags/af.png [new file with mode: 0644]
plugins/xkb/flags/al.png [new file with mode: 0644]
plugins/xkb/flags/am.png [new file with mode: 0644]
plugins/xkb/flags/ar.png [new file with mode: 0644]
plugins/xkb/flags/ara.png [new file with mode: 0644]
plugins/xkb/flags/at.png [new file with mode: 0644]
plugins/xkb/flags/az.png [new file with mode: 0644]
plugins/xkb/flags/ba.png [new file with mode: 0644]
plugins/xkb/flags/bd.png [new file with mode: 0644]
plugins/xkb/flags/be.png [new file with mode: 0644]
plugins/xkb/flags/ben.png [new file with mode: 0644]
plugins/xkb/flags/bg.png [new file with mode: 0644]
plugins/xkb/flags/bh.png [new file with mode: 0644]
plugins/xkb/flags/br.png [new file with mode: 0644]
plugins/xkb/flags/brai.png [new file with mode: 0644]
plugins/xkb/flags/bt.png [new file with mode: 0644]
plugins/xkb/flags/bw.png [new file with mode: 0644]
plugins/xkb/flags/by.png [new file with mode: 0644]
plugins/xkb/flags/ca.png [new file with mode: 0644]
plugins/xkb/flags/cd.png [new file with mode: 0644]
plugins/xkb/flags/ch.png [new file with mode: 0644]
plugins/xkb/flags/cm.png [new file with mode: 0644]
plugins/xkb/flags/cn.png [new file with mode: 0644]
plugins/xkb/flags/cu.png [new file with mode: 0644]
plugins/xkb/flags/cz.png [new file with mode: 0644]
plugins/xkb/flags/de.png [new file with mode: 0644]
plugins/xkb/flags/dev.png [new file with mode: 0644]
plugins/xkb/flags/dj.png [new file with mode: 0644]
plugins/xkb/flags/dk.png [new file with mode: 0644]
plugins/xkb/flags/dvorak.png [new file with mode: 0644]
plugins/xkb/flags/dz.png [new file with mode: 0644]
plugins/xkb/flags/ee.png [new file with mode: 0644]
plugins/xkb/flags/eg.png [new file with mode: 0644]
plugins/xkb/flags/epo.png [new file with mode: 0644]
plugins/xkb/flags/es.png [new file with mode: 0644]
plugins/xkb/flags/et.png [new file with mode: 0644]
plugins/xkb/flags/eu.png [new file with mode: 0644]
plugins/xkb/flags/fi.png [new file with mode: 0644]
plugins/xkb/flags/fo.png [new file with mode: 0644]
plugins/xkb/flags/fr.png [new file with mode: 0644]
plugins/xkb/flags/gb.png [new file with mode: 0644]
plugins/xkb/flags/ge.png [new file with mode: 0644]
plugins/xkb/flags/gh.png [new file with mode: 0644]
plugins/xkb/flags/gn.png [new file with mode: 0644]
plugins/xkb/flags/gr.png [new file with mode: 0644]
plugins/xkb/flags/hr.png [new file with mode: 0644]
plugins/xkb/flags/hu.png [new file with mode: 0644]
plugins/xkb/flags/ie.png [new file with mode: 0644]
plugins/xkb/flags/il.png [new file with mode: 0644]
plugins/xkb/flags/in.png [new file with mode: 0644]
plugins/xkb/flags/iq.png [new file with mode: 0644]
plugins/xkb/flags/ir.png [new file with mode: 0644]
plugins/xkb/flags/is.png [new file with mode: 0644]
plugins/xkb/flags/it.png [new file with mode: 0644]
plugins/xkb/flags/jo.png [new file with mode: 0644]
plugins/xkb/flags/jp.png [new file with mode: 0644]
plugins/xkb/flags/ke.png [new file with mode: 0644]
plugins/xkb/flags/kg.png [new file with mode: 0644]
plugins/xkb/flags/kh.png [new file with mode: 0644]
plugins/xkb/flags/km.png [new file with mode: 0644]
plugins/xkb/flags/kp.png [new file with mode: 0644]
plugins/xkb/flags/kr.png [new file with mode: 0644]
plugins/xkb/flags/kw.png [new file with mode: 0644]
plugins/xkb/flags/kz.png [new file with mode: 0644]
plugins/xkb/flags/la.png [new file with mode: 0644]
plugins/xkb/flags/latam.png [new symlink]
plugins/xkb/flags/lb.png [new file with mode: 0644]
plugins/xkb/flags/lk.png [new file with mode: 0644]
plugins/xkb/flags/lt.png [new file with mode: 0644]
plugins/xkb/flags/lv.png [new file with mode: 0644]
plugins/xkb/flags/ly.png [new file with mode: 0644]
plugins/xkb/flags/ma.png [new file with mode: 0644]
plugins/xkb/flags/mao.png [new file with mode: 0644]
plugins/xkb/flags/me.png [new file with mode: 0644]
plugins/xkb/flags/mk.png [new file with mode: 0644]
plugins/xkb/flags/ml.png [new file with mode: 0644]
plugins/xkb/flags/mm.png [new file with mode: 0644]
plugins/xkb/flags/mn.png [new file with mode: 0644]
plugins/xkb/flags/mt.png [new file with mode: 0644]
plugins/xkb/flags/mv.png [new file with mode: 0644]
plugins/xkb/flags/mx.png [new file with mode: 0644]
plugins/xkb/flags/nec_vndr-jp.png [new symlink]
plugins/xkb/flags/ng.png [new file with mode: 0644]
plugins/xkb/flags/nl.png [new file with mode: 0644]
plugins/xkb/flags/no.png [new file with mode: 0644]
plugins/xkb/flags/np.png [new file with mode: 0644]
plugins/xkb/flags/om.png [new file with mode: 0644]
plugins/xkb/flags/ph.png [new file with mode: 0644]
plugins/xkb/flags/pk.png [new file with mode: 0644]
plugins/xkb/flags/pl.png [new file with mode: 0644]
plugins/xkb/flags/ps.png [new file with mode: 0644]
plugins/xkb/flags/pt.png [new file with mode: 0644]
plugins/xkb/flags/qa.png [new file with mode: 0644]
plugins/xkb/flags/qc.png [new file with mode: 0644]
plugins/xkb/flags/ro.png [new file with mode: 0644]
plugins/xkb/flags/rs.png [new file with mode: 0644]
plugins/xkb/flags/ru.png [new file with mode: 0644]
plugins/xkb/flags/sa.png [new file with mode: 0644]
plugins/xkb/flags/sd.png [new file with mode: 0644]
plugins/xkb/flags/se.png [new file with mode: 0644]
plugins/xkb/flags/si.png [new file with mode: 0644]
plugins/xkb/flags/sk.png [new file with mode: 0644]
plugins/xkb/flags/sn.png [new file with mode: 0644]
plugins/xkb/flags/so.png [new file with mode: 0644]
plugins/xkb/flags/sr.png [new file with mode: 0644]
plugins/xkb/flags/sy.png [new file with mode: 0644]
plugins/xkb/flags/th.png [new file with mode: 0644]
plugins/xkb/flags/tj.png [new file with mode: 0644]
plugins/xkb/flags/tm.png [new file with mode: 0644]
plugins/xkb/flags/tn.png [new file with mode: 0644]
plugins/xkb/flags/tr.png [new file with mode: 0644]
plugins/xkb/flags/tw.png [new file with mode: 0644]
plugins/xkb/flags/tz.png [new file with mode: 0644]
plugins/xkb/flags/ua.png [new file with mode: 0644]
plugins/xkb/flags/uk.png [new file with mode: 0644]
plugins/xkb/flags/un.png [new file with mode: 0644]
plugins/xkb/flags/us.png [new file with mode: 0644]
plugins/xkb/flags/uy.png [new file with mode: 0644]
plugins/xkb/flags/uz.png [new file with mode: 0644]
plugins/xkb/flags/vn.png [new file with mode: 0644]
plugins/xkb/flags/ye.png [new file with mode: 0644]
plugins/xkb/flags/yu.png [new file with mode: 0644]
plugins/xkb/flags/za.png [new file with mode: 0644]
plugins/xkb/flags_svg/ad.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ae.svg [new file with mode: 0644]
plugins/xkb/flags_svg/af.svg [new file with mode: 0644]
plugins/xkb/flags_svg/al.svg [new file with mode: 0644]
plugins/xkb/flags_svg/am.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ar.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ara.svg [new file with mode: 0644]
plugins/xkb/flags_svg/at.svg [new file with mode: 0644]
plugins/xkb/flags_svg/az.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ba.svg [new file with mode: 0644]
plugins/xkb/flags_svg/bd.svg [new file with mode: 0644]
plugins/xkb/flags_svg/be.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ben.svg [new file with mode: 0644]
plugins/xkb/flags_svg/bg.svg [new file with mode: 0644]
plugins/xkb/flags_svg/bh.svg [new file with mode: 0644]
plugins/xkb/flags_svg/br.svg [new file with mode: 0644]
plugins/xkb/flags_svg/brai.svg [new file with mode: 0644]
plugins/xkb/flags_svg/bt.svg [new file with mode: 0644]
plugins/xkb/flags_svg/bw.svg [new file with mode: 0644]
plugins/xkb/flags_svg/by.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ca.svg [new file with mode: 0644]
plugins/xkb/flags_svg/cd.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ch.svg [new file with mode: 0644]
plugins/xkb/flags_svg/cm.svg [new file with mode: 0644]
plugins/xkb/flags_svg/cn.svg [new file with mode: 0644]
plugins/xkb/flags_svg/cu.svg [new file with mode: 0644]
plugins/xkb/flags_svg/cz.svg [new file with mode: 0644]
plugins/xkb/flags_svg/de.svg [new file with mode: 0644]
plugins/xkb/flags_svg/dev.svg [new file with mode: 0644]
plugins/xkb/flags_svg/dj.svg [new file with mode: 0644]
plugins/xkb/flags_svg/dk.svg [new file with mode: 0644]
plugins/xkb/flags_svg/dvorak.svg [new file with mode: 0644]
plugins/xkb/flags_svg/dz.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ee.svg [new file with mode: 0644]
plugins/xkb/flags_svg/eg.svg [new file with mode: 0644]
plugins/xkb/flags_svg/epo.svg [new file with mode: 0644]
plugins/xkb/flags_svg/es.svg [new file with mode: 0644]
plugins/xkb/flags_svg/et.svg [new file with mode: 0644]
plugins/xkb/flags_svg/fi.svg [new file with mode: 0644]
plugins/xkb/flags_svg/fo.svg [new file with mode: 0644]
plugins/xkb/flags_svg/fr.svg [new file with mode: 0644]
plugins/xkb/flags_svg/gb.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ge.svg [new file with mode: 0644]
plugins/xkb/flags_svg/gh.svg [new file with mode: 0644]
plugins/xkb/flags_svg/gn.svg [new file with mode: 0644]
plugins/xkb/flags_svg/gr.svg [new file with mode: 0644]
plugins/xkb/flags_svg/hr.svg [new file with mode: 0644]
plugins/xkb/flags_svg/hu.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ie.svg [new file with mode: 0644]
plugins/xkb/flags_svg/il.svg [new file with mode: 0644]
plugins/xkb/flags_svg/in.svg [new file with mode: 0644]
plugins/xkb/flags_svg/iq.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ir.svg [new file with mode: 0644]
plugins/xkb/flags_svg/is.svg [new file with mode: 0644]
plugins/xkb/flags_svg/it.svg [new file with mode: 0644]
plugins/xkb/flags_svg/jo.svg [new file with mode: 0644]
plugins/xkb/flags_svg/jp.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ke.svg [new file with mode: 0644]
plugins/xkb/flags_svg/kg.svg [new file with mode: 0644]
plugins/xkb/flags_svg/kh.svg [new file with mode: 0644]
plugins/xkb/flags_svg/km.svg [new file with mode: 0644]
plugins/xkb/flags_svg/kp.svg [new file with mode: 0644]
plugins/xkb/flags_svg/kr.svg [new file with mode: 0644]
plugins/xkb/flags_svg/kw.svg [new file with mode: 0644]
plugins/xkb/flags_svg/kz.svg [new file with mode: 0644]
plugins/xkb/flags_svg/la.svg [new file with mode: 0644]
plugins/xkb/flags_svg/lb.svg [new file with mode: 0644]
plugins/xkb/flags_svg/lk.svg [new file with mode: 0644]
plugins/xkb/flags_svg/lt.svg [new file with mode: 0644]
plugins/xkb/flags_svg/lv.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ly.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ma.svg [new file with mode: 0644]
plugins/xkb/flags_svg/mao.svg [new file with mode: 0644]
plugins/xkb/flags_svg/me.svg [new file with mode: 0644]
plugins/xkb/flags_svg/mk.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ml.svg [new file with mode: 0644]
plugins/xkb/flags_svg/mm.svg [new file with mode: 0644]
plugins/xkb/flags_svg/mn.svg [new file with mode: 0644]
plugins/xkb/flags_svg/mt.svg [new file with mode: 0644]
plugins/xkb/flags_svg/mv.svg [new file with mode: 0644]
plugins/xkb/flags_svg/mx.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ng.svg [new file with mode: 0644]
plugins/xkb/flags_svg/nl.svg [new file with mode: 0644]
plugins/xkb/flags_svg/no.svg [new file with mode: 0644]
plugins/xkb/flags_svg/np.svg [new file with mode: 0644]
plugins/xkb/flags_svg/om.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ph.svg [new file with mode: 0644]
plugins/xkb/flags_svg/pk.svg [new file with mode: 0644]
plugins/xkb/flags_svg/pl.svg [new file with mode: 0644]
plugins/xkb/flags_svg/pt.svg [new file with mode: 0644]
plugins/xkb/flags_svg/qa.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ro.svg [new file with mode: 0644]
plugins/xkb/flags_svg/rs.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ru.svg [new file with mode: 0644]
plugins/xkb/flags_svg/sa.svg [new file with mode: 0644]
plugins/xkb/flags_svg/sd.svg [new file with mode: 0644]
plugins/xkb/flags_svg/se.svg [new file with mode: 0644]
plugins/xkb/flags_svg/si.svg [new file with mode: 0644]
plugins/xkb/flags_svg/sk.svg [new file with mode: 0644]
plugins/xkb/flags_svg/sn.svg [new file with mode: 0644]
plugins/xkb/flags_svg/so.svg [new file with mode: 0644]
plugins/xkb/flags_svg/sr.svg [new file with mode: 0644]
plugins/xkb/flags_svg/sy.svg [new file with mode: 0644]
plugins/xkb/flags_svg/th.svg [new file with mode: 0644]
plugins/xkb/flags_svg/tj.svg [new file with mode: 0644]
plugins/xkb/flags_svg/tm.svg [new file with mode: 0644]
plugins/xkb/flags_svg/tn.svg [new file with mode: 0644]
plugins/xkb/flags_svg/tr.svg [new file with mode: 0644]
plugins/xkb/flags_svg/tw.svg [new file with mode: 0644]
plugins/xkb/flags_svg/tz.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ua.svg [new file with mode: 0644]
plugins/xkb/flags_svg/us.svg [new file with mode: 0644]
plugins/xkb/flags_svg/uy.svg [new file with mode: 0644]
plugins/xkb/flags_svg/uz.svg [new file with mode: 0644]
plugins/xkb/flags_svg/vn.svg [new file with mode: 0644]
plugins/xkb/flags_svg/ye.svg [new file with mode: 0644]
plugins/xkb/flags_svg/za.svg [new file with mode: 0644]
plugins/xkb/scripts/svg_to_png.py [new file with mode: 0755]
plugins/xkb/scripts/xkbcfgfromman.py [new file with mode: 0755]
plugins/xkb/scripts/xkbcfgsort.py [new file with mode: 0755]
plugins/xkb/test/layouts_n_variants.c [new file with mode: 0644]
plugins/xkb/test/setxkbmap_query.c [new file with mode: 0644]
plugins/xkb/xkb-plugin.c [new file with mode: 0644]
plugins/xkb/xkb.c [new file with mode: 0644]
plugins/xkb/xkb.h [new file with mode: 0644]
plugins/xkb/xkeyboardconfig/README [new file with mode: 0644]
plugins/xkb/xkeyboardconfig/layouts.cfg [new file with mode: 0644]
plugins/xkb/xkeyboardconfig/models.cfg [new file with mode: 0644]
plugins/xkb/xkeyboardconfig/toggle.cfg [new file with mode: 0644]
po/POTFILES.in
po/POTFILES.skip
src/Makefile.am
src/plugins/Makefile.am [deleted file]
src/plugins/batt/Makefile.am [deleted file]
src/plugins/batt/batt.c [deleted file]
src/plugins/batt/batt_sys.c [deleted file]
src/plugins/batt/batt_sys.h [deleted file]
src/plugins/cpu/Makefile.am [deleted file]
src/plugins/cpu/cpu.c [deleted file]
src/plugins/cpufreq/Makefile.am [deleted file]
src/plugins/cpufreq/cpufreq.c [deleted file]
src/plugins/dclock.c [deleted file]
src/plugins/deskno/Makefile.am [deleted file]
src/plugins/deskno/deskno.c [deleted file]
src/plugins/dirmenu.c [deleted file]
src/plugins/icon.xpm [deleted file]
src/plugins/image.c [deleted file]
src/plugins/indicator/Makefile.am [deleted file]
src/plugins/indicator/indicator.c [deleted file]
src/plugins/kbled/Makefile.am [deleted file]
src/plugins/kbled/kbled.c [deleted file]
src/plugins/launchtaskbar.c [deleted file]
src/plugins/menu.c [deleted file]
src/plugins/monitors/Makefile.am [deleted file]
src/plugins/monitors/monitors.c [deleted file]
src/plugins/netstat/Makefile.am [deleted file]
src/plugins/netstat/devproc.c [deleted file]
src/plugins/netstat/devproc.h [deleted file]
src/plugins/netstat/lxnm_client.c [deleted file]
src/plugins/netstat/lxnm_client.h [deleted file]
src/plugins/netstat/netstat.c [deleted file]
src/plugins/netstat/netstat.h [deleted file]
src/plugins/netstat/nsconfig.h [deleted file]
src/plugins/netstat/passwd_gui.c [deleted file]
src/plugins/netstat/passwd_gui.h [deleted file]
src/plugins/netstat/statusicon.c [deleted file]
src/plugins/netstat/statusicon.h [deleted file]
src/plugins/netstat/wireless.c [deleted file]
src/plugins/netstat/wireless.h [deleted file]
src/plugins/netstatus/COPYING [deleted file]
src/plugins/netstatus/Makefile.am [deleted file]
src/plugins/netstatus/netstatus-dialog.c [deleted file]
src/plugins/netstatus/netstatus-dialog.h [deleted file]
src/plugins/netstatus/netstatus-enums.c [deleted file]
src/plugins/netstatus/netstatus-enums.h [deleted file]
src/plugins/netstatus/netstatus-fallback-pixbuf.h [deleted file]
src/plugins/netstatus/netstatus-icon.c [deleted file]
src/plugins/netstatus/netstatus-icon.h [deleted file]
src/plugins/netstatus/netstatus-iface.c [deleted file]
src/plugins/netstatus/netstatus-iface.h [deleted file]
src/plugins/netstatus/netstatus-sysdeps.c [deleted file]
src/plugins/netstatus/netstatus-sysdeps.h [deleted file]
src/plugins/netstatus/netstatus-util.c [deleted file]
src/plugins/netstatus/netstatus-util.h [deleted file]
src/plugins/netstatus/netstatus.c [deleted file]
src/plugins/pager.c [deleted file]
src/plugins/separator.c [deleted file]
src/plugins/space.c [deleted file]
src/plugins/test.c [deleted file]
src/plugins/thermal/Makefile.am [deleted file]
src/plugins/thermal/thermal.c [deleted file]
src/plugins/tray.c [deleted file]
src/plugins/volume/Makefile.am [deleted file]
src/plugins/volume/volume-impl.c [deleted file]
src/plugins/volume/volume-impl.h [deleted file]
src/plugins/volume/volume.c [deleted file]
src/plugins/volume/volume_xpm.h [deleted file]
src/plugins/volumealsa/Makefile.am [deleted file]
src/plugins/volumealsa/volumealsa.c [deleted file]
src/plugins/weather/Makefile.am [deleted file]
src/plugins/weather/forecast.c [deleted file]
src/plugins/weather/forecast.h [deleted file]
src/plugins/weather/httputil.c [deleted file]
src/plugins/weather/httputil.h [deleted file]
src/plugins/weather/location.c [deleted file]
src/plugins/weather/location.h [deleted file]
src/plugins/weather/logutil.c [deleted file]
src/plugins/weather/logutil.h [deleted file]
src/plugins/weather/weather.c [deleted file]
src/plugins/weather/weatherwidget.c [deleted file]
src/plugins/weather/weatherwidget.h [deleted file]
src/plugins/weather/yahooutil.c [deleted file]
src/plugins/weather/yahooutil.h [deleted file]
src/plugins/wincmd.c [deleted file]
src/plugins/xkb/Makefile.am [deleted file]
src/plugins/xkb/flags/Makefile.am [deleted file]
src/plugins/xkb/flags/ad.png [deleted file]
src/plugins/xkb/flags/ae.png [deleted file]
src/plugins/xkb/flags/af.png [deleted file]
src/plugins/xkb/flags/al.png [deleted file]
src/plugins/xkb/flags/am.png [deleted file]
src/plugins/xkb/flags/ar.png [deleted file]
src/plugins/xkb/flags/ara.png [deleted file]
src/plugins/xkb/flags/at.png [deleted file]
src/plugins/xkb/flags/az.png [deleted file]
src/plugins/xkb/flags/ba.png [deleted file]
src/plugins/xkb/flags/bd.png [deleted file]
src/plugins/xkb/flags/be.png [deleted file]
src/plugins/xkb/flags/ben.png [deleted file]
src/plugins/xkb/flags/bg.png [deleted file]
src/plugins/xkb/flags/bh.png [deleted file]
src/plugins/xkb/flags/br.png [deleted file]
src/plugins/xkb/flags/brai.png [deleted file]
src/plugins/xkb/flags/bt.png [deleted file]
src/plugins/xkb/flags/bw.png [deleted file]
src/plugins/xkb/flags/by.png [deleted file]
src/plugins/xkb/flags/ca.png [deleted file]
src/plugins/xkb/flags/cd.png [deleted file]
src/plugins/xkb/flags/ch.png [deleted file]
src/plugins/xkb/flags/cm.png [deleted file]
src/plugins/xkb/flags/cn.png [deleted file]
src/plugins/xkb/flags/cu.png [deleted file]
src/plugins/xkb/flags/cz.png [deleted file]
src/plugins/xkb/flags/de.png [deleted file]
src/plugins/xkb/flags/dev.png [deleted file]
src/plugins/xkb/flags/dj.png [deleted file]
src/plugins/xkb/flags/dk.png [deleted file]
src/plugins/xkb/flags/dvorak.png [deleted file]
src/plugins/xkb/flags/dz.png [deleted file]
src/plugins/xkb/flags/ee.png [deleted file]
src/plugins/xkb/flags/eg.png [deleted file]
src/plugins/xkb/flags/epo.png [deleted file]
src/plugins/xkb/flags/es.png [deleted file]
src/plugins/xkb/flags/et.png [deleted file]
src/plugins/xkb/flags/eu.png [deleted file]
src/plugins/xkb/flags/fi.png [deleted file]
src/plugins/xkb/flags/fo.png [deleted file]
src/plugins/xkb/flags/fr.png [deleted file]
src/plugins/xkb/flags/gb.png [deleted file]
src/plugins/xkb/flags/ge.png [deleted file]
src/plugins/xkb/flags/gh.png [deleted file]
src/plugins/xkb/flags/gn.png [deleted file]
src/plugins/xkb/flags/gr.png [deleted file]
src/plugins/xkb/flags/hr.png [deleted file]
src/plugins/xkb/flags/hu.png [deleted file]
src/plugins/xkb/flags/ie.png [deleted file]
src/plugins/xkb/flags/il.png [deleted file]
src/plugins/xkb/flags/in.png [deleted file]
src/plugins/xkb/flags/iq.png [deleted file]
src/plugins/xkb/flags/ir.png [deleted file]
src/plugins/xkb/flags/is.png [deleted file]
src/plugins/xkb/flags/it.png [deleted file]
src/plugins/xkb/flags/jo.png [deleted file]
src/plugins/xkb/flags/jp.png [deleted file]
src/plugins/xkb/flags/ke.png [deleted file]
src/plugins/xkb/flags/kg.png [deleted file]
src/plugins/xkb/flags/kh.png [deleted file]
src/plugins/xkb/flags/km.png [deleted file]
src/plugins/xkb/flags/kp.png [deleted file]
src/plugins/xkb/flags/kr.png [deleted file]
src/plugins/xkb/flags/kw.png [deleted file]
src/plugins/xkb/flags/kz.png [deleted file]
src/plugins/xkb/flags/la.png [deleted file]
src/plugins/xkb/flags/latam.png [deleted symlink]
src/plugins/xkb/flags/lb.png [deleted file]
src/plugins/xkb/flags/lk.png [deleted file]
src/plugins/xkb/flags/lt.png [deleted file]
src/plugins/xkb/flags/lv.png [deleted file]
src/plugins/xkb/flags/ly.png [deleted file]
src/plugins/xkb/flags/ma.png [deleted file]
src/plugins/xkb/flags/mao.png [deleted file]
src/plugins/xkb/flags/me.png [deleted file]
src/plugins/xkb/flags/mk.png [deleted file]
src/plugins/xkb/flags/ml.png [deleted file]
src/plugins/xkb/flags/mm.png [deleted file]
src/plugins/xkb/flags/mn.png [deleted file]
src/plugins/xkb/flags/mt.png [deleted file]
src/plugins/xkb/flags/mv.png [deleted file]
src/plugins/xkb/flags/mx.png [deleted file]
src/plugins/xkb/flags/nec_vndr-jp.png [deleted symlink]
src/plugins/xkb/flags/ng.png [deleted file]
src/plugins/xkb/flags/nl.png [deleted file]
src/plugins/xkb/flags/no.png [deleted file]
src/plugins/xkb/flags/np.png [deleted file]
src/plugins/xkb/flags/om.png [deleted file]
src/plugins/xkb/flags/ph.png [deleted file]
src/plugins/xkb/flags/pk.png [deleted file]
src/plugins/xkb/flags/pl.png [deleted file]
src/plugins/xkb/flags/ps.png [deleted file]
src/plugins/xkb/flags/pt.png [deleted file]
src/plugins/xkb/flags/qa.png [deleted file]
src/plugins/xkb/flags/qc.png [deleted file]
src/plugins/xkb/flags/ro.png [deleted file]
src/plugins/xkb/flags/rs.png [deleted file]
src/plugins/xkb/flags/ru.png [deleted file]
src/plugins/xkb/flags/sa.png [deleted file]
src/plugins/xkb/flags/sd.png [deleted file]
src/plugins/xkb/flags/se.png [deleted file]
src/plugins/xkb/flags/si.png [deleted file]
src/plugins/xkb/flags/sk.png [deleted file]
src/plugins/xkb/flags/sn.png [deleted file]
src/plugins/xkb/flags/so.png [deleted file]
src/plugins/xkb/flags/sr.png [deleted file]
src/plugins/xkb/flags/sy.png [deleted file]
src/plugins/xkb/flags/th.png [deleted file]
src/plugins/xkb/flags/tj.png [deleted file]
src/plugins/xkb/flags/tm.png [deleted file]
src/plugins/xkb/flags/tn.png [deleted file]
src/plugins/xkb/flags/tr.png [deleted file]
src/plugins/xkb/flags/tw.png [deleted file]
src/plugins/xkb/flags/tz.png [deleted file]
src/plugins/xkb/flags/ua.png [deleted file]
src/plugins/xkb/flags/uk.png [deleted file]
src/plugins/xkb/flags/un.png [deleted file]
src/plugins/xkb/flags/us.png [deleted file]
src/plugins/xkb/flags/uy.png [deleted file]
src/plugins/xkb/flags/uz.png [deleted file]
src/plugins/xkb/flags/vn.png [deleted file]
src/plugins/xkb/flags/ye.png [deleted file]
src/plugins/xkb/flags/yu.png [deleted file]
src/plugins/xkb/flags/za.png [deleted file]
src/plugins/xkb/flags_svg/ad.svg [deleted file]
src/plugins/xkb/flags_svg/ae.svg [deleted file]
src/plugins/xkb/flags_svg/af.svg [deleted file]
src/plugins/xkb/flags_svg/al.svg [deleted file]
src/plugins/xkb/flags_svg/am.svg [deleted file]
src/plugins/xkb/flags_svg/ar.svg [deleted file]
src/plugins/xkb/flags_svg/ara.svg [deleted file]
src/plugins/xkb/flags_svg/at.svg [deleted file]
src/plugins/xkb/flags_svg/az.svg [deleted file]
src/plugins/xkb/flags_svg/ba.svg [deleted file]
src/plugins/xkb/flags_svg/bd.svg [deleted file]
src/plugins/xkb/flags_svg/be.svg [deleted file]
src/plugins/xkb/flags_svg/ben.svg [deleted file]
src/plugins/xkb/flags_svg/bg.svg [deleted file]
src/plugins/xkb/flags_svg/bh.svg [deleted file]
src/plugins/xkb/flags_svg/br.svg [deleted file]
src/plugins/xkb/flags_svg/brai.svg [deleted file]
src/plugins/xkb/flags_svg/bt.svg [deleted file]
src/plugins/xkb/flags_svg/bw.svg [deleted file]
src/plugins/xkb/flags_svg/by.svg [deleted file]
src/plugins/xkb/flags_svg/ca.svg [deleted file]
src/plugins/xkb/flags_svg/cd.svg [deleted file]
src/plugins/xkb/flags_svg/ch.svg [deleted file]
src/plugins/xkb/flags_svg/cm.svg [deleted file]
src/plugins/xkb/flags_svg/cn.svg [deleted file]
src/plugins/xkb/flags_svg/cu.svg [deleted file]
src/plugins/xkb/flags_svg/cz.svg [deleted file]
src/plugins/xkb/flags_svg/de.svg [deleted file]
src/plugins/xkb/flags_svg/dev.svg [deleted file]
src/plugins/xkb/flags_svg/dj.svg [deleted file]
src/plugins/xkb/flags_svg/dk.svg [deleted file]
src/plugins/xkb/flags_svg/dvorak.svg [deleted file]
src/plugins/xkb/flags_svg/dz.svg [deleted file]
src/plugins/xkb/flags_svg/ee.svg [deleted file]
src/plugins/xkb/flags_svg/eg.svg [deleted file]
src/plugins/xkb/flags_svg/epo.svg [deleted file]
src/plugins/xkb/flags_svg/es.svg [deleted file]
src/plugins/xkb/flags_svg/et.svg [deleted file]
src/plugins/xkb/flags_svg/fi.svg [deleted file]
src/plugins/xkb/flags_svg/fo.svg [deleted file]
src/plugins/xkb/flags_svg/fr.svg [deleted file]
src/plugins/xkb/flags_svg/gb.svg [deleted file]
src/plugins/xkb/flags_svg/ge.svg [deleted file]
src/plugins/xkb/flags_svg/gh.svg [deleted file]
src/plugins/xkb/flags_svg/gn.svg [deleted file]
src/plugins/xkb/flags_svg/gr.svg [deleted file]
src/plugins/xkb/flags_svg/hr.svg [deleted file]
src/plugins/xkb/flags_svg/hu.svg [deleted file]
src/plugins/xkb/flags_svg/ie.svg [deleted file]
src/plugins/xkb/flags_svg/il.svg [deleted file]
src/plugins/xkb/flags_svg/in.svg [deleted file]
src/plugins/xkb/flags_svg/iq.svg [deleted file]
src/plugins/xkb/flags_svg/ir.svg [deleted file]
src/plugins/xkb/flags_svg/is.svg [deleted file]
src/plugins/xkb/flags_svg/it.svg [deleted file]
src/plugins/xkb/flags_svg/jo.svg [deleted file]
src/plugins/xkb/flags_svg/jp.svg [deleted file]
src/plugins/xkb/flags_svg/ke.svg [deleted file]
src/plugins/xkb/flags_svg/kg.svg [deleted file]
src/plugins/xkb/flags_svg/kh.svg [deleted file]
src/plugins/xkb/flags_svg/km.svg [deleted file]
src/plugins/xkb/flags_svg/kp.svg [deleted file]
src/plugins/xkb/flags_svg/kr.svg [deleted file]
src/plugins/xkb/flags_svg/kw.svg [deleted file]
src/plugins/xkb/flags_svg/kz.svg [deleted file]
src/plugins/xkb/flags_svg/la.svg [deleted file]
src/plugins/xkb/flags_svg/lb.svg [deleted file]
src/plugins/xkb/flags_svg/lk.svg [deleted file]
src/plugins/xkb/flags_svg/lt.svg [deleted file]
src/plugins/xkb/flags_svg/lv.svg [deleted file]
src/plugins/xkb/flags_svg/ly.svg [deleted file]
src/plugins/xkb/flags_svg/ma.svg [deleted file]
src/plugins/xkb/flags_svg/mao.svg [deleted file]
src/plugins/xkb/flags_svg/me.svg [deleted file]
src/plugins/xkb/flags_svg/mk.svg [deleted file]
src/plugins/xkb/flags_svg/ml.svg [deleted file]
src/plugins/xkb/flags_svg/mm.svg [deleted file]
src/plugins/xkb/flags_svg/mn.svg [deleted file]
src/plugins/xkb/flags_svg/mt.svg [deleted file]
src/plugins/xkb/flags_svg/mv.svg [deleted file]
src/plugins/xkb/flags_svg/mx.svg [deleted file]
src/plugins/xkb/flags_svg/ng.svg [deleted file]
src/plugins/xkb/flags_svg/nl.svg [deleted file]
src/plugins/xkb/flags_svg/no.svg [deleted file]
src/plugins/xkb/flags_svg/np.svg [deleted file]
src/plugins/xkb/flags_svg/om.svg [deleted file]
src/plugins/xkb/flags_svg/ph.svg [deleted file]
src/plugins/xkb/flags_svg/pk.svg [deleted file]
src/plugins/xkb/flags_svg/pl.svg [deleted file]
src/plugins/xkb/flags_svg/pt.svg [deleted file]
src/plugins/xkb/flags_svg/qa.svg [deleted file]
src/plugins/xkb/flags_svg/ro.svg [deleted file]
src/plugins/xkb/flags_svg/rs.svg [deleted file]
src/plugins/xkb/flags_svg/ru.svg [deleted file]
src/plugins/xkb/flags_svg/sa.svg [deleted file]
src/plugins/xkb/flags_svg/sd.svg [deleted file]
src/plugins/xkb/flags_svg/se.svg [deleted file]
src/plugins/xkb/flags_svg/si.svg [deleted file]
src/plugins/xkb/flags_svg/sk.svg [deleted file]
src/plugins/xkb/flags_svg/sn.svg [deleted file]
src/plugins/xkb/flags_svg/so.svg [deleted file]
src/plugins/xkb/flags_svg/sr.svg [deleted file]
src/plugins/xkb/flags_svg/sy.svg [deleted file]
src/plugins/xkb/flags_svg/th.svg [deleted file]
src/plugins/xkb/flags_svg/tj.svg [deleted file]
src/plugins/xkb/flags_svg/tm.svg [deleted file]
src/plugins/xkb/flags_svg/tn.svg [deleted file]
src/plugins/xkb/flags_svg/tr.svg [deleted file]
src/plugins/xkb/flags_svg/tw.svg [deleted file]
src/plugins/xkb/flags_svg/tz.svg [deleted file]
src/plugins/xkb/flags_svg/ua.svg [deleted file]
src/plugins/xkb/flags_svg/us.svg [deleted file]
src/plugins/xkb/flags_svg/uy.svg [deleted file]
src/plugins/xkb/flags_svg/uz.svg [deleted file]
src/plugins/xkb/flags_svg/vn.svg [deleted file]
src/plugins/xkb/flags_svg/ye.svg [deleted file]
src/plugins/xkb/flags_svg/za.svg [deleted file]
src/plugins/xkb/scripts/svg_to_png.py [deleted file]
src/plugins/xkb/scripts/xkbcfgfromman.py [deleted file]
src/plugins/xkb/scripts/xkbcfgsort.py [deleted file]
src/plugins/xkb/test/layouts_n_variants.c [deleted file]
src/plugins/xkb/test/setxkbmap_query.c [deleted file]
src/plugins/xkb/xkb-plugin.c [deleted file]
src/plugins/xkb/xkb.c [deleted file]
src/plugins/xkb/xkb.h [deleted file]
src/plugins/xkb/xkeyboardconfig/README [deleted file]
src/plugins/xkb/xkeyboardconfig/layouts.cfg [deleted file]
src/plugins/xkb/xkeyboardconfig/models.cfg [deleted file]
src/plugins/xkb/xkeyboardconfig/toggle.cfg [deleted file]

index 91fd855..16faf1a 100644 (file)
@@ -2,7 +2,7 @@
 
 ACLOCAL_AMFLAGS= -I m4
 
-SUBDIRS = src data po man
+SUBDIRS = plugins src data po man
 
 EXTRA_DIST = \
         autogen.sh \
index b195fd8..3f34302 100644 (file)
@@ -415,21 +415,21 @@ AC_CONFIG_FILES([
     lxpanel.pc
     Makefile
     src/Makefile
-    src/plugins/Makefile
-    src/plugins/netstatus/Makefile
-    src/plugins/netstat/Makefile
-    src/plugins/volume/Makefile
-    src/plugins/volumealsa/Makefile
-    src/plugins/cpu/Makefile
-    src/plugins/deskno/Makefile
-    src/plugins/batt/Makefile
-    src/plugins/kbled/Makefile
-    src/plugins/xkb/Makefile
-    src/plugins/thermal/Makefile
-    src/plugins/cpufreq/Makefile
-    src/plugins/monitors/Makefile
-    src/plugins/indicator/Makefile
-    src/plugins/weather/Makefile
+    plugins/Makefile
+    plugins/netstatus/Makefile
+    plugins/netstat/Makefile
+    plugins/volume/Makefile
+    plugins/volumealsa/Makefile
+    plugins/cpu/Makefile
+    plugins/deskno/Makefile
+    plugins/batt/Makefile
+    plugins/kbled/Makefile
+    plugins/xkb/Makefile
+    plugins/thermal/Makefile
+    plugins/cpufreq/Makefile
+    plugins/monitors/Makefile
+    plugins/indicator/Makefile
+    plugins/weather/Makefile
     po/Makefile.in
     data/Makefile
     data/default/panels/panel
diff --git a/plugins/Makefile.am b/plugins/Makefile.am
new file mode 100644 (file)
index 0000000..5de6426
--- /dev/null
@@ -0,0 +1,50 @@
+## Process this file with automake to produce Makefile.in
+
+DIST_SUBDIRS=$(ALL_PLUGINS_LIST)
+
+if BUILD_PLUGIN_LOADER
+DYNAMIC_PLUGINS = \
+       $(PLUGINS_LIST)
+endif
+
+SUBDIRS = $(DYNAMIC_PLUGINS)
+
+noinst_LIBRARIES = libbuiltin_plugins.a
+
+libbuiltin_plugins_a_CFLAGS = \
+       -I$(top_srcdir) \
+       -I$(top_srcdir)/src \
+       -DPACKAGE_DATA_DIR=\""$(datadir)/lxpanel"\" \
+       -DPACKAGE_UI_DIR=\""$(datadir)/lxpanel/ui"\" \
+       -DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
+       $(PACKAGE_CFLAGS) \
+       $(G_CAST_CHECKS)
+
+if ENABLE_MENU_CACHE
+MENU_SOURCES = \
+       menu.c
+endif
+
+PLUGINS_SOURCES = \
+       dclock.c \
+       dirmenu.c \
+       launchtaskbar.c \
+       pager.c \
+       separator.c \
+       space.c \
+       tray.c \
+       wincmd.c \
+       $(MENU_SOURCES)
+
+libbuiltin_plugins_a_SOURCES = \
+       $(PLUGINS_SOURCES)
+
+libbuiltin_plugins_a_LIBADD =
+
+EXTRA_DIST = \
+       icon.xpm
+
+install-exec-hook:
+       rm -f $(DESTDIR)$(libdir)/lxpanel/plugins/*.la
+       rm -f $(DESTDIR)$(libdir)/lxpanel/plugins/*.a
+       rm -f $(DESTDIR)$(libdir)/lxpanel/plugins/*.
diff --git a/plugins/batt/Makefile.am b/plugins/batt/Makefile.am
new file mode 100644 (file)
index 0000000..5c8b544
--- /dev/null
@@ -0,0 +1,22 @@
+batt_la_CFLAGS = \
+       -I. \
+       -I$(top_srcdir)/src \
+       $(PACKAGE_CFLAGS) \
+       $(G_CAST_CHECKS)
+
+module_LTLIBRARIES = batt.la
+
+moduledir = $(libdir)/lxpanel/plugins
+
+batt_la_SOURCES = \
+       batt.c \
+       batt_sys.h \
+       batt_sys.c
+
+
+batt_la_LIBADD = \
+       $(PACKAGE_LIBS)
+
+batt_la_LDFLAGS = \
+       -module \
+       @LXPANEL_MODULE@
diff --git a/plugins/batt/batt.c b/plugins/batt/batt.c
new file mode 100644 (file)
index 0000000..ea6cac5
--- /dev/null
@@ -0,0 +1,672 @@
+/*
+ * ACPI battery monitor plugin for LXPanel
+ *
+ * Copyright (C) 2007 by Greg McNew <gmcnew@gmail.com>
+ * Copyright (C) 2008 by Hong Jen Yee <pcman.tw@gmail.com>
+ * Copyright (C) 2009 by Juergen Hoetzel <juergen@archlinux.org>
+ * Copyright (C) 2014 by Andriy Grytsenko <andrej@rep.kiev.ua>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ *
+ * This plugin monitors battery usage on ACPI-enabled systems by reading the
+ * battery information found in /sys/class/power_supply. The update interval is
+ * user-configurable and defaults to 3 second.
+ *
+ * The battery's remaining life is estimated from its current charge and current
+ * rate of discharge. The user may configure an alarm command to be run when
+ * their estimated remaining battery life reaches a certain level.
+ */
+
+/* FIXME:
+ *  Here are somethings need to be improvec:
+ *  1. Replace pthread stuff with gthread counterparts for portability.
+ *  3. Add an option to hide the plugin when AC power is used or there is no battery.
+ *  4. Handle failure gracefully under systems other than Linux.
+*/
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <pthread.h> /* used by pthread_create() and alarmThread */
+#include <semaphore.h> /* used by update() and alarmProcess() for alarms */
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include "dbg.h" /* for ENTER and RET macros */
+#include "batt_sys.h"
+#include "misc.h" /* used for lxpanel_generic_config_dlg() */
+#include "plugin.h" /* all other APIs including panel configuration */
+
+/* The last MAX_SAMPLES samples are averaged when charge rates are evaluated.
+   This helps prevent spikes in the "time left" values the user sees. */
+#define MAX_SAMPLES 10
+
+typedef struct {
+    char *alarmCommand,
+        *backgroundColor,
+        *chargingColor1,
+        *chargingColor2,
+        *dischargingColor1,
+        *dischargingColor2;
+    GdkColor background,
+        charging1,
+        charging2,
+        discharging1,
+        discharging2;
+    cairo_surface_t *pixmap;
+    GtkWidget *drawingArea;
+    GtkOrientation orientation;
+    unsigned int alarmTime,
+        border,
+        height,
+        length,
+        numSamples,
+        requestedBorder,
+        *rateSamples,
+        rateSamplesSum,
+        thickness,
+        timer,
+        state_elapsed_time,
+        info_elapsed_time,
+        wasCharging,
+        width,
+        hide_if_no_battery;
+    sem_t alarmProcessLock;
+    battery* b;
+    gboolean has_ac_adapter;
+    gboolean show_extended_information;
+    LXPanel *panel;
+    config_setting_t *settings;
+} lx_battery;
+
+
+typedef struct {
+    char *command;
+    sem_t *lock;
+} Alarm;
+
+static void destructor(gpointer data);
+static void update_display(lx_battery *lx_b, gboolean repaint);
+
+/* alarmProcess takes the address of a dynamically allocated alarm struct (which
+   it must free). It ensures that alarm commands do not run concurrently. */
+static void * alarmProcess(void *arg) {
+    Alarm *a = (Alarm *) arg;
+
+    sem_wait(a->lock);
+    if (system(a->command) != 0)
+        g_warning("plugin batt: failed to execute alarm command \"%s\"", a->command);
+    sem_post(a->lock);
+
+    g_free(a);
+    return NULL;
+}
+
+
+static void append(gchar **tooltip, gchar *fmt, ...)
+{
+    gchar *old = *tooltip;
+    gchar *new;
+    va_list va;
+
+    va_start(va, fmt);
+    new = g_strdup_vprintf(fmt, va);
+    va_end(va);
+
+    *tooltip = g_strconcat(old, new, NULL);
+
+    g_free(old);
+    g_free(new);
+}
+
+
+/* Make a tooltip string, and display remaining charge time if the battery
+   is charging or remaining life if it's discharging */
+static gchar* make_tooltip(lx_battery* lx_b, gboolean isCharging)
+{
+    gchar * tooltip;
+    gchar * indent = "  ";
+    battery *b = lx_b->b;
+
+    if (b == NULL)
+        return NULL;
+
+    if (isCharging) {
+        int hours = lx_b->b->seconds / 3600;
+        int left_seconds = lx_b->b->seconds - 3600 * hours;
+        int minutes = left_seconds / 60;
+        tooltip = g_strdup_printf(
+                _("Battery: %d%% charged, %d:%02d until full"),
+                lx_b->b->percentage,
+                hours,
+                minutes );
+    } else {
+        /* if we have enough rate information for battery */
+        if (lx_b->b->percentage != 100) {
+            int hours = lx_b->b->seconds / 3600;
+            int left_seconds = lx_b->b->seconds - 3600 * hours;
+            int minutes = left_seconds / 60;
+            tooltip = g_strdup_printf(
+                    _("Battery: %d%% charged, %d:%02d left"),
+                    lx_b->b->percentage,
+                    hours,
+                    minutes );
+        } else {
+            tooltip = g_strdup_printf(
+                    _("Battery: %d%% charged"),
+                    100 );
+        }
+    }
+
+    if (!lx_b->show_extended_information) {
+        return tooltip;
+    }
+
+    if (b->energy_full_design != -1)
+        append(&tooltip, _("\n%sEnergy full design:\t\t%5d mWh"), indent, b->energy_full_design);
+    if (b->energy_full != -1)
+        append(&tooltip, _("\n%sEnergy full:\t\t\t%5d mWh"), indent, b->energy_full);
+    if (b->energy_now != -1)
+        append(&tooltip, _("\n%sEnergy now:\t\t\t%5d mWh"), indent, b->energy_now);
+    if (b->power_now != -1)
+        append(&tooltip, _("\n%sPower now:\t\t\t%5d mW"), indent, b->power_now);
+
+    if (b->charge_full_design != -1)
+        append(&tooltip, _("\n%sCharge full design:\t%5d mAh"), indent, b->charge_full_design);
+    if (b->charge_full != -1)
+        append(&tooltip, _("\n%sCharge full:\t\t\t%5d mAh"), indent, b->charge_full);
+    if (b->charge_now != -1)
+        append(&tooltip, _("\n%sCharge now:\t\t\t%5d mAh"), indent, b->charge_now);
+    if (b->current_now != -1)
+        append(&tooltip, _("\n%sCurrent now:\t\t\t%5d mA"), indent, b->current_now);
+
+    if (b->voltage_now != -1)
+        append(&tooltip, _("\n%sCurrent Voltage:\t\t%.3lf V"), indent, b->voltage_now / 1000.0);
+
+    return tooltip;
+}
+
+static void set_tooltip_text(lx_battery* lx_b)
+{
+    if (lx_b->b == NULL)
+        return;
+    gboolean isCharging = battery_is_charging(lx_b->b);
+    gchar *tooltip = make_tooltip(lx_b, isCharging);
+    gtk_widget_set_tooltip_text(lx_b->drawingArea, tooltip);
+    g_free(tooltip);
+}
+
+/* FIXME:
+   Don't repaint if percentage of remaining charge and remaining time aren't changed. */
+void update_display(lx_battery *lx_b, gboolean repaint) {
+    cairo_t *cr;
+    battery *b = lx_b->b;
+    /* unit: mW */
+    int rate;
+    gboolean isCharging;
+
+    if (! lx_b->pixmap )
+        return;
+
+    cr = cairo_create(lx_b->pixmap);
+    cairo_set_line_width (cr, 1.0);
+
+    /* draw background */
+    gdk_cairo_set_source_color(cr, &lx_b->background);
+    cairo_rectangle(cr, 0, 0, lx_b->width, lx_b->height);
+    cairo_fill(cr);
+
+    /* no battery is found */
+    if( b == NULL )
+    {
+        gtk_widget_set_tooltip_text( lx_b->drawingArea, _("No batteries found") );
+        if (lx_b->hide_if_no_battery)
+        {
+            gtk_widget_hide(gtk_widget_get_parent(lx_b->drawingArea));
+            repaint = FALSE;
+        }
+        goto update_done;
+    }
+
+    /* fixme: only one battery supported */
+
+    rate = lx_b->b->current_now;
+    isCharging = battery_is_charging ( b );
+
+    /* Consider running the alarm command */
+    if ( !isCharging && rate > 0 &&
+        ( ( battery_get_remaining( b ) / 60 ) < (int)lx_b->alarmTime ) )
+    {
+        /* Shrug this should be done using glibs process functions */
+        /* Alarms should not run concurrently; determine whether an alarm is
+           already running */
+        int alarmCanRun;
+        sem_getvalue(&(lx_b->alarmProcessLock), &alarmCanRun);
+
+        /* Run the alarm command if it isn't already running */
+        if (alarmCanRun) {
+
+            Alarm *a = (Alarm *) malloc(sizeof(Alarm));
+            a->command = lx_b->alarmCommand;
+            a->lock = &(lx_b->alarmProcessLock);
+
+            /* Manage the alarm process in a new thread, which which will be
+               responsible for freeing the alarm struct it's given */
+            pthread_t alarmThread;
+            pthread_create(&alarmThread, NULL, alarmProcess, a);
+        }
+    }
+
+    set_tooltip_text(lx_b);
+
+    int chargeLevel = lx_b->b->percentage * (lx_b->length - 2 * lx_b->border) / 100;
+
+    if (lx_b->orientation == GTK_ORIENTATION_HORIZONTAL) {
+
+        /* Draw the battery bar vertically, using color 1 for the left half and
+           color 2 for the right half */
+        gdk_cairo_set_source_color(cr,
+                isCharging ? &lx_b->charging1 : &lx_b->discharging1);
+        cairo_rectangle(cr, lx_b->border,
+                lx_b->height - lx_b->border - chargeLevel, lx_b->width / 2
+                - lx_b->border, chargeLevel);
+        cairo_fill(cr);
+        gdk_cairo_set_source_color(cr,
+                isCharging ? &lx_b->charging2 : &lx_b->discharging2);
+        cairo_rectangle(cr, lx_b->width / 2,
+                lx_b->height - lx_b->border - chargeLevel, (lx_b->width + 1) / 2
+                - lx_b->border, chargeLevel);
+        cairo_fill(cr);
+
+    }
+    else {
+
+        /* Draw the battery bar horizontally, using color 1 for the top half and
+           color 2 for the bottom half */
+        gdk_cairo_set_source_color(cr,
+                isCharging ? &lx_b->charging1 : &lx_b->discharging1);
+        cairo_rectangle(cr, lx_b->border,
+                lx_b->border, chargeLevel, lx_b->height / 2 - lx_b->border);
+        cairo_fill(cr);
+        gdk_cairo_set_source_color(cr,
+                isCharging ? &lx_b->charging2 : &lx_b->discharging2);
+        cairo_rectangle(cr, lx_b->border, (lx_b->height + 1)
+                / 2, chargeLevel, lx_b->height / 2 - lx_b->border);
+        cairo_fill(cr);
+
+    }
+    gtk_widget_show(gtk_widget_get_parent(lx_b->drawingArea));
+
+update_done:
+    if( repaint )
+        gtk_widget_queue_draw( lx_b->drawingArea );
+
+    check_cairo_status(cr);
+    cairo_destroy(cr);
+}
+
+/* This callback is called every 3 seconds */
+static int update_timout(lx_battery *lx_b) {
+    battery *bat;
+    if (g_source_is_destroyed(g_main_current_source()))
+        return FALSE;
+    GDK_THREADS_ENTER();
+    lx_b->state_elapsed_time++;
+    lx_b->info_elapsed_time++;
+
+    bat = battery_update( lx_b->b );
+    if (bat == NULL)
+    {
+        battery_free(lx_b->b);
+
+        /* maybe in the mean time a battery has been inserted. */
+        lx_b->b = battery_get();
+    }
+
+    update_display( lx_b, TRUE );
+
+    GDK_THREADS_LEAVE();
+    return TRUE;
+}
+
+/* An update will be performed whenever the user clicks on the charge bar */
+static gboolean buttonPressEvent(GtkWidget *p, GdkEventButton *event,
+                                 LXPanel *panel)
+{
+    lx_battery *lx_b = lxpanel_plugin_get_data(p);
+
+    update_display(lx_b, TRUE);
+    /* FIXME: open some application for lid/power management may be? */
+
+    return FALSE;
+}
+
+
+static gint configureEvent(GtkWidget *widget, GdkEventConfigure *event,
+        lx_battery *lx_b)
+{
+    GtkAllocation allocation;
+
+    ENTER;
+
+    gtk_widget_get_allocation(widget, &allocation);
+    if (allocation.width <= 1 && allocation.height <= 1)
+    {
+        /* If plugin is hidden currently then we get 1x1 here */
+        RET(TRUE);
+    }
+
+    if (lx_b->pixmap)
+        cairo_surface_destroy(lx_b->pixmap);
+
+    /* Update the plugin's dimensions */
+    lx_b->width = allocation.width;
+    lx_b->height = allocation.height;
+    if (lx_b->orientation == GTK_ORIENTATION_HORIZONTAL) {
+        lx_b->length = lx_b->height;
+        lx_b->thickness = lx_b->width;
+    }
+    else {
+        lx_b->length = lx_b->width;
+        lx_b->thickness = lx_b->height;
+    }
+
+    lx_b->pixmap = cairo_image_surface_create (CAIRO_FORMAT_RGB24, allocation.width,
+                                               allocation.height);
+    check_cairo_surface_status(&lx_b->pixmap);
+
+    /* Perform an update so the bar will look right in its new orientation */
+    update_display(lx_b, FALSE);
+
+    RET(TRUE);
+}
+
+
+static gint exposeEvent(GtkWidget *widget, GdkEventExpose *event, lx_battery *lx_b) {
+
+    ENTER;
+    cairo_t *cr = gdk_cairo_create(gtk_widget_get_window(widget));
+    GtkStyle *style = gtk_widget_get_style(lx_b->drawingArea);
+
+    gdk_cairo_region(cr, event->region);
+    cairo_clip(cr);
+
+    gdk_cairo_set_source_color(cr, &style->black);
+    cairo_set_source_surface(cr, lx_b->pixmap, 0, 0);
+    cairo_paint(cr);
+
+    check_cairo_status(cr);
+    cairo_destroy(cr);
+
+    RET(FALSE);
+}
+
+
+static GtkWidget * constructor(LXPanel *panel, config_setting_t *settings)
+{
+    ENTER;
+
+    lx_battery *lx_b;
+    GtkWidget *p;
+    const char *str;
+    int tmp_int;
+
+    lx_b = g_new0(lx_battery, 1);
+
+    /* get available battery */
+    lx_b->b = battery_get ();
+
+    p = gtk_event_box_new();
+    lxpanel_plugin_set_data(p, lx_b, destructor);
+    gtk_widget_set_has_window(p, FALSE);
+    gtk_container_set_border_width( GTK_CONTAINER(p), 1 );
+
+    lx_b->drawingArea = gtk_drawing_area_new();
+    gtk_widget_add_events( lx_b->drawingArea, GDK_BUTTON_PRESS_MASK );
+
+    gtk_container_add( (GtkContainer*)p, lx_b->drawingArea );
+
+    lx_b->orientation = panel_get_orientation(panel);
+    if (lx_b->orientation == GTK_ORIENTATION_HORIZONTAL) {
+        lx_b->height = lx_b->length = 20;
+        lx_b->thickness = lx_b->width = 8;
+    }
+    else {
+        lx_b->height = lx_b->thickness = 8;
+        lx_b->length = lx_b->width = 20;
+    }
+    gtk_widget_set_size_request(lx_b->drawingArea, lx_b->width, lx_b->height);
+
+    gtk_widget_show(lx_b->drawingArea);
+
+    g_signal_connect (G_OBJECT (lx_b->drawingArea),"configure-event",
+          G_CALLBACK (configureEvent), (gpointer) lx_b);
+    g_signal_connect (G_OBJECT (lx_b->drawingArea), "expose-event",
+          G_CALLBACK (exposeEvent), (gpointer) lx_b);
+
+    sem_init(&(lx_b->alarmProcessLock), 0, 1);
+
+    lx_b->alarmCommand = lx_b->backgroundColor = lx_b->chargingColor1 = lx_b->chargingColor2
+            = lx_b->dischargingColor1 = lx_b->dischargingColor2 = NULL;
+
+    /* Set default values for integers */
+    lx_b->alarmTime = 5;
+    lx_b->requestedBorder = 1;
+
+    /* remember instance data */
+    lx_b->panel = panel;
+    lx_b->settings = settings;
+
+    lx_b->show_extended_information = false;
+
+    if (config_setting_lookup_int(settings, "HideIfNoBattery", &tmp_int))
+        lx_b->hide_if_no_battery = (tmp_int != 0);
+    if (config_setting_lookup_string(settings, "AlarmCommand", &str))
+        lx_b->alarmCommand = g_strdup(str);
+    if (config_setting_lookup_string(settings, "BackgroundColor", &str))
+        lx_b->backgroundColor = g_strdup(str);
+    if (config_setting_lookup_string(settings, "ChargingColor1", &str))
+        lx_b->chargingColor1 = g_strdup(str);
+    if (config_setting_lookup_string(settings, "ChargingColor2", &str))
+        lx_b->chargingColor2 = g_strdup(str);
+    if (config_setting_lookup_string(settings, "DischargingColor1", &str))
+        lx_b->dischargingColor1 = g_strdup(str);
+    if (config_setting_lookup_string(settings, "DischargingColor2", &str))
+        lx_b->dischargingColor2 = g_strdup(str);
+    if (config_setting_lookup_int(settings, "AlarmTime", &tmp_int))
+        lx_b->alarmTime = MAX(0, tmp_int);
+    if (config_setting_lookup_int(settings, "BorderWidth", &tmp_int))
+        lx_b->requestedBorder = MAX(0, tmp_int);
+    if (config_setting_lookup_int(settings, "Size", &tmp_int)) {
+        lx_b->thickness = MAX(1, tmp_int);
+        if (lx_b->orientation == GTK_ORIENTATION_HORIZONTAL)
+            lx_b->width = lx_b->thickness;
+        else
+            lx_b->height = lx_b->thickness;
+        gtk_widget_set_size_request(lx_b->drawingArea, lx_b->width,
+                                    lx_b->height);
+    }
+    if (config_setting_lookup_int(settings, "ShowExtendedInformation", &tmp_int))
+        lx_b->show_extended_information = (tmp_int != 0);
+
+    /* Make sure the border value is acceptable */
+    lx_b->border = MIN(lx_b->requestedBorder,
+                       (MAX(1, MIN(lx_b->length, lx_b->thickness)) - 1) / 2);
+
+    /* Apply more default options */
+    if (! lx_b->alarmCommand)
+        lx_b->alarmCommand = g_strdup("xmessage Battery low");
+    if (! lx_b->backgroundColor)
+        lx_b->backgroundColor = g_strdup("black");
+    if (! lx_b->chargingColor1)
+        lx_b->chargingColor1 = g_strdup("#28f200");
+    if (! lx_b->chargingColor2)
+        lx_b->chargingColor2 = g_strdup("#22cc00");
+    if (! lx_b->dischargingColor1)
+        lx_b->dischargingColor1 = g_strdup("#ffee00");
+    if (! lx_b->dischargingColor2)
+        lx_b->dischargingColor2 = g_strdup("#d9ca00");
+
+    gdk_color_parse(lx_b->backgroundColor, &lx_b->background);
+    gdk_color_parse(lx_b->chargingColor1, &lx_b->charging1);
+    gdk_color_parse(lx_b->chargingColor2, &lx_b->charging2);
+    gdk_color_parse(lx_b->dischargingColor1, &lx_b->discharging1);
+    gdk_color_parse(lx_b->dischargingColor2, &lx_b->discharging2);
+
+    /* Start the update loop */
+    lx_b->timer = g_timeout_add_seconds( 9, (GSourceFunc) update_timout, (gpointer) lx_b);
+
+    RET(p);
+}
+
+
+static void
+destructor(gpointer data)
+{
+    ENTER;
+
+    lx_battery *b = (lx_battery *)data;
+
+    if (b->b != NULL)
+        battery_free(b->b);
+
+    if (b->pixmap)
+        cairo_surface_destroy(b->pixmap);
+
+    g_free(b->alarmCommand);
+    g_free(b->backgroundColor);
+    g_free(b->chargingColor1);
+    g_free(b->chargingColor2);
+    g_free(b->dischargingColor1);
+    g_free(b->dischargingColor2);
+
+    g_free(b->rateSamples);
+    sem_destroy(&(b->alarmProcessLock));
+    if (b->timer)
+        g_source_remove(b->timer);
+    g_free(b);
+
+    RET();
+
+}
+
+
+static void orientation(LXPanel *panel, GtkWidget *p) {
+
+    ENTER;
+
+    lx_battery *b = lxpanel_plugin_get_data(p);
+
+    if (b->orientation != panel_get_orientation(panel)) {
+        b->orientation = panel_get_orientation(panel);
+        unsigned int swap = b->height;
+        b->height = b->width;
+        b->width = swap;
+        gtk_widget_set_size_request(b->drawingArea, b->width, b->height);
+    }
+
+    RET();
+}
+
+
+static gboolean applyConfig(gpointer user_data)
+{
+    ENTER;
+
+    lx_battery *b = lxpanel_plugin_get_data(user_data);
+
+    /* Update colors */
+    if (b->backgroundColor &&
+            gdk_color_parse(b->backgroundColor, &b->background))
+        config_group_set_string(b->settings, "BackgroundColor", b->backgroundColor);
+    if (b->chargingColor1 && gdk_color_parse(b->chargingColor1, &b->charging1))
+        config_group_set_string(b->settings, "ChargingColor1", b->chargingColor1);
+    if (b->chargingColor2 && gdk_color_parse(b->chargingColor2, &b->charging2))
+        config_group_set_string(b->settings, "ChargingColor2", b->chargingColor2);
+    if (b->dischargingColor1 &&
+            gdk_color_parse(b->dischargingColor1, &b->discharging1))
+        config_group_set_string(b->settings, "DischargingColor1", b->dischargingColor1);
+    if (b->dischargingColor2 &&
+            gdk_color_parse(b->dischargingColor2, &b->discharging2))
+        config_group_set_string(b->settings, "DischargingColor2", b->dischargingColor2);
+
+    /* Make sure the border value is acceptable */
+    b->border = MIN(b->requestedBorder,
+                    (MAX(1, MIN(b->length, b->thickness)) - 1) / 2);
+
+    /* Resize the widget */
+    b->width = b->height = b->length;
+    if (b->orientation == GTK_ORIENTATION_HORIZONTAL)
+        b->width = b->thickness;
+    else
+        b->height = b->thickness;
+    gtk_widget_set_size_request(b->drawingArea, b->width, b->height);
+    /* ensure visibility if requested */
+    if (!b->hide_if_no_battery)
+        gtk_widget_show(user_data);
+    else if (b->b == NULL)
+        gtk_widget_hide(user_data);
+
+    /* update tooltip */
+    set_tooltip_text(b);
+
+    /* update settings */
+    config_group_set_int(b->settings, "HideIfNoBattery", b->hide_if_no_battery);
+    config_group_set_string(b->settings, "AlarmCommand", b->alarmCommand);
+    config_group_set_int(b->settings, "AlarmTime", b->alarmTime);
+    config_group_set_int(b->settings, "BorderWidth", b->requestedBorder);
+    config_group_set_int(b->settings, "Size", b->thickness);
+    config_group_set_int(b->settings, "ShowExtendedInformation",
+                         b->show_extended_information);
+
+    RET(FALSE);
+}
+
+
+static GtkWidget *config(LXPanel *panel, GtkWidget *p) {
+    lx_battery *b = lxpanel_plugin_get_data(p);
+    return lxpanel_generic_config_dlg(_("Battery Monitor"),
+            panel, applyConfig, p,
+            _("Hide if there is no battery"), &b->hide_if_no_battery, CONF_TYPE_BOOL,
+            _("Alarm command"), &b->alarmCommand, CONF_TYPE_STR,
+            _("Alarm time (minutes left)"), &b->alarmTime, CONF_TYPE_INT,
+            _("Background color"), &b->backgroundColor, CONF_TYPE_STR,
+            _("Charging color 1"), &b->chargingColor1, CONF_TYPE_STR,
+            _("Charging color 2"), &b->chargingColor2, CONF_TYPE_STR,
+            _("Discharging color 1"), &b->dischargingColor1, CONF_TYPE_STR,
+            _("Discharging color 2"), &b->dischargingColor2, CONF_TYPE_STR,
+            _("Border width"), &b->requestedBorder, CONF_TYPE_INT,
+            _("Size"), &b->thickness, CONF_TYPE_INT,
+            _("Show Extended Information"), &b->show_extended_information, CONF_TYPE_BOOL,
+            NULL);
+}
+
+
+FM_DEFINE_MODULE(lxpanel_gtk, batt)
+
+/* Plugin descriptor. */
+LXPanelPluginInit fm_module_init_lxpanel_gtk = {
+    .name        = N_("Battery Monitor"),
+    .description = N_("Display battery status using ACPI"),
+
+    .new_instance = constructor,
+    .config      = config,
+    .reconfigure = orientation,
+    .button_press_event = buttonPressEvent
+};
+
+
+/* vim: set sw=4 sts=4 : */
diff --git a/plugins/batt/batt_sys.c b/plugins/batt/batt_sys.c
new file mode 100644 (file)
index 0000000..9262220
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ *      batt_sys.h
+ *
+ *      Copyright 2009 Juergen Hötzel <juergen@archlinux.org>
+ *
+ *     Parts shameless stolen and glibified from acpi package
+ *     Copyright (C) 2001  Grahame Bowland <grahame@angrygoats.net>
+ *     (C) 2008-2009  Michael Meskes  <meskes@debian.org>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "batt_sys.h"
+#include <glib/gstdio.h>
+
+/* shrug: get rid of this */
+#include <stdlib.h>
+#include <string.h>
+
+battery* battery_new() {
+    static int battery_num = 1;
+    battery * b = g_new0 ( battery, 1 );
+    b->type_battery = TRUE;
+    b->capacity_unit = "mAh";
+    b->energy_full = -1;
+    b->charge_full = -1;
+    b->voltage_now = -1;
+    b->energy_full_design = -1;
+    b->charge_full_design = -1;
+    b->energy_now = -1;
+    b->charge_now = -1;
+    b->current_now = -1;
+    b->power_now = -1;
+    b->state = NULL;
+    b->battery_num = battery_num;
+    b->seconds = -1;
+    b->percentage = -1;
+    b->poststr = NULL;
+    battery_num++;
+    return b;
+}
+
+
+static gchar* parse_info_file(battery *b, char *sys_file)
+{
+    char *buf = NULL;
+    gchar *value = NULL;
+    GString *filename = g_string_new(ACPI_PATH_SYS_POWER_SUPPY);
+
+    g_string_append_printf (filename, "/%s/%s", b->path, sys_file);
+
+    if (g_file_get_contents(filename->str, &buf, NULL, NULL) == TRUE) {
+        value = g_strdup( buf );
+        value = g_strstrip( value );
+        g_free( buf );
+    }
+
+    g_string_free(filename, TRUE);
+
+    return value;
+}
+
+/* get_gint_from_infofile():
+ *         If the sys_file exists, then its value is converted to an int,
+ *         divided by 1000, and returned.
+ *         Failure is indicated by returning -1. */
+static gint get_gint_from_infofile(battery *b, gchar *sys_file)
+{
+    gchar *file_content = parse_info_file(b, sys_file);
+
+    if (file_content != NULL)
+        return atoi(file_content) / 1000;
+
+    return -1;
+}
+
+static gchar* get_gchar_from_infofile(battery *b, gchar *sys_file)
+{
+    return parse_info_file(b, sys_file);
+}
+
+void battery_print(battery *b, int show_capacity)
+{
+    if ( b->type_battery )
+    {
+        if (b->state) {
+
+            printf("%s %d: %s, %d%%", BATTERY_DESC, b->battery_num - 1, b->state, b->percentage);
+
+            if (b->seconds > 0) {
+                int hours = b->seconds / 3600;
+                int seconds = b->seconds - 3600 * hours;
+                int minutes = seconds / 60;
+                seconds -= 60 * minutes;
+                printf(", %02d:%02d:%02d%s", hours, minutes, seconds,
+                        b->poststr);
+            } else if (b->poststr != NULL) {
+                printf(", %s", b->poststr);
+            }
+
+            printf("\n");
+
+            if (show_capacity && b->charge_full_design > 0) {
+                int percentage = -1;
+                int charge_full = -1;
+                if (b->charge_full <= 100) {
+                    /* some broken systems just give a percentage here */
+                    percentage = b->charge_full;
+                    charge_full = percentage * b->charge_full_design / 100;
+                } else {
+                    percentage = b->charge_full * 100 / b->charge_full_design;
+                    charge_full = b->charge_full;
+                }
+                if (percentage > 100)
+                    percentage = 100;
+
+                printf ("%s %d: design capacity %d %s, "
+                        "last full capacity %d %s = %d%%\n",
+                        BATTERY_DESC, b->battery_num - 1, b->charge_full_design,
+                        b->capacity_unit, charge_full, b->capacity_unit,
+                        percentage);
+            }
+        }
+    }
+}
+
+
+static gboolean battery_inserted(gchar* path)
+{
+    if (path == NULL)
+        return FALSE;
+
+    GString *dirname = g_string_new(ACPI_PATH_SYS_POWER_SUPPY);
+    GDir *dir;
+
+    g_string_append_printf (dirname, "/%s/", path);
+    dir = g_dir_open(dirname->str, 0, NULL);
+    if (dir)
+        g_dir_close(dir);
+    g_string_free(dirname, TRUE);
+
+    return dir ? TRUE : FALSE;
+}
+
+
+battery* battery_update(battery *b)
+{
+    gchar *gctmp;
+
+    if (b == NULL)
+        return NULL;
+
+    if (!battery_inserted(b->path))
+        return NULL;
+
+    /* read from sysfs */
+    b->charge_now = get_gint_from_infofile(b, "charge_now");
+    b->energy_now = get_gint_from_infofile(b, "energy_now");
+
+    b->current_now = get_gint_from_infofile(b, "current_now");
+    b->power_now   = get_gint_from_infofile(b, "power_now");
+    /* FIXME: Some battery drivers report -1000 when the discharge rate is
+     * unavailable. Others use negative values when discharging. Best we can do
+     * is to treat -1 as an error, and take the absolute value otherwise.
+     * Ideally the kernel would not export the sysfs file when the value is not
+     * available. */
+    if (b->current_now < -1)
+            b->current_now = - b->current_now;
+
+    b->charge_full = get_gint_from_infofile(b, "charge_full");
+    b->energy_full = get_gint_from_infofile(b, "energy_full");
+
+    b->charge_full_design = get_gint_from_infofile(b, "charge_full_design");
+    b->energy_full_design = get_gint_from_infofile(b, "energy_full_design");
+
+    b->voltage_now = get_gint_from_infofile(b, "voltage_now");
+
+    gctmp = get_gchar_from_infofile(b, "type");
+    b->type_battery = gctmp ? (strcasecmp(gctmp, "battery") == 0) : TRUE;
+
+    b->state = get_gchar_from_infofile(b, "status");
+    if (!b->state)
+        b->state = get_gchar_from_infofile(b, "state");
+    if (!b->state) {
+        if (b->charge_now != -1 || b->energy_now != -1
+                || b->charge_full != -1 || b->energy_full != -1)
+            b->state = "available";
+        else
+            b->state = "unavailable";
+    }
+
+
+    /* convert energy values (in mWh) to charge values (in mAh) if needed and possible */
+
+    if (b->energy_full != -1 && b->charge_full == -1) {
+        if (b->voltage_now != -1 && b->voltage_now != 0) {
+            b->charge_full = b->energy_full * 1000 / b->voltage_now;
+        } else {
+            b->charge_full = b->energy_full;
+            b->capacity_unit = "mWh";
+        }
+    }
+
+    if (b->energy_full_design != -1 && b->charge_full_design == -1) {
+        if (b->voltage_now != -1 && b->voltage_now != 0) {
+            b->charge_full_design = b->energy_full_design * 1000 / b->voltage_now;
+        } else {
+            b->charge_full_design = b->energy_full_design;
+            b->capacity_unit = "mWh";
+        }
+    }
+
+    if (b->energy_now != -1 && b->charge_now == -1) {
+        if (b->voltage_now != -1 && b->voltage_now != 0) {
+            b->charge_now = b->energy_now * 1000 / b->voltage_now;
+            if (b->current_now != -1)
+                b->current_now = b->current_now * 1000 / b->voltage_now;
+        } else {
+            b->charge_now = b->energy_now;
+        }
+    }
+
+    if (b->power_now < -1)
+        b->power_now = - b->power_now;
+    else if (b->power_now == -1 && b->voltage_now != -1 && b->current_now != -1)
+        b->power_now = b->voltage_now * b->current_now / 1000; // P = U*I
+    if (b->power_now != -1 && b->current_now == -1) {
+        if (b->voltage_now != -1 && b->voltage_now != 0)
+            b->current_now = b->power_now * 1000 / b->voltage_now;
+    }
+
+
+    if (b->charge_full < MIN_CAPACITY)
+        b->percentage = 0;
+    else {
+        int promille = (b->charge_now * 1000) / b->charge_full;
+        b->percentage = (promille + 5) / 10; /* round properly */
+    }
+    if (b->percentage > 100)
+        b->percentage = 100;
+
+
+    if (b->current_now == -1) {
+        b->poststr = "rate information unavailable";
+        b->seconds = -1;
+    } else if (!strcasecmp(b->state, "charging")) {
+        if (b->current_now > MIN_PRESENT_RATE) {
+            b->seconds = 3600 * (b->charge_full - b->charge_now) / b->current_now;
+            b->poststr = " until charged";
+        } else {
+            b->poststr = "charging at zero rate - will never fully charge.";
+            b->seconds = -1;
+        }
+    } else if (!strcasecmp(b->state, "discharging")) {
+        if (b->current_now > MIN_PRESENT_RATE) {
+            b->seconds = 3600 * b->charge_now / b->current_now;
+            b->poststr = " remaining";
+        } else {
+            b->poststr = "discharging at zero rate - will never fully discharge.";
+            b->seconds = -1;
+        }
+    } else {
+        b->poststr = NULL;
+        b->seconds = -1;
+    }
+
+    return b;
+}
+
+
+battery *battery_get() {
+    GError * error = NULL;
+    const gchar *entry;
+    GDir * dir = g_dir_open( ACPI_PATH_SYS_POWER_SUPPY, 0, &error );
+    battery *b = NULL;
+    if ( dir == NULL )
+    {
+        g_warning( "NO ACPI/sysfs support in kernel: %s", error->message );
+        return NULL;
+    }
+    while ( ( entry = g_dir_read_name (dir) ) != NULL )
+    {
+        b = battery_new();
+        b->path = g_strdup( entry );
+        battery_update ( b );
+        if ( b->type_battery == TRUE )
+            break;
+        /* ignore non-batteries */
+        else {
+            g_free(b);
+            b = NULL;
+        }
+    }
+    g_dir_close( dir );
+    return b;
+}
+
+void battery_free(battery* bat)
+{
+    if (bat) {
+        g_free(bat->path);
+        g_free(bat);
+    }
+}
+
+gboolean battery_is_charging( battery *b )
+{
+    if (!b->state)
+        return TRUE; // Same as "Unkown"
+    return ( strcasecmp( b->state, "Unknown" ) == 0 ||
+             strcasecmp( b->state, "Full" ) == 0
+             || strcasecmp( b->state, "Charging" ) == 0 );
+}
+
+gint battery_get_remaining( battery *b )
+{
+    return b->seconds;
+}
+
+
+/* vim: set sw=4 et sts=4 : */
diff --git a/plugins/batt/batt_sys.h b/plugins/batt/batt_sys.h
new file mode 100644 (file)
index 0000000..ad2bdbe
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ *      batt_sys.h
+ *
+ *      Copyright 2009 Juergen Hötzel <juergen@archlinux.org>
+ *
+ *      This program is free software; you can redistribute it and/or modify
+ *      it under the terms of the GNU General Public License as published by
+ *      the Free Software Foundation; either version 2 of the License, or
+ *      (at your option) any later version.
+ *
+ *      This program is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *      GNU General Public License for more details.
+ *
+ *      You should have received a copy of the GNU General Public License
+ *      along with this program; if not, write to the Free Software
+ *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ *      MA 02110-1301, USA.
+ */
+
+
+#ifndef BATT_SYS_H
+#define BATT_SYS_H
+
+
+#define BUF_SIZE 1024
+#define ACPI_PATH_SYS_POWER_SUPPY   "/sys/class/power_supply"
+#define MIN_CAPACITY    0.01
+#define MIN_PRESENT_RATE 0.01
+#define BATTERY_DESC   "Battery"
+
+#include <glib.h>
+
+typedef struct battery {
+    int battery_num;
+    /* path to battery dir */
+    gchar *path;
+    /* sysfs file contents */
+    int charge_now;
+    int energy_now;
+    int current_now;
+    int power_now;
+    int voltage_now;
+    int charge_full_design;
+    int energy_full_design;
+    int charge_full;
+    int energy_full;
+    /* extra info */
+    int seconds;
+    int percentage;
+    char *state, *poststr;
+    char* capacity_unit;
+    int type_battery;
+} battery;
+
+battery *battery_get();
+battery *battery_update( battery *b );
+void battery_print(battery *b, int show_capacity);
+gboolean battery_is_charging( battery *b );
+gint battery_get_remaining( battery *b );
+void battery_free(battery* bat);
+
+#endif
diff --git a/plugins/cpu/Makefile.am b/plugins/cpu/Makefile.am
new file mode 100644 (file)
index 0000000..38f54cd
--- /dev/null
@@ -0,0 +1,18 @@
+cpu_la_CFLAGS = \
+       -I$(top_srcdir)/src \
+       $(PACKAGE_CFLAGS) \
+       $(G_CAST_CHECKS)
+
+module_LTLIBRARIES = cpu.la
+
+moduledir = $(libdir)/lxpanel/plugins
+
+cpu_la_SOURCES = \
+       cpu.c
+
+cpu_la_LIBADD = \
+       $(PACKAGE_LIBS)
+
+cpu_la_LDFLAGS = \
+       -module \
+       @LXPANEL_MODULE@
diff --git a/plugins/cpu/cpu.c b/plugins/cpu/cpu.c
new file mode 100644 (file)
index 0000000..1cf3fcb
--- /dev/null
@@ -0,0 +1,284 @@
+/**
+ * CPU usage plugin to lxpanel
+ *
+ * Copyright (c) 2008-2014 LxDE Developers, see the file AUTHORS for details.
+ * Copyright (C) 2004 by Alexandre Pereira da Silva <alexandre.pereira@poli.usp.br>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+/*A little bug fixed by Mykola <mykola@2ka.mipt.ru>:) */
+
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+#include <sys/sysinfo.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#include "plugin.h"
+
+#define BORDER_SIZE 2
+#define PANEL_HEIGHT_DEFAULT 26 /* from panel defaults */
+
+/* #include "../../dbg.h" */
+
+typedef unsigned long long CPUTick;            /* Value from /proc/stat */
+typedef float CPUSample;                       /* Saved CPU utilization value as 0.0..1.0 */
+
+struct cpu_stat {
+    CPUTick u, n, s, i;                                /* User, nice, system, idle */
+};
+
+/* Private context for CPU plugin. */
+typedef struct {
+    GdkColor foreground_color;                 /* Foreground color for drawing area */
+    GtkWidget * da;                            /* Drawing area */
+    cairo_surface_t * pixmap;                          /* Pixmap to be drawn on drawing area */
+
+    guint timer;                               /* Timer for periodic update */
+    CPUSample * stats_cpu;                     /* Ring buffer of CPU utilization values */
+    unsigned int ring_cursor;                  /* Cursor for ring buffer */
+    guint pixmap_width;                                /* Width of drawing area pixmap; also size of ring buffer; does not include border size */
+    guint pixmap_height;                       /* Height of drawing area pixmap; does not include border size */
+    struct cpu_stat previous_cpu_stat;         /* Previous value of cpu_stat */
+} CPUPlugin;
+
+static void redraw_pixmap(CPUPlugin * c);
+static gboolean cpu_update(CPUPlugin * c);
+static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c);
+static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c);
+
+static void cpu_destructor(gpointer user_data);
+
+/* Redraw after timer callback or resize. */
+static void redraw_pixmap(CPUPlugin * c)
+{
+    cairo_t * cr = cairo_create(c->pixmap);
+    GtkStyle * style = gtk_widget_get_style(c->da);
+    cairo_set_line_width (cr, 1.0);
+    /* Erase pixmap. */
+    cairo_rectangle(cr, 0, 0, c->pixmap_width, c->pixmap_height);
+    gdk_cairo_set_source_color(cr, &style->black);
+    cairo_fill(cr);
+
+    /* Recompute pixmap. */
+    unsigned int i;
+    unsigned int drawing_cursor = c->ring_cursor;
+    gdk_cairo_set_source_color(cr, &c->foreground_color);
+    for (i = 0; i < c->pixmap_width; i++)
+    {
+        /* Draw one bar of the CPU usage graph. */
+        if (c->stats_cpu[drawing_cursor] != 0.0)
+        {
+            cairo_move_to(cr, i + 0.5, c->pixmap_height);
+            cairo_line_to(cr, i + 0.5, c->pixmap_height - c->stats_cpu[drawing_cursor] * c->pixmap_height);
+            cairo_stroke(cr);
+        }
+
+        /* Increment and wrap drawing cursor. */
+        drawing_cursor += 1;
+        if (drawing_cursor >= c->pixmap_width)
+            drawing_cursor = 0;
+    }
+
+    /* check_cairo_status(cr); */
+    cairo_destroy(cr);
+
+    /* Redraw pixmap. */
+    gtk_widget_queue_draw(c->da);
+}
+
+/* Periodic timer callback. */
+static gboolean cpu_update(CPUPlugin * c)
+{
+    if (g_source_is_destroyed(g_main_current_source()))
+        return FALSE;
+    if ((c->stats_cpu != NULL) && (c->pixmap != NULL))
+    {
+        /* Open statistics file and scan out CPU usage. */
+        struct cpu_stat cpu;
+        FILE * stat = fopen("/proc/stat", "r");
+        if (stat == NULL)
+            return TRUE;
+        int fscanf_result = fscanf(stat, "cpu %llu %llu %llu %llu", &cpu.u, &cpu.n, &cpu.s, &cpu.i);
+        fclose(stat);
+
+        /* Ensure that fscanf succeeded. */
+        if (fscanf_result == 4)
+        {
+            /* Compute delta from previous statistics. */
+            struct cpu_stat cpu_delta;
+            cpu_delta.u = cpu.u - c->previous_cpu_stat.u;
+            cpu_delta.n = cpu.n - c->previous_cpu_stat.n;
+            cpu_delta.s = cpu.s - c->previous_cpu_stat.s;
+            cpu_delta.i = cpu.i - c->previous_cpu_stat.i;
+
+            /* Copy current to previous. */
+            memcpy(&c->previous_cpu_stat, &cpu, sizeof(struct cpu_stat));
+
+            /* Compute user+nice+system as a fraction of total.
+             * Introduce this sample to ring buffer, increment and wrap ring buffer cursor. */
+            float cpu_uns = cpu_delta.u + cpu_delta.n + cpu_delta.s;
+            c->stats_cpu[c->ring_cursor] = cpu_uns / (cpu_uns + cpu_delta.i);
+            c->ring_cursor += 1;
+            if (c->ring_cursor >= c->pixmap_width)
+                c->ring_cursor = 0;
+
+            /* Redraw with the new sample. */
+            redraw_pixmap(c);
+        }
+    }
+    return TRUE;
+}
+
+/* Handler for configure_event on drawing area. */
+static gboolean configure_event(GtkWidget * widget, GdkEventConfigure * event, CPUPlugin * c)
+{
+    GtkAllocation allocation;
+
+    gtk_widget_get_allocation(widget, &allocation);
+    /* Allocate pixmap and statistics buffer without border pixels. */
+    guint new_pixmap_width = MAX(allocation.width - BORDER_SIZE * 2, 0);
+    guint new_pixmap_height = MAX(allocation.height - BORDER_SIZE * 2, 0);
+    if ((new_pixmap_width > 0) && (new_pixmap_height > 0))
+    {
+        /* If statistics buffer does not exist or it changed size, reallocate and preserve existing data. */
+        if ((c->stats_cpu == NULL) || (new_pixmap_width != c->pixmap_width))
+        {
+            CPUSample * new_stats_cpu = g_new0(typeof(*c->stats_cpu), new_pixmap_width);
+            if (c->stats_cpu != NULL)
+            {
+                if (new_pixmap_width > c->pixmap_width)
+                {
+                    /* New allocation is larger.
+                     * Introduce new "oldest" samples of zero following the cursor. */
+                    memcpy(&new_stats_cpu[0],
+                        &c->stats_cpu[0], c->ring_cursor * sizeof(CPUSample));
+                    memcpy(&new_stats_cpu[new_pixmap_width - c->pixmap_width + c->ring_cursor],
+                        &c->stats_cpu[c->ring_cursor], (c->pixmap_width - c->ring_cursor) * sizeof(CPUSample));
+                }
+                else if (c->ring_cursor <= new_pixmap_width)
+                {
+                    /* New allocation is smaller, but still larger than the ring buffer cursor.
+                     * Discard the oldest samples following the cursor. */
+                    memcpy(&new_stats_cpu[0],
+                        &c->stats_cpu[0], c->ring_cursor * sizeof(CPUSample));
+                    memcpy(&new_stats_cpu[c->ring_cursor],
+                        &c->stats_cpu[c->pixmap_width - new_pixmap_width + c->ring_cursor], (new_pixmap_width - c->ring_cursor) * sizeof(CPUSample));
+                }
+                else
+                {
+                    /* New allocation is smaller, and also smaller than the ring buffer cursor.
+                     * Discard all oldest samples following the ring buffer cursor and additional samples at the beginning of the buffer. */
+                    memcpy(&new_stats_cpu[0],
+                        &c->stats_cpu[c->ring_cursor - new_pixmap_width], new_pixmap_width * sizeof(CPUSample));
+                    c->ring_cursor = 0;
+                }
+                g_free(c->stats_cpu);
+            }
+            c->stats_cpu = new_stats_cpu;
+        }
+
+        /* Allocate or reallocate pixmap. */
+        c->pixmap_width = new_pixmap_width;
+        c->pixmap_height = new_pixmap_height;
+        if (c->pixmap)
+            cairo_surface_destroy(c->pixmap);
+        c->pixmap = cairo_image_surface_create(CAIRO_FORMAT_RGB24, c->pixmap_width, c->pixmap_height);
+        /* check_cairo_surface_status(&c->pixmap); */
+
+        /* Redraw pixmap at the new size. */
+        redraw_pixmap(c);
+    }
+    return TRUE;
+}
+
+/* Handler for expose_event on drawing area. */
+static gboolean expose_event(GtkWidget * widget, GdkEventExpose * event, CPUPlugin * c)
+{
+    /* Draw the requested part of the pixmap onto the drawing area.
+     * Translate it in both x and y by the border size. */
+    if (c->pixmap != NULL)
+    {
+        cairo_t * cr = gdk_cairo_create(gtk_widget_get_window(widget));
+        GtkStyle * style = gtk_widget_get_style(c->da);
+        gdk_cairo_region(cr, event->region);
+        cairo_clip(cr);
+        gdk_cairo_set_source_color(cr, &style->black);
+        cairo_set_source_surface(cr, c->pixmap,
+              BORDER_SIZE, BORDER_SIZE);
+        cairo_paint(cr);
+        /* check_cairo_status(cr); */
+        cairo_destroy(cr);
+    }
+    return FALSE;
+}
+
+/* Plugin constructor. */
+static GtkWidget *cpu_constructor(LXPanel *panel, config_setting_t *settings)
+{
+    /* Allocate plugin context and set into Plugin private data pointer. */
+    CPUPlugin * c = g_new0(CPUPlugin, 1);
+    GtkWidget * p;
+
+    /* Allocate top level widget and set into Plugin widget pointer. */
+    p = gtk_event_box_new();
+    lxpanel_plugin_set_data(p, c, cpu_destructor);
+    gtk_container_set_border_width(GTK_CONTAINER(p), 1);
+    gtk_widget_set_has_window(p, FALSE);
+
+    /* Allocate drawing area as a child of top level widget.  Enable button press events. */
+    c->da = gtk_drawing_area_new();
+    gtk_widget_set_size_request(c->da, 40, PANEL_HEIGHT_DEFAULT);
+    gtk_widget_add_events(c->da, GDK_BUTTON_PRESS_MASK);
+    gtk_container_add(GTK_CONTAINER(p), c->da);
+
+    /* Clone a graphics context and set "green" as its foreground color.
+     * We will use this to draw the graph. */
+    gdk_color_parse("green",  &c->foreground_color);
+
+    /* Connect signals. */
+    g_signal_connect(G_OBJECT(c->da), "configure-event", G_CALLBACK(configure_event), (gpointer) c);
+    g_signal_connect(G_OBJECT(c->da), "expose-event", G_CALLBACK(expose_event), (gpointer) c);
+
+    /* Show the widget.  Connect a timer to refresh the statistics. */
+    gtk_widget_show(c->da);
+    c->timer = g_timeout_add(1500, (GSourceFunc) cpu_update, (gpointer) c);
+    return p;
+}
+
+/* Plugin destructor. */
+static void cpu_destructor(gpointer user_data)
+{
+    CPUPlugin * c = (CPUPlugin *)user_data;
+
+    /* Disconnect the timer. */
+    g_source_remove(c->timer);
+
+    /* Deallocate memory. */
+    cairo_surface_destroy(c->pixmap);
+    g_free(c->stats_cpu);
+    g_free(c);
+}
+
+FM_DEFINE_MODULE(lxpanel_gtk, cpu)
+
+/* Plugin descriptor. */
+LXPanelPluginInit fm_module_init_lxpanel_gtk = {
+    .name = N_("CPU Usage Monitor"),
+    .description = N_("Display CPU usage"),
+    .new_instance = cpu_constructor,
+};
diff --git a/plugins/cpufreq/Makefile.am b/plugins/cpufreq/Makefile.am
new file mode 100644 (file)
index 0000000..593c69b
--- /dev/null
@@ -0,0 +1,20 @@
+cpufreq_la_CFLAGS = \
+       -I. \
+       -I$(top_srcdir)/src \
+       $(PACKAGE_CFLAGS) \
+       -DPACKAGE_DATA_DIR=\""$(datadir)/lxpanel"\" \
+       $(G_CAST_CHECKS)
+
+module_LTLIBRARIES = cpufreq.la
+
+moduledir = $(libdir)/lxpanel/plugins
+
+cpufreq_la_SOURCES = \
+       cpufreq.c
+
+cpufreq_la_LIBADD = \
+       $(PACKAGE_LIBS)
+
+cpufreq_la_LDFLAGS = \
+       -module \
+       @LXPANEL_MODULE@
diff --git a/plugins/cpufreq/cpufreq.c b/plugins/cpufreq/cpufreq.c
new file mode 100644 (file)
index 0000000..327acd5
--- /dev/null
@@ -0,0 +1,415 @@
+/**
+ * CPUFreq plugin to lxpanel
+ *
+ * Copyright (C) 2009 by Daniel Kesler <kesler.daniel@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <string.h>
+
+#include "plugin.h"
+#include "misc.h"
+
+#include "dbg.h"
+
+#define PROC_ICON            PACKAGE_DATA_DIR "/images/cpufreq-icon.png"
+#define SYSFS_CPU_DIRECTORY "/sys/devices/system/cpu"
+#define SCALING_GOV         "scaling_governor"
+#define SCALING_AGOV        "scaling_available_governors"
+#define SCALING_AFREQ       "scaling_available_frequencies"
+#define SCALING_CUR_FREQ    "scaling_cur_freq"
+#define SCALING_SETFREQ     "scaling_setspeed"
+#define SCALING_MAX         "scaling_max_freq"
+#define SCALING_MIN         "scaling_min_freq"
+
+
+typedef struct {
+    GtkWidget *main;
+    config_setting_t *settings;
+    GtkWidget *namew;
+    GList *governors;
+    GList *cpus;
+    int has_cpufreq;
+    char* cur_governor;
+    int   cur_freq;
+    unsigned int timer;
+    //gboolean remember;
+} cpufreq;
+
+typedef struct {
+    char *data;
+    cpufreq *cf;
+} Param;
+
+static void cpufreq_destructor(gpointer user_data);
+
+static void
+get_cur_governor(cpufreq *cf){
+    FILE *fp;
+    char buf[ 100 ], sstmp [ 256 ];
+
+    snprintf(sstmp, sizeof(sstmp), "%s/%s", (char*)cf->cpus->data, SCALING_GOV);
+    if ((fp = fopen( sstmp, "r")) != NULL) {
+        if(cf->cur_governor)
+        {
+          g_free(cf->cur_governor);
+          cf->cur_governor = NULL;
+        }
+        if (fgets(buf, 100, fp))
+        {
+            buf[strlen(buf)-1] = '\0';
+            cf->cur_governor = strdup(buf);
+        }
+        fclose(fp);
+    }
+}
+
+static void
+get_cur_freq(cpufreq *cf){
+    FILE *fp;
+    char buf[ 100 ], sstmp [ 256 ];
+
+    snprintf(sstmp, sizeof(sstmp), "%s/%s", (char*)cf->cpus->data, SCALING_CUR_FREQ);
+    if ((fp = fopen( sstmp, "r")) != NULL) {
+        if (fgets(buf, 100, fp))
+        {
+            buf[strlen(buf)-1] = '\0';
+            cf->cur_freq = atoi(buf);
+        }
+        fclose(fp);
+    }
+}
+
+/*static void
+get_governors(cpufreq *cf){
+    FILE *fp;
+    GList *l;
+    char buf[ 100 ], sstmp [ 256 ], c, bufl = 0;
+
+    g_list_free(cf->governors);
+    cf->governors = NULL;
+
+    get_cur_governor(cf);
+
+    if(cf->cpus == NULL){
+        cf->governors = NULL;
+        return;
+    }
+    sprintf(sstmp,"%s/%s",cf->cpus->data, SCALING_AGOV);
+
+    if (!(fp = fopen( sstmp, "r"))) {
+        printf("cpufreq: cannot open %s\n",sstmp);
+        return;
+    }
+
+    while((c = fgetc(fp)) != EOF){
+        if(c == ' '){
+            if(bufl > 1){
+                buf[bufl] = '\0';
+                cf->governors = g_list_append(cf->governors, strdup(buf));
+            }
+            bufl = 0;
+            buf[0] = '\0';
+        }else{
+            buf[bufl++] = c;
+        }
+    }
+
+    fclose(fp);
+}
+
+static void
+cpufreq_set_freq(GtkWidget *widget, Param* p){
+    FILE *fp;
+    char buf[ 100 ], sstmp [ 256 ];
+
+    if(strcmp(p->cf->cur_governor, "userspace")) return;
+
+    sprintf(sstmp,"%s/%s",p->cf->cpus->data, SCALING_SETFREQ);
+    if ((fp = fopen( sstmp, "w")) != NULL) {
+        fprintf(fp,"%s",p->data);
+        fclose(fp);
+    }
+}
+
+static GtkWidget *
+frequency_menu(cpufreq *cf){
+    FILE *fp;
+    Param* param;
+    char buf[ 100 ], sstmp [ 256 ], c, bufl = 0;
+
+    sprintf(sstmp,"%s/%s",cf->cpus->data, SCALING_AFREQ);
+
+    if (!(fp = fopen( sstmp, "r"))) {
+        printf("cpufreq: cannot open %s\n",sstmp);
+        return 0;
+    }
+
+    GtkMenu* menu = GTK_MENU(gtk_menu_new());
+    GtkWidget* menuitem;
+
+    while((c = fgetc(fp)) != EOF){
+        if(c == ' '){
+            if(bufl > 1){
+                buf[bufl] = '\0';
+                menuitem = gtk_menu_item_new_with_label(strdup(buf));
+                gtk_menu_append (GTK_MENU_SHELL (menu), menuitem);
+                gtk_widget_show (menuitem);
+                param = g_new0(Param, 1);
+                param->data = strdup(buf);
+                param->cf = cf;
+                g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(cpufreq_set_freq), param);
+                g_object_weak_ref(G_OBJECT(menuitem), (GWeakNotify)g_free, param);
+            }
+            bufl = 0;
+            buf[0] = '\0';
+        }else{
+            buf[bufl++] = c;
+        }
+    }
+
+    fclose(fp);
+    return GTK_WIDGET(menu);
+}*/
+
+static void
+get_cpus(cpufreq *cf)
+{
+
+    const char *cpu;
+    char cpu_path[100];
+
+    GDir * cpuDirectory = g_dir_open(SYSFS_CPU_DIRECTORY, 0, NULL);
+    if (cpuDirectory == NULL)
+    {
+        cf->cpus = NULL;
+        printf("cpufreq: no cpu found\n");
+        return;
+    }
+
+    while ((cpu = g_dir_read_name(cpuDirectory)))
+    {
+        /* Look for directories of the form "cpu<n>", where "<n>" is a decimal integer. */
+        if ((strncmp(cpu, "cpu", 3) == 0) && (cpu[3] >= '0') && (cpu[3] <= '9'))
+        {
+            snprintf(cpu_path, sizeof(cpu_path), "%s/%s/cpufreq", SYSFS_CPU_DIRECTORY, cpu);
+
+            GDir * cpufreqDir = g_dir_open(SYSFS_CPU_DIRECTORY, 0, NULL);
+            if (cpufreqDir == NULL)
+            {
+                cf->cpus = NULL;
+                cf->has_cpufreq = 0;
+                break;
+            }
+
+            cf->has_cpufreq = 1;
+            cf->cpus = g_list_append(cf->cpus, strdup(cpu_path));
+        }
+    }
+    g_dir_close(cpuDirectory);
+}
+
+/*static void
+cpufreq_set_governor(GtkWidget *widget, Param* p){
+    FILE *fp;
+    char buf[ 100 ], sstmp [ 256 ];
+
+    sprintf(sstmp, "%s/%s", p->cf->cpus->data, SCALING_GOV);
+    if ((fp = fopen( sstmp, "w")) != NULL) {
+        fprintf(fp,"%s",p->data);
+        fclose(fp);
+    }
+}
+
+static GtkWidget *
+cpufreq_menu(cpufreq *cf){
+    GList *l;
+    GSList *group;
+    char buff[100];
+    GtkMenuItem* menuitem;
+    Param* param;
+
+    GtkMenu* menu = GTK_MENU(gtk_menu_new());
+    g_signal_connect(menu, "selection-done", G_CALLBACK(gtk_widget_destroy), NULL);
+
+    get_governors(cf);
+    group = NULL;
+
+    if((cf->governors == NULL) || (!cf->has_cpufreq) || (cf->cur_governor == NULL)){
+        menuitem = GTK_MENU_ITEM(gtk_menu_item_new_with_label("CPUFreq not supported"));
+        gtk_menu_append (GTK_MENU_SHELL (menu), GTK_WIDGET (menuitem));
+        gtk_widget_show (GTK_WIDGET (menuitem));
+        return GTK_WIDGET(menu);
+    }
+
+    if(strcmp(cf->cur_governor, "userspace") == 0){
+        menuitem = GTK_MENU_ITEM(gtk_menu_item_new_with_label("  Frequency"));
+        gtk_menu_append (GTK_MENU_SHELL (menu), GTK_WIDGET (menuitem));
+        gtk_widget_show (GTK_WIDGET (menuitem));
+        gtk_menu_item_set_submenu(menuitem, frequency_menu(cf));
+        menuitem = GTK_MENU_ITEM(gtk_separator_menu_item_new());
+        gtk_menu_append (GTK_MENU_SHELL (menu), GTK_WIDGET (menuitem));
+        gtk_widget_show (GTK_WIDGET(menuitem));
+    }
+
+    for( l = cf->governors; l; l = l->next )
+    {
+      if(strcmp((char*)l->data, cf->cur_governor) == 0){
+        sprintf(buff,"> %s", l->data);
+        menuitem = GTK_MENU_ITEM(gtk_menu_item_new_with_label(strdup(buff)));
+      }else{
+        sprintf(buff,"   %s", l->data);
+        menuitem = GTK_MENU_ITEM(gtk_menu_item_new_with_label(strdup(buff)));
+      }
+
+      gtk_menu_shell_append (GTK_MENU_SHELL (menu), GTK_WIDGET (menuitem));
+      gtk_widget_show (GTK_WIDGET (menuitem));
+      param = g_new0(Param, 1);
+      param->data = l->data;
+      param->cf = cf;
+      g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(cpufreq_set_governor), param);
+      g_object_weak_ref(G_OBJECT(menuitem), (GWeakNotify) g_free, param);
+    }
+
+    return GTK_WIDGET (menu);
+}*/
+
+
+
+static  gboolean
+clicked(GtkWidget *widget, GdkEventButton *evt, LXPanel *panel)
+{
+    ENTER2;
+
+    /* Standard right-click handling. */
+    if( evt->button == 1 )
+    {
+// Setting governor can't work without root privilege
+//      gtk_menu_popup( cpufreq_menu((cpufreq*)plugin->priv), NULL, NULL, NULL, NULL,
+//                      evt->button, evt->time );
+      return TRUE;
+    }
+
+    RET2(FALSE);
+}
+
+static gboolean
+_update_tooltip(cpufreq *cf)
+{
+    char *tooltip;
+
+    get_cur_freq(cf);
+    get_cur_governor(cf);
+
+    ENTER;
+
+    tooltip = g_strdup_printf(_("Frequency: %d MHz\nGovernor: %s"),
+                              cf->cur_freq / 1000, cf->cur_governor);
+    gtk_widget_set_tooltip_text(cf->main, tooltip);
+    g_free(tooltip);
+    RET(TRUE);
+}
+
+static gboolean update_tooltip(gpointer user_data)
+{
+    if (g_source_is_destroyed(g_main_current_source()))
+        return FALSE;
+    return _update_tooltip(user_data);
+}
+
+static GtkWidget *cpufreq_constructor(LXPanel *panel, config_setting_t *settings)
+{
+    cpufreq *cf;
+    //GtkWidget *button;
+
+    ENTER;
+    cf = g_new0(cpufreq, 1);
+    g_return_val_if_fail(cf != NULL, NULL);
+    cf->governors = NULL;
+    cf->cpus = NULL;
+    cf->settings = settings;
+
+    cf->main = gtk_event_box_new();
+    lxpanel_plugin_set_data(cf->main, cf, cpufreq_destructor);
+    gtk_widget_set_has_window(cf->main, FALSE);
+    gtk_container_set_border_width(GTK_CONTAINER(cf->main), 2);
+
+    cf->namew = gtk_image_new_from_file(PROC_ICON);
+    gtk_container_add(GTK_CONTAINER(cf->main), cf->namew);
+
+    cf->has_cpufreq = 0;
+
+    get_cpus(cf);
+
+    //if (config_setting_lookup_int(settings, "Remember", &tmp_int)) cf->remember = tmp_int != 0;
+    //if (config_setting_lookup_int(settings, "Governor", &tmp_str)) cf->cur_governor = g_strdup(tmp_str);
+    //config_setting_lookup_int(settings, "Frequency", &cf->cur_freq);
+
+    _update_tooltip(cf);
+    cf->timer = g_timeout_add_seconds(2, update_tooltip, (gpointer)cf);
+
+    gtk_widget_show(cf->namew);
+
+    RET(cf->main);
+}
+
+/*
+static gboolean applyConfig(gpointer user_data)
+{
+    cpufreq *cf = lxpanel_plugin_get_data(user_data);
+
+    config_group_set_int(cf->settings, "Remember", cf->remember);
+    return FALSE;
+}
+
+static GtkWidget *config(LXPanel *panel, GtkWidget *p, GtkWindow *parent)
+{
+    cpufreq *cf = lxpanel_plugin_get_data(p);
+    return lxpanel_generic_config_dlg(_("CPUFreq frontend"), panel, applyConfig, p,
+            _("Remember governor and frequency"), &cf->remember, CONF_TYPE_BOOL,
+            NULL);
+}
+*/
+
+static void
+cpufreq_destructor(gpointer user_data)
+{
+    cpufreq *cf = (cpufreq *)user_data;
+    g_list_free ( cf->cpus );
+    g_list_free ( cf->governors );
+    g_source_remove(cf->timer);
+    g_free(cf);
+}
+
+
+FM_DEFINE_MODULE(lxpanel_gtk, cpufreq)
+
+/* Plugin descriptor. */
+LXPanelPluginInit fm_module_init_lxpanel_gtk = {
+    .name = N_("CPUFreq frontend"),
+    .description = N_("Display CPU frequency and allow to change governors and frequency"),
+
+    .new_instance = cpufreq_constructor,
+    //.config      = config,
+    .button_press_event = clicked
+};
diff --git a/plugins/dclock.c b/plugins/dclock.c
new file mode 100644 (file)
index 0000000..ffc5656
--- /dev/null
@@ -0,0 +1,448 @@
+/**
+ * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "plugin.h"
+#include "misc.h"
+
+#include <libfm/fm-gtk.h>
+
+#include <time.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib/gi18n.h>
+
+#include "dbg.h"
+
+#define DEFAULT_TIP_FORMAT    "%A %x"
+#define DEFAULT_CLOCK_FORMAT  "%R"
+
+/* Private context for digital clock plugin. */
+typedef struct {
+    GtkWidget * plugin;                                /* Back pointer to plugin */
+    LXPanel * panel;
+    config_setting_t *settings;
+    GtkWidget * clock_label;                   /* Label containing clock value */
+    GtkWidget * clock_icon;                    /* Icon when icon_only */
+    GtkWidget * calendar_window;               /* Calendar window, if it is being displayed */
+    char * clock_format;                       /* Format string for clock value */
+    char * tooltip_format;                     /* Format string for tooltip value */
+    char * action;                             /* Command to execute on a click */
+    gboolean bold;                             /* True if bold font */
+    gboolean icon_only;                                /* True if icon only (no clock value) */
+    gboolean center_text;
+    guint timer;                               /* Timer for periodic update */
+    enum {
+       AWAITING_FIRST_CHANGE,                  /* Experimenting to determine interval, waiting for first change */
+       AWAITING_SECOND_CHANGE,                 /* Experimenting to determine interval, waiting for second change */
+       ONE_SECOND_INTERVAL,                    /* Determined that one second interval is necessary */
+       ONE_MINUTE_INTERVAL                     /* Determined that one minute interval is sufficient */
+    } expiration_interval;
+    int experiment_count;                      /* Count of experiments that have been done to determine interval */
+    char * prev_clock_value;                   /* Previous value of clock */
+    char * prev_tooltip_value;                 /* Previous value of tooltip */
+} DClockPlugin;
+
+static gboolean dclock_update_display(DClockPlugin * dc);
+static void dclock_destructor(gpointer user_data);
+static gboolean dclock_apply_configuration(gpointer user_data);
+
+/* Handler for "map" signal on popup window. */
+static void dclock_popup_map(GtkWidget * widget, DClockPlugin * dc)
+{
+    lxpanel_plugin_adjust_popup_position(widget, dc->plugin);
+}
+
+/* Display a window containing the standard calendar widget. */
+static GtkWidget * dclock_create_calendar(DClockPlugin * dc)
+{
+    /* Create a new window. */
+    GtkWidget * win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+    gtk_window_set_default_size(GTK_WINDOW(win), 180, 180);
+    gtk_window_set_decorated(GTK_WINDOW(win), FALSE);
+    gtk_window_set_resizable(GTK_WINDOW(win), FALSE);
+    gtk_container_set_border_width(GTK_CONTAINER(win), 5);
+    gtk_window_set_skip_taskbar_hint(GTK_WINDOW(win), TRUE);
+    gtk_window_set_skip_pager_hint(GTK_WINDOW(win), TRUE);
+    gtk_window_set_type_hint(GTK_WINDOW(win), GDK_WINDOW_TYPE_HINT_UTILITY);
+    gtk_window_stick(GTK_WINDOW(win));
+
+    /* Create a vertical box as a child of the window. */
+    GtkWidget * box = gtk_vbox_new(FALSE, 0);
+    gtk_container_add(GTK_CONTAINER(win), GTK_WIDGET(box));
+
+    /* Create a standard calendar widget as a child of the vertical box. */
+    GtkWidget * calendar = gtk_calendar_new();
+    gtk_calendar_set_display_options(
+        GTK_CALENDAR(calendar),
+        GTK_CALENDAR_SHOW_WEEK_NUMBERS | GTK_CALENDAR_SHOW_DAY_NAMES | GTK_CALENDAR_SHOW_HEADING);
+    gtk_box_pack_start(GTK_BOX(box), calendar, TRUE, TRUE, 0);
+
+    /* Connect signals. */
+    g_signal_connect(G_OBJECT(win), "map", G_CALLBACK(dclock_popup_map), dc);
+
+    /* Return the widget. */
+    return win;
+}
+
+/* Handler for "button-press-event" event from main widget. */
+static gboolean dclock_button_press_event(GtkWidget * widget, GdkEventButton * evt, LXPanel * panel)
+{
+    DClockPlugin * dc = lxpanel_plugin_get_data(widget);
+
+    /* If an action is set, execute it. */
+    if (dc->action != NULL)
+        fm_launch_command_simple(NULL, NULL, 0, dc->action, NULL);
+
+    /* If no action is set, toggle the presentation of the calendar. */
+    else
+    {
+        if (dc->calendar_window == NULL)
+        {
+            dc->calendar_window = dclock_create_calendar(dc);
+            gtk_widget_show_all(dc->calendar_window);
+        }
+        else
+        {
+            gtk_widget_destroy(dc->calendar_window);
+            dc->calendar_window = NULL;
+        }
+    }
+    return TRUE;
+}
+
+/* Set the timer. */
+static void dclock_timer_set(DClockPlugin * dc, struct timeval *current_time)
+{
+    int milliseconds = 1000;
+
+    /* Get current time to millisecond resolution. */
+    if (gettimeofday(current_time, NULL) >= 0)
+    {
+        /* Compute number of milliseconds until next second boundary. */
+        milliseconds = 1000 - (current_time->tv_usec / 1000);
+
+        /* If the expiration interval is the minute boundary,
+         * add number of milliseconds after that until next minute boundary. */
+        if (dc->expiration_interval == ONE_MINUTE_INTERVAL)
+        {
+            time_t seconds = 60 - (current_time->tv_sec - (current_time->tv_sec / 60) * 60);
+            milliseconds += seconds * 1000;
+        }
+    }
+
+    /* Be defensive, and set the timer. */
+    if (milliseconds <= 0)
+        milliseconds = 1000;
+    dc->timer = g_timeout_add(milliseconds, (GSourceFunc) dclock_update_display, (gpointer) dc);
+}
+
+/* Periodic timer callback.
+ * Also used during initialization and configuration change to do a redraw. */
+static gboolean dclock_update_display(DClockPlugin * dc)
+{
+    /* Determine the current time. */
+    struct timeval now;
+    struct tm * current_time;
+
+    if (g_source_is_destroyed(g_main_current_source()))
+        return FALSE;
+
+    dclock_timer_set(dc, &now);
+    current_time = localtime(&now.tv_sec);
+
+    /* Determine the content of the clock label and tooltip. */
+    char clock_value[64];
+    char tooltip_value[64];
+    clock_value[0] = '\0';
+    if (dc->clock_format != NULL)
+        strftime(clock_value, sizeof(clock_value), dc->clock_format, current_time);
+    tooltip_value[0] = '\0';
+    if (dc->tooltip_format != NULL)
+        strftime(tooltip_value, sizeof(tooltip_value), dc->tooltip_format, current_time);
+
+    /* When we write the clock value, it causes the panel to do a full relayout.
+     * Since this function may be called too often while the timing experiment is underway,
+     * we take the trouble to check if the string actually changed first. */
+    if (( ! dc->icon_only)
+    && ((dc->prev_clock_value == NULL) || (strcmp(dc->prev_clock_value, clock_value) != 0)))
+    {
+        /* Convert "\n" escapes in the user's format string to newline characters. */
+        char * newlines_converted = NULL;
+        if (strstr(clock_value, "\\n") != NULL)
+        {
+            newlines_converted = g_strdup(clock_value);        /* Just to get enough space for the converted result */
+            char * p;
+            char * q;
+            for (p = clock_value, q = newlines_converted; *p != '\0'; p += 1)
+            {
+                if ((p[0] == '\\') && (p[1] == 'n'))
+                {
+                    *q++ = '\n';
+                    p += 1;
+                }
+                else
+                    *q++ = *p;
+            }
+            *q = '\0';
+        }
+
+        gchar * utf8 = g_locale_to_utf8(((newlines_converted != NULL) ? newlines_converted : clock_value), -1, NULL, NULL, NULL);
+        if (utf8 != NULL)
+        {
+            lxpanel_draw_label_text(dc->panel, dc->clock_label, utf8, dc->bold, 1, TRUE);
+            g_free(utf8);
+        }
+        g_free(newlines_converted);
+    }
+
+    /* Determine the content of the tooltip. */
+    gchar * utf8 = g_locale_to_utf8(tooltip_value, -1, NULL, NULL, NULL);
+    if (utf8 != NULL)
+    {
+        gtk_widget_set_tooltip_text(dc->plugin, utf8);
+        g_free(utf8);
+    }
+
+    /* Conduct an experiment to see how often the value changes.
+     * Use this to decide whether we update the value every second or every minute.
+     * We need to account for the possibility that the experiment is being run when we cross a minute boundary. */
+    if (dc->expiration_interval < ONE_SECOND_INTERVAL)
+    {
+        if (dc->prev_clock_value == NULL)
+        {
+            /* Initiate the experiment. */
+            dc->prev_clock_value = g_strdup(clock_value);
+            dc->prev_tooltip_value = g_strdup(tooltip_value);
+        }
+        else
+        {
+            if (((dc->icon_only) || (strcmp(dc->prev_clock_value, clock_value) == 0))
+            && (strcmp(dc->prev_tooltip_value, tooltip_value) == 0))
+            {
+                dc->experiment_count += 1;
+                if (dc->experiment_count > 3)
+                {
+                    /* No change within 3 seconds.  Assume change no more often than once per minute. */
+                    dc->expiration_interval = ONE_MINUTE_INTERVAL;
+                    g_free(dc->prev_clock_value);
+                    g_free(dc->prev_tooltip_value);
+                    dc->prev_clock_value = NULL;
+                    dc->prev_tooltip_value = NULL;
+                }
+            }
+            else if (dc->expiration_interval == AWAITING_FIRST_CHANGE)
+            {
+                /* We have a change at the beginning of the experiment, but we do not know when the next change might occur.
+                 * Continue the experiment for 3 more seconds. */
+                dc->expiration_interval = AWAITING_SECOND_CHANGE;
+                dc->experiment_count = 0;
+                g_free(dc->prev_clock_value);
+                g_free(dc->prev_tooltip_value);
+                dc->prev_clock_value = g_strdup(clock_value);
+                dc->prev_tooltip_value = g_strdup(tooltip_value);
+            }
+            else
+            {
+                /* We have a second change.  End the experiment. */
+                dc->expiration_interval = ((dc->experiment_count > 3) ? ONE_MINUTE_INTERVAL : ONE_SECOND_INTERVAL);
+                g_free(dc->prev_clock_value);
+                g_free(dc->prev_tooltip_value);
+                dc->prev_clock_value = NULL;
+                dc->prev_tooltip_value = NULL;
+            }
+        }
+    }
+
+    /* Reset the timer and return. */
+    return FALSE;
+}
+
+/* Plugin constructor. */
+static GtkWidget *dclock_constructor(LXPanel *panel, config_setting_t *settings)
+{
+    /* Allocate and initialize plugin context and set into Plugin private data pointer. */
+    DClockPlugin * dc = g_new0(DClockPlugin, 1);
+    GtkWidget * p;
+    const char *str;
+    int tmp_int;
+
+    /* Load parameters from the configuration file. */
+    if (config_setting_lookup_string(settings, "ClockFmt", &str))
+        dc->clock_format = g_strdup(str);
+    if (config_setting_lookup_string(settings, "TooltipFmt", &str))
+        dc->tooltip_format = g_strdup(str);
+    if (config_setting_lookup_string(settings, "Action", &str))
+        dc->action = g_strdup(str);
+    if (config_setting_lookup_int(settings, "BoldFont", &tmp_int))
+        dc->bold = tmp_int != 0;
+    if (config_setting_lookup_int(settings, "IconOnly", &tmp_int))
+        dc->icon_only = tmp_int != 0;
+    if (config_setting_lookup_int(settings, "CenterText", &tmp_int))
+        dc->center_text = tmp_int != 0;
+
+    /* Save construction pointers */
+    dc->panel = panel;
+    dc->settings = settings;
+
+    /* Allocate top level widget and set into Plugin widget pointer. */
+    dc->plugin = p = gtk_event_box_new();
+    lxpanel_plugin_set_data(p, dc, dclock_destructor);
+
+    /* Allocate a horizontal box as the child of the top level. */
+    GtkWidget * hbox = gtk_hbox_new(TRUE, 0);
+    gtk_container_add(GTK_CONTAINER(p), hbox);
+    gtk_widget_show(hbox);
+
+    /* Create a label and an image as children of the horizontal box.
+     * Only one of these is visible at a time, controlled by user preference. */
+    dc->clock_label = gtk_label_new(NULL);
+    gtk_misc_set_alignment(GTK_MISC(dc->clock_label), 0.5, 0.5);
+    gtk_misc_set_padding(GTK_MISC(dc->clock_label), 4, 0);
+    gtk_container_add(GTK_CONTAINER(hbox), dc->clock_label);
+    dc->clock_icon = gtk_image_new();
+    gtk_container_add(GTK_CONTAINER(hbox), dc->clock_icon);
+
+    /* Initialize the clock display. */
+    if (dc->clock_format == NULL)
+        dc->clock_format = g_strdup(_(DEFAULT_CLOCK_FORMAT));
+    if (dc->tooltip_format == NULL)
+        dc->tooltip_format = g_strdup(_(DEFAULT_TIP_FORMAT));
+    dclock_apply_configuration(p);
+
+    /* Show the widget and return. */
+    return p;
+}
+
+/* Plugin destructor. */
+static void dclock_destructor(gpointer user_data)
+{
+    DClockPlugin * dc = user_data;
+
+    /* Remove the timer. */
+    if (dc->timer != 0)
+        g_source_remove(dc->timer);
+
+    /* Ensure that the calendar is dismissed. */
+    if (dc->calendar_window != NULL)
+        gtk_widget_destroy(dc->calendar_window);
+
+    /* Deallocate all memory. */
+    g_free(dc->clock_format);
+    g_free(dc->tooltip_format);
+    g_free(dc->action);
+    g_free(dc->prev_clock_value);
+    g_free(dc->prev_tooltip_value);
+    g_free(dc);
+}
+
+/* Callback when the configuration dialog has recorded a configuration change. */
+static gboolean dclock_apply_configuration(gpointer user_data)
+{
+    GtkWidget * p = user_data;
+    DClockPlugin * dc = lxpanel_plugin_get_data(p);
+    struct timeval now;
+
+    /* stop the updater now */
+    if (dc->timer)
+        g_source_remove(dc->timer);
+
+    /* Set up the icon or the label as the displayable widget. */
+    if (dc->icon_only)
+    {
+        if(lxpanel_image_set_icon_theme(dc->panel, dc->clock_icon, "clock") != FALSE) {
+            lxpanel_image_set_from_file(dc->panel, dc->clock_icon, PACKAGE_DATA_DIR "/images/clock.png");
+        }
+        gtk_widget_show(dc->clock_icon);
+        gtk_widget_hide(dc->clock_label);
+    }
+    else
+    {
+        gtk_widget_show(dc->clock_label);
+        gtk_widget_hide(dc->clock_icon);
+    }
+
+    if (dc->center_text)
+    {
+        gtk_label_set_justify(GTK_LABEL(dc->clock_label), GTK_JUSTIFY_CENTER);
+    }
+    else
+    {
+        gtk_label_set_justify(GTK_LABEL(dc->clock_label), GTK_JUSTIFY_LEFT);
+    }
+
+    /* Rerun the experiment to determine update interval and update the display. */
+    g_free(dc->prev_clock_value);
+    g_free(dc->prev_tooltip_value);
+    dc->expiration_interval = AWAITING_FIRST_CHANGE;
+    dc->experiment_count = 0;
+    dc->prev_clock_value = NULL;
+    dc->prev_tooltip_value = NULL;
+    dclock_timer_set(dc, &now);
+
+    /* Hide the calendar. */
+    if (dc->calendar_window != NULL)
+    {
+        gtk_widget_destroy(dc->calendar_window);
+        dc->calendar_window = NULL;
+    }
+
+    /* Save configuration */
+    config_group_set_string(dc->settings, "ClockFmt", dc->clock_format);
+    config_group_set_string(dc->settings, "TooltipFmt", dc->tooltip_format);
+    config_group_set_string(dc->settings, "Action", dc->action);
+    config_group_set_int(dc->settings, "BoldFont", dc->bold);
+    config_group_set_int(dc->settings, "IconOnly", dc->icon_only);
+    config_group_set_int(dc->settings, "CenterText", dc->center_text);
+    return FALSE;
+}
+
+/* Callback when the configuration dialog is to be shown. */
+static GtkWidget *dclock_configure(LXPanel *panel, GtkWidget *p)
+{
+    DClockPlugin * dc = lxpanel_plugin_get_data(p);
+    return lxpanel_generic_config_dlg(_("Digital Clock"), panel,
+        dclock_apply_configuration, p,
+        _("Clock Format"), &dc->clock_format, CONF_TYPE_STR,
+        _("Tooltip Format"), &dc->tooltip_format, CONF_TYPE_STR,
+        _("Format codes: man 3 strftime; \%n for line break"), NULL, CONF_TYPE_TRIM,
+        _("Action when clicked (default: display calendar)"), &dc->action, CONF_TYPE_STR,
+        _("Bold font"), &dc->bold, CONF_TYPE_BOOL,
+        _("Tooltip only"), &dc->icon_only, CONF_TYPE_BOOL,
+        _("Center text"), &dc->center_text, CONF_TYPE_BOOL,
+        NULL);
+}
+
+/* Callback when panel configuration changes. */
+static void dclock_reconfigure(LXPanel *panel, GtkWidget *p)
+{
+    dclock_apply_configuration(p);
+}
+
+/* Plugin descriptor. */
+LXPanelPluginInit lxpanel_static_plugin_dclock = {
+    .name = N_("Digital Clock"),
+    .description = N_("Display digital clock and tooltip"),
+
+    .new_instance = dclock_constructor,
+    .config = dclock_configure,
+    .reconfigure = dclock_reconfigure,
+    .button_press_event = dclock_button_press_event
+};
diff --git a/plugins/deskno/Makefile.am b/plugins/deskno/Makefile.am
new file mode 100644 (file)
index 0000000..36ab3b7
--- /dev/null
@@ -0,0 +1,19 @@
+deskno_la_CFLAGS = \
+       -I. \
+       -I$(top_srcdir)/src \
+       $(PACKAGE_CFLAGS) \
+       $(G_CAST_CHECKS)
+
+module_LTLIBRARIES = deskno.la
+
+moduledir = $(libdir)/lxpanel/plugins
+
+deskno_la_SOURCES = \
+       deskno.c
+
+deskno_la_LIBADD = \
+       $(PACKAGE_LIBS)
+
+deskno_la_LDFLAGS = \
+       -module \
+       @LXPANEL_MODULE@
diff --git a/plugins/deskno/deskno.c b/plugins/deskno/deskno.c
new file mode 100644 (file)
index 0000000..765d064
--- /dev/null
@@ -0,0 +1,201 @@
+/**
+ * Desktop number plugin to lxpanel
+ *
+ * Copyright (c) 2008-2014 LxDE Developers, see the file AUTHORS for details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+// reused dclock.c and variables from pager.c
+// 11/23/04 by cmeury
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#include "plugin.h"
+#include "misc.h"
+#include "ev.h"
+
+/* Private context for desktop number plugin. */
+typedef struct {
+    LXPanel * panel;                   /* Back pointer to Panel */
+    config_setting_t *settings;
+    GtkWidget * label;                 /* The label */
+    int number_of_desktops;            /* Number of desktops */
+    char * * desktop_labels;           /* Vector of desktop labels */
+    gboolean bold;                     /* User preference: True if bold font */
+    gboolean wm_labels;                        /* User preference: True to display window manager labels */
+} DesknoPlugin;
+
+static void deskno_destructor(gpointer user_data);
+
+/* Handler for current_desktop event from window manager. */
+static gboolean deskno_name_update(GtkWidget * widget, DesknoPlugin * dc)
+{
+    /* Compute and redraw the desktop number. */
+    int desktop_number = get_net_current_desktop();
+    if (desktop_number < dc->number_of_desktops)
+        lxpanel_draw_label_text(dc->panel, dc->label, dc->desktop_labels[desktop_number], dc->bold, 1, TRUE);
+    return TRUE;
+}
+
+/* Handler for desktop_name and number_of_desktops events from window manager.
+ * Also used on a configuration change to get a full redraw. */
+static void deskno_redraw(GtkWidget * widget, DesknoPlugin * dc)
+{
+    /* Get the NET_DESKTOP_NAMES property. */
+    dc->number_of_desktops = get_net_number_of_desktops();
+    int number_of_desktop_names;
+    char * * desktop_names;
+    desktop_names = get_utf8_property_list(GDK_ROOT_WINDOW(), a_NET_DESKTOP_NAMES, &number_of_desktop_names);
+
+    /* Reallocate the vector of labels. */
+    if (dc->desktop_labels != NULL)
+        g_strfreev(dc->desktop_labels);
+    dc->desktop_labels = g_new0(gchar *, dc->number_of_desktops + 1);
+
+    /* Loop to copy the desktop names to the vector of labels.
+     * If there are more desktops than labels, label the extras with a decimal number. */
+    int i = 0;
+    if (dc->wm_labels)
+        for ( ; ((desktop_names != NULL) && (i < MIN(dc->number_of_desktops, number_of_desktop_names))); i++)
+            dc->desktop_labels[i] = g_strdup(desktop_names[i]);
+    for ( ; i < dc->number_of_desktops; i++)
+        dc->desktop_labels[i] = g_strdup_printf("%d", i + 1);
+
+    /* Free the property. */
+    if (desktop_names != NULL)
+        g_strfreev(desktop_names);
+
+    /* Redraw the label. */
+    deskno_name_update(widget, dc);
+}
+
+/* Handler for button-press-event on top level widget. */
+static gboolean deskno_button_press_event(GtkWidget * widget, GdkEventButton * event, LXPanel * p)
+{
+    /* Right-click goes to next desktop, wrapping around to first. */
+    int desknum = get_net_current_desktop();
+    int desks = get_net_number_of_desktops();
+    int newdesk = desknum + 1;
+    if (newdesk >= desks)
+        newdesk = 0;
+
+    /* Ask the window manager to make the new desktop current. */
+    Xclimsg(GDK_ROOT_WINDOW(), a_NET_CURRENT_DESKTOP, newdesk, 0, 0, 0, 0);
+    return TRUE;
+}
+
+/* Plugin constructor. */
+static GtkWidget *deskno_constructor(LXPanel *panel, config_setting_t *settings)
+{
+    /* Allocate plugin context and set into Plugin private data pointer. */
+    DesknoPlugin * dc = g_new0(DesknoPlugin, 1);
+    GtkWidget *p;
+    int tmp_int;
+
+    g_return_val_if_fail(dc != NULL, 0);
+    dc->panel = panel;
+    dc->settings = settings;
+
+    /* Default parameters. */
+    dc->wm_labels = TRUE;
+
+    /* Load parameters from the configuration file. */
+    if (config_setting_lookup_int(settings, "BoldFont", &tmp_int))
+        dc->bold = tmp_int != 0;
+    if (config_setting_lookup_int(settings, "WMLabels", &tmp_int))
+        dc->wm_labels = tmp_int != 0;
+
+    /* Allocate top level widget and set into Plugin widget pointer. */
+    p = gtk_event_box_new();
+    lxpanel_plugin_set_data(p, dc, deskno_destructor);
+    gtk_container_set_border_width(GTK_CONTAINER (p), 1);
+
+    /* Allocate label widget and add to top level. */
+    dc->label = gtk_label_new(NULL);
+    gtk_container_add(GTK_CONTAINER(p), dc->label);
+
+    /* Connect signals.  Note use of window manager event object. */
+    g_signal_connect(G_OBJECT(fbev), "current-desktop", G_CALLBACK(deskno_name_update), (gpointer) dc);
+    g_signal_connect(G_OBJECT(fbev), "desktop-names", G_CALLBACK(deskno_redraw), (gpointer) dc);
+    g_signal_connect(G_OBJECT(fbev), "number-of-desktops", G_CALLBACK(deskno_redraw), (gpointer) dc);
+
+    /* Initialize value and show the widget. */
+    deskno_redraw(NULL, dc);
+    gtk_widget_show_all(p);
+    return p;
+}
+
+/* Plugin destructor. */
+static void deskno_destructor(gpointer user_data)
+{
+    DesknoPlugin * dc = (DesknoPlugin *) user_data;
+
+    /* Disconnect signal from window manager event object. */
+    g_signal_handlers_disconnect_by_func(G_OBJECT(fbev), deskno_name_update, dc);
+    g_signal_handlers_disconnect_by_func(G_OBJECT(fbev), deskno_redraw, dc);
+
+    /* Deallocate all memory. */
+    if (dc->desktop_labels != NULL)
+        g_strfreev(dc->desktop_labels);
+    g_free(dc);
+}
+
+/* Callback when the configuration dialog has recorded a configuration change. */
+static gboolean deskno_apply_configuration(gpointer user_data)
+{
+    DesknoPlugin * dc = lxpanel_plugin_get_data(user_data);
+    deskno_redraw(NULL, dc);
+    config_group_set_int(dc->settings, "BoldFont", dc->bold);
+    config_group_set_int(dc->settings, "WMLabels", dc->wm_labels);
+    return FALSE;
+}
+
+/* Callback when the configuration dialog is to be shown. */
+static GtkWidget *deskno_configure(LXPanel *panel, GtkWidget *p)
+{
+    DesknoPlugin * dc = lxpanel_plugin_get_data(p);
+    GtkWidget * dlg = lxpanel_generic_config_dlg(_("Desktop Number / Workspace Name"),
+        panel, deskno_apply_configuration, p,
+        _("Bold font"), &dc->bold, CONF_TYPE_BOOL,
+        _("Display desktop names"), &dc->wm_labels, CONF_TYPE_BOOL,
+        NULL);
+    gtk_widget_set_size_request(GTK_WIDGET(dlg), 400, -1);     /* Improve geometry */
+    return dlg;
+}
+
+/* Callback when panel configuration changes. */
+static void deskno_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
+{
+    DesknoPlugin * dc = lxpanel_plugin_get_data(p);
+    deskno_name_update(NULL, dc);
+}
+
+FM_DEFINE_MODULE(lxpanel_gtk, deskno)
+
+/* Plugin descriptor. */
+LXPanelPluginInit fm_module_init_lxpanel_gtk = {
+    .name = N_("Desktop Number / Workspace Name"),
+    .description = N_("Display workspace number, by cmeury@users.sf.net"),
+
+    .new_instance = deskno_constructor,
+    .config = deskno_configure,
+    .reconfigure = deskno_panel_configuration_changed,
+    .button_press_event = deskno_button_press_event
+};
diff --git a/plugins/dirmenu.c b/plugins/dirmenu.c
new file mode 100644 (file)
index 0000000..b07a815
--- /dev/null
@@ -0,0 +1,377 @@
+/**
+ * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <libfm/fm-gtk.h>
+#include <string.h>
+
+#include "misc.h"
+#include "plugin.h"
+
+/* Temporary for sort of directory names. */
+typedef struct _directory_name {
+    struct _directory_name * flink;
+    char * directory_name;
+    char * directory_name_collate_key;
+} DirectoryName;
+
+/* Private context for directory menu plugin. */
+typedef struct {
+    LXPanel * panel; /* The panel and settings are required to apply config */
+    config_setting_t * settings;
+    char * image;                      /* Icon for top level widget */
+    char * path;                       /* Top level path for widget */
+    char * name;                       /* User's label for widget */
+    GdkPixbuf * folder_icon;           /* Icon for folders */
+} DirMenuPlugin;
+
+static GtkWidget * dirmenu_create_menu(DirMenuPlugin * dm, const char * path, gboolean open_at_top);
+static void dirmenu_destructor(gpointer user_data);
+static gboolean dirmenu_apply_configuration(gpointer user_data);
+
+
+/* Handler for activate event on popup Open menu item. */
+static void dirmenu_menuitem_open_directory(GtkWidget * item, DirMenuPlugin * dm)
+{
+    FmPath *path = fm_path_new_for_str(g_object_get_data(G_OBJECT(gtk_widget_get_parent(item)), "path"));
+    lxpanel_launch_path(dm->panel, path);
+    fm_path_unref(path);
+}
+
+/* Handler for activate event on popup Open In Terminal menu item. */
+static void dirmenu_menuitem_open_in_terminal(GtkWidget * item, DirMenuPlugin * dm)
+{
+    fm_terminal_launch(g_object_get_data(G_OBJECT(gtk_widget_get_parent(item)), "path"), NULL);
+}
+
+/* Handler for select event on popup menu item. */
+static void dirmenu_menuitem_select(GtkMenuItem * item, DirMenuPlugin * dm)
+{
+    GtkWidget * sub = gtk_menu_item_get_submenu(item);
+    if (sub != NULL)
+    {
+        /* On first reference, populate the submenu using the parent directory and the item directory name. */
+        GtkMenu * parent = GTK_MENU(gtk_widget_get_parent(GTK_WIDGET(item)));
+        char * path = (char *) g_object_get_data(G_OBJECT(sub), "path");
+        if (path == NULL)
+        {
+            path = g_build_filename(
+                (char *) g_object_get_data(G_OBJECT(parent), "path"),
+                (char *) g_object_get_data(G_OBJECT(item), "name"),
+                NULL);
+            sub = dirmenu_create_menu(dm, path, TRUE);
+            g_free(path);
+            gtk_menu_item_set_submenu(item, sub);
+        }
+    }
+}
+
+/* Handler for deselect event on popup menu item. */
+static void dirmenu_menuitem_deselect(GtkMenuItem * item, DirMenuPlugin * dm)
+{
+    /* Delete old menu on deselect to save resource. */
+    gtk_menu_item_set_submenu(item, gtk_menu_new());
+}
+
+/* Handler for selection-done event on popup menu. */
+static void dirmenu_menu_selection_done(GtkWidget * menu, DirMenuPlugin * dm)
+{
+    gtk_widget_destroy(menu);
+}
+
+/* Position-calculation callback for popup menu. */
+static void dirmenu_popup_set_position(GtkWidget * menu, gint * px, gint * py, gboolean * push_in, GtkWidget * p)
+{
+    DirMenuPlugin * dm = lxpanel_plugin_get_data(p);
+
+    /* Determine the coordinates. */
+    lxpanel_plugin_popup_set_position_helper(dm->panel, p, menu, px, py);
+    *push_in = TRUE;
+}
+
+/* Create a menu populated with all subdirectories. */
+static GtkWidget * dirmenu_create_menu(DirMenuPlugin * dm, const char * path, gboolean open_at_top)
+{
+    /* Create a menu. */
+    GtkWidget * menu = gtk_menu_new();
+
+    if (dm->folder_icon == NULL)
+    {
+        int w;
+        int h;
+        gtk_icon_size_lookup_for_settings(gtk_widget_get_settings(menu), GTK_ICON_SIZE_MENU, &w, &h);
+        dm->folder_icon = gtk_icon_theme_load_icon(
+            panel_get_icon_theme(dm->panel),
+            "gnome-fs-directory", MAX(w, h), 0, NULL);
+        if (dm->folder_icon == NULL)
+            dm->folder_icon = gtk_widget_render_icon(menu, GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU, NULL);
+    }
+
+    g_object_set_data_full(G_OBJECT(menu), "path", g_strdup(path), g_free);
+
+    /* Scan the specified directory to populate the menu with its subdirectories. */
+    DirectoryName * dir_list = NULL;
+    GDir * dir = g_dir_open(path, 0, NULL);
+    if (dir != NULL)
+    {
+        const char * name;
+        while ((name = g_dir_read_name(dir)) != NULL)  /* Memory owned by glib */
+        {
+            /* Omit hidden files. */
+            if (name[0] != '.')
+            {
+                char * full = g_build_filename(path, name, NULL);
+                if (g_file_test(full, G_FILE_TEST_IS_DIR))
+                {
+                    /* Convert name to UTF-8 and to the collation key. */
+                    char * directory_name = g_filename_display_name(name);
+                    char * directory_name_collate_key = g_utf8_collate_key(directory_name, -1);
+
+                    /* Locate insertion point. */
+                    DirectoryName * dir_pred = NULL;
+                    DirectoryName * dir_cursor;
+                    for (dir_cursor = dir_list; dir_cursor != NULL; dir_pred = dir_cursor, dir_cursor = dir_cursor->flink)
+                    {
+                        if (strcmp(directory_name_collate_key, dir_cursor->directory_name_collate_key) <= 0)
+                            break;
+                    }
+
+                    /* Allocate and initialize sorted directory name entry. */
+                    dir_cursor = g_new0(DirectoryName, 1);
+                    dir_cursor->directory_name = directory_name;
+                    dir_cursor->directory_name_collate_key = directory_name_collate_key;
+                    if (dir_pred == NULL)
+                    {
+                        dir_cursor->flink = dir_list;
+                        dir_list = dir_cursor;
+                    }
+                    else
+                    {
+                        dir_cursor->flink = dir_pred->flink;
+                        dir_pred->flink = dir_cursor;
+                    }
+                }
+                g_free(full);
+            }
+        }
+        g_dir_close(dir);
+    }
+
+    /* The sorted directory name list is complete.  Loop to create the menu. */
+    DirectoryName * dir_cursor;
+    while ((dir_cursor = dir_list) != NULL)
+    {
+        /* Create and initialize menu item. */
+        GtkWidget * item = gtk_image_menu_item_new_with_label(dir_cursor->directory_name);
+        gtk_image_menu_item_set_image(
+            GTK_IMAGE_MENU_ITEM(item),
+            gtk_image_new_from_stock(GTK_STOCK_DIRECTORY, GTK_ICON_SIZE_MENU));
+        GtkWidget * dummy = gtk_menu_new();
+        gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), dummy);
+        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+
+        /* Unlink and free sorted directory name element, but reuse the directory name string. */
+        dir_list = dir_cursor->flink;
+        g_object_set_data_full(G_OBJECT(item), "name", dir_cursor->directory_name, g_free);
+        g_free(dir_cursor->directory_name_collate_key);
+        g_free(dir_cursor);
+
+        /* Connect signals. */
+        g_signal_connect(G_OBJECT(item), "select", G_CALLBACK(dirmenu_menuitem_select), dm);
+        g_signal_connect(G_OBJECT(item), "deselect", G_CALLBACK(dirmenu_menuitem_deselect), dm);
+    }
+
+    /* Create "Open" and "Open in Terminal" items. */
+    GtkWidget * item = gtk_image_menu_item_new_from_stock( GTK_STOCK_OPEN, NULL );
+    g_signal_connect(item, "activate", G_CALLBACK(dirmenu_menuitem_open_directory), dm);
+    GtkWidget * term = gtk_menu_item_new_with_mnemonic( _("Open in _Terminal") );
+    g_signal_connect(term, "activate", G_CALLBACK(dirmenu_menuitem_open_in_terminal), dm);
+
+    /* Insert or append based on caller's preference. */
+    if (open_at_top)
+    {
+        gtk_menu_shell_insert(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new(), 0);
+        gtk_menu_shell_insert(GTK_MENU_SHELL(menu), term, 0);
+        gtk_menu_shell_insert(GTK_MENU_SHELL(menu), item, 0);
+    }
+    else {
+        gtk_menu_shell_append(GTK_MENU_SHELL(menu), gtk_separator_menu_item_new());
+        gtk_menu_shell_append(GTK_MENU_SHELL(menu), term);
+        gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
+    }
+
+    /* Show the menu and return. */
+    gtk_widget_show_all(menu);
+    return menu;
+}
+
+/* Show a menu of subdirectories. */
+static void dirmenu_show_menu(GtkWidget * widget, DirMenuPlugin * dm, int btn, guint32 time)
+{
+    /* Create a menu populated with all subdirectories. */
+    GtkWidget * menu = dirmenu_create_menu(dm, dm->path, FALSE);
+    g_signal_connect(menu, "selection-done", G_CALLBACK(dirmenu_menu_selection_done), dm);
+
+    /* Show the menu.  Use a positioning function to get it placed next to the top level widget. */
+    gtk_menu_popup(GTK_MENU(menu), NULL, NULL, (GtkMenuPositionFunc) dirmenu_popup_set_position, widget, btn, time);
+}
+
+/* Handler for button-press-event on top level widget. */
+static gboolean dirmenu_button_press_event(GtkWidget * widget, GdkEventButton * event, LXPanel * p)
+{
+    DirMenuPlugin * dm = lxpanel_plugin_get_data(widget);
+
+    if (event->button == 1)
+    {
+        dirmenu_show_menu(widget, dm, event->button, event->time);
+    }
+    else
+    {
+        fm_terminal_launch(dm->path, NULL);
+    }
+    return TRUE;
+}
+
+/* Plugin constructor. */
+static GtkWidget *dirmenu_constructor(LXPanel *panel, config_setting_t *settings)
+{
+    /* Allocate and initialize plugin context and set into Plugin private data pointer. */
+    DirMenuPlugin * dm = g_new0(DirMenuPlugin, 1);
+    GtkWidget * p;
+    const char *str;
+
+    /* Load parameters from the configuration file. */
+    if (config_setting_lookup_string(settings, "image", &str))
+        dm->image = g_strdup(str);
+    if (config_setting_lookup_string(settings, "path", &str))
+        dm->path = expand_tilda(str);
+    else
+        dm->path = g_strdup(fm_get_home_dir());
+    if (config_setting_lookup_string(settings, "name", &str))
+        dm->name = g_strdup(str);
+
+    /* Save construction pointers */
+    dm->panel = panel;
+    dm->settings = settings;
+
+    /* Allocate top level widget and set into Plugin widget pointer.
+     * It is not known why, but the button text will not draw if it is edited from empty to non-empty
+     * unless this strategy of initializing it with a non-empty value first is followed. */
+    p = lxpanel_button_new_for_icon(panel,
+                            ((dm->image != NULL) ? dm->image : "file-manager"),
+                            NULL, "Temp");
+    lxpanel_plugin_set_data(p, dm, dirmenu_destructor);
+    gtk_container_set_border_width(GTK_CONTAINER(p), 0);
+
+    /* Initialize the widget. */
+    dirmenu_apply_configuration(p);
+
+    /* Show the widget and return. */
+    return p;
+}
+
+/* Plugin destructor. */
+static void dirmenu_destructor(gpointer user_data)
+{
+    DirMenuPlugin * dm = (DirMenuPlugin *)user_data;
+
+    /* Release a reference on the folder icon if held. */
+    if (dm->folder_icon)
+        g_object_unref(dm->folder_icon);
+
+    /* Deallocate all memory. */
+    g_free(dm->image);
+    g_free(dm->path);
+    g_free(dm->name);
+    g_free(dm);
+}
+
+/* Recursively apply a configuration change. */
+static void dirmenu_apply_configuration_to_children(GtkWidget * w, DirMenuPlugin * dm)
+{
+    if (GTK_IS_CONTAINER(w))
+        gtk_container_foreach(GTK_CONTAINER(w), (GtkCallback) dirmenu_apply_configuration_to_children, (gpointer) dm);
+    else if (GTK_IS_LABEL(w))
+    {
+        if (dm->name == NULL)
+            gtk_label_set_text(GTK_LABEL(w), NULL);
+        else
+            lxpanel_draw_label_text(dm->panel, w, dm->name, FALSE, 1, TRUE);
+    }
+}
+
+/* Callback when the configuration dialog has recorded a configuration change. */
+static gboolean dirmenu_apply_configuration(gpointer user_data)
+{
+    GtkWidget * p = user_data;
+    DirMenuPlugin * dm = lxpanel_plugin_get_data(p);
+    char * path = dm->path;
+
+    /* Normalize path */
+    if (path == NULL)
+        dm->path = g_strdup(fm_get_home_dir());
+    else if (path[0] == '~')
+    {
+        dm->path = expand_tilda(path);
+        g_free(path);
+    }
+
+    /* Save configuration */
+    config_group_set_string(dm->settings, "path", dm->path);
+    config_group_set_string(dm->settings, "name", dm->name);
+    config_group_set_string(dm->settings, "image", dm->image);
+
+    lxpanel_button_set_icon(p, ((dm->image != NULL) ? dm->image : "file-manager"),
+                            panel_get_icon_size(dm->panel));
+
+    gtk_widget_set_tooltip_text(p, dm->path);
+    gtk_container_foreach(GTK_CONTAINER(p), (GtkCallback) dirmenu_apply_configuration_to_children, (gpointer) dm);
+    return FALSE;
+}
+
+/* Callback when the configuration dialog is to be shown. */
+static GtkWidget *dirmenu_configure(LXPanel *panel, GtkWidget *p)
+{
+    DirMenuPlugin * dm = lxpanel_plugin_get_data(p);
+    return lxpanel_generic_config_dlg(_("Directory Menu"),
+        panel, dirmenu_apply_configuration, p,
+        _("Directory"), &dm->path, CONF_TYPE_DIRECTORY_ENTRY,
+        _("Label"), &dm->name, CONF_TYPE_STR,
+        _("Icon"), &dm->image, CONF_TYPE_FILE_ENTRY,
+        NULL);
+}
+
+/* Callback when panel configuration changes. */
+static void dirmenu_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
+{
+    dirmenu_apply_configuration(p);
+}
+
+/* Plugin descriptor. */
+LXPanelPluginInit lxpanel_static_plugin_dirmenu = {
+    .name = N_("Directory Menu"),
+    .description = N_("Browse directory tree via menu (Author = PCMan)"),
+
+    .new_instance = dirmenu_constructor,
+    .config = dirmenu_configure,
+    .reconfigure = dirmenu_panel_configuration_changed,
+    .button_press_event = dirmenu_button_press_event
+};
diff --git a/plugins/icon.xpm b/plugins/icon.xpm
new file mode 100644 (file)
index 0000000..288cfba
--- /dev/null
@@ -0,0 +1,35 @@
+/* XPM */
+static char * icon_xpm[] = {
+"16 16 16 1",
+"      c None",
+".     c #323232",
+"+     c #535353",
+"@     c #4A8A8E",
+"#     c #DEE2E2",
+"$     c #7E827A",
+"%     c #8A9292",
+"&     c #D6D6D6",
+"*     c #36767E",
+"=     c #9E9E9E",
+"-     c #FAFAFA",
+";     c #B2B2B2",
+">     c #DEEEEA",
+",     c #464646",
+"'     c #5EA2A2",
+")     c #52969A",
+"                ",
+"                ",
+" --#>>>>>>#-#-; ",
+" -&%')))))=&=&+ ",
+" >;$@*****=;%;+ ",
+" &$$$$$$$$$$$$, ",
+" &;;;;;;;;;;;;+ ",
+" &;;;;;;;;;;;;+ ",
+" #;;;;;;;;;;;;+ ",
+" &;;;;;;;;;;;;+ ",
+" #;;;;;;;;;;;;+ ",
+" #;;;;;;;;;;;;+ ",
+" &;;;;;;;;;;;;+ ",
+" $............. ",
+"                ",
+"                "};
diff --git a/plugins/image.c b/plugins/image.c
new file mode 100644 (file)
index 0000000..37ceac6
--- /dev/null
@@ -0,0 +1,167 @@
+/**
+ * Copyright (c) 2006 LxDE Developers, see the file AUTHORS for details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <stdlib.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <glib/gi18n.h>
+
+#include "panel.h"
+#include "misc.h"
+#include "private.h"
+
+#include "dbg.h"
+
+
+typedef struct {
+    GtkWidget *mainw;
+    char* config_data;
+} image;
+
+static void
+image_destructor(Plugin *p)
+{
+    image *img = (image *)p->priv;
+
+    ENTER;
+    gtk_widget_destroy(img->mainw);
+    g_free( img->config_data );
+    g_free(img);
+    RET();
+}
+
+static int
+image_constructor(Plugin *p, char **fp)
+{
+    gchar *tooltip, *fname;
+    image *img;
+    GdkPixbuf *gp, *gps;
+    GtkWidget *wid;
+    GError *err = NULL;
+    char *config_start, *config_end;
+
+    line s;
+
+    s.len = 256;
+    ENTER;
+    img = g_new0(image, 1);
+    g_return_val_if_fail(img != NULL, 0);
+    p->priv = img;
+    tooltip = fname = 0;
+    if( fp ) {
+        config_start = *fp;
+        while (lxpanel_get_line(fp, &s) != LINE_BLOCK_END) {
+            if (s.type == LINE_NONE) {
+                ERR( "image: illegal token %s\n", s.str);
+                goto error;
+            }
+            if (s.type == LINE_VAR) {
+                if (!g_ascii_strcasecmp(s.t[0], "image"))
+                    fname = expand_tilda(s.t[1]);
+                else if (!g_ascii_strcasecmp(s.t[0], "tooltip"))
+                    tooltip = g_strdup(s.t[1]);
+                else {
+                    ERR( "image: unknown var %s\n", s.t[0]);
+                    goto error;
+                }
+            } else {
+                ERR( "image: illegal in this context %s\n", s.str);
+                goto error;
+            }
+        }
+        config_end = *fp - 1;
+        while( *config_end != '}' && config_end > config_start ) {
+            --config_end;
+        }
+        if( *config_end == '}' )
+            --config_end;
+        img->config_data = g_strndup( config_start,
+                                      (config_end-config_start) );
+    }
+    else {
+        config_start = config_end = NULL;
+    }
+    img->mainw = gtk_event_box_new();
+    gtk_widget_show(img->mainw);
+    //g_signal_connect(G_OBJECT(img->mainw), "expose_event",
+    //      G_CALLBACK(gtk_widget_queue_draw), NULL);
+    gp = gdk_pixbuf_new_from_file(fname, &err);
+    if (!gp) {
+        g_warning("image: can't read image %s\n", fname);
+        wid = gtk_label_new("?");
+    } else {
+        float ratio;
+        ratio = (p->panel->orientation == GTK_ORIENTATION_HORIZONTAL) ?
+            (float) (p->panel->ah - 2) / (float) gdk_pixbuf_get_height(gp)
+            : (float) (p->panel->aw - 2) / (float) gdk_pixbuf_get_width(gp);
+        gps =  gdk_pixbuf_scale_simple (gp,
+              ratio * ((float) gdk_pixbuf_get_width(gp)),
+              ratio * ((float) gdk_pixbuf_get_height(gp)),
+              GDK_INTERP_HYPER);
+        wid = gtk_image_new_from_pixbuf(gps);
+        g_object_unref(gp);
+        g_object_unref(gps);
+
+    }
+    gtk_widget_show(wid);
+    gtk_container_add(GTK_CONTAINER(img->mainw), wid);
+    gtk_container_set_border_width(GTK_CONTAINER(img->mainw), 0);
+    g_free(fname);
+
+    if (tooltip) {
+        gtk_widget_set_tooltip_text(img->mainw, tooltip);
+        g_free(tooltip);
+    }
+    RET(1);
+
+ error:
+    g_free(fname);
+    g_free(tooltip);
+    image_destructor(p);
+    RET(0);
+}
+
+static void save_config( Plugin* p, FILE* fp )
+{
+    image *img = (image *)p->priv;
+    if( img->config_data ) {
+        char** lines = g_strsplit( img->config_data, "\n", 0 );
+        char** line;
+        for( line = lines; *line; ++line ) {
+            g_strstrip( *line );
+            if( **line )
+                lxpanel_put_line( fp, *line );
+        }
+        g_strfreev( lines );
+    }
+}
+
+PluginClass image_plugin_class = {
+    .fname = NULL,
+    .count = 0,
+
+    .type = "image",
+    .name = "image",
+    .version = "1.0",
+    .description = N_("Display Image and Tooltip"),
+
+    .constructor = image_constructor,
+    .destructor  = image_destructor,
+    .config = NULL,
+    .save = save_config
+};
diff --git a/plugins/indicator/Makefile.am b/plugins/indicator/Makefile.am
new file mode 100644 (file)
index 0000000..07ffb5c
--- /dev/null
@@ -0,0 +1,23 @@
+indicator_la_CFLAGS = \
+       -I. \
+       -I$(top_srcdir)/src \
+       -DINDICATOR_DIR=\""$(INDICATORDIR)"\" \
+       -DINDICATOR_ICONS_DIR=\""$(INDICATORICONSDIR)"\" \
+       $(PACKAGE_CFLAGS) \
+       $(APPLET_CFLAGS) \
+       $(G_CAST_CHECKS)
+
+module_LTLIBRARIES = indicator.la
+
+moduledir = $(libdir)/lxpanel/plugins
+
+indicator_la_SOURCES = \
+       indicator.c
+
+indicator_la_LIBADD = \
+       $(PACKAGE_LIBS) \
+       $(APPLET_LIBS)
+
+indicator_la_LDFLAGS = \
+       -module \
+       @LXPANEL_MODULE@
diff --git a/plugins/indicator/indicator.c b/plugins/indicator/indicator.c
new file mode 100644 (file)
index 0000000..238fb6d
--- /dev/null
@@ -0,0 +1,888 @@
+/*
+Copyright 2010 Julien Lavergne <gilir@ubuntu.com>
+
+Based on indicator-applet :
+Copyright 2009 Canonical Ltd.
+
+Authors:
+    Ted Gould <ted@canonical.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+version 3.0 as published by the Free Software Foundation.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License version 3.0 for more details.
+
+You should have received a copy of the GNU General Public
+License along with this library. If not, see
+<http://www.gnu.org/licenses/>.
+
+TODO Check also http://bazaar.launchpad.net/~unity-team/unity/trunk/view/head:/services/panel-service.c
+
+TODO ? : add hotkey support (r348 + r352)
+
+TODO : vertical support (r354)
+
+*/
+
+#include "plugin.h"
+#include "misc.h"
+
+#include <stdlib.h>
+#include <glib/gi18n.h>
+
+#include <gtk/gtk.h>
+#include <libindicator/indicator-object.h>
+
+static gchar * indicator_order[][2] = {
+  {"libappmenu.so", NULL},
+  {"libapplication.so", NULL},
+  {"libapplication.so", "gst-keyboard-xkb"},
+  {"libmessaging.so", NULL},
+  {"libpower.so", NULL},
+  {"libapplication.so", "bluetooth-manager"},
+  {"libnetwork.so", NULL},
+  {"libnetworkmenu.so", NULL},
+  {"libapplication.so", "nm-applet"},
+  {"libsoundmenu.so", NULL},
+  {"libdatetime.so", NULL},
+  {"libsession.so", NULL},
+  {NULL, NULL}
+};
+
+#define  MENU_DATA_BOX               "box"
+#define  MENU_DATA_INDICATOR_OBJECT  "indicator-object"
+#define  MENU_DATA_INDICATOR_ENTRY   "indicator-entry"
+#define  MENU_DATA_IN_MENUITEM       "in-menuitem"
+#define  MENU_DATA_MENUITEM_PRESSED  "menuitem-pressed"
+
+#define  IO_DATA_NAME                "indicator-name"
+#define  IO_DATA_ORDER_NUMBER        "indicator-order-number"
+
+#define LOG_FILE_NAME  "lxpanel-indicator-plugin.log"
+
+GOutputStream * log_file = NULL;
+
+typedef struct {
+    LXPanel *panel;
+    config_setting_t *settings;
+
+    IndicatorObject *io;               /* Indicators applets */
+
+    GList *images;                             /* List of images of applets */
+    GList *menus;                              /* List of menus of applets */
+
+    GtkWidget * menubar;               /* Displayed menubar */
+
+    gboolean applications;      /* Support for differents indicators */
+    gboolean datetime;
+    gboolean me;
+    gboolean messages;
+    gboolean network;
+    gboolean session;
+    gboolean sound;
+    /* gboolean appmenu; */
+
+
+} IndicatorPlugin;
+
+static const gchar * indicator_env[] = {
+  "indicator-applet",
+  NULL
+};
+
+static gint
+name2order (const gchar * name, const gchar * hint) {
+  int i;
+
+  for (i = 0; indicator_order[i][0] != NULL; i++) {
+    if (g_strcmp0(name, indicator_order[i][0]) == 0 &&
+        g_strcmp0(hint, indicator_order[i][1]) == 0) {
+      return i;
+    }
+  }
+
+  return -1;
+}
+
+typedef struct _incoming_position_t incoming_position_t;
+struct _incoming_position_t {
+        gint objposition;
+        gint entryposition;
+        gint menupos;
+        gboolean found;
+};
+
+/* This function helps by determining where in the menu list
+   this new entry should be placed.  It compares the objects
+   that they're on, and then the individual entries.  Each
+   is progressively more expensive. */
+static void
+place_in_menu_cb (GtkWidget * widget, gpointer user_data)
+{
+  incoming_position_t * position = (incoming_position_t *)user_data;
+  if (position->found) {
+    /* We've already been placed, just finish the foreach */
+    return;
+  }
+
+  IndicatorObject * io = INDICATOR_OBJECT(g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_OBJECT));
+  g_return_if_fail(INDICATOR_IS_OBJECT(io));
+
+  gint objposition = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER));
+  /* We've already passed it, well, then this is where
+     we should be be.  Stop! */
+  if (objposition > position->objposition) {
+    position->found = TRUE;
+    return;
+  }
+
+  /* The objects don't match yet, keep looking */
+  if (objposition < position->objposition) {
+    position->menupos++;
+    return;
+  }
+
+  /* The objects are the same, let's start looking at entries. */
+  IndicatorObjectEntry * entry = (IndicatorObjectEntry *)g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
+  gint entryposition = indicator_object_get_location(io, entry);
+
+  if (entryposition > position->entryposition) {
+    position->found = TRUE;
+    return;
+  }
+
+  if (entryposition < position->entryposition) {
+    position->menupos++;
+    return;
+  }
+
+  /* We've got the same object and the same entry.  Well,
+     let's just put it right here then. */
+  position->found = TRUE;
+  return;
+}
+
+/* Position the entry */
+static void
+place_in_menu (GtkWidget *menubar,
+               GtkWidget *menuitem,
+               IndicatorObject *io,
+               IndicatorObjectEntry *entry)
+{
+  incoming_position_t position;
+
+  /* Start with the default position for this indicator object */
+  gint io_position = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER));
+
+  /* If name-hint is set, try to find the entry's position */
+  if (entry->name_hint != NULL) {
+    const gchar *name = (const gchar *)g_object_get_data(G_OBJECT(io), IO_DATA_NAME);
+    gint entry_position = name2order(name, entry->name_hint);
+
+    /* If we don't find the entry, fall back to the indicator object's position */
+    if (entry_position > -1)
+      io_position = entry_position;
+  }
+
+  position.objposition = io_position;
+  position.entryposition = indicator_object_get_location(io, entry);
+  position.menupos = 0;
+  position.found = FALSE;
+
+  gtk_container_foreach(GTK_CONTAINER(menubar), place_in_menu_cb, &position);
+
+  gtk_menu_shell_insert(GTK_MENU_SHELL(menubar), menuitem, position.menupos);
+}
+
+static void
+something_shown (GtkWidget * widget, gpointer user_data)
+{
+        GtkWidget * menuitem = GTK_WIDGET(user_data);
+        gtk_widget_show(menuitem);
+}
+
+static void
+something_hidden (GtkWidget * widget, gpointer user_data)
+{
+        GtkWidget * menuitem = GTK_WIDGET(user_data);
+        gtk_widget_hide(menuitem);
+}
+
+static void
+sensitive_cb (GObject * obj, GParamSpec * pspec, gpointer user_data)
+{
+    g_return_if_fail(GTK_IS_WIDGET(obj));
+    g_return_if_fail(GTK_IS_WIDGET(user_data));
+
+    gtk_widget_set_sensitive(GTK_WIDGET(user_data), gtk_widget_get_sensitive(GTK_WIDGET(obj)));
+    return;
+}
+
+static void
+entry_activated (GtkWidget * widget, gpointer user_data)
+{
+  g_return_if_fail(GTK_IS_WIDGET(widget));
+
+  IndicatorObject *io = g_object_get_data (G_OBJECT (widget), MENU_DATA_INDICATOR_OBJECT);
+  IndicatorObjectEntry *entry = g_object_get_data (G_OBJECT (widget), MENU_DATA_INDICATOR_ENTRY);
+
+  g_return_if_fail(INDICATOR_IS_OBJECT(io));
+
+  return indicator_object_entry_activate(io, entry, gtk_get_current_event_time());
+}
+
+static gboolean
+entry_secondary_activated (GtkWidget * widget, GdkEvent * event, gpointer user_data)
+{
+  g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
+
+  switch (event->type) {
+    case GDK_ENTER_NOTIFY:
+      g_object_set_data(G_OBJECT(widget), MENU_DATA_IN_MENUITEM, GINT_TO_POINTER(TRUE));
+      break;
+
+    case GDK_LEAVE_NOTIFY:
+      g_object_set_data(G_OBJECT(widget), MENU_DATA_IN_MENUITEM, GINT_TO_POINTER(FALSE));
+      g_object_set_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED, GINT_TO_POINTER(FALSE));
+      break;
+
+    case GDK_BUTTON_PRESS:
+      if (event->button.button == 2) {
+        g_object_set_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED, GINT_TO_POINTER(TRUE));
+      }
+      break;
+
+    case GDK_BUTTON_RELEASE:
+      if (event->button.button == 2) {
+        gboolean in_menuitem = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), MENU_DATA_IN_MENUITEM));
+        gboolean menuitem_pressed = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED));
+
+        if (in_menuitem && menuitem_pressed) {
+          g_object_set_data(G_OBJECT(widget), MENU_DATA_MENUITEM_PRESSED, GINT_TO_POINTER(FALSE));
+
+          IndicatorObject *io = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_OBJECT);
+          IndicatorObjectEntry *entry = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
+
+          g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE);
+
+          g_signal_emit_by_name(io, INDICATOR_OBJECT_SIGNAL_SECONDARY_ACTIVATE,
+              entry, event->button.time);
+        }
+      }
+      break;
+    default: ;
+  }
+
+  return FALSE;
+}
+
+static gboolean
+entry_scrolled (GtkWidget *menuitem, GdkEventScroll *event, gpointer data)
+{
+  g_return_val_if_fail(GTK_IS_WIDGET(menuitem), FALSE);
+
+  IndicatorObject *io = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_OBJECT);
+  IndicatorObjectEntry *entry = g_object_get_data (G_OBJECT (menuitem), MENU_DATA_INDICATOR_ENTRY);
+
+  g_return_val_if_fail(INDICATOR_IS_OBJECT(io), FALSE);
+
+  g_signal_emit_by_name (io, INDICATOR_OBJECT_SIGNAL_ENTRY_SCROLLED, entry, 1, event->direction);
+
+  return FALSE;
+}
+
+static void
+entry_added (IndicatorObject * io, IndicatorObjectEntry * entry, GtkWidget * menubar)
+{
+    const char *indicator_name = (const gchar *)g_object_get_data(G_OBJECT(io), IO_DATA_NAME);
+    g_debug("Signal: Entry Added from %s", indicator_name);
+    gboolean something_visible = FALSE;
+    gboolean something_sensitive = FALSE;
+
+    GtkWidget * menuitem = gtk_menu_item_new();
+    GtkWidget * hbox = gtk_hbox_new(FALSE, 3);
+
+    g_object_set_data (G_OBJECT (menuitem), MENU_DATA_BOX, hbox);
+    g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_OBJECT, io);
+    g_object_set_data(G_OBJECT(menuitem), MENU_DATA_INDICATOR_ENTRY,  entry);
+
+    g_signal_connect(G_OBJECT(menuitem), "activate", G_CALLBACK(entry_activated), NULL);
+    g_signal_connect(G_OBJECT(menuitem), "button-press-event", G_CALLBACK(entry_secondary_activated), NULL);
+    g_signal_connect(G_OBJECT(menuitem), "button-release-event", G_CALLBACK(entry_secondary_activated), NULL);
+    g_signal_connect(G_OBJECT(menuitem), "enter-notify-event", G_CALLBACK(entry_secondary_activated), NULL);
+    g_signal_connect(G_OBJECT(menuitem), "leave-notify-event", G_CALLBACK(entry_secondary_activated), NULL);
+    g_signal_connect(G_OBJECT(menuitem), "scroll-event", G_CALLBACK(entry_scrolled), NULL);
+
+    if (entry->image != NULL)
+    {
+        gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry->image), FALSE, FALSE, 1);
+        if (gtk_widget_get_visible(GTK_WIDGET(entry->image))) {
+                something_visible = TRUE;
+        }
+
+        if (gtk_widget_get_sensitive(GTK_WIDGET(entry->image))) {
+            something_sensitive = TRUE;
+        }
+
+        g_signal_connect(G_OBJECT(entry->image), "show", G_CALLBACK(something_shown), menuitem);
+        g_signal_connect(G_OBJECT(entry->image), "hide", G_CALLBACK(something_hidden), menuitem);
+        g_signal_connect(G_OBJECT(entry->image), "notify::sensitive", G_CALLBACK(sensitive_cb), menuitem);
+    }
+    if (entry->label != NULL)
+    {
+        gtk_box_pack_start(GTK_BOX(hbox), GTK_WIDGET(entry->label), FALSE, FALSE, 1);
+
+        if (gtk_widget_get_visible(GTK_WIDGET(entry->label))) {
+                something_visible = TRUE;
+        }
+
+        if (gtk_widget_get_sensitive(GTK_WIDGET(entry->label))) {
+
+            something_sensitive = TRUE;
+        }
+
+        g_signal_connect(G_OBJECT(entry->label), "show", G_CALLBACK(something_shown), menuitem);
+        g_signal_connect(G_OBJECT(entry->label), "hide", G_CALLBACK(something_hidden), menuitem);
+        g_signal_connect(G_OBJECT(entry->label), "notify::sensitive", G_CALLBACK(sensitive_cb), menuitem);
+
+    }
+    gtk_container_add(GTK_CONTAINER(menuitem), hbox);
+    gtk_widget_show(hbox);
+
+    if (entry->menu != NULL)
+    {
+        gtk_menu_item_set_submenu(GTK_MENU_ITEM(menuitem), GTK_WIDGET(entry->menu));
+    }
+
+    place_in_menu(menubar, menuitem, io, entry);
+
+    if (something_visible) {
+        gtk_widget_show(menuitem);
+    }
+    gtk_widget_set_sensitive(menuitem, something_sensitive);
+
+    return;
+}
+
+static void
+entry_removed_cb (GtkWidget * widget, gpointer userdata)
+{
+    gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
+
+    if (data != userdata)
+    {
+        return;
+    }
+
+    IndicatorObjectEntry * entry = (IndicatorObjectEntry *)data;
+    if (entry->label != NULL) {
+        g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(something_shown), widget);
+        g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(something_hidden), widget);
+        g_signal_handlers_disconnect_by_func(G_OBJECT(entry->label), G_CALLBACK(sensitive_cb), widget);
+    }
+    if (entry->image != NULL) {
+        g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(something_shown), widget);
+        g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(something_hidden), widget);
+        g_signal_handlers_disconnect_by_func(G_OBJECT(entry->image), G_CALLBACK(sensitive_cb), widget);
+    }
+
+    gtk_widget_destroy(widget);
+    return;
+}
+
+static void
+entry_moved_find_cb (GtkWidget * widget, gpointer userdata)
+{
+    gpointer * array = (gpointer *)userdata;
+    if (array[1] != NULL) {
+        return;
+    }
+
+    gpointer data = g_object_get_data(G_OBJECT(widget), MENU_DATA_INDICATOR_ENTRY);
+
+    if (data != array[0]) {
+        return;
+    }
+
+    array[1] = widget;
+    return;
+}
+
+/* Gets called when an entry for an object was moved. */
+static void
+entry_moved (IndicatorObject * io, IndicatorObjectEntry * entry,
+             gint old G_GNUC_UNUSED, gint new G_GNUC_UNUSED, gpointer user_data)
+{
+    GtkWidget * menubar = GTK_WIDGET(user_data);
+
+    gpointer array[2];
+    array[0] = entry;
+    array[1] = NULL;
+
+    gtk_container_foreach(GTK_CONTAINER(menubar), entry_moved_find_cb, array);
+    if (array[1] == NULL) {
+        g_warning("Moving an entry that isn't in our menus.");
+        return;
+    }
+
+    GtkWidget * mi = GTK_WIDGET(array[1]);
+    g_object_ref(G_OBJECT(mi));
+    gtk_container_remove(GTK_CONTAINER(menubar), mi);
+    place_in_menu(menubar, mi, io, entry);
+    g_object_unref(G_OBJECT(mi));
+
+    return;
+}
+
+static void
+entry_removed (IndicatorObject * io G_GNUC_UNUSED, IndicatorObjectEntry * entry,
+               gpointer user_data)
+{
+    g_debug("Signal: Entry Removed");
+
+    gtk_container_foreach(GTK_CONTAINER(user_data), entry_removed_cb, entry);
+
+    return;
+}
+
+static void
+menu_show (IndicatorObject * io, IndicatorObjectEntry * entry,
+           guint32 timestamp, gpointer user_data)
+{
+  GtkWidget * menubar = GTK_WIDGET(user_data);
+
+  if (entry == NULL) {
+    /* Close any open menus instead of opening one */
+    GList * entries = indicator_object_get_entries(io);
+    GList * entry = NULL;
+    for (entry = entries; entry != NULL; entry = g_list_next(entry)) {
+      IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)entry->data;
+      gtk_menu_popdown(entrydata->menu);
+    }
+    g_list_free(entries);
+
+    /* And tell the menubar to exit activation mode too */
+    gtk_menu_shell_cancel(GTK_MENU_SHELL(menubar));
+    return;
+  }
+
+  // TODO: do something sensible here
+}
+
+static gboolean
+load_module (const gchar * name, GtkWidget * menubar)
+{
+    g_debug("Looking at Module: %s", name);
+    g_return_val_if_fail(name != NULL, FALSE);
+
+    if (!g_str_has_suffix(name, G_MODULE_SUFFIX))
+    {
+        return FALSE;
+    }
+
+    g_debug("Loading Module: %s", name);
+
+    /* Build the object for the module */
+    gchar *fullpath = g_build_filename(INDICATOR_DIR, name, NULL);
+    g_debug("Full path: %s", fullpath);
+    IndicatorObject * io = indicator_object_new_from_file(fullpath);
+    g_free(fullpath);
+
+    /* Set the environment it's in */
+    indicator_object_set_environment(io, (const GStrv)indicator_env);
+
+    /* Attach the 'name' to the object */
+    g_object_set_data_full(G_OBJECT(io), IO_DATA_NAME, g_strdup(name), g_free);
+    g_object_set_data(G_OBJECT(io), IO_DATA_ORDER_NUMBER, GINT_TO_POINTER(name2order(name, NULL)));
+
+    /* Connect to it's signals */
+    g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_ADDED,   G_CALLBACK(entry_added),    menubar);
+    g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_REMOVED, G_CALLBACK(entry_removed),  menubar);
+    g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_ENTRY_MOVED,   G_CALLBACK(entry_moved),    menubar);
+    g_signal_connect(G_OBJECT(io), INDICATOR_OBJECT_SIGNAL_MENU_SHOW,     G_CALLBACK(menu_show),      menubar);
+
+    /* Work on the entries */
+    GList * entries = indicator_object_get_entries(io);
+    GList * entry = NULL;
+
+    for (entry = entries; entry != NULL; entry = g_list_next(entry))
+    {
+        IndicatorObjectEntry * entrydata = (IndicatorObjectEntry *)entry->data;
+        entry_added(io, entrydata, menubar);
+    }
+
+    g_list_free(entries);
+
+    return TRUE;
+}
+
+static void
+log_to_file_cb (GObject * source_obj G_GNUC_UNUSED,
+                GAsyncResult * result G_GNUC_UNUSED, gpointer user_data)
+{
+    g_free(user_data);
+    return;
+}
+
+static void
+log_to_file (const gchar * domain G_GNUC_UNUSED,
+             GLogLevelFlags level G_GNUC_UNUSED,
+             const gchar * message,
+             gpointer data G_GNUC_UNUSED)
+{
+    if (log_file == NULL) {
+        GError * error = NULL;
+        gchar * filename = g_build_filename(g_get_user_cache_dir(), LOG_FILE_NAME, NULL);
+        GFile * file = g_file_new_for_path(filename);
+        g_free(filename);
+
+        if (!g_file_test(g_get_user_cache_dir(), G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
+            GFile * cachedir = g_file_new_for_path(g_get_user_cache_dir());
+            g_file_make_directory_with_parents(cachedir, NULL, &error);
+
+            if (error != NULL) {
+                g_error("Unable to make directory '%s' for log file: %s", g_get_user_cache_dir(), error->message);
+                return;
+            }
+        }
+
+        g_file_delete(file, NULL, NULL);
+
+        GFileIOStream * io = g_file_create_readwrite(file,
+                                G_FILE_CREATE_REPLACE_DESTINATION, /* flags */
+                                NULL, /* cancelable */
+                                &error); /* error */
+        if (error != NULL) {
+            g_error("Unable to replace file: %s", error->message);
+            return;
+        }
+
+        log_file = g_io_stream_get_output_stream(G_IO_STREAM(io));
+    }
+
+    gchar * outputstring = g_strdup_printf("%s\n", message);
+    g_output_stream_write_async(log_file,
+                                outputstring, /* data */
+                                strlen(outputstring), /* length */
+                                G_PRIORITY_LOW, /* priority */
+                                NULL, /* cancelable */
+                                log_to_file_cb, /* callback */
+                                outputstring); /* data */
+
+    return;
+}
+
+static gboolean
+menubar_press (GtkWidget * widget,
+                    GdkEventButton *event,
+                    gpointer data G_GNUC_UNUSED)
+
+{
+    if (event->button != 1) {
+        g_signal_stop_emission_by_name(widget, "button-press-event");
+    }
+
+    return FALSE;
+
+}
+
+static gboolean
+menubar_scroll (GtkWidget      *widget G_GNUC_UNUSED,
+                GdkEventScroll *event,
+                gpointer        data G_GNUC_UNUSED)
+{
+
+    GtkWidget *menuitem;
+
+    menuitem = gtk_get_event_widget ((GdkEvent *)event);
+
+    IndicatorObject *io = g_object_get_data (G_OBJECT (menuitem), "indicator");
+    g_signal_emit_by_name (io, "scroll", 1, event->direction);
+
+    return FALSE;
+
+}
+
+static gboolean
+menubar_on_expose (GtkWidget * widget,
+                    GdkEventExpose *event G_GNUC_UNUSED,
+                    GtkWidget * menubar)
+{
+    if (GTK_WIDGET_HAS_FOCUS(menubar))
+        gtk_paint_focus(widget->style, widget->window, GTK_WIDGET_STATE(menubar),
+                        NULL, widget, "menubar-applet", 0, 0, -1, -1);
+
+    return FALSE;
+}
+
+static void indicator_load_modules(LXPanel *panel, GtkWidget *p)
+{
+
+    gint indicators_loaded = 0;
+    IndicatorPlugin * indicator = lxpanel_plugin_get_data(p);
+
+    gtk_widget_hide_all(p);
+
+    gtk_container_forall(GTK_CONTAINER(indicator->menubar),
+                         (GtkCallback)gtk_widget_destroy, NULL);
+
+    if (g_file_test(INDICATOR_DIR, (G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
+    {
+        GDir *dir = g_dir_open(INDICATOR_DIR, 0, NULL);
+
+        const gchar *name;
+        while ((name = g_dir_read_name(dir)) != NULL)
+        {
+
+            if (g_strcmp0(name, "libsession.so")== 0) {
+                if (indicator->session == 1){
+                    load_module(name, indicator->menubar);
+                    indicators_loaded++;
+                }
+            }
+            else if (g_strcmp0(name, "libapplication.so")== 0) {
+                if (indicator->applications == 1){
+                    load_module(name, indicator->menubar);
+                    indicators_loaded++;
+                }
+            }
+            else if (g_strcmp0(name, "libdatetime.so")== 0) {
+                if (indicator->datetime == 1) {
+                    load_module(name, indicator->menubar);
+                    indicators_loaded++;
+                }
+            }
+            else if (g_strcmp0(name, "libmessaging.so")== 0) {
+                if (indicator->messages == 1) {
+                    load_module(name, indicator->menubar);
+                    indicators_loaded++;
+                }
+            }
+            else if (g_strcmp0(name, "libnetworkmenu.so")== 0) {
+                if (indicator->network == 1) {
+                    load_module(name, indicator->menubar);
+                    indicators_loaded++;
+                }
+            }
+            else if (g_strcmp0(name, "libsoundmenu.so")== 0) {
+                if (indicator->sound == 1) {
+                    load_module(name, indicator->menubar);
+                    indicators_loaded++;
+                }
+            }
+            /* else if (g_strcmp0(name, "libappmenu.so") == 0) {
+                if (indicator->appmenu == 1) {
+                    load_module(name, indicator->menubar);
+                    indicators_loaded++;
+                }
+            }*/
+        }
+        g_dir_close (dir);
+    }
+
+    if (indicators_loaded == 0)
+    {
+        /* A label to allow for click through */
+        gtk_container_add(GTK_CONTAINER(p), gtk_label_new(_("No Indicators")));
+    }
+    else
+    {
+        gtk_container_add(GTK_CONTAINER(p), indicator->menubar);
+        /* Set background to default. */
+        gtk_widget_set_style(indicator->menubar, panel_get_defstyle(panel));
+    }
+
+    /* Update the display, show the widget, and return. */
+    gtk_widget_show_all(p);
+
+}
+
+/* Plugin constructor. */
+static GtkWidget *indicator_constructor(LXPanel *panel, config_setting_t *settings)
+{
+    /* Allocate and initialize plugin context and set into Plugin private data pointer. */
+    IndicatorPlugin * indicator = g_new0(IndicatorPlugin, 1);
+    GtkWidget *p;
+    int tmp_int;
+
+    indicator->panel = panel;
+    indicator->settings = settings;
+
+    /* Default support for indicators */
+    indicator->applications = TRUE;
+    indicator->datetime     = FALSE;
+    indicator->messages     = FALSE;
+    indicator->network      = FALSE;
+    indicator->session      = FALSE;
+    indicator->sound        = FALSE;
+    /* indicator->appmenu      = FALSE; */
+
+    /* Load parameters from the configuration file. */
+    if (config_setting_lookup_int(settings, "applications", &tmp_int))
+        indicator->applications = tmp_int != 0;
+    if (config_setting_lookup_int(settings, "datetime", &tmp_int))
+        indicator->datetime = tmp_int != 0;
+    if (config_setting_lookup_int(settings, "messages", &tmp_int))
+        indicator->messages = tmp_int != 0;
+    if (config_setting_lookup_int(settings, "network", &tmp_int))
+        indicator->network = tmp_int != 0;
+    if (config_setting_lookup_int(settings, "session", &tmp_int))
+        indicator->session = tmp_int != 0;
+    if (config_setting_lookup_int(settings, "sound", &tmp_int))
+        indicator->sound = tmp_int != 0;
+    /* if (config_setting_lookup_int(settings, "appmenu", &tmp_int))
+        indicator->appmenu = tmp_int != 0;*/
+
+    /* Allocate top level widget and set into Plugin widget pointer. */
+    p = gtk_event_box_new();
+    lxpanel_plugin_set_data(p, indicator, g_free);
+
+    gtk_rc_parse_string (
+        "style \"indicator-applet-style\"\n"
+        "{\n"
+        "    GtkMenuBar::shadow-type = none\n"
+        "    GtkMenuBar::internal-padding = 0\n"
+        "    GtkWidget::focus-line-width = 0\n"
+        "    GtkWidget::focus-padding = 0\n"
+        "}\n"
+        "style \"indicator-applet-menubar-style\"\n"
+        "{\n"
+        "    GtkMenuBar::shadow-type = none\n"
+        "    GtkMenuBar::internal-padding = 0\n"
+        "    GtkWidget::focus-line-width = 0\n"
+        "    GtkWidget::focus-padding = 0\n"
+        "    GtkMenuItem::horizontal-padding = 0\n"
+        "}\n"
+        "style \"indicator-applet-menuitem-style\"\n"
+        "{\n"
+        "    GtkWidget::focus-line-width = 0\n"
+        "    GtkWidget::focus-padding = 0\n"
+        "    GtkMenuItem::horizontal-padding = 0\n"
+        "}\n"
+        "widget \"*.fast-user-switch-applet\" style \"indicator-applet-style\""
+        "widget \"*.fast-user-switch-menuitem\" style \"indicator-applet-menuitem-style\""
+        "widget \"*.fast-user-switch-menubar\" style \"indicator-applet-menubar-style\"");
+
+    gtk_widget_set_name(p, "fast-user-switch-applet");
+
+    /* Connect signals for container */
+    g_log_set_default_handler(log_to_file, NULL);
+
+    /* Allocate icon as a child of top level. */
+    indicator->menubar = gtk_menu_bar_new();
+    gtk_widget_set_can_focus(indicator->menubar, TRUE);
+
+    /* Init some theme/icon stuff */
+    gtk_icon_theme_append_search_path(panel_get_icon_theme(panel),
+                                    INDICATOR_ICONS_DIR);
+    g_debug("Icons directory: %s", INDICATOR_ICONS_DIR);
+
+    gtk_widget_set_name(indicator->menubar, "fast-user-switch-menubar");
+
+    /* Connect signals. */
+    g_signal_connect(indicator->menubar, "button-press-event", G_CALLBACK(menubar_press), NULL);
+    g_signal_connect(indicator->menubar, "scroll-event", G_CALLBACK (menubar_scroll), NULL);
+    g_signal_connect_after(indicator->menubar, "expose-event", G_CALLBACK(menubar_on_expose), indicator->menubar);
+
+    gtk_container_set_border_width(GTK_CONTAINER(indicator->menubar), 0);
+
+    /* load 'em */
+    indicator_load_modules(panel, p);
+
+    return p;
+}
+
+#if 0
+/* Plugin destructor. */
+static void indicator_destructor(gpointer user_data)
+{
+    IndicatorPlugin * indicator = (IndicatorPlugin *) user_data;
+
+    /* Deallocate all memory. */
+    g_free(indicator);
+}
+#endif
+
+/* Callback when panel configuration changes. */
+static void indicator_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
+{
+    /*
+    Update when configuration changed
+    */
+
+    /* load 'em */
+    indicator_load_modules(panel, p);
+
+    /* Determine if the orientation changed in a way that requires action. */
+    /*
+    GtkWidget * sep = gtk_bin_get_child(GTK_BIN(p->pwid));
+    if (GTK_IS_VSEPARATOR(sep))
+    {
+        if (p->panel->orientation == GTK_ORIENTATION_HORIZONTAL)
+        return;
+    }
+    else
+    {
+        if (p->panel->orientation == GTK_ORIENTATION_VERTICAL)
+            return;
+    }
+*/
+}
+
+/* Callback when the configuration dialog has recorded a configuration change. */
+static gboolean indicator_apply_configuration(gpointer user_data)
+{
+    IndicatorPlugin * indicator = lxpanel_plugin_get_data(user_data);
+
+    /* load 'em */
+    indicator_load_modules(indicator->panel, user_data);
+
+    config_group_set_int(indicator->settings, "applications", indicator->applications);
+    config_group_set_int(indicator->settings, "datetime", indicator->datetime);
+    config_group_set_int(indicator->settings, "messages", indicator->messages);
+    config_group_set_int(indicator->settings, "network", indicator->network);
+    config_group_set_int(indicator->settings, "session", indicator->session);
+    config_group_set_int(indicator->settings, "sound", indicator->sound);
+    /* Apply settings. */
+/*
+    if (p->panel->orientation == ORIENT_HORIZ)
+        gtk_widget_set_size_request(p->pwid, sp->size, 2);
+    else
+        gtk_widget_set_size_request(p->pwid, 2, sp->size);
+*/
+    return FALSE;
+}
+
+/* Callback when the configuration dialog is to be shown. */
+static GtkWidget *indicator_configure(LXPanel *panel, GtkWidget *p)
+{
+    IndicatorPlugin * indicator = lxpanel_plugin_get_data(p);
+    GtkWidget * dlg = lxpanel_generic_config_dlg(_("Indicator applets"),
+        panel, indicator_apply_configuration, p,
+        _("Indicator Applications"), &indicator->applications, CONF_TYPE_BOOL,
+        _("Clock Indicator"), &indicator->datetime, CONF_TYPE_BOOL,
+        _("Messaging Menu"), &indicator->messages, CONF_TYPE_BOOL,
+        _("Network Menu"), &indicator->network, CONF_TYPE_BOOL,
+        _("Session Menu"), &indicator->session, CONF_TYPE_BOOL,
+        _("Sound Menu"), &indicator->sound, CONF_TYPE_BOOL,
+        /* _("Applications menus"), &indicator->appmenu, CONF_TYPE_BOOL,*/
+        NULL);
+    gtk_widget_set_size_request(GTK_WIDGET(dlg), 300, -1);
+    return dlg;
+}
+
+FM_DEFINE_MODULE(lxpanel_gtk, indicator)
+
+/* Plugin descriptor. */
+LXPanelPluginInit fm_module_init_lxpanel_gtk = {
+    .name = N_("Indicator applets"),
+    .description = N_("Add indicator applets to the panel"),
+
+    .new_instance = indicator_constructor,
+    .config = indicator_configure,
+    .reconfigure = indicator_panel_configuration_changed
+};
diff --git a/plugins/kbled/Makefile.am b/plugins/kbled/Makefile.am
new file mode 100644 (file)
index 0000000..c752cf7
--- /dev/null
@@ -0,0 +1,18 @@
+kbled_la_CFLAGS = \
+    -I. \
+    -I$(top_srcdir)/src \
+    -DPACKAGE_DATA_DIR=\""$(datadir)/lxpanel"\" \
+    $(PACKAGE_CFLAGS) \
+    $(G_CAST_CHECKS)
+
+module_LTLIBRARIES = kbled.la
+
+moduledir = $(libdir)/lxpanel/plugins
+
+kbled_la_SOURCES = kbled.c
+
+kbled_la_LIBADD = $(PACKAGE_LIBS)
+
+kbled_la_LDFLAGS = \
+    -module \
+    @LXPANEL_MODULE@
diff --git a/plugins/kbled/kbled.c b/plugins/kbled/kbled.c
new file mode 100644 (file)
index 0000000..c0ec8cf
--- /dev/null
@@ -0,0 +1,266 @@
+/**
+ * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "plugin.h"
+
+#include <gtk/gtk.h>
+#include <gdk/gdk.h>
+#include <gdk/gdkx.h>
+#include <glib/gi18n.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <string.h>
+
+#include <X11/XKBlib.h>
+
+#include "misc.h"
+#include "icon-grid.h"
+
+static const char * on_icons_theme[] = {
+    "capslock-on",
+    "numlock-on",
+    "scrllock-on"
+};
+
+static const char * off_icons_theme[] = {
+    "capslock-off",
+    "numlock-off",
+    "scrllock-off"
+};
+
+static const char * on_icons[] = {
+    "capslock-on.png",
+    "numlock-on.png",
+    "scrllock-on.png"
+};
+
+static const char * off_icons[] = {
+    "capslock-off.png",
+    "numlock-off.png",
+    "scrllock-off.png"
+};
+
+static int xkb_event_base = 0;
+static int xkb_error_base = 0;
+
+/* Private context for keyboard LED plugin. */
+typedef struct {
+    LXPanel * panel;                           /* Back pointer to panel */
+    config_setting_t *settings;
+    GtkWidget *indicator_image[3];             /* Image for each indicator */
+    unsigned int current_state;                        /* Current LED state, bit encoded */
+    gboolean visible[3];                       /* True if control is visible (per user configuration) */
+} KeyboardLEDPlugin;
+
+static void kbled_update_image(KeyboardLEDPlugin * kl, int i, unsigned int state);
+static void kbled_update_display(KeyboardLEDPlugin * kl, unsigned int state);
+static void kbled_destructor(gpointer user_data);
+
+static void kbled_theme_changed(GtkWidget * widget, KeyboardLEDPlugin * kl)
+{
+    /* Set orientation into the icon grid. */
+
+    /* Do a full redraw. */
+    int current_state = kl->current_state;
+    kl->current_state = ~ kl->current_state;
+    kbled_update_display(kl, current_state);
+}
+
+/* Update image to correspond to current state. */
+static void kbled_update_image(KeyboardLEDPlugin * kl, int i, unsigned int state)
+{
+    if(lxpanel_image_set_icon_theme(kl->panel, kl->indicator_image[i], (state ? on_icons_theme[i] : off_icons_theme[i])) != TRUE) {
+        char * file = g_build_filename(
+            PACKAGE_DATA_DIR "/images",
+            ((state) ? on_icons[i] : off_icons[i]),
+            NULL);
+        lxpanel_image_set_from_file(kl->panel, kl->indicator_image[i], file);
+        g_free(file);
+    }
+}
+
+/* Redraw after Xkb event or initialization. */
+static void kbled_update_display(KeyboardLEDPlugin * kl, unsigned int new_state)
+{
+    int i;
+    for (i = 0; i < 3; i++)
+    {
+        /* If the control changed state, redraw it. */
+        int current_is_lit = kl->current_state & (1 << i);
+        int new_is_lit = new_state & (1 << i);
+        if (current_is_lit != new_is_lit)
+            kbled_update_image(kl, i, new_is_lit);
+    }
+
+    /* Save new state. */
+    kl->current_state = new_state;
+}
+
+/* GDK event filter. */
+static GdkFilterReturn kbled_event_filter(GdkXEvent * gdkxevent, GdkEvent * event, KeyboardLEDPlugin * kl)
+{
+    /* Look for XkbIndicatorStateNotify events and update the display. */
+    XEvent * xev = (XEvent *) gdkxevent;
+    if (xev->xany.type == xkb_event_base + XkbEventCode)
+    {
+        XkbEvent * xkbev = (XkbEvent *) xev;
+        if (xkbev->any.xkb_type == XkbIndicatorStateNotify)
+            kbled_update_display(kl, xkbev->indicators.state);
+    }
+    return GDK_FILTER_CONTINUE;
+}
+
+/* Plugin constructor. */
+static GtkWidget *kbled_constructor(LXPanel *panel, config_setting_t *settings)
+{
+    /* Allocate and initialize plugin context and set into Plugin private data pointer. */
+    KeyboardLEDPlugin * kl = g_new0(KeyboardLEDPlugin, 1);
+    GtkWidget *p;
+    int i;
+    unsigned int current_state;
+    Display *xdisplay = GDK_DISPLAY_XDISPLAY(gdk_display_get_default());
+
+    kl->panel = panel;
+    kl->settings = settings;
+    kl->visible[0] = FALSE;
+    kl->visible[1] = TRUE;
+    kl->visible[2] = TRUE;
+
+    /* Load parameters from the configuration file. */
+    if (config_setting_lookup_int(settings, "ShowCapsLock", &i))
+        kl->visible[0] = i != 0;
+    if (config_setting_lookup_int(settings, "ShowNumLock", &i))
+        kl->visible[1] = i != 0;
+    if (config_setting_lookup_int(settings, "ShowScrollLock", &i))
+        kl->visible[2] = i != 0;
+
+    /* Allocate top level widget and set into Plugin widget pointer. */
+    p = panel_icon_grid_new(panel_get_orientation(panel),
+                            panel_get_icon_size(panel),
+                            panel_get_icon_size(panel),
+                            0, 0, panel_get_height(panel));
+    lxpanel_plugin_set_data(p, kl, kbled_destructor);
+    gtk_widget_add_events(p, GDK_BUTTON_PRESS_MASK);
+    g_signal_connect(panel_get_icon_theme(panel), "changed", G_CALLBACK(kbled_theme_changed), kl);
+
+    /* Then allocate three images for the three indications, but make them visible only when the configuration requests. */
+    for (i = 0; i < 3; i++)
+    {
+        kl->indicator_image[i] = gtk_image_new();
+        gtk_container_add(GTK_CONTAINER(p), kl->indicator_image[i]);
+        gtk_widget_set_visible(kl->indicator_image[i], kl->visible[i]);
+    }
+
+    /* Initialize Xkb extension if not yet done. */
+    if (xkb_event_base == 0)
+    {
+        int opcode;
+        int maj = XkbMajorVersion;
+        int min = XkbMinorVersion;
+        if ( ! XkbLibraryVersion(&maj, &min))
+            return 0;
+        if ( ! XkbQueryExtension(xdisplay, &opcode, &xkb_event_base, &xkb_error_base, &maj, &min))
+            return 0;
+    }
+
+    /* Add GDK event filter and enable XkbIndicatorStateNotify events. */
+    gdk_window_add_filter(NULL, (GdkFilterFunc) kbled_event_filter, kl);
+    if ( ! XkbSelectEvents(xdisplay, XkbUseCoreKbd, XkbIndicatorStateNotifyMask, XkbIndicatorStateNotifyMask))
+        return 0;
+
+    /* Get current indicator state and update display.
+     * Force current state to differ in all bits so a full redraw will occur. */
+    XkbGetIndicatorState(xdisplay, XkbUseCoreKbd, &current_state);
+    kl->current_state = ~ current_state;
+    kbled_update_display(kl, current_state);
+
+    /* Show the widget. */
+    return p;
+}
+
+/* Plugin destructor. */
+static void kbled_destructor(gpointer user_data)
+{
+    KeyboardLEDPlugin * kl = (KeyboardLEDPlugin *) user_data;
+
+    /* Remove GDK event filter. */
+    gdk_window_remove_filter(NULL, (GdkFilterFunc) kbled_event_filter, kl);
+    g_signal_handlers_disconnect_by_func(panel_get_icon_theme(kl->panel),
+                                         kbled_theme_changed, kl);
+    g_free(kl);
+}
+
+/* Callback when the configuration dialog has recorded a configuration change. */
+static gboolean kbled_apply_configuration(gpointer user_data)
+{
+    KeyboardLEDPlugin * kl = lxpanel_plugin_get_data(user_data);
+    int i;
+
+    for (i = 0; i < 3; i++)
+        gtk_widget_set_visible(kl->indicator_image[i], kl->visible[i]);
+    config_group_set_int(kl->settings, "ShowCapsLock", kl->visible[0]);
+    config_group_set_int(kl->settings, "ShowNumLock", kl->visible[1]);
+    config_group_set_int(kl->settings, "ShowScrollLock", kl->visible[2]);
+    return FALSE;
+}
+
+/* Callback when the configuration dialog is to be shown. */
+static GtkWidget *kbled_configure(LXPanel *panel, GtkWidget *p)
+{
+    KeyboardLEDPlugin * kl = lxpanel_plugin_get_data(p);
+    GtkWidget * dlg = lxpanel_generic_config_dlg(_("Keyboard LED"),
+        panel, kbled_apply_configuration, p,
+        _("Show CapsLock"), &kl->visible[0], CONF_TYPE_BOOL,
+        _("Show NumLock"), &kl->visible[1], CONF_TYPE_BOOL,
+        _("Show ScrollLock"), &kl->visible[2], CONF_TYPE_BOOL,
+        NULL);
+    gtk_widget_set_size_request(GTK_WIDGET(dlg), 200, -1);     /* Improve geometry */
+    return dlg;
+}
+
+/* Callback when panel configuration changes. */
+static void kbled_panel_configuration_changed(LXPanel *panel, GtkWidget *p)
+{
+    /* Set orientation into the icon grid. */
+    KeyboardLEDPlugin * kl = lxpanel_plugin_get_data(p);
+
+    panel_icon_grid_set_geometry(PANEL_ICON_GRID(p), panel_get_orientation(panel),
+                                 panel_get_icon_size(panel),
+                                 panel_get_icon_size(panel),
+                                 0, 0, panel_get_height(panel));
+
+    /* Do a full redraw. */
+    int current_state = kl->current_state;
+    kl->current_state = ~ kl->current_state;
+    kbled_update_display(kl, current_state);
+}
+
+FM_DEFINE_MODULE(lxpanel_gtk, kbled)
+
+/* Plugin descriptor. */
+LXPanelPluginInit fm_module_init_lxpanel_gtk = {
+    .name = N_("Keyboard LED"),
+    .description = N_("Indicators for CapsLock, NumLock, and ScrollLock keys"),
+
+    .new_instance = kbled_constructor,
+    .config = kbled_configure,
+    .reconfigure = kbled_panel_configuration_changed
+};
diff --git a/plugins/launchtaskbar.c b/plugins/launchtaskbar.c
new file mode 100644 (file)
index 0000000..55f57c4
--- /dev/null
@@ -0,0 +1,3491 @@
+/**
+ * Copyright (c) 2006-2014 LxDE Developers, see the file AUTHORS for details.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/*
+ * Started by Giuseppe Penone <giuspen@gmail.com> merging launchbar and taskbar
+ * and adding interoperability between them.
+*/
+
+/*
+ * Taskbar plugin:
+ * 2006.09.10 modified by Hong Jen Yee (PCMan) pcman.tw (AT) gmail.com
+ * Following features are added:
+ * 1. Add XUrgencyHint support. (Flashing task bar buttons, can be disabled)
+ * 2. Raise window when files get dragged over taskbar buttons.
+ * 3. Add Restore & Maximize menu items to popup menu of task bar buttons.
+ */
+//#define DEBUG_WITH_GPRINTS // killall lxpanel && lxpanel --profile Lubuntu &
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <errno.h>
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gdk-pixbuf-xlib/gdk-pixbuf-xlib.h>
+#include <gdk/gdk.h>
+#include <glib/gi18n.h>
+
+#include <libfm/fm-gtk.h>
+
+#include "misc.h"
+#include "ev.h"
+#include "plugin.h"
+#include "icon.xpm"
+#include "icon-grid.h"
+#ifndef DISABLE_MENU
+# include "menu-policy.h"
+#endif
+
+#include "dbg.h" /* for ERR macro. FIXME: is it required? */
+
+
+#define PANEL_ICON_SIZE 24 /* see the private.h */
+
+/* Column definitions for configuration dialogs. */
+enum {
+    COL_ICON,
+    COL_TITLE,
+    COL_ICON_NAME,
+    COL_BTN,
+    N_COLS
+};
+
+typedef enum {
+    LAUNCHBAR = 0,  /* GtkComboBox is 0-indexed. */
+    TASKBAR,
+    LAUNCHTASKBAR
+} LtbMode;
+
+typedef struct LaunchTaskBarPlugin LaunchTaskBarPlugin;
+
+/* Structure representing a class.  This comes from WM_CLASS, and should identify windows that come from an application. */
+typedef struct _task_class {
+    struct _task_class *p_taskclass_flink; /* Forward link */
+    char * res_class;                      /* Class name */
+    struct _task * p_task_head;            /* Head of list of tasks with this class */
+    struct _task * p_task_visible;         /* Task that is visible in current desktop, if any */
+    char * visible_name;                   /* Name that will be visible for grouped tasks */
+    int visible_count;                     /* Count of tasks that are visible in current desktop */
+} TaskClass;
+
+/* Structure representing a "task", an open window. */
+typedef struct _task {
+    struct _task * p_task_flink_xwid;       /* Forward link to next task in X window ID order */
+    LaunchTaskBarPlugin * tb;               /* Back pointer to plugin */
+    Window win;                             /* X window ID */
+    char * name;                            /* Taskbar label when normal, from WM_NAME or NET_WM_NAME */
+    char * name_iconified;                  /* Taskbar label when iconified */
+    char * exec_bin;                        /* Exec bin associated to Window */
+    Atom name_source;                       /* Atom that is the source of taskbar label */
+    TaskClass * p_taskclass;                /* Class, from WM_CLASS */
+    struct _task * p_task_flink_same_class; /* Forward link to task in same class */
+    GtkWidget * button;                     /* Button representing task in taskbar */
+    GtkWidget * image;                      /* Icon for task, child of button */
+    Atom image_source;                      /* Atom that is the source of taskbar icon */
+    GtkWidget * label;                      /* Label for task, child of button */
+    int desktop;                            /* Desktop that contains task, needed to switch to it on Raise */
+    gint monitor;                           /* Monitor that the window is on or closest to */
+    guint flash_timeout;                    /* Timer for urgency notification */
+    unsigned int focused                :1; /* True if window has focus */
+    unsigned int iconified              :1; /* True if window is iconified, from WM_STATE */
+    unsigned int urgency                :1; /* True if window has an urgency hint, from WM_HINTS */
+    unsigned int flash_state            :1; /* One-bit counter to flash taskbar */
+    unsigned int entered_state          :1; /* True if cursor is inside taskbar button */
+    unsigned int present_in_client_list :1; /* State during WM_CLIENT_LIST processing to detect deletions */
+} Task;
+
+/* Representative of one launch button.
+ * Note that the launch parameters come from the specified desktop file, or from the configuration file.
+ * This structure is also used during the "add to launchtaskbar" dialog to hold menu items. */
+typedef struct {
+    LaunchTaskBarPlugin * p;            /* Back pointer to plugin */
+    GtkWidget * widget;         /* Pointer to button */
+    GtkWidget * image_widget;   /* Pointer to image */
+    FmFileInfo * fi;                    /* Launcher application descriptor */
+    config_setting_t * settings;        /* Pointer to settings */
+    FmDndDest * dd;                     /* Drag and drop support */
+} LaunchButton;
+
+/* Private context for taskbar plugin. */
+struct LaunchTaskBarPlugin {
+    /* LAUNCHBAR */
+    GtkWidget *lb_icon_grid;         /* Icon grid managing the container */
+    GSList        *buttons;          /* Launchbar buttons */
+    LaunchButton  *bootstrap_button; /* Bootstrapping button for empty launchtaskbar */
+    FmIcon * add_icon;                  /* Icon for bootstrap_button */
+    GtkWidget     *p_button_add, *p_button_remove, *p_label_menu_app_exec, *p_label_def_app_exec;
+    /* TASKBAR */
+    Task * p_task_list;            /* List of tasks to be displayed in taskbar */
+    TaskClass * p_taskclass_list;  /* Window class list */
+    GtkWidget * tb_icon_grid;      /* Manager for taskbar buttons */
+    GtkWidget * menu;              /* Popup menu for task control (Close, Raise, etc.) */
+    GtkWidget * group_menu;        /* Popup menu for grouping selection */
+    GtkWidget * workspace_menu0;   /* "Workspace 1" menu item */
+    GdkPixbuf * fallback_pixbuf;   /* Fallback task icon when none is available */
+    int number_of_desktops;        /* Number of desktops, from NET_WM_NUMBER_OF_DESKTOPS */
+    int current_desktop;           /* Current desktop, from NET_WM_CURRENT_DESKTOP */
+    Task * focused;                /* Task that has focus */
+    Task * focused_previous;       /* Task that had focus just before panel got it */
+    Task * menutask;               /* Task for which popup menu is open */
+    guint dnd_delay_timer;         /* Timer for drag and drop delay */
+    int icon_size;                 /* Size of task icons */
+    gboolean show_all_desks;       /* User preference: show windows from all desktops */
+    gboolean tooltips;             /* User preference: show tooltips */
+    gboolean icons_only;           /* User preference: show icons only, omit name */
+    gboolean use_mouse_wheel;      /* User preference: scroll wheel does iconify and raise */
+    gboolean use_urgency_hint;     /* User preference: windows with urgency will flash */
+    gboolean flat_button;          /* User preference: taskbar buttons have visible background */
+    gboolean grouped_tasks;        /* User preference: windows from same task are grouped onto a single button */
+    gboolean same_monitor_only;    /* User preference: only show windows that are in the same monitor as the taskbar */
+    int task_width_max;            /* Maximum width of a taskbar button in horizontal orientation */
+    int spacing;                   /* Spacing between taskbar buttons */
+    gboolean use_net_active;       /* NET_WM_ACTIVE_WINDOW is supported by the window manager */
+    gboolean net_active_checked;   /* True if use_net_active is valid */
+    /* COMMON */
+#ifndef DISABLE_MENU
+    GtkWidget       *p_menuitem_lock_tbp;
+    GtkWidget       *p_menuitem_unlock_tbp;
+    GtkWidget       *p_menuitem_new_instance;
+    GtkWidget       *p_menuitem_separator;
+#endif
+    GtkWidget * plugin;                 /* Back pointer to Plugin */
+    LXPanel * panel;                    /* Back pointer to panel */
+    config_setting_t * settings;
+    GdkScreen       *screen;
+    GtkWidget       *config_dlg;        /* Configuration dialog */
+    GtkNotebook     *p_notebook;
+    GtkWidget       *p_notebook_page_launch;
+    GtkWidget       *p_notebook_page_task;
+    GKeyFile        *p_key_file_special_cases;
+    int              mode;
+    gboolean         lb_built;
+    gboolean         tb_built;
+    gboolean         fixed_mode;        /* if mode cannot be changed */
+};
+
+static gchar *launchtaskbar_rc = "style 'launchtaskbar-style' = 'theme-panel'\n"
+        "{\n"
+        "GtkWidget::focus-line-width=0\n"
+        "GtkWidget::focus-padding=0\n"
+        "GtkButton::default-border={0,0,0,0}\n"
+        "GtkButton::default-outside-border={0,0,0,0}\n"
+        "GtkButton::inner-border={0,0,0,0}\n"
+        "}\n"
+        "widget '*launchbar.*' style 'launchtaskbar-style'\n"
+        "widget '*taskbar.*' style 'launchtaskbar-style'";
+
+#define DRAG_ACTIVE_DELAY    1000
+#define TASK_WIDTH_MAX       200
+#define ALL_WORKSPACES       -1
+#define ICON_ONLY_EXTRA      6      /* Amount needed to have button lay out symmetrically */
+#define ICON_BUTTON_TRIM 4      /* Amount needed to have button remain on panel */
+
+static void launchtaskbar_destructor(gpointer user_data);
+
+static void taskbar_redraw(LaunchTaskBarPlugin * tb);
+static void task_delete(LaunchTaskBarPlugin * tb, Task * tk, gboolean unlink, gboolean remove);
+static GdkPixbuf * task_update_icon(LaunchTaskBarPlugin * tb, Task * tk, Atom source);
+static void flash_window_update(Task * tk);
+static gboolean flash_window_timeout(gpointer tk);
+static void task_group_menu_destroy(LaunchTaskBarPlugin * tb);
+static gboolean taskbar_popup_activate_event(GtkWidget * widget, GdkEventButton * event, Task * tk);
+static void taskbar_update_style(LaunchTaskBarPlugin * tb);
+static void taskbar_net_client_list(GtkWidget * widget, LaunchTaskBarPlugin * tb);
+static void taskbar_net_current_desktop(GtkWidget * widget, LaunchTaskBarPlugin * tb);
+static void taskbar_net_number_of_desktops(GtkWidget * widget, LaunchTaskBarPlugin * tb);
+static void taskbar_net_active_window(GtkWidget * widget, LaunchTaskBarPlugin * tb);
+static gboolean task_has_urgency(Task * tk);
+static GdkFilterReturn taskbar_event_filter(XEvent * xev, GdkEvent * event, LaunchTaskBarPlugin * tb);
+static void taskbar_make_menu(LaunchTaskBarPlugin * tb);
+static void taskbar_window_manager_changed(GdkScreen * screen, LaunchTaskBarPlugin * tb);
+static void taskbar_apply_configuration(LaunchTaskBarPlugin * ltbp);
+
+static void f_get_exec_cmd_from_pid(GPid pid, gchar *buffer_128, const gchar *proc_file)
+{
+    buffer_128[0] = '\0';
+    FILE *pipe;
+    gchar  command[64];
+    snprintf(command, 64, "cat /proc/%u/%s", pid, proc_file);
+    pipe = popen(command, "r");
+    if(pipe == NULL)
+        ERR("ltbp: popen '%s'\n", command);
+    else if(fgets(buffer_128, 128, pipe) == NULL)
+        ERR("ltbp: fgets '%s'\n", command);
+    else
+    {
+        gchar *p_char = strchr(buffer_128, '\n');
+        if(p_char != NULL) *p_char = '\0';
+    }
+    if(pipe != NULL) pclose(pipe);
+}
+
+#ifndef DISABLE_MENU
+static FmFileInfo *f_find_menu_launchbutton_recursive(const char *exec_bin)
+{
+    MenuCache *mc;
+    guint32 flags;
+    GSList *apps, *l;
+    size_t len;
+    const char *exec, *short_exec;
+    char *str_path;
+    FmPath *path;
+    FmFileInfoJob *job;
+    FmFileInfo *fi = NULL;
+
+    /* FIXME: cache it in Task object */
+    mc = panel_menu_cache_new(&flags);
+    apps = menu_cache_list_all_apps(mc);
+    short_exec = strrchr(exec_bin, '/');
+    if (short_exec != NULL)
+        short_exec++;
+    else
+        short_exec = exec_bin;
+    len = strlen(short_exec);
+    /* the same executable may be used in numerous applications so wild guess
+       estimation check for desktop id equal to short_exec+".desktop" first */
+    for (l = apps; l; l = l->next)
+    {
+        exec = menu_cache_item_get_id(MENU_CACHE_ITEM(l->data));
+        /* we don't check flags here because user always can manually
+           start any app that isn't visible in the desktop menu */
+        if (strncmp(exec, short_exec, len) == 0 && exec[len] == '.')
+            break;
+    }
+    /* if not found then check for non-absolute exec name in application
+       since it usually is expanded by application starting functions */
+    if (l == NULL) for (l = apps; l; l = l->next)
+    {
+        exec = menu_cache_app_get_exec(MENU_CACHE_APP(l->data));
+        if (exec[0] != '/' && strncmp(exec, short_exec, len) == 0 &&
+            (exec[len] == ' ' || exec[len] == 0))
+            break;
+    }
+    /* well, not matched, let try full path, we assume here if application
+       starts executable by full path then process cannot have short name */
+    if (l == NULL && exec_bin[0] == '/')
+    {
+        len = strlen(exec_bin);
+        for (l = apps; l; l = l->next)
+        {
+            exec = menu_cache_app_get_exec(MENU_CACHE_APP(l->data));
+            if (exec[0] == '/' && strncmp(exec, exec_bin, len) == 0 &&
+                (exec[len] == ' ' || exec[len] == 0))
+                break;
+        }
+    }
+    if (l)
+    {
+        str_path = menu_cache_dir_make_path(MENU_CACHE_DIR(l->data));
+        path = fm_path_new_relative(fm_path_get_apps_menu(), str_path+13); /* skip /Applications */
+        g_free(str_path);
+        job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
+        fm_file_info_job_add(job, path);
+        fm_path_unref(path);
+        if (!fm_job_run_sync(FM_JOB(job)))
+            g_warning("launchtaskbar: problem running file info job");
+        else
+            fi = fm_file_info_list_pop_head(job->file_infos);
+        g_object_unref(job);
+    }
+    g_slist_foreach(apps, (GFunc)menu_cache_item_unref, NULL);
+    g_slist_free(apps);
+    menu_cache_unref(mc);
+    g_debug("f_find_menu_launchbutton_recursive: search '%s' found=%d", exec_bin, (fi != NULL));
+    return fi;
+}
+#endif
+
+/* Deallocate a LaunchButton. */
+static void launchbutton_free(LaunchButton * btn)
+{
+    if (btn->fi)
+        fm_file_info_unref(btn->fi);
+    if (btn->dd)
+        g_object_unref(btn->dd);
+    g_free(btn);
+}
+
+/* Handler for "button-press-event" event from launchtaskbar button. */
+static gboolean launchbutton_press_event(GtkWidget * widget, GdkEventButton * event, LaunchButton * b)
+{
+    if (event->button == 1 && event->type == GDK_BUTTON_PRESS) /* left button */
+    {
+        if (b->fi == NULL)  /* The bootstrap button */
+            lxpanel_plugin_show_config_dialog(b->p->plugin);
+        else
+            lxpanel_launch_path(b->p->panel, fm_file_info_get_path(b->fi));
+    }
+    return TRUE;
+}
+
+/* Handler for "drag-motion" event from launchtaskbar button. */
+static gboolean launchbutton_drag_motion_event(
+    GtkWidget * widget,
+    GdkDragContext * context,
+    gint x,
+    gint y,
+    guint time,
+    LaunchButton * b)
+{
+    GdkAtom target;
+    GdkDragAction action = 0;
+
+    fm_dnd_dest_set_dest_file(b->dd, b->fi);
+    target = fm_dnd_dest_find_target(b->dd, context);
+    if (target != GDK_NONE && fm_dnd_dest_is_target_supported(b->dd, target))
+        action = fm_dnd_dest_get_default_action(b->dd, context, target);
+    gdk_drag_status(context, action, time);
+    /* g_debug("launchbutton_drag_motion_event: act=%u",action); */
+    return (action != 0);
+}
+
+/* Build the graphic elements for the bootstrap launchtaskbar button. */
+static void launchbutton_build_bootstrap(LaunchTaskBarPlugin *lb)
+{
+    if(lb->bootstrap_button == NULL)
+    {
+        GdkPixbuf * icon;
+        /* Build a button that has the stock "Add" icon.
+         * The "desktop-id" being NULL is the marker that this is the bootstrap button. */
+        lb->bootstrap_button = g_new0(LaunchButton, 1);
+        lb->bootstrap_button->p = lb;
+
+        /* Create an event box. */
+        GtkWidget * event_box = gtk_event_box_new();
+        gtk_container_set_border_width(GTK_CONTAINER(event_box), 0);
+        gtk_widget_set_can_focus            (event_box, FALSE);
+        lb->bootstrap_button->widget = event_box;
+        g_signal_connect(event_box, "button-press-event", G_CALLBACK(launchbutton_press_event), lb->bootstrap_button);
+
+        /* Create an image containing the stock "Add" icon as a child of the event box. */
+        lb->add_icon = fm_icon_from_name(GTK_STOCK_ADD);
+        icon = fm_pixbuf_from_icon(lb->add_icon, lb->icon_size);
+        lb->bootstrap_button->image_widget = gtk_image_new_from_pixbuf(icon);
+        g_object_unref(icon);
+        gtk_misc_set_padding(GTK_MISC(lb->bootstrap_button->image_widget), 0, 0);
+        gtk_misc_set_alignment(GTK_MISC(lb->bootstrap_button->image_widget), 0, 0);
+        gtk_container_add(GTK_CONTAINER(event_box), lb->bootstrap_button->image_widget);
+
+        /* Add the bootstrap button to the icon grid.  By policy it is empty at this point. */
+        gtk_container_add(GTK_CONTAINER(lb->lb_icon_grid), event_box);
+        gtk_widget_show(event_box);
+    }
+    else
+        gtk_widget_show(lb->bootstrap_button->widget);
+}
+
+#ifndef DISABLE_MENU
+static LaunchButton *launchbar_exec_bin_exists(LaunchTaskBarPlugin *lb, FmFileInfo *fi)
+{
+    LaunchButton *ret_val = NULL;
+    FmPath *path;
+    GSList* l;
+
+    if (!fi)
+        return NULL;
+    path = fm_file_info_get_path(fi);
+    for(l = lb->buttons; l != NULL; l = l->next)
+    {
+        LaunchButton *btn = (LaunchButton *)l->data;
+        if (btn->fi && fm_path_equal(path, fm_file_info_get_path(btn->fi)))
+        {
+            ret_val = btn;
+            break;
+        }
+    }
+    return ret_val;
+}
+#endif
+
+static void launchbar_update_after_taskbar_class_added(LaunchTaskBarPlugin *ltbp, Task *tk)
+{
+    GPid   pid = get_net_wm_pid(tk->win);
+    gchar  exec_bin_full[128];
+    f_get_exec_cmd_from_pid(pid, exec_bin_full, "cmdline");
+    gchar *p_char = strrchr(exec_bin_full, '/');
+    if(p_char == NULL) p_char = exec_bin_full;
+    else p_char++;
+    g_free(tk->exec_bin);
+    if(strcmp(p_char, "python") == 0)
+    {
+        f_get_exec_cmd_from_pid(pid, exec_bin_full, "comm");
+    }
+    else
+    {
+        tk->exec_bin = g_key_file_get_string(ltbp->p_key_file_special_cases,
+                                             "special_cases", p_char, NULL);
+        if (tk->exec_bin != NULL) /* found this key */
+            return;
+    }
+    tk->exec_bin = g_strdup(exec_bin_full);
+
+#ifdef DEBUG_WITH_GPRINTS
+    if(ltbp->mode == LAUNCHTASKBAR)
+    {
+        FmFileInfo *fi = f_find_menu_launchbutton_recursive(tk->exec_bin);
+        LaunchButton *btn = launchbar_exec_bin_exists(ltbp, fi);
+        g_print("\nTB '%s' OPEN (pid=%u), in LB: %c\n",
+            tk->exec_bin, pid, btn != NULL ? 'Y':'N');
+        if (fi)
+            fm_file_info_unref(fi);
+    }
+#endif
+}
+
+static void launchbar_update_after_taskbar_class_removed(LaunchTaskBarPlugin *ltbp, Task *tk)
+{
+#ifdef DEBUG_WITH_GPRINTS
+    if(ltbp->mode == LAUNCHTASKBAR)
+    {
+        FmFileInfo *fi = f_find_menu_launchbutton_recursive(tk->exec_bin);
+        LaunchButton *btn = launchbar_exec_bin_exists(ltbp, fi);
+        g_print("\nTB '%s' CLOSE, in LB: %c\n", tk->exec_bin, btn != NULL ? 'Y':'N');
+        if (fi)
+            fm_file_info_unref(fi);
+    }
+#endif
+}
+
+/* Build the graphic elements for a launchtaskbar button.  The desktop_id field is already established. */
+/* NOTE: this func consumes reference on fi */
+static LaunchButton *launchbutton_for_file_info(LaunchTaskBarPlugin * lb, FmFileInfo * fi)
+{
+    LaunchButton *btn;
+    GtkWidget *button;
+
+    if (fi == NULL)
+    {
+        g_warning("launchbar: desktop entry does not exist\n");
+        return NULL;
+    }
+
+    /* Allocate the LaunchButton structure. */
+    btn = g_new0(LaunchButton, 1);
+    btn->p = lb;
+    btn->fi = fi;
+
+    /* Create a button with the specified icon. */
+    button = lxpanel_button_new_for_fm_icon(lb->panel, fm_file_info_get_icon(fi),
+                                            NULL, NULL);
+    btn->widget = button;
+    gtk_widget_set_can_focus(button, FALSE);
+
+    gtk_widget_set_tooltip_text(button, fm_file_info_get_disp_name(fi));
+
+    /* Add the button to the icon grid. */
+    gtk_container_add(GTK_CONTAINER(lb->lb_icon_grid), button);
+
+    /* Drag and drop support. */
+    btn->dd = fm_dnd_dest_new_with_handlers(button);
+
+    /* Connect signals. */
+    g_signal_connect(button, "button-press-event", G_CALLBACK(launchbutton_press_event), (gpointer) btn);
+    g_signal_connect(button, "drag-motion", G_CALLBACK(launchbutton_drag_motion_event), btn);
+
+    /* If the list goes from null to non-null, remove the bootstrap button. */
+    if ((lb->buttons == NULL) && (lb->bootstrap_button != NULL))
+        gtk_widget_hide(lb->bootstrap_button->widget);
+
+    /* Append at end of list to preserve configured order. */
+    lb->buttons = g_slist_append(lb->buttons, btn);
+
+    /* Show the widget and return. */
+    gtk_widget_show(button);
+    plugin_widget_set_background(button, lb->panel);
+    return btn;
+}
+
+static LaunchButton *launchbutton_build_gui(LaunchTaskBarPlugin * lb, FmPath * id)
+{
+    /* Try to get the file data */
+    FmFileInfoJob *job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_NONE);
+    FmFileInfo *fi;
+
+    fm_file_info_job_add(job, id);
+    if (!fm_job_run_sync(FM_JOB(job)))
+    {
+        g_warning("launchbar: problem running file info job\n");
+        g_object_unref(job);
+        return NULL;
+    }
+    fi = fm_file_info_list_pop_head(job->file_infos);
+    g_object_unref(job);
+    return launchbutton_for_file_info(lb, fi);
+}
+
+static LaunchButton *launchbutton_search_and_build_gui(LaunchTaskBarPlugin * lb, FmPath * id)
+{
+    FmDirListJob *job = fm_dir_list_job_new2(id, FM_DIR_LIST_JOB_FAST);
+    FmFileInfo