[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kfm-devel
Subject:    Re: Patch: window menu keyboard shortcuts
From:       Keunwoo Lee <klee () cs ! washington ! edu>
Date:       2000-11-14 3:00:04
[Download RAW message or body]

I thought about this some more, & decided to go back and make the behavior
more intelligent.  The attached patch works against the version *without*
my previous patch; apply from within the konqueror source directory.

The behavior in this new and improved patch is as follows:

+ There are no artificial numbers to visually distract mouse users.

+ Instead, it attempts to find a unique letter or number, in each profile
name, that is appropriate for a hotkey.  "Appropriate" means it does not
clash with any other hotkeys in the menu.  Letters/numbers that begin
words (either beginning the string, or following spaces) are preferred
over internal letters/numbers.

+ If the user manually specifies any hotkeys using '&', then the code will
not attempt to add a hotkey for that menu item.  Instead, the character
following that hotkey will be saved, and no automatically generated
assignment will be allowed to clash with that hotkey.

+ We do not attempt to save the user from gross stupidity, such as
manually setting two profiles to have the same hotkey or attempting to
manually hotkey a non-alphanumeric character.

I've tested this behavior briefly & I'm happy with it so far.  The major
drawbacks over previous suggestions by myself & David are

- It's more complicated.

- When the user adds a new profile, the hotkeys can get shuffled around
quite a bit.  The user still has the option of hardcoding a fixed hotkey,
but users who do not know about the '&' trick may get confused a bit.

In spite of these drawbacks, I find that this policy sets hotkeys fairly
intuitively in practice.  In any case, I think it's much better than
having no hotkeys in that menu at all.

Let me know if you have problems.

BTW "smart automatic hotkey generation" seems like it would be useful for
other apps' dynamically generated menus, or for lazy programmers who don't
want to manually set hotkeys in static menus.  It may be worthwhile at
some point to factor this code into something reusable.

~k.lee


["klee-20001113-2.diff" (TEXT/PLAIN)]

*** konq_mainwindow.cc	Mon Nov 13 14:50:54 2000
--- konq_mainwindow.cc.new	Mon Nov 13 14:32:28 2000
***************
*** 2457,2463 ****
    m_paRemoveView = new KAction( i18n( "&Remove Active View" ), "remove_view", \
CTRL+SHIFT+Key_R, this, SLOT( slotRemoveView() ), actionCollection(), "removeview" ); \
  m_paSaveRemoveViewProfile = new KAction( i18n( "Configure View Profiles..." ), 0, \
m_pViewManager, SLOT( slotProfileDlg() ), actionCollection(), "saveremoveviewprofile" \
                );
!   m_pamLoadViewProfile = new KActionMenu( i18n( "Load View Profile" ), \
actionCollection(), "loadviewprofile" );  
    m_pViewManager->setProfiles( m_pamLoadViewProfile );
  
--- 2457,2463 ----
    m_paRemoveView = new KAction( i18n( "&Remove Active View" ), "remove_view", \
CTRL+SHIFT+Key_R, this, SLOT( slotRemoveView() ), actionCollection(), "removeview" ); \
  m_paSaveRemoveViewProfile = new KAction( i18n( "Configure View Profiles..." ), 0, \
m_pViewManager, SLOT( slotProfileDlg() ), actionCollection(), "saveremoveviewprofile" \
                );
!   m_pamLoadViewProfile = new KActionMenu( i18n( "Load &View Profile" ), \
actionCollection(), "loadviewprofile" );  
    m_pViewManager->setProfiles( m_pamLoadViewProfile );
  
*** konq_viewmgr.cc	Mon Nov 13 14:49:25 2000
--- konq_viewmgr.cc.new	Mon Nov 13 18:39:31 2000
***************
*** 778,788 ****
  
  void KonqViewManager::slotProfileActivated( int id )
  {
  
!   QMap<QString, QString>::ConstIterator nameIt = m_mapProfileNames.find( \
                m_pamProfiles->popupMenu()->text( id ) );
!   if ( nameIt == m_mapProfileNames.end() )
!     return;
  
    KURL u; u.setPath( *nameIt );
    loadViewProfile( *nameIt, u.fileName() );
  }
--- 778,803 ----
  
  void KonqViewManager::slotProfileActivated( int id )
  {
+   QString item = m_pamProfiles->popupMenu()->text( id );
  
!   QMap<QString, QString>::ConstIterator nameIt = m_mapProfileNames.find( item );
!   
!   if( nameIt == m_mapProfileNames.end() ) {
!     // Unfortunate side effect of all the smart (well, ok, arguably
!     // smart) behavior in slotProfileListAboutToShow(): we must try
!     // a fallback test here with the first colon removed.
!     int first_colon = item.find(QChar('&'));
!     if (first_colon < 0) {
!       return;
!     } else {
!       item.remove(first_colon,1);
!       nameIt = m_mapProfileNames.find( item );
!       if( nameIt == m_mapProfileNames.end() ) // Still not found.
!         return;
!     }
  
+   }
+     
    KURL u; u.setPath( *nameIt );
    loadViewProfile( *nameIt, u.fileName() );
  }
***************
*** 798,807 ****
  
    m_mapProfileNames = KonqProfileDlg::readAllProfiles();
  
!   QMap<QString,QString>::ConstIterator eIt = m_mapProfileNames.begin();
    QMap<QString,QString>::ConstIterator eEnd = m_mapProfileNames.end();
!   for (; eIt != eEnd; ++eIt )
!     popup->insertItem( eIt.key() );
  
    m_bProfileListDirty = false;
  }
