1 ////////////////////////////////////////
3 ////////////////////////////////////////
7 Jrugr::Jrugr (int &argc
, char **argv
) :
8 QApplication(argc
, argv
),
14 // mandatory to keep it running after closing about dialogs...
15 setQuitOnLastWindowClosed(false);
16 keys
= new XKeyboard();
18 setWindowIcon(QIcon(":/about/jrugr.png"));
19 keys
->groupInfo(mGroupInfo
);
20 mActiveGroup
= keys
->activeGroup();
29 //! \warning the trayIcon *has* to be deleted here to prevent XFreeColormap() free corruption.
34 void Jrugr::setEventNames () {
35 event_names
.insert(0, "0");
36 event_names
.insert(1, "1");
37 event_names
.insert(2, "KeyPress");
38 event_names
.insert(3, "KeyRelease");
39 event_names
.insert(4, "ButtonPress");
40 event_names
.insert(5, "ButtonRelease");
41 event_names
.insert(6, "MotionNotify");
42 event_names
.insert(7, "EnterNotify");
43 event_names
.insert(8, "LeaveNotify");
44 event_names
.insert(9, "FocusIn");
45 event_names
.insert(10, "FocusOut");
46 event_names
.insert(11, "KeymapNotify");
47 event_names
.insert(12, "Expose");
48 event_names
.insert(13, "GraphicsExpose");
49 event_names
.insert(14, "NoExpose");
50 event_names
.insert(15, "VisibilityNotify");
51 event_names
.insert(16, "CreateNotify");
52 event_names
.insert(17, "DestroyNotify");
53 event_names
.insert(18, "UnmapNotify");
54 event_names
.insert(19, "MapNotify");
55 event_names
.insert(20, "MapRequest");
56 event_names
.insert(21, "ReparentNotify");
57 event_names
.insert(22, "ConfigureNotify");
58 event_names
.insert(23, "ConfigureRequest");
59 event_names
.insert(24, "GravityNotify");
60 event_names
.insert(25, "ResizeRequest");
61 event_names
.insert(26, "CirculateNotify");
62 event_names
.insert(27, "CirculateRequest");
63 event_names
.insert(28, "PropertyNotify");
64 event_names
.insert(29, "SelectionClear");
65 event_names
.insert(30, "SelectionRequest");
66 event_names
.insert(31, "SelectionNotify");
67 event_names
.insert(32, "ColormapNotify");
68 event_names
.insert(33, "ClientMessage");
69 event_names
.insert(34, "MappingNotify");
71 stat_names
.insert(0, "DONT_USE_XKB");
72 stat_names
.insert(1, "ONLY_INDICATION");
73 stat_names
.insert(2, "USE_XKB");
77 void Jrugr::setStartup () {
78 // Display the config dialog only on first run.
79 // It was really annoying to see it all time
80 bool first
= firstStart();
82 QSettings
jrugr(QDir::homePath()+"/.config/jrugr.cfg", QSettings::IniFormat
, this);
83 jrugr
.beginGroup("Style");
84 map_path
= jrugr
.value("path").toString()+"/language/";
85 jrugr
.endGroup(); //Style
86 xkbConf
= X11tools::loadXKBconf();
87 trayIcon
= new QSystemTrayIcon(this);
89 qDebug() << "Use next status:" << stat_names
.value(xkbConf
->status
);
91 if (xkbConf
->status
!= DONT_USE_XKB
) {
92 qDebug() << " Jrugr:XKB status:" << xkbConf
->status
;
93 if (xkbConf
->status
== USE_XKB
) configureXkb();
94 qDebug() << " Jrugr:Show single:" << xkbConf
->showSingle
;
95 if (mGroupInfo
.count() > 1 || xkbConf
->showSingle
) {
99 if (first
) configure();
102 //if (first) show the cfg dialog whenthere is no tray icon
108 bool Jrugr::firstStart () {
109 if (!QFile::exists(QDir::homePath()+"/.config/jrugr.cfg")) {
110 qDebug() << " Jrugr:Config file not found in:" << QDir::homePath();
111 qDebug() << " Jrugr:Create new";
112 QSettings
jrugr(QDir::homePath()+"/.config/jrugr.cfg", QSettings::IniFormat
, this);
114 QDir
themeDir("/usr/share/jrugr/theme/default");
115 if (themeDir
.exists()) themePath
= themeDir
.path();
116 else themePath
= QCoreApplication::applicationDirPath()+"/theme/default";;
117 jrugr
.beginGroup("Style");
118 map_path
= themePath
;
119 jrugr
.setValue("path", map_path
);
120 jrugr
.endGroup(); //Style
127 bool Jrugr::x11EventFilter (XEvent
*event
) {
128 qDebug() << "Jrugr::x11EventFilter:" << event_names
[event
->type
];
129 switch (event
->type
) {
131 qDebug() << "FocusIn:" << (int)(event
->xfocus
.window
);
134 qDebug() << "PropertyNotify:" << (int)(event
->xproperty
.window
);
136 keys
->processEvent(event
);
143 void Jrugr::init () {
144 keys
->groupInfo(mGroupInfo
);
145 if (xkbConf
->status
== DONT_USE_XKB
) return;
146 connect(keys
, SIGNAL(groupChanged(int)), this, SLOT(groupChange(int)));
147 connect(keys
, SIGNAL(layoutChanged()), this, SLOT(layoutChange()));
148 connect(this, SIGNAL(changeLayout(int)), this, SLOT(onChangeLayout(int)));
152 void Jrugr::buildIcon () {
155 if (mGroupInfo
.count() == 0) return;
156 if (xkbConf
->status
== DONT_USE_XKB
) {
157 qDebug() << "Jrugr::buildIcon() exit";
160 sym
= mGroupInfo
[mActiveGroup
].sym
;
162 trayIcon
->setContextMenu(contextMenu
);
163 connect(trayIcon
, SIGNAL(activated(QSystemTrayIcon::ActivationReason
)), this, SLOT(trayClicked(QSystemTrayIcon::ActivationReason
)));
164 trayIcon
->setToolTip(mGroupInfo
[mActiveGroup
].name
);
165 QString PNGfile
= map_path
+"/"+fixLayoutName(sym
)+".png";
166 QString SVGfile
= map_path
+"/"+fixLayoutName(sym
)+".svg";
167 bool havePNG
= QFile::exists(PNGfile
);
168 bool haveSVG
= QFile::exists(SVGfile
);
169 QSvgRenderer flagSVG
;
172 flagSVG
.load(SVGfile
);
173 haveSVG
= haveSVG
&& flagSVG
.isValid();
176 if (xkbConf
->showFlag
&& (havePNG
|| haveSVG
)) {
181 flagSVG
.render(&painter
, QRectF(0, 0, 32, 22));
183 trayIcon
->setIcon(QIcon(pix
));
184 } else if (havePNG
) {
185 trayIcon
->setIcon(QIcon(PNGfile
));
188 qDebug() << ":::" << sym
;
189 QFont
font("Helvetica [Cronyx]", 15);
190 //QFont font("Tahoma", 15);
192 font
.setLetterSpacing(QFont::PercentageSpacing
, 120);
193 QPixmap
pix(sym
.length()*20, 26);
196 painter
.setFont(font
);
197 painter
.fillRect(pix
.rect(), Qt::darkGray
);
198 painter
.setPen(Qt::white
);
199 painter
.drawText(pix
.rect(), Qt::AlignVCenter
|Qt::AlignCenter
, sym
);
201 trayIcon
->setIcon(QIcon(pix
));
203 if (xkbConf
->status
> 1) trayIcon
->setContextMenu(contextMenu
);
208 void Jrugr::trayClicked (QSystemTrayIcon::ActivationReason reason
) {
210 case QSystemTrayIcon::Trigger
: setNextGroup(); break;
211 case QSystemTrayIcon::DoubleClick
: setNextGroup(); break;
212 case QSystemTrayIcon::MiddleClick
: setPrevGroup(); break;
218 void Jrugr::layoutChange () {
219 qDebug()<<"Jrugr::layoutChange : reconfig";
221 qDebug()<<"Jrugr::layoutChange : done";
225 void Jrugr::groupChange (int index
) {
226 if (xkbConf
->status
== DONT_USE_XKB
) return;
227 qDebug() << "Jrugr::groupChange: index:" << index
;
228 mActiveGroup
= index
;
233 int Jrugr::setKeyLayout (QString keyConf
) {
234 qDebug() << "Jrugr::setKeyLayout:" << keyConf
;
235 QStringList argument
= keyConf
.split(" ");
236 qDebug() << " Jrugr:setxkbmap argumetns:" << argument
;
237 int result
= QProcess::execute("setxkbmap", argument
);
238 qDebug() << " Jrugr:setxkbmap result:" << result
;
243 void Jrugr::setNextGroup () {
244 keys
->setActiveGroup(mActiveGroup
+1>=mGroupInfo
.size()?0:mActiveGroup
+1);
248 void Jrugr::setPrevGroup () {
249 keys
->setActiveGroup(mActiveGroup
-1<0?mGroupInfo
.size()-1:mActiveGroup
-1);
253 void Jrugr::createMenu () {
254 qDebug() << "Jrugr::createMenu" << mGroupInfo
.size();
256 if (!contextMenu
) contextMenu
= new QMenu();
257 contextMenu
->clear();
259 for (int index
= 0; index
< mGroupInfo
.size(); ++index
) {
260 QString lname
= mGroupInfo
[index
].name
, lsym
= mGroupInfo
[index
].sym
;
262 qDebug() << " layout:" << fixLayoutName(lsym
) << "lname:" << lname
<< "sym:" << lsym
;
264 QAction
*act
= new QAction(lname
, this);
265 QString PNGfile
= map_path
+fixLayoutName(lsym
)+".png";
266 QString SVGfile
= map_path
+fixLayoutName(lsym
)+".svg";
268 if (QFile::exists(SVGfile
)) {
269 QSvgRenderer
flagSVG(SVGfile
);
271 if (flagSVG
.isValid()) {
275 flagSVG
.render(&painter
, QRectF(0, 0, 32, 22));
277 act
->setIcon(QIcon(pix
));
278 } else if (QFile::exists(PNGfile
)) {
279 act
->setIcon(QIcon(PNGfile
));
281 } else if (QFile::exists(PNGfile
)) {
282 act
->setIcon(QIcon(PNGfile
));
285 contextMenu
->addAction(act
);
288 contextMenu
->addSeparator();
289 QAction
*config
= new QAction(tr("Configure"), this);
290 config
->setData("configure");
291 contextMenu
->addAction(config
);
293 contextMenu
->addSeparator();
294 QAction
*about_qt
= new QAction(tr("About Qt"), this);
295 about_qt
->setData("about_qt");
296 contextMenu
->addAction(about_qt
);
298 QAction
*about_jrugr
= new QAction(tr("About Jrugr"), this);
299 about_jrugr
->setData("about_jrugr");
300 contextMenu
->addAction(about_jrugr
);
302 contextMenu
->addSeparator();
304 QAction
*jrugrExit
= new QAction(tr("Exit"), this);
305 jrugrExit
->setData("exit");
306 contextMenu
->addAction(jrugrExit
);
308 connect(contextMenu
, SIGNAL(triggered(QAction
*)), SLOT(actionsActivate(QAction
*)));
312 void Jrugr::reconfigure () {
313 XKBConf
*newConf
= X11tools::loadXKBconf();
316 bool diffGI
= nl
.size() != mGroupInfo
.size();
319 for (int f
= nl
.size()-1; f
>= 0; --f
) if (nl
[f
].name
!= mGroupInfo
[f
].name
|| nl
[f
].sym
!= mGroupInfo
[f
].sym
) { diffGI
= true; break; }
322 if (diffGI
|| newConf
->layouts
!= xkbConf
->layouts
|| newConf
->showFlag
!= xkbConf
->showFlag
||
323 newConf
->showSingle
!= xkbConf
->showSingle
|| newConf
->status
!= xkbConf
->status
) {
324 /*! \todo This prevent crashing when I remove one language from the
325 destination list. QStringList then contains ("us", "cz", "") and the
326 empty string causes a crash. There should be bettre way how to fix it imho... */
338 void Jrugr::configureXkb () {
339 if (xkbConf
->status
== DONT_USE_XKB
) return;
340 model
= xkbConf
->model
;
341 for (int f
= 0; f
< xkbConf
->layouts
.count(); ++f
) {
342 layout
+= xkbConf
->layouts
[f
].layout
;
343 variant
+= xkbConf
->layouts
[f
].variant
;
344 if (f
< xkbConf
->layouts
.count()-1) { layout
+= ','; variant
+= ','; }
346 option
= xkbConf
->options
.join(",");
347 qDebug() << "Set layout:" << layout
;
348 if (!(model
.isEmpty() || model
.isNull()) && !(layout
.isEmpty() || layout
.isNull())) {
349 QString args
= "-model "+model
+" -layout "+layout
;
350 QString tmp
= variant
;
351 if (!variant
.isNull() && !variant
.isEmpty() && !tmp
.remove(",").isEmpty()) args
+=" -variant "+variant
;
352 if (!option
.isNull() && !option
.isEmpty()) args
+=" -option "+option
;
353 qDebug() << " Jrugr:XKB args " << args
;
354 if (setKeyLayout(args
) == QProcess::CrashExit
) {
355 qDebug() << " Jrugr:XKB isn`t set";
356 qDebug() << " Jrugr:------------------------------";
359 qDebug() << " Jrugr:XKB set";
364 void Jrugr::onChangeLayout (int index
) {
365 qDebug() << "Jrugr::changeLayout:" << index
;
366 for (int f
= 5; f
> 0; --f
) keys
->setActiveGroup(index
); // k8: don't even ask me why this loop is here
370 void Jrugr::actionsActivate (QAction
*action
) {
371 QString cmd
= action
->data().toString();
373 qDebug() << "Jrugr::actionsActivate() command" << cmd
;
374 if (cmd
== "configure") {
376 } else if (cmd
== "about_qt") {
378 } else if (cmd
== "about_jrugr") {
379 QMessageBox::about(0,
381 tr("<h2>Jrugr, the keyboard layout switcher</h2>"
382 "<b>Version</b>: %1<p>"
383 "Gui tool to configure XKB extentions of X server.<p>"
384 "(c) 2009-2011 Fedor Chelbarakh, Petr Vanek, Ketmar").arg(JRUGR_VERSION
));
385 } else if (cmd
== "exit") {
386 qDebug() << "Jrugr::actionsActivate() exit";
389 for (int f
= mGroupInfo
.size()-1; f
>= 0; --f
) {
390 if (cmd
== mGroupInfo
[f
].name
) {
391 //qDebug() << " FOUND!" << f;
392 //keys->setActiveGroup(f);
393 emit
changeLayout(f
);
401 void Jrugr::configure () {
403 cfgForm
= new JrugrConfigForm();
404 xkbConf
->lockKeys
= true;
405 connect(cfgForm
, SIGNAL(saveConfig()), SLOT(reconfigure()));
411 bool Jrugr::loadRules () {
412 /*Load aceptet keyboard language layouts and options from X11
414 and create list for configuration window
415 Some code taken from kxkb project
418 QString x11dir
= X11tools::findX11Dir();
419 if (x11dir
.isNull() || x11dir
.isEmpty()) return false;
420 QString rulesFile
= X11tools::findXkbRulesFile(x11dir
,QX11Info::display());
421 if (rulesFile
.isNull() || rulesFile
.isEmpty()) return false;
423 rule
= X11tools::loadRules(rulesFile
,false);