Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Widget: MenuBar #169

Merged
merged 13 commits into from
Aug 12, 2020
2 changes: 2 additions & 0 deletions SOURCECONF.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ SET( ${TARGETLIB}_SOURCES
YLabel.cc
YLayoutBox.cc
YLogView.cc
YMenuBar.cc
YMenuButton.cc
YMultiLineEdit.cc
YMultiProgressMeter.cc
Expand Down Expand Up @@ -154,6 +155,7 @@ SET( ${TARGETLIB}_HEADERS
YLabel.h
YLayoutBox.h
YLogView.h
YMenuBar.h
YMenuButton.h
YMultiLineEdit.h
YMultiProgressMeter.h
Expand Down
2 changes: 2 additions & 0 deletions src/YItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,10 @@ class YItem;

//! Collection of pointers to YItem.
typedef std::vector<YItem *> YItemCollection;

//! Mutable iterator over @ref YItemCollection.
typedef YItemCollection::iterator YItemIterator;

//! Const iterator over @ref YItemCollection.
typedef YItemCollection::const_iterator YItemConstIterator;

Expand Down
318 changes: 318 additions & 0 deletions src/YMenuBar.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
/*
Copyright (c) [2020] SUSE LLC
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, or (at your option) version 3.0 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., 51 Franklin Street, Fifth
Floor, Boston, MA 02110-1301 USA
*/


/*-/

File: YMenuBar.cc

Author: Stefan Hundhammer <shundhammer@suse.de>

/-*/


#define YUILogComponent "ui"
#include "YUILog.h"

#include "YUISymbols.h"
#include "YShortcut.h"
#include "YMenuBar.h"


using std::string;


struct YMenuBarPrivate
{
YMenuBarPrivate()
: nextSerialNo( 0 )
{}

int nextSerialNo;
};


YMenuBar::YMenuBar( YWidget * parent )
: YSelectionWidget( parent,
"", // label
false ) // enforceSingleSelection
, priv( new YMenuBarPrivate() )
{
YUI_CHECK_NEW( priv );
}




YMenuBar::~YMenuBar()
{
// NOP
}


void
YMenuBar::addItems( const YItemCollection & itemCollection )
{
YSelectionWidget::addItems( itemCollection );
resolveShortcutConflicts();
rebuildMenuTree();
}


void
YMenuBar::addItem( YItem * item )
{
YSelectionWidget::addItem( item );
item->setIndex( ++(priv->nextSerialNo) );

if ( item->hasChildren() )
assignUniqueIndex( item->childrenBegin(), item->childrenEnd() );
}


void
YMenuBar::assignUniqueIndex( YItemIterator begin, YItemIterator end )
{
for ( YItemIterator it = begin; it != end; ++it )
{
YItem * item = *it;

item->setIndex( ++(priv->nextSerialNo) );

if ( item->hasChildren() )
assignUniqueIndex( item->childrenBegin(), item->childrenEnd() );
}
}


void
YMenuBar::deleteAllItems()
{
YSelectionWidget::deleteAllItems();
priv->nextSerialNo = 0;
}


YMenuItem *
YMenuBar::findMenuItem( int index )
{
return findMenuItem( index, itemsBegin(), itemsEnd() );
}


YMenuItem *
YMenuBar::findMenuItem( int wantedIndex,
YItemConstIterator begin,
YItemConstIterator end )
{
for ( YItemConstIterator it = begin; it != end; ++it )
{
YMenuItem * item = dynamic_cast<YMenuItem *> (*it);

if ( item )
{
if ( item->index() == wantedIndex )
return item;

if ( item->hasChildren() )
{
YMenuItem * result = findMenuItem( wantedIndex,
item->childrenBegin(),
item->childrenEnd() );
if ( result )
return result;
}
}
}

return 0;
}


static void resolveShortcutsConflict( YItemConstIterator begin,
YItemConstIterator end )
{
bool used[ sizeof( char ) << 8 ];

for ( unsigned i=0; i < sizeof( char ) << 8; i++ )
used[i] = false;
std::vector<YMenuItem*> conflicts;

for ( YItemConstIterator it = begin; it != end; ++it )
{
YMenuItem * item = dynamic_cast<YMenuItem *> (*it);

if ( item )
{
if ( item->hasChildren() )
{
resolveShortcutsConflict( item->childrenBegin(), item->childrenEnd() );
}

char shortcut = YShortcut::normalized(YShortcut::findShortcut(item->label()));

if ( shortcut == 0 )
{
conflicts.push_back(item);
yuiMilestone() << "No or invalid shortcut found " << item->label() << endl;
}
else if ( used[ (unsigned)shortcut ] )
{
conflicts.push_back(item);
yuiWarning() << "Conflicting shortcut found " << item->label() << endl;
}
else
{
used[ (unsigned) shortcut ] = true;
}
}
else
{
yuiWarning() << "non menu item used in call " << (*it)->label() << endl;
}
}

// cannot use YShortcut directly as an YItem is not a YWidget
for( YMenuItem *i: conflicts )
{
string clean = YShortcut::cleanShortcutString(i->label());
char new_c = 0;

size_t index = 0;
for (; index < clean.size(); ++index)
{
char ch = YShortcut::normalized( clean[index] );
// ch is set to 0 by normalized() if not valid
if ( ch != 0 && ! used[ (unsigned)ch ] )
{
new_c = ch;
used[(unsigned)ch] = true;
break;
}
}

if (new_c != 0)
{
clean.insert(index, 1, YShortcut::shortcutMarker());
yuiMilestone() << "New label used: " << clean << endl;
}

i->setLabel( clean );
}
}


void
YMenuBar::resolveShortcutConflicts()
{
resolveShortcutsConflict( itemsBegin(), itemsEnd() );
}


YMenuItem *
YMenuBar::findItem( std::vector<std::string> & path ) const
{
return findItem( path.begin(), path.end(),
itemsBegin(), itemsEnd() );
}


YMenuItem *
YMenuBar::findItem( std::vector<std::string>::iterator path_begin,
std::vector<std::string>::iterator path_end,
YItemConstIterator begin,
YItemConstIterator end ) const
{
for ( YItemConstIterator it = begin; it != end; ++it )
{
YMenuItem * item = dynamic_cast<YMenuItem *>(*it);
// Test that dynamic_cast didn't fail

if ( !item )
return 0;

if ( item->label() == *path_begin )
{
if ( std::next(path_begin) == path_end )
{
// Only return items which can trigger an action.
// Intermediate items only open a submenu, so continue looking.
if( item->hasChildren() )
continue;

return item;
}

// Look in child nodes and return if found one
YMenuItem * result = findItem( ++path_begin, path_end,
item->childrenBegin(), item->childrenEnd() );
if ( result )
return result;
}
}
return 0;
}


const YPropertySet &
YMenuBar::propertySet()
{
static YPropertySet propSet;
shundhammer marked this conversation as resolved.
Show resolved Hide resolved

if ( propSet.isEmpty() )
{
/*
* @property itemList Items All menu items and submenus
* @property string IconPath Base path for icons (on menu items)
*/
propSet.add( YProperty( YUIProperty_Items, YOtherProperty ) );
propSet.add( YProperty( YUIProperty_IconPath, YStringProperty ) );
propSet.add( YWidget::propertySet() );
}

return propSet;
}


bool
YMenuBar::setProperty( const string & propertyName, const YPropertyValue & val )
{
propertySet().check( propertyName, val.type() ); // throws exceptions if not found or type mismatch

if ( propertyName == YUIProperty_Items ) return false; // Needs special handling
else if ( propertyName == YUIProperty_IconPath ) setIconBasePath( val.stringVal() );
else
{
return YWidget::setProperty( propertyName, val );
}

return true; // success -- no special processing necessary
}


YPropertyValue
YMenuBar::getProperty( const string & propertyName )
{
propertySet().check( propertyName ); // throws exceptions if not found

if ( propertyName == YUIProperty_Label ) return YPropertyValue( label() );
else if ( propertyName == YUIProperty_Items ) return YPropertyValue( YOtherProperty );
else if ( propertyName == YUIProperty_IconPath ) return YPropertyValue( iconBasePath() );
else
{
return YWidget::getProperty( propertyName );
}
}