libyui
3.0.10
|
00001 /* 00002 Copyright (C) 2000-2012 Novell, Inc 00003 This library is free software; you can redistribute it and/or modify 00004 it under the terms of the GNU Lesser General Public License as 00005 published by the Free Software Foundation; either version 2.1 of the 00006 License, or (at your option) version 3.0 of the License. This library 00007 is distributed in the hope that it will be useful, but WITHOUT ANY 00008 WARRANTY; without even the implied warranty of MERCHANTABILITY or 00009 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 00010 License for more details. You should have received a copy of the GNU 00011 Lesser General Public License along with this library; if not, write 00012 to the Free Software Foundation, Inc., 51 Franklin Street, Fifth 00013 Floor, Boston, MA 02110-1301 USA 00014 */ 00015 00016 00017 /*-/ 00018 00019 File: YShortcut.cc 00020 00021 Author: Stefan Hundhammer <sh@suse.de> 00022 00023 /-*/ 00024 00025 00026 #include <ctype.h> // toupper(), tolower() 00027 #include <string.h> // strstr() 00028 00029 #define YUILogComponent "ui-shortcuts" 00030 #include "YUILog.h" 00031 00032 #include "YShortcut.h" 00033 #include "YPushButton.h" 00034 #include "YDumbTab.h" 00035 00036 00037 // Return the number of elements of an array of any type 00038 #define DIM( ARRAY ) ( (int) ( sizeof( ARRAY)/( sizeof( ARRAY[0] ) ) ) ) 00039 00040 00041 YShortcut::YShortcut( YWidget * shortcutWidget ) 00042 : _widget( shortcutWidget ) 00043 { 00044 _preferred = -1; 00045 _shortcut = -1; 00046 _distinctShortcutChars = -1; 00047 _conflict = false; 00048 _shortcutStringCached = false; 00049 _cleanShortcutStringCached = false; 00050 00051 YPushButton * button = dynamic_cast<YPushButton *>( shortcutWidget); 00052 _isButton = ( button != 0 ); 00053 00054 if ( _isButton ) 00055 { 00056 _isWizardButton = strstr( shortcutWidget->widgetClass(), "WizardButton" ); 00057 00058 // yuiDebug() << shortcutWidget << ( _isWizardButton ? " is " : " is not " ) << "a wizard button" << endl; 00059 } 00060 else 00061 { 00062 _isWizardButton = 0; 00063 } 00064 00065 // yuiDebug() << shortcutWidget << ( _isButton ? " is " : " is not " ) << "a button" << endl; 00066 } 00067 00068 00069 YShortcut::~YShortcut() 00070 { 00071 } 00072 00073 00074 std::string 00075 YShortcut::shortcutString() 00076 { 00077 if ( ! _shortcutStringCached ) 00078 { 00079 _shortcutString = getShortcutString(); 00080 _shortcutStringCached = true; 00081 00082 // Note: We really need a separate variable here - an empty string 00083 // might be a valid value! 00084 } 00085 00086 return _shortcutString; 00087 } 00088 00089 00090 std::string 00091 YShortcut::cleanShortcutString() 00092 { 00093 if ( ! _cleanShortcutStringCached ) 00094 { 00095 _cleanShortcutString = cleanShortcutString( shortcutString() ); 00096 } 00097 00098 return _cleanShortcutString; 00099 } 00100 00101 00102 std::string 00103 YShortcut::cleanShortcutString( std::string shortcutString ) 00104 { 00105 std::string::size_type pos = 0; 00106 00107 while ( ( pos = findShortcutPos( shortcutString, pos ) ) != std::string::npos ) 00108 { 00109 shortcutString.erase( pos, ( std::string::size_type ) 1 ); 00110 } 00111 00112 return shortcutString; 00113 } 00114 00115 00116 char 00117 YShortcut::preferred() 00118 { 00119 if ( _preferred < 0 ) 00120 { 00121 _preferred = normalized( findShortcut( shortcutString() ) ); 00122 } 00123 00124 return (char) _preferred; 00125 } 00126 00127 00128 char 00129 YShortcut::shortcut() 00130 { 00131 if ( _shortcut < 0 ) 00132 { 00133 _shortcut = preferred(); 00134 } 00135 00136 return (char) _shortcut; 00137 } 00138 00139 00140 void 00141 YShortcut::setShortcut( char newShortcut ) 00142 { 00143 std::string str = cleanShortcutString(); 00144 00145 if ( newShortcut != YShortcut::None ) 00146 { 00147 char findme[] = { (char)tolower( newShortcut ), (char)toupper( newShortcut ), 0 }; 00148 std::string::size_type pos = str.find_first_of( findme ); 00149 00150 if ( pos == std::string::npos ) 00151 { 00152 yuiError() << "Can't find '<< " << newShortcut 00153 << "' in " << widgetClass() 00154 << " \"" << cleanShortcutString() << "\"" 00155 << std::endl; 00156 00157 return; 00158 } 00159 00160 str.insert( pos, 00161 std::string( 1, shortcutMarker() ) ); // equivalent to 'std::string( "& " )' 00162 } 00163 00164 widget()->setShortcutString( str ); 00165 00166 _shortcutStringCached = false; 00167 _cleanShortcutStringCached = false; 00168 _shortcut = newShortcut; 00169 } 00170 00171 00172 void 00173 YShortcut::clearShortcut() 00174 { 00175 setShortcut( YShortcut::None ); 00176 } 00177 00178 00179 int 00180 YShortcut::distinctShortcutChars() 00181 { 00182 if ( _distinctShortcutChars < 0 ) // cache this value - it's expensive! 00183 { 00184 // Create and initialize "contained" array - what possible shortcut 00185 // characters are contained in that string? 00186 00187 bool contained[ sizeof(char) << 8 ]; 00188 00189 for ( int i=0; i < DIM( contained ); i++ ) 00190 contained[i] = false; 00191 00192 00193 // Mark characters as contained 00194 00195 std::string clean = cleanShortcutString(); 00196 00197 for ( std::string::size_type pos=0; pos < clean.length(); pos++ ) 00198 { 00199 if ( YShortcut::isValid( clean[ pos ] ) ) 00200 contained[ (int) clean[ pos ] ] = true; 00201 } 00202 00203 00204 // Count number of contained characters 00205 00206 _distinctShortcutChars=0; 00207 00208 for ( int i=0; i < DIM( contained ); i++ ) 00209 { 00210 if ( contained[i] ) 00211 { 00212 _distinctShortcutChars++; 00213 } 00214 } 00215 } 00216 00217 return _distinctShortcutChars; 00218 } 00219 00220 00221 bool 00222 YShortcut::hasValidShortcutChar() 00223 { 00224 std::string clean = cleanShortcutString(); 00225 00226 for ( std::string::size_type pos=0; pos < clean.length(); pos++ ) 00227 { 00228 if ( YShortcut::isValid( clean[ pos ] ) ) 00229 return true; 00230 } 00231 00232 return false; 00233 } 00234 00235 00236 std::string 00237 YShortcut::getShortcutString() 00238 { 00239 return getShortcutString( widget() ); 00240 } 00241 00242 00243 std::string 00244 YShortcut::getShortcutString( const YWidget * widget ) 00245 { 00246 if ( ! widget ) 00247 return std::string( "" ); 00248 00249 return widget->shortcutString(); 00250 } 00251 00252 00253 std::string::size_type 00254 YShortcut::findShortcutPos( const std::string & str, std::string::size_type pos ) 00255 { 00256 while ( ( pos = str.find( shortcutMarker(), pos ) ) != std::string::npos ) 00257 { 00258 if ( pos+1 < str.length() ) 00259 { 00260 if ( str[ pos+1 ] == shortcutMarker() ) // escaped marker? ( "&&" ) 00261 { 00262 pos += 2; // skip this and search for more 00263 } 00264 else 00265 return pos; 00266 } 00267 else 00268 { 00269 // A pathological case: The string ends with '& '. 00270 // This is invalid anyway, but prevent endless loop even in this case. 00271 return std::string::npos; 00272 } 00273 } 00274 00275 return std::string::npos; // not found 00276 } 00277 00278 00279 char 00280 YShortcut::findShortcut( const std::string & str, std::string::size_type pos ) 00281 { 00282 pos = findShortcutPos( str, pos ); 00283 00284 return pos == std::string::npos ? (char) 0 : str[ pos+1 ]; 00285 } 00286 00287 00288 bool 00289 YShortcut::isValid( char c ) 00290 { 00291 if ( c >= 'a' && c <= 'z' ) return true; 00292 if ( c >= 'A' && c <= 'Z' ) return true; 00293 if ( c >= '0' && c <= '9' ) return true; 00294 return false; 00295 } 00296 00297 00298 char 00299 YShortcut::normalized( char c ) 00300 { 00301 if ( c >= 'a' && c <= 'z' ) return c - 'a' + 'A'; 00302 if ( c >= 'A' && c <= 'Z' ) return c; 00303 if ( c >= '0' && c <= '9' ) return c; 00304 return (char) 0; 00305 } 00306 00307 00308 00309 std::string 00310 YItemShortcut::getShortcutString() 00311 { 00312 if ( ! _item ) 00313 return ""; 00314 00315 return _item->label(); 00316 } 00317 00318 00319 void 00320 YItemShortcut::setShortcut( char newShortcut ) 00321 { 00322 std::string str = cleanShortcutString(); 00323 00324 if ( newShortcut != YShortcut::None ) 00325 { 00326 char findme[] = { (char)tolower( newShortcut ), (char)toupper( newShortcut ), 0 }; 00327 std::string::size_type pos = str.find_first_of( findme ); 00328 00329 if ( pos == std::string::npos ) 00330 { 00331 yuiError() << "Can't find '<< " << newShortcut 00332 << "' in item " 00333 << " \"" << cleanShortcutString() << "\"" 00334 << std::endl; 00335 00336 return; 00337 } 00338 00339 str.insert( pos, 00340 std::string( 1, shortcutMarker() ) ); // equivalent to 'std::string( "& " )' 00341 } 00342 00343 _item->setLabel( str ); 00344 00345 // Notify the parent widget 00346 widget()->setShortcutString( widget()->shortcutString() ); 00347 00348 _shortcutStringCached = false; 00349 _cleanShortcutStringCached = false; 00350 _shortcut = newShortcut; 00351 00352 }