-rw-r--r-- | library/qpeapplication.cpp | 8 |
1 files changed, 2 insertions, 6 deletions
diff --git a/library/qpeapplication.cpp b/library/qpeapplication.cpp index df313ce..3efba20 100644 --- a/library/qpeapplication.cpp +++ b/library/qpeapplication.cpp @@ -1,346 +1,347 @@ /********************************************************************** ** Copyright (C) 2000-2002 Trolltech AS. All rights reserved. ** ** This file is part of the Qtopia Environment. ** ** This file may be distributed and/or modified under the terms of the ** GNU General Public License version 2 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. ** ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ** ** See http://www.trolltech.com/gpl/ for GPL licensing information. ** ** Contact info@trolltech.com if any conditions of this licensing are ** not clear to you. ** */ #define QTOPIA_INTERNAL_LANGLIST #include <stdlib.h> #include <unistd.h> #ifndef Q_OS_MACX #include <linux/limits.h> // needed for some toolchains (PATH_MAX) #endif #include <qfile.h> #include <qqueue.h> #ifdef Q_WS_QWS #ifndef QT_NO_COP #if QT_VERSION <= 231 #define private public #define sendLocally processEvent #include "qcopenvelope_qws.h" #undef private #else #include "qcopenvelope_qws.h" #endif #endif #include <qwindowsystem_qws.h> #endif #include <qtextstream.h> #include <qpalette.h> #include <qbuffer.h> #include <qptrdict.h> #include <qregexp.h> #include <qdir.h> #include <qlabel.h> #include <qdialog.h> #include <qdragobject.h> #include <qtextcodec.h> #include <qevent.h> #include <qtooltip.h> #include <qsignal.h> #include <qmainwindow.h> #include <qwidgetlist.h> #include <qpixmapcache.h> #if defined(Q_WS_QWS) && !defined(QT_NO_COP) #define QTOPIA_INTERNAL_INITAPP #include "qpeapplication.h" #include "qpestyle.h" #include "styleinterface.h" #if QT_VERSION >= 300 #include <qstylefactory.h> #else #include <qplatinumstyle.h> #include <qwindowsstyle.h> #include <qmotifstyle.h> #include <qmotifplusstyle.h> #include "lightstyle.h" #include <qpe/qlibrary.h> #endif #include "global.h" #include "resource.h" #if QT_VERSION <= 230 && defined(QT_NO_CODECS) #include "qutfcodec.h" #endif #include "config.h" #include "network.h" #ifdef QWS #include "fontmanager.h" #include "fontdatabase.h" #endif #include "alarmserver.h" #include "applnk.h" #include "qpemenubar.h" #include "textcodecinterface.h" #include "imagecodecinterface.h" +#include <qtopia/qpeglobal.h> #include <unistd.h> #include <sys/file.h> #include <sys/ioctl.h> #ifndef QT_NO_SOUND #include <sys/soundcard.h> #endif #include "qt_override_p.h" #include <backend/rohfeedback.h> static bool useBigPixmaps = 0; class HackWidget : public QWidget { public: bool needsOk() { return (getWState() & WState_Reserved1 ); } QRect normalGeometry() { return topData()->normalGeometry; }; }; class QPEApplicationData { public: QPEApplicationData ( ) : presstimer( 0 ), presswidget( 0 ), rightpressed( false ), kbgrabbed( false ), notbusysent( false ), preloaded( false ), forceshow( false ), nomaximize( false ), keep_running( true ), qcopQok( false ), fontFamily( "Vera" ), fontSize( 10 ), smallIconSize( 14 ), bigIconSize( 32 ), qpe_main_widget( 0 ) { Config cfg( "qpe" ); cfg.setGroup( "Appearance" ); useBigPixmaps = cfg.readBoolEntry( "useBigPixmaps", false ); fontFamily = cfg.readEntry( "FontFamily", "Vera" ); fontSize = cfg.readNumEntry( "FontSize", 10 ); smallIconSize = cfg.readNumEntry( "SmallIconSize", 14 ); bigIconSize = cfg.readNumEntry( "BigIconSize", 32 ); #ifdef OPIE_WITHROHFEEDBACK RoH = 0; #endif } int presstimer; QWidget* presswidget; QPoint presspos; #ifdef OPIE_WITHROHFEEDBACK Opie::Internal::RoHFeedback *RoH; #endif bool rightpressed : 1; bool kbgrabbed : 1; bool notbusysent : 1; bool preloaded : 1; bool forceshow : 1; bool nomaximize : 1; bool keep_running : 1; bool qcopQok : 1; QCString fontFamily; int fontSize; int smallIconSize; int bigIconSize; QStringList langs; QString appName; struct QCopRec { QCopRec( const QCString &ch, const QCString &msg, const QByteArray &d ) : channel( ch ), message( msg ), data( d ) { } QCString channel; QCString message; QByteArray data; }; QWidget* qpe_main_widget; QGuardedPtr<QWidget> lastraised; QQueue<QCopRec> qcopq; QString styleName; QString decorationName; void enqueueQCop( const QCString &ch, const QCString &msg, const QByteArray &data ) { qcopq.enqueue( new QCopRec( ch, msg, data ) ); } void sendQCopQ() { if (!qcopQok ) return; QCopRec * r; while((r=qcopq.dequeue())) { // remove from queue before sending... // event loop can come around again before getting // back from sendLocally #ifndef QT_NO_COP QCopChannel::sendLocally( r->channel, r->message, r->data ); #endif delete r; } } static void show_mx(QWidget* mw, bool nomaximize, QString &strName) { if ( mw->inherits("QMainWindow") || mw->isA("QMainWindow") ) { ( ( QMainWindow* ) mw )->setUsesBigPixmaps( useBigPixmaps ); } QPoint p; QSize s; bool max; if ( mw->isVisible() ) { if ( read_widget_rect(strName, max, p, s) && validate_widget_size(mw, p, s) ) { mw->resize(s); mw->move(p); } mw->raise(); } else { if ( mw->layout() && mw->inherits("QDialog") ) { if ( read_widget_rect(strName, max, p, s) && validate_widget_size(mw, p, s) ) { mw->resize(s); mw->move(p); if ( max && !nomaximize ) { mw->showMaximized(); } else { mw->show(); } } else { QPEApplication::showDialog((QDialog*)mw,nomaximize); } } else { if ( read_widget_rect(strName, max, p, s) && validate_widget_size(mw, p, s) ) { mw->resize(s); mw->move(p); } else { //no stored rectangle, make an estimation int x = (qApp->desktop()->width()-mw->frameGeometry().width())/2; int y = (qApp->desktop()->height()-mw->frameGeometry().height())/2; mw->move( QMAX(x,0), QMAX(y,0) ); #ifdef Q_WS_QWS if ( !nomaximize ) mw->showMaximized(); #endif } if ( max && !nomaximize ) mw->showMaximized(); else mw->show(); } } } static bool read_widget_rect(const QString &app, bool &maximized, QPoint &p, QSize &s) { maximized = TRUE; // 350 is the trigger in qwsdefaultdecoration for providing a resize button if ( qApp->desktop()->width() <= 350 ) return FALSE; Config cfg( "qpe" ); cfg.setGroup("ApplicationPositions"); QString str = cfg.readEntry( app, QString::null ); QStringList l = QStringList::split(",", str); if ( l.count() == 5) { p.setX( l[0].toInt() ); p.setY( l[1].toInt() ); s.setWidth( l[2].toInt() ); s.setHeight( l[3].toInt() ); maximized = l[4].toInt(); return TRUE; } return FALSE; } static bool validate_widget_size(const QWidget *w, QPoint &p, QSize &s) { #ifndef Q_WS_QWS QRect qt_maxWindowRect = qApp->desktop()->geometry(); #endif int maxX = qt_maxWindowRect.width(); int maxY = qt_maxWindowRect.height(); int wWidth = s.width() + ( w->frameGeometry().width() - w->geometry().width() ); int wHeight = s.height() + ( w->frameGeometry().height() - w->geometry().height() ); // total window size is not allowed to be larger than desktop window size if ( ( wWidth >= maxX ) && ( wHeight >= maxY ) ) return FALSE; if ( wWidth > maxX ) { s.setWidth( maxX - (w->frameGeometry().width() - w->geometry().width() ) ); wWidth = maxX; } if ( wHeight > maxY ) { s.setHeight( maxY - (w->frameGeometry().height() - w->geometry().height() ) ); wHeight = maxY; } // any smaller than this and the maximize/close/help buttons will be overlapping if ( wWidth < 80 || wHeight < 60 ) return FALSE; if ( p.x() < 0 ) p.setX(0); if ( p.y() < 0 ) p.setY(0); if ( p.x() + wWidth > maxX ) p.setX( maxX - wWidth ); if ( p.y() + wHeight > maxY ) p.setY( maxY - wHeight ); return TRUE; } static void store_widget_rect(QWidget *w, QString &app) { // 350 is the trigger in qwsdefaultdecoration for providing a resize button if ( qApp->desktop()->width() <= 350 ) return; // we use these to map the offset of geometry and pos. ( we can only use normalGeometry to // get the non-maximized version, so we have to do it the hard way ) int offsetX = w->x() - w->geometry().left(); int offsetY = w->y() - w->geometry().top(); QRect r; if ( w->isMaximized() ) r = ( (HackWidget *) w)->normalGeometry(); else r = w->geometry(); // Stores the window placement as pos(), size() (due to the offset mapping) Config cfg( "qpe" ); cfg.setGroup("ApplicationPositions"); QString s; s.sprintf("%d,%d,%d,%d,%d", r.left() + offsetX, r.top() + offsetY, r.width(), r.height(), w->isMaximized() ); cfg.writeEntry( app, s ); } static bool setWidgetCaptionFromAppName( QWidget* /*mw*/, const QString& /*appName*/, const QString& /*appsPath*/ ) { @@ -1041,518 +1042,513 @@ bool QPEApplication::qwsEventFilter( QWSEvent * e ) if ( e->asMouse() ->simpleData.state && !QWidget::find( e->window() ) ) emit clientMoused(); break; default: break; } } if ( e->type == QWSEvent::Key ) { QWSKeyEvent *ke = ( QWSKeyEvent * ) e; if ( ke->simpleData.keycode == Qt::Key_F33 ) { // Use special "OK" key to press "OK" on top level widgets QWidget * active = activeWindow(); QWidget *popup = 0; if ( active && active->isPopup() ) { popup = active; active = active->parentWidget(); } if ( active && ( int ) active->winId() == ke->simpleData.window && !active->testWFlags( WStyle_Customize | WType_Popup | WType_Desktop ) ) { if ( ke->simpleData.is_press ) { if ( popup ) popup->close(); if ( active->inherits( "QDialog" ) ) { HackDialog * d = ( HackDialog * ) active; d->acceptIt(); return TRUE; } else if ( ( ( HackWidget * ) active ) ->needsOk() ) { QSignal s; s.connect( active, SLOT( accept() ) ); s.activate(); } else { // do the same as with the select key: Map to the default action of the widget: mapToDefaultAction( ke, Qt::Key_Return ); } } } } else if ( ke->simpleData.keycode == Qt::Key_F30 ) { // Use special "select" key to do whatever default action a widget has mapToDefaultAction( ke, Qt::Key_Space ); } else if ( ke->simpleData.keycode == Qt::Key_Escape && ke->simpleData.is_press ) { // Escape key closes app if focus on toplevel QWidget * active = activeWindow(); if ( active && active->testWFlags( WType_TopLevel ) && ( int ) active->winId() == ke->simpleData.window && !active->testWFlags( WStyle_Dialog | WStyle_Customize | WType_Popup | WType_Desktop ) ) { if ( active->inherits( "QDialog" ) ) { HackDialog * d = ( HackDialog * ) active; d->rejectIt(); return TRUE; } else /*if ( strcmp( argv() [ 0 ], "embeddedkonsole" ) != 0 )*/ { active->close(); } } } else if ( ke->simpleData.keycode >= Qt::Key_F1 && ke->simpleData.keycode <= Qt::Key_F29 ) { // this should be if ( ODevice::inst ( )-> buttonForKeycode ( ... )) // but we cannot access libopie function within libqpe :( QWidget * active = activeWindow ( ); if ( active && ((int) active-> winId ( ) == ke-> simpleData.window )) { if ( d-> kbgrabbed ) { // we grabbed the keyboard QChar ch ( ke-> simpleData.unicode ); QKeyEvent qke ( ke-> simpleData. is_press ? QEvent::KeyPress : QEvent::KeyRelease, ke-> simpleData.keycode, ch. latin1 ( ), ke-> simpleData.modifiers, QString ( ch ), ke-> simpleData.is_auto_repeat, 1 ); QObject *which = QWidget::keyboardGrabber ( ); if ( !which ) which = QApplication::focusWidget ( ); if ( !which ) which = QApplication::activeWindow ( ); if ( !which ) which = qApp; QApplication::sendEvent ( which, &qke ); } else { // we didn't grab the keyboard, so send the event to the launcher QCopEnvelope e ( "QPE/Launcher", "deviceButton(int,int,int)" ); e << int( ke-> simpleData.keycode ) << int( ke-> simpleData. is_press ) << int( ke-> simpleData.is_auto_repeat ); } } return true; } } if ( e->type == QWSEvent::Focus ) { QWSFocusEvent * fe = ( QWSFocusEvent* ) e; if ( !fe->simpleData.get_focus ) { QWidget * active = activeWindow(); while ( active && active->isPopup() ) { active->close(); active = activeWindow(); } } else { // make sure our modal widget is ALWAYS on top QWidget *topm = activeModalWidget(); if ( topm && static_cast<int>( topm->winId() ) != fe->simpleData.window) { topm->raise(); } } if ( fe->simpleData.get_focus && inputMethodDict ) { InputMethodHint m = inputMethodHint( QWidget::find( e->window() ) ); if ( m == AlwaysOff ) Global::hideInputMethod(); if ( m == AlwaysOn ) Global::showInputMethod(); } } return QApplication::qwsEventFilter( e ); } #endif /*! Destroys the QPEApplication. */ QPEApplication::~QPEApplication() { ungrabKeyboard(); #if defined(Q_WS_QWS) && !defined(QT_NO_COP) // Need to delete QCopChannels early, since the display will // be gone by the time we get to ~QObject(). delete sysChannel; delete pidChannel; #endif #ifdef OPIE_WITHROHFEEDBACK if( d->RoH ) delete d->RoH; #endif delete d; } /*! Returns <tt>$OPIEDIR/</tt>. */ QString QPEApplication::qpeDir() { const char * base = getenv( "OPIEDIR" ); if ( base ) return QString( base ) + "/"; return QString( "../" ); } /*! Returns the user's current Document directory. There is a trailing "/". .. well, it does now,, and there's no trailing '/' */ QString QPEApplication::documentDir() { const char* base = getenv( "HOME"); if ( base ) return QString( base ) + "/Documents"; return QString( "../Documents" ); } static int deforient = -1; /*! \internal */ int QPEApplication::defaultRotation() { if ( deforient < 0 ) { QString d = getenv( "QWS_DISPLAY" ); if ( d.contains( "Rot90" ) ) { deforient = 90; } else if ( d.contains( "Rot180" ) ) { deforient = 180; } else if ( d.contains( "Rot270" ) ) { deforient = 270; } else { deforient = 0; } } return deforient; } /*! \internal */ void QPEApplication::setDefaultRotation( int r ) { if ( qApp->type() == GuiServer ) { deforient = r; setenv( "QWS_DISPLAY", QString( "Transformed:Rot%1:0" ).arg( r ).latin1(), 1 ); Config config("qpe"); config.setGroup( "Rotation" ); config.writeEntry( "Rot", r ); } else { #ifndef QT_NO_COP { QCopEnvelope e( "QPE/System", "setDefaultRotation(int)" ); e << r; } #endif } } #include <qgfx_qws.h> #include <qwindowsystem_qws.h> #if QT_VERSION > 236 extern void qws_clearLoadedFonts(); #endif void QPEApplication::setCurrentMode( int x, int y, int depth ) { // Reset the caches #if QT_VERSION > 236 qws_clearLoadedFonts(); #endif QPixmapCache::clear(); // Change the screen mode qt_screen->setMode(x, y, depth); if ( qApp->type() == GuiServer ) { #if QT_VERSION > 236 // Reconfigure the GuiServer qwsServer->beginDisplayReconfigure(); qwsServer->endDisplayReconfigure(); #endif // Get all the running apps to reset QCopEnvelope env( "QPE/System", "reset()" ); } } void QPEApplication::reset() { // Reconnect to the screen qt_screen->disconnect(); qt_screen->connect( QString::null ); // Redraw everything applyStyle(); } #if (QT_VERSION < 238) && defined Q_OS_MACX bool qt_left_hand_scrollbars = false; #else -#ifdef Q_OS_MACX -#define WEAK_SYMBOL __attribute__((weak_import)) -#else -#define WEAK_SYMBOL __attribute__((weak)) -#endif -extern bool qt_left_hand_scrollbars WEAK_SYMBOL; +extern bool qt_left_hand_scrollbars QPE_WEAK_SYMBOL; #endif /*! \internal */ void QPEApplication::applyStyle() { Config config( "qpe" ); config.setGroup( "Appearance" ); #if QT_VERSION > 233 #if !defined(OPIE_NO_OVERRIDE_QT) // don't block ourselves ... Opie::force_appearance = 0; static QString appname = Opie::binaryName ( ); QStringList ex = config. readListEntry ( "NoStyle", ';' ); int nostyle = 0; for ( QStringList::Iterator it = ex. begin ( ); it != ex. end ( ); ++it ) { if ( QRegExp (( *it ). mid ( 1 ), false, true ). find ( appname, 0 ) >= 0 ) { nostyle = ( *it ). left ( 1 ). toInt ( 0, 32 ); break; } } #else int nostyle = 0; #endif // Widget style QString style = config.readEntry( "Style", "FlatStyle" ); // don't set a custom style if ( nostyle & Opie::Force_Style ) style = "FlatStyle"; internalSetStyle ( style ); // Colors - from /etc/colors/Liquid.scheme QColor bgcolor( config.readEntry( "Background", "#E0E0E0" ) ); QColor btncolor( config.readEntry( "Button", "#96c8fa" ) ); QPalette pal( btncolor, bgcolor ); QString color = config.readEntry( "Highlight", "#73adef" ); pal.setColor( QColorGroup::Highlight, QColor( color ) ); color = config.readEntry( "HighlightedText", "#FFFFFF" ); pal.setColor( QColorGroup::HighlightedText, QColor( color ) ); color = config.readEntry( "Text", "#000000" ); pal.setColor( QColorGroup::Text, QColor( color ) ); color = config.readEntry( "ButtonText", "#000000" ); pal.setColor( QPalette::Active, QColorGroup::ButtonText, QColor( color ) ); color = config.readEntry( "Base", "#FFFFFF" ); pal.setColor( QColorGroup::Base, QColor( color ) ); pal.setColor( QPalette::Disabled, QColorGroup::Text, pal.color( QPalette::Active, QColorGroup::Background ).dark() ); setPalette( pal, TRUE ); // Set the ScrollBar on the 'right' side but only if the weak symbol is present if (&qt_left_hand_scrollbars ) qt_left_hand_scrollbars = config.readBoolEntry( "LeftHand", false ); // Window Decoration QString dec = config.readEntry( "Decoration", "Flat" ); // don't set a custom deco if ( nostyle & Opie::Force_Decoration ) dec = ""; if ( dec != d->decorationName ) { qwsSetDecoration( new QPEDecoration( dec ) ); d->decorationName = dec; } // Font QString ff = config.readEntry( "FontFamily", font().family() ); int fs = config.readNumEntry( "FontSize", font().pointSize() ); // don't set a custom font if ( nostyle & Opie::Force_Font ) { ff = "Vera"; fs = 10; } setFont ( QFont ( ff, fs ), true ); #if !defined(OPIE_NO_OVERRIDE_QT) // revert to global blocking policy ... Opie::force_appearance = config. readBoolEntry ( "ForceStyle", false ) ? Opie::Force_All : Opie::Force_None; Opie::force_appearance &= ~nostyle; #endif #endif } void QPEApplication::systemMessage( const QCString& msg, const QByteArray& data ) { #ifdef Q_WS_QWS QDataStream stream( data, IO_ReadOnly ); if ( msg == "applyStyle()" ) { applyStyle(); } else if ( msg == "toggleApplicationMenu()" ) { QWidget *active = activeWindow ( ); if ( active ) { QPEMenuToolFocusManager *man = QPEMenuToolFocusManager::manager ( ); bool oldactive = man-> isActive ( ); man-> setActive( !man-> isActive() ); if ( !oldactive && !man-> isActive ( )) { // no menubar to toggle -> try O-Menu QCopEnvelope e ( "QPE/TaskBar", "toggleStartMenu()" ); } } } else if ( msg == "setDefaultRotation(int)" ) { if ( type() == GuiServer ) { int r; stream >> r; setDefaultRotation( r ); } } else if ( msg == "setCurrentMode(int,int,int)" ) { // Added: 2003-06-11 by Tim Ansell <mithro@mithis.net> if ( type() == GuiServer ) { int x, y, depth; stream >> x; stream >> y; stream >> depth; setCurrentMode( x, y, depth ); } } else if ( msg == "reset()" ) { if ( type() != GuiServer ) reset(); } else if ( msg == "setCurrentRotation(int)" ) { int r; stream >> r; setCurrentRotation( r ); } else if ( msg == "shutdown()" ) { if ( type() == GuiServer ) shutdown(); } else if ( msg == "quit()" ) { if ( type() != GuiServer ) tryQuit(); } else if ( msg == "forceQuit()" ) { if ( type() != GuiServer ) quit(); } else if ( msg == "restart()" ) { if ( type() == GuiServer ) restart(); } else if ( msg == "language(QString)" ) { if ( type() == GuiServer ) { QString l; stream >> l; QString cl = getenv( "LANG" ); if ( cl != l ) { if ( l.isNull() ) unsetenv( "LANG" ); else setenv( "LANG", l.latin1(), 1 ); restart(); } } } else if ( msg == "timeChange(QString)" ) { QString t; stream >> t; if ( t.isNull() ) unsetenv( "TZ" ); else setenv( "TZ", t.latin1(), 1 ); // emit the signal so everyone else knows... emit timeChanged(); } else if ( msg == "addAlarm(QDateTime,QCString,QCString,int)" ) { if ( type() == GuiServer ) { QDateTime when; QCString channel, message; int data; stream >> when >> channel >> message >> data; AlarmServer::addAlarm( when, channel, message, data ); } } else if ( msg == "deleteAlarm(QDateTime,QCString,QCString,int)" ) { if ( type() == GuiServer ) { QDateTime when; QCString channel, message; int data; stream >> when >> channel >> message >> data; AlarmServer::deleteAlarm( when, channel, message, data ); } } else if ( msg == "clockChange(bool)" ) { int tmp; stream >> tmp; emit clockChanged( tmp ); } else if ( msg == "weekChange(bool)" ) { int tmp; stream >> tmp; emit weekChanged( tmp ); } else if ( msg == "setDateFormat(DateFormat)" ) { DateFormat tmp; stream >> tmp; emit dateFormatChanged( tmp ); } else if ( msg == "setVolume(int,int)" ) { int t, v; stream >> t >> v; setVolume( t, v ); emit volumeChanged( muted ); } else if ( msg == "volumeChange(bool)" ) { stream >> muted; setVolume(); emit volumeChanged( muted ); } else if ( msg == "setMic(int,int)" ) { // Added: 2002-02-08 by Jeremy Cowgar <jc@cowgar.com> int t, v; stream >> t >> v; setMic( t, v ); emit micChanged( micMuted ); } else if ( msg == "micChange(bool)" ) { // Added: 2002-02-08 by Jeremy Cowgar <jc@cowgar.com> stream >> micMuted; setMic(); emit micChanged( micMuted ); } else if ( msg == "setBass(int,int)" ) { // Added: 2002-12-13 by Maximilian Reiss <harlekin@handhelds.org> int t, v; stream >> t >> v; setBass( t, v ); } else if ( msg == "bassChange(bool)" ) { // Added: 2002-12-13 by Maximilian Reiss <harlekin@handhelds.org> setBass(); } else if ( msg == "setTreble(int,int)" ) { // Added: 2002-12-13 by Maximilian Reiss <harlekin@handhelds.org> int t, v; stream >> t >> v; setTreble( t, v ); } else if ( msg == "trebleChange(bool)" ) { // Added: 2002-12-13 by Maximilian Reiss <harlekin@handhelds.org> setTreble(); } else if ( msg == "getMarkedText()" ) { if ( type() == GuiServer ) { const ushort unicode = 'C'-'@'; const int scan = Key_C; |