finalcut/src/fmenuitem.cpp

752 lines
17 KiB
C++
Raw Normal View History

2017-11-04 07:03:53 +01:00
/***********************************************************************
* fmenuitem.cpp - Widget FMenuItem *
* *
* This file is part of the Final Cut widget toolkit *
* *
* Copyright 2015-2019 Markus Gans *
2017-11-04 07:03:53 +01:00
* *
* The Final Cut 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 3 of *
* the License, or (at your option) any later version. *
* *
* The Final Cut 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 program. If not, see *
* <http://www.gnu.org/licenses/>. *
***********************************************************************/
2018-12-28 22:57:43 +01:00
#include <memory>
#include "final/fapplication.h"
#include "final/fdialog.h"
#include "final/fevent.h"
#include "final/fmenu.h"
#include "final/fmenubar.h"
#include "final/fmenulist.h"
#include "final/fmenuitem.h"
#include "final/fstatusbar.h"
namespace finalcut
{
//----------------------------------------------------------------------
// class FMenuItem
//----------------------------------------------------------------------
// constructor and destructor
//----------------------------------------------------------------------
2015-09-22 04:18:20 +02:00
FMenuItem::FMenuItem (FWidget* parent)
: FWidget(parent)
{
init (parent);
}
//----------------------------------------------------------------------
2017-03-17 22:59:06 +01:00
FMenuItem::FMenuItem (const FString& txt, FWidget* parent)
2015-09-22 04:18:20 +02:00
: FWidget(parent)
, text(txt)
{
init (parent);
}
//----------------------------------------------------------------------
2018-11-21 20:07:08 +01:00
FMenuItem::FMenuItem (FKey k, const FString& txt, FWidget* parent)
2015-11-12 01:33:16 +01:00
: FWidget(parent)
, text(txt)
, accel_key(k)
{
init (parent);
}
//----------------------------------------------------------------------
FMenuItem::~FMenuItem() // destructor
{
if ( super_menu && (isMenu(super_menu) || isMenuBar(super_menu)) )
{
auto menu_list = getFMenuList(*super_menu);
if ( menu_list )
menu_list->remove(this);
}
delAccelerator();
// remove dialog list item callback from the dialog
if ( associated_window )
associated_window->delCallback(this);
}
// public methods of FMenuItem
//----------------------------------------------------------------------
2018-12-22 23:50:10 +01:00
bool FMenuItem::setEnable (bool enable)
{
2018-12-22 23:50:10 +01:00
FWidget::setEnable(enable);
auto super = getSuperMenu();
2018-12-22 23:50:10 +01:00
if ( enable )
{
if ( super && isMenuBar(super) )
2015-09-15 23:07:24 +02:00
{
// Meta + hotkey
2018-11-21 20:07:08 +01:00
super->addAccelerator ( fc::Fmkey_meta + FKey(std::tolower(hotkey))
, this );
2015-09-15 23:07:24 +02:00
}
}
else
{
if ( super && isMenuBar(super) )
super->delAccelerator (this);
}
2015-10-29 21:10:50 +01:00
2018-12-22 23:50:10 +01:00
return enable;
}
2015-08-16 20:05:39 +02:00
//----------------------------------------------------------------------
2018-12-22 23:50:10 +01:00
bool FMenuItem::setFocus (bool enable)
2015-08-16 20:05:39 +02:00
{
2018-12-22 23:50:10 +01:00
FWidget::setFocus(enable);
2015-08-16 20:05:39 +02:00
2018-12-22 23:50:10 +01:00
if ( enable )
2015-09-30 22:39:02 +02:00
{
if ( isEnabled() )
2015-09-30 22:39:02 +02:00
{
if ( ! selected )
{
auto menu_list = getFMenuList(*getSuperMenu());
setSelected();
if ( menu_list )
{
menu_list->unselectItem();
menu_list->setSelectedItem(this);
}
2015-08-16 20:05:39 +02:00
if ( getStatusBar() )
getStatusBar()->drawMessage();
auto parent = getSuperMenu();
2015-09-15 23:07:24 +02:00
if ( isMenuBar(parent) )
{
auto menubar_ptr = static_cast<FMenuBar*>(parent);
if ( menubar_ptr )
menubar_ptr->redraw();
}
else if ( isMenu(parent) )
{
auto menu_ptr = static_cast<FMenu*>(parent);
if ( menu_ptr )
menu_ptr->redraw();
}
}
if ( getStatusBar() )
{
2018-12-26 23:41:49 +01:00
const auto& msg = getStatusbarMessage();
const auto& curMsg = getStatusBar()->getMessage();
2016-07-31 20:25:25 +02:00
if ( curMsg != msg )
getStatusBar()->setMessage(msg);
}
}
}
else
{
if ( isEnabled() && getStatusBar() )
getStatusBar()->clearMessage();
}
2018-12-22 23:50:10 +01:00
return enable;
}
//----------------------------------------------------------------------
void FMenuItem::setSelected()
{
if ( isEnabled() )
{
selected = true;
processActivate();
}
}
//----------------------------------------------------------------------
void FMenuItem::unsetSelected()
{
selected = false;
unsetCursorPos();
processDeactivate();
}
//----------------------------------------------------------------------
2017-03-17 22:59:06 +01:00
void FMenuItem::setText (const FString& txt)
{
2019-08-25 22:16:00 +02:00
text.setString(txt);
text_length = text.getLength();
hotkey = hotKey();
if ( hotkey )
text_length--;
updateSuperMenuDimensions();
2016-06-12 23:06:58 +02:00
}
2015-11-12 01:33:16 +01:00
//----------------------------------------------------------------------
2018-11-21 20:07:08 +01:00
void FMenuItem::addAccelerator (FKey key, FWidget* obj)
2015-11-12 01:33:16 +01:00
{
auto root = getRootWidget();
accelerator accel = { key, obj };
2015-11-12 01:33:16 +01:00
if ( root && root->getAcceleratorList() )
2015-11-12 01:33:16 +01:00
{
accel_key = key;
root->getAcceleratorList()->push_back(accel);
}
updateSuperMenuDimensions();
2015-11-12 01:33:16 +01:00
}
//----------------------------------------------------------------------
void FMenuItem::delAccelerator (FWidget* obj)
{
auto root = getRootWidget();
if ( root
&& root->getAcceleratorList()
&& ! root->getAcceleratorList()->empty() )
2015-11-12 01:33:16 +01:00
{
auto iter = root->getAcceleratorList()->begin();
2015-11-12 01:33:16 +01:00
while ( iter != root->getAcceleratorList()->end() )
2015-11-12 01:33:16 +01:00
{
if ( iter->object == obj )
2015-11-12 01:33:16 +01:00
{
accel_key = 0;
iter = root->getAcceleratorList()->erase(iter);
2015-11-12 01:33:16 +01:00
}
else
++iter;
2015-11-12 01:33:16 +01:00
}
}
updateSuperMenuDimensions();
2015-11-12 01:33:16 +01:00
}
//----------------------------------------------------------------------
void FMenuItem::openMenu()
{
if ( ! hasMenu() )
return;
auto dd_menu = getMenu(); // Drop-down menu
if ( dd_menu->isShown() )
return;
auto openmenu = static_cast<FMenu*>(getOpenMenu());
2018-12-01 21:28:25 +01:00
if ( openmenu && openmenu != dd_menu )
{
2018-12-01 21:28:25 +01:00
openmenu->hide();
openmenu->hideSubMenus();
}
if ( dialog_index )
createDialogList (dd_menu);
setOpenMenu(dd_menu);
dd_menu->show();
2017-08-12 20:10:27 +02:00
dd_menu->raiseWindow();
dd_menu->redraw();
updateTerminal();
flush_out();
}
//----------------------------------------------------------------------
2015-10-29 21:10:50 +01:00
void FMenuItem::onKeyPress (FKeyEvent* ev)
{
if ( ! super_menu )
return;
if ( isMenu(super_menu) )
{
auto smenu = static_cast<FMenu*>(super_menu);
smenu->onKeyPress(ev);
}
2015-10-29 21:10:50 +01:00
if ( isMenuBar(super_menu) )
{
auto mbar = static_cast<FMenuBar*>(super_menu);
if ( mbar )
{
if ( mbar->hotkeyMenu(ev) )
return;
mbar->onKeyPress(ev);
}
}
}
2016-06-12 23:06:58 +02:00
//----------------------------------------------------------------------
void FMenuItem::onMouseDoubleClick (FMouseEvent* ev)
{
if ( ! super_menu )
return;
if ( isMenu(super_menu) )
2016-06-12 23:06:58 +02:00
{
auto smenu = static_cast<FMenu*>(super_menu);
passMouseEvent (smenu, ev, fc::MouseDoubleClick_Event);
}
2016-06-12 23:06:58 +02:00
if ( isMenuBar(super_menu) )
{
auto mbar = static_cast<FMenuBar*>(super_menu);
passMouseEvent (mbar, ev, fc::MouseDoubleClick_Event);
}
2016-06-12 23:06:58 +02:00
if ( isWindowsMenu(super_menu) )
{
auto dgl = static_cast<FDialog*>(super_menu);
passMouseEvent (dgl, ev, fc::MouseDoubleClick_Event);
2016-06-12 23:06:58 +02:00
}
}
2015-09-28 04:31:29 +02:00
//----------------------------------------------------------------------
void FMenuItem::onMouseDown (FMouseEvent* ev)
{
if ( ! super_menu )
return;
if ( isMenu(super_menu) )
2015-09-30 22:39:02 +02:00
{
auto smenu = static_cast<FMenu*>(super_menu);
passMouseEvent (smenu, ev, fc::MouseDown_Event);
}
2015-10-01 03:48:58 +02:00
if ( isMenuBar(super_menu) )
{
auto mbar = static_cast<FMenuBar*>(super_menu);
passMouseEvent (mbar, ev, fc::MouseDown_Event);
}
2016-06-12 23:06:58 +02:00
if ( isWindowsMenu(super_menu) )
{
auto dgl = static_cast<FDialog*>(super_menu);
passMouseEvent (dgl, ev, fc::MouseDown_Event);
}
2015-09-28 04:31:29 +02:00
}
//----------------------------------------------------------------------
void FMenuItem::onMouseUp (FMouseEvent* ev)
{
if ( ! super_menu )
return;
if ( isMenu(super_menu) )
2015-09-30 22:39:02 +02:00
{
auto smenu = static_cast<FMenu*>(super_menu);
passMouseEvent (smenu, ev, fc::MouseUp_Event);
}
2015-10-01 03:48:58 +02:00
if ( isMenuBar(super_menu) )
{
auto mbar = static_cast<FMenuBar*>(super_menu);
passMouseEvent (mbar, ev, fc::MouseUp_Event);
}
2016-06-12 23:06:58 +02:00
if ( isWindowsMenu(super_menu) )
{
auto dgl = static_cast<FDialog*>(super_menu);
passMouseEvent (dgl, ev, fc::MouseUp_Event);
}
2015-09-28 04:31:29 +02:00
}
//----------------------------------------------------------------------
void FMenuItem::onMouseMove (FMouseEvent* ev)
{
if ( ! super_menu )
return;
if ( isMenu(super_menu) )
2015-09-30 22:39:02 +02:00
{
auto smenu = static_cast<FMenu*>(super_menu);
passMouseEvent (smenu, ev, fc::MouseMove_Event);
}
2015-10-01 03:48:58 +02:00
if ( isMenuBar(super_menu) )
{
auto mbar = static_cast<FMenuBar*>(super_menu);
passMouseEvent (mbar, ev, fc::MouseMove_Event);
}
2015-09-28 04:31:29 +02:00
if ( isWindowsMenu(super_menu) )
{
auto dgl = static_cast<FDialog*>(super_menu);
passMouseEvent (dgl, ev, fc::MouseMove_Event);
}
2015-09-28 04:31:29 +02:00
}
2015-10-29 21:10:50 +01:00
//----------------------------------------------------------------------
void FMenuItem::onAccel (FAccelEvent* ev)
{
if ( ! isEnabled() || isSelected() )
return;
if ( ! super_menu || ! isMenuBar(super_menu) )
2015-10-29 21:10:50 +01:00
{
processClicked();
return;
}
auto mbar = static_cast<FMenuBar*>(super_menu);
if ( menu )
{
if ( mbar->getSelectedItem() )
mbar->getSelectedItem()->unsetSelected();
setSelected();
mbar->setSelectedItem(this);
openMenu();
auto focused_widget = static_cast<FWidget*>(ev->focusedWidget());
2017-08-24 22:58:34 +02:00
menu->unselectItem();
menu->selectFirstItem();
2017-08-24 22:58:34 +02:00
if ( menu->getSelectedItem() )
menu->getSelectedItem()->setFocus();
2017-08-24 22:58:34 +02:00
if ( focused_widget && focused_widget->isWidget() )
2017-06-14 01:23:10 +02:00
focused_widget->redraw();
2017-08-24 22:58:34 +02:00
menu->redraw();
2017-06-11 17:47:50 +02:00
2017-08-24 22:58:34 +02:00
if ( getStatusBar() )
getStatusBar()->drawMessage();
mbar->redraw();
mbar->drop_down = true;
}
else
{
unsetSelected();
mbar->unsetSelectedItem();
mbar->redraw();
processClicked();
mbar->drop_down = false;
2015-10-29 21:10:50 +01:00
}
ev->accept();
2015-10-29 21:10:50 +01:00
}
//----------------------------------------------------------------------
void FMenuItem::onFocusIn (FFocusEvent*)
{
if ( getStatusBar() )
getStatusBar()->drawMessage();
2015-10-29 21:10:50 +01:00
}
//----------------------------------------------------------------------
void FMenuItem::onFocusOut (FFocusEvent*)
{
unsetSelected();
if ( super_menu && isMenuBar(super_menu) )
{
auto mbar = static_cast<FMenuBar*>(super_menu);
mbar->redraw();
}
if ( getStatusBar() )
2015-10-29 21:10:50 +01:00
{
getStatusBar()->clearMessage();
getStatusBar()->drawMessage();
2015-10-29 21:10:50 +01:00
}
}
// protected methods of FMenuItem
2015-10-29 21:10:50 +01:00
//----------------------------------------------------------------------
bool FMenuItem::isWindowsMenu (FWidget* w) const
2015-10-29 21:10:50 +01:00
{
return ( w ) ? w->isDialogWidget() : false;
}
2015-10-29 21:10:50 +01:00
//----------------------------------------------------------------------
bool FMenuItem::isMenuBar (FWidget* w) const
{
return ( w ) ? w->isInstanceOf("FMenuBar") : false;
}
2015-10-29 21:10:50 +01:00
//----------------------------------------------------------------------
bool FMenuItem::isMenu (FWidget* w) const
{
if ( ! w )
return false;
bool m1 = w->isInstanceOf("FMenu");
bool m2 = w->isInstanceOf("FDialogListMenu");
return bool( m1 || m2 );
2015-10-29 21:10:50 +01:00
}
// private methods of FMenuItem
//----------------------------------------------------------------------
FMenuList* FMenuItem::getFMenuList (FWidget& widget)
{
2019-08-25 22:16:00 +02:00
FMenuList* menu_list{};
if ( isMenu(&widget) )
{
auto Menu = static_cast<FMenu*>(&widget);
menu_list = static_cast<FMenuList*>(Menu);
}
else if ( isMenuBar(&widget) )
{
auto Menubar = static_cast<FMenuBar*>(&widget);
menu_list = static_cast<FMenuList*>(Menubar);
}
else
menu_list = nullptr;
return menu_list;
}
2015-10-29 21:10:50 +01:00
//----------------------------------------------------------------------
void FMenuItem::init (FWidget* parent)
2015-10-29 21:10:50 +01:00
{
text_length = text.getLength();
hotkey = hotKey();
2015-10-29 21:10:50 +01:00
if ( hotkey )
text_length--;
setGeometry (FPoint(1, 1), FSize(text_length + 2, 1), false);
2015-11-07 23:16:09 +01:00
if ( ! parent )
return;
setSuperMenu (parent);
2018-11-21 20:07:08 +01:00
if ( accel_key )
addAccelerator (accel_key);
auto menu_list = getFMenuList(*parent);
if ( menu_list )
menu_list->insert(this);
if ( isMenuBar(parent) ) // Parent is menubar
{
auto menubar_ptr = static_cast<FMenuBar*>(parent);
menubar_ptr->calculateDimensions();
if ( hotkey ) // Meta + hotkey
2018-11-21 20:07:08 +01:00
menubar_ptr->addAccelerator ( fc::Fmkey_meta + FKey(std::tolower(hotkey))
, this );
2015-10-29 21:10:50 +01:00
addCallback // for this element
(
"deactivate",
F_METHOD_CALLBACK (parent, &FMenuBar::cb_item_deactivated)
);
}
else if ( isMenu(parent) ) // Parent is menu
{
auto menu_ptr = static_cast<FMenu*>(parent);
menu_ptr->calculateDimensions();
2015-10-29 21:10:50 +01:00
}
}
//----------------------------------------------------------------------
uChar FMenuItem::hotKey()
{
if ( text.isEmpty() )
return 0;
2019-08-25 22:16:00 +02:00
std::size_t length = text.getLength();
2019-08-25 22:16:00 +02:00
for (std::size_t i{0}; i < length; i++)
2015-08-22 18:53:52 +02:00
{
try
{
if ( i + 1 < length && text[i] == '&' )
return uChar(text[++i]);
}
catch (const std::out_of_range&)
{
return 0;
}
2015-08-22 18:53:52 +02:00
}
return 0;
}
//----------------------------------------------------------------------
void FMenuItem::updateSuperMenuDimensions()
{
if ( ! super_menu || ! isMenu(super_menu) )
return;
auto menu_ptr = static_cast<FMenu*>(super_menu);
if ( menu_ptr )
menu_ptr->calculateDimensions();
}
//----------------------------------------------------------------------
void FMenuItem::processActivate()
2015-09-30 22:39:02 +02:00
{
emitCallback("activate");
2015-09-30 22:39:02 +02:00
}
//----------------------------------------------------------------------
void FMenuItem::processDeactivate()
{
emitCallback("deactivate");
}
//----------------------------------------------------------------------
void FMenuItem::createDialogList (FMenu* winmenu)
{
winmenu->clear();
if ( getDialogList() && ! getDialogList()->empty() )
{
auto first = getDialogList()->begin();
auto iter = first;
while ( iter != getDialogList()->end() && *iter )
{
auto win = static_cast<FDialog*>(*iter);
if ( win )
{
2019-08-25 22:16:00 +02:00
FMenuItem* win_item{};
2018-11-21 20:07:08 +01:00
uInt32 n = uInt32(std::distance(first, iter));
// get the dialog title
2018-12-26 23:41:49 +01:00
const auto& name = win->getText();
2017-08-12 22:55:29 +02:00
try
{
// create a new dialog list item
win_item = new FMenuItem (name, winmenu);
}
catch (const std::bad_alloc& ex)
{
std::cerr << bad_alloc_str << ex.what() << std::endl;
2017-08-12 23:11:21 +02:00
return;
2017-08-12 22:55:29 +02:00
}
if ( n < 9 )
2017-09-11 03:06:02 +02:00
win_item->addAccelerator (fc::Fmkey_1 + n); // Meta + 1..9
win_item->addCallback
(
"clicked",
F_METHOD_CALLBACK (win_item, &FMenuItem::cb_switchToDialog),
2018-12-27 00:14:46 +01:00
static_cast<FDataPtr>(win)
);
win->addCallback
(
"destroy",
F_METHOD_CALLBACK (win_item, &FMenuItem::cb_destroyDialog)
);
win_item->associated_window = win;
}
++iter;
}
}
winmenu->calculateDimensions();
}
//----------------------------------------------------------------------
2018-10-20 22:50:35 +02:00
template <typename T>
void FMenuItem::passMouseEvent ( T widget, FMouseEvent* ev
, fc::events ev_type )
{
if ( ! widget )
return;
2018-12-26 23:41:49 +01:00
const auto& t = ev->getTermPos();
const auto& p2 = widget->termToWidgetPos(t);
int b = ev->getButton();
2018-12-19 22:04:02 +01:00
std::shared_ptr<FMouseEvent> _ev;
try
{
2018-12-19 22:04:02 +01:00
_ev = std::make_shared<FMouseEvent>(ev_type, p2, t, b);
}
catch (const std::bad_alloc& ex)
{
std::cerr << bad_alloc_str << ex.what() << std::endl;
return;
}
switch ( int(ev_type) )
{
case fc::MouseDoubleClick_Event:
2018-12-19 22:04:02 +01:00
widget->onMouseDoubleClick(_ev.get());
break;
case fc::MouseDown_Event:
2018-12-19 22:04:02 +01:00
widget->onMouseDown(_ev.get());
break;
case fc::MouseUp_Event:
2018-12-19 22:04:02 +01:00
widget->onMouseUp(_ev.get());
break;
case fc::MouseMove_Event:
2018-12-19 22:04:02 +01:00
widget->onMouseMove(_ev.get());
break;
}
}
2015-09-30 22:39:02 +02:00
//----------------------------------------------------------------------
2018-12-27 00:14:46 +01:00
void FMenuItem::cb_switchToDialog (FWidget*, FDataPtr data)
{
auto win = static_cast<FDialog*>(data);
if ( win )
{
auto focus = getFocusWidget();
2018-12-01 21:28:25 +01:00
FAccelEvent a_ev (fc::Accelerator_Event, focus);
FApplication::sendEvent (win, &a_ev);
}
}
//----------------------------------------------------------------------
2018-12-27 00:14:46 +01:00
void FMenuItem::cb_destroyDialog (FWidget* widget, FDataPtr)
{
auto win = static_cast<FDialog*>(widget);
auto fapp = FApplication::getApplicationObject();
if ( win && fapp )
{
delAccelerator(win);
delCallback(win);
associated_window = nullptr;
}
2015-08-16 20:05:39 +02:00
}
//----------------------------------------------------------------------
void FMenuItem::processClicked()
2015-08-16 20:05:39 +02:00
{
emitCallback("clicked");
}
} // namespace finalcut