finalcut/src/flistbox.cpp

2072 lines
44 KiB
C++

/***********************************************************************
* flistbox.cpp - Widget FListBox and FListBoxItem *
* *
* This file is part of the Final Cut widget toolkit *
* *
* Copyright 2014-2018 Markus Gans *
* *
* 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/>. *
***********************************************************************/
#include <algorithm>
#include "final/fapplication.h"
#include "final/flistbox.h"
#include "final/fscrollbar.h"
#include "final/fstatusbar.h"
namespace finalcut
{
//----------------------------------------------------------------------
// class FListBoxItem
//----------------------------------------------------------------------
// constructor and destructor
//----------------------------------------------------------------------
FListBoxItem::FListBoxItem()
: text()
, data_pointer(0)
, brackets(fc::NoBrackets)
, selected(false)
{ }
//----------------------------------------------------------------------
FListBoxItem::FListBoxItem (const FListBoxItem& item)
: text(item.text)
, data_pointer(item.data_pointer)
, brackets(item.brackets)
, selected(item.selected)
{ }
//----------------------------------------------------------------------
FListBoxItem::FListBoxItem (const FString& txt, FWidget::data_ptr data)
: text(txt)
, data_pointer(data)
, brackets(fc::NoBrackets)
, selected(false)
{ }
//----------------------------------------------------------------------
FListBoxItem::~FListBoxItem() // destructor
{ }
// public methods of FListBoxItem
//----------------------------------------------------------------------
FListBoxItem& FListBoxItem::operator = (const FListBoxItem& item)
{
if ( &item == this )
{
return *this;
}
else
{
text = item.text;
data_pointer = item.data_pointer;
brackets = item.brackets;
selected = item.selected;
return *this;
}
}
//----------------------------------------------------------------------
// class FListBox
//----------------------------------------------------------------------
// constructor and destructor
//----------------------------------------------------------------------
FListBox::FListBox (FWidget* parent)
: FWidget(parent)
, convertToItem(0)
, itemlist()
, source_container(0)
, conv_type(FListBox::no_convert)
, vbar(0)
, hbar(0)
, text()
, inc_search()
, multi_select(false)
, mouse_select(false)
, drag_scroll(fc::noScroll)
, scroll_timer(false)
, scroll_repeat(100)
, scroll_distance(1)
, current(0)
, last_current(-1)
, secect_from_item(-1)
, xoffset(0)
, yoffset(0)
, last_yoffset(-1)
, nf_offset(0)
, max_line_width(0)
{
init();
}
//----------------------------------------------------------------------
FListBox::~FListBox() // destructor
{
delOwnTimer();
delete vbar;
delete hbar;
}
// public methods of FListBox
//----------------------------------------------------------------------
void FListBox::setCurrentItem (int index)
{
int element_count;
if ( index == current )
return;
element_count = int(getCount());
if ( index > element_count )
current = element_count;
else if ( index < 1 )
current = 1;
else
current = index;
xoffset = 0;
yoffset = 0;
adjustSize();
vbar->setValue(yoffset);
if ( isVisible() )
redraw();
}
//----------------------------------------------------------------------
void FListBox::setCurrentItem (listBoxItems::iterator iter)
{
int index = int(std::distance(itemlist.begin(), iter) + 1);
setCurrentItem(index);
}
//----------------------------------------------------------------------
void FListBox::showInsideBrackets ( int index
, fc::brackets_type b )
{
listBoxItems::iterator iter = index2iterator(index - 1);
iter->brackets = b;
if ( b == fc::NoBrackets )
return;
int len = int(iter->getText().getLength() + 2);
if ( len > max_line_width )
{
max_line_width = len;
if ( len >= getWidth() - nf_offset - 3 )
{
hbar->setMaximum (max_line_width - getWidth() + nf_offset + 4);
hbar->setPageSize (max_line_width, getWidth() - nf_offset - 4);
hbar->setValue (xoffset);
if ( ! hbar->isVisible() )
hbar->setVisible();
}
}
}
//----------------------------------------------------------------------
void FListBox::setGeometry (int x, int y, int w, int h, bool adjust)
{
// Set the widget geometry
FWidget::setGeometry(x, y, w, h, adjust);
if ( isNewFont() )
{
vbar->setGeometry (getWidth(), 2, 2, getHeight() - 2);
hbar->setGeometry (1, getHeight(), getWidth() - 2 - nf_offset, 1);
}
else
{
vbar->setGeometry (getWidth(), 2, 1, getHeight() - 2);
hbar->setGeometry (2, getHeight(), getWidth() - 2, 1);
}
}
//----------------------------------------------------------------------
bool FListBox::setFocus (bool on)
{
FWidget::setFocus(on);
if ( on )
{
if ( getStatusBar() )
{
const FString& msg = getStatusbarMessage();
const FString& curMsg = getStatusBar()->getMessage();
if ( curMsg != msg )
getStatusBar()->setMessage(msg);
}
}
else
{
if ( getStatusBar() )
getStatusBar()->clearMessage();
}
return on;
}
//----------------------------------------------------------------------
void FListBox::setText (const FString& txt)
{
text = txt;
}
//----------------------------------------------------------------------
void FListBox::hide()
{
int n, size;
short fg, bg;
char* blank;
FWidget* parent_widget = getParentWidget();
FWidget::hide();
if ( parent_widget )
{
fg = parent_widget->getForegroundColor();
bg = parent_widget->getBackgroundColor();
}
else
{
fg = wc.dialog_fg;
bg = wc.dialog_bg;
}
setColor (fg, bg);
n = isNewFont() ? 1 : 0;
size = getWidth() + n;
if ( size < 0 )
return;
try
{
blank = new char[uInt(size) + 1];
}
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(); y++)
{
setPrintPos (1, 1 + y);
print (blank);
}
delete[] blank;
}
//----------------------------------------------------------------------
void FListBox::insert (FListBoxItem listItem)
{
int len = int(listItem.text.getLength());
bool has_brackets = bool(listItem.brackets);
recalculateHorizontalBar (len, has_brackets);
itemlist.push_back (listItem);
int element_count = int(getCount());
recalculateVerticalBar (element_count);
}
//----------------------------------------------------------------------
void FListBox::insert ( const FString& item
, fc::brackets_type b
, bool s
, data_ptr d )
{
FListBoxItem listItem (item, d);
listItem.brackets = b;
listItem.selected = s;
insert (listItem);
}
//----------------------------------------------------------------------
void FListBox::insert ( long item
, fc::brackets_type b
, bool s
, data_ptr d )
{
insert (FString() << item, b, s, d);
}
//----------------------------------------------------------------------
void FListBox::remove (int item)
{
int element_count;
if ( int(getCount()) < item )
return;
itemlist.erase (itemlist.begin() + item - 1);
element_count = int(getCount());
max_line_width = 0;
listBoxItems::iterator iter = itemlist.begin();
while ( iter != itemlist.end() )
{
int len = int(iter->getText().getLength());
if ( len > max_line_width )
max_line_width = len;
++iter;
}
hbar->setMaximum (max_line_width - getWidth() + nf_offset + 4);
hbar->setPageSize (max_line_width, getWidth() - nf_offset - 4);
if ( hbar->isVisible() && max_line_width < getWidth() - nf_offset - 3 )
hbar->hide();
vbar->setMaximum (element_count - getHeight() + 2);
vbar->setPageSize (element_count, getHeight() - 2);
if ( vbar->isVisible() && element_count < getHeight() - 1 )
vbar->hide();
if ( current >= item && current > 1 )
current--;
if ( current > element_count )
current = element_count;
if ( yoffset > element_count - getHeight() + 2 )
yoffset = element_count - getHeight() + 2;
if ( yoffset < 0 )
yoffset = 0;
}
//----------------------------------------------------------------------
void FListBox::clear()
{
int size;
char* blank;
itemlist.clear();
current = 0;
xoffset = 0;
yoffset = 0;
max_line_width = 0;
last_current = -1;
last_yoffset = -1;
vbar->setMinimum(0);
vbar->setValue(0);
vbar->hide();
hbar->setMinimum(0);
hbar->setValue(0);
hbar->hide();
// clear list from screen
setColor (wc.list_fg, wc.list_bg);
size = getWidth() - 2;
if ( size < 0 )
return;
try
{
blank = new char[uInt(size) + 1];
}
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() - 2; y++)
{
setPrintPos (2, 2 + y);
print (blank);
}
delete[] blank;
}
//----------------------------------------------------------------------
void FListBox::onKeyPress (FKeyEvent* ev)
{
int current_before = current
, xoffset_before = xoffset
, yoffset_before = yoffset
, key = ev->key();
switch ( key )
{
case fc::Fkey_return:
case fc::Fkey_enter:
keyEnter();
ev->accept();
break;
case fc::Fkey_up:
keyUp();
ev->accept();
break;
case fc::Fkey_down:
keyDown();
ev->accept();
break;
case fc::Fkey_left:
keyLeft();
ev->accept();
break;
case fc::Fkey_right:
keyRight();
ev->accept();
break;
case fc::Fkey_ppage:
keyPgUp();
ev->accept();
break;
case fc::Fkey_npage:
keyPgDn();
ev->accept();
break;
case fc::Fkey_home:
keyHome();
ev->accept();
break;
case fc::Fkey_end:
keyEnd();
ev->accept();
break;
case fc::Fkey_ic: // insert key
if ( keyInsert() )
ev->accept();
break;
case fc::Fkey_space:
if ( keySpace() )
ev->accept();
break;
case fc::Fkey_erase:
case fc::Fkey_backspace:
if ( keyBackspace() )
ev->accept();
break;
case fc::Fkey_escape:
case fc::Fkey_escape_mintty:
if ( keyEsc() )
ev->accept();
break;
default:
if ( keyIncSearchInput(key) )
ev->accept();
}
if ( current_before != current )
{
processChanged();
if ( ! isMultiSelection() )
processSelect();
}
if ( ev->isAccepted() )
{
bool draw_vbar = yoffset_before != yoffset;
bool draw_hbar = xoffset_before != xoffset;
updateDrawing (draw_vbar, draw_hbar);
}
}
//----------------------------------------------------------------------
void FListBox::onMouseDown (FMouseEvent* ev)
{
int yoffset_before
, mouse_x
, mouse_y;
if ( ev->getButton() != fc::LeftButton
&& ev->getButton() != fc::RightButton )
{
return;
}
if ( ev->getButton() == fc::RightButton && ! isMultiSelection() )
return;
getWidgetFocus();
yoffset_before = yoffset;
mouse_x = ev->getX();
mouse_y = ev->getY();
if ( mouse_x > 1 && mouse_x < getWidth()
&& mouse_y > 1 && mouse_y < getHeight() )
{
int element_count = int(getCount());
current = yoffset + mouse_y - 1;
if ( current > element_count )
current = element_count;
inc_search.clear();
if ( ev->getButton() == fc::RightButton )
multiSelection(current);
if ( isVisible() )
drawList();
vbar->setValue (yoffset);
if ( vbar->isVisible() && yoffset_before != yoffset )
vbar->drawBar();
updateTerminal();
flush_out();
}
}
//----------------------------------------------------------------------
void FListBox::onMouseUp (FMouseEvent* ev)
{
if ( drag_scroll != fc::noScroll )
stopDragScroll();
if ( ev->getButton() == fc::LeftButton )
{
int mouse_x = ev->getX();
int mouse_y = ev->getY();
if ( mouse_x > 1 && mouse_x < getWidth()
&& mouse_y > 1 && mouse_y < getHeight() )
{
processChanged();
if ( ! isMultiSelection() )
processSelect();
}
}
}
//----------------------------------------------------------------------
void FListBox::onMouseMove (FMouseEvent* ev)
{
if ( ev->getButton() != fc::LeftButton
&& ev->getButton() != fc::RightButton )
return;
if ( ev->getButton() == fc::RightButton && ! isMultiSelection() )
return;
int current_before = current;
int yoffset_before = yoffset;
int mouse_x = ev->getX();
int mouse_y = ev->getY();
if ( mouse_x > 1 && mouse_x < getWidth()
&& mouse_y > 1 && mouse_y < getHeight() )
{
int element_count = int(getCount());
current = yoffset + mouse_y - 1;
if ( current > element_count )
current = element_count;
inc_search.clear();
// Handle multiple selections
if ( ev->getButton() == fc::RightButton
&& current_before != current )
{
multiSelectionUpTo(current);
}
if ( isVisible() )
drawList();
vbar->setValue (yoffset);
if ( vbar->isVisible() && yoffset_before != yoffset )
vbar->drawBar();
updateTerminal();
flush_out();
}
// Auto-scrolling when dragging mouse outside the widget
if ( mouse_y < 2 )
dragUp (ev->getButton());
else if ( mouse_y >= getHeight() )
dragDown (ev->getButton());
else
stopDragScroll();
}
//----------------------------------------------------------------------
void FListBox::onMouseDoubleClick (FMouseEvent* ev)
{
int mouse_x, mouse_y;
if ( ev->getButton() != fc::LeftButton )
return;
mouse_x = ev->getX();
mouse_y = ev->getY();
if ( mouse_x > 1 && mouse_x < getWidth()
&& mouse_y > 1 && mouse_y < getHeight() )
{
if ( yoffset + mouse_y - 1 > int(getCount()) )
return;
processClick();
}
}
//----------------------------------------------------------------------
void FListBox::onTimer (FTimerEvent*)
{
int current_before = current;
int yoffset_before = yoffset;
switch ( int(drag_scroll) )
{
case fc::noScroll:
return;
case fc::scrollUp:
case fc::scrollUpSelect:
if ( ! dragScrollUp() )
return;
break;
case fc::scrollDown:
case fc::scrollDownSelect:
if ( ! dragScrollDown() )
return;
break;
default:
break;
}
if ( current_before != current )
{
inc_search.clear();
// Handle multiple selections
if ( drag_scroll == fc::scrollUpSelect
|| drag_scroll == fc::scrollDownSelect )
multiSelectionUpTo(current);
}
if ( isVisible() )
drawList();
vbar->setValue (yoffset);
if ( vbar->isVisible() && yoffset_before != yoffset )
vbar->drawBar();
updateTerminal();
flush_out();
}
//----------------------------------------------------------------------
void FListBox::onWheel (FWheelEvent* ev)
{
int wheel
, current_before = current
, yoffset_before = yoffset
, pagesize = 4;
wheel = ev->getWheel();
if ( drag_scroll != fc::noScroll )
stopDragScroll();
switch ( wheel )
{
case fc::WheelUp:
wheelUp (pagesize);
break;
case fc::WheelDown:
wheelDown (pagesize);
break;
default:
break;
}
if ( current_before != current )
{
inc_search.clear();
processChanged();
if ( ! isMultiSelection() )
processSelect();
}
if ( isVisible() )
drawList();
vbar->setValue (yoffset);
if ( vbar->isVisible() && yoffset_before != yoffset )
vbar->drawBar();
updateTerminal();
flush_out();
}
//----------------------------------------------------------------------
void FListBox::onFocusIn (FFocusEvent*)
{
if ( getStatusBar() )
getStatusBar()->drawMessage();
inc_search.clear();
}
//----------------------------------------------------------------------
void FListBox::onFocusOut (FFocusEvent*)
{
if ( getStatusBar() )
{
getStatusBar()->clearMessage();
getStatusBar()->drawMessage();
}
delOwnTimer();
inc_search.clear();
}
// protected methods of FListBox
//----------------------------------------------------------------------
void FListBox::adjustYOffset()
{
int element_count = int(getCount());
if ( getClientHeight() < 0 )
return;
if ( element_count == 0 || getClientHeight() <= 0 )
return;
if ( yoffset > element_count - getClientHeight() )
yoffset = element_count - getClientHeight();
if ( yoffset < 0 )
yoffset = 0;
if ( current < yoffset )
current = yoffset;
if ( yoffset < current - getClientHeight() )
yoffset = current - getClientHeight();
}
//----------------------------------------------------------------------
void FListBox::adjustSize()
{
int element_count;
FWidget::adjustSize();
adjustYOffset();
element_count = int(getCount());
vbar->setMaximum (element_count - getClientHeight());
vbar->setPageSize (element_count, getClientHeight());
vbar->setX (getWidth());
vbar->setHeight (getClientHeight(), false);
vbar->resize();
hbar->setMaximum (max_line_width - getClientWidth() + 2);
hbar->setPageSize (max_line_width, getClientWidth() - 2);
hbar->setY (getHeight());
hbar->setWidth (getClientWidth() + nf_offset, false);
hbar->resize();
if ( element_count <= getClientHeight() )
vbar->hide();
else
vbar->setVisible();
if ( max_line_width < getClientWidth() - 1 )
hbar->hide();
else
hbar->setVisible();
}
// private methods of FListBox
//----------------------------------------------------------------------
inline FString& FListBox::getString (listBoxItems::iterator iter)
{
return iter->getText();
}
//----------------------------------------------------------------------
void FListBox::init()
{
setForegroundColor (wc.dialog_fg);
setBackgroundColor (wc.dialog_bg);
try
{
vbar = new FScrollbar(fc::vertical, this);
vbar->setMinimum(0);
vbar->setValue(0);
vbar->hide();
hbar = new FScrollbar(fc::horizontal, this);
hbar->setMinimum(0);
hbar->setValue(0);
hbar->hide();
}
catch (const std::bad_alloc& ex)
{
std::cerr << "not enough memory to alloc " << ex.what() << std::endl;
return;
}
setGeometry (1, 1, 5, 4, false); // initialize geometry values
vbar->addCallback
(
"change-value",
F_METHOD_CALLBACK (this, &FListBox::cb_VBarChange)
);
hbar->addCallback
(
"change-value",
F_METHOD_CALLBACK (this, &FListBox::cb_HBarChange)
);
nf_offset = isNewFont() ? 1 : 0;
setTopPadding(1);
setLeftPadding(1);
setBottomPadding(1);
setRightPadding(1 + nf_offset);
}
//----------------------------------------------------------------------
void FListBox::draw()
{
bool isFocus;
if ( current < 1 )
current = 1;
setColor();
if ( isMonochron() )
setReverse(true);
if ( isNewFont() )
drawBorder (1, 1, getWidth() - 1, getHeight());
else
drawBorder();
if ( isNewFont() && ! vbar->isVisible() )
{
setColor();
for (int y = 2; y < getHeight(); y++)
{
setPrintPos (getWidth(), y);
print (' '); // clear right side of the scrollbar
}
}
drawLabel();
if ( isMonochron() )
setReverse(false);
if ( vbar->isVisible() )
vbar->redraw();
if ( hbar->isVisible() )
hbar->redraw();
drawList();
isFocus = ((flags & fc::focus) != 0);
if ( isFocus && getStatusBar() )
{
const FString& msg = getStatusbarMessage();
const FString& curMsg = getStatusBar()->getMessage();
if ( curMsg != msg )
{
getStatusBar()->setMessage(msg);
getStatusBar()->drawMessage();
}
}
}
//----------------------------------------------------------------------
void FListBox::drawLabel()
{
FString txt;
uInt length;
if ( text.isNull() || text.isEmpty() )
return;
txt = " " + text + " ";
length = txt.getLength();
setPrintPos (2, 1);
if ( isEnabled() )
setColor(wc.label_emphasis_fg, wc.label_bg);
else
setColor(wc.label_inactive_fg, wc.label_inactive_bg);
if ( length <= uInt(getClientWidth()) )
print (txt);
else
{
// Print ellipsis
print (text.left(uInt(getClientWidth() - 2)));
setColor (wc.label_ellipsis_fg, wc.label_bg);
print("..");
}
}
//----------------------------------------------------------------------
void FListBox::drawList()
{
uInt start, num;
listBoxItems::iterator iter;
if ( itemlist.empty() || getHeight() <= 2 || getWidth() <= 4 )
return;
start = 0;
num = uInt(getHeight() - 2);
if ( num > getCount() )
num = getCount();
if ( last_yoffset >= 0
&& last_yoffset == yoffset
&& last_current != current )
{
// speed up: redraw only the changed rows
uInt last_pos = uInt(current - yoffset) - 1;
uInt current_pos = uInt(last_current - yoffset) - 1;
start = std::min(last_pos, current_pos);
num = std::max(last_pos, current_pos) + 1;
}
iter = index2iterator(int(start) + yoffset);
for (uInt y = start; y < num && iter != itemlist.end() ; y++)
{
bool serach_mark = false;
bool lineHasBrackets = hasBrackets(iter);
// Import data via lazy conversion
lazyConvert (iter, int(y));
// Set screen position and attributes
setLineAttributes ( int(y), isSelected(iter), lineHasBrackets
, serach_mark );
// print the entry
if ( lineHasBrackets )
{
drawListBracketsLine (int(y), iter, serach_mark);
}
else // line has no brackets
{
drawListLine (int(y), iter, serach_mark);
}
++iter;
}
unsetAttributes();
last_yoffset = yoffset;
last_current = current;
}
//----------------------------------------------------------------------
inline void FListBox::drawListLine ( int y
, listBoxItems::iterator iter
, bool serach_mark )
{
uInt i, len;
uInt inc_len = inc_search.getLength();
bool isCurrentLine = bool(y + yoffset + 1 == current);
bool isFocus = ((flags & fc::focus) != 0);
FString element;
element = getString(iter).mid ( uInt(1 + xoffset)
, uInt(getWidth() - nf_offset - 4) );
const wchar_t* const& element_str = element.wc_str();
len = element.getLength();
if ( isMonochron() && isCurrentLine && isFocus )
print (fc::BlackRightPointingPointer); // ►
else
print (' ');
if ( serach_mark )
setColor ( wc.current_inc_search_element_fg
, wc.current_element_focus_bg );
for (i = 0; i < len; i++)
{
if ( serach_mark && i == inc_len && isFocus )
setColor ( wc.current_element_focus_fg
, wc.current_element_focus_bg );
print (element_str[i]);
}
if ( isMonochron() && isCurrentLine && isFocus )
{
print (fc::BlackLeftPointingPointer); // ◄
i++;
}
for (; i < uInt(getWidth() - nf_offset - 3); i++)
print (' ');
}
//----------------------------------------------------------------------
inline void FListBox::printLeftBracket (fc::brackets_type bracket_type)
{
switch ( bracket_type )
{
case fc::NoBrackets:
break;
case fc::SquareBrackets:
print ('[');
break;
case fc::Parenthesis:
print ('(');
break;
case fc::CurlyBrackets:
print ('{');
break;
case fc::AngleBrackets:
print ('<');
break;
}
}
//----------------------------------------------------------------------
inline void FListBox::printRightBracket (fc::brackets_type bracket_type)
{
switch ( bracket_type )
{
case fc::NoBrackets:
break;
case fc::SquareBrackets:
print (']');
break;
case fc::Parenthesis:
print (')');
break;
case fc::CurlyBrackets:
print ('}');
break;
case fc::AngleBrackets:
print ('>');
break;
}
}
//----------------------------------------------------------------------
inline void FListBox::drawListBracketsLine ( int y
, listBoxItems::iterator iter
, bool serach_mark )
{
int full_length;
FString element;
uInt len
, inc_len = inc_search.getLength()
, i = 0
, b = 0;
bool isCurrentLine = bool(y + yoffset + 1 == current);
bool isFocus = ((flags & fc::focus) != 0);
if ( isMonochron() && isCurrentLine && isFocus )
print (fc::BlackRightPointingPointer); // ►
else
print (' ');
if ( xoffset == 0 )
{
b = 1;
printLeftBracket (iter->brackets);
element = getString(iter).mid ( uInt(1 + xoffset)
, uInt(getWidth() - nf_offset - 5) );
}
else
element = getString(iter).mid ( uInt(xoffset)
, uInt(getWidth() - nf_offset - 4) );
const wchar_t* const& element_str = element.wc_str();
len = element.getLength();
for (; i < len; i++)
{
if ( serach_mark && i == 0 )
setColor ( wc.current_inc_search_element_fg
, wc.current_element_focus_bg );
if ( serach_mark && i == inc_len )
setColor ( wc.current_element_focus_fg
, wc.current_element_focus_bg );
print (element_str[i]);
}
full_length = int(getString(iter).getLength());
if ( b + i < uInt(getWidth() - nf_offset - 4 )
&& xoffset <= full_length + 1 )
{
if ( serach_mark && i == inc_len )
setColor ( wc.current_element_focus_fg
, wc.current_element_focus_bg );
printRightBracket (iter->brackets);
i++;
}
if ( isMonochron() && isCurrentLine && isFocus )
{
print (fc::BlackLeftPointingPointer); // ◄
i++;
}
for (; b + i < uInt(getWidth() - nf_offset - 3); i++)
print (' ');
}
//----------------------------------------------------------------------
inline void FListBox::setLineAttributes ( int y
, bool isLineSelected
, bool lineHasBrackets
, bool& serach_mark )
{
bool isFocus = ((flags & fc::focus) != 0)
, isCurrentLine = bool(y + yoffset + 1 == current);
uInt inc_len = inc_search.getLength();
setPrintPos (2, 2 + int(y));
if ( isLineSelected )
{
if ( isMonochron() )
setBold();
else
setColor (wc.selected_list_fg, wc.selected_list_bg);
}
else
{
if ( isMonochron() )
unsetBold();
else
setColor (wc.list_fg, wc.list_bg);
}
if ( isCurrentLine )
{
if ( isFocus && getMaxColor() < 16 )
setBold();
if ( isLineSelected )
{
if ( isMonochron() )
setBold();
else if ( isFocus )
setColor ( wc.selected_current_element_focus_fg
, wc.selected_current_element_focus_bg );
else
setColor ( wc.selected_current_element_fg
, wc.selected_current_element_bg );
setCursorPos (3, 2 + int(y)); // first character
}
else
{
if ( isMonochron() )
unsetBold();
if ( isFocus )
{
setColor ( wc.current_element_focus_fg
, wc.current_element_focus_bg );
int b = ( lineHasBrackets ) ? 1: 0;
if ( inc_len > 0 ) // incremental search
{
serach_mark = true;
// Place the cursor on the last found character
setCursorPos (2 + b + int(inc_len), 2 + int(y));
}
else // only highlighted
setCursorPos (3 + b, 2 + int(y)); // first character
}
else
setColor ( wc.current_element_fg
, wc.current_element_bg );
}
if ( isMonochron() )
setReverse(false);
}
else
{
if ( isMonochron() )
setReverse(true);
else if ( isFocus && getMaxColor() < 16 )
unsetBold();
}
}
//----------------------------------------------------------------------
inline void FListBox::unsetAttributes()
{
if ( isMonochron() ) // unset for the last element
setReverse(false);
unsetBold();
}
//----------------------------------------------------------------------
inline void FListBox::updateDrawing (bool draw_vbar, bool draw_hbar)
{
if ( isVisible() )
drawList();
vbar->setValue (yoffset);
if ( vbar->isVisible() && draw_vbar )
vbar->drawBar();
hbar->setValue (xoffset);
if ( hbar->isVisible() && draw_hbar )
hbar->drawBar();
updateTerminal();
flush_out();
}
//----------------------------------------------------------------------
void FListBox::recalculateHorizontalBar (int len, bool has_brackets)
{
if ( has_brackets )
len += 2;
if ( len <= max_line_width )
return;
max_line_width = len;
if ( len >= getWidth() - nf_offset - 3 )
{
hbar->setMaximum (max_line_width - getWidth() + nf_offset + 4);
hbar->setPageSize (max_line_width, getWidth() - nf_offset - 4);
hbar->calculateSliderValues();
if ( ! hbar->isVisible() )
hbar->setVisible();
}
}
//----------------------------------------------------------------------
void FListBox::recalculateVerticalBar (int element_count)
{
vbar->setMaximum (element_count - getHeight() + 2);
vbar->setPageSize (element_count, getHeight() - 2);
vbar->calculateSliderValues();
if ( ! vbar->isVisible() && element_count >= getHeight() - 1 )
vbar->setVisible();
}
//----------------------------------------------------------------------
inline void FListBox::getWidgetFocus()
{
if ( hasFocus() )
return;
FWidget* focused_widget = getFocusWidget();
FFocusEvent out (fc::FocusOut_Event);
FApplication::queueEvent(focused_widget, &out);
setFocus();
if ( focused_widget )
focused_widget->redraw();
if ( getStatusBar() )
getStatusBar()->drawMessage();
}
//----------------------------------------------------------------------
void FListBox::multiSelection (int pos)
{
if ( ! isMultiSelection() )
return;
if ( isSelected(pos) )
{
mouse_select = false;
unselectItem(pos);
}
else
{
mouse_select = true;
selectItem(pos);
}
processSelect();
secect_from_item = pos;
}
//----------------------------------------------------------------------
void FListBox::multiSelectionUpTo (int pos)
{
int from, to;
if ( ! isMultiSelection() )
return;
if ( secect_from_item > pos )
{
from = pos;
to = secect_from_item - 1;
}
else
{
from = secect_from_item + 1;
to = pos;
}
for (int i = from; i <= to; i++)
{
if ( mouse_select )
{
selectItem(i);
processSelect();
}
else
{
unselectItem(i);
processSelect();
}
}
secect_from_item = pos;
}
//----------------------------------------------------------------------
void FListBox::wheelUp (int pagesize)
{
if ( yoffset == 0 )
return;
yoffset -= pagesize;
if ( yoffset < 0 )
{
current -= pagesize + yoffset;
yoffset = 0;
}
else
current -= pagesize;
if ( current < 1 )
current = 1;
}
//----------------------------------------------------------------------
void FListBox::wheelDown (int pagesize)
{
int element_count = int(getCount());
int yoffset_end = element_count - getClientHeight();
if ( yoffset_end < 0 )
yoffset_end = 0;
if ( yoffset == yoffset_end )
return;
yoffset += pagesize;
if ( yoffset > yoffset_end )
{
current += pagesize - (yoffset - yoffset_end);
yoffset = yoffset_end;
}
else
current += pagesize;
if ( current > element_count )
current = element_count;
}
//----------------------------------------------------------------------
bool FListBox::dragScrollUp()
{
if ( current == 1 )
{
drag_scroll = fc::noScroll;
return false;
}
prevListItem (scroll_distance);
return true;
}
//----------------------------------------------------------------------
bool FListBox::dragScrollDown()
{
int element_count = int(getCount());
if ( current == element_count )
{
drag_scroll = fc::noScroll;
return false;
}
nextListItem (scroll_distance);
return true;
}
//----------------------------------------------------------------------
void FListBox::dragUp (int mouse_button)
{
if ( drag_scroll != fc::noScroll
&& scroll_distance < getClientHeight() )
scroll_distance++;
if ( ! scroll_timer && current > 1 )
{
scroll_timer = true;
addTimer(scroll_repeat);
if ( mouse_button == fc::RightButton )
drag_scroll = fc::scrollUpSelect;
else
drag_scroll = fc::scrollUp;
}
if ( current == 1 )
{
delOwnTimer();
drag_scroll = fc::noScroll;
}
}
//----------------------------------------------------------------------
void FListBox::dragDown (int mouse_button)
{
if ( drag_scroll != fc::noScroll
&& scroll_distance < getClientHeight() )
scroll_distance++;
if ( ! scroll_timer && current < int(getCount()) )
{
scroll_timer = true;
addTimer(scroll_repeat);
if ( mouse_button == fc::RightButton )
drag_scroll = fc::scrollDownSelect;
else
drag_scroll = fc::scrollDown;
}
if ( current == int(getCount()) )
{
delOwnTimer();
drag_scroll = fc::noScroll;
}
}
//----------------------------------------------------------------------
void FListBox::stopDragScroll()
{
delOwnTimer();
drag_scroll = fc::noScroll;
scroll_distance = 1;
scroll_timer = false;
}
//----------------------------------------------------------------------
void FListBox::prevListItem (int distance)
{
if ( current == 1 )
return;
current -= distance;
if ( current < 1 )
current = 1;
if ( current <= yoffset )
{
yoffset -= distance;
if ( yoffset < 0 )
yoffset = 0;
}
}
//----------------------------------------------------------------------
void FListBox::nextListItem (int distance)
{
int element_count = int(getCount());
int yoffset_end = element_count - getClientHeight();
if ( current == element_count )
return;
current += distance;
if ( current > element_count )
current = element_count;
if ( current - yoffset > getClientHeight() )
{
yoffset += distance;
if ( yoffset > yoffset_end )
yoffset = yoffset_end;
}
}
//----------------------------------------------------------------------
void FListBox::scrollToX (int val)
{
static const int padding_space = 2; // 1 leading space + 1 trailing space
int xoffset_end = max_line_width - getClientWidth() + padding_space;
if ( xoffset == val )
return;
xoffset = val;
if ( xoffset > xoffset_end )
xoffset = xoffset_end;
if ( xoffset < 0 )
xoffset = 0;
}
//----------------------------------------------------------------------
void FListBox::scrollToY (int val)
{
int element_count = int(getCount());
int yoffset_end = element_count - getClientHeight();
if ( yoffset == val )
return;
int c = current - yoffset;
yoffset = val;
if ( yoffset > yoffset_end )
yoffset = yoffset_end;
if ( yoffset < 0 )
yoffset = 0;
current = yoffset + c;
if ( current < yoffset )
current = yoffset;
if ( current > element_count )
current = element_count;
}
//----------------------------------------------------------------------
void FListBox::scrollLeft (int distance)
{
if ( xoffset == 0 )
return;
xoffset -= distance;
if ( xoffset < 0 )
xoffset = 0;
}
//----------------------------------------------------------------------
void FListBox::scrollRight (int distance)
{
static const int padding_space = 2; // 1 leading space + 1 trailing space
int xoffset_end = max_line_width - getClientWidth() + padding_space;
xoffset += distance;
if ( xoffset == xoffset_end )
return;
if ( xoffset > xoffset_end )
xoffset = xoffset_end;
if ( xoffset < 0 )
xoffset = 0;
}
//----------------------------------------------------------------------
inline void FListBox::keyUp()
{
prevListItem (1);
inc_search.clear();
}
//----------------------------------------------------------------------
inline void FListBox::keyDown()
{
nextListItem (1);
inc_search.clear();
}
//----------------------------------------------------------------------
inline void FListBox::keyLeft()
{
scrollLeft(1);
inc_search.clear();
}
//----------------------------------------------------------------------
inline void FListBox::keyRight()
{
scrollRight(1);
inc_search.clear();
}
//----------------------------------------------------------------------
inline void FListBox::keyPgUp()
{
int pagesize = getClientHeight() - 1;
prevListItem (pagesize);
inc_search.clear();
}
//----------------------------------------------------------------------
inline void FListBox::keyPgDn()
{
int pagesize = getClientHeight() - 1;
nextListItem (pagesize);
inc_search.clear();
}
//----------------------------------------------------------------------
inline void FListBox::keyHome()
{
current = 1;
yoffset = 0;
inc_search.clear();
}
//----------------------------------------------------------------------
inline void FListBox::keyEnd()
{
int element_count = int(getCount());
int yoffset_end = element_count - getClientHeight();
current = element_count;
if ( current > getClientHeight() )
yoffset = yoffset_end;
inc_search.clear();
}
//----------------------------------------------------------------------
inline bool FListBox::keyEsc()
{
if ( inc_search.getLength() > 0 )
{
inc_search.clear();
return true;
}
return false;
}
//----------------------------------------------------------------------
inline void FListBox::keyEnter()
{
processClick();
inc_search.clear();
}
//----------------------------------------------------------------------
inline bool FListBox::keySpace()
{
uInt inc_len = inc_search.getLength();
if ( inc_len > 0 )
{
inc_search += L' ';
bool inc_found = false;
listBoxItems::iterator iter = itemlist.begin();
while ( iter != itemlist.end() )
{
if ( ! inc_found
&& inc_search.toLower()
== iter->getText().left(inc_len + 1).toLower() )
{
setCurrentItem(iter);
inc_found = true;
break;
}
++iter;
}
if ( ! inc_found )
{
inc_search.remove(inc_len, 1);
return false;
}
}
else if ( isMultiSelection() )
{
if ( isSelected(current) )
unselectItem(current);
else
selectItem(current);
processSelect();
inc_search.clear();
}
return true;
}
//----------------------------------------------------------------------
inline bool FListBox::keyInsert()
{
if ( isMultiSelection() )
{
int element_count = int(getCount());
if ( isSelected(current) )
unselectItem(current);
else
selectItem(current);
processSelect();
current++;
if ( current > element_count )
current = element_count;
if ( current - yoffset >= getHeight() - 1 )
yoffset++;
return true;
}
inc_search.clear();
return false;
}
//----------------------------------------------------------------------
inline bool FListBox::keyBackspace()
{
uInt inc_len = inc_search.getLength();
if ( inc_len > 0 )
{
inc_search.remove(inc_len - 1, 1);
if ( inc_len > 1 )
{
listBoxItems::iterator iter = itemlist.begin();
while ( iter != itemlist.end() )
{
if ( inc_search.toLower()
== iter->getText().left(inc_len - 1).toLower() )
{
setCurrentItem(iter);
break;
}
++iter;
}
}
return true;
}
return false;
}
//----------------------------------------------------------------------
inline bool FListBox::keyIncSearchInput (int key)
{
if ( key <= 0x20 || key > 0x10fff )
return false;
// incremental search
if ( inc_search.getLength() == 0 )
inc_search = wchar_t(key);
else
inc_search += wchar_t(key);
uInt inc_len = inc_search.getLength();
bool inc_found = false;
listBoxItems::iterator iter = itemlist.begin();
while ( iter != itemlist.end() )
{
if ( ! inc_found
&& inc_search.toLower()
== iter->getText().left(inc_len).toLower() )
{
setCurrentItem(iter);
inc_found = true;
break;
}
++iter;
}
if ( ! inc_found )
{
inc_search.remove(inc_len - 1, 1);
if ( inc_len == 1 )
return false;
else
return true;
}
return true;
}
//----------------------------------------------------------------------
void FListBox::processClick()
{
emitCallback("clicked");
}
//----------------------------------------------------------------------
void FListBox::processSelect()
{
emitCallback("row-selected");
}
//----------------------------------------------------------------------
void FListBox::processChanged()
{
emitCallback("row-changed");
}
//----------------------------------------------------------------------
void FListBox::lazyConvert(listBoxItems::iterator iter, int y)
{
if ( conv_type != lazy_convert || ! iter->getText().isNull() )
return;
convertToItem (*iter, source_container, y + yoffset);
int len = int(iter->text.getLength());
recalculateHorizontalBar (len, hasBrackets(iter));
if ( hbar->isVisible() )
hbar->redraw();
}
//----------------------------------------------------------------------
void FListBox::cb_VBarChange (FWidget*, data_ptr)
{
FScrollbar::sType scrollType;
int distance = 1
, pagesize = 4
, current_before = current
, yoffset_before = yoffset;
scrollType = vbar->getScrollType();
switch ( scrollType )
{
case FScrollbar::noScroll:
break;
case FScrollbar::scrollPageBackward:
distance = getClientHeight();
// fall through
case FScrollbar::scrollStepBackward:
prevListItem (distance);
break;
case FScrollbar::scrollPageForward:
distance = getClientHeight();
// fall through
case FScrollbar::scrollStepForward:
nextListItem (distance);
break;
case FScrollbar::scrollJump:
scrollToY (vbar->getValue());
break;
case FScrollbar::scrollWheelUp:
wheelUp (pagesize);
break;
case FScrollbar::scrollWheelDown:
wheelDown (pagesize);
break;
}
if ( current_before != current )
{
inc_search.clear();
processChanged();
if ( ! isMultiSelection() )
processSelect();
}
if ( isVisible() )
drawList();
if ( scrollType >= FScrollbar::scrollStepBackward
&& scrollType <= FScrollbar::scrollWheelDown )
{
vbar->setValue (yoffset);
if ( vbar->isVisible() && yoffset_before != yoffset )
vbar->drawBar();
updateTerminal();
flush_out();
}
}
//----------------------------------------------------------------------
void FListBox::cb_HBarChange (FWidget*, data_ptr)
{
static const int padding_space = 2; // 1 leading space + 1 trailing space
FScrollbar::sType scrollType;
int distance = 1
, pagesize = 4
, xoffset_before = xoffset;
scrollType = hbar->getScrollType();
switch ( scrollType )
{
case FScrollbar::noScroll:
break;
case FScrollbar::scrollPageBackward:
distance = getClientWidth() - padding_space;
// fall through
case FScrollbar::scrollStepBackward:
scrollLeft (distance);
break;
case FScrollbar::scrollPageForward:
distance = getClientWidth() - padding_space;
// fall through
case FScrollbar::scrollStepForward:
scrollRight (distance);
break;
case FScrollbar::scrollJump:
scrollToX (hbar->getValue());
break;
case FScrollbar::scrollWheelUp:
scrollLeft (pagesize);
break;
case FScrollbar::scrollWheelDown:
scrollRight (pagesize);
break;
}
if ( xoffset_before != xoffset )
inc_search.clear();
if ( isVisible() )
{
drawList();
updateTerminal();
flush_out();
}
if ( scrollType >= FScrollbar::scrollStepBackward
&& scrollType <= FScrollbar::scrollWheelDown )
{
hbar->setValue (xoffset);
if ( hbar->isVisible() && xoffset_before != xoffset )
hbar->drawBar();
updateTerminal();
flush_out();
}
}
} // namespace finalcut