diff options
Diffstat (limited to 'kxkb')
35 files changed, 6098 insertions, 0 deletions
diff --git a/kxkb/LICENSE b/kxkb/LICENSE new file mode 100644 index 000000000..d28a48f92 --- /dev/null +++ b/kxkb/LICENSE @@ -0,0 +1,16 @@ +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/kxkb/Makefile.am b/kxkb/Makefile.am new file mode 100644 index 000000000..8398e3946 --- /dev/null +++ b/kxkb/Makefile.am @@ -0,0 +1,41 @@ +INCLUDES= $(all_includes) +METASOURCES = AUTO +SUBDIRS = pics + +# it's basicly impossible to get this done with all the X headers used ;( +KDE_OPTIONS = nofinal + +bin_PROGRAMS = +lib_LTLIBRARIES = +kdeinit_LTLIBRARIES = kxkb.la +kde_module_LTLIBRARIES = kcm_keyboard.la + +kcm_keyboard_la_SOURCES = extension.cpp x11helper.cpp rules.cpp kxkbconfig.cpp pixmap.cpp \ + kcmlayout.cpp kcmlayoutwidget.ui kcmmisc.cpp kcmmiscwidget.ui +kcm_keyboard_la_LDFLAGS = $(all_libraries) -module -avoid-version -no-undefined +kcm_keyboard_la_LIBADD = -lxkbfile $(XTESTLIB) $(LIB_KIO) + +#kxkb_la_includedir = . +#kxkb_la_include_HEADERS = kxkb.h +kxkb_la_SOURCES = extension.cpp x11helper.cpp rules.cpp kxkbconfig.cpp pixmap.cpp \ + layoutmap.cpp kxkb.cpp kxkbtraywindow.cpp kxkb.skel +kxkb_la_LDFLAGS = $(all_libraries) -module -avoid-version +kxkb_la_LIBADD = -lxkbfile -lX11 -lXext $(LIB_KDEUI) + +xdg_apps_DATA = keyboard.desktop keyboard_layout.desktop + +install-data-local: + $(mkinstalldirs) $(DESTDIR)$(kde_confdir) + $(INSTALL_DATA) $(srcdir)/kxkb_groups $(DESTDIR)$(kde_confdir)/kxkb_groups + +uninstall-local: + -rm -f $(DESTDIR)$(kde_confdir)/kxkb_groups + +servicesdir = $(kde_servicesdir) +services_DATA = kxkb.desktop + +messages: rc.cpp + $(XGETTEXT) kxkb.cpp kxkbtraywindow.cpp rules.cpp extension.cpp pixmap.cpp kxkbbindings.cpp -o $(podir)/kxkb.pot + $(XGETTEXT) rules.cpp kcmlayout.cpp pixmap.cpp kcmmisc.cpp rc.cpp kxkbbindings.cpp -o $(podir)/kcmlayout.pot + +noinst_HEADERS = kxkbtraywindow.h kxkbconfig.h layoutmap.h diff --git a/kxkb/TODO b/kxkb/TODO new file mode 100644 index 000000000..344422bac --- /dev/null +++ b/kxkb/TODO @@ -0,0 +1,23 @@ +- setting up own layout if you're not root - using xkbcomp + +- allowing other latin base groups instead of "us" (e.g. "dvorak") + +- checking each layout in kxkb.cpp if it's ok for single group +- checking each layout in kcmlayout.cpp whether it's latin and don't need latin group + +- check the problem with gnome apps, first switch of layout with keyboard shortcut locks input + +- kdesktop_lock: layouts menu, flag icons, better error handling + +- parse xfree86.xml instead of xfree86.lst for XFree86 4.3.0 and higher + +- prevent application/window list from growing endlessly + +- bug 59203: call kcontrol/keys/modifiers.cpp: + KConfigGroupSaver cgs( KGlobal::config(), "Keyboard" ); + bool bMacSwap = KGlobal::config()->readBoolEntry( "Mac Modifier Swap", false ); + if( bMacSwap ) + ModifiersModule::setupMacModifierKeys(); + after each switch + +-
\ No newline at end of file diff --git a/kxkb/configure.in.in b/kxkb/configure.in.in new file mode 100644 index 000000000..64a70bdf1 --- /dev/null +++ b/kxkb/configure.in.in @@ -0,0 +1,15 @@ +# add this here so the test programs below compile +KDE_CHECK_HEADER(X11/Xlib.h) +KDE_CHECK_HEADER(X11/extensions/XKBstr.h, , + DO_NOT_COMPILE="$DO_NOT_COMPILE kxkb", +[ +#include <X11/Xlib.h> +]) +dnl Solaris lacks this file, so we should skip kxkbd here +KDE_CHECK_HEADER(X11/extensions/XKBrules.h, , + DO_NOT_COMPILE="$DO_NOT_COMPILE kxkb", +[ +#include <stdio.h> +#include <X11/Xlib.h> +#include <X11/XKBlib.h> +]) diff --git a/kxkb/extension.cpp b/kxkb/extension.cpp new file mode 100644 index 000000000..f4eee125c --- /dev/null +++ b/kxkb/extension.cpp @@ -0,0 +1,326 @@ +#include <string.h> +#include <errno.h> + +#include <qstring.h> +#include <qmap.h> +#include <qfile.h> + +#include <kdebug.h> +#include <kstandarddirs.h> +#include <kprocess.h> + +#include <X11/Xatom.h> +#include <X11/Xos.h> +#include <X11/Xlib.h> +#include <X11/XKBlib.h> +#include <X11/extensions/XKBfile.h> +#include <X11/extensions/XKBgeom.h> +#include <X11/extensions/XKM.h> + +#include "extension.h" + + +QMap<QString, FILE*> XKBExtension::fileCache; //TODO: move to class? + + +static QString getLayoutKey(const QString& layout, const QString& variant) +{ + return layout + "." + variant; +} + +QString XKBExtension::getPrecompiledLayoutFilename(const QString& layoutKey) +{ + QString compiledLayoutFileName = m_tempDir + layoutKey + ".xkm"; + return compiledLayoutFileName; +} + +XKBExtension::XKBExtension(Display *d) +{ + if ( d == NULL ) + d = qt_xdisplay(); + m_dpy = d; + +// QStringList dirs = KGlobal::dirs()->findDirs ( "tmp", "" ); +// m_tempDir = dirs.count() == 0 ? "/tmp/" : dirs[0]; + m_tempDir = locateLocal("tmp", ""); +} + +bool XKBExtension::init() +{ + // Verify the Xlib has matching XKB extension. + + int major = XkbMajorVersion; + int minor = XkbMinorVersion; + + if (!XkbLibraryVersion(&major, &minor)) + { + kdError() << "Xlib XKB extension " << major << '.' << minor << + " != " << XkbMajorVersion << '.' << XkbMinorVersion << endl; + return false; + } + + // Verify the X server has matching XKB extension. + + int opcode_rtrn; + int error_rtrn; + int xkb_opcode; + if (!XkbQueryExtension(m_dpy, &opcode_rtrn, &xkb_opcode, &error_rtrn, + &major, &minor)) + { + kdError() << "X server XKB extension " << major << '.' << minor << + " != " << XkbMajorVersion << '.' << XkbMinorVersion << endl; + return false; + } + + // Do it, or face horrible memory corrupting bugs + ::XkbInitAtoms(NULL); + + return true; +} + +void XKBExtension::reset() +{ + for(QMap<QString, FILE*>::ConstIterator it = fileCache.begin(); it != fileCache.end(); it++) { + fclose(*it); +// remove( QFile::encodeName(getPrecompiledLayoutFileName(*it)) ); + } + fileCache.clear(); +} + +XKBExtension::~XKBExtension() +{ +/* if( m_compiledLayoutFileNames.isEmpty() == false ) + deletePrecompiledLayouts();*/ +} + +bool XKBExtension::setXkbOptions(const QString& options, bool resetOld) +{ + if (options.isEmpty()) + return true; + + QString exe = KGlobal::dirs()->findExe("setxkbmap"); + if (exe.isEmpty()) + return false; + + KProcess p; + p << exe; + if( resetOld ) + p << "-option"; + p << "-option" << options; + + p.start(KProcess::Block); + + return p.normalExit() && (p.exitStatus() == 0); +} + +bool XKBExtension::setLayout(const QString& model, + const QString& layout, const QString& variant, + const QString& includeGroup, bool useCompiledLayouts) +{ + if( useCompiledLayouts == false ) { + return setLayoutInternal( model, layout, variant, includeGroup ); + } + + const QString layoutKey = getLayoutKey(layout, variant); + + bool res; + if( fileCache.contains(layoutKey) ) { + res = setCompiledLayout( layoutKey ); + kdDebug() << "setCompiledLayout " << layoutKey << ": " << res << endl; + + if( res ) + return res; + } +// else { + res = setLayoutInternal( model, layout, variant, includeGroup ); + kdDebug() << "setRawLayout " << layoutKey << ": " << res << endl; + if( res ) + compileCurrentLayout( layoutKey ); + +// } + return res; +} + +// private +bool XKBExtension::setLayoutInternal(const QString& model, + const QString& layout, const QString& variant, + const QString& includeGroup) +{ + if ( layout.isEmpty() ) + return false; + + QString exe = KGlobal::dirs()->findExe("setxkbmap"); + if( exe.isEmpty() ) { + kdError() << "Can't find setxkbmap" << endl; + return false; + } + + QString fullLayout = layout; + QString fullVariant = variant; + if( includeGroup.isEmpty() == false ) { + fullLayout = includeGroup; + fullLayout += ","; + fullLayout += layout; + +// fullVariant = baseVar; + fullVariant = ","; + fullVariant += variant; + } + + KProcess p; + p << exe; +// p << "-rules" << rule; + if( model.isEmpty() == false ) + p << "-model" << model; + p << "-layout" << fullLayout; + if( !fullVariant.isNull() && !fullVariant.isEmpty() ) + p << "-variant" << fullVariant; + + if (p.start(KProcess::Block) && p.normalExit() && (p.exitStatus() == 0)) { + return true; //setGroup( group ); + } + else { + return false; + } +} + +bool XKBExtension::setGroup(unsigned int group) +{ + kdDebug() << "Setting group " << group << endl; + return XkbLockGroup( m_dpy, XkbUseCoreKbd, group ); +} + +unsigned int XKBExtension::getGroup() const +{ + XkbStateRec xkbState; + XkbGetState( m_dpy, XkbUseCoreKbd, &xkbState ); + return xkbState.group; +} + +/** + * @brief Gets the current layout in its binary compiled form + * and write it to the file specified by 'fileName' + * @param[in] fileName file to store compiled layout to + * @return true if no problem, false otherwise + */ +bool XKBExtension::compileCurrentLayout(const QString &layoutKey) +{ + XkbFileInfo result; + memset(&result, 0, sizeof(result)); + result.type = XkmKeymapFile; + XkbReadFromServer(m_dpy, XkbAllMapComponentsMask, XkbAllMapComponentsMask, &result); + + const QString fileName = getPrecompiledLayoutFilename(layoutKey); + + kdDebug() << "compiling layout " << this << " cache size: " << fileCache.count() << endl; + if( fileCache.contains(layoutKey) ) { + kdDebug() << "trashing old compiled layout for " << fileName << endl; + if( fileCache[ layoutKey ] != NULL ) + fclose( fileCache[ layoutKey ] ); // recompiling - trash the old file + fileCache.remove(fileName); + } + + FILE *output = fopen(QFile::encodeName(fileName), "w"); + + if ( output == NULL ) + { + kdWarning() << "Could not open " << fileName << " to precompile: " << strerror(errno) << endl; + XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); + return false; + } + + if( !XkbWriteXKMFile(output, &result) ) { + kdWarning() << "Could not write compiled layout to " << fileName << endl; + fclose(output); + return false; + } + + fclose(output); // TODO: can we change mode w/out reopening? + FILE *input = fopen(QFile::encodeName(fileName), "r"); + fileCache[ layoutKey ] = input; + + XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); + return true; +} + +/** + * @brief takes layout from its compiled binary snapshot in file + * and sets it as current + * TODO: cache layout in memory rather than in file + */ +bool XKBExtension::setCompiledLayout(const QString &layoutKey) +{ + FILE *input = NULL; + + if( fileCache.contains(layoutKey) ) { + input = fileCache[ layoutKey ]; + } + + if( input == NULL ) { + kdWarning() << "setCompiledLayout trying to reopen xkb file" << endl; // should never happen + const QString fileName = getPrecompiledLayoutFilename(layoutKey); + input = fopen(QFile::encodeName(fileName), "r"); + + // FILE *input = fopen(QFile::encodeName(fileName), "r"); + if ( input == NULL ) { + kdDebug() << "Unable to open " << fileName << ": " << strerror(errno) << endl; + fileCache.remove(layoutKey); + return false; + } + } + else { + rewind(input); + } + + XkbFileInfo result; + memset(&result, 0, sizeof(result)); + if ((result.xkb = XkbAllocKeyboard())==NULL) { + kdWarning() << "Unable to allocate memory for keyboard description" << endl; +// fclose(input); +// fileCache.remove(layoutKey); + return false; + } + + unsigned retVal = XkmReadFile(input, 0, XkmKeymapLegal, &result); + if (retVal == XkmKeymapLegal) + { + // this means reading the Xkm didn't manage to read any section + kdWarning() << "Unable to load map from file" << endl; + XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); + fclose(input); + fileCache.remove(layoutKey); + return false; + } + + // fclose(input); // don't close - goes in cache + + if (XkbChangeKbdDisplay(m_dpy, &result) == Success) + { + if (!XkbWriteToServer(&result)) + { + kdWarning() << "Unable to write the keyboard layout to X display" << endl; + XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); + return false; + } + } + else + { + kdWarning() << "Unable prepare the keyboard layout for X display" << endl; + } + + XkbFreeKeyboard(result.xkb, XkbAllControlsMask, True); + return true; +} + + +// Deletes the precompiled layouts stored in temporary files +// void XKBExtension::deletePrecompiledLayouts() +// { +// QMapConstIterator<LayoutUnit, QString> it, end; +// end = m_compiledLayoutFileNames.end(); +// for (it = m_compiledLayoutFileNames.begin(); it != end; ++it) +// { +// unlink(QFile::encodeName(it.data())); +// } +// m_compiledLayoutFileNames.clear(); +// } diff --git a/kxkb/extension.h b/kxkb/extension.h new file mode 100644 index 000000000..24a0324bc --- /dev/null +++ b/kxkb/extension.h @@ -0,0 +1,37 @@ +#ifndef __EXTENSION_H__ +#define __EXTENSION_H__ + +#include <X11/Xlib.h> + + +class XKBExtension +{ +public: + XKBExtension(Display *display=NULL); + ~XKBExtension(); + bool init(); + void reset(); + + static bool setXkbOptions(const QString& options, bool resetOldOptions); + bool setLayout(const QString& model, + const QString& layout, const QString& variant, + const QString& includeGroup, bool useCompiledLayouts=true); + bool setGroup(unsigned int group); + unsigned int getGroup() const; + +private: + Display *m_dpy; + QString m_tempDir; + static QMap<QString, FILE*> fileCache; + + bool setLayoutInternal(const QString& model, + const QString& layout, const QString& variant, + const QString& includeGroup); + bool compileCurrentLayout(const QString& layoutKey); + bool setCompiledLayout(const QString& layoutKey); + + QString getPrecompiledLayoutFilename(const QString& layoutKey); +// void deletePrecompiledLayouts(); +}; + +#endif diff --git a/kxkb/kcmlayout.cpp b/kxkb/kcmlayout.cpp new file mode 100644 index 000000000..5f5b1ff84 --- /dev/null +++ b/kxkb/kcmlayout.cpp @@ -0,0 +1,976 @@ +#include <qlayout.h> +#include <qlabel.h> +#include <qcombobox.h> +#include <qtabwidget.h> +#include <qvgroupbox.h> +#include <qpushbutton.h> +#include <qlistview.h> +#include <qheader.h> +#include <qwhatsthis.h> +#include <qcheckbox.h> +#include <qradiobutton.h> +#include <qlineedit.h> +#include <qlistview.h> +#include <qbuttongroup.h> +#include <qspinbox.h> + +#include <kkeydialog.h> +#include <kglobal.h> +#include <kconfig.h> +#include <klocale.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <kapplication.h> +#include <kiconloader.h> + +#include "extension.h" +#include "kxkbconfig.h" +#include "rules.h" +#include "pixmap.h" +#include "kcmmisc.h" +#include "kcmlayoutwidget.h" + +#include "kcmlayout.h" +#include "kcmlayout.moc" + + +enum { + LAYOUT_COLUMN_FLAG = 0, + LAYOUT_COLUMN_NAME = 1, + LAYOUT_COLUMN_MAP = 2, + LAYOUT_COLUMN_VARIANT = 3, + LAYOUT_COLUMN_INCLUDE = 4, + LAYOUT_COLUMN_DISPLAY_NAME = 5, + SRC_LAYOUT_COLUMN_COUNT = 3, + DST_LAYOUT_COLUMN_COUNT = 6 +}; + +static const QString DEFAULT_VARIANT_NAME("<default>"); + + +class OptionListItem : public QCheckListItem +{ + public: + + OptionListItem( OptionListItem *parent, const QString &text, Type tt, + const QString &optionName ); + OptionListItem( QListView *parent, const QString &text, Type tt, + const QString &optionName ); + ~OptionListItem() {} + + QString optionName() const { return m_OptionName; } + + OptionListItem *findChildItem( const QString& text ); + + protected: + QString m_OptionName; +}; + + +static QString lookupLocalized(const QDict<char> &dict, const QString& text) +{ + QDictIterator<char> it(dict); + while (it.current()) + { + if ( i18n(it.current()) == text ) + return it.currentKey(); + ++it; + } + + return QString::null; +} + +static QListViewItem* copyLVI(const QListViewItem* src, QListView* parent) +{ + QListViewItem* ret = new QListViewItem(parent); + for(int i = 0; i < SRC_LAYOUT_COLUMN_COUNT; i++) + { + ret->setText(i, src->text(i)); + if ( src->pixmap(i) ) + ret->setPixmap(i, *src->pixmap(i)); + } + + return ret; +} + + +LayoutConfig::LayoutConfig(QWidget *parent, const char *name) + : KCModule(parent, name), + m_rules(NULL) +{ + QVBoxLayout *main = new QVBoxLayout(this, 0, KDialog::spacingHint()); + + widget = new LayoutConfigWidget(this, "widget"); + main->addWidget(widget); + + connect( widget->chkEnable, SIGNAL( toggled( bool )), this, SLOT(changed())); + connect( widget->chkShowSingle, SIGNAL( toggled( bool )), this, SLOT(changed())); + connect( widget->chkShowFlag, SIGNAL( toggled( bool )), this, SLOT(changed())); + connect( widget->comboModel, SIGNAL(activated(int)), this, SLOT(changed())); + + connect( widget->listLayoutsSrc, SIGNAL(doubleClicked(QListViewItem*,const QPoint&, int)), + this, SLOT(add())); + connect( widget->btnAdd, SIGNAL(clicked()), this, SLOT(add())); + connect( widget->btnRemove, SIGNAL(clicked()), this, SLOT(remove())); + + connect( widget->comboVariant, SIGNAL(activated(int)), this, SLOT(changed())); + connect( widget->comboVariant, SIGNAL(activated(int)), this, SLOT(variantChanged())); + connect( widget->listLayoutsDst, SIGNAL(selectionChanged(QListViewItem *)), + this, SLOT(layoutSelChanged(QListViewItem *))); + + connect( widget->editDisplayName, SIGNAL(textChanged(const QString&)), this, SLOT(displayNameChanged(const QString&))); + + connect( widget->chkLatin, SIGNAL(clicked()), this, SLOT(changed())); + connect( widget->chkLatin, SIGNAL(clicked()), this, SLOT(latinChanged())); + + widget->btnUp->setIconSet(SmallIconSet("1uparrow")); + connect( widget->btnUp, SIGNAL(clicked()), this, SLOT(changed())); + connect( widget->btnUp, SIGNAL(clicked()), this, SLOT(moveUp())); + widget->btnDown->setIconSet(SmallIconSet("1downarrow")); + connect( widget->btnDown, SIGNAL(clicked()), this, SLOT(changed())); + connect( widget->btnDown, SIGNAL(clicked()), this, SLOT(moveDown())); + + connect( widget->grpSwitching, SIGNAL( clicked( int ) ), SLOT(changed())); + + connect( widget->chkEnableSticky, SIGNAL(toggled(bool)), this, SLOT(changed())); + connect( widget->spinStickyDepth, SIGNAL(valueChanged(int)), this, SLOT(changed())); + + widget->listLayoutsSrc->setColumnText(LAYOUT_COLUMN_FLAG, ""); + widget->listLayoutsDst->setColumnText(LAYOUT_COLUMN_FLAG, ""); + widget->listLayoutsDst->setColumnText(LAYOUT_COLUMN_INCLUDE, ""); +// widget->listLayoutsDst->setColumnText(LAYOUT_COLUMN_DISPLAY_NAME, ""); + + widget->listLayoutsSrc->setColumnWidth(LAYOUT_COLUMN_FLAG, 28); + widget->listLayoutsDst->setColumnWidth(LAYOUT_COLUMN_FLAG, 28); + + widget->listLayoutsDst->header()->setResizeEnabled(FALSE, LAYOUT_COLUMN_INCLUDE); + widget->listLayoutsDst->header()->setResizeEnabled(FALSE, LAYOUT_COLUMN_DISPLAY_NAME); + widget->listLayoutsDst->setColumnWidthMode(LAYOUT_COLUMN_INCLUDE, QListView::Manual); + widget->listLayoutsDst->setColumnWidth(LAYOUT_COLUMN_INCLUDE, 0); +// widget->listLayoutsDst->setColumnWidth(LAYOUT_COLUMN_DISPLAY_NAME, 0); + + widget->listLayoutsDst->setSorting(-1); +#if 0 + widget->listLayoutsDst->setResizeMode(QListView::LastColumn); + widget->listLayoutsSrc->setResizeMode(QListView::LastColumn); +#endif + widget->listLayoutsDst->setResizeMode(QListView::LastColumn); + + //Read rules - we _must_ read _before_ creating xkb-options comboboxes + loadRules(); + + makeOptionsTab(); + + load(); +} + + +LayoutConfig::~LayoutConfig() +{ + delete m_rules; +} + + +void LayoutConfig::load() +{ + m_kxkbConfig.load(KxkbConfig::LOAD_ALL); + + initUI(); +} + +void LayoutConfig::initUI() { + const char* modelName = m_rules->models()[m_kxkbConfig.m_model]; + if( modelName == NULL ) + modelName = DEFAULT_MODEL; + + widget->comboModel->setCurrentText(i18n(modelName)); + + QValueList<LayoutUnit> otherLayouts = m_kxkbConfig.m_layouts; + widget->listLayoutsDst->clear(); +// to optimize we should have gone from it.end to it.begin + QValueList<LayoutUnit>::ConstIterator it; + for (it = otherLayouts.begin(); it != otherLayouts.end(); ++it ) { + QListViewItemIterator src_it( widget->listLayoutsSrc ); + LayoutUnit layoutUnit = *it; + + for ( ; src_it.current(); ++src_it ) { + QListViewItem* srcItem = src_it.current(); + + if ( layoutUnit.layout == src_it.current()->text(LAYOUT_COLUMN_MAP) ) { // check if current config knows about this layout + QListViewItem* newItem = copyLVI(srcItem, widget->listLayoutsDst); + + newItem->setText(LAYOUT_COLUMN_VARIANT, layoutUnit.variant); + newItem->setText(LAYOUT_COLUMN_INCLUDE, layoutUnit.includeGroup); + newItem->setText(LAYOUT_COLUMN_DISPLAY_NAME, layoutUnit.displayName); + widget->listLayoutsDst->insertItem(newItem); + newItem->moveItem(widget->listLayoutsDst->lastItem()); + + break; + } + } + } + + // display KXKB switching options + widget->chkShowSingle->setChecked(m_kxkbConfig.m_showSingle); + widget->chkShowFlag->setChecked(m_kxkbConfig.m_showFlag); + + widget->chkEnableOptions->setChecked( m_kxkbConfig.m_enableXkbOptions ); + widget->checkResetOld->setChecked(m_kxkbConfig.m_resetOldOptions); + + switch( m_kxkbConfig.m_switchingPolicy ) { + default: + case SWITCH_POLICY_GLOBAL: + widget->grpSwitching->setButton(0); + break; + case SWITCH_POLICY_WIN_CLASS: + widget->grpSwitching->setButton(1); + break; + case SWITCH_POLICY_WINDOW: + widget->grpSwitching->setButton(2); + break; + } + + widget->chkEnableSticky->setChecked(m_kxkbConfig.m_stickySwitching); + widget->spinStickyDepth->setEnabled(m_kxkbConfig.m_stickySwitching); + widget->spinStickyDepth->setValue( m_kxkbConfig.m_stickySwitchingDepth); + + updateStickyLimit(); + + widget->chkEnable->setChecked( m_kxkbConfig.m_useKxkb ); + widget->grpLayouts->setEnabled( m_kxkbConfig.m_useKxkb ); + widget->optionsFrame->setEnabled( m_kxkbConfig.m_useKxkb ); + + // display xkb options + QStringList options = QStringList::split(',', m_kxkbConfig.m_options); + for (QStringList::ConstIterator it = options.begin(); it != options.end(); ++it) + { + QString option = *it; + QString optionKey = option.mid(0, option.find(':')); + QString optionName = m_rules->options()[option]; + OptionListItem *item = m_optionGroups[i18n(optionKey.latin1())]; + + if (item != NULL) { + OptionListItem *child = item->findChildItem( option ); + + if ( child ) + child->setState( QCheckListItem::On ); + else + kdDebug() << "load: Unknown option: " << option << endl; + } + else { + kdDebug() << "load: Unknown option group: " << optionKey << " of " << option << endl; + } + } + + updateOptionsCommand(); + emit KCModule::changed( false ); +} + + +void LayoutConfig::save() +{ + QString model = lookupLocalized(m_rules->models(), widget->comboModel->currentText()); + m_kxkbConfig.m_model = model; + + m_kxkbConfig.m_enableXkbOptions = widget->chkEnableOptions->isChecked(); + m_kxkbConfig.m_resetOldOptions = widget->checkResetOld->isChecked(); + m_kxkbConfig.m_options = createOptionString(); + + QListViewItem *item = widget->listLayoutsDst->firstChild(); + QValueList<LayoutUnit> layouts; + while (item) { + QString layout = item->text(LAYOUT_COLUMN_MAP); + QString variant = item->text(LAYOUT_COLUMN_VARIANT); + QString includes = item->text(LAYOUT_COLUMN_INCLUDE); + QString displayName = item->text(LAYOUT_COLUMN_DISPLAY_NAME); + + LayoutUnit layoutUnit(layout, variant); + layoutUnit.includeGroup = includes; + layoutUnit.displayName = displayName; + layouts.append( layoutUnit ); + + item = item->nextSibling(); + kdDebug() << "To save: layout " << layoutUnit.toPair() + << ", inc: " << layoutUnit.includeGroup + << ", disp: " << layoutUnit.displayName << endl; + } + m_kxkbConfig.m_layouts = layouts; + + if( m_kxkbConfig.m_layouts.count() == 0 ) { + m_kxkbConfig.m_layouts.append(LayoutUnit(DEFAULT_LAYOUT_UNIT)); + widget->chkEnable->setChecked(false); + } + + m_kxkbConfig.m_useKxkb = widget->chkEnable->isChecked(); + m_kxkbConfig.m_showSingle = widget->chkShowSingle->isChecked(); + m_kxkbConfig.m_showFlag = widget->chkShowFlag->isChecked(); + + int modeId = widget->grpSwitching->id(widget->grpSwitching->selected()); + switch( modeId ) { + default: + case 0: + m_kxkbConfig.m_switchingPolicy = SWITCH_POLICY_GLOBAL; + break; + case 1: + m_kxkbConfig.m_switchingPolicy = SWITCH_POLICY_WIN_CLASS; + break; + case 2: + m_kxkbConfig.m_switchingPolicy = SWITCH_POLICY_WINDOW; + break; + } + + m_kxkbConfig.m_stickySwitching = widget->chkEnableSticky->isChecked(); + m_kxkbConfig.m_stickySwitchingDepth = widget->spinStickyDepth->value(); + + m_kxkbConfig.save(); + + kapp->kdeinitExec("kxkb"); + emit KCModule::changed( false ); +} + + +void LayoutConfig::updateStickyLimit() +{ + int layoutsCnt = widget->listLayoutsDst->childCount(); + int maxDepth = layoutsCnt - 1; + + if( maxDepth < 2 ) { + maxDepth = 2; + } + + widget->spinStickyDepth->setMaxValue(maxDepth); +/* if( value > maxDepth ) + setValue(maxDepth);*/ +} + +void LayoutConfig::add() +{ + QListViewItem* sel = widget->listLayoutsSrc->selectedItem(); + if( sel == 0 ) + return; + + // Create a copy of the sel widget, as one might add the same layout more + // than one time, with different variants. + QListViewItem* toadd = copyLVI(sel, widget->listLayoutsDst); + + widget->listLayoutsDst->insertItem(toadd); + if( widget->listLayoutsDst->childCount() > 1 ) + toadd->moveItem(widget->listLayoutsDst->lastItem()); +// disabling temporary: does not work reliable in Qt :( +// widget->listLayoutsDst->setSelected(sel, true); +// layoutSelChanged(sel); + + updateStickyLimit(); + changed(); +} + +void LayoutConfig::remove() +{ + QListViewItem* sel = widget->listLayoutsDst->selectedItem(); + QListViewItem* newSel = 0; + + if( sel == 0 ) + return; + + if( sel->itemBelow() ) + newSel = sel->itemBelow(); + else + if( sel->itemAbove() ) + newSel = sel->itemAbove(); + + delete sel; + if( newSel ) + widget->listLayoutsSrc->setSelected(newSel, true); + layoutSelChanged(newSel); + + updateStickyLimit(); + changed(); +} + +void LayoutConfig::moveUp() +{ + QListViewItem* sel = widget->listLayoutsDst->selectedItem(); + if( sel == 0 || sel->itemAbove() == 0 ) + return; + + if( sel->itemAbove()->itemAbove() == 0 ) { + widget->listLayoutsDst->takeItem(sel); + widget->listLayoutsDst->insertItem(sel); + widget->listLayoutsDst->setSelected(sel, true); + } + else + sel->moveItem(sel->itemAbove()->itemAbove()); +} + +void LayoutConfig::moveDown() +{ + QListViewItem* sel = widget->listLayoutsDst->selectedItem(); + if( sel == 0 || sel->itemBelow() == 0 ) + return; + + sel->moveItem(sel->itemBelow()); +} + +void LayoutConfig::variantChanged() +{ + QListViewItem* selLayout = widget->listLayoutsDst->selectedItem(); + if( selLayout == NULL ) { + widget->comboVariant->clear(); + widget->comboVariant->setEnabled(false); + return; + } + + QString selectedVariant = widget->comboVariant->currentText(); + if( selectedVariant == DEFAULT_VARIANT_NAME ) + selectedVariant = ""; + selLayout->setText(LAYOUT_COLUMN_VARIANT, selectedVariant); +} + +// helper +LayoutUnit LayoutConfig::getLayoutUnitKey(QListViewItem *sel) +{ + QString kbdLayout = sel->text(LAYOUT_COLUMN_MAP); + QString kbdVariant = sel->text(LAYOUT_COLUMN_VARIANT); + return LayoutUnit(kbdLayout, kbdVariant); +} + +void LayoutConfig::displayNameChanged(const QString& newDisplayName) +{ + QListViewItem* selLayout = widget->listLayoutsDst->selectedItem(); + if( selLayout == NULL ) + return; + + const LayoutUnit layoutUnitKey = getLayoutUnitKey( selLayout ); + LayoutUnit& layoutUnit = *m_kxkbConfig.m_layouts.find(layoutUnitKey); + + QString oldName = selLayout->text(LAYOUT_COLUMN_DISPLAY_NAME); + + if( oldName.isEmpty() ) + oldName = KxkbConfig::getDefaultDisplayName( layoutUnit ); + + if( oldName != newDisplayName ) { + kdDebug() << "setting label for " << layoutUnit.toPair() << " : " << newDisplayName << endl; + selLayout->setText(LAYOUT_COLUMN_DISPLAY_NAME, newDisplayName); + updateIndicator(selLayout); + emit changed(); + } +} + +/** will update flag with label if layout label has been edited +*/ +void LayoutConfig::updateIndicator(QListViewItem* selLayout) +{ +} + + +void LayoutConfig::latinChanged() +{ + QListViewItem* selLayout = widget->listLayoutsDst->selectedItem(); + if ( !selLayout ) { + widget->chkLatin->setChecked( false ); + widget->chkLatin->setEnabled( false ); + return; + } + + QString include; + if( widget->chkLatin->isChecked() ) + include = "us"; + else + include = ""; + selLayout->setText(LAYOUT_COLUMN_INCLUDE, include); + + LayoutUnit layoutUnitKey = getLayoutUnitKey(selLayout); + kdDebug() << "layout " << layoutUnitKey.toPair() << ", inc: " << include << endl; +} + +void LayoutConfig::layoutSelChanged(QListViewItem *sel) +{ + widget->comboVariant->clear(); + widget->comboVariant->setEnabled( sel != NULL ); + widget->chkLatin->setChecked( false ); + widget->chkLatin->setEnabled( sel != NULL ); + + if( sel == NULL ) { + updateLayoutCommand(); + return; + } + + + LayoutUnit layoutUnitKey = getLayoutUnitKey(sel); + QString kbdLayout = layoutUnitKey.layout; + + // TODO: need better algorithm here for determining if needs us group + if ( ! m_rules->isSingleGroup(kbdLayout) + || kbdLayout.startsWith("us") || kbdLayout.startsWith("en") ) { + widget->chkLatin->setEnabled( false ); + } + else { + QString inc = sel->text(LAYOUT_COLUMN_INCLUDE); + if ( inc.startsWith("us") || inc.startsWith("en") ) { + widget->chkLatin->setChecked(true); + } + else { + widget->chkLatin->setChecked(false); + } + } + + QStringList vars = m_rules->getAvailableVariants(kbdLayout); + kdDebug() << "layout " << kbdLayout << " has " << vars.count() << " variants" << endl; + + if( vars.count() > 0 ) { + vars.prepend(DEFAULT_VARIANT_NAME); + widget->comboVariant->insertStringList(vars); + + QString variant = sel->text(LAYOUT_COLUMN_VARIANT); + if( variant != NULL && variant.isEmpty() == false ) { + widget->comboVariant->setCurrentText(variant); + } + else { + widget->comboVariant->setCurrentItem(0); + } + } + updateLayoutCommand(); +} + +QWidget* LayoutConfig::makeOptionsTab() +{ + QListView *listView = widget->listOptions; + + listView->setMinimumHeight(150); + listView->setSortColumn( -1 ); + listView->setColumnText( 0, i18n( "Options" ) ); + listView->clear(); + + connect(listView, SIGNAL(clicked(QListViewItem *)), SLOT(changed())); + connect(listView, SIGNAL(clicked(QListViewItem *)), SLOT(updateOptionsCommand())); + + connect(widget->chkEnableOptions, SIGNAL(toggled(bool)), SLOT(changed())); + + connect(widget->checkResetOld, SIGNAL(toggled(bool)), SLOT(changed())); + connect(widget->checkResetOld, SIGNAL(toggled(bool)), SLOT(updateOptionsCommand())); + + //Create controllers for all options + QDictIterator<char> it(m_rules->options()); + OptionListItem *parent; + for (; it.current(); ++it) + { + if (!it.currentKey().contains(':')) + { + if( it.currentKey() == "ctrl" || it.currentKey() == "caps" + || it.currentKey() == "altwin" ) { + parent = new OptionListItem(listView, i18n( it.current() ), + QCheckListItem::RadioButtonController, it.currentKey()); + OptionListItem *item = new OptionListItem(parent, i18n( "None" ), + QCheckListItem::RadioButton, "none"); + item->setState(QCheckListItem::On); + } + else { + parent = new OptionListItem(listView, i18n( it.current() ), + QCheckListItem::CheckBoxController, it.currentKey()); + } + parent->setOpen(true); + m_optionGroups.insert(i18n(it.currentKey().local8Bit()), parent); + } + } + + it.toFirst(); + for( ; it.current(); ++it) + { + QString key = it.currentKey(); + int pos = key.find(':'); + if (pos >= 0) + { + OptionListItem *parent = m_optionGroups[key.left(pos)]; + if (parent == NULL ) + parent = m_optionGroups["misc"]; + if (parent != NULL) { + // workaroung for mistake in rules file for xkb options in XFree 4.2.0 + QString text(it.current()); + text = text.replace( "Cap$", "Caps." ); + if( parent->type() == QCheckListItem::RadioButtonController ) + new OptionListItem(parent, i18n(text.utf8()), + QCheckListItem::RadioButton, key); + else + new OptionListItem(parent, i18n(text.utf8()), + QCheckListItem::CheckBox, key); + } + } + } + + //scroll->setMinimumSize(450, 330); + + return listView; +} + +void LayoutConfig::updateOptionsCommand() +{ + QString setxkbmap; + QString options = createOptionString(); + + if( !options.isEmpty() ) { + setxkbmap = "setxkbmap -option "; //-rules " + m_rule + if( widget->checkResetOld->isChecked() ) + setxkbmap += "-option "; + setxkbmap += options; + } + widget->editCmdLineOpt->setText(setxkbmap); +} + +void LayoutConfig::updateLayoutCommand() +{ + QString setxkbmap; + QString layoutDisplayName; + QListViewItem* sel = widget->listLayoutsDst->selectedItem(); + + if( sel != NULL ) { + QString kbdLayout = sel->text(LAYOUT_COLUMN_MAP); + QString variant = widget->comboVariant->currentText(); + if( variant == DEFAULT_VARIANT_NAME ) + variant = ""; + + setxkbmap = "setxkbmap"; //-rules " + m_rule + setxkbmap += " -model " + lookupLocalized(m_rules->models(), widget->comboModel->currentText()) + + " -layout "; + setxkbmap += kbdLayout; + if( widget->chkLatin->isChecked() ) + setxkbmap += ",us"; + +/* LayoutUnit layoutUnitKey = getLayoutUnitKey(sel); + layoutDisplayName = m_kxkbConfig.getLayoutDisplayName( *m_kxkbConfig.m_layouts.find(layoutUnitKey) );*/ + layoutDisplayName = sel->text(LAYOUT_COLUMN_DISPLAY_NAME); + if( layoutDisplayName.isEmpty() ) { + int count = 0; + QListViewItem *item = widget->listLayoutsDst->firstChild(); + while (item) { + QString layout_ = item->text(LAYOUT_COLUMN_MAP); + if( layout_ == kbdLayout ) + ++count; + item = item->nextSibling(); + } + bool single = count < 2; + layoutDisplayName = m_kxkbConfig.getDefaultDisplayName(LayoutUnit(kbdLayout, variant), single); + } + kdDebug() << "disp: '" << layoutDisplayName << "'" << endl; + + if( !variant.isEmpty() ) { + setxkbmap += " -variant "; + if( widget->chkLatin->isChecked() ) + setxkbmap += ","; + setxkbmap += variant; + } + } + + widget->editCmdLine->setText(setxkbmap); + + widget->editDisplayName->setEnabled( sel != NULL ); + widget->editDisplayName->setText(layoutDisplayName); +} + +void LayoutConfig::changed() +{ + updateLayoutCommand(); + emit KCModule::changed( true ); +} + + +void LayoutConfig::loadRules() +{ + // do we need this ? + // this could obly be used if rules are changed and 'Defaults' is pressed + delete m_rules; + m_rules = new XkbRules(); + + QStringList modelsList; + QDictIterator<char> it(m_rules->models()); + while (it.current()) { + modelsList.append(i18n(it.current())); + ++it; + } + modelsList.sort(); + + widget->comboModel->clear(); + widget->comboModel->insertStringList(modelsList); + widget->comboModel->setCurrentItem(0); + + // fill in the additional layouts + widget->listLayoutsSrc->clear(); + widget->listLayoutsDst->clear(); + QDictIterator<char> it2(m_rules->layouts()); + + while (it2.current()) + { + QString layout = it2.currentKey(); + QString layoutName = it2.current(); + QListViewItem *item = new QListViewItem(widget->listLayoutsSrc); + + item->setPixmap(LAYOUT_COLUMN_FLAG, LayoutIcon::getInstance().findPixmap(layout, true)); + item->setText(LAYOUT_COLUMN_NAME, i18n(layoutName.latin1())); + item->setText(LAYOUT_COLUMN_MAP, layout); + ++it2; + } + widget->listLayoutsSrc->setSorting(LAYOUT_COLUMN_NAME); // from Qt3 QListView sorts by language + + //TODO: reset options and xkb options +} + + +QString LayoutConfig::createOptionString() +{ + QString options; + for (QDictIterator<char> it(m_rules->options()); it.current(); ++it) + { + QString option(it.currentKey()); + + if (option.contains(':')) { + + QString optionKey = option.mid(0, option.find(':')); + OptionListItem *item = m_optionGroups[optionKey]; + + if( !item ) { + kdDebug() << "WARNING: skipping empty group for " << it.currentKey() + << endl; + continue; + } + + OptionListItem *child = item->findChildItem( option ); + + if ( child ) { + if ( child->state() == QCheckListItem::On ) { + QString selectedName = child->optionName(); + if ( !selectedName.isEmpty() && selectedName != "none" ) { + if (!options.isEmpty()) + options.append(','); + options.append(selectedName); + } + } + } + else + kdDebug() << "Empty option button for group " << it.currentKey() << endl; + } + } + return options; +} + + +void LayoutConfig::defaults() +{ + loadRules(); + m_kxkbConfig.setDefaults(); + + initUI(); + + emit KCModule::changed( true ); +} + + +OptionListItem::OptionListItem( OptionListItem *parent, const QString &text, + Type tt, const QString &optionName ) + : QCheckListItem( parent, text, tt ), m_OptionName( optionName ) +{ +} + +OptionListItem::OptionListItem( QListView *parent, const QString &text, + Type tt, const QString &optionName ) + : QCheckListItem( parent, text, tt ), m_OptionName( optionName ) +{ +} + +OptionListItem * OptionListItem::findChildItem( const QString& optionName ) +{ + OptionListItem *child = static_cast<OptionListItem *>( firstChild() ); + + while ( child ) + { + if ( child->optionName() == optionName ) + break; + child = static_cast<OptionListItem *>( child->nextSibling() ); + } + + return child; +} + + +extern "C" +{ + KDE_EXPORT KCModule *create_keyboard_layout(QWidget *parent, const char *) + { + return new LayoutConfig(parent, "kcmlayout"); + } + + KDE_EXPORT KCModule *create_keyboard(QWidget *parent, const char *) + { + return new KeyboardConfig(parent, "kcmlayout"); + } + + KDE_EXPORT void init_keyboard() + { + KeyboardConfig::init_keyboard(); + + KxkbConfig m_kxkbConfig; + m_kxkbConfig.load(KxkbConfig::LOAD_INIT_OPTIONS); + + if( m_kxkbConfig.m_useKxkb == true ) { + kapp->startServiceByDesktopName("kxkb"); + } + else { + // Even if the layouts have been disabled we still want to set Xkb options + // user can always switch them off now in the "Options" tab + if( m_kxkbConfig.m_enableXkbOptions ) { + if( !XKBExtension::setXkbOptions(m_kxkbConfig.m_options, m_kxkbConfig.m_resetOldOptions) ) { + kdDebug() << "Setting XKB options failed!" << endl; + } + } + } + } +} + + + +#if 0// do not remove! +// please don't change/fix messages below +// they're taken from XFree86 as is and should stay the same + I18N_NOOP("Brazilian ABNT2"); + I18N_NOOP("Dell 101-key PC"); + I18N_NOOP("Everex STEPnote"); + I18N_NOOP("Generic 101-key PC"); + I18N_NOOP("Generic 102-key (Intl) PC"); + I18N_NOOP("Generic 104-key PC"); + I18N_NOOP("Generic 105-key (Intl) PC"); + I18N_NOOP("Japanese 106-key"); + I18N_NOOP("Microsoft Natural"); + I18N_NOOP("Northgate OmniKey 101"); + I18N_NOOP("Keytronic FlexPro"); + I18N_NOOP("Winbook Model XP5"); + +// These options are from XFree 4.1.0 + I18N_NOOP("Group Shift/Lock behavior"); + I18N_NOOP("R-Alt switches group while pressed"); + I18N_NOOP("Right Alt key changes group"); + I18N_NOOP("Caps Lock key changes group"); + I18N_NOOP("Menu key changes group"); + I18N_NOOP("Both Shift keys together change group"); + I18N_NOOP("Control+Shift changes group"); + I18N_NOOP("Alt+Control changes group"); + I18N_NOOP("Alt+Shift changes group"); + I18N_NOOP("Control Key Position"); + I18N_NOOP("Make CapsLock an additional Control"); + I18N_NOOP("Swap Control and Caps Lock"); + I18N_NOOP("Control key at left of 'A'"); + I18N_NOOP("Control key at bottom left"); + I18N_NOOP("Use keyboard LED to show alternative group"); + I18N_NOOP("Num_Lock LED shows alternative group"); + I18N_NOOP("Caps_Lock LED shows alternative group"); + I18N_NOOP("Scroll_Lock LED shows alternative group"); + +//these seem to be new in XFree86 4.2.0 + I18N_NOOP("Left Win-key switches group while pressed"); + I18N_NOOP("Right Win-key switches group while pressed"); + I18N_NOOP("Both Win-keys switch group while pressed"); + I18N_NOOP("Left Win-key changes group"); + I18N_NOOP("Right Win-key changes group"); + I18N_NOOP("Third level choosers"); + I18N_NOOP("Press Right Control to choose 3rd level"); + I18N_NOOP("Press Menu key to choose 3rd level"); + I18N_NOOP("Press any of Win-keys to choose 3rd level"); + I18N_NOOP("Press Left Win-key to choose 3rd level"); + I18N_NOOP("Press Right Win-key to choose 3rd level"); + I18N_NOOP("CapsLock key behavior"); + I18N_NOOP("uses internal capitalization. Shift cancels Caps."); + I18N_NOOP("uses internal capitalization. Shift doesn't cancel Caps."); + I18N_NOOP("acts as Shift with locking. Shift cancels Caps."); + I18N_NOOP("acts as Shift with locking. Shift doesn't cancel Caps."); + I18N_NOOP("Alt/Win key behavior"); + I18N_NOOP("Add the standard behavior to Menu key."); + I18N_NOOP("Alt and Meta on the Alt keys (default)."); + I18N_NOOP("Meta is mapped to the Win-keys."); + I18N_NOOP("Meta is mapped to the left Win-key."); + I18N_NOOP("Super is mapped to the Win-keys (default)."); + I18N_NOOP("Hyper is mapped to the Win-keys."); + I18N_NOOP("Right Alt is Compose"); + I18N_NOOP("Right Win-key is Compose"); + I18N_NOOP("Menu is Compose"); + +//these seem to be new in XFree86 4.3.0 + I18N_NOOP( "Both Ctrl keys together change group" ); + I18N_NOOP( "Both Alt keys together change group" ); + I18N_NOOP( "Left Shift key changes group" ); + I18N_NOOP( "Right Shift key changes group" ); + I18N_NOOP( "Right Ctrl key changes group" ); + I18N_NOOP( "Left Alt key changes group" ); + I18N_NOOP( "Left Ctrl key changes group" ); + I18N_NOOP( "Compose Key" ); + +//these seem to be new in XFree86 4.4.0 + I18N_NOOP("Shift with numpad keys works as in MS Windows."); + I18N_NOOP("Special keys (Ctrl+Alt+<key>) handled in a server."); + I18N_NOOP("Miscellaneous compatibility options"); + I18N_NOOP("Right Control key works as Right Alt"); + +//these seem to be in x.org and Debian XFree86 4.3 + I18N_NOOP("Right Alt key switches group while pressed"); + I18N_NOOP("Left Alt key switches group while pressed"); + I18N_NOOP("Press Right Alt-key to choose 3rd level"); + +//new in Xorg 6.9 + I18N_NOOP("R-Alt switches group while pressed."); + I18N_NOOP("Left Alt key switches group while pressed."); + I18N_NOOP("Left Win-key switches group while pressed."); + I18N_NOOP("Right Win-key switches group while pressed."); + I18N_NOOP("Both Win-keys switch group while pressed."); + I18N_NOOP("Right Ctrl key switches group while pressed."); + I18N_NOOP("Right Alt key changes group."); + I18N_NOOP("Left Alt key changes group."); + I18N_NOOP("CapsLock key changes group."); + I18N_NOOP("Shift+CapsLock changes group."); + I18N_NOOP("Both Shift keys together change group."); + I18N_NOOP("Both Alt keys together change group."); + I18N_NOOP("Both Ctrl keys together change group."); + I18N_NOOP("Ctrl+Shift changes group."); + I18N_NOOP("Alt+Ctrl changes group."); + I18N_NOOP("Alt+Shift changes group."); + I18N_NOOP("Menu key changes group."); + I18N_NOOP("Left Win-key changes group."); + I18N_NOOP("Right Win-key changes group."); + I18N_NOOP("Left Shift key changes group."); + I18N_NOOP("Right Shift key changes group."); + I18N_NOOP("Left Ctrl key changes group."); + I18N_NOOP("Right Ctrl key changes group."); + I18N_NOOP("Press Right Ctrl to choose 3rd level."); + I18N_NOOP("Press Menu key to choose 3rd level."); + I18N_NOOP("Press any of Win-keys to choose 3rd level."); + I18N_NOOP("Press Left Win-key to choose 3rd level."); + I18N_NOOP("Press Right Win-key to choose 3rd level."); + I18N_NOOP("Press any of Alt keys to choose 3rd level."); + I18N_NOOP("Press Left Alt key to choose 3rd level."); + I18N_NOOP("Press Right Alt key to choose 3rd level."); + I18N_NOOP("Ctrl key position"); + I18N_NOOP("Make CapsLock an additional Ctrl."); + I18N_NOOP("Swap Ctrl and CapsLock."); + I18N_NOOP("Ctrl key at left of 'A'"); + I18N_NOOP("Ctrl key at bottom left"); + I18N_NOOP("Right Ctrl key works as Right Alt."); + I18N_NOOP("Use keyboard LED to show alternative group."); + I18N_NOOP("NumLock LED shows alternative group."); + I18N_NOOP("CapsLock LED shows alternative group."); + I18N_NOOP("ScrollLock LED shows alternative group."); + I18N_NOOP("CapsLock uses internal capitalization. Shift cancels CapsLock."); + I18N_NOOP("CapsLock uses internal capitalization. Shift doesn't cancel CapsLock."); + I18N_NOOP("CapsLock acts as Shift with locking. Shift cancels CapsLock."); + I18N_NOOP("CapsLock acts as Shift with locking. Shift doesn't cancel CapsLock."); + I18N_NOOP("CapsLock just locks the Shift modifier."); + I18N_NOOP("CapsLock toggles normal capitalization of alphabetic characters."); + I18N_NOOP("CapsLock toggles Shift so all keys are affected."); + I18N_NOOP("Alt and Meta are on the Alt keys (default)."); + I18N_NOOP("Alt is mapped to the right Win-key and Super to Menu."); + I18N_NOOP("Compose key position"); + I18N_NOOP("Right Alt is Compose."); + I18N_NOOP("Right Win-key is Compose."); + I18N_NOOP("Menu is Compose."); + I18N_NOOP("Right Ctrl is Compose."); + I18N_NOOP("Caps Lock is Compose."); + I18N_NOOP("Special keys (Ctrl+Alt+<key>) handled in a server."); + I18N_NOOP("Adding the EuroSign to certain keys"); + I18N_NOOP("Add the EuroSign to the E key."); + I18N_NOOP("Add the EuroSign to the 5 key."); + I18N_NOOP("Add the EuroSign to the 2 key."); +#endif diff --git a/kxkb/kcmlayout.h b/kxkb/kcmlayout.h new file mode 100644 index 000000000..76c8b6d55 --- /dev/null +++ b/kxkb/kcmlayout.h @@ -0,0 +1,62 @@ +#ifndef __KCM_LAYOUT_H__ +#define __KCM_LAYOUT_H__ + + +#include <kcmodule.h> + +#include <qstring.h> +#include <qlistview.h> + +#include "kxkbconfig.h" + + +class OptionListItem; +class LayoutConfigWidget; +class XkbRules; + +class LayoutConfig : public KCModule +{ + Q_OBJECT + +public: + LayoutConfig(QWidget *parent = 0L, const char *name = 0L); + virtual ~LayoutConfig(); + + void load(); + void save(); + void defaults(); + void initUI(); + +protected: + QString createOptionString(); + void updateIndicator(QListViewItem* selLayout); + +protected slots: + void moveUp(); + void moveDown(); + void variantChanged(); + void displayNameChanged(const QString& name); + void latinChanged(); + void layoutSelChanged(QListViewItem *); + void loadRules(); + void updateLayoutCommand(); + void updateOptionsCommand(); + void add(); + void remove(); + + void changed(); + +private: + LayoutConfigWidget* widget; + + XkbRules *m_rules; + KxkbConfig m_kxkbConfig; + QDict<OptionListItem> m_optionGroups; + + QWidget* makeOptionsTab(); + void updateStickyLimit(); + static LayoutUnit getLayoutUnitKey(QListViewItem *sel); +}; + + +#endif diff --git a/kxkb/kcmlayoutwidget.ui b/kxkb/kcmlayoutwidget.ui new file mode 100644 index 000000000..82c866009 --- /dev/null +++ b/kxkb/kcmlayoutwidget.ui @@ -0,0 +1,715 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>LayoutConfigWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>LayoutConfigWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>709</width> + <height>563</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>7</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>600</width> + <height>510</height> + </size> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QTabWidget" row="0" column="0"> + <property name="name"> + <cstring>tabWidget</cstring> + </property> + <property name="minimumSize"> + <size> + <width>600</width> + <height>500</height> + </size> + </property> + <widget class="QWidget"> + <property name="name"> + <cstring>tabLayout</cstring> + </property> + <attribute name="title"> + <string>Layout</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0"> + <property name="name"> + <cstring>chkEnable</cstring> + </property> + <property name="text"> + <string>&Enable keyboard layouts</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QGroupBox" row="1" column="0"> + <property name="name"> + <cstring>grpLayouts</cstring> + </property> + <property name="title"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string><h1>Keyboard Layout</h1> Here you can choose your keyboard layout and model. The 'model' refers to the type of keyboard that is connected to your computer, while the keyboard layout defines "which key does what" and may be different for different countries.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="2" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Available layouts:</string> + </property> + </widget> + <widget class="QLabel" row="2" column="2" rowspan="1" colspan="3"> + <property name="name"> + <cstring>textLabel1_4_2</cstring> + </property> + <property name="text"> + <string>Active layouts:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="2" rowspan="1" colspan="3"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Keyboard &model:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>comboModel</cstring> + </property> + </widget> + <widget class="QComboBox" row="1" column="2" rowspan="1" colspan="3"> + <property name="name"> + <cstring>comboModel</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="whatsThis" stdset="0"> + <string>Here you can choose a keyboard model. This setting is independent of your keyboard layout and refers to the "hardware" model, i.e. the way your keyboard is manufactured. Modern keyboards that come with your computer usually have two extra keys and are referred to as "104-key" models, which is probably what you want if you do not know what kind of keyboard you have. +</string> + </property> + </widget> + <widget class="QListView" row="3" column="2" rowspan="1" colspan="3"> + <column> + <property name="text"> + <string>1</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Layout</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Keymap</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Variant</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>5</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Label</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <property name="name"> + <cstring>listLayoutsDst</cstring> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>If more than one layout is present in this list, the KDE panel will offer a docked flag. By clicking on this flag you can easily switch between layouts. The first layout will be default one.</string> + </property> + </widget> + <widget class="QLayoutWidget" row="4" column="2" rowspan="1" colspan="3"> + <property name="name"> + <cstring>layout10</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnAdd</cstring> + </property> + <property name="text"> + <string>Add >></string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnRemove</cstring> + </property> + <property name="text"> + <string><< Remove</string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnUp</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + <widget class="QPushButton"> + <property name="name"> + <cstring>btnDown</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel" row="8" column="0"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>Command:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>editCmdLine</cstring> + </property> + </widget> + <widget class="QListView" row="3" column="0" rowspan="5" colspan="2"> + <column> + <property name="text"> + <string></string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>false</bool> + </property> + </column> + <column> + <property name="text"> + <string>Layout</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <column> + <property name="text"> + <string>Keymap</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>listLayoutsSrc</cstring> + </property> + <property name="minimumSize"> + <size> + <width>260</width> + <height>0</height> + </size> + </property> + <property name="allColumnsShowFocus"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>This is the list of available keyboard layouts in your system. You can add layout to the active list by selecting it and pressing "Add" button.</string> + </property> + </widget> + <widget class="QLineEdit" row="8" column="1" rowspan="1" colspan="4"> + <property name="name"> + <cstring>editCmdLine</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>This is the command which is executed when switching to the selected layout. It may help you if you want to debug layout switching, or if you want to switch layouts without the help of KDE.</string> + </property> + </widget> + <widget class="QCheckBox" row="7" column="2" rowspan="1" colspan="3"> + <property name="name"> + <cstring>chkLatin</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Include latin layout</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If after you switch to this layout some keyboard shortcuts based on latin keys do not work try to enable this option.</string> + </property> + </widget> + <widget class="QLabel" row="6" column="2"> + <property name="name"> + <cstring>textLabel1_6</cstring> + </property> + <property name="text"> + <string>Label:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>editDisplayName</cstring> + </property> + </widget> + <widget class="QLineEdit" row="6" column="3"> + <property name="name"> + <cstring>editDisplayName</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxLength"> + <number>3</number> + </property> + </widget> + <widget class="QLabel" row="5" column="2"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="text"> + <string>Layout variant:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>comboVariant</cstring> + </property> + </widget> + <widget class="QComboBox" row="5" column="3"> + <property name="name"> + <cstring>comboVariant</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>Here you can choose a variant of selected keyboard layout. Layout variants usually represent different key maps for the same language. For example, Ukrainian layout might have four variants: basic, winkeys (as in Windows), typewriter (as in typewriters) and phonetic (each Ukrainian letter is placed on a transliterated latin one). +</string> + </property> + </widget> + <spacer row="5" column="4"> + <property name="name"> + <cstring>spacer3</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>210</width> + <height>20</height> + </size> + </property> + </spacer> + <spacer row="6" column="4"> + <property name="name"> + <cstring>spacer2</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>210</width> + <height>20</height> + </size> + </property> + </spacer> + </grid> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tab</cstring> + </property> + <attribute name="title"> + <string>Switching Options</string> + </attribute> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer1</cstring> + </property> + <property name="orientation"> + <enum>Vertical</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>20</width> + <height>40</height> + </size> + </property> + </spacer> + <widget class="QFrame" row="0" column="0"> + <property name="name"> + <cstring>optionsFrame</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QButtonGroup" row="2" column="0"> + <property name="name"> + <cstring>grpSwitching</cstring> + </property> + <property name="title"> + <string>Switching Policy</string> + </property> + <property name="exclusive"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>If you select "Application" or "Window" switching policy, changing the keyboard layout will only affect the current application or window.</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioButton1</cstring> + </property> + <property name="text"> + <string>&Global</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioButton1_3</cstring> + </property> + <property name="text"> + <string>Application</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>radioButton1_2</cstring> + </property> + <property name="text"> + <string>&Window</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>chkShowFlag</cstring> + </property> + <property name="text"> + <string>Show country flag</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>Shows country flag on background of layout name in tray icon</string> + </property> + </widget> + <widget class="QGroupBox" row="2" column="1"> + <property name="name"> + <cstring>grpBoxStickySwitching</cstring> + </property> + <property name="title"> + <string>Sticky Switching</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>chkEnableSticky</cstring> + </property> + <property name="text"> + <string>Enable sticky switching</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If you have more than two layouts and turn this option on, switching with the keyboard shortcut or clicking on the kxkb indicator will only cycle through the last few layouts. You can specify the number of layouts to rotate below. You can still access all layouts by right-clicking on the kxkb indicator.</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>textLabel1_5</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Number of layouts to rotate:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>spinBox1</cstring> + </property> + </widget> + <widget class="QSpinBox" row="1" column="1"> + <property name="name"> + <cstring>spinStickyDepth</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="maxValue"> + <number>10</number> + </property> + <property name="minValue"> + <number>2</number> + </property> + </widget> + </grid> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>chkShowSingle</cstring> + </property> + <property name="text"> + <string>Show indicator for single layout</string> + </property> + </widget> + </grid> + </widget> + </grid> + </widget> + <widget class="QWidget"> + <property name="name"> + <cstring>tabOptions</cstring> + </property> + <attribute name="title"> + <string>Xkb Options</string> + </attribute> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox"> + <property name="name"> + <cstring>chkEnableOptions</cstring> + </property> + <property name="text"> + <string>&Enable xkb options</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QGroupBox"> + <property name="name"> + <cstring>groupBox3_2</cstring> + </property> + <property name="title"> + <string>Xkb Options</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Here you can set xkb extension options instead of, or in addition to, specifying them in the X11 configuration file.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>checkResetOld</cstring> + </property> + <property name="text"> + <string>&Reset old options</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>textLabel1_3_2</cstring> + </property> + <property name="text"> + <string>Command:</string> + </property> + </widget> + <widget class="QLineEdit" row="2" column="1"> + <property name="name"> + <cstring>editCmdLineOpt</cstring> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QListView" row="1" column="0" rowspan="1" colspan="2"> + <column> + <property name="text"> + <string>Options</string> + </property> + <property name="clickable"> + <bool>false</bool> + </property> + <property name="resizable"> + <bool>true</bool> + </property> + </column> + <property name="name"> + <cstring>listOptions</cstring> + </property> + </widget> + </grid> + </widget> + </vbox> + </widget> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>chkEnable</sender> + <signal>toggled(bool)</signal> + <receiver>grpLayouts</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>chkEnableOptions</sender> + <signal>toggled(bool)</signal> + <receiver>groupBox3_2</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>chkEnableSticky</sender> + <signal>toggled(bool)</signal> + <receiver>spinStickyDepth</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>chkEnableSticky</sender> + <signal>toggled(bool)</signal> + <receiver>textLabel1_5</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>chkEnable</sender> + <signal>toggled(bool)</signal> + <receiver>optionsFrame</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">kiconloader.h</include> + <include location="local" impldecl="in implementation">kdialogbase.h</include> +</includes> +<pixmapfunction>SmallIcon</pixmapfunction> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/kxkb/kcmmisc.cpp b/kxkb/kcmmisc.cpp new file mode 100644 index 000000000..3453c4abb --- /dev/null +++ b/kxkb/kcmmisc.cpp @@ -0,0 +1,516 @@ +/* + * kcmmisc.cpp + * + * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * + * Layout management, cleanups: + * Copyright (c) 1999 Dirk A. Mueller <dmuell@gmx.net> + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * 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 <config.h> +#include <math.h> + +#include <qslider.h> +#include <qfileinfo.h> +#include <qcheckbox.h> +#include <qstring.h> +#include <qlayout.h> +#include <qwhatsthis.h> +#include <qvbuttongroup.h> +#include <qradiobutton.h> + +#include <klocale.h> +#include <kconfig.h> +#include <knuminput.h> +#include <kapplication.h> +#include <kglobal.h> +#include <kstandarddirs.h> +#include <kprocess.h> +#include <kdialog.h> + +#include "kcmmisc.h" +#include "kcmmiscwidget.h" +#include <X11/Xlib.h> + +KeyboardConfig::KeyboardConfig (QWidget * parent, const char *) + : KCModule (parent, "kcmlayout") +{ + QString wtstr; + QBoxLayout* lay = new QVBoxLayout(this, 0, KDialog::spacingHint()); + ui = new KeyboardConfigWidget(this, "ui"); + lay->addWidget(ui); + lay->addStretch(); + + ui->click->setRange(0, 100, 10); + ui->delay->setRange(100, 5000, 50, false); + ui->rate->setRange(0.2, 50, 5, false); + + sliderMax = (int)floor (0.5 + + 2*(log(5000)-log(100)) / (log(5000)-log(4999))); + ui->delaySlider->setRange(0, sliderMax); + ui->delaySlider->setSteps(sliderMax/100, sliderMax/10); + ui->delaySlider->setTickInterval(sliderMax/10); + + ui->rateSlider->setRange(20, 5000); + ui->rateSlider->setSteps(30, 500); + ui->rateSlider->setTickInterval(498); + + connect(ui->repeatBox, SIGNAL(clicked()), this, SLOT(changed())); + connect(ui->delay, SIGNAL(valueChanged(int)), this, SLOT(delaySpinboxChanged(int))); + connect(ui->delaySlider, SIGNAL(valueChanged(int)), this, SLOT(delaySliderChanged(int))); + connect(ui->rate, SIGNAL(valueChanged(double)), this, SLOT(rateSpinboxChanged(double))); + connect(ui->rateSlider, SIGNAL(valueChanged(int)), this, SLOT(rateSliderChanged(int))); + + connect(ui->click, SIGNAL(valueChanged(int)), this, SLOT(changed())); + connect(ui->numlockGroup, SIGNAL(released(int)), this, SLOT(changed())); + +#if !defined(HAVE_XTEST) && !defined(HAVE_XKB) + ui->numlockGroup->setDisabled( true ); +#endif +#if !defined(HAVE_XKB) && !defined(HAVE_XF86MISC) +// delay->setDisabled( true ); +// rate->setDisabled( true ); +#endif +// lay->addStretch(); + load(); +} + +int KeyboardConfig::getClick() +{ + return ui->click->value(); +} + +// set the slider and LCD values +void KeyboardConfig::setRepeat(int r, int delay_, double rate_) +{ + ui->repeatBox->setChecked(r == AutoRepeatModeOn); + ui->delay->setValue(delay_); + ui->rate->setValue(rate_); +} + +void KeyboardConfig::setClick(int v) +{ + ui->click->setValue(v); +} + +int KeyboardConfig::getNumLockState() +{ + QButton* selected = ui->numlockGroup->selected(); + if( selected == NULL ) + return 2; + int ret = ui->numlockGroup->id( selected ); + if( ret == -1 ) + ret = 2; + return ret; +} + +void KeyboardConfig::setNumLockState( int s ) +{ + ui->numlockGroup->setButton( s ); +} + +void KeyboardConfig::load() +{ + KConfig config("kcminputrc"); + + XKeyboardState kbd; + + XGetKeyboardControl(kapp->getDisplay(), &kbd); + + config.setGroup("Keyboard"); + bool key = config.readBoolEntry("KeyboardRepeating", true); + keyboardRepeat = (key ? AutoRepeatModeOn : AutoRepeatModeOff); + ui->delay->setValue(config.readNumEntry( "RepeatDelay", 660 )); + ui->rate->setValue(config.readDoubleNumEntry( "RepeatRate", 25 )); + clickVolume = config.readNumEntry("ClickVolume", kbd.key_click_percent); + numlockState = config.readNumEntry( "NumLock", 2 ); + + setClick(kbd.key_click_percent); + setRepeat(kbd.global_auto_repeat, ui->delay->value(), ui->rate->value()); + setNumLockState( numlockState ); +} + +void KeyboardConfig::save() +{ + KConfig config("kcminputrc"); + + XKeyboardControl kbd; + + clickVolume = getClick(); + keyboardRepeat = ui->repeatBox->isChecked() ? AutoRepeatModeOn : AutoRepeatModeOff; + numlockState = getNumLockState(); + + kbd.key_click_percent = clickVolume; + kbd.auto_repeat_mode = keyboardRepeat; + XChangeKeyboardControl(kapp->getDisplay(), + KBKeyClickPercent | KBAutoRepeatMode, + &kbd); + if( keyboardRepeat ) { + set_repeatrate(ui->delay->value(), ui->rate->value()); + } + + config.setGroup("Keyboard"); + config.writeEntry("ClickVolume",clickVolume); + config.writeEntry("KeyboardRepeating", (keyboardRepeat == AutoRepeatModeOn)); + config.writeEntry("RepeatRate", ui->rate->value() ); + config.writeEntry("RepeatDelay", ui->delay->value() ); + config.writeEntry("NumLock", numlockState ); + config.sync(); +} + +void KeyboardConfig::defaults() +{ + setClick(50); + setRepeat(true, 660, 25); + setNumLockState( 2 ); +} + +QString KeyboardConfig::quickHelp() const +{ + return QString::null; + + /* "<h1>Keyboard</h1> This module allows you to choose options" + " for the way in which your keyboard works. The actual effect of" + " setting these options depends upon the features provided by your" + " keyboard hardware and the X server on which KDE is running.<p>" + " For example, you may find that changing the key click volume" + " has no effect because this feature is not available on your system." */ +} + +void KeyboardConfig::delaySliderChanged (int value) { + double alpha = sliderMax / (log(5000) - log(100)); + double linearValue = exp (value/alpha + log(100)); + + ui->delay->setValue((int)floor(0.5 + linearValue)); + + emit KCModule::changed(true); +} + +void KeyboardConfig::delaySpinboxChanged (int value) { + double alpha = sliderMax / (log(5000) - log(100)); + double logVal = alpha * (log(value)-log(100)); + + ui->delaySlider->setValue ((int)floor (0.5 + logVal)); + + emit KCModule::changed(true); +} + +void KeyboardConfig::rateSliderChanged (int value) { + ui->rate->setValue(value/100.0); + + emit KCModule::changed(true); +} + +void KeyboardConfig::rateSpinboxChanged (double value) { + ui->rateSlider->setValue ((int)(value*100)); + + emit KCModule::changed(true); +} + +void KeyboardConfig::changed() +{ + emit KCModule::changed(true); +} + +/* + Originally comes from NumLockX http://dforce.sh.cvut.cz/~seli/en/numlockx + + NumLockX + + Copyright (C) 2000-2001 Lubos Lunak <l.lunak@kde.org> + Copyright (C) 2001 Oswald Buddenhagen <ossi@kde.org> + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + +****************************************************************************/ + +#include <X11/Xlib.h> + +#ifdef HAVE_XTEST +#include <X11/extensions/XTest.h> +#endif + +#ifdef HAVE_XKB +#define explicit myexplicit +#include <X11/XKBlib.h> +#undef explicit +#endif + +#include <X11/keysym.h> + +#if defined(HAVE_XTEST) || defined(HAVE_XKB) + +/* the XKB stuff is based on code created by Oswald Buddenhagen <ossi@kde.org> */ +#ifdef HAVE_XKB +int xkb_init() + { + int xkb_opcode, xkb_event, xkb_error; + int xkb_lmaj = XkbMajorVersion; + int xkb_lmin = XkbMinorVersion; + return XkbLibraryVersion( &xkb_lmaj, &xkb_lmin ) + && XkbQueryExtension( qt_xdisplay(), &xkb_opcode, &xkb_event, &xkb_error, + &xkb_lmaj, &xkb_lmin ); + } + +unsigned int xkb_mask_modifier( XkbDescPtr xkb, const char *name ) + { + int i; + if( !xkb || !xkb->names ) + return 0; + for( i = 0; + i < XkbNumVirtualMods; + i++ ) + { + char* modStr = XGetAtomName( xkb->dpy, xkb->names->vmods[i] ); + if( modStr != NULL && strcmp(name, modStr) == 0 ) + { + unsigned int mask; + XkbVirtualModsToReal( xkb, 1 << i, &mask ); + return mask; + } + } + return 0; + } + +unsigned int xkb_numlock_mask() + { + XkbDescPtr xkb; + if(( xkb = XkbGetKeyboard( qt_xdisplay(), XkbAllComponentsMask, XkbUseCoreKbd )) != NULL ) + { + unsigned int mask = xkb_mask_modifier( xkb, "NumLock" ); + XkbFreeKeyboard( xkb, 0, True ); + return mask; + } + return 0; + } + +int xkb_set_on() + { + unsigned int mask; + if( !xkb_init()) + return 0; + mask = xkb_numlock_mask(); + if( mask == 0 ) + return 0; + XkbLockModifiers ( qt_xdisplay(), XkbUseCoreKbd, mask, mask); + return 1; + } + +int xkb_set_off() + { + unsigned int mask; + if( !xkb_init()) + return 0; + mask = xkb_numlock_mask(); + if( mask == 0 ) + return 0; + XkbLockModifiers ( qt_xdisplay(), XkbUseCoreKbd, mask, 0); + return 1; + } +#endif + +#ifdef HAVE_XTEST +int xtest_get_numlock_state() + { + int i; + int numlock_mask = 0; + Window dummy1, dummy2; + int dummy3, dummy4, dummy5, dummy6; + unsigned int mask; + KeyCode numlock_keycode = XKeysymToKeycode( qt_xdisplay(), XK_Num_Lock ); + if( numlock_keycode == NoSymbol ) + return 0; + XModifierKeymap* map = XGetModifierMapping( qt_xdisplay() ); + for( i = 0; + i < 8; + ++i ) + { + if( map->modifiermap[ map->max_keypermod * i ] == numlock_keycode ) + numlock_mask = 1 << i; + } + XQueryPointer( qt_xdisplay(), DefaultRootWindow( qt_xdisplay() ), &dummy1, &dummy2, + &dummy3, &dummy4, &dummy5, &dummy6, &mask ); + XFreeModifiermap( map ); + return mask & numlock_mask; + } + +void xtest_change_numlock() + { + XTestFakeKeyEvent( qt_xdisplay(), XKeysymToKeycode( qt_xdisplay(), XK_Num_Lock ), True, CurrentTime ); + XTestFakeKeyEvent( qt_xdisplay(), XKeysymToKeycode( qt_xdisplay(), XK_Num_Lock ), False, CurrentTime ); + } + +void xtest_set_on() + { + if( !xtest_get_numlock_state()) + xtest_change_numlock(); + } + +void xtest_set_off() + { + if( xtest_get_numlock_state()) + xtest_change_numlock(); + } +#endif + +void numlock_set_on() + { +#ifdef HAVE_XKB + if( xkb_set_on()) + return; +#endif +#ifdef HAVE_XTEST + xtest_set_on(); +#endif + } + +void numlock_set_off() + { +#ifdef HAVE_XKB + if( xkb_set_off()) + return; +#endif +#ifdef HAVE_XTEST + xtest_set_off(); +#endif + } + +void numlockx_change_numlock_state( bool set_P ) + { + if( set_P ) + numlock_set_on(); + else + numlock_set_off(); + } +#else +void numlockx_change_numlock_state( bool ) {} // dummy +#endif // defined(HAVE_XTEST) || defined(HAVE_XKB) + + +// This code is taken from xset utility from XFree 4.3 (http://www.xfree86.org/) + + +#if 0 +//HAVE_XF86MISC +#include <X11/extensions/xf86misc.h> +void set_repeatrate(int delay, double rate) +{ + Display* dpy = qt_xdisplay(); + XF86MiscKbdSettings values; + + XF86MiscGetKbdSettings(dpy, &values); + values.delay = delay; + values.rate = rate; + XF86MiscSetKbdSettings(dpy, &values); + return; +} +#else +void set_repeatrate(int delay, double rate) +{ +#if HAVE_XKB + Display* dpy = qt_xdisplay(); + int xkbmajor = XkbMajorVersion, xkbminor = XkbMinorVersion; + int xkbopcode, xkbevent, xkberror; + + if (XkbQueryExtension(dpy, &xkbopcode, &xkbevent, &xkberror, &xkbmajor, + &xkbminor)) { + XkbDescPtr xkb = XkbAllocKeyboard(); + if (xkb) { + int res = XkbGetControls(dpy, XkbRepeatKeysMask, xkb); + xkb->ctrls->repeat_delay = delay; + xkb->ctrls->repeat_interval = (int)floor(1000/rate + 0.5); + res = XkbSetControls(dpy, XkbRepeatKeysMask, xkb); + return; + } + } +#endif + // Fallback: use the xset utility. + + // Unfortunately xset does only support int parameters, so + // really slow repeat rates cannot be supported this way. + // (the FSG Accessibility standard requires support for repeat rates + // of several seconds per character) + int r; + if (rate < 1) + r = 1; + else + r = (int)floor(rate + 0.5); + + QString exe = KGlobal::dirs()->findExe("xset"); + if (exe.isEmpty()) + return; + + KProcess p; + p << exe << "r" << "rate" << QString::number(delay) << QString::number(r); + + p.start(KProcess::Block); +} +#endif + +void KeyboardConfig::init_keyboard() +{ + KConfig *config = new KConfig("kcminputrc", true); // Read-only, no globals + config->setGroup("Keyboard"); + + XKeyboardState kbd; + XKeyboardControl kbdc; + + XGetKeyboardControl(kapp->getDisplay(), &kbd); + bool key = config->readBoolEntry("KeyboardRepeating", true); + kbdc.key_click_percent = config->readNumEntry("ClickVolume", kbd.key_click_percent); + kbdc.auto_repeat_mode = (key ? AutoRepeatModeOn : AutoRepeatModeOff); + + XChangeKeyboardControl(kapp->getDisplay(), + KBKeyClickPercent | KBAutoRepeatMode, + &kbdc); + + if( key ) { + int delay_ = config->readNumEntry("RepeatDelay", 250); + double rate_ = config->readDoubleNumEntry("RepeatRate", 30); + set_repeatrate(delay_, rate_); + } + + + int numlockState = config->readNumEntry( "NumLock", 2 ); + if( numlockState != 2 ) + numlockx_change_numlock_state( numlockState == 0 ); + + delete config; +} + +#include "kcmmisc.moc" + diff --git a/kxkb/kcmmisc.h b/kxkb/kcmmisc.h new file mode 100644 index 000000000..02829cd20 --- /dev/null +++ b/kxkb/kcmmisc.h @@ -0,0 +1,76 @@ +/* + * keyboard.h + * + * Copyright (c) 1997 Patrick Dowler dowler@morgul.fsh.uvic.ca + * + * Requires the Qt widget libraries, available at no cost at + * http://www.troll.no/ + * + * 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 __KCMMISC_H__ +#define __KCMMISC_H__ + + +#include <kapplication.h> +#include <kcmodule.h> + +class KeyboardConfigWidget; + +class KeyboardConfig : public KCModule +{ + Q_OBJECT +public: + KeyboardConfig( QWidget *parent=0, const char* name=0); + + void save(); + void load(); + void defaults(); + + QString quickHelp() const; + + static void init_keyboard(); + +private slots: + void changed(); + + void delaySliderChanged (int value); + void delaySpinboxChanged (int value); + void rateSliderChanged (int value); + void rateSpinboxChanged (double value); + +private: + + void setClick( int ); + void setRepeat( int flag, int delay, double rate); + void setRepeatRate( int ); + void setNumLockState( int ); + + int getClick(); + int getRepeatRate(); + int getNumLockState(); + + int sliderMax; + int clickVolume, keyboardRepeat; + int numlockState; // 0 = on, 1 = off, 2 = don't change + KeyboardConfigWidget* ui; +}; + +void numlockx_change_numlock_state( bool set_P ); +void set_repeatrate(int delay, double rate); + +#endif + diff --git a/kxkb/kcmmiscwidget.ui b/kxkb/kcmmiscwidget.ui new file mode 100644 index 000000000..479188293 --- /dev/null +++ b/kxkb/kcmmiscwidget.ui @@ -0,0 +1,344 @@ +<!DOCTYPE UI><UI version="3.1" stdsetdef="1"> +<class>KeyboardConfigWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>KeyboardConfigWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>599</width> + <height>284</height> + </rect> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QButtonGroup" row="1" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>numlockGroup</cstring> + </property> + <property name="frameShape"> + <enum>GroupBoxPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <property name="title"> + <string>NumLock on KDE Startup</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If supported, this option allows you to setup the state of NumLock after KDE startup.<p>You can configure NumLock to be turned on or off, or configure KDE not to set NumLock state.</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QRadioButton" row="1" column="0"> + <property name="name"> + <cstring>RadioButton1_2</cstring> + </property> + <property name="text"> + <string>Turn o&ff</string> + </property> + <property name="buttonGroupId"> + <number>1</number> + </property> + </widget> + <widget class="QRadioButton" row="2" column="0"> + <property name="name"> + <cstring>RadioButton1_3</cstring> + </property> + <property name="text"> + <string>Leave unchan&ged</string> + </property> + <property name="buttonGroupId"> + <number>2</number> + </property> + </widget> + <widget class="QRadioButton" row="0" column="0"> + <property name="name"> + <cstring>RadioButton1</cstring> + </property> + <property name="text"> + <string>T&urn on</string> + </property> + <property name="buttonGroupId"> + <number>0</number> + </property> + </widget> + </grid> + </widget> + <widget class="QGroupBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>groupBox1</cstring> + </property> + <property name="title"> + <string>Keyboard Repeat</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>lblDelay</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Delay:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>delay</cstring> + </property> + </widget> + <widget class="QSlider" row="1" column="1"> + <property name="name"> + <cstring>delaySlider</cstring> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="maxValue"> + <number>10000</number> + </property> + <property name="lineStep"> + <number>1000</number> + </property> + <property name="pageStep"> + <number>1000</number> + </property> + <property name="value"> + <number>5000</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Below</enum> + </property> + <property name="tickInterval"> + <number>1000</number> + </property> + <property name="whatsThis" stdset="0"> + <string>If supported, this option allows you to set the delay after which a pressed key will start generating keycodes. The 'Repeat rate' option controls the frequency of these keycodes.</string> + </property> + </widget> + <widget class="KIntNumInput" row="1" column="2" rowspan="1" colspan="1"> + <property name="name"> + <cstring>delay</cstring> + </property> + <property name="value"> + <number>250</number> + </property> + <property name="minValue"> + <number>100</number> + </property> + <property name="maxValue"> + <number>5000</number> + </property> + <property name="suffix"> + <string> msec</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If supported, this option allows you to set the delay after which a pressed key will start generating keycodes. The 'Repeat rate' option controls the frequency of these keycodes.</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>lblRate</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>&Rate:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>rate</cstring> + </property> + </widget> + <widget class="QSlider" row="2" column="1" rowspan="1" colspan="1"> + <property name="name"> + <cstring>rateSlider</cstring> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="maxValue"> + <number>10000</number> + </property> + <property name="lineStep"> + <number>1000</number> + </property> + <property name="pageStep"> + <number>1000</number> + </property> + <property name="value"> + <number>5000</number> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="tickmarks"> + <enum>Below</enum> + </property> + <property name="tickInterval"> + <number>1000</number> + </property> + <property name="whatsThis" stdset="0"> + <string>If supported, this option allows you to set the rate at which keycodes are generated while a key is pressed.</string> + </property> + </widget> + <widget class="KDoubleNumInput" row="2" column="2" rowspan="1" colspan="1"> + <property name="name"> + <cstring>rate</cstring> + </property> + <property name="value"> + <number>30</number> + </property> + <property name="minValue"> + <number>0.2</number> + </property> + <property name="precision"> + <number>1</number> + </property> + <property name="maxValue"> + <number>50.0</number> + </property> + <property name="suffix"> + <string>/s</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If supported, this option allows you to set the rate at which keycodes are generated while a key is pressed.</string> + </property> + </widget> + <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>repeatBox</cstring> + </property> + <property name="text"> + <string>&Enable keyboard repeat</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="whatsThis" stdset="0"> + <string>If you check this option, pressing and holding down a key emits the same character over and over again. For example, pressing and holding down the Tab key will have the same effect as that of pressing that key several times in succession: Tab characters continue to be emitted until you release the key.</string> + </property> + </widget> + </grid> + </widget> + <widget class="KIntNumInput" row="2" column="1"> + <property name="name"> + <cstring>click</cstring> + </property> + <property name="value"> + <number>0</number> + </property> + <property name="minValue"> + <number>0</number> + </property> + <property name="maxValue"> + <number>100</number> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="whatsThis" stdset="0"> + <string>If supported, this option allows you to hear audible clicks from your computer's speakers when you press the keys on your keyboard. This might be useful if your keyboard does not have mechanical keys, or if the sound that the keys make is very soft.<p>You can change the loudness of the key click feedback by dragging the slider button or by clicking the up/down arrows on the spin box. Setting the volume to 0% turns off the key click.</string> + </property> + </widget> + <widget class="QLabel" row="2" column="0"> + <property name="name"> + <cstring>TextLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Key click &volume:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>click</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>If supported, this option allows you to hear audible clicks from your computer's speakers when you press the keys on your keyboard. This might be useful if your keyboard does not have mechanical keys, or if the sound that the keys make is very soft.<p>You can change the loudness of the key click feedback by dragging the slider button or by clicking the up/down arrows on the spin box. Setting the volume to 0% turns off the key click.</string> + </property> + </widget> + </grid> +</widget> +<connections> + <connection> + <sender>repeatBox</sender> + <signal>toggled(bool)</signal> + <receiver>delaySlider</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>repeatBox</sender> + <signal>toggled(bool)</signal> + <receiver>delay</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>repeatBox</sender> + <signal>toggled(bool)</signal> + <receiver>rateSlider</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>repeatBox</sender> + <signal>toggled(bool)</signal> + <receiver>rate</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>repeatBox</sender> + <signal>toggled(bool)</signal> + <receiver>lblDelay</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>repeatBox</sender> + <signal>toggled(bool)</signal> + <receiver>lblRate</receiver> + <slot>setEnabled(bool)</slot> + </connection> +</connections> +<tabstops> + <tabstop>repeatBox</tabstop> + <tabstop>click</tabstop> + <tabstop>RadioButton1</tabstop> + <tabstop>RadioButton1_2</tabstop> + <tabstop>RadioButton1_3</tabstop> +</tabstops> +<includes> + <include location="global" impldecl="in implementation">kdialog.h</include> + <include location="global" impldecl="in implementation">knuminput.h</include> +</includes> +<layoutdefaults spacing="6" margin="11"/> +<layoutfunctions spacing="KDialog::spacingHint" margin="KDialog::marginHint"/> +</UI> diff --git a/kxkb/keyboard.desktop b/kxkb/keyboard.desktop new file mode 100644 index 000000000..893d92d36 --- /dev/null +++ b/kxkb/keyboard.desktop @@ -0,0 +1,243 @@ +[Desktop Entry] +Exec=kcmshell keyboard +Icon=keyboard +Type=Application +DocPath=kcontrol/keyboard/index.html + +X-KDE-ModuleType=Library +X-KDE-Library=keyboard +X-KDE-FactoryName=keyboard +X-KDE-Init=keyboard + +Name=Keyboard +Name[af]=Sleutelbord +Name[ar]=لوحة المفاتيح +Name[az]=Klaviatura +Name[be]=Клавіятура +Name[bg]=Клавиатура +Name[bn]=কীবোর্ড +Name[br]=Stokellaoueg +Name[bs]=Tastatura +Name[ca]=Teclat +Name[cs]=Klávesnice +Name[csb]=Klawiatura +Name[cy]=Bysellfwrdd +Name[da]=Tastatur +Name[de]=Tastatur +Name[el]=Πληκτρολόγιο +Name[eo]=Klavaro +Name[es]=Teclado +Name[et]=Klaviatuur +Name[eu]=Teklatua +Name[fa]=صفحه کلید +Name[fi]=Näppäimistö +Name[fo]=Knappaborð +Name[fr]=Clavier +Name[fy]=Toetseboerd +Name[ga]=Méarchlár +Name[gl]=Teclado +Name[he]=מקלדת +Name[hi]=कुंजीपट +Name[hr]=Tipkovnica +Name[hu]=Billentyűzet +Name[is]=Lyklaborð +Name[it]=Tastiera +Name[ja]=キーボード +Name[ka]=კლავიატურა +Name[kk]=Перенетақта +Name[km]=ក្ដារចុច +Name[ko]=키보드 +Name[lo]=ແປ້ນພິມ +Name[lt]=Klaviatūra +Name[lv]=Tastatūra +Name[mk]=Тастатура +Name[mn]=Гар +Name[mt]=Tastiera +Name[nb]=Tastatur +Name[nds]=Tastatuur +Name[ne]=कुञ्जीपाटी +Name[nl]=Toetsenbord +Name[nn]=Tastatur +Name[oc]=Teclat +Name[pa]=ਕੀਬੋਰਡ +Name[pl]=Klawiatura +Name[pt]=Teclado +Name[pt_BR]=Teclado +Name[ro]=Tastatură +Name[ru]=Клавиатура +Name[rw]=Mwandikisho +Name[se]=Boallobeavdi +Name[sk]=Klávesnica +Name[sl]=Tipkovnica +Name[sr]=Тастатура +Name[sr@Latn]=Tastatura +Name[ss]=Libhodi letinkhinobho +Name[sv]=Tangentbord +Name[ta]=விசைப்பலகை +Name[te]=కీబోర్డ్ +Name[tg]=Забонак +Name[th]=แป้นพิมพ์ +Name[tr]=Klavye +Name[tt]=Töylek +Name[uk]=Клавіатура +Name[uz]=Tugmatag +Name[uz@cyrillic]=Тугматаг +Name[ven]=Khiibodo +Name[vi]=Bàn phím +Name[wa]=Taprece +Name[xh]=Ibhodi enezitshixo +Name[zh_CN]=键盘 +Name[zh_TW]=鍵盤 +Name[zu]=Ibhodi lokhiye + +Comment=Keyboard settings +Comment[af]=Sleutelbord instellings +Comment[ar]=تعيينات لوحة المفاتيح +Comment[az]=Klaviatura qurğuları +Comment[be]=Настаўленні клавіятуры +Comment[bg]=Настройване на клавиатурата +Comment[bn]=কীবোর্ড সেটিংস +Comment[br]=Kefluniañ ar stokellaoueg +Comment[bs]=Podešavanje tastature +Comment[ca]=Arranjament del teclat +Comment[cs]=Nastavení klávesnice +Comment[csb]=Nastôw klawiaturë +Comment[cy]=Gosodiadau Bysellfwrdd +Comment[da]=Tastaturindstillinger +Comment[de]=Einstellung der Tastatur +Comment[el]=Ρυθμίσεις πληκτρολογίου +Comment[eo]=Agordo de la klavaro +Comment[es]=Preferencias del teclado +Comment[et]=Klaviatuuri seadistused +Comment[eu]=Teklatuaren konfigurazioa +Comment[fa]=تنظیمات صفحه کلید +Comment[fi]=Näppäimistön asetukset +Comment[fo]=Knappaborðsuppseting +Comment[fr]=Configuration du clavier +Comment[fy]=Toetseboerd ynstelle +Comment[ga]=Socruithe Méarchláir +Comment[gl]=Configuración do teclado +Comment[he]=שינוי הגדרות המקלדת +Comment[hi]=कुंजीपट विन्यास +Comment[hr]=Postavke tipkovnice +Comment[hu]=A billentyűzet beállításai +Comment[id]=Seting Keyboard +Comment[is]=Stillingar lyklaborðs +Comment[it]=Impostazioni della tastiera +Comment[ja]=キーボードの設定 +Comment[ka]=კლავიატურის კონფიგურაცია +Comment[kk]=Перенетақтаны баптау +Comment[km]=ការកំណត់ក្ដារចុច +Comment[ko]=키보드 설정 +Comment[lo]=ຕັ້ງຄ່າແປ້ນພິມ +Comment[lt]=Klaviatūros parametrai +Comment[lv]=Tastatūras parametri +Comment[mk]=Поставувања на тастатурата +Comment[mn]=Гарын тохируулга +Comment[mt]=Setings tat-tastiera +Comment[nb]=Tastaturinnstillinger +Comment[nds]=Tastatuurinstellen +Comment[ne]=कुञ्जीपाटी सेटिङ +Comment[nl]=Toetsenbord instellen +Comment[nn]=Tastaturinnstillingar +Comment[nso]=Dipeakanyo tsa Keyboard +Comment[oc]=Arranjament dèu teclat +Comment[pa]=ਕੀਬੋਰਡ ਸੈਟਿੰਗ +Comment[pl]=Ustawienia klawiatury +Comment[pt]=Configuração do teclado +Comment[pt_BR]=Configurações de teclado +Comment[ro]=Setări tastatură +Comment[ru]=Настройка клавиатуры +Comment[rw]=Amagenamiterere ya Mwandikisho +Comment[se]=Boallobeavdeheivehusat +Comment[sk]=Nastavenie klávesnice +Comment[sl]=Nastavitve tipkovnice +Comment[sr]=Поставке тастатуре +Comment[sr@Latn]=Postavke tastature +Comment[sv]=Anpassa tangentbordets inställningar +Comment[ta]=விசைப்பலகை அமைப்புகள் +Comment[te]=కీబోర్డ్ అమరికలు +Comment[tg]=Танзими забонак +Comment[th]=ตั้งค่าทั่วไปของแป้นพิมพ์ +Comment[tr]=Klavye ayarları +Comment[tt]=Töylek köyläw +Comment[uk]=Параметри клавіатури +Comment[uz]=Tugmatagning moslamalari +Comment[uz@cyrillic]=Тугматагнинг мосламалари +Comment[ven]=Mavhekanyele a khiibodo +Comment[vi]=Các thiết lập bàn phím +Comment[wa]=Apontiaedje del taprece +Comment[xh]=Izicwangciso zebhodi enezitshixo +Comment[zh_CN]=键盘设置 +Comment[zh_TW]=鍵盤設定 +Comment[zu]=Izilungiselelo zebhodi lokhiye + +Keywords=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume +Keywords[ar]=لوحة المفاتيح,تكرار لوحة المحاتيح,حجم صوت النقرة,أجهزة الإدخال,تكرار,علو الصوت +Keywords[be]=Клавіятура,Паўтор клавішаў,Гучнасць націску,Прылады ўводу,Паўтор,Гучнасць,Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume +Keywords[bg]=клавиатура, настройки, повторение, клавиши, задържане, Keyboard, Keyboard repeat, Click volume, Input Devices, repeat, volume +Keywords[ca]=Teclat,Repetició del teclat,Volum del clic,dispositius d'entrada,repetició,volum +Keywords[cs]=Klávesnice,Opakování klávesy,Hlasitost stisknutí,Vstupní zařízení,Opakování,Hlasitost +Keywords[csb]=Klawiatura,powtórzenié klawiaturë,głosnosc klëkniãcô,ùrządzenia wéńdzeniowé,powtórzenié,głosnosc +Keywords[cy]=Bysellfwrdd,Allweddell,Ailadrodd bysellfwrdd,Swn y clic,Dyfeisiau Mewnbwn,ailadrodd,sain +Keywords[da]=Tastatur,Tastaturgentagelse,Klikstyrke,Inddata-enheder,gentagelse,lydstyrke +Keywords[de]=Tastatur,Tastenwiederholung,Klicklautstärke,Eingabegeräte,Wiederholung,Lautstärke +Keywords[el]=Πληκτρολόγιο,Επανάληψη πληκτρολογίου,Ένταση κλικ,Συσκευές εισόδου,επανάληψη,ένταση +Keywords[eo]=klavaro,klavripeto,klako,laŭteco,enigo,aparatoj,ripeto,laŭteco,aranĝo +Keywords[es]=Teclado,Repetición del teclado,Volumen de la pulsación,Dispositivos de entrada,repetición,volumen,Disposición,Internacional +Keywords[et]=klaviatuur,klaviatuuri kordus,kliki helitugevus,klõpsu helitugevus,sisendseadmed,kordus,helitugevus +Keywords[eu]=Teklatua,Teklatu errepikapena,Klikadaren bolumena, Sarrerako gailuak,errepikatu,bolumena +Keywords[fa]=صفحه کلید، تکرار صفحه کلید، حجم فشار، دستگاههای ورودی، تکرار، حجم صدا +Keywords[fi]=Näppäimistö,Näppäintoisto,Näppäinäänet,Syöttölaitteet,toisto, äänenvoimakkuus +Keywords[fr]=clavier,répétition,clics de touches,périphériques d'entrée,volume +Keywords[fy]=keyboard,toetseboerd,keyboard repeat,toetseboerd werhelling,klikvolume,klikfolume,input devices,ynfier apparaten,invoerapparaten,toetsenbord,toetsenbordherhaling,herhaling,werhelling,volume,folume,randapparatuur,internationaal,randapparaten +Keywords[gl]=Teclado,repetición do teclado,Volume da tecla,Dispositivos de entrada,repetición,volume +Keywords[he]=לוח מקשים,מקלדת,חזרה על תו,עוצמת הקשה,התקני קלט,חזרה,עצמה, Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume +Keywords[hi]=कुंजीपट,कुंजी का दोहराव,क्लिक आवाज़ निर्धारक,इनपुट औज़ार,रिपीट,आवाज़ निर्धारक +Keywords[hr]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volme,Tipkovnica,glasnoća,uređaji za unos,ponavljanje,raspored, +Keywords[hu]=billentyűzet,billentyűismétlés,leütési hang,beviteli eszközök,ismétlés,hangerő +Keywords[id]=Keyboard,Pengulangan keyboard,Volume klik,Divais Input,repeat,volume +Keywords[is]=Lyklaborð,inntakstæki,endurtekning,útlit,alþjóðlegt +Keywords[it]=tastiera,ripetizione dei tasti,volume del clic,dispositivi di input,layout,volume +Keywords[ja]=キーボード,キーボードリピート,クリック音量,入力デバイス,リピート,音量 +Keywords[km]=ក្ដារចុច,ធ្វើក្ដារចុចម្ដងទៀត,សំឡេងចុច,ឧបករណ៍ព័ត៌មានបញ្ចូល,ធ្វើម្ដងទៀត,សំឡេង +Keywords[lo]=ແປ້ນພິມ ,ການຈັດວາງແປ້ນພິມ,ອຸປະກອນຂໍ້ມູນເຂົ້າ +Keywords[lt]=Klaviatūra,Klaviatūros kartojimai,Paspaudimo garsas,Įvedimo įrenginiai,kartojimas, garsas +Keywords[lv]=Tastatūra,Tastatūras atkārtošanās,Klikšķu līmenis,Ievades Iekārtas,atkārtošanās,līmenis,Izkārtojums +Keywords[mk]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,Тастатура,Повторување на копчиња,Гласност на кликот,Влезни уреди,повтори,гласност +Keywords[mn]=Гар,Товчилуур давталт,Товшилт-Дууны чанга,Оруулах төхөөрөмж, Давталт,Дууны чанга +Keywords[mt]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,Layout,International,tastiera,kijbord,kibord +Keywords[nb]=Tastatur,Tastaturgjentagelse,Klikkevolum,Innenheter,gjentagelse,volum +Keywords[nds]=Tastatuur,Tastwedderhalen,Klick-Luutstärk,Ingaavreedschappen,wedderhalen,Luutstärk +Keywords[ne]=कुञ्जीपाटी, कुञ्जीपाटी दोहोर्याउनुहोस्, भोल्युम क्लिक गर्नुहोस्, आगत यन्त्रहरू, दोहोर्याउनुहोस्, भोल्युम +Keywords[nl]=keyboard,keyboard repeat,klikvolume,input devices,invoerapparaten,toetsenbord,toetsenbordherhaling,herhaling,volume,randapparatuur,internationaal,randapparaten +Keywords[nn]=tastatur,tastaturrepetisjon,tasterepetisjon,inneiningar,tasteklikk,repetisjon,lydstyrke,volum +Keywords[nso]=Keyboard,poeletso ya Keyboard,Tobetsa volume,Maano a Ditsenyo,poeletso,volume +Keywords[pa]=ਕੀ-ਬੋਰਡ,ਕੀ-ਬੋਰਡ repeat,Click volume,Input ਜੰਤਰ,repeat,ਆਵਾਜ਼ +Keywords[pl]=Klawiatura,powtórzenie klawiatury,głośność kliknięcia,urządzenia wejściowe,powtórzenie,głośność +Keywords[pt]=teclado,repetição do teclado,volume,dispositivos de entrada,repetição,formato,internacional +Keywords[pt_BR]=Teclado,repetição de teclado,volume do clique,dispositivos de entrada,repetição,volume +Keywords[ro]=tastatură,repetiție,volum,clic,dispozitiv de intrare,repetare,volum +Keywords[rw]=Mwandikisho,Isubiramo Mwandikisho,Gukanda agahindurajwi, Apareye z'Icyinjira,gusubiramo,agahindurajwi +Keywords[se]=boallobeavdi,boallobeavdegeardduheapmi,jietnadássi,geardduheapmi,sisa ovttadagat +Keywords[sk]=Klávesnica,vstupné zariadenia,Opakovanie kláves,Hlasitosť stlačenia,opakovanie,hlasitosť +Keywords[sl]=tipkovnica,ponovitev tipk,glasnost tipkanja,vhodne naprave,ponovitev,glasnost +Keywords[sr]=Тастатура, ниво, понављање, улазни уређаји +Keywords[sr@Latn]=Tastatura, nivo, ponavljanje, ulazni uređaji +Keywords[sv]=Tangentbord,Tangentbordsupprepning,Klickvolym,Inenheter,upprepning,volym +Keywords[ta]=விசைப்பலகை,விசைப்பலகை திரும்பவும்,ஒலியளவை அழுத்தவும்,உள்ளிடு சாதனங்கள்,திரும்ப,ஒலியளவு +Keywords[tg]=Забонак,такрори Забонак,андозаи пахш,дастгоҳҳои вурудӣ,такрор,андоза +Keywords[th]=แป้นพิมพ์,การจัดวางแป้นพิมพ์,อุปกรณ์ใส่ข้อมูลา,การกดปุ่มค้าง,กดปุ่มค้างซ้ำ,,ระดับเสียง +Keywords[tr]=Klavye,Klavye tekrarı,Tıklama sesi,Girdi Aygıtları,tekrar,ses,yerleşim,uluslar arası +Keywords[uk]=клавіатура,повтор клавіш,гучність клацань,вхідні пристрої,повтор,гучність +Keywords[uz]=Tugmatag,tovush balandligi,qaytarish,Kiritish uskunalari,Bosish tovushi +Keywords[uz@cyrillic]=Тугматаг,товуш баландлиги,қайтариш,Киритиш ускуналари,Босиш товуши +Keywords[ven]=Khiibodo,U dovholola ha Khiibodo,Tshileme tshau putedza,maano,dovholola,Tshileme +Keywords[vi]=Bàn phím,Bàn phím lặp,Nhấn âm lượng,Thiết bị Vào,lặp,âm lượng +Keywords[wa]=Taprece,repetaedje di taece,clitchî li volume,éndjin en intrêye,repeter,volume +Keywords[xh]=Ibhodi yezitshixo,Ibhodi yezitshixo phinda, Nqakraza izinga lelizwi,Amacebo Ongeniso,phinda, izinga lelizwi +Keywords[zh_CN]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,键盘,键盘重复,击键音量,输入设备,重复,音量 +Keywords[zh_TW]=Keyboard,Keyboard repeat,Click volume,Input Devices,repeat,volume,鍵盤,鍵盤重複,敲擊音量,輸入裝置,重複速度,音量 +Keywords[zu]=Ibhodi lokhiye,ukuphindwa kwebhodi lokhiye,Cofa izinga lomsindo, Amathuluzi anenjongo omphumela wangaphakathi,phinda,izina lomsindo + +Categories=Qt;KDE;X-KDE-settings-hardware; diff --git a/kxkb/keyboard_layout.desktop b/kxkb/keyboard_layout.desktop new file mode 100644 index 000000000..2ba997aa0 --- /dev/null +++ b/kxkb/keyboard_layout.desktop @@ -0,0 +1,239 @@ +[Desktop Entry] +Exec=kcmshell keyboard_layout +Icon=keyboard +Type=Application +DocPath=kxkb/index.html + +X-KDE-ModuleType=Library +X-KDE-Library=keyboard +X-KDE-FactoryName=keyboard_layout +X-KDE-ParentApp=kcontrol + +Name=Keyboard Layout +Name[af]=Sleutelbord Uitleg +Name[ar]=تصميم لوحة المفاتيح +Name[az]=Klaviatura Düzülüşü +Name[be]=Раскладка клавіятуры +Name[bg]=Клавиатурни подредби +Name[bn]=কীবোর্ড বিন্যাস +Name[br]=Reizhadur ar stokellaoueg +Name[bs]=Raspored tastature +Name[ca]=Disposició del teclat +Name[cs]=Rozvržení klávesnice +Name[csb]=Ùstôw klawiaturë +Name[cy]=Cynllun Bysellfwrdd +Name[da]=Tastaturlayout +Name[de]=Tastaturlayout +Name[el]=Διάταξη πληκτρολογίου +Name[eo]=Klavararanĝo +Name[es]=Disposición del teclado +Name[et]=Klaviatuuri paigutus +Name[eu]=Teklatuaren diseinua +Name[fa]=طرحبندی صفحه کلید +Name[fi]=Näppäimistön järjestys +Name[fr]=Disposition du clavier +Name[fy]=Toetseboerdyndieling +Name[ga]=Leagan Amach Méarchláir +Name[gl]=Tipo de Teclado +Name[he]=פריסת מקלדת +Name[hi]=कुंजीपट ख़ाका +Name[hr]=Raspored tipkovnice +Name[hu]=Billentyűzetkiosztás +Name[id]=Tata letak Keyboard +Name[is]=Lyklaborð +Name[it]=Mappatura della tastiera +Name[ja]=キーボード配列 +Name[ka]=კლავიატურის განლაგება +Name[kk]=Перенетақта сәйкестігі +Name[km]=ប្លង់ក្ដារចុច +Name[ko]=키보드 레이아웃 +Name[lo]=ກາານຈັດວາງແປ້ນພິມ +Name[lt]=Klaviatūros išdėstymas +Name[lv]=Tastatūras izkārtojums +Name[mk]=Распоред на тастатура +Name[mn]=Гарын байрлал +Name[mt]=Tqassim tat-Tastiera +Name[nb]=Tastaturoppsett +Name[nds]=Tasttoornen +Name[ne]=कुञ्जीपाटी सजावट +Name[nl]=Toetsenbordindeling +Name[nn]=Tasteoppsett +Name[nso]=Peakanyo ya Keyboard +Name[oc]=Disposicion del clavièr +Name[pa]=ਕੀ-ਬੋਰਡ ਖਾਕਾ +Name[pl]=Układ klawiatury +Name[pt]=Disposição do Teclado +Name[pt_BR]=Layout do teclado +Name[ro]=Mapare de tastatură +Name[ru]=Раскладка клавиатуры +Name[rw]=Imigaragarire ya Mwandisho +Name[se]=Boallobeavdehápmi +Name[sk]=Rozloženie klávesnice +Name[sl]=Razpored tipk +Name[sr]=Распоред тастера +Name[sr@Latn]=Raspored tastera +Name[sv]=Tangentbordslayout +Name[ta]=விசைப்பலகை உருவரை +Name[te]=కీబోర్డ్ కూర్పు +Name[tg]=Тарҳбандии забонак +Name[th]=รูปแบบแป้นพิมพ์ +Name[tr]=Klavye Düzeni +Name[tt]=Töylek Caylawı +Name[uk]=Розкладка клавіатури +Name[uz]=Tugmalar tartibi +Name[uz@cyrillic]=Тугмалар тартиби +Name[ven]=Vhuvha ha khiibodo +Name[vi]=Kiểu bàn phím +Name[wa]=Adjinçmint del taprece +Name[xh]=Ubeko Lwebhodi yezitshixo +Name[zh_CN]=键盘布局 +Name[zh_TW]=鍵盤配置 +Name[zu]=Isendlalelo sebhodi lokhiye + +Comment=Keyboard Layout +Comment[af]=Sleutelbord Uitleg +Comment[ar]=تصميم لوحة المفاتيح +Comment[be]=Раскладка клавіятуры +Comment[bg]=Настройване на клавиатурните подредби +Comment[bn]=কীবোর্ড বিন্যাস +Comment[br]=Reizhadur ar stokellaoueg +Comment[bs]=Raspored tastature +Comment[ca]=Disposició del teclat +Comment[cs]=Rozvržení klávesnice +Comment[csb]=Ùstôw klawiaturë +Comment[cy]=Haenlun Bysellfwrdd +Comment[da]=Tastaturlayout +Comment[de]=Tastaturbelegung +Comment[el]=Διάταξη πληκτρολογίου +Comment[eo]=Klavararanĝo +Comment[es]=Disposición-Diseño del teclado +Comment[et]=Klaviatuuri paigutus +Comment[eu]=Teklatuaren diseinua +Comment[fa]=طرحبندی صفحه کلید +Comment[fi]=Näppäimistön järjestys +Comment[fr]=Disposition du clavier +Comment[fy]=Toetseboerdyndieling +Comment[ga]=Leagan Amach Méarchláir +Comment[gl]=Tipo de Teclado +Comment[he]=שינוי הגדרות פריסת המקלדת +Comment[hi]=कुंजीपट ख़ाका +Comment[hr]=Raspored tipkovnice +Comment[hu]=A billentyűk elrendezése +Comment[is]=Lyklaborðsútlit +Comment[it]=Mappatura della tastiera +Comment[ja]=キーボード配列 +Comment[ka]=კლავიატურის განლაგება +Comment[kk]=Пернетакта сәйкестігі +Comment[km]=ប្លង់ក្ដារចុច +Comment[ko]=키보드 레이아웃 +Comment[lo]=ການຈັດວາງແປ້ນພິມ +Comment[lt]=Klaviatūros maketas (išdėstymas) +Comment[lv]=Tastatūras Izkārtojums +Comment[mk]=Распоред на тастатура +Comment[mn]=Гарын байрлал +Comment[mt]=Tqassim tat-Tastiera +Comment[nb]=Tastaturoppsett +Comment[nds]=Tasttoornen +Comment[ne]=कुञ्जीपाटी सजावट +Comment[nl]=Toetsenbordindeling +Comment[nn]=Tasteoppsett +Comment[nso]=Peakanyo ya Keyboard +Comment[pa]=ਕੀ-ਬੋਰਡ ਖਾਕਾ +Comment[pl]=Układ klawiatury +Comment[pt]=Disposição do Teclado +Comment[pt_BR]=Layout do teclado +Comment[ro]=Mapare de tastatură +Comment[ru]=Раскладка клавиатуры +Comment[rw]=Imigaragarire ya Mwandikisho +Comment[se]=Boallobeavdehápmi +Comment[sk]=Rozloženie klávesnice +Comment[sl]=Razpored tipk +Comment[sr]=Распоред тастера +Comment[sr@Latn]=Raspored tastera +Comment[sv]=Anpassa tangentbordets layout +Comment[ta]=விசைப்பலகை உருவரை +Comment[te]=కీబోర్డ్ కూర్పు +Comment[tg]=Тарҳбандии забонак +Comment[th]=รูปแบบของแป้นพิมพ์ต่างๆ +Comment[tr]=Klavye Düzeni +Comment[tt]=Töylek Caylawı +Comment[uk]=Розклад клавіатури +Comment[uz]=Tugmalar tartibi +Comment[uz@cyrillic]=Тугмалар тартиби +Comment[ven]=Vhuvha ha khiibodo +Comment[vi]=Kiểu bàn phím +Comment[wa]=Adjinçmint des tapes del taprece +Comment[xh]=Ubeko Lwebhodi yezitshixo +Comment[zh_CN]=键盘布局 +Comment[zh_TW]=鍵盤配置 +Comment[zu]=Isendlalelo sebhodi lokhiye + +Keywords=Keyboard,Layout,International +Keywords[ar]=لوحة المحاتيح,تصميم,عالمي +Keywords[be]=Клавіятура,Раскладка,Міжнародны,Інтэрнацыяналізацыя,Keyboard,Layout,International +Keywords[bg]=клавиатура, подредба, български, фонетична, кирилица, БДС, Keyboard, Layout, International +Keywords[ca]=Teclat,Disposició,Internacional +Keywords[cs]=Klávesnice,Rozvržení klávesnice,Mezinárodní +Keywords[csb]=klawiatura,ùstôw klawiaturë,midzënôrodné +Keywords[cy]=Bysellfwrdd,Haenlun,Rhyngwladol +Keywords[da]=Tastatur,Layout,International +Keywords[de]=Tastatur,Belegung,Layout,International +Keywords[el]=Πληκτρολόγιο,Διάταξη,Διεθνής +Keywords[eo]=klavaro,aranĝo,internacia +Keywords[es]=Teclado,Disposición,Diseño,Internacional +Keywords[et]=klaviatuur,paigutus,rahvusvaheline +Keywords[eu]=Teklatua,Diseinua,Nazio artekoa +Keywords[fa]=صفحه کلید، طرحبندی، بینالمللی +Keywords[fi]=Näppäimistö,Näppäimistön järjestys,Kansainvälinen +Keywords[fr]=clavier,disposition du clavier,périphériques d'entrée,International +Keywords[fy]=keyboard,toetsenbord,toetseboerd,keyboard layout,toetseboerdyndieling,toetsenbordindeling,input devices,invoerapparaten,ynfierapparaten,randapparaten +Keywords[ga]=Méarchlár,Leagan Amach,Idirnáisiúnta +Keywords[gl]=Teclado,Tipo de teclado,Internacional +Keywords[he]=מקלדת,פריסה,בינלאומי, Keyboard,Layout,International +Keywords[hi]=कुंजीपट,ख़ाका,अंतर्राष्ट्रीय +Keywords[hr]=Keyboard,Keyboard layout,Input Devices,Tipkovnica,Raspored tipkovnice,Međunarodno +Keywords[hu]=billentyűzet,kiosztás,nemzetközi +Keywords[is]=Lyklaborð,inntakstæki,alþjóðlegt +Keywords[it]=tastiera,mappatura,internazionale +Keywords[ja]=キーボード,キーボードレイアウト,国際化 +Keywords[km]=ក្ដារចុច,ប្លង់,អន្តរជាតិ +Keywords[lo]=ແປ້່ນພິມ,ການຈັດວາງແປ້ນພິມ,ອຸຸປະກອນຂໍ້ມູນເຂົ້າ +Keywords[lt]=Klaviatūra,Klaviatūros išdėstymas,klaviatūros maketas,tarptautinis +Keywords[lv]=Tastatūra,Tastatūras izkārtojums, Ievada Iekārtas +Keywords[mk]=Keyboard,Layout,International,Тастатура,Распоред,Интернационален +Keywords[mn]=Гар,Баталгаа,Layout,International +Keywords[mt]=Tastiera,Keyboard,Tqassim tal-keyboard,Input Devices +Keywords[nb]=tastatur,tasteoppsett,oppsett,språk,internasjonal +Keywords[nds]=Tastatuur,Tasttoornen,Layout,Internatschonaal +Keywords[ne]=कुञ्जीपाटी, सजावट, अन्तरराष्ट्रिय +Keywords[nl]=keyboard,toetsenbord,keyboard layout,toetsenbordindeling,input devices,invoerapparaten,randapparaten +Keywords[nn]=tastatur,tasteoppsett,oppsett,språk,internasjonal +Keywords[nso]=Keyboard,Peakanyo,Boditshabatshaba +Keywords[pa]=ਕੀ-ਬੋਰਡ,ਖਾਕਾ,ਅੰਤਰਰਾਸ਼ਟਰੀ +Keywords[pl]=Klawiatura,Układ klawiatury,Międzynarodowe +Keywords[pt]=Teclado,Disposição,Internacional +Keywords[pt_BR]=Teclado,Layout de teclado,internacional +Keywords[ro]=tastatură,mapare,internațional +Keywords[rw]=Mwandikisho, Imigaragarire, Mpuzamahanga +Keywords[se]=boallobeavdi,hápmi,riikkaidgaskasaš +Keywords[sk]=klávesnica,rozloženie,medzinárodné +Keywords[sl]=tipkovnica,razpored,mednarodno +Keywords[sr]=Keyboard,Layout,International,тастатура,распоред,интернационално +Keywords[sr@Latn]=Keyboard,Layout,International,tastatura,raspored,internacionalno +Keywords[sv]=Tangentbord,Layout,Internationell +Keywords[ta]=விசைப்பலகை, உருவரை,சர்வதேச +Keywords[th]=แป้นพิมพ์,รูปแบบแป้นพิมพ์,ระหว่างประเทศ +Keywords[tr]=Klavye,Yerleşim,Düzen,Uluslararası +Keywords[tt]=Töylek,Caylaw,Telläşterü,İlläşterü +Keywords[uk]=клавіатура,розкладка клавіатури,інтернаціоналізація +Keywords[uz]=Tugmatag,Tugmalar tartibi,Halqaro +Keywords[uz@cyrillic]=Тугматаг,Тугмалар тартиби,Ҳалқаро +Keywords[ven]=khiibodo,vhuvha ha khiibodo,shango lothe +Keywords[vi]=Bàn phím,Kiểu,Quốc tế +Keywords[wa]=Taprece,Adjinçmint,eternåcionå,eternåcionåle +Keywords[xh]=Ibhodi yezitshixo,Ubeko lwebhodi yezitshixo, Okwemazwe ngamazwe +Keywords[zh_CN]=Keyboard,Layout,International,键盘,布局,国际 +Keywords[zh_TW]=Keyboard,Layout,International,鍵盤,配置,國際化 +Keywords[zu]=Ibhodi lokhiye,Isendlalelo,Okwaphakathi kwezizwe + +Categories=Qt;KDE;X-KDE-settings-accessibility; diff --git a/kxkb/kxkb.cpp b/kxkb/kxkb.cpp new file mode 100644 index 000000000..114ddc7a9 --- /dev/null +++ b/kxkb/kxkb.cpp @@ -0,0 +1,390 @@ +/* + Copyright (C) 2001, S.R.Haque <srhaque@iee.org>. Derived from an + original by Matthias H�zer-Klpfel released under the QPL. + This file is part of the KDE project + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +DESCRIPTION + + KDE Keyboard Tool. Manages XKB keyboard mappings. +*/ + +#include <unistd.h> +#include <stdlib.h> +#include <assert.h> + +#include <qregexp.h> +#include <qfile.h> +#include <qstringlist.h> +#include <qimage.h> + +#include <kaboutdata.h> +#include <kcmdlineargs.h> +#include <kglobal.h> +#include <kglobalaccel.h> +#include <klocale.h> +#include <kprocess.h> +#include <kwinmodule.h> +#include <kwin.h> +#include <ktempfile.h> +#include <kstandarddirs.h> +#include <kipc.h> +#include <kaction.h> +#include <kpopupmenu.h> +#include <kdebug.h> +#include <kconfig.h> + +#include "x11helper.h" +#include "kxkb.h" +#include "extension.h" +#include "rules.h" +#include "kxkbconfig.h" +#include "layoutmap.h" + +#include "kxkb.moc" + + +KXKBApp::KXKBApp(bool allowStyles, bool GUIenabled) + : KUniqueApplication(allowStyles, GUIenabled), + m_prevWinId(X11Helper::UNKNOWN_WINDOW_ID), + m_rules(NULL), + m_tray(NULL), + kWinModule(NULL), + m_forceSetXKBMap( false ) +{ + m_extension = new XKBExtension(); + if( !m_extension->init() ) { + kdDebug() << "xkb initialization failed, exiting..." << endl; + ::exit(1); + } + + // keep in sync with kcmlayout.cpp + keys = new KGlobalAccel(this); +#include "kxkbbindings.cpp" + keys->updateConnections(); + + m_layoutOwnerMap = new LayoutMap(kxkbConfig); + + connect( this, SIGNAL(settingsChanged(int)), SLOT(slotSettingsChanged(int)) ); + addKipcEventMask( KIPC::SettingsChanged ); +} + + +KXKBApp::~KXKBApp() +{ +// deletePrecompiledLayouts(); + + delete keys; + delete m_tray; + delete m_rules; + delete m_extension; + delete m_layoutOwnerMap; + delete kWinModule; +} + +int KXKBApp::newInstance() +{ + m_extension->reset(); + + if( settingsRead() ) + layoutApply(); + + return 0; +} + +bool KXKBApp::settingsRead() +{ + kxkbConfig.load( KxkbConfig::LOAD_ACTIVE_OPTIONS ); + + if( kxkbConfig.m_enableXkbOptions ) { + kdDebug() << "Setting XKB options " << kxkbConfig.m_options << endl; + if( !m_extension->setXkbOptions(kxkbConfig.m_options, kxkbConfig.m_resetOldOptions) ) { + kdDebug() << "Setting XKB options failed!" << endl; + } + } + + if ( kxkbConfig.m_useKxkb == false ) { + kapp->quit(); + return false; + } + + m_prevWinId = X11Helper::UNKNOWN_WINDOW_ID; + + if( kxkbConfig.m_switchingPolicy == SWITCH_POLICY_GLOBAL ) { + delete kWinModule; + kWinModule = NULL; + } + else { + QDesktopWidget desktopWidget; + if( desktopWidget.numScreens() > 1 && desktopWidget.isVirtualDesktop() == false ) { + kdWarning() << "With non-virtual desktop only global switching policy supported on non-primary screens" << endl; + //TODO: find out how to handle that + } + + if( kWinModule == NULL ) { + kWinModule = new KWinModule(0, KWinModule::INFO_DESKTOP); + connect(kWinModule, SIGNAL(activeWindowChanged(WId)), SLOT(windowChanged(WId))); + } + m_prevWinId = kWinModule->activeWindow(); + kdDebug() << "Active window " << m_prevWinId << endl; + } + + m_layoutOwnerMap->reset(); + m_layoutOwnerMap->setCurrentWindow( m_prevWinId ); + + if( m_rules == NULL ) + m_rules = new XkbRules(false); + + for(int ii=0; ii<(int)kxkbConfig.m_layouts.count(); ii++) { + LayoutUnit& layoutUnit = kxkbConfig.m_layouts[ii]; + layoutUnit.defaultGroup = m_rules->getDefaultGroup(layoutUnit.layout, layoutUnit.includeGroup); + kdDebug() << "default group for " << layoutUnit.toPair() << " is " << layoutUnit.defaultGroup << endl; + } + + m_currentLayout = kxkbConfig.getDefaultLayout(); + + if( kxkbConfig.m_layouts.count() == 1 ) { + QString layoutName = m_currentLayout.layout; + QString variantName = m_currentLayout.variant; + QString includeName = m_currentLayout.includeGroup; + int group = m_currentLayout.defaultGroup; + + if( !m_extension->setLayout(kxkbConfig.m_model, layoutName, variantName, includeName, false) + || !m_extension->setGroup( group ) ) { + kdDebug() << "Error switching to single layout " << m_currentLayout.toPair() << endl; + // TODO: alert user + } + + if( kxkbConfig.m_showSingle == false ) { + kapp->quit(); + return false; + } + } + else { +// initPrecompiledLayouts(); + } + + initTray(); + + KGlobal::config()->reparseConfiguration(); // kcontrol modified kdeglobals + keys->readSettings(); + keys->updateConnections(); + + return true; +} + +void KXKBApp::initTray() +{ + if( !m_tray ) + { + KSystemTray* sysTray = new KxkbSystemTray(); + KPopupMenu* popupMenu = sysTray->contextMenu(); + // popupMenu->insertTitle( kapp->miniIcon(), kapp->caption() ); + + m_tray = new KxkbLabelController(sysTray, popupMenu); + connect(popupMenu, SIGNAL(activated(int)), this, SLOT(menuActivated(int))); + connect(sysTray, SIGNAL(toggled()), this, SLOT(toggled())); + } + + m_tray->setShowFlag(kxkbConfig.m_showFlag); + m_tray->initLayoutList(kxkbConfig.m_layouts, *m_rules); + m_tray->setCurrentLayout(m_currentLayout); + m_tray->show(); +} + +// This function activates the keyboard layout specified by the +// configuration members (m_currentLayout) +void KXKBApp::layoutApply() +{ + setLayout(m_currentLayout); +} + +// kdcop +bool KXKBApp::setLayout(const QString& layoutPair) +{ + const LayoutUnit layoutUnitKey(layoutPair); + if( kxkbConfig.m_layouts.contains(layoutUnitKey) ) { + return setLayout( *kxkbConfig.m_layouts.find(layoutUnitKey) ); + } + return false; +} + + +// Activates the keyboard layout specified by 'layoutUnit' +bool KXKBApp::setLayout(const LayoutUnit& layoutUnit, int group) +{ + bool res = false; + + if( group == -1 ) + group = layoutUnit.defaultGroup; + + res = m_extension->setLayout(kxkbConfig.m_model, + layoutUnit.layout, layoutUnit.variant, + layoutUnit.includeGroup); + if( res ) + m_extension->setGroup(group); // not checking for ret - not important + + if( res ) + m_currentLayout = layoutUnit; + + if (m_tray) { + if( res ) + m_tray->setCurrentLayout(layoutUnit); + else + m_tray->setError(layoutUnit.toPair()); + } + + return res; +} + +void KXKBApp::toggled() +{ + const LayoutUnit& layout = m_layoutOwnerMap->getNextLayout().layoutUnit; + setLayout(layout); +} + +void KXKBApp::menuActivated(int id) +{ + if( KxkbLabelController::START_MENU_ID <= id + && id < KxkbLabelController::START_MENU_ID + (int)kxkbConfig.m_layouts.count() ) + { + const LayoutUnit& layout = kxkbConfig.m_layouts[id - KxkbLabelController::START_MENU_ID]; + m_layoutOwnerMap->setCurrentLayout( layout ); + setLayout( layout ); + } + else if (id == KxkbLabelController::CONFIG_MENU_ID) + { + KProcess p; + p << "kcmshell" << "keyboard_layout"; + p.start(KProcess::DontCare); + } + else if (id == KxkbLabelController::HELP_MENU_ID) + { + KApplication::kApplication()->invokeHelp(0, "kxkb"); + } + else + { + quit(); + } +} + +// TODO: we also have to handle deleted windows +void KXKBApp::windowChanged(WId winId) +{ +// kdDebug() << "window switch" << endl; + if( kxkbConfig.m_switchingPolicy == SWITCH_POLICY_GLOBAL ) { // should not happen actually + kdDebug() << "windowChanged() signal in GLOBAL switching policy" << endl; + return; + } + + int group = m_extension->getGroup(); + + kdDebug() << "old WinId: " << m_prevWinId << ", new WinId: " << winId << endl; + + if( m_prevWinId != X11Helper::UNKNOWN_WINDOW_ID ) { // saving layout/group from previous window +// kdDebug() << "storing " << m_currentLayout.toPair() << ":" << group << " for " << m_prevWinId << endl; +// m_layoutOwnerMap->setCurrentWindow(m_prevWinId); + m_layoutOwnerMap->setCurrentLayout(m_currentLayout); + m_layoutOwnerMap->setCurrentGroup(group); + } + + m_prevWinId = winId; + + if( winId != X11Helper::UNKNOWN_WINDOW_ID ) { + m_layoutOwnerMap->setCurrentWindow(winId); + const LayoutState& layoutState = m_layoutOwnerMap->getCurrentLayout(); + + if( layoutState.layoutUnit != m_currentLayout ) { + kdDebug() << "switching to " << layoutState.layoutUnit.toPair() << ":" << group << " for " << winId << endl; + setLayout( layoutState.layoutUnit, layoutState.group ); + } + else if( layoutState.group != group ) { // we need to change only the group + m_extension->setGroup(layoutState.group); + } + } +} + + +void KXKBApp::slotSettingsChanged(int category) +{ + if ( category != KApplication::SETTINGS_SHORTCUTS) + return; + + KGlobal::config()->reparseConfiguration(); // kcontrol modified kdeglobals + keys->readSettings(); + keys->updateConnections(); +} + +/* + Viki (onscreen keyboard) has problems determining some modifiers states + when kxkb uses precompiled layouts instead of setxkbmap. Probably a bug + in the xkb functions used for the precompiled layouts *shrug*. +*/ +void KXKBApp::forceSetXKBMap( bool set ) +{ + if( m_forceSetXKBMap == set ) + return; + m_forceSetXKBMap = set; + layoutApply(); +} + +/*Precompiles the keyboard layouts for faster activation later. +This is done by loading each one of them and then dumping the compiled +map from the X server into our local buffer.*/ +// void KXKBApp::initPrecompiledLayouts() +// { +// QStringList dirs = KGlobal::dirs()->findDirs ( "tmp", "" ); +// QString tempDir = dirs.count() == 0 ? "/tmp/" : dirs[0]; +// +// QValueList<LayoutUnit>::ConstIterator end = kxkbConfig.m_layouts.end(); +// +// for (QValueList<LayoutUnit>::ConstIterator it = kxkbConfig.m_layouts.begin(); it != end; ++it) +// { +// LayoutUnit layoutUnit(*it); +// // const char* baseGr = m_includes[layout]; +// // int group = m_rules->getGroup(layout, baseGr); +// // if( m_extension->setLayout(m_model, layout, m_variants[layout], group, baseGr) ) { +// QString compiledLayoutFileName = tempDir + layoutUnit.layout + "." + layoutUnit.variant + ".xkm"; +// // if( m_extension->getCompiledLayout(compiledLayoutFileName) ) +// m_compiledLayoutFileNames[layoutUnit.toPair()] = compiledLayoutFileName; +// // } +// // else { +// // kdDebug() << "Error precompiling layout " << layout << endl; +// // } +// } +// } + + +const char * DESCRIPTION = + I18N_NOOP("A utility to switch keyboard maps"); + +extern "C" KDE_EXPORT int kdemain(int argc, char *argv[]) +{ + KAboutData about("kxkb", I18N_NOOP("KDE Keyboard Tool"), "1.0", + DESCRIPTION, KAboutData::License_LGPL, + "Copyright (C) 2001, S.R.Haque\n(C) 2002-2003, 2006 Andriy Rysin"); + KCmdLineArgs::init(argc, argv, &about); + KXKBApp::addCmdLineOptions(); + + if (!KXKBApp::start()) + return 0; + + KXKBApp app; + app.disableSessionManagement(); + app.exec(); + return 0; +} diff --git a/kxkb/kxkb.desktop b/kxkb/kxkb.desktop new file mode 100644 index 000000000..283e0a663 --- /dev/null +++ b/kxkb/kxkb.desktop @@ -0,0 +1,82 @@ +[Desktop Entry] +Type=Service +Name=Keyboard Map Tool +Name[af]=Sleutelbord Kaart Program +Name[ar]=أداة خريطة المحارف للوحة المفاتيح +Name[az]=Klaviatura Xəritə Vasitəsi +Name[be]=Інструмент вызначэння раскладак +Name[bg]=Превключвател на клавиатури +Name[bn]=কীবোর্ড ম্যাপ টুল +Name[bs]=Alat za mapu tastature +Name[ca]=Eina per al mapa de teclat +Name[cs]=Nástroj k nastavení klávesnice +Name[csb]=Nôrzãdze mapòwaniô klawiaturë +Name[cy]=Erfyn Map Bysellfwrdd +Name[da]=Tastaturkortværktøj +Name[de]=Dienstprogramm zum Tastaturlayout +Name[el]=Εργαλείο χάρτη πληκτρολογίου +Name[eo]=Klavararanĝilo +Name[es]=Redefinición del teclado +Name[et]=Klaviatuuri paigutus +Name[eu]=Teklatu-mapa tresna +Name[fa]=ابزار نگاشت صفحه کلید +Name[fi]=Näppäimistön järjestys +Name[fr]=Outil de sélection du clavier +Name[fy]=Toetseboerdyndieling +Name[ga]=Uirlis Mhapála Méarchláir +Name[gl]=Ferramenta do Mapa de Teclado +Name[he]=כלי מיפוי מקשים +Name[hi]=कुंजीपट मैप औज़ार +Name[hr]=Alat za mapiranje tipkovnice +Name[hu]=Billentyűzetkiosztás-kezelő alkalmazás +Name[id]=Tool Memetakan Keyboard +Name[is]=Skipulagstól lyklaborðs +Name[it]=Strumento per la mappatura della tastiera +Name[ja]=キーボードマップレイアウト +Name[ka]=კლავიატურის განლაგების გადართვა +Name[kk]=Перенетақтаны ауыстырушысы +Name[km]=ឧបករណ៍ប្លង់ក្ដារចុច +Name[ko]=키보드 맵 도구 +Name[lo]=ເຄື່ອງມືຈັດຕາລາງປຸຸ່ມພິມ +Name[lt]=Klaviatūros maketų įrankis +Name[lv]=Tastatūras Piesaistes Rīks +Name[mk]=Алатка за мапирање на тастатурата +Name[mn]=Гарын байрлалын үйлчилгээний програм +Name[mt]=Għodda tat-tqassim tat-tastiera +Name[nb]=Verktøy for tastaturoppsett +Name[nds]=Tasttoornen-Warktüüch +Name[ne]=कुञ्जीपाटी मानचित्र उपकरण +Name[nl]=Toetsenbordindeling +Name[nn]=Verktøy for tastaturoppsett +Name[nso]=Sebereka sa Mmepe wa Keyboard +Name[oc]=Otilh de mapa de clavièr +Name[pa]=ਕੀ-ਬੋਰਡ ਮੈਪ ਸੰਦ +Name[pl]=Narzędzie mapowania klawiatury +Name[pt]=Disposição do Teclado +Name[pt_BR]=Ferramenta de Mapeamento do Tclado +Name[ro]=Utilitar mapare tastatură +Name[ru]=Переключатель раскладки клавиатуры +Name[rw]=Igikoresho cy'Ikarita ya Mwandikisho +Name[se]=Reaidu boallobeavdeheiveheapmái +Name[sk]=Nástroj pre rozloženie kláves +Name[sl]=Razpored tipk +Name[sr]=Алат за мапирање тастатуре +Name[sr@Latn]=Alat za mapiranje tastature +Name[sv]=Verktyg för tangentbordslayout +Name[ta]=விசைப்பலகை குறுக்குவழி கருவி +Name[tg]=Обзори нақшаи сафҳаи калид +Name[th]=เครื่องมือจัดตารางปุ่มพิมพ์ +Name[tr]=Klavye Harita Aracı +Name[tt]=Töylek Caylatqıç Qoral +Name[uk]=Утиліта розкладки клавіатури +Name[uz]=Tugmalar tartibi uchun vosita +Name[uz@cyrillic]=Тугмалар тартиби учун восита +Name[ven]=Tshishumiswa tsha mapa wa Khiibodo +Name[vi]=Công cụ Ánh xạ Bàn phím +Name[wa]=Adjinçmint del taprece +Name[xh]=Isixhobo semaphu yebhodi enezitshixo +Name[zh_CN]=键盘映射工具 +Name[zh_TW]=鍵盤映射工具 +Name[zu]=Ithuluzi Lebalazwe lebhodi lokhiye +Exec=kxkb +X-DCOP-ServiceType=Unique diff --git a/kxkb/kxkb.h b/kxkb/kxkb.h new file mode 100644 index 000000000..914ae0f92 --- /dev/null +++ b/kxkb/kxkb.h @@ -0,0 +1,99 @@ +/* + Copyright (C) 2001, S.R.Haque <srhaque@iee.org>. Derived from an + original by Matthias H�zer-Klpfel released under the QPL. + This file is part of the KDE project + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. + +DESCRIPTION + + KDE Keyboard Tool. Manages XKB keyboard mappings. +*/ +#ifndef __K_XKB_H__ +#define __K_XKB_H__ + + +#include <qstring.h> +#include <qstringlist.h> +#include <qdict.h> +#include <qptrqueue.h> + +#include <kuniqueapplication.h> + +#include "kxkbtraywindow.h" +#include "kxkbconfig.h" + + +class XKBExtension; +class XkbRules; +class KGlobalAccel; +class KWinModule; +class LayoutMap; + +/* This is the main Kxkb class responsible for reading options + and switching layouts +*/ + +class KXKBApp : public KUniqueApplication +{ + Q_OBJECT + K_DCOP + +public: + KXKBApp(bool allowStyles=true, bool GUIenabled=true); + ~KXKBApp(); + + virtual int newInstance(); + + bool setLayout(const LayoutUnit& layoutUnit, int group=-1); +k_dcop: + bool setLayout(const QString& layoutPair); + QString getCurrentLayout() { return m_currentLayout.toPair(); } + QStringList getLayoutsList() { return kxkbConfig.getLayoutStringList(); } + void forceSetXKBMap( bool set ); + +protected slots: + void menuActivated(int id); + void toggled(); + void windowChanged(WId winId); + + void slotSettingsChanged(int category); + +protected: + // Read settings, and apply them. + bool settingsRead(); + void layoutApply(); + +private: + void initTray(); + +private: + KxkbConfig kxkbConfig; + + WId m_prevWinId; // for tricky part of saving xkb group + LayoutMap* m_layoutOwnerMap; + + LayoutUnit m_currentLayout; + + XKBExtension *m_extension; + XkbRules *m_rules; + KxkbLabelController *m_tray; + KGlobalAccel *keys; + KWinModule* kWinModule; + bool m_forceSetXKBMap; +}; + +#endif diff --git a/kxkb/kxkb_groups b/kxkb/kxkb_groups new file mode 100644 index 000000000..e83ccbe41 --- /dev/null +++ b/kxkb/kxkb_groups @@ -0,0 +1,43 @@ +ar 1 +be 0 +ben 0 +br 0 +bg 1 +by 1 +ca 0 +cs 0 +cz 0 +de 0 +de_CH 0 +dev 1 +dk 0 +ee 0 +en_US 0 +el 1 +es 0 +eo 0 +fi 0 +fr 0 +fr_CH 0 +he 1 +hu 0 +hr 0 +il 1 +ir 1 +it 0 +kl 0 +lt 1 +lv 0 +mk 1 +nl 0 +no 0 +pl 0 +pt 0 +ro 0 +ru 1 +ru_UA 0 +se 0 +sk 0 +th 1 +us 0 +ua 1 diff --git a/kxkb/kxkbbindings.cpp b/kxkb/kxkbbindings.cpp new file mode 100644 index 000000000..d8cfc36d5 --- /dev/null +++ b/kxkb/kxkbbindings.cpp @@ -0,0 +1,12 @@ +#ifndef NOSLOTS +# define DEF( name, key3, key4, fnSlot ) \ + keys->insert( name, i18n(name), QString::null, key3, key4, this, SLOT(fnSlot) ) +#else +# define DEF( name, key3, key4, fnSlot ) \ + keys->insert( name, i18n(name), QString::null, key3, key4 ) +#endif + + keys->insert( "Program:kxkb", i18n("Keyboard") ); + DEF( I18N_NOOP("Switch to Next Keyboard Layout"), ALT+CTRL+Qt::Key_K, KKey::QtWIN+CTRL+Qt::Key_K, toggled() ); + +#undef DEF diff --git a/kxkb/kxkbconfig.cpp b/kxkb/kxkbconfig.cpp new file mode 100644 index 000000000..4164a06c3 --- /dev/null +++ b/kxkb/kxkbconfig.cpp @@ -0,0 +1,340 @@ +// +// C++ Implementation: kxkbconfig +// +// Description: +// +// +// Author: Andriy Rysin <rysin@kde.org>, (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include <assert.h> + +#include <qregexp.h> +#include <qstringlist.h> +#include <qdict.h> + +#include <kconfig.h> +#include <kdebug.h> + +#include "kxkbconfig.h" +#include "x11helper.h" + + + +static const char* switchModes[SWITCH_POLICY_COUNT] = { + "Global", "WinClass", "Window" +}; + +const LayoutUnit DEFAULT_LAYOUT_UNIT = LayoutUnit("us", ""); +const char* DEFAULT_MODEL = "pc104"; + +LayoutUnit KxkbConfig::getDefaultLayout() +{ + if( m_layouts.size() == 0 ) + return DEFAULT_LAYOUT_UNIT; + + return m_layouts[0]; +} + +bool KxkbConfig::load(int loadMode) +{ + KConfig *config = new KConfig("kxkbrc", true, false); + config->setGroup("Layout"); + +// Even if the layouts have been disabled we still want to set Xkb options +// user can always switch them off now in the "Options" tab + m_enableXkbOptions = config->readBoolEntry("EnableXkbOptions", false); + + if( m_enableXkbOptions == true || loadMode == LOAD_ALL ) { + m_resetOldOptions = config->readBoolEntry("ResetOldOptions", false); + m_options = config->readEntry("Options", ""); + kdDebug() << "Xkb options (enabled=" << m_enableXkbOptions << "): " << m_options << endl; + } + + m_useKxkb = config->readBoolEntry("Use", false); + kdDebug() << "Use kxkb " << m_useKxkb << endl; + + if( (m_useKxkb == false && loadMode == LOAD_ACTIVE_OPTIONS ) + || loadMode == LOAD_INIT_OPTIONS ) + return true; + + m_model = config->readEntry("Model", DEFAULT_MODEL); + kdDebug() << "Model: " << m_model << endl; + + QStringList layoutList; + if( config->hasKey("LayoutList") ) { + layoutList = config->readListEntry("LayoutList"); + } + else { // old config + QString mainLayout = config->readEntry("Layout", DEFAULT_LAYOUT_UNIT.toPair()); + layoutList = config->readListEntry("Additional"); + layoutList.prepend(mainLayout); + } + if( layoutList.count() == 0 ) + layoutList.append("us"); + + m_layouts.clear(); + for(QStringList::ConstIterator it = layoutList.begin(); it != layoutList.end() ; ++it) { + m_layouts.append( LayoutUnit(*it) ); + kdDebug() << " layout " << LayoutUnit(*it).toPair() << " in list: " << m_layouts.contains( LayoutUnit(*it) ) << endl; + } + + kdDebug() << "Found " << m_layouts.count() << " layouts, default is " << getDefaultLayout().toPair() << endl; + + QStringList displayNamesList = config->readListEntry("DisplayNames", ','); + for(QStringList::ConstIterator it = displayNamesList.begin(); it != displayNamesList.end() ; ++it) { + QStringList displayNamePair = QStringList::split(':', *it ); + if( displayNamePair.count() == 2 ) { + LayoutUnit layoutUnit( displayNamePair[0] ); + if( m_layouts.contains( layoutUnit ) ) { + m_layouts[m_layouts.findIndex(layoutUnit)].displayName = displayNamePair[1].left(3); + } + } + } + +// m_includes.clear(); + if( X11Helper::areSingleGroupsSupported() ) { + if( config->hasKey("IncludeGroups") ) { + QStringList includeList = config->readListEntry("IncludeGroups", ','); + for(QStringList::ConstIterator it = includeList.begin(); it != includeList.end() ; ++it) { + QStringList includePair = QStringList::split(':', *it ); + if( includePair.count() == 2 ) { + LayoutUnit layoutUnit( includePair[0] ); + if( m_layouts.contains( layoutUnit ) ) { + m_layouts[m_layouts.findIndex(layoutUnit)].includeGroup = includePair[1]; + kdDebug() << "Got inc group: " << includePair[0] << ": " << includePair[1] << endl; + } + } + } + } + else { //old includes format + kdDebug() << "Old includes..." << endl; + QStringList includeList = config->readListEntry("Includes"); + for(QStringList::ConstIterator it = includeList.begin(); it != includeList.end() ; ++it) { + QString layoutName = LayoutUnit::parseLayout( *it ); + LayoutUnit layoutUnit( layoutName, "" ); + kdDebug() << "old layout for inc: " << layoutUnit.toPair() << " included " << m_layouts.contains( layoutUnit ) << endl; + if( m_layouts.contains( layoutUnit ) ) { + QString variantName = LayoutUnit::parseVariant(*it); + m_layouts[m_layouts.findIndex(layoutUnit)].includeGroup = variantName; + kdDebug() << "Got inc group: " << layoutUnit.toPair() << ": " << variantName << endl; + } + } + } + } + + m_showSingle = config->readBoolEntry("ShowSingle", false); + m_showFlag = config->readBoolEntry("ShowFlag", true); + + QString layoutOwner = config->readEntry("SwitchMode", "Global"); + + if( layoutOwner == "WinClass" ) { + m_switchingPolicy = SWITCH_POLICY_WIN_CLASS; + } + else if( layoutOwner == "Window" ) { + m_switchingPolicy = SWITCH_POLICY_WINDOW; + } + else /*if( layoutOwner == "Global" )*/ { + m_switchingPolicy = SWITCH_POLICY_GLOBAL; + } + + if( m_layouts.count() < 2 && m_switchingPolicy != SWITCH_POLICY_GLOBAL ) { + kdWarning() << "Layout count is less than 2, using Global switching policy" << endl; + m_switchingPolicy = SWITCH_POLICY_GLOBAL; + } + + kdDebug() << "Layout owner mode " << layoutOwner << endl; + + m_stickySwitching = config->readBoolEntry("StickySwitching", false); + m_stickySwitchingDepth = config->readEntry("StickySwitchingDepth", "2").toInt(); + if( m_stickySwitchingDepth < 2 ) + m_stickySwitchingDepth = 2; + + if( m_stickySwitching == true ) { + if( m_layouts.count() < 3 ) { + kdWarning() << "Layout count is less than 3, sticky switching will be off" << endl; + m_stickySwitching = false; + } + else + if( (int)m_layouts.count() - 1 < m_stickySwitchingDepth ) { + kdWarning() << "Sticky switching depth is more than layout count -1, adjusting..." << endl; + m_stickySwitchingDepth = m_layouts.count() - 1; + } + } + + delete config; + + return true; +} + +void KxkbConfig::save() +{ + KConfig *config = new KConfig("kxkbrc", false, false); + config->setGroup("Layout"); + + config->writeEntry("Model", m_model); + + config->writeEntry("EnableXkbOptions", m_enableXkbOptions ); + config->writeEntry("ResetOldOptions", m_resetOldOptions); + config->writeEntry("Options", m_options ); + + QStringList layoutList; + QStringList includeList; + QStringList displayNamesList; + + QValueList<LayoutUnit>::ConstIterator it; + for(it = m_layouts.begin(); it != m_layouts.end(); ++it) { + const LayoutUnit& layoutUnit = *it; + + layoutList.append( layoutUnit.toPair() ); + + if( layoutUnit.includeGroup.isEmpty() == false ) { + QString incGroupUnit = QString("%1:%2").arg(layoutUnit.toPair(), layoutUnit.includeGroup); + includeList.append( incGroupUnit ); + } + + QString displayName( layoutUnit.displayName ); + kdDebug() << " displayName " << layoutUnit.toPair() << " : " << displayName << endl; + if( displayName.isEmpty() == false && displayName != layoutUnit.layout ) { + displayName = QString("%1:%2").arg(layoutUnit.toPair(), displayName); + displayNamesList.append( displayName ); + } + } + + config->writeEntry("LayoutList", layoutList); + kdDebug() << "Saving Layouts: " << layoutList << endl; + + config->writeEntry("IncludeGroups", includeList); + kdDebug() << "Saving includeGroups: " << includeList << endl; + +// if( displayNamesList.empty() == false ) + config->writeEntry("DisplayNames", displayNamesList); +// else +// config->deleteEntry("DisplayNames"); + + config->writeEntry("Use", m_useKxkb); + config->writeEntry("ShowSingle", m_showSingle); + config->writeEntry("ShowFlag", m_showFlag); + + config->writeEntry("SwitchMode", switchModes[m_switchingPolicy]); + + config->writeEntry("StickySwitching", m_stickySwitching); + config->writeEntry("StickySwitchingDepth", m_stickySwitchingDepth); + + // remove old options + config->deleteEntry("Variants"); + config->deleteEntry("Includes"); + config->deleteEntry("Encoding"); + config->deleteEntry("AdditionalEncodings"); + config->deleteEntry("Additional"); + config->deleteEntry("Layout"); + + config->sync(); + + delete config; +} + +void KxkbConfig::setDefaults() +{ + m_model = DEFAULT_MODEL; + + m_enableXkbOptions = false; + m_resetOldOptions = false; + m_options = ""; + + m_layouts.clear(); + m_layouts.append( DEFAULT_LAYOUT_UNIT ); + + m_useKxkb = false; + m_showSingle = false; + m_showFlag = true; + + m_switchingPolicy = SWITCH_POLICY_GLOBAL; + + m_stickySwitching = false; + m_stickySwitchingDepth = 2; +} + +QStringList KxkbConfig::getLayoutStringList(/*bool compact*/) +{ + QStringList layoutList; + for(QValueList<LayoutUnit>::ConstIterator it = m_layouts.begin(); it != m_layouts.end(); ++it) { + const LayoutUnit& layoutUnit = *it; + layoutList.append( layoutUnit.toPair() ); + } + return layoutList; +} + + +QString KxkbConfig::getDefaultDisplayName(const QString& code_) +{ + QString displayName; + + if( code_.length() <= 2 ) { + displayName = code_; + } + else { + int sepPos = code_.find(QRegExp("[-_]")); + QString leftCode = code_.mid(0, sepPos); + QString rightCode; + if( sepPos != -1 ) + rightCode = code_.mid(sepPos+1); + + if( rightCode.length() > 0 ) + displayName = leftCode.left(2) + rightCode.left(1).lower(); + else + displayName = leftCode.left(3); + } + + return displayName; +} + +QString KxkbConfig::getDefaultDisplayName(const LayoutUnit& layoutUnit, bool single) +{ + if( layoutUnit.variant == "" ) + return getDefaultDisplayName( layoutUnit.layout ); + + QString displayName = layoutUnit.layout.left(2); + if( single == false ) + displayName += layoutUnit.variant.left(1); + return displayName; +} + +/** + * @brief Gets the single layout part of a layout(variant) string + * @param[in] layvar String in form layout(variant) to parse + * @return The layout found in the string + */ +const QString LayoutUnit::parseLayout(const QString &layvar) +{ + static const char* LAYOUT_PATTERN = "[a-zA-Z0-9_/-]*"; + QString varLine = layvar.stripWhiteSpace(); + QRegExp rx(LAYOUT_PATTERN); + int pos = rx.search(varLine, 0); + int len = rx.matchedLength(); + // check for errors + if( pos < 0 || len < 2 ) + return ""; +// kdDebug() << "getLayout: " << varLine.mid(pos, len) << endl; + return varLine.mid(pos, len); +} + +/** + * @brief Gets the single variant part of a layout(variant) string + * @param[in] layvar String in form layout(variant) to parse + * @return The variant found in the string, no check is performed + */ +const QString LayoutUnit::parseVariant(const QString &layvar) +{ + static const char* VARIANT_PATTERN = "\\([a-zA-Z0-9_-]*\\)"; + QString varLine = layvar.stripWhiteSpace(); + QRegExp rx(VARIANT_PATTERN); + int pos = rx.search(varLine, 0); + int len = rx.matchedLength(); + // check for errors + if( pos < 2 || len < 2 ) + return ""; + return varLine.mid(pos+1, len-2); +} diff --git a/kxkb/kxkbconfig.h b/kxkb/kxkbconfig.h new file mode 100644 index 000000000..df32b026c --- /dev/null +++ b/kxkb/kxkbconfig.h @@ -0,0 +1,122 @@ +// +// C++ Interface: kxkbconfig +// +// Description: +// +// +// Author: Andriy Rysin <rysin@kde.org>, (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef KXKBCONFIG_H +#define KXKBCONFIG_H + +#include <qstring.h> +#include <qstringlist.h> +#include <qptrqueue.h> +#include <qmap.h> + + +/* Utility classes for per-window/per-application layout implementation +*/ +enum SwitchingPolicy { + SWITCH_POLICY_GLOBAL = 0, + SWITCH_POLICY_WIN_CLASS = 1, + SWITCH_POLICY_WINDOW = 2, + SWITCH_POLICY_COUNT = 3 +}; + + + +inline QString createPair(QString key, QString value) +{ + if( value.isEmpty() ) + return key; + return QString("%1(%2)").arg(key, value); +} + +struct LayoutUnit { + QString layout; + QString variant; + QString includeGroup; + QString displayName; + int defaultGroup; + + LayoutUnit() {} + + LayoutUnit(QString layout_, QString variant_): + layout(layout_), + variant(variant_) + {} + + LayoutUnit(QString pair) { + setFromPair( pair ); + } + + void setFromPair(const QString& pair) { + layout = parseLayout(pair); + variant = parseVariant(pair); + } + + QString toPair() const { + return createPair(layout, variant); + } + + bool operator<(const LayoutUnit& lu) const { + return layout<lu.layout || + (layout==lu.layout && variant<lu.variant); + } + + bool operator!=(const LayoutUnit& lu) const { + return layout!=lu.layout || variant!=lu.variant; + } + + bool operator==(const LayoutUnit& lu) const { +// kdDebug() << layout << "==" << lu.layout << "&&" << variant << "==" << lu.variant << endl; + return layout==lu.layout && variant==lu.variant; + } + +//private: + static const QString parseLayout(const QString &layvar); + static const QString parseVariant(const QString &layvar); +}; + +extern const LayoutUnit DEFAULT_LAYOUT_UNIT; +extern const char* DEFAULT_MODEL; + + +class KxkbConfig +{ +public: + enum { LOAD_INIT_OPTIONS, LOAD_ACTIVE_OPTIONS, LOAD_ALL }; + + bool m_useKxkb; + bool m_showSingle; + bool m_showFlag; + bool m_enableXkbOptions; + bool m_resetOldOptions; + SwitchingPolicy m_switchingPolicy; + bool m_stickySwitching; + int m_stickySwitchingDepth; + + QString m_model; + QString m_options; + QValueList<LayoutUnit> m_layouts; + + LayoutUnit getDefaultLayout(); + + bool load(int loadMode); + void save(); + void setDefaults(); + + QStringList getLayoutStringList(/*bool compact*/); + static QString getDefaultDisplayName(const QString& code_); + static QString getDefaultDisplayName(const LayoutUnit& layoutUnit, bool single=false); + +private: + static const QMap<QString, QString> parseIncludesMap(const QStringList& pairList); +}; + + +#endif diff --git a/kxkb/kxkbtraywindow.cpp b/kxkb/kxkbtraywindow.cpp new file mode 100644 index 000000000..50fab7323 --- /dev/null +++ b/kxkb/kxkbtraywindow.cpp @@ -0,0 +1,130 @@ +// +// C++ Implementation: kxkbtraywindow +// +// Description: +// +// +// Author: Andriy Rysin <rysin@kde.org>, (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#include <qtooltip.h> + +#include <kdebug.h> +#include <klocale.h> +#include <kiconeffect.h> +#include <kiconloader.h> +#include <kpopupmenu.h> +#include <kaction.h> +#include <kuniqueapplication.h> + +#include "kxkbtraywindow.h" +#include "pixmap.h" +#include "rules.h" +#include "kxkbconfig.h" + + +KxkbLabelController::KxkbLabelController(QLabel* label_, QPopupMenu* contextMenu_) : + label(label_), + contextMenu(contextMenu_), + m_menuStartIndex(contextMenu_->count()), + m_prevLayoutCount(0) +{ +// kdDebug() << "Creating KxkbLabelController with " << label_ << ", " << contextMenu_ << endl; +// kdDebug() << "Creating KxkbLabelController with startMenuIndex " << m_menuStartIndex << endl; +} + +void KxkbLabelController::setToolTip(const QString& tip) +{ + QToolTip::remove(label); + QToolTip::add(label, tip); +} + +void KxkbLabelController::setPixmap(const QPixmap& pixmap) +{ + KIconEffect iconeffect; + label->setPixmap( iconeffect.apply(pixmap, KIcon::Panel, KIcon::DefaultState) ); +} + + +void KxkbLabelController::setCurrentLayout(const LayoutUnit& layoutUnit) +{ + setToolTip(m_descriptionMap[layoutUnit.toPair()]); + setPixmap( LayoutIcon::getInstance().findPixmap(layoutUnit.layout, m_showFlag, layoutUnit.displayName) ); +} + + +void KxkbLabelController::setError(const QString& layoutInfo) +{ + QString msg = i18n("Error changing keyboard layout to '%1'").arg(layoutInfo); + setToolTip(msg); + + label->setPixmap(LayoutIcon::getInstance().findPixmap("error", m_showFlag)); +} + + +void KxkbLabelController::initLayoutList(const QValueList<LayoutUnit>& layouts, const XkbRules& rules) +{ +// KPopupMenu* menu = contextMenu(); + QPopupMenu* menu = contextMenu; +// int index = menu->indexOf(0); + + m_descriptionMap.clear(); +// menu->clear(); +// menu->insertTitle( kapp->miniIcon(), kapp->caption() ); + + for(int ii=0; ii<m_prevLayoutCount; ++ii) { + menu->removeItem(START_MENU_ID + ii); + kdDebug() << "remove item: " << START_MENU_ID + ii << endl; + } +/* menu->removeItem(CONFIG_MENU_ID); + menu->removeItem(HELP_MENU_ID);*/ + + KIconEffect iconeffect; + + int cnt = 0; + QValueList<LayoutUnit>::ConstIterator it; + for (it=layouts.begin(); it != layouts.end(); ++it) + { + const QString layoutName = (*it).layout; + const QString variantName = (*it).variant; + + const QPixmap& layoutPixmap = LayoutIcon::getInstance().findPixmap(layoutName, m_showFlag, (*it).displayName); + const QPixmap pix = iconeffect.apply(layoutPixmap, KIcon::Small, KIcon::DefaultState); + + QString fullName = i18n((rules.layouts()[layoutName])); + if( variantName.isEmpty() == false ) + fullName += " (" + variantName + ")"; + contextMenu->insertItem(pix, fullName, START_MENU_ID + cnt, m_menuStartIndex + cnt); + m_descriptionMap.insert((*it).toPair(), fullName); + + cnt++; + } + + m_prevLayoutCount = cnt; + + // if show config, if show help + if( menu->indexOf(CONFIG_MENU_ID) == -1 ) { + contextMenu->insertSeparator(); + contextMenu->insertItem(SmallIcon("configure"), i18n("Configure..."), CONFIG_MENU_ID); + if( menu->indexOf(HELP_MENU_ID) == -1 ) + contextMenu->insertItem(SmallIcon("help"), i18n("Help"), HELP_MENU_ID); + } + +/* if( index != -1 ) { //not first start + menu->insertSeparator(); + KAction* quitAction = KStdAction::quit(this, SIGNAL(quitSelected()), actionCollection()); + if (quitAction) + quitAction->plug(menu); + }*/ +} + +// void KxkbLabelController::mouseReleaseEvent(QMouseEvent *ev) +// { +// if (ev->button() == QMouseEvent::LeftButton) +// emit toggled(); +// KSystemTray::mouseReleaseEvent(ev); +// } + +#include "kxkbtraywindow.moc" diff --git a/kxkb/kxkbtraywindow.h b/kxkb/kxkbtraywindow.h new file mode 100644 index 000000000..9c2c070c6 --- /dev/null +++ b/kxkb/kxkbtraywindow.h @@ -0,0 +1,92 @@ +// +// C++ Interface: kxkbtraywindow +// +// Description: +// +// +// Author: Andriy Rysin <rysin@kde.org>, (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// +#ifndef KXKBSYSTEMTRAY_H +#define KXKBSYSTEMTRAY_H + +#include <ksystemtray.h> + +#include <qstring.h> +#include <qvaluelist.h> + +#include "kxkbconfig.h" + + +class QLabel; +class QPopupMenu; +class XkbRules; + +/* This class is responsible for displaying flag/label for the layout, + catching keyboard/mouse events and displaying menu when selected +*/ + +class KxkbLabelController: public QObject +{ +// Q_OBJECT + +public: + enum { START_MENU_ID = 100, CONFIG_MENU_ID = 130, HELP_MENU_ID = 131 }; + + KxkbLabelController(QLabel *label, QPopupMenu* contextMenu); + + void initLayoutList(const QValueList<LayoutUnit>& layouts, const XkbRules& rule); + void setCurrentLayout(const LayoutUnit& layout); +// void setCurrentLayout(const QString& layout, const QString &variant); + void setError(const QString& layoutInfo=""); + void setShowFlag(bool showFlag) { m_showFlag = showFlag; } + void show() { label->show(); } + +// signals: +// +// void menuActivated(int); +// void toggled(); + +// protected: +// +// void mouseReleaseEvent(QMouseEvent *); + +private: + QLabel* label; + QPopupMenu* contextMenu; + + const int m_menuStartIndex; + bool m_showFlag; + int m_prevLayoutCount; + QMap<QString, QString> m_descriptionMap; + + void setToolTip(const QString& tip); + void setPixmap(const QPixmap& pixmap); +}; + + +class KxkbSystemTray : public KSystemTray +{ + Q_OBJECT + + public: + KxkbSystemTray(): + KSystemTray(NULL) + {} + + void mouseReleaseEvent(QMouseEvent *ev) + { + if (ev->button() == QMouseEvent::LeftButton) + emit toggled(); + KSystemTray::mouseReleaseEvent(ev); + } + + signals: + void menuActivated(int); + void toggled(); +}; + + +#endif diff --git a/kxkb/layoutmap.cpp b/kxkb/layoutmap.cpp new file mode 100644 index 000000000..f6273c967 --- /dev/null +++ b/kxkb/layoutmap.cpp @@ -0,0 +1,132 @@ +// +// C++ Implementation: layoutmap +// +// Description: +// +// +// Author: Andriy Rysin <rysin@kde.org>, (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#include "layoutmap.h" + +#include "x11helper.h" + + +LayoutMap::LayoutMap(const KxkbConfig& kxkbConfig_): + m_kxkbConfig(kxkbConfig_), + m_currentWinId( X11Helper::UNKNOWN_WINDOW_ID ) +{ +} + +// private +void LayoutMap::clearMaps() +{ + m_appLayouts.clear(); + m_winLayouts.clear(); + m_globalLayouts.clear(); + //setCurrentWindow( -1 ); +} + +void LayoutMap::reset() +{ + clearMaps(); + setCurrentWindow( X11Helper::UNKNOWN_WINDOW_ID ); +} + + + +void LayoutMap::setCurrentWindow(WId winId) +{ + m_currentWinId = winId; + if( m_kxkbConfig.m_switchingPolicy == SWITCH_POLICY_WIN_CLASS ) + m_currentWinClass = X11Helper::getWindowClass(winId, qt_xdisplay()); +} + +// private +//LayoutQueue& +QPtrQueue<LayoutState>& LayoutMap::getCurrentLayoutQueueInternal(WId winId) +{ + if( winId == X11Helper::UNKNOWN_WINDOW_ID ) + return m_globalLayouts; + + switch( m_kxkbConfig.m_switchingPolicy ) { + case SWITCH_POLICY_WIN_CLASS: { +// QString winClass = X11Helper::getWindowClass(winId, qt_xdisplay()); + return m_appLayouts[ m_currentWinClass ]; + } + case SWITCH_POLICY_WINDOW: + return m_winLayouts[ winId ]; + + default: + return m_globalLayouts; + } +} + +// private +//LayoutQueue& +QPtrQueue<LayoutState>& LayoutMap::getCurrentLayoutQueue(WId winId) +{ + QPtrQueue<LayoutState>& layoutQueue = getCurrentLayoutQueueInternal(winId); + if( layoutQueue.count() == 0 ) { + initLayoutQueue(layoutQueue); + kdDebug() << "map: Created queue for " << winId << " size: " << layoutQueue.count() << endl; + } + + return layoutQueue; +} + +LayoutState& LayoutMap::getCurrentLayout() { + return *getCurrentLayoutQueue(m_currentWinId).head(); +} + +LayoutState& LayoutMap::getNextLayout() { + LayoutQueue& layoutQueue = getCurrentLayoutQueue(m_currentWinId); + LayoutState* layoutState = layoutQueue.dequeue(); + layoutQueue.enqueue(layoutState); + + kdDebug() << "map: Next layout: " << layoutQueue.head()->layoutUnit.toPair() + << " group: " << layoutQueue.head()->layoutUnit.defaultGroup << " for " << m_currentWinId << endl; + + return *layoutQueue.head(); +} + +void LayoutMap::setCurrentGroup(int group) { + getCurrentLayout().group = group; +} + +void LayoutMap::setCurrentLayout(const LayoutUnit& layoutUnit) { + LayoutQueue& layoutQueue = getCurrentLayoutQueue(m_currentWinId); + kdDebug() << "map: Storing layout: " << layoutUnit.toPair() + << " group: " << layoutUnit.defaultGroup << " for " << m_currentWinId << endl; + + int queueSize = (int)layoutQueue.count(); + for(int ii=0; ii<queueSize; ii++) { + if( layoutQueue.head()->layoutUnit == layoutUnit ) + return; // if present return when it's in head + + LayoutState* layoutState = layoutQueue.dequeue(); + if( ii < queueSize - 1 ) { + layoutQueue.enqueue(layoutState); + } + else { + delete layoutState; + layoutQueue.enqueue(new LayoutState(layoutUnit)); + } + } + for(int ii=0; ii<queueSize - 1; ii++) { + LayoutState* layoutState = layoutQueue.dequeue(); + layoutQueue.enqueue(layoutState); + } +} + +// private +void LayoutMap::initLayoutQueue(LayoutQueue& layoutQueue) { + int queueSize = ( m_kxkbConfig.m_stickySwitching ) + ? m_kxkbConfig.m_stickySwitchingDepth : m_kxkbConfig.m_layouts.count(); + for(int ii=0; ii<queueSize; ii++) { + layoutQueue.enqueue( new LayoutState(m_kxkbConfig.m_layouts[ii]) ); + } +} diff --git a/kxkb/layoutmap.h b/kxkb/layoutmap.h new file mode 100644 index 000000000..198990327 --- /dev/null +++ b/kxkb/layoutmap.h @@ -0,0 +1,75 @@ +// +// C++ Interface: layoutmap +// +// Description: +// +// +// Author: Andriy Rysin <rysin@kde.org>, (C) 2006 +// +// Copyright: See COPYING file that comes with this distribution +// +// + +#ifndef __LAYOUTMAP_H +#define __LAYOUTMAP_H + +#include <assert.h> + +#include <qptrqueue.h> +#include <qmap.h> + +#include <kwinmodule.h> +#include <kdebug.h> + +#include "kxkbconfig.h" + + +// LayoutInfo is used for sticky switching and per-window/application switching policy +struct LayoutState { + const LayoutUnit& layoutUnit; + int group; + + LayoutState(const LayoutUnit& layoutUnit_): + layoutUnit(layoutUnit_), + group(layoutUnit_.defaultGroup) + { +// kdDebug() << "new LayoutState " << layoutUnit.toPair() << " group: " << group << endl; + } +}; + + +// LayoutMap is used for per-window or per-application switching policy +class LayoutMap { + typedef QPtrQueue<LayoutState> LayoutQueue; + typedef QMap<WId, LayoutQueue> WinLayoutMap; + typedef QMap<QString, LayoutQueue> WinClassLayoutMap; + +public: + LayoutMap(const KxkbConfig& kxkbConfig); +// void setConfig(const KxkbConfig& kxkbConfig); + + void setCurrentLayout(const LayoutUnit& layoutUnit); + void setCurrentGroup(int group); + LayoutState& getNextLayout(); + LayoutState& getCurrentLayout(); + + void setCurrentWindow(WId winId); + void reset(); + +private: + // pseudo-union + LayoutQueue m_globalLayouts; + WinLayoutMap m_winLayouts; + WinClassLayoutMap m_appLayouts; + + const KxkbConfig& m_kxkbConfig; + WId m_currentWinId; + QString m_currentWinClass; // only for SWITCH_POLICY_WIN_CLASS + + void initLayoutQueue(LayoutQueue& layoutQueue); + LayoutQueue& getCurrentLayoutQueue(WId winId); + LayoutQueue& getCurrentLayoutQueueInternal(WId winId); + void clearMaps(); +}; + +#endif diff --git a/kxkb/pics/Makefile.am b/kxkb/pics/Makefile.am new file mode 100644 index 000000000..a4b97f06a --- /dev/null +++ b/kxkb/pics/Makefile.am @@ -0,0 +1 @@ +KDE_ICON=AUTO diff --git a/kxkb/pics/hi16-app-kxkb.png b/kxkb/pics/hi16-app-kxkb.png Binary files differnew file mode 100644 index 000000000..cdc03064a --- /dev/null +++ b/kxkb/pics/hi16-app-kxkb.png diff --git a/kxkb/pics/hi32-app-kxkb.png b/kxkb/pics/hi32-app-kxkb.png Binary files differnew file mode 100644 index 000000000..c34e2ebc4 --- /dev/null +++ b/kxkb/pics/hi32-app-kxkb.png diff --git a/kxkb/pics/hi48-app-kxkb.png b/kxkb/pics/hi48-app-kxkb.png Binary files differnew file mode 100644 index 000000000..d9d7b78be --- /dev/null +++ b/kxkb/pics/hi48-app-kxkb.png diff --git a/kxkb/pixmap.cpp b/kxkb/pixmap.cpp new file mode 100644 index 000000000..37176b097 --- /dev/null +++ b/kxkb/pixmap.cpp @@ -0,0 +1,357 @@ +#include <qimage.h> +//#include <qbitmap.h> +#include <qfont.h> +#include <qpainter.h> +#include <qregexp.h> +#include <qdict.h> + +#include <kstandarddirs.h> +#include <klocale.h> +#include <kdebug.h> + +#include "pixmap.h" +#include "x11helper.h" +#include "kxkbconfig.h" + + +static const int FLAG_MAX_WIDTH = 21; +static const int FLAG_MAX_HEIGHT = 14; + +const QString LayoutIcon::flagTemplate("l10n/%1/flag.png"); +const QString& LayoutIcon::ERROR_CODE("error"); +LayoutIcon* LayoutIcon::instance; + + +LayoutIcon& LayoutIcon::getInstance() { + if( instance == NULL ) { + instance = new LayoutIcon(); + } + return *instance; +} + +LayoutIcon::LayoutIcon(): + m_pixmapCache(80), + m_labelFont("sans") +{ + m_labelFont.setPixelSize(10); + m_labelFont.setWeight(QFont::Bold); +} + +const QPixmap& +LayoutIcon::findPixmap(const QString& code_, bool showFlag, const QString& displayName_) +{ + QPixmap* pm = NULL; + + if( code_ == ERROR_CODE ) { + pm = m_pixmapCache[ERROR_CODE]; + if( pm == NULL ) { + pm = createErrorPixmap(); + m_pixmapCache.insert(ERROR_CODE, pm); + } + return *pm; + } + + QString displayName(displayName_); + + if( displayName.isEmpty() ) { + displayName = KxkbConfig::getDefaultDisplayName(code_); + } + if( displayName.length() > 3 ) + displayName = displayName.left(3); + + const QString pixmapKey( showFlag ? code_ + "." + displayName : displayName ); + + pm = m_pixmapCache[pixmapKey]; + if( pm ) + return *pm; + + QString flag; + if( showFlag ) { + QString countryCode = getCountryFromLayoutName( code_ ); + flag = locate("locale", flagTemplate.arg(countryCode)); + } + + if( flag.isEmpty() ) { + pm = new QPixmap(FLAG_MAX_WIDTH, FLAG_MAX_HEIGHT); + pm->fill(Qt::gray); + } + else { + pm = new QPixmap(flag); + dimPixmap( *pm ); + +#if 0 + if( pm->height() < FLAG_MAX_HEIGHT ) { + QPixmap* pix = new QPixmap(FLAG_MAX_WIDTH, FLAG_MAX_HEIGHT); + pix->fill( Qt::lightGray ); +// pix->fill( QColor(qRgba(127,127,127,255)) ); +// QBitmap mask; +// mask.fill(1); +// pix->setMask(mask); + + int dy = (pix->height() - pm->height()) / 2; + copyBlt( pix, 0, dy, pm, 0, 0, -1, -1 ); +// QPixmap* px = new QPixmap(21, 14); +// px->convertFromImage(img);*/ + delete pm; + pm = pix; + } +#endif + } + + QPainter p(pm); + p.setFont(m_labelFont); + + p.setPen(Qt::black); + p.drawText(1, 1, pm->width(), pm->height()-2, Qt::AlignCenter, displayName); + p.setPen(Qt::white); + p.drawText(0, 0, pm->width(), pm->height()-2, Qt::AlignCenter, displayName); + + m_pixmapCache.insert(pixmapKey, pm); + + return *pm; +} + +/** +@brief Try to get country code from layout name in xkb before xorg 6.9.0 +*/ +QString LayoutIcon::getCountryFromLayoutName(const QString& layoutName) +{ + QString flag; + + if( X11Helper::areLayoutsClean() ) { // >= Xorg 6.9.0 + if( layoutName == "mkd" ) + flag = "mk"; + else + if( layoutName == "srp" ) { + QString csFlagFile = locate("locale", flagTemplate.arg("cs")); + flag = csFlagFile.isEmpty() ? "yu" : "cs"; + } + else + if( layoutName.endsWith("/jp") ) + flag = "jp"; + else + if( layoutName == "trq" || layoutName == "trf" || layoutName == "tralt" ) + flag = "tr"; + else + if( layoutName.length() > 2 ) + flag = ""; + else + flag = layoutName; + } + else { + if( layoutName == "ar" ) // Arabic - not argentina + ; + else + if( layoutName == "sr" || layoutName == "cs") // Serbian language - Yugoslavia + flag = "yu"; + else + if( layoutName == "bs" ) // Bosnian language - Bosnia + flag = "ba"; + else + if( layoutName == "la" ) // Latin America + ; + else + if( layoutName == "lo" ) // Lao + flag = "la"; + else + if( layoutName == "pl2" ) // Poland + flag = "pl"; + else + if( layoutName == "iu" ) // Inuktitut - Canada + flag = "ca"; + else + if( layoutName == "syr" ) // Syriac + flag = "sy"; + else + if( layoutName == "dz" ) // Dzongka/Tibetian - Buthan + flag = "bt"; + else + if( layoutName == "ogham" ) // Ogham - Ireland + flag = "ie"; + else + if( layoutName == "ge_la" || layoutName == "ge_ru" ) + flag = "ge"; + else + if( layoutName == "el" ) + flag = "gr"; + else + if( layoutName.endsWith("/jp") ) + flag = "jp"; + else + if( layoutName == "ml" || layoutName == "dev" || layoutName == "gur" + || layoutName == "guj" || layoutName == "kan" || layoutName == "ori" + || layoutName == "tel" || layoutName == "tml" || layoutName == "ben" ) // some Indian languages + flag = "in"; + else { + int sepPos = layoutName.find(QRegExp("[-_]")); + QString leftCode = layoutName.mid(0, sepPos); + QString rightCode; + if( sepPos != -1 ) + rightCode = layoutName.mid(sepPos+1); +// kdDebug() << "layout name breakup: " << leftCode << ":" << rightCode << endl; + + if( rightCode.length() == 2 + && QRegExp("[A-Z][A-Z]").exactMatch(rightCode) ) { + flag = rightCode.lower(); + } + else { + flag = leftCode.length() == 2 ? leftCode : ""; + } + } + } + + return flag; +} + + +void LayoutIcon::dimPixmap(QPixmap& pm) +{ + QImage image = pm.convertToImage(); + for (int y=0; y<image.height(); y++) + for(int x=0; x<image.width(); x++) + { + QRgb rgb = image.pixel(x,y); + QRgb dimRgb(qRgb(qRed(rgb)*3/4, qGreen(rgb)*3/4, qBlue(rgb)*3/4)); + image.setPixel(x, y, dimRgb); + } + pm.convertFromImage(image); +} + +static const char* ERROR_LABEL = "err"; + +//private +QPixmap* LayoutIcon::createErrorPixmap() +{ + QPixmap* pm = new QPixmap(21, 14); + pm->fill(Qt::white); + + QPainter p(pm); + + p.setFont(m_labelFont); + p.setPen(Qt::red); + p.drawText(1, 1, pm->width(), pm->height()-2, Qt::AlignCenter, ERROR_LABEL); + p.setPen(Qt::blue); + p.drawText(0, 0, pm->width(), pm->height()-2, Qt::AlignCenter, ERROR_LABEL); + m_pixmapCache.insert(ERROR_CODE, pm); + + return pm; +} + + +// Note: this seems stupid, but allows for translations +#if 0 + I18N_NOOP("Belgian"); + I18N_NOOP("Bulgarian"); + I18N_NOOP("Brazilian"); + I18N_NOOP("Canadian"); + I18N_NOOP("Czech"); + I18N_NOOP("Czech (qwerty)"); + I18N_NOOP("Danish"); + I18N_NOOP("Estonian"); + I18N_NOOP("Finnish"); + I18N_NOOP("French"); + I18N_NOOP("German"); + I18N_NOOP("Hungarian"); + I18N_NOOP("Hungarian (qwerty)"); + I18N_NOOP("Italian"); + I18N_NOOP("Japanese"); + I18N_NOOP("Lithuanian"); + I18N_NOOP("Norwegian"); + I18N_NOOP("PC-98xx Series"); + I18N_NOOP("Polish"); + I18N_NOOP("Portuguese"); + I18N_NOOP("Romanian"); + I18N_NOOP("Russian"); + I18N_NOOP("Slovak"); + I18N_NOOP("Slovak (qwerty)"); + I18N_NOOP("Spanish"); + I18N_NOOP("Swedish"); + I18N_NOOP("Swiss German"); + I18N_NOOP("Swiss French"); + I18N_NOOP("Thai"); + I18N_NOOP("United Kingdom"); + I18N_NOOP("U.S. English"); + I18N_NOOP("U.S. English w/ deadkeys"); + I18N_NOOP("U.S. English w/ISO9995-3"); + + //lukas: these seem to be new in XF 4.0.2 + I18N_NOOP("Armenian"); + I18N_NOOP("Azerbaijani"); + I18N_NOOP("Icelandic"); + I18N_NOOP("Israeli"); + I18N_NOOP("Lithuanian azerty standard"); + I18N_NOOP("Lithuanian querty \"numeric\""); //for bw compatibility + I18N_NOOP("Lithuanian querty \"programmer's\""); + I18N_NOOP("Macedonian"); + I18N_NOOP("Serbian"); + I18N_NOOP("Slovenian"); + I18N_NOOP("Vietnamese"); + + //these seem to be new in XFree86 4.1.0 + I18N_NOOP("Arabic"); + I18N_NOOP("Belarusian"); + I18N_NOOP("Bengali"); + I18N_NOOP("Croatian"); + I18N_NOOP("Greek"); + I18N_NOOP("Latvian"); + I18N_NOOP("Lithuanian qwerty \"numeric\""); + I18N_NOOP("Lithuanian qwerty \"programmer's\""); + I18N_NOOP("Turkish"); + I18N_NOOP("Ukrainian"); + + //these seem to be new in XFree86 4.2.0 + I18N_NOOP("Albanian"); + I18N_NOOP("Burmese"); + I18N_NOOP("Dutch"); + I18N_NOOP("Georgian (latin)"); + I18N_NOOP("Georgian (russian)"); + I18N_NOOP("Gujarati"); + I18N_NOOP("Gurmukhi"); + I18N_NOOP("Hindi"); + I18N_NOOP("Inuktitut"); + I18N_NOOP("Iranian"); +// I18N_NOOP("Iranian"); // should be not Iranian but Farsi + I18N_NOOP("Latin America"); + I18N_NOOP("Maltese"); + I18N_NOOP("Maltese (US layout)"); + I18N_NOOP("Northern Saami (Finland)"); + I18N_NOOP("Northern Saami (Norway)"); + I18N_NOOP("Northern Saami (Sweden)"); + I18N_NOOP("Polish (qwertz)"); + I18N_NOOP("Russian (cyrillic phonetic)"); + I18N_NOOP("Tajik"); + I18N_NOOP("Turkish (F)"); + I18N_NOOP("U.S. English w/ ISO9995-3"); + I18N_NOOP("Yugoslavian"); + + //these seem to be new in XFree86 4.3.0 + I18N_NOOP("Bosnian"); + I18N_NOOP("Croatian (US)"); + I18N_NOOP("Dvorak"); + I18N_NOOP("French (alternative)"); + I18N_NOOP("French Canadian"); + I18N_NOOP("Kannada"); + I18N_NOOP("Lao"); + I18N_NOOP("Malayalam"); + I18N_NOOP("Mongolian"); + I18N_NOOP("Ogham"); + I18N_NOOP("Oriya"); + I18N_NOOP("Syriac"); + I18N_NOOP("Telugu"); + I18N_NOOP("Thai (Kedmanee)"); + I18N_NOOP("Thai (Pattachote)"); + I18N_NOOP("Thai (TIS-820.2538)"); + + //these seem to be new in XFree86 4.4.0 + I18N_NOOP("Uzbek"); + I18N_NOOP("Faroese"); + + //these seem to be new in XOrg 6.8.2 + I18N_NOOP("Dzongkha / Tibetan"); + I18N_NOOP("Hungarian (US)"); + I18N_NOOP("Irish"); + I18N_NOOP("Israeli (phonetic)"); + I18N_NOOP("Serbian (Cyrillic)"); + I18N_NOOP("Serbian (Latin)"); + I18N_NOOP("Swiss"); +#endif diff --git a/kxkb/pixmap.h b/kxkb/pixmap.h new file mode 100644 index 000000000..42764c04f --- /dev/null +++ b/kxkb/pixmap.h @@ -0,0 +1,31 @@ +#ifndef __PIXMAP_H__ +#define __PIXMAP_H__ + + +#include <qpixmap.h> +#include <qdict.h> +#include <qstring.h> + + +class LayoutIcon { + +private: + static LayoutIcon* instance; + static const QString flagTemplate; + + QDict<QPixmap> m_pixmapCache; + QFont m_labelFont; + + LayoutIcon(); + QPixmap* createErrorPixmap(); + void dimPixmap(QPixmap& pixmap); + QString getCountryFromLayoutName(const QString& layoutName); + + public: + static const QString& ERROR_CODE; + + static LayoutIcon& getInstance(); + const QPixmap& findPixmap(const QString& code, bool showFlag, const QString& displayName=""); +}; + +#endif diff --git a/kxkb/rules.cpp b/kxkb/rules.cpp new file mode 100644 index 000000000..85c93f72b --- /dev/null +++ b/kxkb/rules.cpp @@ -0,0 +1,156 @@ +#include <qwindowdefs.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qregexp.h> +#include <qstringlist.h> +#include <qdir.h> + +#include <kstandarddirs.h> +#include <kglobal.h> +#include <klocale.h> +#include <kdebug.h> +#include <config.h> + +#include "x11helper.h" +#include "rules.h" + + +XkbRules::XkbRules(bool layoutsOnly): + m_layouts(90) +{ + X11_DIR = X11Helper::findX11Dir(); + + if( X11_DIR == NULL ) { + kdError() << "Cannot find X11 directory!" << endl; +// throw Exception(); + return; + } + + QString rulesFile = X11Helper::findXkbRulesFile(X11_DIR, qt_xdisplay()); + + if( rulesFile.isEmpty() ) { + kdError() << "Cannot find rules file in " << X11_DIR << endl; +// throw Exception(); + return; + } + + loadRules(rulesFile, layoutsOnly); + loadOldLayouts(rulesFile); + loadGroups(::locate("config", "kxkb_groups")); +} + + +void XkbRules::loadRules(QString file, bool layoutsOnly) +{ + RulesInfo* rules = X11Helper::loadRules(file, layoutsOnly); + + if (rules == NULL) { + kdDebug() << "Unable to load rules" << endl; + return; + } + + m_layouts= rules->layouts; + if( layoutsOnly == false ) { + m_models = rules->models; + m_options = rules->options; + } + + // fixLayouts(); +} + +// void XkbRules::fixLayouts() { +// // THIS IS TEMPORARY!!! +// // This should be fixed in XFree86 (and actually is fixed in XFree 4.2) +// // some handcoded ones, because the X11 rule file doesn't get them correctly, or in case +// // the rule file wasn't found +// static struct { +// const char * locale; +// const char * layout; +// } fixedLayouts[] = { +// { "ben", "Bengali" }, +// { "ar", "Arabic" }, +// { "ir", "Farsi" }, +// { 0, 0 } +// }; +// +// for(int i=0; fixedLayouts[i].layout != 0; i++ ) { +// if( m_layouts.find(fixedLayouts[i].locale) == 0 ) +// m_layouts.insert(fixedLayouts[i].locale, fixedLayouts[i].layout); +// } +// } + +bool XkbRules::isSingleGroup(const QString& layout) +{ + return X11Helper::areSingleGroupsSupported() + && !m_oldLayouts.contains(layout) + && !m_nonLatinLayouts.contains(layout); +} + + +// check $oldlayouts and $nonlatin groups for XFree 4.3 and later +void XkbRules::loadOldLayouts(QString rulesFile) +{ + OldLayouts* oldLayoutsStruct = X11Helper::loadOldLayouts( rulesFile ); + m_oldLayouts = oldLayoutsStruct->oldLayouts; + m_nonLatinLayouts = oldLayoutsStruct->nonLatinLayouts; +} + +// for multi-group layouts in XFree 4.2 and older +// or if layout is present in $oldlayout or $nonlatin groups +void XkbRules::loadGroups(QString file) +{ + QFile f(file); + if (f.open(IO_ReadOnly)) + { + QTextStream ts(&f); + QString locale; + unsigned int grp; + + while (!ts.eof()) { + ts >> locale >> grp; + locale.simplifyWhiteSpace(); + + if (locale[0] == '#' || locale.left(2) == "//" || locale.isEmpty()) + continue; + + m_initialGroups.insert(locale, grp); + } + + f.close(); + } +} + +unsigned int +XkbRules::getDefaultGroup(const QString& layout, const QString& includeGroup) +{ +// check for new one-group layouts in XFree 4.3 and older + if( isSingleGroup(layout) ) { + if( includeGroup.isEmpty() == false ) + return 1; + else + return 0; + } + + QMap<QString, unsigned int>::iterator it = m_initialGroups.find(layout); + return it == m_initialGroups.end() ? 0 : it.data(); +} + + +QStringList +XkbRules::getAvailableVariants(const QString& layout) +{ + if( layout.isEmpty() || !layouts().find(layout) ) + return QStringList(); + + QStringList* result1 = m_varLists[layout]; + if( result1 ) + return *result1; + + bool oldLayouts = m_oldLayouts.contains(layout); + QStringList* result = X11Helper::getVariants(layout, X11_DIR, oldLayouts); + + m_varLists.insert(layout, result); + + return *result; +} + diff --git a/kxkb/rules.h b/kxkb/rules.h new file mode 100644 index 000000000..d3e72b7a6 --- /dev/null +++ b/kxkb/rules.h @@ -0,0 +1,46 @@ +#ifndef __RULES_H__ +#define __RULES_H__ + +#include <qstring.h> +#include <qdict.h> +#include <qmap.h> + + +class XkbRules +{ +public: + + XkbRules(bool layoutsOnly=false); + + const QDict<char> &models() const { return m_models; }; + const QDict<char> &layouts() const { return m_layouts; }; + const QDict<char> &options() const { return m_options; }; + + QStringList getAvailableVariants(const QString& layout); + unsigned int getDefaultGroup(const QString& layout, const QString& includeGroup); + + bool isSingleGroup(const QString& layout); + +protected: + + void loadRules(QString filename, bool layoutsOnly=false); + void loadGroups(QString filename); + void loadOldLayouts(QString filename); + +private: + + QDict<char> m_models; + QDict<char> m_layouts; + QDict<char> m_options; + QMap<QString, unsigned int> m_initialGroups; + QDict<QStringList> m_varLists; + QStringList m_oldLayouts; + QStringList m_nonLatinLayouts; + + QString X11_DIR; // pseudo-constant + +// void fixLayouts(); +}; + + +#endif diff --git a/kxkb/x11helper.cpp b/kxkb/x11helper.cpp new file mode 100644 index 000000000..7e3c83c0b --- /dev/null +++ b/kxkb/x11helper.cpp @@ -0,0 +1,320 @@ +#include <qdir.h> +#include <qstring.h> +#include <qwindowdefs.h> +#include <qstring.h> +#include <qstringlist.h> +#include <qdict.h> +#include <qregexp.h> + +#include <kdebug.h> + +#include <X11/Xlib.h> +#include <X11/Xatom.h> +#define explicit int_explicit // avoid compiler name clash in XKBlib.h +#include <X11/XKBlib.h> +#undef explicit +#include <X11/extensions/XKBrules.h> + +#include "x11helper.h" +#include "config.h" + + +// Compiler will size array automatically. +static const char* X11DirList[] = + { + XLIBDIR, + "/usr/share/X11/", + "/etc/X11/", + "/usr/local/share/X11/", + "/usr/X11R6/lib/X11/", + "/usr/X11R6/lib64/X11/", + "/usr/local/X11R6/lib/X11/", + "/usr/local/X11R6/lib64/X11/", + "/usr/lib/X11/", + "/usr/lib64/X11/", + "/usr/local/lib/X11/", + "/usr/local/lib64/X11/", + "/usr/pkg/share/X11/", + "/usr/pkg/xorg/lib/X11/" + }; + +// Compiler will size array automatically. +static const char* rulesFileList[] = + { + "xkb/rules/xorg", + "xkb/rules/xfree86" + }; + +// Macro will return number of elements in any static array as long as the +// array has at least one element. +#define ARRAY_SIZE(array) (sizeof(array) / sizeof(array[0])) + +static const int X11_DIR_COUNT = ARRAY_SIZE(X11DirList); +static const int X11_RULES_COUNT = ARRAY_SIZE(rulesFileList); + +const QString X11Helper::X11_WIN_CLASS_ROOT = "<root>"; +const QString X11Helper::X11_WIN_CLASS_UNKNOWN = "<unknown>"; + +static const QRegExp NON_CLEAN_LAYOUT_REGEXP("[^a-z]"); + +bool X11Helper::m_layoutsClean = true; + +const QString +X11Helper::findX11Dir() +{ + for(int ii=0; ii<X11_DIR_COUNT; ii++) { + const char* xDir = X11DirList[ii]; + if( xDir != NULL && QDir(QString(xDir) + "xkb").exists() ) { +// for(int jj=0; jj<X11_RULES_COUNT; jj++) { +// +// } + return QString(xDir); + } + +// if( X11_DIR.isEmpty() ) { +// return; +// } + } + return NULL; +} + +const QString +X11Helper::findXkbRulesFile(QString x11Dir, Display *dpy) +{ + QString rulesFile; + XkbRF_VarDefsRec vd; + char *tmp = NULL; + + if (XkbRF_GetNamesProp(dpy, &tmp, &vd) && tmp != NULL ) { +// kdDebug() << "namesprop " << tmp << endl; + rulesFile = x11Dir + QString("xkb/rules/%1").arg(tmp); +// kdDebug() << "rulesF " << rulesFile << endl; + } + else { + // old way + for(int ii=0; ii<X11_RULES_COUNT; ii++) { + const char* ruleFile = rulesFileList[ii]; + QString xruleFilePath = x11Dir + ruleFile; +// kdDebug() << "trying " << xruleFilePath << endl; + if( QFile(xruleFilePath).exists() ) { + rulesFile = xruleFilePath; + break; + } + } + } + + return rulesFile; +} + +RulesInfo* +X11Helper::loadRules(const QString& file, bool layoutsOnly) +{ + XkbRF_RulesPtr xkbRules = XkbRF_Load(QFile::encodeName(file).data(), "", true, true); + + if (xkbRules == NULL) { +// throw Exception + return NULL; + } + + RulesInfo* rulesInfo = new RulesInfo(); + + for (int i = 0; i < xkbRules->layouts.num_desc; ++i) { + QString layoutName(xkbRules->layouts.desc[i].name); + rulesInfo->layouts.replace( layoutName, qstrdup( xkbRules->layouts.desc[i].desc ) ); + + if( m_layoutsClean == true + && layoutName.find( NON_CLEAN_LAYOUT_REGEXP ) != -1 + && layoutName.endsWith("/jp") == false ) { + kdDebug() << "Layouts are not clean (Xorg < 6.9.0 or XFree86)" << endl; + m_layoutsClean = false; + } + } + + if( layoutsOnly == true ) { + XkbRF_Free(xkbRules, true); + return rulesInfo; + } + + for (int i = 0; i < xkbRules->models.num_desc; ++i) + rulesInfo->models.replace(xkbRules->models.desc[i].name, qstrdup( xkbRules->models.desc[i].desc ) ); + for (int i = 0; i < xkbRules->options.num_desc; ++i) + rulesInfo->options.replace(xkbRules->options.desc[i].name, qstrdup( xkbRules->options.desc[i].desc ) ); + + XkbRF_Free(xkbRules, true); + +// workaround for empty 'compose' options group description + if( rulesInfo->options.find("compose:menu") && !rulesInfo->options.find("compose") ) { + rulesInfo->options.replace("compose", "Compose Key Position"); + } + + for(QDictIterator<char> it(rulesInfo->options) ; it.current() != NULL; ++it ) { + QString option(it.currentKey()); + int columnPos = option.find(":"); + + if( columnPos != -1 ) { + QString group = option.mid(0, columnPos); + if( rulesInfo->options.find(group) == NULL ) { + rulesInfo->options.replace(group, group.latin1()); + kdDebug() << "Added missing option group: " << group << endl; + } + } + } + +// // workaround for empty misc options group description in XFree86 4.4.0 +// if( rulesInfo->options.find("numpad:microsoft") && !rulesInfo->options.find("misc") ) { +// rulesInfo->options.replace("misc", "Miscellaneous compatibility options" ); +// } + + return rulesInfo; +} + +// check $oldlayouts and $nonlatin groups for XFree 4.3 and later +OldLayouts* +X11Helper::loadOldLayouts(const QString& rulesFile) +{ + static const char* oldLayoutsTag = "! $oldlayouts"; + static const char* nonLatinLayoutsTag = "! $nonlatin"; + QStringList m_oldLayouts; + QStringList m_nonLatinLayouts; + + QFile f(rulesFile); + + if (f.open(IO_ReadOnly)) + { + QTextStream ts(&f); + QString line; + + while (!ts.eof()) { + line = ts.readLine().simplifyWhiteSpace(); + + if( line.find(oldLayoutsTag) == 0 ) { + + line = line.mid(strlen(oldLayoutsTag)); + line = line.mid(line.find('=')+1).simplifyWhiteSpace(); + while( !ts.eof() && line.endsWith("\\") ) + line = line.left(line.length()-1) + ts.readLine(); + line = line.simplifyWhiteSpace(); + + m_oldLayouts = QStringList::split(QRegExp("\\s"), line); +// kdDebug() << "oldlayouts " << m_oldLayouts.join("|") << endl; + if( !m_nonLatinLayouts.empty() ) + break; + + } + else + if( line.find(nonLatinLayoutsTag) == 0 ) { + + line = line.mid(strlen(nonLatinLayoutsTag)+1).simplifyWhiteSpace(); + line = line.mid(line.find('=')+1).simplifyWhiteSpace(); + while( !ts.eof() && line.endsWith("\\") ) + line = line.left(line.length()-1) + ts.readLine(); + line = line.simplifyWhiteSpace(); + + m_nonLatinLayouts = QStringList::split(QRegExp("\\s"), line); +// kdDebug() << "nonlatin " << m_nonLatinLayouts.join("|") << endl; + if( !m_oldLayouts.empty() ) + break; + + } + } + + f.close(); + } + + OldLayouts* oldLayoutsStruct = new OldLayouts(); + oldLayoutsStruct->oldLayouts = m_oldLayouts; + oldLayoutsStruct->nonLatinLayouts = m_nonLatinLayouts; + + return oldLayoutsStruct; +} + + +/* pretty simple algorithm - reads the layout file and + tries to find "xkb_symbols" + also checks whether previous line contains "hidden" to skip it +*/ +QStringList* +X11Helper::getVariants(const QString& layout, const QString& x11Dir, bool oldLayouts) +{ + QStringList* result = new QStringList(); + + QString file = x11Dir + "xkb/symbols/"; + // workaround for XFree 4.3 new directory for one-group layouts + if( QDir(file+"pc").exists() && !oldLayouts ) + file += "pc/"; + + file += layout; + +// kdDebug() << "reading variants from " << file << endl; + + QFile f(file); + if (f.open(IO_ReadOnly)) + { + QTextStream ts(&f); + + QString line; + QString prev_line; + + while (!ts.eof()) { + prev_line = line; + line = ts.readLine().simplifyWhiteSpace(); + + if (line[0] == '#' || line.left(2) == "//" || line.isEmpty()) + continue; + + int pos = line.find("xkb_symbols"); + if (pos < 0) + continue; + + if( prev_line.find("hidden") >=0 ) + continue; + + pos = line.find('"', pos) + 1; + int pos2 = line.find('"', pos); + if( pos < 0 || pos2 < 0 ) + continue; + + result->append(line.mid(pos, pos2-pos)); +// kdDebug() << "adding variant " << line.mid(pos, pos2-pos) << endl; + } + + f.close(); + } + + return result; +} + +QString +X11Helper::getWindowClass(WId winId, Display* dpy) +{ + unsigned long nitems_ret, bytes_after_ret; + unsigned char* prop_ret; + Atom type_ret; + int format_ret; + Window w = (Window)winId; // suppose WId == Window + QString property; + + if( winId == X11Helper::UNKNOWN_WINDOW_ID ) { + kdDebug() << "Got window class for " << winId << ": '" << X11_WIN_CLASS_ROOT << "'" << endl; + return X11_WIN_CLASS_ROOT; + } + +// kdDebug() << "Getting window class for " << winId << endl; + if((XGetWindowProperty(dpy, w, XA_WM_CLASS, 0L, 256L, 0, XA_STRING, + &type_ret, &format_ret, &nitems_ret, + &bytes_after_ret, &prop_ret) == Success) && (type_ret != None)) { + property = QString::fromLocal8Bit(reinterpret_cast<char*>(prop_ret)); + XFree(prop_ret); + } + else { + property = X11_WIN_CLASS_UNKNOWN; + } + kdDebug() << "Got window class for " << winId << ": '" << property << "'" << endl; + + return property; +} + +bool X11Helper::areSingleGroupsSupported() +{ + return true; //TODO: +} diff --git a/kxkb/x11helper.h b/kxkb/x11helper.h new file mode 100644 index 000000000..a4faba899 --- /dev/null +++ b/kxkb/x11helper.h @@ -0,0 +1,41 @@ +#ifndef X11HELPER_H_ +#define X11HELPER_H_ + +#include <qdict.h> +#include <qstringlist.h> + + +struct RulesInfo { + QDict<char> models; + QDict<char> layouts; + QDict<char> options; +}; + +struct OldLayouts { + QStringList oldLayouts; + QStringList nonLatinLayouts; +}; + +class X11Helper +{ + static bool m_layoutsClean; + +public: + static const WId UNKNOWN_WINDOW_ID = (WId) 0; + static const QString X11_WIN_CLASS_ROOT; + static const QString X11_WIN_CLASS_UNKNOWN; + /** + * Tries to find X11 xkb config dir + */ + static const QString findX11Dir(); + static const QString findXkbRulesFile(QString x11Dir, Display* dpy); + static QString getWindowClass(WId winId, Display* dpy); + static QStringList* getVariants(const QString& layout, const QString& x11Dir, bool oldLayouts=false); + static RulesInfo* loadRules(const QString& rulesFile, bool layoutsOnly=false); + static OldLayouts* loadOldLayouts(const QString& rulesFile); + + static bool areLayoutsClean() { return m_layoutsClean; } + static bool areSingleGroupsSupported(); +}; + +#endif /*X11HELPER_H_*/ |