finalcut/src/fbutton.cpp

809 lines
18 KiB
C++
Raw Normal View History

/************************************************************************
* fbutton.cpp - Widget FButton *
* *
* This file is part of the Final Cut widget toolkit *
* *
* Copyright 2012-2017 Markus Gans *
* *
* The Final Cut is free software; you can redistribute it and/or modify *
* it under the terms of the GNU 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
************************************************************************/
2015-05-23 13:35:12 +02:00
#include "final/fapplication.h"
#include "final/fbutton.h"
#include "final/fstatusbar.h"
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
// class FButton
//----------------------------------------------------------------------
// constructors and destructor
//----------------------------------------------------------------------
2015-09-22 04:18:20 +02:00
FButton::FButton(FWidget* parent)
: FWidget(parent)
, text()
, button_down(false)
, click_animation(true)
, click_time(150)
, button_fg(wc.button_active_fg)
, button_bg(wc.button_active_bg)
, button_hotkey_fg(wc.button_hotkey_fg)
, button_focus_fg(wc.button_active_focus_fg)
, button_focus_bg(wc.button_active_focus_bg)
, button_inactive_fg(wc.button_inactive_fg)
, button_inactive_bg(wc.button_inactive_bg)
{
init();
}
//----------------------------------------------------------------------
FButton::FButton (const FString& txt, FWidget* parent)
: FWidget(parent)
, text(txt)
, button_down(false)
, click_animation(true)
, click_time(150)
, button_fg(wc.button_active_fg)
, button_bg(wc.button_active_bg)
, button_hotkey_fg(wc.button_hotkey_fg)
, button_focus_fg(wc.button_active_focus_fg)
, button_focus_bg(wc.button_active_focus_bg)
, button_inactive_fg(wc.button_inactive_fg)
, button_inactive_bg(wc.button_inactive_bg)
{
init();
detectHotkey();
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
FButton::~FButton() // destructor
{
2015-11-12 01:33:16 +01:00
delAccelerator();
2015-12-19 20:49:01 +01:00
delOwnTimer();
2015-05-23 13:35:12 +02:00
}
// public methods of FButton
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FButton::setForegroundColor (short color)
2015-05-23 13:35:12 +02:00
{
FWidget::setForegroundColor(color);
updateButtonColor();
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
void FButton::setBackgroundColor (short color)
2015-05-23 13:35:12 +02:00
{
FWidget::setBackgroundColor(color);
updateButtonColor();
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FButton::setHotkeyForegroundColor (short color)
{
// valid colors -1..254
if ( color == fc::Default || color >> 8 == 0 )
button_hotkey_fg = color;
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FButton::setFocusForegroundColor (short color)
{
// valid colors -1..254
if ( color == fc::Default || color >> 8 == 0 )
button_focus_fg = color;
updateButtonColor();
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
void FButton::setFocusBackgroundColor (short color)
2015-05-23 13:35:12 +02:00
{
// valid colors -1..254
if ( color == fc::Default || color >> 8 == 0 )
button_focus_bg = color;
updateButtonColor();
2015-05-23 13:35:12 +02:00
}
2015-09-22 04:18:20 +02:00
//----------------------------------------------------------------------
void FButton::setInactiveForegroundColor (short color)
2015-09-22 04:18:20 +02:00
{
// valid colors -1..254
if ( color == fc::Default || color >> 8 == 0 )
button_inactive_fg = color;
updateButtonColor();
2015-09-22 04:18:20 +02:00
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FButton::setInactiveBackgroundColor (short color)
2015-05-23 13:35:12 +02:00
{
// valid colors -1..254
if ( color == fc::Default || color >> 8 == 0 )
button_inactive_bg = color;
2015-05-23 13:35:12 +02:00
updateButtonColor();
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
bool FButton::setNoUnderline (bool on)
{
if ( on )
flags |= fc::no_underline;
2015-05-23 13:35:12 +02:00
else
flags &= ~fc::no_underline;
return on;
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
bool FButton::setEnable (bool on)
{
FWidget::setEnable(on);
2015-05-23 13:35:12 +02:00
if ( on )
setHotkeyAccelerator();
2015-05-23 13:35:12 +02:00
else
delAccelerator();
updateButtonColor();
return on;
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
bool FButton::setFocus (bool on)
{
FWidget::setFocus(on);
2015-05-23 13:35:12 +02:00
if ( on )
2015-05-23 13:35:12 +02:00
{
if ( isEnabled() )
{
if ( getStatusBar() )
2015-05-23 13:35:12 +02:00
{
2017-03-26 20:40:04 +02:00
const FString& msg = getStatusbarMessage();
const FString& curMsg = getStatusBar()->getMessage();
if ( curMsg != msg )
getStatusBar()->setMessage(msg);
2015-05-23 13:35:12 +02:00
}
}
}
else
2015-05-23 13:35:12 +02:00
{
if ( isEnabled() && getStatusBar() )
getStatusBar()->clearMessage();
2015-05-23 13:35:12 +02:00
}
updateButtonColor();
return on;
}
//----------------------------------------------------------------------
bool FButton::setFlat (bool on)
{
if ( on )
flags |= fc::flat;
else
flags &= ~fc::flat;
return on;
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
bool FButton::setShadow (bool on)
{
if ( on
&& Encoding != fc::VT100
&& Encoding != fc::ASCII )
{
flags |= fc::shadow;
setShadowSize(1,1);
}
else
{
flags &= ~fc::shadow;
setShadowSize(0,0);
}
2015-05-23 13:35:12 +02:00
return on;
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
bool FButton::setDown (bool on)
{
if ( button_down != on )
{
button_down = on;
redraw();
}
return on;
}
//----------------------------------------------------------------------
void FButton::setText (const FString& txt)
{
if ( txt )
text = txt;
2015-05-23 13:35:12 +02:00
else
text = "";
2015-05-23 13:35:12 +02:00
detectHotkey();
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FButton::hide()
{
int s, f, size;
short fg, bg;
char* blank;
FWidget* parent_widget = getParentWidget();
FWidget::hide();
2015-05-23 13:35:12 +02:00
if ( parent_widget )
{
fg = parent_widget->getForegroundColor();
bg = parent_widget->getBackgroundColor();
}
2015-05-23 13:35:12 +02:00
else
{
fg = wc.dialog_fg;
bg = wc.dialog_bg;
}
2015-05-23 13:35:12 +02:00
setColor (fg, bg);
s = hasShadow() ? 1 : 0;
f = isFlat() ? 1 : 0;
size = getWidth() + s + (f << 1);
if ( size < 0 )
return;
2017-08-12 22:55:29 +02:00
try
{
blank = new char[size + 1];
2017-08-12 22:55:29 +02:00
}
catch (const std::bad_alloc& ex)
{
std::cerr << "not enough memory to alloc " << ex.what() << std::endl;
return;
}
std::memset(blank, ' ', uLong(size));
blank[size] = '\0';
for (int y = 0; y < getHeight() + s + (f << 1); y++)
2015-05-23 13:35:12 +02:00
{
setPrintPos (1 - f, 1 + y - f);
print (blank);
}
delete[] blank;
}
//----------------------------------------------------------------------
void FButton::onKeyPress (FKeyEvent* ev)
{
int key;
if ( ! isEnabled() )
return;
key = ev->key();
switch ( key )
{
case fc::Fkey_return:
case fc::Fkey_enter:
case fc::Fkey_space:
if ( click_animation )
{
setDown();
addTimer(click_time);
}
processClick();
ev->accept();
break;
default:
break;
2015-05-23 13:35:12 +02:00
}
}
//----------------------------------------------------------------------
void FButton::onMouseDown (FMouseEvent* ev)
{
if ( ev->getButton() != fc::LeftButton )
{
setUp();
return;
}
if ( ! hasFocus() )
{
FWidget* focused_widget = getFocusWidget();
FFocusEvent out (fc::FocusOut_Event);
FApplication::queueEvent(focused_widget, &out);
setFocus();
2015-05-23 13:35:12 +02:00
if ( focused_widget )
focused_widget->redraw();
if ( getStatusBar() )
getStatusBar()->drawMessage();
}
FPoint tPos = ev->getTermPos();
if ( getTermGeometry().contains(tPos) )
setDown();
}
//----------------------------------------------------------------------
void FButton::onMouseUp (FMouseEvent* ev)
{
if ( ev->getButton() != fc::LeftButton )
return;
if ( button_down )
2015-05-23 13:35:12 +02:00
{
setUp();
if ( getTermGeometry().contains(ev->getTermPos()) )
processClick();
}
}
//----------------------------------------------------------------------
void FButton::onMouseMove (FMouseEvent* ev)
{
if ( ev->getButton() != fc::LeftButton )
return;
FPoint tPos = ev->getTermPos();
if ( click_animation )
{
if ( getTermGeometry().contains(tPos) )
setDown();
else
setUp();
2015-05-23 13:35:12 +02:00
}
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FButton::onTimer (FTimerEvent* ev)
{
delTimer(ev->timerId());
setUp();
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FButton::onAccel (FAccelEvent* ev)
{
if ( ! isEnabled() )
return;
if ( ! hasFocus() )
2015-05-23 13:35:12 +02:00
{
FWidget* focused_widget = static_cast<FWidget*>(ev->focusedWidget());
2017-06-14 01:23:10 +02:00
if ( focused_widget && focused_widget->isWidget() )
2017-06-11 17:47:50 +02:00
{
FFocusEvent out (fc::FocusOut_Event);
FApplication::queueEvent(focused_widget, &out);
setFocus();
2017-06-14 01:23:10 +02:00
focused_widget->redraw();
2017-06-11 17:47:50 +02:00
if ( click_animation )
setDown();
else
redraw();
if ( getStatusBar() )
getStatusBar()->drawMessage();
}
2015-05-23 13:35:12 +02:00
}
else if ( click_animation )
setDown();
2015-05-23 13:35:12 +02:00
if ( click_animation )
addTimer(click_time);
2015-10-11 21:56:16 +02:00
processClick();
ev->accept();
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FButton::onFocusIn (FFocusEvent*)
{
if ( getStatusBar() )
getStatusBar()->drawMessage();
}
//----------------------------------------------------------------------
void FButton::onFocusOut (FFocusEvent*)
{
if ( getStatusBar() )
2015-05-23 13:35:12 +02:00
{
getStatusBar()->clearMessage();
getStatusBar()->drawMessage();
}
}
// private methods of FButton
//----------------------------------------------------------------------
void FButton::init()
{
setForegroundColor (wc.button_active_fg);
setBackgroundColor (wc.button_active_bg);
setShadow();
}
//----------------------------------------------------------------------
uChar FButton::getHotkey()
{
int length;
if ( text.isEmpty() )
return 0;
length = int(text.getLength());
for (int i = 0; i < length; i++)
{
try
{
if ( i + 1 < length && text[uInt(i)] == '&' )
return uChar(text[uInt(++i)]);
}
catch (const std::out_of_range&)
2015-05-23 13:35:12 +02:00
{
return 0;
2015-05-23 13:35:12 +02:00
}
}
return 0;
2015-05-23 13:35:12 +02:00
}
//----------------------------------------------------------------------
void FButton::setHotkeyAccelerator()
{
int hotkey = getHotkey();
if ( hotkey )
{
if ( std::isalpha(hotkey) || std::isdigit(hotkey) )
{
addAccelerator (std::tolower(hotkey));
addAccelerator (std::toupper(hotkey));
// Meta + hotkey
addAccelerator (fc::Fmkey_meta + std::tolower(hotkey));
}
else
addAccelerator (getHotkey());
}
else
delAccelerator();
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
inline void FButton::detectHotkey()
{
if ( isEnabled() )
{
delAccelerator();
setHotkeyAccelerator();
}
}
//----------------------------------------------------------------------
void FButton::draw()
{
int active_focus
, d
, i
, j
, x
, mono_offset
, mono_1st_char
, margin
, length
, hotkeypos
, hotkey_offset
, space;
bool is_ActiveFocus
, is_Active
, is_Focus
, is_Flat
, is_NonFlatShadow
, is_NoUnderline;
register wchar_t* src;
register wchar_t* dest;
wchar_t* ButtonText;
FString txt;
FWidget* parent_widget = getParentWidget();
length = int(text.getLength());
hotkeypos = -1;
hotkey_offset = 0;
space = int(' ');
2017-08-12 22:55:29 +02:00
try
{
if ( isMonochron() || getMaxColor() < 16 )
ButtonText = new wchar_t[length + 3]();
2017-08-12 22:55:29 +02:00
else
ButtonText = new wchar_t[length + 1]();
2017-08-12 22:55:29 +02:00
}
catch (const std::bad_alloc& ex)
{
std::cerr << "not enough memory to alloc " << ex.what() << std::endl;
return;
}
txt = FString(text);
src = const_cast<wchar_t*>(txt.wc_str());
dest = const_cast<wchar_t*>(ButtonText);
active_focus = fc::active + fc::focus;
is_ActiveFocus = ((flags & active_focus) == active_focus);
is_Active = ((flags & fc::active) != 0);
is_Focus = ((flags & fc::focus) != 0);
is_Flat = isFlat();
is_NonFlatShadow = ((flags & (fc::shadow + fc::flat)) == fc::shadow);
is_NoUnderline = ((flags & fc::no_underline) != 0);
if ( isMonochron() )
setReverse(true);
if ( button_down && click_animation )
{
// noshadow + indent one character to the right
if ( is_Flat )
clearFlatBorder();
else
clearShadow();
if ( parent_widget )
setColor ( parent_widget->getForegroundColor()
, parent_widget->getBackgroundColor() );
for (int y = 1; y <= getHeight(); y++)
{
setPrintPos (1, y);
2017-09-11 03:06:02 +02:00
print (' '); // clear one left █
}
d = 1;
}
else
d = 0;
if ( ! is_Active && isMonochron() )
space = fc::MediumShade; // ▒
if ( isMonochron() && is_ActiveFocus )
{
txt = "<" + txt + ">";
length = int(txt.getLength());
src = const_cast<wchar_t*>(txt.wc_str());
mono_1st_char = 1;
mono_offset = 2;
}
else
{
mono_1st_char = 0;
mono_offset = 0;
}
// find hotkey position in string
// + generate a new string without the '&'-sign
for (i = 0; i < length; i++)
2015-05-23 13:35:12 +02:00
{
if ( i < length && txt[uInt(i)] == '&' && hotkeypos == -1 )
{
hotkeypos = i;
i++;
src++;
}
*dest++ = *src++;
2015-05-23 13:35:12 +02:00
}
if ( hotkeypos != -1 )
hotkey_offset = 1;
2015-05-23 13:35:12 +02:00
if ( length - hotkey_offset + mono_offset - hotkey_offset <= getWidth() )
margin = 1;
2015-05-23 13:35:12 +02:00
else
margin = 0;
2015-05-23 13:35:12 +02:00
if ( isMonochron() && (is_Active || is_Focus) )
setReverse(false);
2015-05-23 13:35:12 +02:00
if ( margin == 1 )
2015-05-23 13:35:12 +02:00
{
setColor (getForegroundColor(), button_bg);
for (int y = 0; y < getHeight(); y++)
{
setPrintPos (1 + d, 1 + y);
2017-09-11 03:06:02 +02:00
print (space); // full block █
}
}
2015-05-23 13:35:12 +02:00
if ( is_Flat && ! button_down )
drawFlatBorder();
if ( ! button_down
&& ! isNewFont()
&& (is_Flat || ! hasShadow() || isMonochron()) )
2015-05-23 13:35:12 +02:00
{
// clear the right █ from button down
if ( parent_widget )
setColor ( parent_widget->getForegroundColor()
, parent_widget->getBackgroundColor() );
2015-05-23 13:35:12 +02:00
for (int y = 1; y <= getHeight(); y++)
2015-05-23 13:35:12 +02:00
{
if ( isMonochron() )
setReverse(true);
setPrintPos (1 + getWidth(), y);
2017-09-11 03:06:02 +02:00
print (' '); // clear right
if ( isMonochron() )
setReverse(false);
2015-05-23 13:35:12 +02:00
}
}
if ( hotkeypos != -1 )
length--;
i = getWidth() - length - 1;
i = int(i / 2);
2015-05-23 13:35:12 +02:00
if ( getHeight() >= 2 )
j = int((getHeight() - 1) / 2);
2015-05-23 13:35:12 +02:00
else
j = 0;
2015-05-23 13:35:12 +02:00
setPrintPos (1 + margin + d, 1 + j);
setColor (button_fg, button_bg);
for (x = 0; x < i; x++)
2017-09-11 03:06:02 +02:00
print (space); // █
if ( hotkeypos == -1 )
2017-09-11 03:06:02 +02:00
setCursorPos (1 + margin + i + mono_1st_char, 1 + j ); // first character
2015-05-23 13:35:12 +02:00
else
2017-09-11 03:06:02 +02:00
setCursorPos (1 + margin + i + hotkeypos, 1 + j ); // hotkey
if ( is_ActiveFocus && (isMonochron() || getMaxColor() < 16) )
setBold();
2015-05-23 13:35:12 +02:00
2017-09-11 03:06:02 +02:00
for (int z = 0; x < i + length && z < getWidth(); z++, x++)
2015-05-23 13:35:12 +02:00
{
if ( (z == hotkeypos) && is_Active )
{
setColor (button_hotkey_fg, button_bg);
if ( ! is_ActiveFocus && getMaxColor() < 16 )
setBold();
2015-05-23 13:35:12 +02:00
if ( ! is_NoUnderline )
setUnderline();
2015-05-23 13:35:12 +02:00
print ( ButtonText[z] );
2015-05-23 13:35:12 +02:00
if ( ! is_ActiveFocus && getMaxColor() < 16 )
unsetBold();
2015-05-23 13:35:12 +02:00
if ( ! is_NoUnderline )
unsetUnderline();
2015-09-20 05:44:50 +02:00
setColor (button_fg, button_bg);
}
else
{
print ( ButtonText[z] );
}
2015-05-23 13:35:12 +02:00
}
if ( is_ActiveFocus && (isMonochron() || getMaxColor() < 16) )
unsetBold();
2015-05-23 13:35:12 +02:00
for (x = i + length; x < getWidth() - 1; x++)
2017-09-11 03:06:02 +02:00
print (space); // █
if ( getHeight() >= 2 )
2015-05-23 13:35:12 +02:00
{
for (i = 0; i < j; i++)
{
setPrintPos (2 + d, 1 + i);
for (int z = 1; z < getWidth(); z++)
2017-09-11 03:06:02 +02:00
print (space); // █
}
for (i = j + 1; i < getHeight(); i++)
{
setPrintPos (2 + d, 1 + i);
for (int z = 1; z < getWidth(); z++)
2017-09-11 03:06:02 +02:00
print (space); // █
}
2015-05-23 13:35:12 +02:00
}
if ( isMonochron() )
setReverse(true);
2015-05-23 13:35:12 +02:00
if ( is_NonFlatShadow && ! button_down )
2015-05-23 13:35:12 +02:00
{
if ( parent_widget )
setColor ( parent_widget->getForegroundColor()
, parent_widget->getBackgroundColor() );
2017-09-11 03:06:02 +02:00
print(' '); // restore background after button down
drawShadow();
}
2015-05-23 13:35:12 +02:00
if ( isMonochron() )
setReverse(false);
2015-05-23 13:35:12 +02:00
delete[] ButtonText;
if ( is_Focus && getStatusBar() )
{
2017-03-26 20:40:04 +02:00
const FString& msg = getStatusbarMessage();
const FString& curMsg = getStatusBar()->getMessage();
2015-05-23 13:35:12 +02:00
if ( curMsg != msg )
{
getStatusBar()->setMessage(msg);
getStatusBar()->drawMessage();
}
}
}
2015-05-23 13:35:12 +02:00
//----------------------------------------------------------------------
void FButton::updateButtonColor()
2015-05-23 13:35:12 +02:00
{
if ( isEnabled() )
{
if ( hasFocus() )
{
button_fg = button_focus_fg;
button_bg = button_focus_bg;
}
else
{
button_fg = getForegroundColor();
2017-09-11 03:06:02 +02:00
button_bg = getBackgroundColor();
}
2015-05-23 13:35:12 +02:00
}
else // inactive
2015-05-23 13:35:12 +02:00
{
button_fg = button_inactive_fg;
button_bg = button_inactive_bg;
2015-05-23 13:35:12 +02:00
}
}
//----------------------------------------------------------------------
void FButton::processClick()
2015-05-23 13:35:12 +02:00
{
emitCallback("clicked");
2015-05-23 13:35:12 +02:00
}