/*
* Copyright (C) 2013 National University of Defense Technology(NUDT) & Kylin Ltd.
*
* Authors:
* lenky gao lenky0401@gmail.com/gaoqunkai@ubuntukylin.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; version 3.
*
* 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, see <http://www.gnu.org/licenses/>.
*/
#include <QDebug>
#include <QDesktopServices>
#include <QUrl>
#include <QDir>
#include <QString>
#include <QMessageBox>
#include <QSettings>
#include <libintl.h>
#include <QProcess>
#include <fcitx-config/fcitx-config.h>
#include <stdio.h>
#include "main.h"
#include "my_action.h"
#include "main_controller.h"
#include "system_tray_menu.h"
#include "skin/skinfcitx.h"
SystemTrayMenu::SystemTrayMenu(PanelAgent *agent)
: QMenu()
{
mAgent = agent;
configtoolPro= new QProcess;
}
SystemTrayMenu::~SystemTrayMenu()
{
if(configtoolPro)
free(configtoolPro);
delete mVKListMenu;
delete mSkinMenu;
delete mMozcHiraganaMenu;
delete mMozcToolMenu;
}
void SystemTrayMenu::init()
{
mVKListMenu = new QMenu(gettext("Virtual Keyboard"), this);
mSkinMenu = new QMenu(gettext("Skin"), this);
mMozcHiraganaMenu = new QMenu(gettext("Mozc Edit mode"), this);
mMozcToolMenu = new QMenu(gettext("Mozc Tool"), this);
QObject::connect(this, SIGNAL(aboutToHide()), this,
SLOT(clearMenu()));
QObject::connect(this, SIGNAL(aboutToShow()), this,
SLOT(triggerUpdateVKListMenu()));
QObject::connect(this, SIGNAL(aboutToShow()), this,
SLOT(truggerUpdateMozcHiraganaMenu()));
QObject::connect(this, SIGNAL(aboutToShow()), this,
SLOT(truggerUpdateMozcToolMenu()));
QObject::connect(this, SIGNAL(aboutToShow()), this,
SLOT(triggerUpdateIMListMenu()));
QObject::connect(this, SIGNAL(aboutToShow()), this,
SLOT(updateMainMenu()));
QObject::connect(this, SIGNAL(triggered(QAction*)), this,
SLOT(menuItemOnClick(QAction *)));
QObject::connect(mAgent, SIGNAL(execMenu(const QList<KimpanelProperty>)),
this, SLOT(execMenu(const QList<KimpanelProperty>)));
QObject::connect(mAgent, SIGNAL(registerProperties(const QList<KimpanelProperty>)),
this, SLOT(registerProperties(const QList<KimpanelProperty>)));
QObject::connect(mAgent, SIGNAL(updateProperty(KimpanelProperty)), this,
SLOT(updateProperty(KimpanelProperty)));
triggerUpdateVKListMenu();
truggerUpdateMozcHiraganaMenu();
truggerUpdateMozcToolMenu();
triggerUpdateIMListMenu();
triggerUpdateIMListMenu();
updateMainMenu();
}
void SystemTrayMenu::registerProperties(const QList<KimpanelProperty> &props)
{
int count = 0;
mStatusMenuList.clear();
foreach(const KimpanelProperty &prop, props) {
// qDebug() << i<<QString("registerProperties(1:%1 2:%2 3:%3 4:%4 5:%5 6:%6)\n").arg(prop.key)
// .arg(prop.label).arg(prop.icon).arg(prop.tip).arg(prop.state).arg(prop.menu);
if (count ++ < StatusMenuSkip)
continue;
if (prop.key == "/Fcitx/vk")
continue;
this->mStatusMenuList << prop;
}
}
void SystemTrayMenu::updateProperty(const KimpanelProperty &prop)
{
if (gettext("No input window") == prop.label)
return;
this->mCurtIMLabel = prop.label;
}
void SystemTrayMenu::clearMenu()
{
// this->clear();//会崩溃
}
void SystemTrayMenu::updateMainMenu()
{
if(!this->isEmpty())
this->clear();
this->addAction(QIcon::fromTheme("help-contents"), gettext("Online &Help!"));
this->addSeparator();
QString currentIM = this->doUpdateIMListMenu(mIMList);
this->addSeparator();
if (currentIM=="fcitx-kbd")
{
this->doUpdateVKListMenu(mVKList);
this->addSeparator();
}
if (currentIM =="/Fcitx/im/mozc")
{
this->doUpdateMozcHiraganaListMenu(mMozcHiraganaList);
this->doUpdateMozcToolListMenu(mMozcToolList);
this->addSeparator();
}
else
{
foreach(const KimpanelProperty &prop, this->mStatusMenuList) {
MyAction *menu=NULL;
if(prop.icon.contains(PREFIX "/share/fcitx"))
menu = new MyAction(QIcon(prop.icon), prop.label, this);
else
menu = new MyAction(QIcon::fromTheme(prop.icon), prop.label, this);
menu->setMyActionType(StatusAction);
menu->setProp(prop);
this->addAction(menu);
}
this->addSeparator();
}
this->addAction(QIcon::fromTheme("preferences-desktop"), gettext("ConfigureFcitx"));
this->addAction(QIcon::fromTheme("preferences-desktop"), gettext("ConfigureIMPanel"));
this->addSeparator();
if (isUnity()) {
this->addAction(gettext("Character Map"));
this->addAction(gettext("Keyboard Layout Chart"));
this->addAction(gettext("Text Entry Settings..."));
this->addSeparator();
}
doUpdateSkinListMenu();
}
void SystemTrayMenu::triggerUpdateVKListMenu()
{
mAgent->triggerProperty(QString("/Fcitx/vk"));
}
void SystemTrayMenu::triggerUpdateIMListMenu()
{
mAgent->triggerProperty(QString("/Fcitx/im"));
}
void SystemTrayMenu::truggerUpdateMozcHiraganaMenu()
{
mAgent->triggerProperty(QString("/Fcitx/mozc-composition-mode"));
}
void SystemTrayMenu::truggerUpdateMozcToolMenu()
{
mAgent->triggerProperty(QString("/Fcitx/mozc-tool"));
}
void SystemTrayMenu::doUpdateVKListMenu(const QList<KimpanelProperty> &prop_list)
{
MyAction *menu;
QList<KimpanelProperty>::const_iterator iter;
mVKListMenu->clear();
for (iter = prop_list.begin(); iter != prop_list.end(); ++ iter) {
menu = new MyAction(QIcon::fromTheme(iter->icon), iter->label, this);
menu->setProp(*iter);
mVKListMenu->addAction(menu);
}
this->addMenu(mVKListMenu);
}
QString SystemTrayMenu::doUpdateIMListMenu(const QList<KimpanelProperty> &prop_list)
{
bool checked = false;
QString value = "fcitx-kbd";
MyAction *firstAction = NULL, *action = NULL, *actionChecked = NULL;
QList<KimpanelProperty>::const_iterator iter;
for (iter = prop_list.begin(); iter != prop_list.end(); ++ iter) {
if(iter->icon=="fcitx-kbd" || iter->icon==""||iter->icon.indexOf("indicator-keyboard")!=-1)
action = new MyAction(QIcon::fromTheme("fcitx-kbd"), iter->label, this);
else if(iter->icon.contains(PREFIX "/share/fcitx"))
action = new MyAction(QIcon(iter->icon), iter->label, this);
else
action = new MyAction(QIcon::fromTheme(iter->icon), iter->label, this);
action->setProp(*iter);
action->setMyActionType(IMAction);
this->addAction(action);
if (firstAction == NULL)
firstAction = action;
action->setCheckable(true);
if (iter->label == this->mCurtIMLabel) {
if(iter->icon=="fcitx-kbd" || iter->icon==""||iter->icon.indexOf("indicator-keyboard")!=-1)
value = "fcitx-kbd";
else
value = iter->key;
checked = true;
actionChecked = action;
action->setChecked(true);
}
}
if (!checked && firstAction)
{
actionChecked = firstAction;
firstAction->setChecked(true);
}
if (actionChecked == NULL)
value = "fcitx-kbd";
return value;
}
void SystemTrayMenu::doUpdateMozcToolListMenu(const QList<KimpanelProperty> &prop_list)
{
MyAction *menu;
QList<KimpanelProperty>::const_iterator iter;
mMozcHiraganaMenu->clear();
for (iter = prop_list.begin(); iter != prop_list.end(); ++ iter) {
menu = new MyAction(QIcon(iter->icon), iter->label, this);
menu->setProp(*iter);
mMozcHiraganaMenu->addAction(menu);
}
this->addMenu(mMozcHiraganaMenu);
}
void SystemTrayMenu::doUpdateMozcHiraganaListMenu(const QList<KimpanelProperty> &prop_list)
{
MyAction *menu;
QList<KimpanelProperty>::const_iterator iter;
mMozcToolMenu->clear();
for (iter = prop_list.begin(); iter != prop_list.end(); ++ iter) {
menu =new MyAction(QIcon(iter->icon), iter->label, this);
menu->setProp(*iter);
mMozcToolMenu->addAction(menu);
}
this->addMenu(mMozcToolMenu);
}
bool SystemTrayMenu::isIMList(const QString &key)
{
return key.startsWith("/Fcitx/im");
}
bool SystemTrayMenu::isVKList(const QString &key)
{
return key.startsWith("/Fcitx/vk");
}
bool SystemTrayMenu::isMozcHiraganaList(const QString &key)
{
return key.startsWith("/Fcitx/mozc-composition-mode");
}
bool SystemTrayMenu::isMozcToolList(const QString &key)
{
return key.startsWith("/Fcitx/mozc-tool");
}
void SystemTrayMenu::execMenu(const QList<KimpanelProperty> &prop_list)
{
if(prop_list.isEmpty())
return;
QList<KimpanelProperty>::const_iterator iter = prop_list.begin();
if(iter->key.isNull())
return;
if (isIMList(iter->key))
mIMList = prop_list;
else if (isVKList(iter->key))
mVKList = prop_list;
else if (isMozcHiraganaList(iter->key))
mMozcHiraganaList=prop_list;
else if (isMozcToolList(iter->key))
mMozcToolList = prop_list;
else{
for (iter = prop_list.begin(); iter != prop_list.end(); ++ iter) {
// qDebug() << QString("execMenuCallback(1:%1 2:%2 3:%3 4:%4 5:%5 6:%6)").arg(iter->key)
// .arg(iter->label).arg(iter->icon).arg(iter->tip).arg(iter->state).arg(iter->menu);
}
}
}
void SystemTrayMenu::restart()
{
/* exec command */
pid_t child_pid;
child_pid = fork();
if (child_pid < 0) {
perror("fork");
} else if (child_pid == 0) { /* child process */
pid_t grandchild_pid;
grandchild_pid = fork();
if (grandchild_pid < 0) {
perror("fork");
_exit(1);
} else if (grandchild_pid == 0) { /* grandchild process */
char *argv_exec[] = {(char *)"fcitx-qimpanel", NULL};
execvp("fcitx-qimpanel", argv_exec);
perror("execvp");
_exit(1);
} else {
_exit(0);
}
} else { /* parent process */
int status;
waitpid(child_pid, &status, 0);
_exit(0);
}
}
void SystemTrayMenu::startChildApp(const char *app_exe, const char * const argv[])
{
/* exec command */
pid_t child_pid;
child_pid = fork();
if (child_pid < 0) {
perror("fork");
} else if (child_pid == 0) { /* child process */
pid_t grandchild_pid;
grandchild_pid = fork();
if (grandchild_pid < 0) {
perror("fork");
_exit(1);
} else if (grandchild_pid == 0) { /* grandchild process */
execvp(app_exe, const_cast<char * const *>(argv));
perror("execvp");
_exit(1);
} else {
_exit(0);
}
}
}
void SystemTrayMenu::menuItemOnClick(QAction *action)
{
if (gettext("Online &Help!") == action->text()) {
QDesktopServices::openUrl(QUrl("http://fcitx-im.org/"));
} else if (gettext("ConfigureIM") == action->text()) {
mAgent->triggerProperty(QString("/Fcitx/logo/configureim"));
} else if (gettext("ConfigureIMPanel") == action->text()) {
QFile toolFile(getQimpanelBinPath("fcitx-qimpanel-configtool"));
if (!toolFile.exists()) {
QMessageBox::warning(this,gettext("Warning"),gettext("Please install fcitx-qimpanel-configtool!"));
}
// getRunCmdOuput("fcitx-qimpanel-configtool");
if(configtoolPro->state()!=QProcess::Running)
configtoolPro->start("fcitx-qimpanel-configtool");
} else if (gettext("ConfigureFcitx") == action->text()) {
mAgent->configure();
} else if (gettext("Restart") == action->text()) {
mAgent->restart();
this->restart();
} else if (gettext("Exit") == action->text()) {
mAgent->exit();
exit(0);
} else if (gettext("Character Map") == action->text()) {
startChildApp("gucharmap");
} else if (gettext("Keyboard Layout Chart") == action->text()) {
const char *argv[4] = { "gkbd-keyboard-display", "-l", NULL, NULL };
QString layout = "us";
QList<KimpanelProperty>::const_iterator i;
for (i = mIMList.begin(); i != mIMList.end(); ++i) {
if (i->label == mCurtIMLabel) {
if (i->key.startsWith("/Fcitx/im/fcitx-keyboard-")) {
layout = i->key.mid(strlen("/Fcitx/im/fcitx-keyboard-"));
}
break;
}
}
QByteArray bytes = layout.toLatin1();
int separator = bytes.indexOf('-');
if (separator != -1)
bytes[separator] = '\t';
argv[2] = bytes.constData();
startChildApp("gkbd-keyboard-display", argv);
} else if (gettext("Text Entry Settings...") == action->text()) {
const char *argv[4] = { "unity-control-center", "region", "layouts", NULL };
startChildApp("unity-control-center", argv);
} else {
MyAction *myAction = (MyAction *)action;
if (SkinAction == myAction->getMyActionType()) {
skinMenuItemOnClick(myAction);
} else if (myAction->getProp().key != "") {
// qDebug()<<"---------------------"<<myAction->getProp().key;
mAgent->triggerProperty(myAction->getProp().key);
}
}
// this->clear();
}
bool SystemTrayMenu::isUnity()
{
const char *desktop = getenv("XDG_CURRENT_DESKTOP");
return desktop && !strcmp(desktop, "Unity");
}
void SystemTrayMenu::doUpdateSkinListMenu()
{
int i;
size_t len;
QDir skinDir;
bool checked = false;
MyAction *firstMenu = NULL, *menu;
QFileInfoList list;
QFileInfoList::Iterator iter;
QString skinName = MainController::self()->getSkinName();
SkinClass skinClass;
bool localExist;
MyAction *skinNameMenu = NULL;
mSkinMenu->clear();
QString localSkinPath = qgetenv("HOME") + "/.config/fcitx-qimpanel/skin/";
for (i = 0; i < 1; i ++) {
skinDir = QDir(localSkinPath);
if (!skinDir.exists())
continue;
skinDir.setFilter(QDir::Dirs);
list = skinDir.entryInfoList();
for (iter = list.begin(); iter != list.end(); ++ iter) {
if (iter->isDir() && "." != iter->fileName() && ".." != iter->fileName()) {
QFile fcitxSkinConfFile(iter->absoluteFilePath() + "/fcitx_skin.conf");
QFile sogouSkinConfFile(iter->absoluteFilePath() + "/skin.ini");
if (fcitxSkinConfFile.exists()){
skinClass = FCITX;
}else continue;
menu = new MyAction(iter->fileName(), this);
menu->setMyActionType(SkinAction);
menu->setSkinPath(iter->absoluteFilePath() + "/");
menu->setSkinClass(skinClass);
mSkinMenu->addAction(menu);
if (firstMenu == NULL)
firstMenu = menu;
menu->setCheckable(true);
if (iter->fileName() == skinName) {
checked = true;
menu->setChecked(true);
skinNameMenu = menu;
}
}
}
}
char* ukSkinPath = getQimpanelSharePath("skin");
for (i = 0; i < 1; i ++) {
skinDir = QDir(ukSkinPath);
if (!skinDir.exists())
continue;
skinDir.setFilter(QDir::Dirs);
list = skinDir.entryInfoList();
for (iter = list.begin(); iter != list.end(); ++ iter) {
if (iter->isDir() && "." != iter->fileName() && ".." != iter->fileName()) {
QFile fcitxSkinConfFile(iter->absoluteFilePath() + "/fcitx_skin.conf");
QFile sogouSkinConfFile(iter->absoluteFilePath() + "/skin.ini");
if (fcitxSkinConfFile.exists()){
skinClass = FCITX;
}else continue;
//check if exist in local
localExist = false;
QList<QAction *> localExistList = this->actions();
QList<QAction *>::iterator localExistIter;
for (localExistIter = localExistList.begin();
localExistIter != localExistList.end(); ++ localExistIter)
{
if (((MyAction *)(*localExistIter))->text() == iter->fileName()) {
localExist = true;
break;
}
}
if (localExist)
continue;
menu = new MyAction(iter->fileName(), this);
menu->setMyActionType(SkinAction);
//qDebug() << iter->absoluteFilePath();
menu->setSkinPath(iter->absoluteFilePath() + "/");
menu->setSkinClass(skinClass);
mSkinMenu->addAction(menu);
if (firstMenu == NULL)
firstMenu = menu;
menu->setCheckable(true);
if (iter->fileName() == skinName) {
checked = true;
menu->setChecked(true);
skinNameMenu = menu;
}
}
}
}
char **skinPath = FcitxXDGGetPathWithPrefix(&len, "skin");
for (i = 0; i < len; i ++) {
skinDir = QDir(skinPath[i]);
if (!skinDir.exists())
continue;
skinDir.setFilter(QDir::Dirs);
list = skinDir.entryInfoList();
for (iter = list.begin(); iter != list.end(); ++ iter) {
if (iter->isDir() && "." != iter->fileName() && ".." != iter->fileName()) {
QFile fcitxSkinConfFile(iter->absoluteFilePath() + "/fcitx_skin.conf");
QFile sogouSkinConfFile(iter->absoluteFilePath() + "/skin.ini");
if (fcitxSkinConfFile.exists()){
skinClass = FCITX;
}else continue;
menu = new MyAction(iter->fileName(), this);
menu->setMyActionType(SkinAction);
menu->setSkinPath(iter->absoluteFilePath() + "/");
menu->setSkinClass(skinClass);
mSkinMenu->addAction(menu);
if (firstMenu == NULL)
firstMenu = menu;
menu->setCheckable(true);
if (iter->fileName() == skinName) {
checked = true;
menu->setChecked(true);
skinNameMenu = menu;
}
}
}
}
if (!checked)
firstMenu->setChecked(true);
if (skinNameMenu == NULL)
skinNameMenu = firstMenu;
FcitxXDGFreePath(skinPath);
skinMenuItemOnClick(skinNameMenu);
this->addMenu(mSkinMenu);
}
void SystemTrayMenu::skinMenuItemOnClick(QAction* action)
{
SkinBase *skin;
MyAction *myAction = (MyAction *)action;
MainController::self()->setSkinName(myAction->text());
if (FCITX == myAction->getSkinClass())
skin = new SkinFcitx;
else qDebug() << "Load skin failed!";
skin->loadSkin(myAction->getSkinPath());
MainController::self()->setSkinBase(skin, myAction->getSkinClass());
}