From: san Date: Fri, 30 Dec 2011 07:34:46 +0000 (+0000) Subject: Shortcut edit preferences functionality was added. X-Git-Tag: V5_1_20~2 X-Git-Url: http://git.salome-platform.org/gitweb/?a=commitdiff_plain;h=0de312a180f0a2e3157c447e68fdeb795bf64668;p=modules%2Fgui.git Shortcut edit preferences functionality was added. --- diff --git a/src/Qtx/Qtx.cxx b/src/Qtx/Qtx.cxx index 896eed39a..89f7e729c 100755 --- a/src/Qtx/Qtx.cxx +++ b/src/Qtx/Qtx.cxx @@ -37,9 +37,14 @@ #include #include +#include + #include #include #include +#include + +#define BICOLOR_CHANGE_HUE /*! \class Qtx @@ -431,7 +436,7 @@ QString Qtx::library( const QString& str ) */ QString Qtx::tmpDir() { - char* tmpdir = ::getenv( "TEMP" ); + const char* tmpdir = ::getenv( "TEMP" ); if ( !tmpdir ) tmpdir = ::getenv ( "TMP" ); if ( !tmpdir ) @@ -624,6 +629,99 @@ QCompleter* Qtx::pathCompleter( const PathType type, const QString& filter ) return cmp; } +/*! + \brief Parse given string to retrieve environment variable. + + Looks through the string for the patterns: ${name} or $(name) or %name%. + If string contains variable satisfying any pattern, the variable name + is returned, start index of the variable is returned in the \a start parameter, + and length of the variable is returned in the \a len parameter. + + \param str string being processed + \param start if variable is found, this parameter contains its starting + position in the \a str + \param len if variable is found, this parameter contains its length + \return first found variable or null QString if there is no ones +*/ +QString Qtx::findEnvVar( const QString& str, int& start, int& len ) +{ + QString varName; + len = 0; + + QRegExp rx( "(^\\$\\{|[^\\$]\\$\\{)([a-zA-Z]+[a-zA-Z0-9_]*)(\\})|(^\\$\\(|[^\\$]\\$\\()([a-zA-Z]+[a-zA-Z0-9_]*)(\\))|(^\\$|[^\\$]\\$)([a-zA-Z]+[a-zA-Z0-9_]*)|(^%|[^%]%)([a-zA-Z]+[a-zA-Z0-9_]*)(%[^%]|%$)" ); + + int pos = rx.indexIn( str, start ); + if ( pos != -1 ) + { + int i = 1; + while ( i <= rx.numCaptures() && varName.isEmpty() ) + { + QString capStr = rx.cap( i ); + if ( !capStr.contains( "%" ) && !capStr.contains( "$" ) ) + varName = capStr; + i++; + } + + if ( !varName.isEmpty() ) + { + int capIdx = i - 1; + start = rx.pos( capIdx ); + int end = start + varName.length(); + if ( capIdx > 1 && rx.cap( capIdx - 1 ).contains( QRegExp( "\\$|%" ) ) ) + start = rx.pos( capIdx - 1 ) + rx.cap( capIdx - 1 ).indexOf( QRegExp( "\\$|%" ) ); + if ( capIdx < rx.numCaptures() && !rx.cap( capIdx - 1 ).isEmpty() ) + end++; + len = end - start; + } + } + return varName; +} + +/*! + \brief Substitute environment variables by their values. + + Environment variable is substituted by its value. + + \param str string to be processed + \return processed string (with all substitutions made) +*/ +QString Qtx::makeEnvVarSubst( const QString& str, const SubstMode mode ) +{ + QString res = str; + if ( mode != Never ) + { + QMap ignoreMap; + + int start( 0 ), len( 0 ); + while ( true ) + { + QString envName = findEnvVar( res, start, len ); + if ( envName.isNull() ) + break; + + QString newStr; + if ( ::getenv( envName.toLatin1() ) || mode == Always ) + newStr = QString( ::getenv( envName.toLatin1() ) ); + + if ( newStr.isNull() ) + { + if ( ignoreMap.contains( envName ) ) + { + start += len; + continue; + } + ignoreMap.insert( envName, 0 ); + } + res.replace( start, len, newStr ); + } + + res.replace( "$$", "$" ); + res.replace( "%%", "%" ); + } + + return res; +} + /*! \brief Pack the specified color into integer RGB set. \param c unpacked color @@ -718,7 +816,7 @@ void Qtx::scaleColors( const int num, QColorList& lst ) /*! \brief Scale the pixmap to the required size. - If \h is 0 (default) the value of \a w is used instead (to create + If \a h is 0 (default) the value of \a w is used instead (to create square pixmap). \param icon pixmap to be resized @@ -730,7 +828,7 @@ QPixmap Qtx::scaleIcon( const QPixmap& icon, const unsigned w, const unsigned h { QPixmap p; int aw = w, ah = h <= 0 ? w : h; - if ( icon.isNull() || aw <= 0 || ah <= 0 || aw == icon.width() && ah == icon.height() ) + if ( icon.isNull() || aw <= 0 || ah <= 0 || ( aw == icon.width() && ah == icon.height() ) ) p = icon; else p = icon.fromImage( icon.toImage().scaled( aw, ah, Qt::KeepAspectRatio, Qt::SmoothTransformation ) ); @@ -962,7 +1060,7 @@ QString Qtx::colorToString( const QColor& color ) - "RR,GG,BB[,AA]" or "RR GG BB[ AA]" (\c RR, \c GG, \c BB and optional \c AA values represent red, green, blue and alpha components of the color in decimal form) - - #RRGGBB" - (\c RR, \c GG and \c BB values represent red, green and blue + - "#RRGGBB" - (\c RR, \c GG and \c BB values represent red, green and blue components of the color in hexadecimal form) - an integer value representing packed color components (see rgbSet()) - a name from the list of colors defined in the list of SVG color keyword names @@ -1013,6 +1111,106 @@ bool Qtx::stringToColor( const QString& str, QColor& color ) return res; } +/*! + \brief Convert bi-color value to the string representation. + + Bi-color value is specified as main color and integer delta + value that is used to calculate secondary color by changing + paremeters of the main color ("saturation" and "value" + components in HSV notation). + + The resulting string consists of two sub-strings separated by + '|' symbol. The first part represents main color + (see colorToString() for more details), the second part is a + delta value. + + Backward conversion can be done with stringToBiColor() method. + + \param color color to be converted + \param delta delta value + \return string representation of the bi-color value + + \sa stringToBiColor(), stringToColor() +*/ +QString Qtx::biColorToString( const QColor& color, const int delta ) +{ + return QString("%1|%2").arg( Qtx::colorToString( color ) ).arg( delta ); +} + +/*! + \brief Restore bi-color value from the string representation. + + Bi-color value is specified as main color and integer delta + value that is used to calculate secondary color by changing + paremeters of the main color ("saturation" and "value" + components in HSV notation). + + The parameter \a str should consist of two sub-strings separated + by '|' symbol. The first part represents main color + (see stringToColor() for more details), the second part is a + delta value. + + Backward conversion can be done with biColorToString() method. + + \param str string representation of the bi-color value + \param color resulting color value + \param delta resulting delta value + \return \c true if the conversion is successful and \c false otherwise + + \sa biColorToString(), stringToColor(), rgbSet() +*/ +bool Qtx::stringToBiColor( const QString& str, QColor& color, int& delta ) +{ + QStringList data = str.split( "|", QString::KeepEmptyParts ); + QColor c; + int d = 0; + bool ok = data.count() > 0 && Qtx::stringToColor( data[0], c ); + bool dok = false; + if ( data.count() > 1 ) d = data[1].toInt( &dok ); + ok = ok && dok; + color = ok ? c : QColor(); + delta = ok ? d : 0; + return ok; +} + +/*! + \brief Compute secondary color value from specified main color + and delta. + + Secondary color is calculated by changing paremeters of the main + color ("saturation" and "value" components in HSV notation) using + specified delta. + + If main color is invalid, result of the function is also invalid color. + + \param color source main color + \param delta delta value + \return resulting secondary color + + \sa biColorToString(), stringToBiColor() +*/ +QColor Qtx::mainColorToSecondary( const QColor& color, int delta ) +{ + QColor cs = color; + if ( cs.isValid() ) { + int val = qMin( 255, qMax( cs.value() + delta, 0 ) ); + int sat = qMin( 255, qMax( cs.saturation() + delta-(val-cs.value()), 0 ) ); +#ifdef BICOLOR_CHANGE_HUE + const int BICOLOR_HUE_MAXDELTA = 40; + int dh = delta-(val-cs.value())-(sat-cs.saturation()); + dh = qMin( BICOLOR_HUE_MAXDELTA, qAbs( dh ) ) * ( dh > 0 ? 1 : -1 ); + //int hue = qMin( 359, qMax( cs.hue() + delta-(val-cs.value())-(sat-cs.saturation()), 0 ) ); + //int hue = qMin( 359, qMax( cs.hue() + delta-(val-cs.value())-ds, 0 ) ); + int hue = cs.hue() + dh; + if ( hue < 0 ) hue = 360 - hue; +#else + int hue = cs.hue(); +#endif + cs.setHsv( hue, sat, val ); + } + return cs; +} + /*! \brief Dump linear gradient to the string description. \param gradient linear gradient to be converted @@ -1191,7 +1389,7 @@ bool Qtx::stringToRadialGradient( const QString& str, QRadialGradient& gradient { bool success = false; QStringList vals = str.split( "|", QString::SkipEmptyParts ); - if ( vals.count() > 5 && vals[0] == "radial" || vals[0] == "rg" ) + if ( ( vals.count() > 5 && vals[0] == "radial" ) || vals[0] == "rg" ) { // center, radius and focal point double cx, cy, r, fx, fy; @@ -1245,7 +1443,7 @@ bool Qtx::stringToConicalGradient( const QString& str, QConicalGradient& gradien { bool success = false; QStringList vals = str.split( "|", QString::SkipEmptyParts ); - if ( vals.count() > 3 && vals[0] == "conical" || vals[0] == "cg" ) + if ( ( vals.count() > 3 && vals[0] == "conical" ) || vals[0] == "cg" ) { // center and angle double cx, cy, a; @@ -1286,34 +1484,108 @@ bool Qtx::stringToConicalGradient( const QString& str, QConicalGradient& gradien return success; } -/** - * Cheque for existing any system printers +/*! + \class Qtx::Localizer + \brief Localization helper + + This helper class can be used to solve the localization problems, + usually related to the textual files reading/writing, namely when + floating point values are read / written with API functions. + The problem relates to such locale specific settings as decimal point + separator, thousands separator, etc. + + To use the Localizer class, just create a local variable in the beginning + of the code where you need to read / write data from textual file(s). + The constructor of the class forces setting "C" locale temporariy. + The destructor switches back to the initial locale. + + \code + Qtx::Localizer loc; + readSomething(); + writeSomething(); + \endcode */ -#if !defined(WIN32) && !defined(QT_NO_CUPS) -#if QT_VERSION < 0x040303 -#include -#include +/*! + \brief Constructor. Forces "C" locale to be set. +*/ +Qtx::Localizer::Localizer() +{ + myCurLocale = setlocale( LC_NUMERIC, 0 ); + setlocale( LC_NUMERIC, "C" ); +} -typedef int (*CupsGetDests)(cups_dest_t **dests); -static CupsGetDests _cupsGetDests = 0; -#endif -#endif -bool Qtx::hasAnyPrinters() +/*! + \brief Destructor. Reverts back to the initial locale. +*/ +Qtx::Localizer::~Localizer() { - bool aRes = true; -#if !defined(WIN32) && !defined(QT_NO_CUPS) -#if QT_VERSION < 0x040303 - QLibrary aCupsLib( QString( "cups" ), 2 ); - if ( !aCupsLib.load() ) - aRes = false; - else { - cups_dest_t *printers; - _cupsGetDests = (CupsGetDests) aCupsLib.resolve("cupsGetDests"); - int prnCount = _cupsGetDests(&printers); - aRes = prnCount > 0; + setlocale( LC_NUMERIC, myCurLocale.toLatin1().constData() ); } -#endif -#endif - return aRes; + +#ifndef WIN32 + +#include +#include + +/*! + \brief Open the default X display and returns pointer to it. + This method is available on Linux only. + \return Pointer to X display. + \sa getVisual() +*/ +void* Qtx::getDisplay() +{ + static Display* pDisplay = NULL; + if ( !pDisplay ) + pDisplay = XOpenDisplay( NULL ); + return pDisplay; +} + +/*! + \brief Returns pointer to X visual suitable for 3D rendering. + This method is available on Linux only. + \return Pointer to X visual. + \sa getDisplay() +*/ +Qt::HANDLE Qtx::getVisual() +{ + Qt::HANDLE res = (Qt::HANDLE)NULL; + + Display* pDisplay = (Display*)getDisplay(); + if ( !pDisplay ) + return res; + + int errorBase; + int eventBase; + + // Make sure OpenGL's GLX extension supported + if( !glXQueryExtension( pDisplay, &errorBase, &eventBase ) ){ + qCritical( "Could not find glx extension" ); + return res; + } + + // Find an appropriate visual + + int doubleBufferVisual[] = { + GLX_RGBA, // Needs to support OpenGL + GLX_DEPTH_SIZE, 16, // Needs to support a 16 bit depth buffer + GLX_DOUBLEBUFFER, // Needs to support double-buffering + None // end of list + }; + + // Try for the double-bufferd visual first + XVisualInfo *visualInfo = NULL; + visualInfo = glXChooseVisual( pDisplay, DefaultScreen(pDisplay), doubleBufferVisual ); + + if( visualInfo == NULL ){ + qCritical( "Could not find matching glx visual" ); + return res; + } + + qDebug() << "Picked visual 0x" << hex << XVisualIDFromVisual( visualInfo->visual ); + res = (Qt::HANDLE)( visualInfo->visual ); + + return res; } +#endif // WIN32 diff --git a/src/Qtx/Qtx.h b/src/Qtx/Qtx.h index 8e67524fc..907cb5afa 100755 --- a/src/Qtx/Qtx.h +++ b/src/Qtx/Qtx.h @@ -23,7 +23,7 @@ #define QTX_H #if defined WIN32 -# if defined QTX_EXPORTS +# if defined QTX_EXPORTS || defined qtx_EXPORTS # define QTX_EXPORT _declspec( dllexport ) # else # define QTX_EXPORT _declspec( dllimport ) @@ -96,6 +96,49 @@ public: AppropriateRole = Qt::UserRole + 100 //!< can be used to return \c true if data is appropriate }; + typedef enum { + Shown, //!< column should be always visible + Hidden, //!< column should be always hidden + Toggled //!< it should be possible to show/hide the column with help of popup menu + } Appropriate; //!< appropriate status + + //! Environment variables substitution mode + typedef enum { + Always, //!< substitute environment variable by it's value if variable exists, and "" otherwise + Never, //!< keep environment variable as is without any substitution + Auto //!< substitute environment variable by it's value if variable exists, and keep it as is otherwise + } SubstMode; + + //! object visibility state + typedef enum { + ShownState, //!< Object is shown in viewer + HiddenState, //!< Object is hidden in viewer + UnpresentableState, //!< Unpresentable object + } VisibilityState; + + //! Header view flags + typedef enum { + ShowText = 0x001, //!< Show only text in the header + ShowIcon = 0x010, //!< Show only icon in the header + ShowAll = ShowText | ShowIcon //!< Show icon and text in the header + } HeaderViewFlags; + + //Type of the custom data + typedef enum { + IdType + } CustomDataType; + + + + class QTX_EXPORT Localizer + { + public: + Localizer(); + ~Localizer(); + private: + QString myCurLocale; + }; + static QString toQString( const char*, const int = -1 ); static QString toQString( const short*, const int = -1 ); static QString toQString( const unsigned char*, const int = -1 ); @@ -123,6 +166,8 @@ public: static QString addSlash( const QString& ); static QCompleter* pathCompleter( const PathType, const QString& = QString() ); + static QString findEnvVar( const QString&, int&, int& ); + static QString makeEnvVarSubst( const QString&, const SubstMode = Auto ); static int rgbSet( const QColor& ); static int rgbSet( const int, const int, const int ); @@ -142,6 +187,9 @@ public: static QString colorToString( const QColor& ); static bool stringToColor( const QString&, QColor& ); + static QString biColorToString( const QColor&, const int ); + static bool stringToBiColor( const QString&, QColor&, int& ); + static QColor mainColorToSecondary( const QColor&, int ); static QString gradientToString( const QLinearGradient& ); static QString gradientToString( const QRadialGradient& ); @@ -150,7 +198,10 @@ public: static bool stringToRadialGradient( const QString&, QRadialGradient& ); static bool stringToConicalGradient( const QString&, QConicalGradient& ); - static bool hasAnyPrinters(); +#ifndef WIN32 + static void* getDisplay(); + static Qt::HANDLE getVisual(); +#endif }; #endif diff --git a/src/Qtx/Qtx.pro b/src/Qtx/Qtx.pro index 1a3a451db..afbc2f0da 100644 --- a/src/Qtx/Qtx.pro +++ b/src/Qtx/Qtx.pro @@ -6,6 +6,7 @@ include(../Common.pro) CONFIG += embed_manifest_exe win32:LIBS *= -L$(QTLIB) +unix:LIBS *= -lGL win32:INCLUDEPATH *= $(QTINC) $(QTINC)\QtCore $(QTINC)\QtGui $(QTINC)\QtXml QT += xml diff --git a/src/Qtx/QtxFontEdit.cxx b/src/Qtx/QtxFontEdit.cxx index 30970d8f1..5db88d298 100644 --- a/src/Qtx/QtxFontEdit.cxx +++ b/src/Qtx/QtxFontEdit.cxx @@ -57,7 +57,8 @@ */ QtxFontEdit::QtxFontEdit( const int feat, QWidget* parent ) : QFrame( parent ), - myFeatures( feat ) + myFeatures( feat ), + myMode( Native ) { initialize(); } @@ -70,7 +71,8 @@ QtxFontEdit::QtxFontEdit( const int feat, QWidget* parent ) */ QtxFontEdit::QtxFontEdit( QWidget* parent ) : QFrame( parent ), - myFeatures( All ) + myFeatures( All ), + myMode( Native ) { initialize(); } @@ -119,6 +121,7 @@ QFont QtxFontEdit::currentFont() const fnt.setBold( script & Bold ); fnt.setItalic( script & Italic ); fnt.setUnderline( script & Underline ); + fnt.setOverline( script & Shadow ); //addVtkFontPref( tr( "LABELS" ), valLblFontGr, "values_labeling_font" ); return fnt; } @@ -130,11 +133,28 @@ QFont QtxFontEdit::currentFont() const */ void QtxFontEdit::setCurrentFont( const QFont& fnt ) { + myFamily->blockSignals( true ); + myCustomFams->blockSignals( true ); + mySize->blockSignals( true ); + myB->blockSignals( true ); + myI->blockSignals( true ); + myU->blockSignals( true ); + setFontFamily( fnt.family() ); setFontSize( fnt.pointSize() ); setFontScripting( ( fnt.bold() ? Bold : 0 ) | ( fnt.italic() ? Italic : 0 ) | - ( fnt.underline() ? Underline : 0 ) ); + ( fnt.underline() ? Underline : 0 ) | + ( fnt.overline() ? Shadow : 0 ) ); + + myFamily->blockSignals( false ); + myCustomFams->blockSignals( false ); + mySize->blockSignals( false ); + myB->blockSignals( false ); + myI->blockSignals( false ); + myU->blockSignals( false ); + + emit( changed( currentFont() ) ); } /*! @@ -144,7 +164,10 @@ void QtxFontEdit::setCurrentFont( const QFont& fnt ) */ QString QtxFontEdit::fontFamily() const { + if ( myMode == Native ) return myFamily->currentFont().family(); + else + return myCustomFams->currentText(); } /*! @@ -168,7 +191,8 @@ int QtxFontEdit::fontScripting() const { return ( myB->isChecked() ? Bold : 0 ) | ( myI->isChecked() ? Italic : 0 ) | - ( myU->isChecked() ? Underline : 0 ); + ( myU->isChecked() ? Underline : 0 ) | + ( myS->isChecked() ? Shadow : 0 ) ; } /*! @@ -178,9 +202,18 @@ int QtxFontEdit::fontScripting() const */ void QtxFontEdit::setFontFamily( const QString& fam ) { + if ( myMode == Native ) + { myFamily->setCurrentFont( QFont( fam ) ); onFontChanged( myFamily->currentFont() ); } + else + { + myCustomFams->setCurrentIndex( myCustomFams->findText( fam ) ); + if ( !myCustomFams->signalsBlocked() ) + emit( changed( currentFont() ) ); + } +} /*! \brief Set font size. @@ -209,6 +242,7 @@ void QtxFontEdit::setFontScripting( const int script ) myB->setChecked( script & Bold ); myI->setChecked( script & Italic ); myU->setChecked( script & Underline ); + myS->setChecked( script & Shadow ); } /*! @@ -218,11 +252,13 @@ void QtxFontEdit::updateState() { int feat = features(); - myFamily->setVisible( feat & Family ); + myFamily->setVisible( ( feat & Family ) && myMode == Native ); + myCustomFams->setVisible( ( feat & Family ) && myMode == Custom ); mySize->setVisible( feat & Size ); myB->setVisible( feat & Bold ); myI->setVisible( feat & Italic ); myU->setVisible( feat & Underline ); + myS->setVisible( feat & Shadow ); myPreview->setVisible( feat & Preview ); mySize->setEditable( feat & UserSize ); @@ -234,6 +270,9 @@ void QtxFontEdit::updateState() */ void QtxFontEdit::onFontChanged( const QFont& /*f*/ ) { + bool blocked = mySize->signalsBlocked(); + mySize->blockSignals( true ); + int s = fontSize(); mySize->clear(); @@ -244,6 +283,16 @@ void QtxFontEdit::onFontChanged( const QFont& /*f*/ ) mySize->addItems( sizes ); setFontSize( s ); + + mySize->blockSignals( blocked ); + + if ( !myFamily->signalsBlocked() ) + emit( changed( currentFont() ) ); +} + +void QtxFontEdit::onPropertyChanged() +{ + emit( changed( currentFont() ) ); } /*! @@ -269,6 +318,7 @@ void QtxFontEdit::initialize() base->setSpacing( 5 ); base->addWidget( myFamily = new QFontComboBox( this ) ); + base->addWidget( myCustomFams = new QComboBox( this ) ); base->addWidget( mySize = new QtxComboBox( this ) ); mySize->setInsertPolicy( QComboBox::NoInsert ); mySize->setValidator( new QIntValidator( 1, 250, mySize ) ); @@ -285,14 +335,159 @@ void QtxFontEdit::initialize() myU->setText( tr( "U" ) ); myU->setCheckable( true ); + base->addWidget( myS = new QToolButton( this ) ); + myS->setText( tr( "S" ) ); + myS->setCheckable( true ); + base->addWidget( myPreview = new QToolButton( this ) ); myPreview->setText( "..." ); myFamily->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred ); + myCustomFams->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Preferred ); connect( myPreview, SIGNAL( clicked( bool ) ), this, SLOT( onPreview( bool ) ) ); connect( myFamily, SIGNAL( currentFontChanged( const QFont& ) ), this, SLOT( onFontChanged( const QFont& ) ) ); + connect( mySize, SIGNAL( currentIndexChanged( int ) ), this, SLOT( onPropertyChanged() ) ); + connect( mySize, SIGNAL( editTextChanged( QString ) ), this, SLOT( onPropertyChanged() ) ); + connect( myB, SIGNAL( toggled( bool ) ), this, SLOT( onPropertyChanged() ) ); + connect( myI, SIGNAL( toggled( bool ) ), this, SLOT( onPropertyChanged() ) ); + connect( myU, SIGNAL( toggled( bool ) ), this, SLOT( onPropertyChanged() ) ); + + myCustomFams->hide(); + myS->hide(); updateState(); onFontChanged( currentFont() ); } + +/*! + \brief Specifies whether widget works in Native or Custom mode. Native mode + is intended for working with system fonts. Custom mode is intended for + working with manually defined set of fonts. Set of custom fonts can be + specified with setCustomFonts() method + \param mode mode from QtxFontEdit::Mode enumeration + \sa mode() +*/ +void QtxFontEdit::setMode( const int mode ) +{ + if ( myMode == mode ) + return; + + myMode = mode; + + myFamily->setShown( myMode == Native ); + myCustomFams->setShown( myMode == Custom ); + + updateGeometry(); +} + +/*! + \brief Verifies whether widget works in Native or Custom mode + \return Native or Custom mode + \sa setMode() +*/ +int QtxFontEdit::mode() const +{ + return myMode; +} + +/*! + \brief Sets list of custom fonts. + This method is intended for working in Custom mode. + \param fams list of families + \sa fonts(), setMode() +*/ +void QtxFontEdit::setFonts( const QStringList& fams ) +{ + QString currFam = myCustomFams->currentText(); + + myCustomFams->clear(); + myCustomFams->addItems( fams ); + + int ind = myCustomFams->findText( currFam ); + if ( ind != -1 ) + myCustomFams->setCurrentIndex( ind ); + + setSizes( QList() ); +} + +/*! + \brief Sets list of available font sizes. + This method is intended for working in Custom mode. The list of sizes can + be empty. In this case system generate listof size automatically from 8 till 72. + \param sizes list of sizes + \sa sizes(), setMode() +*/ +void QtxFontEdit::setSizes( const QList& sizes ) +{ + QString currSize = mySize->currentText(); + + mySize->clear(); + if ( !sizes.isEmpty() ) + { + QStringList szList; + for ( QList::const_iterator it = sizes.begin(); it != sizes.end(); ++it ) + szList.append( QString::number( *it ) ); + mySize->addItems( szList ); + } + else + { + static QStringList defLst; + if ( defLst.isEmpty() ) + { + QString str( "8 9 10 11 12 14 16 18 20 22 24 26 28 36 48 72" ); + defLst = str.split( " " ); + } + mySize->addItems( defLst ); + } + + int ind = mySize->findText( currSize ); + if ( ind != -1 ) + mySize->setCurrentIndex( ind ); +} + +/*! + \brief Gets list of custom fonts + \return list of families + \sa setFonts(), setMode() +*/ +QStringList QtxFontEdit::fonts() const +{ + QStringList fams; + for ( int i = 0, n = myCustomFams->count(); i < n; i++ ) + fams.append( myCustomFams->itemText( i ) ); + return fams; +} + +/*! + \brief Gets list of custom fonts + \return list of families + \sa setCustomFonts(), setMode() +*/ +QList QtxFontEdit::sizes() const +{ + QList lst; + for ( int i = 0, n = mySize->count(); i < n; i++ ) + lst.append( mySize->itemText( i ).toInt() ); + return lst; +} + + + + + + + + + + + + + + + + + + + + diff --git a/src/Qtx/QtxFontEdit.h b/src/Qtx/QtxFontEdit.h index b0ef25eef..052303a63 100644 --- a/src/Qtx/QtxFontEdit.h +++ b/src/Qtx/QtxFontEdit.h @@ -26,6 +26,7 @@ #include +class QComboBox; class QtxComboBox; class QToolButton; class QFontComboBox; @@ -43,11 +44,18 @@ public: Bold = 0x08, //!< show 'bold' widget Italic = 0x10, //!< show 'italic' widget Underline = 0x20, //!< show 'underline' widget - Preview = 0x40, //!< show font preview widget + Shadow = 0x40, //!< show 'shadow' widget + Preview = 0x80, //!< show font preview widget Scripting = Bold | Italic | Underline, //!< show font scripting widgets ('bold','italic','underline') All = Family | Size | UserSize | Scripting | Preview //!< show all font widgets } Features; + typedef enum + { + Native, //!< Native mode intended for working with system fonts + Custom //!< Custom mode intended for working with manually defined set of fonts + } Mode; + public: QtxFontEdit( const int, QWidget* = 0 ); QtxFontEdit( QWidget* = 0 ); @@ -67,9 +75,22 @@ public: int features() const; void setFeatures( const int ); + void setMode( const int ); + int mode() const; + + void setFonts( const QStringList& ); + QStringList fonts() const; + + void setSizes( const QList& = QList() ); + QList sizes() const; + +signals: + void changed( const QFont& ); + private slots: void onPreview( bool ); void onFontChanged( const QFont& ); + void onPropertyChanged(); private: void initialize(); @@ -80,7 +101,9 @@ private: QFontComboBox* myFamily; QToolButton* myPreview; int myFeatures; - QToolButton *myB, *myI, *myU; + QToolButton *myB, *myI, *myU, *myS; + int myMode; + QComboBox* myCustomFams; }; #endif // QTXFONTEDIT_H diff --git a/src/Qtx/QtxMainWindow.cxx b/src/Qtx/QtxMainWindow.cxx index 8e77da2e1..dad786f4a 100644 --- a/src/Qtx/QtxMainWindow.cxx +++ b/src/Qtx/QtxMainWindow.cxx @@ -22,11 +22,15 @@ #include "QtxMainWindow.h" #include "QtxToolBar.h" -#include "QtxResourceMgr.h" #include +#include +#include +#include #include #include +#include +#include #include #include @@ -89,6 +93,131 @@ bool QtxMainWindow::Filter::eventFilter( QObject* o, QEvent* e ) return QObject::eventFilter( o, e ); } +/*! + \class QtxMainWindow::Resizer + \internal + \brief Internal object used to resize dock widgets. +*/ + +class QtxMainWindow::Resizer : public QObject +{ +public: + Resizer( const QPoint&, const Qt::Orientation, QtxMainWindow* ); + virtual ~Resizer(); + + QMouseEvent* finalEvent() const; + void setFinalEvent( QMouseEvent* ); + + void setPosition( const QPoint& ); + virtual bool eventFilter( QObject*, QEvent* ); + +private: + void setFilters( bool ); + +private: + QPoint myPos; + QMainWindow* myMain; + QRubberBand* myRubber; + Qt::Orientation myOrient; + QMouseEvent* myFinEvent; +}; + +/*! + \brief Constructor. + \param mw parent main window +*/ +QtxMainWindow::Resizer::Resizer( const QPoint& p, const Qt::Orientation o, QtxMainWindow* mw ) +: QObject( mw ), + myMain( mw ), + myOrient( o ), + myFinEvent( 0 ) +{ + setFilters( true ); + + myRubber = new QRubberBand( QRubberBand::Line, 0 ); + + setPosition( p ); + + myRubber->show(); +}; + +/*! + \brief Destructor. +*/ +QtxMainWindow::Resizer::~Resizer() +{ + delete myRubber; + + setFilters( false ); +} + +void QtxMainWindow::Resizer::setPosition( const QPoint& pos ) +{ + myPos = pos; + if ( myRubber ) { + QRect r; + QPoint min = myMain->mapToGlobal( myMain->rect().topLeft() ); + QPoint max = myMain->mapToGlobal( myMain->rect().bottomRight() ); + if ( myOrient == Qt::Horizontal ) { + int p = qMax( qMin( myPos.y(), max.y() ), min.y() ); + r = QRect( myMain->mapToGlobal( QPoint( 0, 0 ) ).x(), p - 1, myMain->width(), 3 ); + } + else { + int p = qMax( qMin( myPos.x(), max.x() ), min.x() ); + r = QRect( p - 1, myMain->mapToGlobal( QPoint( 0, 0 ) ).y(), 3, myMain->height() ); + } + myRubber->setGeometry( r ); + } +} + +QMouseEvent* QtxMainWindow::Resizer::finalEvent() const +{ + return myFinEvent; +} + +void QtxMainWindow::Resizer::setFinalEvent( QMouseEvent* e ) +{ + myFinEvent = e; +} + +/*! + \brief Event filter. + + \param o recevier object + \param e event +*/ +bool QtxMainWindow::Resizer::eventFilter( QObject* o, QEvent* e ) +{ + if ( e->type() == QEvent::Timer ) { + if ( !finalEvent() ) + return true; + + setFilters( false ); + QApplication::postEvent( myMain, finalEvent() ); + myFinEvent = 0; + deleteLater(); + } + + return QObject::eventFilter( o, e ); +} + +void QtxMainWindow::Resizer::setFilters( bool on ) +{ + if ( myMain ) { + if ( on ) + myMain->layout()->installEventFilter( this ); + else + myMain->layout()->removeEventFilter( this ); + } + + QTimer* t = qFindChild( myMain->layout() ); + if ( t ) { + if ( on ) + t->installEventFilter( this ); + else + t->removeEventFilter( this ); + } +} /*! \class QtxMainWindow @@ -104,7 +233,10 @@ bool QtxMainWindow::Filter::eventFilter( QObject* o, QEvent* e ) QtxMainWindow::QtxMainWindow( QWidget* parent, Qt::WindowFlags f ) : QMainWindow( parent, f ), myMenuBar( 0 ), - myStatusBar( 0 ) + myStatusBar( 0 ), + myOpaque( true ), + myResizer( 0 ), + myMouseMove( 0 ) { } @@ -213,6 +345,16 @@ void QtxMainWindow::setDockableStatusBar( const bool on ) } } +bool QtxMainWindow::isOpaqueResize() const +{ + return myOpaque; +} + +void QtxMainWindow::setOpaqueResize( bool on ) +{ + myOpaque = on; +} + /*! \brief Dump main window geometry to the string. \return string represenation of the window geometry @@ -410,3 +552,53 @@ void QtxMainWindow::onDestroyed( QObject* obj ) } } +bool QtxMainWindow::event( QEvent* e ) +{ + if ( myResizer && e->type() == QEvent::MouseButtonRelease ) { + if ( myMouseMove ) { + QMainWindow::event( myMouseMove ); + delete myMouseMove; + myMouseMove = 0; + } + + QMouseEvent* me = static_cast( e ); + myResizer->setFinalEvent( new QMouseEvent( me->type(), me->pos(), me->globalPos(), + me->button(), me->buttons(), me->modifiers() ) ); + myResizer = 0; + return true; + } + + if ( myResizer && e->type() == QEvent::MouseMove ) { + QMouseEvent* me = static_cast( e ); + myMouseMove = new QMouseEvent( me->type(), me->pos(), me->globalPos(), + me->button(), me->buttons(), me->modifiers() ); + myResizer->setPosition( me->globalPos() ); + } + + bool ok = QMainWindow::event( e ); + + if ( e->type() == QEvent::MouseButtonPress ) { + if ( !isOpaqueResize() && ok && testAttribute( Qt::WA_SetCursor ) ) { + bool status = true; + Qt::Orientation o; + switch ( cursor().shape() ) + { + case Qt::SplitHCursor: + o = Qt::Vertical; + break; + case Qt::SplitVCursor: + o = Qt::Horizontal; + break; + default: + status = false; + break; + } + if ( status ) { + QMouseEvent* me = static_cast( e ); + myResizer = new Resizer( me->globalPos(), o, this ); + } + } + } + + return ok; +} diff --git a/src/Qtx/QtxMainWindow.h b/src/Qtx/QtxMainWindow.h index 10accc407..d574cf024 100644 --- a/src/Qtx/QtxMainWindow.h +++ b/src/Qtx/QtxMainWindow.h @@ -33,11 +33,15 @@ class QTX_EXPORT QtxMainWindow : public QMainWindow Q_OBJECT class Filter; + class Resizer; public: QtxMainWindow( QWidget* = 0, Qt::WindowFlags = 0 ); virtual ~QtxMainWindow(); + bool isOpaqueResize() const; + void setOpaqueResize( bool ); + bool isDockableMenuBar() const; void setDockableMenuBar( const bool ); @@ -47,6 +51,9 @@ public: QString storeGeometry() const; void retrieveGeometry( const QString& ); +protected: + virtual bool event( QEvent* ); + private slots: void onDestroyed( QObject* ); @@ -56,6 +63,10 @@ private: private: QToolBar* myMenuBar; //!< dockable menu bar QToolBar* myStatusBar; //!< dockable status bar + + bool myOpaque; + Resizer* myResizer; + QMouseEvent* myMouseMove; }; #endif // QTXMAINWINDOW_H diff --git a/src/Qtx/QtxPagePrefMgr.cxx b/src/Qtx/QtxPagePrefMgr.cxx index cc8c5cefd..f9b422156 100644 --- a/src/Qtx/QtxPagePrefMgr.cxx +++ b/src/Qtx/QtxPagePrefMgr.cxx @@ -26,11 +26,14 @@ #include "QtxGroupBox.h" #include "QtxComboBox.h" #include "QtxIntSpinBox.h" +#include "QtxResourceMgr.h" #include "QtxColorButton.h" +#include "QtxShortcutEdit.h" #include "QtxDoubleSpinBox.h" #include +#include #include #include #include @@ -2081,6 +2084,36 @@ void QtxPagePrefCheckItem::retrieve() for string, integer and double values. */ +static void fixupAndSet( QLineEdit* le, const QString& txt ) +{ + if ( le ) { + QString val = txt; + if ( le->validator() ) { + const QDoubleValidator* de = dynamic_cast( le->validator() ); + if ( de ) { + int dec = de->decimals(); + int idx = val.lastIndexOf( QRegExp( QString("[.|%1]").arg( le->locale().decimalPoint () ) ) ); + if ( idx >= 0 ) { + QString tmp = val.mid( idx+1 ); + QString exp; + val = val.left( idx+1 ); + idx = tmp.indexOf( QRegExp( QString("[e|E]") ) ); + if ( idx >= 0 ) { + exp = tmp.mid( idx ); + tmp = tmp.left( idx ); + } + tmp.truncate( dec ); + val = val + tmp + exp; + } + } + int pos = 0; + if ( le->validator()->validate( val, pos ) == QValidator::Invalid ) + val.clear(); + } + le->setText( val ); + } +} + /*! \brief Constructor. @@ -2094,7 +2127,9 @@ void QtxPagePrefCheckItem::retrieve() QtxPagePrefEditItem::QtxPagePrefEditItem( const QString& title, QtxPreferenceItem* parent, const QString& sect, const QString& param ) : QtxPageNamedPrefItem( title, parent, sect, param ), - myType( String ) + myType( String ), + myDecimals( 1000 ), + myEchoMode( 0 ) { setControl( myEditor = new QLineEdit() ); updateEditor(); @@ -2116,7 +2151,9 @@ QtxPagePrefEditItem::QtxPagePrefEditItem( const int type, const QString& title, QtxPreferenceItem* parent, const QString& sect, const QString& param ) : QtxPageNamedPrefItem( title, parent, sect, param ), - myType( type ) + myType( type ), + myDecimals( 1000 ), + myEchoMode( 0 ) { setControl( myEditor = new QLineEdit() ); updateEditor(); @@ -2153,6 +2190,54 @@ void QtxPagePrefEditItem::setInputType( const int type ) updateEditor(); } +/*! + \brief Get number of digits after decimal point (for Double input type) + \return preference item decimals value + \sa setDecimals() +*/ +int QtxPagePrefEditItem::decimals() const +{ + return myDecimals; +} + +/*! + \brief Set number of digits after decimal point (for Double input type) + \param dec new preference item decimals value + \sa decimals() +*/ +void QtxPagePrefEditItem::setDecimals( const int dec ) +{ + if ( myDecimals == dec ) + return; + + myDecimals = dec; + updateEditor(); +} + +/*! + \brief Get the line edit's echo mode + \return preference item echo mode value + \sa setEchoMode() +*/ +int QtxPagePrefEditItem::echoMode() const +{ + return myEchoMode; +} + +/*! + \brief Set the line edit's echo mode + \param echo new preference item echo mode value + \sa echoMode() +*/ +void QtxPagePrefEditItem::setEchoMode( const int echo ) +{ + if ( myEchoMode == echo ) + return; + + myEchoMode = echo; + updateEditor(); +} + /*! \brief Store preference item to the resource manager. \sa retrieve() @@ -2169,13 +2254,7 @@ void QtxPagePrefEditItem::store() void QtxPagePrefEditItem::retrieve() { QString txt = getString(); - if ( myEditor->validator() ) - { - int pos = 0; - if ( myEditor->validator()->validate( txt, pos ) == QValidator::Invalid ) - txt.clear(); - } - myEditor->setText( txt ); + fixupAndSet( myEditor, txt ); } /*! @@ -2188,6 +2267,10 @@ QVariant QtxPagePrefEditItem::optionValue( const QString& name ) const { if ( name == "input_type" || name == "type" ) return inputType(); + else if ( name == "precision" || name == "prec" || name == "decimals" ) + return decimals(); + else if ( name == "echo" || name == "echo_mode" || name == "echomode") + return echoMode(); else return QtxPageNamedPrefItem::optionValue( name ); } @@ -2205,6 +2288,14 @@ void QtxPagePrefEditItem::setOptionValue( const QString& name, const QVariant& v if ( val.canConvert( QVariant::Int ) ) setInputType( val.toInt() ); } + else if ( name == "precision" || name == "prec" || name == "decimals" ) { + if ( val.canConvert( QVariant::Int ) ) + setDecimals( val.toInt() ); + } + else if ( name == "echo" || name == "echo_mode" || name == "echomode") { + if ( val.canConvert( QVariant::Int ) ) + setEchoMode( val.toInt() ); + } else QtxPageNamedPrefItem::setOptionValue( name, val ); } @@ -2214,6 +2305,24 @@ void QtxPagePrefEditItem::setOptionValue( const QString& name, const QVariant& v */ void QtxPagePrefEditItem::updateEditor() { + switch (myEchoMode) + { + case 0: + myEditor->setEchoMode(QLineEdit::Normal); + break; + case 1: + myEditor->setEchoMode(QLineEdit::NoEcho); + break; + case 2: + myEditor->setEchoMode(QLineEdit::Password); + break; + case 3: + myEditor->setEchoMode(QLineEdit::PasswordEchoOnEdit); + break; + default: + myEditor->setEchoMode(QLineEdit::Normal); + } + QValidator* val = 0; switch ( inputType() ) { @@ -2222,21 +2331,301 @@ void QtxPagePrefEditItem::updateEditor() break; case Double: val = new QDoubleValidator( myEditor ); + dynamic_cast( val )->setDecimals( myDecimals < 0 ? 1000 : myDecimals ); break; default: break; } - if ( !myEditor->text().isEmpty() && val ) + QString txt = myEditor->text(); + delete myEditor->validator(); + myEditor->setValidator( val ); + fixupAndSet( myEditor, txt ); +} + +/*! + \class QtxPagePrefSliderItem +*/ + +/*! + \brief Constructor. + + Creates preference item with slider widget + + \param title preference item title + \param parent parent preference item + \param sect resource file section associated with the preference item + \param param resource file parameter associated with the preference item +*/ +QtxPagePrefSliderItem::QtxPagePrefSliderItem( const QString& title, QtxPreferenceItem* parent, + const QString& sect, const QString& param ) +: QtxPageNamedPrefItem( title, parent, sect, param ) { - int pos = 0; - QString str = myEditor->text(); - if ( val->validate( str, pos ) == QValidator::Invalid ) - myEditor->clear(); + setControl( mySlider = new QSlider( Qt::Horizontal ) ); + widget()->layout()->addWidget( myLabel = new QLabel( ) ); + + setMinimum( 0 ); + setMaximum( 0 ); + setSingleStep( 1 ); + setPageStep( 1 ); + + mySlider->setTickPosition( QSlider::TicksBothSides ); + + connect (mySlider, SIGNAL(valueChanged(int)), this, SLOT(setIcon(int))); + updateSlider(); } - delete myEditor->validator(); - myEditor->setValidator( val ); +/*! + \brief Destructor. +*/ +QtxPagePrefSliderItem::~QtxPagePrefSliderItem() +{ +} + +/*! + \brief Get slider preference item step value. + \return slider single step value + \sa setSingleStep() +*/ +int QtxPagePrefSliderItem::singleStep() const +{ + return mySlider->singleStep(); +} + +/*! + \brief Get slider preference item step value. + \return slider page step value + \sa setPageStep() +*/ +int QtxPagePrefSliderItem::pageStep() const +{ + return mySlider->pageStep(); +} + +/*! + \brief Get slider preference item minimum value. + \return slider minimum value + \sa setMinimum() +*/ +int QtxPagePrefSliderItem::minimum() const +{ + return mySlider->minimum(); +} + +/*! + \brief Get slider preference item maximum value. + \return slider maximum value + \sa setMaximum() +*/ +int QtxPagePrefSliderItem::maximum() const +{ + return mySlider->maximum(); +} + +/*! + \brief Get the list of the icons associated with the selection widget items + \return list of icons + \sa setIcons() +*/ +QList QtxPagePrefSliderItem::icons() const +{ + return myIcons; +} + +/*! + \brief Set slider preference item step value. + \param step new slider single step value + \sa step() +*/ +void QtxPagePrefSliderItem::setSingleStep( const int& step ) +{ + mySlider->setSingleStep( step ); +} + +/*! + \brief Set slider preference item step value. + \param step new slider single step value + \sa step() +*/ +void QtxPagePrefSliderItem::setPageStep( const int& step ) +{ + mySlider->setPageStep( step ); +} + +/*! + \brief Set slider preference item minimum value. + \param min new slider minimum value + \sa minimum() +*/ +void QtxPagePrefSliderItem::setMinimum( const int& min ) +{ + mySlider->setMinimum( min ); + setIcon( mySlider->value() ); +} + +/*! + \brief Set slider preference item maximum value. + \param max new slider maximum value + \sa maximum() +*/ +void QtxPagePrefSliderItem::setMaximum( const int& max ) +{ + mySlider->setMaximum( max ); + setIcon( mySlider->value() ); +} + +/*! + \brief Set the list of the icons to the selection widget items + \param icns new list of icons + \sa icons() +*/ +void QtxPagePrefSliderItem::setIcons( const QList& icns ) +{ + if ( icns.isEmpty() ) + return; + myIcons = icns; + /* + QSize maxsize( 0, 0 ); + for ( QList::const_iterator it = myIcons.begin(); it != myIcons.end(); ++it ) { + const QIcon& ico = *it; + if ( ico.isNull() ) + continue; + + maxsize = maxsize.expandedTo( ico.availableSizes().first() ); + } + myLabel->setFixedSize( maxsize ); + */ + updateSlider(); +} + +/*! + \brief Store preference item to the resource manager. + \sa retrieve() +*/ +void QtxPagePrefSliderItem::store() +{ + setInteger( mySlider->value() ); +} + +/*! + \brief Retrieve preference item from the resource manager. + \sa store() +*/ +void QtxPagePrefSliderItem::retrieve() +{ + mySlider->setValue( getInteger( mySlider->value() ) ); +} + +/*! + \brief Get preference item option value. + \param name option name + \return property value or null integer if option is not set + \sa setOptionValue() +*/ +QVariant QtxPagePrefSliderItem::optionValue( const QString& name ) const +{ + if ( name == "minimum" || name == "min" ) + return minimum(); + else if ( name == "maximum" || name == "max" ) + return maximum(); + else if ( name == "single_step" ) + return singleStep(); + else if ( name == "page_step" ) + return pageStep(); + else if ( name == "icons" || name == "pixmaps" ) + { + QList lst; + QList ics = icons(); + for ( QList::const_iterator it = ics.begin(); it != ics.end(); ++it ) + lst.append( *it ); + return lst; + } + else + return QtxPageNamedPrefItem::optionValue( name ); +} + +/*! + \brief Set preference item option value. + \param name option name + \param val new property value + \sa optionValue() +*/ +void QtxPagePrefSliderItem::setOptionValue( const QString& name, const QVariant& val ) +{ + if ( val.canConvert( QVariant::Int ) ) + { + if ( name == "minimum" || name == "min" ) + setMinimum( val.toInt() ); + else if ( name == "maximum" || name == "max" ) + setMaximum( val.toInt() ); + else if ( name == "single_step" ) + setSingleStep( val.toInt() ); + else if ( name == "page_step" ) + setPageStep( val.toInt() ); + else + QtxPageNamedPrefItem::setOptionValue( name, val ); + } + else if ( name == "icons" || name == "pixmaps" ) + setIcons( val ); + else + QtxPageNamedPrefItem::setOptionValue( name, val ); +} + +void QtxPagePrefSliderItem::setIcon( int pos ) +{ + int index = pos - mySlider->minimum(); + if ( !myIcons.isEmpty() && index >= 0 && index < myIcons.size() && !myIcons[index].isNull() ) + myLabel->setPixmap( myIcons[index].pixmap( /*myIcons[index].availableSizes().first()*/myLabel->size() ) ); + else + myLabel->clear(); +} + +/*! + \brief Update slider widget. +*/ +void QtxPagePrefSliderItem::updateSlider() +{ + int val = mySlider->value(); + int stp = singleStep(); + int ptp = pageStep(); + int min = minimum(); + int max = maximum(); + + control()->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); + mySlider->setFocusPolicy(Qt::StrongFocus); + + mySlider->setValue( val ); + setSingleStep( stp ); + setPageStep( ptp ); + setMinimum( min ); + setMaximum( max ); + + myLabel->setVisible( !myIcons.empty() ); + widget()->layout()->setSpacing( !myIcons.empty() ? 6 : 0 ); +} + +/*! + \brief Set the list of the icons from the resource manager. + \param var new icons list + \internal +*/ +void QtxPagePrefSliderItem::setIcons( const QVariant& var ) +{ + if ( var.type() != QVariant::List ) + return; + + QList lst; + QList varList = var.toList(); + for ( QList::const_iterator it = varList.begin(); it != varList.end(); ++it ) + { + if ( (*it).canConvert() ) + lst.append( (*it).value() ); + else if ( (*it).canConvert() ) + lst.append( (*it).value() ); + else + lst.append( QIcon() ); + } + setIcons( lst ); } /*! @@ -2354,7 +2743,7 @@ QList QtxPagePrefSelectItem::numbers() const } /*! - \brief Get the list of the icons from the selection widget. + \brief Get the list of the icons associated with the selection widget.items \return list of icons \sa strings(), numbers(), setIcons() */ @@ -2369,7 +2758,7 @@ QList QtxPagePrefSelectItem::icons() const /*! \brief Set the list of the values to the selection widget. \param lst new list of values - \sa strings(), setNumbers() + \sa strings(), setNumbers(), setIcons() */ void QtxPagePrefSelectItem::setStrings( const QStringList& lst ) { @@ -2378,26 +2767,33 @@ void QtxPagePrefSelectItem::setStrings( const QStringList& lst ) } /*! - \brief Set the list of the values identifiers to the selection widget. + \brief Set the list of the values identifiers to the selection widget \param ids new list of values IDs - \sa numbers(), setStrings() + \sa numbers(), setStrings(), setIcons() */ void QtxPagePrefSelectItem::setNumbers( const QList& ids ) { int i = 0; - for ( QList::const_iterator it = ids.begin(); it != ids.end() && i < mySelector->count(); ++it, i++ ) + for ( QList::const_iterator it = ids.begin(); it != ids.end(); ++it, i++ ) { + if ( i >= mySelector->count() ) + mySelector->addItem( QString( "" ) ); + mySelector->setId( i, *it ); + } } /*! - \brief Set the list of the icons to the selection widget. - \param lst new list of icons - \sa numbers(), strings(), setIcons() + \brief Set the list of the icons to the selection widget items + + Important: call this method after setStrings() or setNumbers() + + \param icns new list of icons + \sa icons(), setStrings(), setNumbers() */ -void QtxPagePrefSelectItem::setIcons( const QList& icons ) +void QtxPagePrefSelectItem::setIcons( const QList& icns ) { int i = 0; - for ( QList::const_iterator it = icons.begin(); it != icons.end() && i < mySelector->count(); ++it, i++ ) + for ( QList::const_iterator it = icns.begin(); it != icns.end() && i < mySelector->count(); ++it, i++ ) mySelector->setItemIcon( i, *it ); } @@ -2478,6 +2874,14 @@ QVariant QtxPagePrefSelectItem::optionValue( const QString& name ) const lst.append( *it ); return lst; } + else if ( name == "icons" || name == "pixmaps" ) + { + QList lst; + QList ics = icons(); + for ( QList::const_iterator it = ics.begin(); it != ics.end(); ++it ) + lst.append( *it ); + return lst; + } else return QtxPageNamedPrefItem::optionValue( name ); } @@ -2499,6 +2903,8 @@ void QtxPagePrefSelectItem::setOptionValue( const QString& name, const QVariant& setStrings( val ); else if ( name == "numbers" || name == "ids" || name == "indexes" ) setNumbers( val ); + else if ( name == "icons" || name == "pixmaps" ) + setIcons( val ); else QtxPageNamedPrefItem::setOptionValue( name, val ); } @@ -2536,6 +2942,30 @@ void QtxPagePrefSelectItem::setNumbers( const QVariant& var ) setNumbers( lst ); } +/*! + \brief Set the list of the icons from the resource manager. + \param var new icons list + \internal +*/ +void QtxPagePrefSelectItem::setIcons( const QVariant& var ) +{ + if ( var.type() != QVariant::List ) + return; + + QList lst; + QList varList = var.toList(); + for ( QList::const_iterator it = varList.begin(); it != varList.end(); ++it ) + { + if ( (*it).canConvert() ) + lst.append( (*it).value() ); + else if ( (*it).canConvert() ) + lst.append( (*it).value() ); + else + lst.append( QIcon() ); + } + setIcons( lst ); +} + /*! \brief Update selector widget. */ @@ -2659,6 +3089,19 @@ QVariant QtxPagePrefSpinItem::step() const return QVariant(); } +/*! + \brief Get double spin box preference item precision value. + \return double spin box precision + \sa setPrecision() +*/ +QVariant QtxPagePrefSpinItem::precision() const +{ + if ( QtxDoubleSpinBox* dsb = ::qobject_cast( control() ) ) + return dsb->decimals(); + else + return QVariant(); +} + /*! \brief Get spin box preference item minimum value. \return spin box minimum value @@ -2753,6 +3196,22 @@ void QtxPagePrefSpinItem::setStep( const QVariant& step ) } } +/*! + \brief Set double spin box preference item precision value. + \param step new double spin box precision value + \sa precision() +*/ +void QtxPagePrefSpinItem::setPrecision( const QVariant& prec ) +{ + if ( QtxDoubleSpinBox* dsb = ::qobject_cast( control() ) ) + { + if ( prec.canConvert( QVariant::Int ) ) { + dsb->setDecimals( qAbs( prec.toInt() ) ); + dsb->setPrecision( prec.toInt() ); + } + } +} + /*! \brief Set spin box preference item minimum value. \param min new spin box minimum value @@ -2870,6 +3329,8 @@ QVariant QtxPagePrefSpinItem::optionValue( const QString& name ) const return maximum(); else if ( name == "step" ) return step(); + else if ( name == "precision" ) + return precision(); else if ( name == "prefix" ) return prefix(); else if ( name == "suffix" ) @@ -2899,6 +3360,8 @@ void QtxPagePrefSpinItem::setOptionValue( const QString& name, const QVariant& v setMaximum( val ); else if ( name == "step" ) setStep( val ); + else if ( name == "precision" ) + setPrecision( val ); else if ( name == "prefix" ) { if ( val.canConvert( QVariant::String ) ) @@ -2925,6 +3388,7 @@ void QtxPagePrefSpinItem::updateSpinBox() { QVariant val; QVariant stp = step(); + QVariant prec = precision(); QVariant min = minimum(); QVariant max = maximum(); @@ -2948,6 +3412,7 @@ void QtxPagePrefSpinItem::updateSpinBox() control()->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ); setStep( stp ); + setPrecision( prec ); setMinimum( min ); setMaximum( max ); @@ -3137,6 +3602,72 @@ void QtxPagePrefFontItem::setFeatures( const int f ) myFont->setFeatures( f ); } +/*! + \brief Specifies whether widget works in Native or Custom mode. Native mode + is intended for working with system fonts. Custom mode is intended for + working with manually defined set of fonts. Set of custom fonts can be + specified with setFonts() method + \param mode mode from QtxFontEdit::Mode enumeration + \sa mode() +*/ +void QtxPagePrefFontItem::setMode( const int mode ) +{ + myFont->setMode( mode ); +} + +/*! + \brief Verifies whether widget works in Native or Custom mode + \return Native or Custom mode + \sa setMode() +*/ +int QtxPagePrefFontItem::mode() const +{ + return myFont->mode(); +} + +/*! + \brief Sets list of custom fonts. + This method is intended for working in Custom mode only. + \param fams list of families + \sa fonts(), setMode() +*/ +void QtxPagePrefFontItem::setFonts( const QStringList& fams ) +{ + myFont->setFonts( fams ); +} + +/*! + \brief Gets list of custom fonts + \return list of families + \sa setFonts(), setMode() +*/ +QStringList QtxPagePrefFontItem::fonts() const +{ + return myFont->fonts(); +} + +/*! + \brief Sets list of available font sizes. + This method is intended for working in Custom mode only. The list of sizes can + be empty. In this case system generate listof size automatically from 8 till 72. + \param sizes list of sizes + \sa sizes(), setMode() +*/ +void QtxPagePrefFontItem::setSizes( const QList& sizes ) +{ + myFont->setSizes( sizes ); +} + +/*! + \brief Gets list of custom fonts + \return list of families + \sa setFonts(), setMode() +*/ +QList QtxPagePrefFontItem::sizes() const +{ + return myFont->sizes(); +} + /*! \brief Store preference item to the resource manager. \sa retrieve() @@ -3165,6 +3696,18 @@ QVariant QtxPagePrefFontItem::optionValue( const QString& name ) const { if ( name == "features" ) return features(); + else if ( name == "mode" ) + return mode(); + else if ( name == "fonts" || name == "families" ) + return fonts(); + else if ( name == "sizes" ) + { + QList lst; + QList nums = sizes(); + for ( QList::const_iterator it = nums.begin(); it != nums.end(); ++it ) + lst.append( *it ); + return lst; + } else return QtxPageNamedPrefItem::optionValue( name ); } @@ -3182,6 +3725,30 @@ void QtxPagePrefFontItem::setOptionValue( const QString& name, const QVariant& v if ( val.canConvert( QVariant::Int ) ) setFeatures( val.toInt() ); } + else if ( name == "mode" ) + { + if ( val.canConvert( QVariant::Int ) ) + setMode( val.toInt() ); + } + else if ( name == "fonts" || name == "families" ) + { + if ( val.canConvert( QVariant::StringList ) ) + setFonts( val.toStringList() ); + } + else if ( name == "sizes" ) + { + if ( val.type() == QVariant::List ) + { + QList lst; + QList varList = val.toList(); + for ( QList::const_iterator it = varList.begin(); it != varList.end(); ++it ) + { + if ( (*it).canConvert( QVariant::Int ) ) + lst.append( (*it).toInt() ); + } + setSizes( lst ); + } + } else QtxPageNamedPrefItem::setOptionValue( name, val ); } @@ -3828,3 +4395,165 @@ void QtxPagePrefStyleItem::retrieve() myStyle->setCleared( true ); */ } + +/*! + \brief Constructor. + \param title preference item title + \param parent parent preference item + \param sect resource file section associated with the preference item + \param param resource file parameter associated with the preference item +*/ +QtxPagePrefShortcutBtnsItem::QtxPagePrefShortcutBtnsItem( const QString& title, QtxPreferenceItem* parent, const QString& sect, + const QString& param ): QtxPageNamedPrefItem( title, parent, sect, param ) +{ + setControl( myShortcut = new QtxShortcutEdit() ); +} + +/*! + \brief Destructor. +*/ +QtxPagePrefShortcutBtnsItem::~QtxPagePrefShortcutBtnsItem() +{ +} + +/*! + \brief Store preference item to the resource manager. + \sa retrieve() +*/ +void QtxPagePrefShortcutBtnsItem::store() +{ + setString( myShortcut->shortcut().toString() ); +} + +/*! + \brief Retrieve preference item from the resource manager. + \sa store() +*/ +void QtxPagePrefShortcutBtnsItem::retrieve() +{ + myShortcut->setShortcut( QKeySequence::fromString( getString() ) ); +} + +/*! + \brief Constructor. + + Creates preference item for editing of key bindings + + \param title preference item title + \param parent parent preference item + \param sect resource file section associated with the preference item + \param param resource file parameter associated with the preference item +*/ +QtxPagePrefShortcutTreeItem::QtxPagePrefShortcutTreeItem( const QString& title, QtxPreferenceItem* parent, + const QString& sect, const QString& param ) +: QtxPageNamedPrefItem( title, parent, sect, param ) +{ + myShortcutTree = new QtxShortcutTree(); + + // Retrieve shortcuts common sections from resources + QtxResourceMgr* resMgr = resourceMgr(); + if ( resMgr ) { + QStringList sectionsList = resourceMgr()->subSections( sect, false ); + myShortcutTree->setGeneralSections( sectionsList ); + myShortcutTree->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); + } + + setControl( myShortcutTree ); +} + +/*! + \brief Destructor. +*/ +QtxPagePrefShortcutTreeItem::~QtxPagePrefShortcutTreeItem() +{ +} + +/*! + \brief Retrieve preference item from the resource manager. + \sa store() +*/ +void QtxPagePrefShortcutTreeItem::retrieve() +{ + QtxResourceMgr* resMgr = resourceMgr(); + if ( resMgr ) { + QString sect, param; + resource( sect, param ); + QStringList secLst = resMgr->subSections( sect, false ); + ShortcutMap aMap; + QStringList paramLst; + for ( int i = 0; i < secLst.size(); i++ ) { + QString section = sect + resMgr->sectionsToken() + secLst.at( i ); + paramLst = resMgr->parameters( QStringList() << sect << secLst.at( i ) ); + QMap nameMap = paramToName( section ); + for ( int j = 0; j < paramLst.size(); j++ ) { + QString action = nameMap.contains( paramLst.at( j ) ) ? nameMap[paramLst.at( j )] : paramLst.at( j ); + resMgr->value( section, paramLst.at( j ), aMap[action], false ); + } + myShortcutTree->setBindings( secLst.at( i ), aMap ); + aMap.clear(); + } + } +} + +/*! + \brief Store preference item to the resource manager. + \sa retrieve() +*/ +void QtxPagePrefShortcutTreeItem::store() +{ + QString aSection; + QStringList lst = myShortcutTree->sections(); + QtxResourceMgr* resMgr = resourceMgr(); + + QString sect, param; + resource( sect, param ); + + if ( resMgr ) { + for ( int i = 0; i < lst.size(); i++ ) { + ShortcutMap* aMap( myShortcutTree->bindings( lst.at( i ) ) ); + aSection = sect + resMgr->sectionsToken() + lst.at( i ); + QMap paramMap = nameToParam( aSection ); + for ( ShortcutMap::const_iterator it = aMap->constBegin(); it != aMap->constEnd(); ++it ) { + QString param = paramMap.contains( it.key() ) ? paramMap[it.key()] : it.key(); + resMgr->setValue( aSection, param, it.value() ); + } + } + } +} + +QMap QtxPagePrefShortcutTreeItem::paramToName( const QString& sect ) const +{ + QMap aMap; + QtxResourceMgr* resMgr = resourceMgr(); + if ( resMgr ) { + QStringList paramLst = resMgr->parameters( sect ); + for ( QStringList::iterator it = paramLst.begin(); it != paramLst.end(); ++it ) + aMap.insert( *it, actionName( *it ) ); + } + + return aMap; +} + +QMap QtxPagePrefShortcutTreeItem::nameToParam( const QString& sect ) const +{ + QMap aMap; + QtxResourceMgr* resMgr = resourceMgr(); + if ( resMgr ) { + QStringList paramLst = resMgr->parameters( sect ); + for ( QStringList::iterator it = paramLst.begin(); it != paramLst.end(); ++it ) + aMap.insert( actionName( *it ), *it ); + } + + return aMap; +} + +QtxShortcutTree* QtxPagePrefShortcutTreeItem::shortcutTree() const +{ + return myShortcutTree; +} + +QString QtxPagePrefShortcutTreeItem::actionName( const QString& param ) const +{ + QString res = QApplication::tr( "", (const char*)param.toLatin1() ); + return res.isEmpty() ? param : res; +} diff --git a/src/Qtx/QtxPagePrefMgr.h b/src/Qtx/QtxPagePrefMgr.h index 922cc94f5..bdaa048ce 100644 --- a/src/Qtx/QtxPagePrefMgr.h +++ b/src/Qtx/QtxPagePrefMgr.h @@ -36,7 +36,10 @@ class QtxFontEdit; class QtxGroupBox; class QtxComboBox; class QtxColorButton; +class QtxShortcutEdit; +class QtxShortcutTree; +class QSlider; class QToolBox; class QComboBox; class QLineEdit; @@ -404,6 +407,12 @@ public: int inputType() const; void setInputType( const int ); + int decimals() const; + void setDecimals( const int ); + + int echoMode() const; + void setEchoMode( const int ); + virtual void store(); virtual void retrieve(); @@ -416,9 +425,52 @@ private: private: int myType; + int myDecimals; + int myEchoMode; QLineEdit* myEditor; }; +class QTX_EXPORT QtxPagePrefSliderItem : public QObject,public QtxPageNamedPrefItem +{ + Q_OBJECT + +public: + QtxPagePrefSliderItem( const QString&, QtxPreferenceItem* = 0, + const QString& = QString(), const QString& = QString() ); + virtual ~QtxPagePrefSliderItem(); + + int singleStep() const; + int pageStep() const; + int minimum() const; + int maximum() const; + QList icons() const; + + void setSingleStep( const int& ); + void setPageStep( const int& ); + void setMinimum( const int& ); + void setMaximum( const int& ); + void setIcons( const QList& ); + + virtual void store(); + virtual void retrieve(); + +protected: + virtual QVariant optionValue( const QString& ) const; + virtual void setOptionValue( const QString&, const QVariant& ); + +private slots: + void setIcon( int ); + +private: + void updateSlider(); + void setIcons( const QVariant& ); + +private: + QSlider* mySlider; + QLabel* myLabel; + QList myIcons; +}; + class QTX_EXPORT QtxPagePrefSelectItem : public QtxPageNamedPrefItem { public: @@ -453,6 +505,7 @@ private: void updateSelector(); void setStrings( const QVariant& ); void setNumbers( const QVariant& ); + void setIcons( const QVariant& ); private: int myType; @@ -472,6 +525,7 @@ public: virtual ~QtxPagePrefSpinItem(); QVariant step() const; + QVariant precision() const; QVariant minimum() const; QVariant maximum() const; @@ -480,6 +534,7 @@ public: QString specialValueText() const; void setStep( const QVariant& ); + void setPrecision( const QVariant& ); void setMinimum( const QVariant& ); void setMaximum( const QVariant& ); @@ -548,6 +603,15 @@ public: int features() const; void setFeatures( const int ); + void setMode( const int ); + int mode() const; + + void setFonts( const QStringList& ); + QStringList fonts() const; + + void setSizes( const QList& = QList() ); + QList sizes() const; + virtual void store(); virtual void retrieve(); @@ -668,4 +732,39 @@ private: QComboBox* myStyle; }; +class QTX_EXPORT QtxPagePrefShortcutBtnsItem : public QtxPageNamedPrefItem +{ +public: + QtxPagePrefShortcutBtnsItem( const QString&, QtxPreferenceItem* = 0, + const QString& = QString(), const QString& = QString() ); + virtual ~QtxPagePrefShortcutBtnsItem(); + virtual void store(); + virtual void retrieve(); + +private: + QtxShortcutEdit* myShortcut; +}; + +class QTX_EXPORT QtxPagePrefShortcutTreeItem : public QtxPageNamedPrefItem +{ +public: + QtxPagePrefShortcutTreeItem( const QString&, QtxPreferenceItem* = 0, + const QString& = QString(), const QString& = QString() ); + virtual ~QtxPagePrefShortcutTreeItem(); + + virtual void store(); + virtual void retrieve(); + +protected: + QtxShortcutTree* shortcutTree() const; + virtual QString actionName( const QString& ) const; + +private: + QMap paramToName( const QString& ) const; + QMap nameToParam( const QString& ) const; + +private: + QtxShortcutTree* myShortcutTree; +}; + #endif diff --git a/src/Qtx/QtxPathListEdit.cxx b/src/Qtx/QtxPathListEdit.cxx index 0c3adfede..82e9cf6cc 100644 --- a/src/Qtx/QtxPathListEdit.cxx +++ b/src/Qtx/QtxPathListEdit.cxx @@ -206,8 +206,7 @@ QtxPathListEdit::Delegate::~Delegate() \param option style option \param index data model index */ -QWidget* QtxPathListEdit::Delegate::createEditor( QWidget* parent, const QStyleOptionViewItem& option, - const QModelIndex& index ) const +QWidget* QtxPathListEdit::Delegate::createEditor( QWidget* parent, const QStyleOptionViewItem&, const QModelIndex& ) const { return myPathEdit->createEditor( parent ); } @@ -219,8 +218,7 @@ QWidget* QtxPathListEdit::Delegate::createEditor( QWidget* parent, const QStyleO \param model data model \param index data model index */ -void QtxPathListEdit::Delegate::setModelData( QWidget* editor, QAbstractItemModel* model, - const QModelIndex& index ) const +void QtxPathListEdit::Delegate::setModelData( QWidget* editor, QAbstractItemModel*, const QModelIndex& index ) const { myPathEdit->setModelData( editor, index ); } @@ -268,8 +266,7 @@ void QtxPathListEdit::Delegate::paint( QPainter* painter, const QStyleOptionView \param option style option \param rect selection rectangle */ -void QtxPathListEdit::Delegate::drawFocus( QPainter* painter, const QStyleOptionViewItem& option, - const QRect& rect ) const +void QtxPathListEdit::Delegate::drawFocus( QPainter* painter, const QStyleOptionViewItem& option, const QRect& ) const { QItemDelegate::drawFocus( painter, option, option.rect ); } @@ -306,8 +303,8 @@ void QtxPathListEdit::Delegate::drawFocus( QPainter* painter, const QStyleOption */ QtxPathListEdit::QtxPathListEdit( const Qtx::PathType type, QWidget* parent ) : QFrame( parent ), - myCompleter( 0 ), myType( type ), + myCompleter( 0 ), myDuplicate( false ) { initialize(); @@ -323,8 +320,8 @@ QtxPathListEdit::QtxPathListEdit( const Qtx::PathType type, QWidget* parent ) */ QtxPathListEdit::QtxPathListEdit( QWidget* parent ) : QFrame( parent ), - myCompleter( 0 ), myType( Qtx::PT_OpenFile ), + myCompleter( 0 ), myDuplicate( false ) { initialize(); diff --git a/src/Qtx/QtxResourceMgr.cxx b/src/Qtx/QtxResourceMgr.cxx index 2640b7ce5..f834f3b11 100644 --- a/src/Qtx/QtxResourceMgr.cxx +++ b/src/Qtx/QtxResourceMgr.cxx @@ -22,17 +22,22 @@ #include "QtxResourceMgr.h" #include "QtxTranslator.h" +#include #include #include #include +#include #include #include +#include #ifndef QT_NO_DOM #include #include #include #endif +#include + #include /*! @@ -62,7 +67,6 @@ public: QPixmap loadPixmap( const QString&, const QString&, const QString& ) const; QTranslator* loadTranslator( const QString&, const QString&, const QString& ) const; - QString environmentVariable( const QString&, int&, int& ) const; QString makeSubstitution( const QString&, const QString&, const QString& ) const; void clear(); @@ -403,54 +407,6 @@ QTranslator* QtxResourceMgr::Resources::loadTranslator( const QString& sect, con return trans; } -/*! - \brief Parse given string to retrieve environment variable. - - Looks through the string for the patterns: ${name} or $(name) or %name%. - If string contains variable satisfying any pattern, the variable name - is returned, start index of the variable is returned in the \a start parameter, - and length of the variable is returned in the \a len parameter. - - \param str string being processed - \param start if variable is found, this parameter contains its starting - position in the \a str - \param len if variable is found, this parameter contains its length - \return first found variable or null QString if there is no ones -*/ -QString QtxResourceMgr::Resources::environmentVariable( const QString& str, int& start, int& len ) const -{ - QString varName; - len = 0; - - QRegExp rx( "(^\\$\\{|[^\\$]\\$\\{)([a-zA-Z]+[a-zA-Z0-9_]*)(\\})|(^\\$\\(|[^\\$]\\$\\()([a-zA-Z]+[a-zA-Z0-9_]*)(\\))|(^\\$|[^\\$]\\$)([a-zA-Z]+[a-zA-Z0-9_]*)|(^%|[^%]%)([a-zA-Z]+[a-zA-Z0-9_]*)(%[^%]|%$)" ); - - int pos = rx.indexIn( str, start ); - if ( pos != -1 ) - { - int i = 1; - while ( i <= rx.numCaptures() && varName.isEmpty() ) - { - QString capStr = rx.cap( i ); - if ( !capStr.contains( "%" ) && !capStr.contains( "$" ) ) - varName = capStr; - i++; - } - - if ( !varName.isEmpty() ) - { - int capIdx = i - 1; - start = rx.pos( capIdx ); - int end = start + varName.length(); - if ( capIdx > 1 && rx.cap( capIdx - 1 ).contains( QRegExp( "\\$|%" ) ) ) - start = rx.pos( capIdx - 1 ) + rx.cap( capIdx - 1 ).indexOf( QRegExp( "\\$|%" ) ); - if ( capIdx < rx.numCaptures() && !rx.cap( capIdx - 1 ).isEmpty() ) - end++; - len = end - start; - } - } - return varName; -} - /*! \brief Substitute variables by their values. @@ -472,7 +428,7 @@ QString QtxResourceMgr::Resources::makeSubstitution( const QString& str, const Q int start( 0 ), len( 0 ); while ( true ) { - QString envName = environmentVariable( res, start, len ); + QString envName = Qtx::findEnvVar( res, start, len ); if ( envName.isNull() ) break; @@ -516,6 +472,9 @@ public: protected: virtual bool load( const QString&, QMap& ); virtual bool save( const QString&, const QMap& ); + +private: + bool load( const QString&, QMap&, QSet& ); }; /*! @@ -541,9 +500,41 @@ QtxResourceMgr::IniFormat::~IniFormat() */ bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap& secMap ) { - QFile file( fname ); + QSet importHistory; + return load( fname, secMap, importHistory ); +} + + +/*! + \brief Load resources from xml-file. + \param fname resources file name + \param secMap resources map to be filled in + \param importHistory list of already imported resources files (to prevent import loops) + \return \c true on success or \c false on error +*/ +bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap& secMap, QSet& importHistory) +{ + QString aFName = fname.trimmed(); + if ( !QFileInfo( aFName ).exists() ) + { + if ( QFileInfo( aFName + ".ini" ).exists() ) + aFName += ".ini"; + else if ( QFileInfo( aFName + ".INI" ).exists() ) + aFName += ".INI"; + else + return false; // file does not exist + } + QFileInfo aFinfo( aFName ); + aFName = aFinfo.canonicalFilePath(); + + if ( !importHistory.contains( aFName ) ) + importHistory.insert( aFName ); + else + return true; // already imported (prevent import loops) + + QFile file( aFName ); if ( !file.open( QFile::ReadOnly ) ) - return false; + return false; // file is not accessible QTextStream ts( &file ); @@ -575,27 +566,64 @@ bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap impMap; + if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) ) + { + qDebug() << "QtxResourceMgr: Error with importing file:" << data; + } + else + { + QMap::const_iterator it = impMap.constBegin(); + for ( ; it != impMap.constEnd() ; ++it ) + { + if ( !secMap.contains( it.key() ) ) + { + // insert full section + secMap.insert( it.key(), it.value() ); + } + else + { + // insert all parameters from the section + Section::ConstIterator paramIt = it.value().begin(); + for ( ; paramIt != it.value().end() ; ++paramIt ) + { + if ( !secMap[it.key()].contains( paramIt.key() ) ) + secMap[it.key()].insert( paramIt.key(), paramIt.value() ); + } + } + } + } + } else { res = false; - section.isEmpty() ? qWarning( "Current section is empty" ) : qWarning( "Error in line: %d", line ); + if ( section.isEmpty() ) + qWarning() << "QtxResourceMgr: Current section is empty"; + else + qWarning() << "QtxResourceMgr: Error in line:" << line; } } @@ -612,6 +640,9 @@ bool QtxResourceMgr::IniFormat::load( const QString& fname, QMap& secMap ) { + if ( !Qtx::mkDir( QFileInfo( fname ).absolutePath() ) ) + return false; + QFile file( fname ); if ( !file.open( QFile::WriteOnly ) ) return false; @@ -655,8 +686,11 @@ private: QString docTag() const; QString sectionTag() const; QString parameterTag() const; + QString importTag() const; QString nameAttribute() const; QString valueAttribute() const; + + bool load( const QString&, QMap&, QSet& ); }; /*! @@ -682,14 +716,45 @@ QtxResourceMgr::XmlFormat::~XmlFormat() */ bool QtxResourceMgr::XmlFormat::load( const QString& fname, QMap& secMap ) { + QSet importHistory; + return load( fname, secMap, importHistory ); +} + +/*! + \brief Load resources from xml-file. + \param fname resources file name + \param secMap resources map to be filled in + \param importHistory list of already imported resources files (to prevent import loops) + \return \c true on success and \c false on error +*/ +bool QtxResourceMgr::XmlFormat::load( const QString& fname, QMap& secMap, QSet& importHistory ) +{ + QString aFName = fname.trimmed(); + if ( !QFileInfo( aFName ).exists() ) + { + if ( QFileInfo( aFName + ".xml" ).exists() ) + aFName += ".xml"; + else if ( QFileInfo( aFName + ".XML" ).exists() ) + aFName += ".XML"; + else + return false; // file does not exist + } + QFileInfo aFinfo( aFName ); + aFName = aFinfo.canonicalFilePath(); + + if ( !importHistory.contains( aFName ) ) + importHistory.insert( aFName ); + else + return true; // already imported (prevent import loops) + bool res = false; #ifndef QT_NO_DOM - QFile file( fname ); + QFile file( aFName ); if ( !file.open( QFile::ReadOnly ) ) { - qDebug( "File '%s' cannot be opened", (const char*)fname.toLatin1() ); + qDebug() << "QtxResourceMgr: File is not accessible:" << aFName; return false; } @@ -700,14 +765,14 @@ bool QtxResourceMgr::XmlFormat::load( const QString& fname, QMap impMap; + if ( !load( impFInfo.absoluteFilePath(), impMap, importHistory ) ) + { + qDebug() << "QtxResourceMgr: Error with importing file:" << sectElem.attribute( nameAttribute() ); + } + else + { + QMap::const_iterator it = impMap.constBegin(); + for ( ; it != impMap.constEnd() ; ++it ) + { + if ( !secMap.contains( it.key() ) ) + { + // insert full section + secMap.insert( it.key(), it.value() ); + } + else + { + // insert all parameters from the section + Section::ConstIterator paramIt = it.value().begin(); + for ( ; paramIt != it.value().end() ; ++paramIt ) + { + if ( !secMap[it.key()].contains( paramIt.key() ) ) + secMap[it.key()].insert( paramIt.key(), paramIt.value() ); + } + } + } + } + } else { - qDebug( "Invalid section" ); + qDebug() << "QtxResourceMgr: Invalid section in file:" << aFName; res = false; } } @@ -762,7 +860,7 @@ bool QtxResourceMgr::XmlFormat::load( const QString& fname, QMapmySections = sections; else - qDebug( "QtxResourceMgr: Could not load resource file \"%s\"", (const char*)res->myFileName.toLatin1() ); + qDebug() << "QtxResourceMgr: Can't load resource file:" << res->myFileName; return status; } @@ -1049,8 +1162,11 @@ bool QtxResourceMgr::Format::save( Resources* res ) resources environment variable has higher priority. Priority has the meaning when searching requested resources (application preference, pixmap file name, translation file, etc). - Loading of the user configuration file can be omitted by calling setIgnoreUserValues() - with \c true parameter. + + When retrieving preferences, it is sometimes helpful to ignore values coming from the + user preference file and take into account only global preferences. + To do this, use setWorkingMode() method passing QtxResourceMgr::IgnoreUserValues enumerator + as parameter. Resources manager operates with such terms like options, sections and parameters. Parametets are named application resources, for example, application preferences like @@ -1090,7 +1206,8 @@ QtxResourceMgr::QtxResourceMgr( const QString& appName, const QString& resVarTem myCheckExist( true ), myDefaultPix( 0 ), myIsPixmapCached( true ), - myIsIgnoreUserValues( false ) + myHasUserValues( true ), + myWorkingMode( AllowUserValues ) { QString envVar = !resVarTemplate.isEmpty() ? resVarTemplate : QString( "%1Resources" ); if ( envVar.contains( "%1" ) ) @@ -1180,18 +1297,19 @@ QStringList QtxResourceMgr::dirList() const Prepare the resources containers and load resources (if \a autoLoad is \c true). \param autoLoad if \c true (default) then all resources are loaded - \param loadUser if \c true (default) then user settings are also loaded */ -void QtxResourceMgr::initialize( const bool autoLoad, const bool loadUser ) const +void QtxResourceMgr::initialize( const bool autoLoad ) const { if ( !myResources.isEmpty() ) return; QtxResourceMgr* that = (QtxResourceMgr*)this; - if ( loadUser && !userFileName( appName() ).isEmpty() ) + if ( !userFileName( appName() ).isEmpty() ) that->myResources.append( new Resources( that, userFileName( appName() ) ) ); + that->myHasUserValues = myResources.count() > 0; + for ( QStringList::ConstIterator it = myDirList.begin(); it != myDirList.end(); ++it ) { QString path = Qtx::addSlash( *it ) + globalFileName( appName() ); @@ -1238,27 +1356,31 @@ void QtxResourceMgr::clear() } /*! - \brief Set "ignore user values" option value. - - If this option is \c true, then all resources loaded from user home directory are ignored. + \brief Get current working mode. - \param val new option value - \sa ignoreUserValues() + \return current working mode + \sa setWorkingMode(), value(), hasValue(), hasSection(), setValue() */ -void QtxResourceMgr::setIgnoreUserValues( const bool val ) +QtxResourceMgr::WorkingMode QtxResourceMgr::workingMode() const { - myIsIgnoreUserValues = val; + return myWorkingMode; } /*! - \brief Get "ignore user values" option value. + \brief Set resource manager's working mode. + + The resource manager can operate in the following working modes: + * AllowUserValues : methods values(), hasValue(), hasSection() take into account user values (default) + * IgnoreUserValues : methods values(), hasValue(), hasSection() do not take into account user values + + Note, that setValue() method always put the value to the user settings file. - \return "ignore user values" option value - \sa setIgnoreUserValues() + \param mode new working mode + \sa workingMode(), value(), hasValue(), hasSection(), setValue() */ -bool QtxResourceMgr::ignoreUserValues() const +void QtxResourceMgr::setWorkingMode( WorkingMode mode ) { - return myIsIgnoreUserValues; + myWorkingMode = mode; } /*! @@ -1387,6 +1509,8 @@ bool QtxResourceMgr::value( const QString& sect, const QString& name, QFont& fVa fVal.setItalic( true ); else if ( curval == QString( "underline" ) ) fVal.setUnderline( true ); + else if ( curval == QString( "shadow" ) || curval == QString( "overline" ) ) + fVal.setOverline( true ); else { bool isOk = false; @@ -1501,7 +1625,7 @@ bool QtxResourceMgr::value( const QString& sect, const QString& name, QString& v bool ok = false; ResList::ConstIterator it = myResources.begin(); - if ( ignoreUserValues() ) + if ( myHasUserValues && workingMode() == IgnoreUserValues ) ++it; for ( ; it != myResources.end() && !ok; ++it ) @@ -1710,7 +1834,12 @@ bool QtxResourceMgr::hasValue( const QString& sect, const QString& name ) const initialize(); bool ok = false; - for ( ResList::ConstIterator it = myResources.begin(); it != myResources.end() && !ok; ++it ) + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end() && !ok; ++it ) ok = (*it)->hasValue( sect, name ); return ok; @@ -1726,7 +1855,12 @@ bool QtxResourceMgr::hasSection( const QString& sect ) const initialize(); bool ok = false; - for ( ResList::ConstIterator it = myResources.begin(); it != myResources.end() && !ok; ++it ) + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end() && !ok; ++it ) ok = (*it)->hasSection( sect ); return ok; @@ -1812,6 +1946,8 @@ void QtxResourceMgr::setValue( const QString& sect, const QString& name, const Q fontDescr.append( "Italic" ); if ( val.underline() ) fontDescr.append( "Underline" ); + if ( val.overline() ) + fontDescr.append( "Overline" ); fontDescr.append( QString( "%1" ).arg( val.pointSize() ) ); setResource( sect, name, fontDescr.join( "," ) ); @@ -2070,6 +2206,9 @@ bool QtxResourceMgr::import( const QString& fname ) if ( !fmt ) return false; + if ( myResources.isEmpty() || !myHasUserValues ) + return false; + Resources* r = myResources[0]; if ( !r ) return false; @@ -2093,7 +2232,7 @@ bool QtxResourceMgr::save() if ( !fmt ) return false; - if ( myResources.isEmpty() ) + if ( myResources.isEmpty() || !myHasUserValues ) return true; return fmt->save( myResources[0] ); @@ -2107,21 +2246,97 @@ QStringList QtxResourceMgr::sections() const { initialize(); - QMap map; - for ( ResList::ConstIterator it = myResources.begin(); it != myResources.end(); ++it ) + QSet set; + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + QStringList res; + for ( ; it != myResources.end(); ++it ) { QStringList lst = (*it)->sections(); - for ( QStringList::ConstIterator itr = lst.begin(); itr != lst.end(); ++itr ) - map.insert( *itr, 0 ); + for ( QStringList::ConstIterator itr = lst.begin(); itr != lst.end(); ++itr ) { + QString sect = *itr; + if ( !set.contains( sect ) ) { + set.insert( sect ); + res.append( sect ); + } + } } - QStringList res; - for ( QMap::ConstIterator iter = map.begin(); iter != map.end(); ++iter ) - res.append( iter.key() ); - return res; } +/*! + \brief Get all sections names matching specified regular expression. + \param re searched regular expression + \return list of sections names +*/ +QStringList QtxResourceMgr::sections(const QRegExp& re) const +{ + return sections().filter( re ); +} + +/*! + \brief Get all sections names with the prefix specified by passed + list of parent sections names. + + Sub-sections are separated inside the section name by the sections + separator token, for example "splash:color:label". + + \param names parent sub-sections names + \return list of sections names +*/ +QStringList QtxResourceMgr::sections(const QStringList& names) const +{ + QStringList nm = names; + nm << ".+"; + QRegExp re( QString( "^%1$" ).arg( nm.join( sectionsToken() ) ) ); + return sections( re ); +} + +/*! + \brief Get list of sub-sections names for the specified parent section name. + + Sub-sections are separated inside the section name by the sections + separator token, for example "splash:color:label". + + \param section parent sub-section name + \param full if \c true return full names of child sub-sections, if \c false, + return only top-level sub-sections names + \return list of sub-sections names +*/ +QStringList QtxResourceMgr::subSections(const QString& section, const bool full) const +{ + QStringList names = sections( QStringList() << section ); + QMutableListIterator it( names ); + while ( it.hasNext() ) { + QString name = it.next().mid( section.size() + 1 ).trimmed(); + if ( name.isEmpty() ) { + it.remove(); + continue; + } + if ( !full ) + name = name.split( sectionsToken() ).first(); + it.setValue( name ); + } + /* + names.removeDuplicates(); + */ + QSet set; + QStringList simpleList; + for ( QStringList::iterator it = names.begin(); it != names.end(); ++it ) { + if ( !set.contains( *it ) ) { + set.insert( *it ); + simpleList.append( *it ); + } + } + names = simpleList; + names.sort(); + return names; +} + /*! \brief Get all parameters name in specified section. \param sec section name @@ -2138,20 +2353,40 @@ QStringList QtxResourceMgr::parameters( const QString& sec ) const #endif PMap pmap; - ResList::ConstIterator it = myResources.end(); - while ( it != myResources.begin() ) + Resources* ur = !myResources.isEmpty() && workingMode() == IgnoreUserValues ? myResources[0] : 0; + + QListIterator it( myResources ); + it.toBack(); + while ( it.hasPrevious() ) { - --it; - QStringList lst = (*it)->parameters( sec ); + Resources* r = it.previous(); + if ( r == ur ) break; + QStringList lst = r->parameters( sec ); + for ( QStringList::ConstIterator itr = lst.begin(); itr != lst.end(); ++itr ) +#if defined(QTX_NO_INDEXED_MAP) + if ( !pmap.contains( *itr ) ) pmap.insert( *itr, 0 ); +#else pmap.insert( *itr, 0, false ); +#endif } - QStringList res; - for ( PMap::ConstIterator iter = pmap.begin(); iter != pmap.end(); ++iter ) - res.append( iter.key() ); + return pmap.keys(); +} - return res; +/*! + \brief Get all parameters name in specified + list of sub-sections names. + + Sub-sections are separated inside the section name by the sections + separator token, for example "splash:color:label". + + \param names parent sub-sections names + \return list of settings names +*/ +QStringList QtxResourceMgr::parameters( const QStringList& names ) const +{ + return parameters( names.join( sectionsToken() ) ); } /*! @@ -2171,7 +2406,12 @@ QStringList QtxResourceMgr::parameters( const QString& sec ) const QString QtxResourceMgr::path( const QString& sect, const QString& prefix, const QString& name ) const { QString res; - for ( ResList::ConstIterator it = myResources.begin(); it != myResources.end() && res.isEmpty(); ++it ) + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end() && res.isEmpty(); ++it ) res = (*it)->path( sect, prefix, name ); return res; } @@ -2180,7 +2420,7 @@ QString QtxResourceMgr::path( const QString& sect, const QString& prefix, const \brief Get application resources section name. By default, application resources section name is "resources" but - it can be changed by setting the corresponding resources manager option. + it can be changed by setting the "res_section_name" resources manager option. \return section corresponding to the resources directories \sa option(), setOption() @@ -2197,7 +2437,7 @@ QString QtxResourceMgr::resSection() const \brief Get application language section name. By default, application language section name is "language" but - it can be changed by setting the corresponding resources manager option. + it can be changed by setting the "lang_section_name" resources manager option. \return section corresponding to the application language settings \sa option(), setOption() @@ -2210,6 +2450,23 @@ QString QtxResourceMgr::langSection() const return res; } +/*! + \brief Get sections separator token. + + By default, sections separator token is colon symbol ":" but + it can be changed by setting the "section_token" resources manager option. + + \return string corresponding to the current section separator token + \sa option(), setOption() +*/ +QString QtxResourceMgr::sectionsToken() const +{ + QString res = option( "section_token" ); + if ( res.isEmpty() ) + res = QString( ":" ); + return res; +} + /*! \brief Get default pixmap. @@ -2284,39 +2541,18 @@ QPixmap QtxResourceMgr::loadPixmap( const QString& prefix, const QString& name, initialize(); QPixmap pix; - for ( ResList::ConstIterator it = myResources.begin(); it != myResources.end() && pix.isNull(); ++it ) + + ResList::ConstIterator it = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++it; + + for ( ; it != myResources.end() && pix.isNull(); ++it ) pix = (*it)->loadPixmap( resSection(), prefix, name ); if ( pix.isNull() ) pix = defPix; return pix; } -/*! - \brief Load translation files according to the specified language. - - Names of the translation files are calculated according to the pattern specified - by the "translators" option (this option is read from the section "language" of resources files). - By default, "%P_msg_%L.qm" pattern is used. - Keywords \%A, \%P, \%L in the pattern are substituted by the application name, prefix and language name - correspondingly. - For example, for prefix "SUIT" an language "en", all translation files "SUIT_msg_en.qm" are searched and - loaded. - - If prefix is empty or null string, all translation files specified in the "resources" section of resources - files are loaded (actually, the section is retrieved from resSection() method). - If language is not specified, it is retrieved from the langSection() method, and if the latest is also empty, - by default "en" (English) language is used. - - \param pref parameter which defines translation context (for example, package name) - \param l language name - - \sa resSection(), langSection(), loadTranslators() -*/ -void QtxResourceMgr::loadLanguage( const QString& pref, const QString& l ) -{ - loadLanguage( true, pref, l ); -} - /*! \brief Load translation files according to the specified language. @@ -2335,15 +2571,14 @@ void QtxResourceMgr::loadLanguage( const QString& pref, const QString& l ) By default, settings from the user preferences file are also loaded (if user resource file is valid, see userFileName()). To avoid loading user settings, pass \c false as first parameter. - \param loadUser if \c true then user settings are also loaded \param pref parameter which defines translation context (for example, package name) \param l language name \sa resSection(), langSection(), loadTranslators() */ -void QtxResourceMgr::loadLanguage( const bool loadUser, const QString& pref, const QString& l ) +void QtxResourceMgr::loadLanguage( const QString& pref, const QString& l ) { - initialize( true, loadUser ); + initialize( true ); QMap substMap; substMap.insert( 'A', appName() ); @@ -2355,7 +2590,7 @@ void QtxResourceMgr::loadLanguage( const bool loadUser, const QString& pref, con if ( lang.isEmpty() ) { lang = QString( "en" ); - qWarning( "Language not specified. Assumed: %s", (const char*)lang.toLatin1() ); + qWarning() << "QtxResourceMgr: Language not specified. Assumed:" << lang; } substMap.insert( 'L', lang ); @@ -2377,7 +2612,7 @@ void QtxResourceMgr::loadLanguage( const bool loadUser, const QString& pref, con if ( trList.isEmpty() ) { trList.append( "%P_msg_%L.qm" ); - qWarning( "Translators not defined. Assumed: %s", (const char*)trList[0].toLatin1() ); + qWarning() << "QtxResourceMgr: Translators not defined. Assumed:" << trList[0]; } QStringList prefixList; @@ -2386,6 +2621,20 @@ void QtxResourceMgr::loadLanguage( const bool loadUser, const QString& pref, con else prefixList = parameters( resSection() ); + if ( pref.isEmpty() && lang != "en" ) { + // load Qt resources + QString qt_translations = QLibraryInfo::location( QLibraryInfo::TranslationsPath ); + QString qt_dir_trpath; + if ( ::getenv( "QTDIR" ) ) + qt_dir_trpath = QString( ::getenv( "QTDIR" ) ); + if ( !qt_dir_trpath.isEmpty() ) + qt_dir_trpath = QDir( qt_dir_trpath ).absoluteFilePath( "translations" ); + + QTranslator* trans = new QtxTranslator( 0 ); + if ( trans->load( QString("qt_%1").arg( lang ), qt_translations ) || trans->load( QString("qt_%1").arg( lang ), qt_dir_trpath ) ) + QApplication::instance()->installTranslator( trans ); + } + for ( QStringList::ConstIterator iter = prefixList.begin(); iter != prefixList.end(); ++iter ) { QString prefix = *iter; @@ -2410,7 +2659,12 @@ void QtxResourceMgr::loadTranslators( const QString& prefix, const QStringList& initialize(); ResList lst; - for ( ResList::Iterator iter = myResources.begin(); iter != myResources.end(); ++iter ) + + ResList::ConstIterator iter = myResources.begin(); + if ( myHasUserValues && workingMode() == IgnoreUserValues ) + ++iter; + + for ( ; iter != myResources.end(); ++iter ) lst.prepend( *iter ); QTranslator* trans = 0; @@ -2442,11 +2696,16 @@ void QtxResourceMgr::loadTranslator( const QString& prefix, const QString& name QTranslator* trans = 0; - ResList::ConstIterator it = myResources.end(); - while ( it != myResources.begin() ) + Resources* ur = !myResources.isEmpty() && workingMode() == IgnoreUserValues ? myResources[0] : 0; + + QListIterator it( myResources ); + it.toBack(); + while ( it.hasPrevious() ) { - --it; - trans = (*it)->loadTranslator( resSection(), prefix, name ); + Resources* r = it.previous(); + if ( r == ur ) break; + + trans = r->loadTranslator( resSection(), prefix, name ); if ( trans ) { if ( !myTranslator[prefix].contains( trans ) ) @@ -2532,7 +2791,7 @@ void QtxResourceMgr::setResource( const QString& sect, const QString& name, cons { initialize(); - if ( !myResources.isEmpty() ) + if ( !myResources.isEmpty() && myHasUserValues ) myResources.first()->setValue( sect, name, val ); } @@ -2550,7 +2809,7 @@ void QtxResourceMgr::setResource( const QString& sect, const QString& name, cons the configuration file from the previous versions of the application). \param appName application name - \param for_load boolean flag indicating that file is opened for loading or saving (not used) + \param for_load boolean flag indicating that file is opened for loading or saving (not used in default implementation) \return user configuration file name \sa globalFileName() */ @@ -2559,6 +2818,10 @@ QString QtxResourceMgr::userFileName( const QString& appName, const bool /*for_l QString fileName; QString pathName = QDir::homePath(); + QString cfgAppName = QApplication::applicationName(); + if ( !cfgAppName.isEmpty() ) + pathName = Qtx::addSlash( Qtx::addSlash( pathName ) + QString( ".config" ) ) + cfgAppName; + #ifdef WIN32 fileName = QString( "%1.%2" ).arg( appName ).arg( currentFormat() ); #else diff --git a/src/Qtx/QtxResourceMgr.h b/src/Qtx/QtxResourceMgr.h index 550c14e30..14514a28d 100644 --- a/src/Qtx/QtxResourceMgr.h +++ b/src/Qtx/QtxResourceMgr.h @@ -60,6 +60,12 @@ public: typedef IMap Section; //!< resource section #endif + //! Working mode; defines a way how resource manager handles user preferences + typedef enum { + AllowUserValues, //!< User values are processed by the resource manager + IgnoreUserValues //!< User values are ignored by the resource manager + } WorkingMode; + public: QtxResourceMgr( const QString&, const QString& = QString() ); virtual ~QtxResourceMgr(); @@ -75,8 +81,8 @@ public: void clear(); - void setIgnoreUserValues( const bool = true ); - bool ignoreUserValues() const; + WorkingMode workingMode() const; + void setWorkingMode( WorkingMode ); bool value( const QString&, const QString&, int& ) const; bool value( const QString&, const QString&, double& ) const; @@ -133,12 +139,12 @@ public: QString resSection() const; QString langSection() const; + QString sectionsToken() const; QPixmap loadPixmap( const QString&, const QString& ) const; QPixmap loadPixmap( const QString&, const QString&, const bool ) const; QPixmap loadPixmap( const QString&, const QString&, const QPixmap& ) const; void loadLanguage( const QString& = QString(), const QString& = QString() ); - void loadLanguage( const bool, const QString& = QString(), const QString& = QString() ); void raiseTranslators( const QString& ); void removeTranslators( const QString& ); @@ -152,7 +158,11 @@ public: bool save(); QStringList sections() const; + QStringList sections(const QRegExp&) const; + QStringList sections(const QStringList&) const; + QStringList subSections(const QString&, const bool = true) const; QStringList parameters( const QString& ) const; + QStringList parameters( const QStringList& ) const; void refresh(); @@ -164,7 +174,7 @@ protected: virtual QString globalFileName( const QString& ) const; private: - void initialize( const bool = true, const bool = true ) const; + void initialize( const bool = true ) const; QString substMacro( const QString&, const QMap& ) const; private: @@ -185,7 +195,8 @@ private: QPixmap* myDefaultPix; //!< default icon bool myIsPixmapCached; //!< "cached pixmaps" flag - bool myIsIgnoreUserValues; //!< "ignore user values" flag + bool myHasUserValues; //!< \c true if user preferences has been read + WorkingMode myWorkingMode; //!< working mode friend class QtxResourceMgr::Format; }; diff --git a/src/Qtx/QtxShortcutEdit.cxx b/src/Qtx/QtxShortcutEdit.cxx new file mode 100755 index 000000000..3578e8f29 --- /dev/null +++ b/src/Qtx/QtxShortcutEdit.cxx @@ -0,0 +1,404 @@ +// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#include "QtxShortcutEdit.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#define COLUMN_SIZE 300 + +static const char* delete_icon[] = { +"16 16 3 1", +"` c #810000", +" c none", +"# c #ffffff", +" ", +" ", +" ``# ``# ", +" ````# ``# ", +" ````# ``# ", +" ```# `# ", +" `````# ", +" ```# ", +" `````# ", +" ```# ``# ", +" ```# ``# ", +" ```# `# ", +" ```# `# ", +" `# `# ", +" ", +" " +}; + +/*! + \brief Constructor + \param parent parent widget +*/ +QtxShortcutEdit::QtxShortcutEdit( QWidget* parent ) +: QFrame( parent ) +{ + initialize(); + myShortcut->installEventFilter(this); +} + +/*! + \brief Destructor +*/ +QtxShortcutEdit::~QtxShortcutEdit() +{ +} + +/*! + \brief Sets custom shortcut + \param seq a key sequence describes a combination of keys + \sa shortcut() +*/ +void QtxShortcutEdit::setShortcut( const QKeySequence& seq ) +{ + QString txt = seq.toString(); + myPrevShortcutText = txt; + myShortcut->setText( txt ); +} + +/*! + \brief Gets custom shortcut + \return a key sequence describes a combination of keys + \sa setShortcut() +*/ +QKeySequence QtxShortcutEdit::shortcut() +{ + return QKeySequence::fromString( myShortcut->text() ); +} + +/*! + \brief Gets the key sequence from keys that were pressed + \param e a key event + \return a string representation of the key sequence +*/ +QString QtxShortcutEdit::parseEvent( QKeyEvent* e ) +{ + bool isShiftPressed = e->modifiers() & Qt::ShiftModifier; + bool isControlPressed = e->modifiers() & Qt::ControlModifier; + bool isAltPressed = e->modifiers() & Qt::AltModifier; + bool isMetaPressed = e->modifiers() & Qt::MetaModifier; + bool isModifiersPressed = isShiftPressed || isControlPressed || isAltPressed || isMetaPressed; + int result=0; + if( isControlPressed ) + result += Qt::CTRL; + if( isAltPressed ) + result += Qt::ALT; + if( isShiftPressed ) + result += Qt::SHIFT; + if( isMetaPressed ) + result += Qt::META; + + int aKey = e->key(); + if ( ( isValidKey( aKey ) && isModifiersPressed ) || ( ( aKey >= Qt::Key_F1 ) && ( aKey <= Qt::Key_F12 ) ) ) + result += aKey; + + return QKeySequence( result ).toString(); +} + +/*! + \brief Check if the key event contains a 'valid' key + \param aKey the code of the key + \return \c true if the key is 'valid' +*/ + +bool QtxShortcutEdit::isValidKey( int aKey ) +{ + if ( aKey == Qt::Key_Underscore || aKey == Qt::Key_Escape || + ( aKey >= Qt::Key_Backspace && aKey <= Qt::Key_Delete ) || + ( aKey >= Qt::Key_Home && aKey <= Qt::Key_PageDown ) || + ( aKey >= Qt::Key_F1 && aKey <= Qt::Key_F12 ) || + ( aKey >= Qt::Key_Space && aKey <= Qt::Key_Asterisk ) || + ( aKey >= Qt::Key_Comma && aKey <= Qt::Key_Question ) || + ( aKey >= Qt::Key_A && aKey <= Qt::Key_AsciiTilde ) ) + return true; + return false; +} + +/*! + \brief Called when "Clear" button is clicked. +*/ +void QtxShortcutEdit::onCliked() +{ + myShortcut->setText( "" ); +} + +/*! + \brief Called when myShortcut loses focus. +*/ +void QtxShortcutEdit::onEditingFinished() +{ + if ( myShortcut->text().endsWith("+") ) + myShortcut->setText( myPrevShortcutText ); +} + +/*! + \brief Custom event filter. + \param obj event receiver object + \param event event + \return \c true if further event processing should be stopped +*/ +bool QtxShortcutEdit::eventFilter(QObject* obj, QEvent* event) +{ + if ( obj == myShortcut ) { + if (event->type() == QEvent::KeyPress ) { + QKeyEvent* keyEvent = static_cast(event); + QString text = parseEvent( keyEvent ); + if ( keyEvent->key() == Qt::Key_Delete || keyEvent->key() == Qt::Key_Backspace ) + onCliked(); + if ( text != "" ) + myShortcut->setText( text ); + return true; + } + if ( event->type() == QEvent::KeyRelease ) { + if ( myShortcut->text().endsWith("+") ) + myShortcut->setText( myPrevShortcutText ); + else myPrevShortcutText = myShortcut->text(); + + return true; + } + } + return false; +} + +/* + \brief Perform internal intialization. +*/ +void QtxShortcutEdit::initialize() +{ + myPrevShortcutText = QString(); + + QHBoxLayout* base = new QHBoxLayout( this ); + base->setMargin( 0 ); + base->setSpacing( 5 ); + + base->addWidget( myShortcut = new QLineEdit( this ) ); + + QToolButton* deleteBtn = new QToolButton(); + deleteBtn->setIcon( QPixmap( delete_icon ) ); + base->addWidget( deleteBtn ); + + myShortcut->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::Fixed ); + deleteBtn->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed ); + + connect( deleteBtn, SIGNAL( clicked() ), this, SLOT( onCliked() ) ); + connect( myShortcut, SIGNAL( editingFinished() ), this, SLOT( onEditingFinished() ) ); +} + +/*! + \brief Constructor + \param parent parent widget +*/ +QtxShortcutTree::QtxShortcutTree( QWidget * parent ) : QTreeWidget( parent ) +{ + setColumnCount( 2 ); + setSelectionMode( QAbstractItemView::SingleSelection ); + setColumnWidth ( 0, COLUMN_SIZE); + setSortingEnabled(false); + headerItem()->setHidden ( true ); + + this->installEventFilter(this); + connect( this, SIGNAL( currentItemChanged ( QTreeWidgetItem*, QTreeWidgetItem* ) ), this, SLOT( onCurrentItemChanged ( QTreeWidgetItem*, QTreeWidgetItem* ) ) ); + +} + +/*! + \brief Destructor +*/ +QtxShortcutTree::~QtxShortcutTree(){} + +/*! + \brief Custom event filter. + \param obj event receiver object + \param event event + \return \c true if further event processing should be stopped +*/ +bool QtxShortcutTree::eventFilter(QObject* obj, QEvent* event) +{ + if ( currentItem() && currentItem()->isSelected() ) { + + if (event->type() == QEvent::KeyPress ) { + QKeyEvent* keyEvent = static_cast(event); + QString text = QtxShortcutEdit::parseEvent( keyEvent ); + if ( keyEvent->key() == Qt::Key_Delete || keyEvent->key() == Qt::Key_Backspace ) + currentItem()->setText( 1, "" ); + if ( text != "" ) { + if ( text.endsWith( "+" ) || checkUniqueness( currentItem(), text ) ) + currentItem()->setText( 1, text ); + } + return true; + } + if ( event->type() == QEvent::KeyRelease ) { + if ( currentItem()->text( 1 ).endsWith( "+" ) ) + currentItem()->setText( 1, myPrevBindings[ currentItem()->parent()->text( 0 ) ][ currentItem()->text( 0 ) ] ); + else myPrevBindings[ currentItem()->parent()->text( 0 ) ][ currentItem()->text( 0 ) ] = currentItem()->text( 1 ); + + return true; + } + } + return false; +} + +/*! + \brief Called when the current item changes. + \param cur the current item + \param prev the previous current item +*/ +void QtxShortcutTree::onCurrentItemChanged( QTreeWidgetItem* cur, QTreeWidgetItem* prev ) +{ + if ( prev && prev->text( 1 ).endsWith( "+" ) ) + prev->setText( 1, myPrevBindings[ prev->parent()->text( 0 ) ][ prev->text( 0 ) ] ); +} + +/*! + \brief Set key bindings to the tree + \param title the name of top-level item + \param theShortcutMap map of key bindings +*/ +void QtxShortcutTree::setBindings( const QString& title, const ShortcutMap& theShortcutMap ) +{ + QTreeWidgetItem* item= new QTreeWidgetItem(); + QFont font = item->font(0); + font.setBold(true); + + if ( findItems( title, Qt::MatchFixedString ).isEmpty() ) { + item->setText( 0, title ); + item->setFont( 0, font ); + addTopLevelItem( item ); + item->setFlags( Qt::ItemIsEnabled ); + } else { + item = findItems( title, Qt::MatchFixedString ).first(); + item->takeChildren(); + } + for( ShortcutMap::const_iterator it = theShortcutMap.constBegin(); it != theShortcutMap.constEnd(); ++it ) + item->addChild( new QTreeWidgetItem( QStringList() << it.key() << it.value() ) ); + myPrevBindings.insert( title, theShortcutMap); +} + +/*! + \brief Get all sections names. + \return list of section names +*/ +QStringList QtxShortcutTree::sections() const +{ + QStringList lst; + for( int i = 0; i < topLevelItemCount(); i++ ) + lst << topLevelItem( i )->text( 0 ); + return lst; +} + +ShortcutMap* QtxShortcutTree::bindings( const QString& sec ) const +{ + ShortcutMap* aMap = new ShortcutMap(); + QTreeWidgetItem* item = findItems( sec, Qt::MatchFixedString ).first(); + int nbChildren = item->childCount(); + + for( int i = 0; i < nbChildren; i++ ) { + QTreeWidgetItem* child = item->child(i); + aMap->insert( child->text( 0 ), child->text( 1 ) ); + } + + return aMap; +} + +void QtxShortcutTree::focusOutEvent ( QFocusEvent* event ) +{ + QWidget::focusOutEvent( event ); + if ( currentItem() && currentItem()->isSelected() ) + currentItem()->setText( 1, myPrevBindings[ currentItem()->parent()->text( 0 ) ][ currentItem()->text( 0 ) ] ); +} + +/*! + \brief Set the list of shortcuts general sections. + + Key combinations in general sections should not intersect + with any other key combinations. + + \param sectionsList list of common section names +*/ +void QtxShortcutTree::setGeneralSections( const QStringList& sectionsList ) +{ + myGeneralSections = sectionsList; +} + +/*! + \brief Check uniqueness of the shortcut. + \param item current item of the shortcut tree + \param shortcut shortcut appointed for the current item + \return \c true if the given shortcut is allowed +*/ +bool QtxShortcutTree::checkUniqueness( QTreeWidgetItem* item, const QString& shortcut ) +{ + // List of sections to check shortcut intersections + QStringList sectionsList; + + // Current section + QString currentSection = currentItem()->parent()->text( 0 ); + + // If the current section is general + if ( myGeneralSections.contains(currentSection) ) { + sectionsList = sections(); + int currentSectionIndex = sectionsList.indexOf(currentSection); + sectionsList.move( currentSectionIndex, 0); + } + else { + sectionsList = myGeneralSections; + sectionsList.prepend(currentSection); + } + + // Iterate on sections + QStringList::const_iterator it; + for( it = sectionsList.constBegin(); it != sectionsList.constEnd(); ++it ) { + QString section = *it; + + // Iterate on actual section + QTreeWidgetItem* sectionRoot = findItems( section, Qt::MatchFixedString ).first(); + int nbChildren = sectionRoot->childCount(); + + for( int i = 0; i < nbChildren; i++ ) { + QTreeWidgetItem* child = sectionRoot->child(i); + + if ( (child != item) && (shortcut == child->text( 1 )) ) { + bool res = QMessageBox::warning( parentWidget(), tr("Warning"), + tr("The \"%1\" shortcut has already used by the \"%2\" action.\n") + .arg(shortcut, section + ":" + child->text( 0 ) ) + + tr("Do you want to reassign it from that action to the current one?"), + QMessageBox::Yes, QMessageBox::No ) == QMessageBox::Yes; + if (res) + child->setText( 1, "" ); + return res; + } + } + } + + return true; +} diff --git a/src/Qtx/QtxShortcutEdit.h b/src/Qtx/QtxShortcutEdit.h new file mode 100755 index 000000000..01ed17ad4 --- /dev/null +++ b/src/Qtx/QtxShortcutEdit.h @@ -0,0 +1,87 @@ +// Copyright (C) 2007-2011 CEA/DEN, EDF R&D, OPEN CASCADE +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License. +// +// 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// +// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com +// + +#ifndef QTXSHORTCUTEDIT_H +#define QTXSHORTCUTEDIT_H + +#include "Qtx.h" + +#include +#include + +class QLineEdit; +class QPushButton; +class QTreeWidgetItem; + +typedef QMap< QString, QString > ShortcutMap; + +class QTX_EXPORT QtxShortcutEdit : public QFrame +{ + Q_OBJECT + +public: + QtxShortcutEdit( QWidget* = 0 ); + virtual ~QtxShortcutEdit(); + void setShortcut( const QKeySequence& ); + QKeySequence shortcut(); + static QString parseEvent( QKeyEvent* ); + static bool isValidKey( int ); + + +private slots: + void onCliked(); + void onEditingFinished(); + +protected: + virtual bool eventFilter( QObject*, QEvent* ); + +private: + void initialize(); + +private: + QLineEdit* myShortcut; + QString myPrevShortcutText; +}; + +class QTX_EXPORT QtxShortcutTree : public QTreeWidget +{ + Q_OBJECT + +public: + QtxShortcutTree( QWidget * parent = 0 ); + virtual ~QtxShortcutTree(); + void setBindings( const QString&, const ShortcutMap& ); + ShortcutMap* bindings( const QString& ) const; + QStringList sections() const; + void setGeneralSections( const QStringList& ); + +protected: + virtual bool eventFilter( QObject*, QEvent* ); + virtual void focusOutEvent( QFocusEvent* ); + virtual bool checkUniqueness( QTreeWidgetItem*, const QString& ); + +private slots: + void onCurrentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* ); + +private: + QMap< QString, ShortcutMap > myPrevBindings; + QStringList myGeneralSections; +}; + +#endif // QTXSHORTCUTEDIT_H