This repository has been archived on 2026-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
ollie-kde/tray/ollie_tray.cpp

230 lines
6.5 KiB
C++

#include "ollie_tray.h"
#include <QApplication>
#include <QDBusReply>
#include <QDBusConnection>
#include <QDir>
#include <QIcon>
#include <QDebug>
#include <QInputDialog>
static const char *SERVICE = "org.ollie.SessionManager";
static const char *OBJ_PATH = "/org/ollie/SessionManager";
static const char *IFACE = "org.ollie.SessionManager";
OllieTray::OllieTray(QObject *parent)
: QObject(parent)
{
// D-Bus interface to the daemon
m_iface = new QDBusInterface(SERVICE, OBJ_PATH, IFACE,
QDBusConnection::sessionBus(), this);
// KStatusNotifierItem
m_sni = new KStatusNotifierItem("ollie-kde", this);
m_sni->setCategory(KStatusNotifierItem::ApplicationStatus);
m_sni->setTitle("Ollie");
m_sni->setIconByName("system-run"); // fallback icon
m_sni->setStatus(KStatusNotifierItem::Active);
setupMenu();
updateStatus();
updateTooltip();
// Connect D-Bus signals
QDBusConnection bus = QDBusConnection::sessionBus();
bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionCreated",
this, SLOT(onSessionCreated(QString)));
bus.connect(SERVICE, OBJ_PATH, IFACE, "SessionKilled",
this, SLOT(onSessionKilled(QString)));
bus.connect(SERVICE, OBJ_PATH, IFACE, "StateChanged",
this, SLOT(onStateChanged(QString,QString)));
}
OllieTray::~OllieTray()
{
}
void OllieTray::setupMenu()
{
m_menu = new QMenu();
m_menu->addAction("New Session", this, &OllieTray::newSession);
m_menu->addSeparator();
m_sessionsMenu = m_menu->addMenu("Sessions");
connect(m_sessionsMenu, &QMenu::aboutToShow, this, &OllieTray::refreshSessions);
m_menu->addSeparator();
m_menu->addAction("Stop All", this, &OllieTray::stopAll);
m_menu->addSeparator();
m_menu->addAction("Quit Daemon", this, &OllieTray::quitDaemon);
m_sni->setContextMenu(m_menu);
}
void OllieTray::updateStatus()
{
QStringList sessions = callListSessions();
m_totalSessions = sessions.size();
m_activeSessions = 0;
for (const QString &entry : sessions) {
QStringList parts = entry.split('\t');
if (parts.size() >= 2) {
QString state = parts[1];
if (state != "idle") {
m_activeSessions++;
}
}
}
// Icon status: active (bright) if any session is thinking, passive if all idle
if (m_activeSessions > 0) {
m_sni->setStatus(KStatusNotifierItem::NeedsAttention);
m_sni->setIconByName("ollie-active");
m_sni->setAttentionIconByName("ollie-active");
} else if (m_totalSessions > 0) {
m_sni->setStatus(KStatusNotifierItem::Active);
m_sni->setIconByName("system-run");
} else {
m_sni->setStatus(KStatusNotifierItem::Passive);
m_sni->setIconByName("system-run");
}
updateTooltip();
}
void OllieTray::updateTooltip()
{
QString tooltip;
if (m_totalSessions == 0) {
tooltip = "Ollie — no active sessions";
} else {
tooltip = QString("Ollie — %1 session%2")
.arg(m_totalSessions)
.arg(m_totalSessions == 1 ? "" : "s");
if (m_activeSessions > 0) {
tooltip += QString(", %1 active").arg(m_activeSessions);
}
}
m_sni->setToolTipTitle("Ollie");
m_sni->setToolTipSubTitle(tooltip);
}
// --- D-Bus signal handlers ---
void OllieTray::onSessionCreated(const QString &session_id)
{
Q_UNUSED(session_id);
updateStatus();
}
void OllieTray::onSessionKilled(const QString &session_id)
{
Q_UNUSED(session_id);
updateStatus();
}
void OllieTray::onStateChanged(const QString &session_id, const QString &new_state)
{
Q_UNUSED(session_id);
Q_UNUSED(new_state);
updateStatus();
}
// --- Menu actions ---
void OllieTray::newSession()
{
QString cwd = QDir::homePath();
QDBusReply<QString> reply = m_iface->call("CreateSession", cwd, QString(), QString(), "default", QString(), QString());
if (reply.isValid()) {
qDebug() << "Created session:" << reply.value();
} else {
qWarning() << "Failed to create session:" << reply.error().message();
}
}
void OllieTray::stopAll()
{
QStringList sessions = callListSessions();
for (const QString &entry : sessions) {
QStringList parts = entry.split('\t');
if (!parts.isEmpty()) {
callKillSession(parts[0]);
}
}
}
void OllieTray::quitDaemon()
{
// Stop all sessions then tell the daemon to exit
stopAll();
// Send SIGTERM to the daemon process via D-Bus (or just quit ourselves)
// For now, just quit the tray; the daemon stays alive.
// To actually quit the daemon, we'd need a Quit method on D-Bus.
QApplication::quit();
}
void OllieTray::refreshSessions()
{
m_sessionsMenu->clear();
QStringList sessions = callListSessions();
if (sessions.isEmpty()) {
m_sessionsMenu->addAction("(none)")->setEnabled(false);
return;
}
for (const QString &entry : sessions) {
QStringList parts = entry.split('\t');
if (parts.size() < 4) continue;
QString id = parts[0];
QString state = parts[1];
QString model = parts[2];
QString agent = parts[3];
// State indicator
QString indicator;
if (state == "idle") indicator = "○";
else if (state.startsWith("calling")) indicator = "⚡";
else indicator = "●";
QString label = QString("%1 %2 [%3] %4").arg(indicator, id, agent, model);
QMenu *sessionMenu = m_sessionsMenu->addMenu(label);
QString capturedId = id;
sessionMenu->addAction("Rename", this, [this, capturedId]() {
bool ok;
QString newName = QInputDialog::getText(
nullptr, "Rename Session",
"New name for session " + capturedId + ":",
QLineEdit::Normal, capturedId, &ok);
if (ok && !newName.trimmed().isEmpty()) {
m_iface->call("RenameSession", capturedId, newName.trimmed());
}
});
sessionMenu->addAction("Kill", this, [this, capturedId]() {
callKillSession(capturedId);
});
}
}
// --- D-Bus helpers ---
QStringList OllieTray::callListSessions()
{
QDBusReply<QStringList> reply = m_iface->call("ListSessions");
if (reply.isValid()) {
return reply.value();
}
return {};
}
bool OllieTray::callKillSession(const QString &id)
{
QDBusReply<bool> reply = m_iface->call("KillSession", id);
return reply.isValid() && reply.value();
}