--- 813,884 ----
  
    m_mapProfileNames = KonqProfileDlg::readAllProfiles();
  
!   // Will keep track of used hotkey-chars
!   QMap<QChar,bool> used_hotkeys;
! 
!   QMap<QString,QString>::ConstIterator preIt = m_mapProfileNames.begin();
    QMap<QString,QString>::ConstIterator eEnd = m_mapProfileNames.end();
! 
!   // Prepass to detect manually user-coded hotkeys
!   for( ; preIt != eEnd; ++preIt ) {
!     QString item(preIt.key());
!     int user_ampersand = item.find(QChar('&'));
!     if( user_ampersand >= 0 ) {
!       // Sanity check.  Note that we don't try to find a hotkey if the
!       // user shoots him/herself in the foot by adding a bad '&'.
!       if( uint(user_ampersand) < item.length() - 2
!           && item[user_ampersand+1].isLetterOrNumber() ) {
!         used_hotkeys.insert(item[user_ampersand+1], true);
!       }
!     }
!   }
! 
!   QMap<QString,QString>::ConstIterator eIt = m_mapProfileNames.begin();
! 
!   for( ; eIt != eEnd; ++eIt ) {
!     QString item(eIt.key());
! 
!     // Attempt to find a good hotkey, but only if the user has not
!     // manually hardcoded a hotkey.
!     int user_ampersand = item.find(QChar('&'));
!     if( user_ampersand < 0 ) {
!       bool found = false;
!       uint found_idx;
!       uint j;
! 
!       // Check word-starting letters first.
!       for( j=0; j < item.length(); ++j ) {
!         const QChar current = item[j];
!         if( current.isLetterOrNumber()
!             && !used_hotkeys.contains(current)
!             && (0 == j || j > 0 && item[j-1].isSpace()) ) {
!           found = true;
!           found_idx = j;
!           break;
!         }
!       }
! 
!       if( !found ) {
!         // No word-starting letter; search for any letter.
!         for( j=0; j < item.length(); ++j ) {
!           const QChar current = item[j];
!           if( current.isLetterOrNumber()
!               && !used_hotkeys.contains(current) ) {
!             found = true;
!             found_idx = j;
!             break;
!           }
!         }
!       }
! 
!       if( found ) {
!         used_hotkeys.insert(item[j],true);
!         item.insert(j,QChar('&'));
!       }
!     }
! 
!     popup->insertItem( item );
!   }
  
    m_bProfileListDirty = false;
  }



[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic