mirror of https://github.com/KLayout/klayout.git
1192 lines
31 KiB
C++
1192 lines
31 KiB
C++
|
|
/*
|
|
|
|
KLayout Layout Viewer
|
|
Copyright (C) 2006-2017 Matthias Koefferlein
|
|
|
|
This program 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 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program 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, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
*/
|
|
|
|
|
|
#include <QGridLayout>
|
|
#include <QMenu>
|
|
#include <QColorDialog>
|
|
#include <QPainter>
|
|
#include <QPixmap>
|
|
#include <QMouseEvent>
|
|
|
|
#include "layWidgets.h"
|
|
|
|
#include "layLayoutView.h"
|
|
#include "layDialogs.h"
|
|
#include "tlExceptions.h"
|
|
#include "layStipplePalette.h"
|
|
#include "layColorPalette.h"
|
|
#include "laybasicConfig.h"
|
|
#include "layPlugin.h"
|
|
|
|
#include "dbLayout.h"
|
|
#include "dbLibrary.h"
|
|
#include "dbLibraryManager.h"
|
|
|
|
#include "tlInternational.h"
|
|
|
|
#include "laySelectStippleForm.h"
|
|
|
|
#include <vector>
|
|
|
|
namespace lay
|
|
{
|
|
|
|
// -------------------------------------------------------------
|
|
// DitherPatternSelectionButton implementation
|
|
|
|
DitherPatternSelectionButton::DitherPatternSelectionButton (QWidget *parent)
|
|
: QPushButton (parent), mp_view (0), m_dither_pattern (-1)
|
|
{
|
|
setMenu (new QMenu (this));
|
|
update_pattern ();
|
|
connect (menu (), SIGNAL (aboutToShow ()), this, SLOT (menu_about_to_show ()));
|
|
}
|
|
|
|
DitherPatternSelectionButton::~DitherPatternSelectionButton ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
DitherPatternSelectionButton::set_view (lay::LayoutView *view)
|
|
{
|
|
if (view != mp_view) {
|
|
mp_view = view;
|
|
update_menu ();
|
|
}
|
|
}
|
|
|
|
void
|
|
DitherPatternSelectionButton::set_dither_pattern (int dp)
|
|
{
|
|
if (dp != m_dither_pattern) {
|
|
m_dither_pattern = dp;
|
|
update_pattern ();
|
|
}
|
|
}
|
|
|
|
int
|
|
DitherPatternSelectionButton::dither_pattern () const
|
|
{
|
|
return m_dither_pattern;
|
|
}
|
|
|
|
void
|
|
DitherPatternSelectionButton::menu_selected ()
|
|
{
|
|
QAction *action = dynamic_cast <QAction *> (sender ());
|
|
if (action) {
|
|
|
|
m_dither_pattern = action->data ().toInt ();
|
|
update_pattern ();
|
|
emit (dither_pattern_changed (m_dither_pattern));
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
DitherPatternSelectionButton::browse_selected ()
|
|
{
|
|
if (mp_view) {
|
|
|
|
SelectStippleForm stipples_form (0, mp_view->dither_pattern (), true);
|
|
stipples_form.set_selected (m_dither_pattern);
|
|
|
|
if (stipples_form.exec ()) {
|
|
|
|
m_dither_pattern = stipples_form.selected ();
|
|
update_pattern ();
|
|
emit (dither_pattern_changed (m_dither_pattern));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Use the default (non-custom) pattern if no view is set.
|
|
lay::DitherPattern default_pattern;
|
|
|
|
SelectStippleForm stipples_form (0, default_pattern, true);
|
|
stipples_form.set_selected (m_dither_pattern);
|
|
|
|
if (stipples_form.exec ()) {
|
|
|
|
m_dither_pattern = stipples_form.selected ();
|
|
update_pattern ();
|
|
emit (dither_pattern_changed (m_dither_pattern));
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
DitherPatternSelectionButton::update_pattern ()
|
|
{
|
|
QPushButton::setText (QString::fromUtf8 (" "));
|
|
|
|
QString text = QString::fromUtf8 ("XXXXXXX");
|
|
QFontMetrics fm (font (), this);
|
|
QRect rt (fm.boundingRect (text)); // dummy text to be compliant with the other color button
|
|
|
|
QPushButton::setIconSize (QSize (rt.width (), rt.height ()));
|
|
|
|
if (m_dither_pattern < 0) {
|
|
|
|
QPixmap pixmap (rt.width (), rt.height ());
|
|
pixmap.fill (QColor (0, 0, 0, 0));
|
|
|
|
QPainter pxpainter (&pixmap);
|
|
pxpainter.setFont (font ());
|
|
QColor text_color = palette ().color (QPalette::Active, QPalette::Text);
|
|
pxpainter.setPen (QPen (text_color));
|
|
|
|
QRect r (0, 0, pixmap.width () - 1, pixmap.height () - 1);
|
|
pxpainter.drawText (r, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, QObject::tr ("None"));
|
|
|
|
QPushButton::setIcon (QIcon (pixmap));
|
|
|
|
} else {
|
|
|
|
if (mp_view) {
|
|
QPushButton::setIcon (QIcon (mp_view->dither_pattern ().get_bitmap ((unsigned int) m_dither_pattern, rt.width (), rt.height ())));
|
|
} else {
|
|
lay::DitherPattern default_pattern;
|
|
QPushButton::setIcon (QIcon (default_pattern.get_bitmap ((unsigned int) m_dither_pattern, rt.width (), rt.height ())));
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void
|
|
DitherPatternSelectionButton::menu_about_to_show ()
|
|
{
|
|
update_menu ();
|
|
}
|
|
|
|
void
|
|
DitherPatternSelectionButton::update_menu ()
|
|
{
|
|
menu ()->clear ();
|
|
menu ()->addAction (QObject::tr ("None"), this, SLOT (menu_selected ()))->setData (-1);
|
|
menu ()->addAction (QObject::tr ("Choose ..."), this, SLOT (browse_selected ()));
|
|
menu ()->addSeparator ();
|
|
|
|
// from_string might throw an exception ...
|
|
try {
|
|
|
|
lay::DitherPattern patterns;
|
|
|
|
std::string s;
|
|
lay::PluginRoot::instance ()->config_get (cfg_stipple_palette, s);
|
|
lay::StipplePalette palette;
|
|
palette.from_string (s);
|
|
|
|
// fill the list of stipple palette items
|
|
for (unsigned int i = 0; i < palette.stipples (); ++i) {
|
|
|
|
unsigned int n = palette.stipple_by_index (i);
|
|
if (int (n) < std::distance (patterns.begin (), patterns.end ())) {
|
|
|
|
const lay::DitherPatternInfo &info = patterns.begin () [n];
|
|
|
|
std::string name (info.name ());
|
|
if (name.empty ()) {
|
|
name = tl::sprintf ("#%d", n);
|
|
}
|
|
|
|
menu ()->addAction (QIcon (info.get_bitmap ()), tl::to_qstring (name), this, SLOT (menu_selected ()))->setData (n);
|
|
|
|
}
|
|
}
|
|
|
|
} catch (...) { }
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// CellViewSelectionComboBox implementation
|
|
|
|
struct CellViewSelectionComboBoxPrivateData
|
|
{
|
|
const lay::LayoutView *layout_view;
|
|
};
|
|
|
|
CellViewSelectionComboBox::CellViewSelectionComboBox (QWidget * /*parent*/)
|
|
{
|
|
mp_private = new CellViewSelectionComboBoxPrivateData ();
|
|
mp_private->layout_view = 0;
|
|
}
|
|
|
|
CellViewSelectionComboBox::~CellViewSelectionComboBox ()
|
|
{
|
|
delete mp_private;
|
|
mp_private = 0;
|
|
}
|
|
|
|
const lay::LayoutView *
|
|
CellViewSelectionComboBox::layout_view () const
|
|
{
|
|
return mp_private->layout_view;
|
|
}
|
|
|
|
void
|
|
CellViewSelectionComboBox::set_layout_view (const lay::LayoutView *layout_view)
|
|
{
|
|
// TODO: should register a listener, so it does the update automatically.
|
|
mp_private->layout_view = layout_view;
|
|
|
|
int current = current_cv_index ();
|
|
|
|
clear ();
|
|
for (unsigned int cv = 0; cv < layout_view->cellviews (); ++cv) {
|
|
if (layout_view->cellview (cv).is_valid ()) {
|
|
addItem (tl::to_qstring (layout_view->cellview (cv)->name () + ", " + tl::to_string (QObject::tr ("Cell")) + " '"
|
|
+ layout_view->cellview (cv)->layout ().cell_name (layout_view->cellview (cv).cell_index ()) + "'"));
|
|
} else {
|
|
addItem (tl::to_qstring (layout_view->cellview (cv)->name () + ", " + tl::to_string (QObject::tr ("Undefined cell"))));
|
|
}
|
|
}
|
|
|
|
if (current < 0 || current >= int (layout_view->cellviews ())) {
|
|
set_current_cv_index (layout_view->cellviews () > 0 ? 0 : -1);
|
|
} else {
|
|
set_current_cv_index (current);
|
|
}
|
|
}
|
|
|
|
void
|
|
CellViewSelectionComboBox::set_current_cv_index (int cv)
|
|
{
|
|
setCurrentIndex (cv);
|
|
}
|
|
|
|
int
|
|
CellViewSelectionComboBox::current_cv_index () const
|
|
{
|
|
return currentIndex ();
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// LayerSelectionComboBox implementation
|
|
|
|
struct LayerSelectionComboBoxPrivateData
|
|
{
|
|
std::vector <std::pair <db::LayerProperties, int> > layers;
|
|
bool no_layer_available;
|
|
bool new_layer_enabled;
|
|
bool all_layers;
|
|
const db::Layout *layout;
|
|
lay::LayoutView *view;
|
|
int cv_index;
|
|
};
|
|
|
|
LayerSelectionComboBox::LayerSelectionComboBox (QWidget * /*parent*/)
|
|
{
|
|
mp_private = new LayerSelectionComboBoxPrivateData ();
|
|
mp_private->no_layer_available = false;
|
|
mp_private->new_layer_enabled = true;
|
|
mp_private->layout = 0;
|
|
mp_private->view = 0;
|
|
mp_private->cv_index = -1;
|
|
mp_private->all_layers = false;
|
|
|
|
connect (this, SIGNAL (activated (int)), this, SLOT (item_selected (int)));
|
|
}
|
|
|
|
LayerSelectionComboBox::~LayerSelectionComboBox ()
|
|
{
|
|
delete mp_private;
|
|
mp_private = 0;
|
|
}
|
|
|
|
void
|
|
LayerSelectionComboBox::set_new_layer_enabled (bool f)
|
|
{
|
|
if (mp_private->new_layer_enabled != f) {
|
|
mp_private->new_layer_enabled = f;
|
|
update_layer_list ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayerSelectionComboBox::is_new_layer_enabled () const
|
|
{
|
|
return mp_private->new_layer_enabled;
|
|
}
|
|
|
|
void
|
|
LayerSelectionComboBox::set_no_layer_available (bool f)
|
|
{
|
|
if (mp_private->no_layer_available != f) {
|
|
mp_private->no_layer_available = f;
|
|
update_layer_list ();
|
|
}
|
|
}
|
|
|
|
bool
|
|
LayerSelectionComboBox::is_no_layer_available () const
|
|
{
|
|
return mp_private->no_layer_available;
|
|
}
|
|
|
|
void
|
|
LayerSelectionComboBox::item_selected (int index)
|
|
{
|
|
BEGIN_PROTECTED
|
|
|
|
if (mp_private->view != 0 && index == count () - 1 && mp_private->new_layer_enabled) {
|
|
|
|
setCurrentIndex (-1);
|
|
|
|
const lay::CellView &cv = mp_private->view->cellview (mp_private->cv_index);
|
|
db::LayerProperties lp;
|
|
|
|
if (! mp_private->view->current_layer ().is_null ()) {
|
|
int li = mp_private->view->current_layer ()->layer_index ();
|
|
if (li >= 0) {
|
|
lp = mp_private->view->cellview (mp_private->view->current_layer ()->cellview_index ())->layout ().get_properties (li);
|
|
}
|
|
}
|
|
|
|
lay::NewLayerPropertiesDialog prop_dia (this);
|
|
if (prop_dia.exec_dialog (cv, lp)) {
|
|
|
|
for (unsigned int l = 0; l < cv->layout ().layers (); ++l) {
|
|
if (cv->layout ().is_valid_layer (l) && cv->layout ().get_properties (l).log_equal (lp)) {
|
|
throw tl::Exception (tl::to_string (QObject::tr ("A layer with that signature already exists: ")) + lp.to_string ());
|
|
}
|
|
}
|
|
|
|
mp_private->view->manager ()->transaction (tl::to_string (QObject::tr ("New layer")));
|
|
|
|
unsigned int l = cv->layout ().insert_layer (lp);
|
|
std::vector <unsigned int> nl;
|
|
nl.push_back (l);
|
|
mp_private->view->add_new_layers (nl, mp_private->cv_index);
|
|
mp_private->view->update_content ();
|
|
|
|
mp_private->view->manager ()->commit ();
|
|
|
|
insertItem (index, tl::to_qstring (lp.to_string ()));
|
|
setCurrentIndex (index);
|
|
|
|
mp_private->layers.push_back (std::make_pair (lp, int (l)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
END_PROTECTED;
|
|
}
|
|
|
|
struct LPIPairCompareOp
|
|
{
|
|
bool operator() (const std::pair <db::LayerProperties, int> &a, const std::pair <db::LayerProperties, int> &b) const
|
|
{
|
|
if (! a.first.log_equal (b.first)) {
|
|
return a.first.log_less (b.first);
|
|
}
|
|
return a.second < b.second;
|
|
}
|
|
};
|
|
|
|
void
|
|
LayerSelectionComboBox::set_view (lay::LayoutView *view, int cv_index, bool all_layers)
|
|
{
|
|
if (view == 0 || cv_index < 0) {
|
|
set_layout (0);
|
|
return;
|
|
}
|
|
|
|
mp_private->layout = &view->cellview (cv_index)->layout ();
|
|
mp_private->view = view;
|
|
mp_private->cv_index = cv_index;
|
|
mp_private->all_layers = all_layers;
|
|
|
|
update_layer_list ();
|
|
}
|
|
|
|
void
|
|
LayerSelectionComboBox::set_layout (const db::Layout *layout)
|
|
{
|
|
mp_private->layout = layout;
|
|
mp_private->view = 0;
|
|
mp_private->cv_index = -1;
|
|
mp_private->all_layers = false;
|
|
|
|
update_layer_list ();
|
|
}
|
|
|
|
void
|
|
LayerSelectionComboBox::update_layer_list ()
|
|
{
|
|
int i = currentIndex ();
|
|
db::LayerProperties props;
|
|
if (i >= 0 && i < int (mp_private->layers.size ())) {
|
|
props = mp_private->layers [i].first;
|
|
}
|
|
|
|
mp_private->layers.clear ();
|
|
if (mp_private->no_layer_available) {
|
|
mp_private->layers.push_back (std::make_pair (db::LayerProperties (), -1));
|
|
}
|
|
|
|
clear ();
|
|
|
|
if (mp_private->view) {
|
|
|
|
LPIPairCompareOp cmp_op;
|
|
std::map<std::pair <db::LayerProperties, int>, std::string, LPIPairCompareOp> name_for_layer (cmp_op);
|
|
LayerPropertiesConstIterator lp = mp_private->view->begin_layers ();
|
|
while (! lp.at_end ()) {
|
|
if (lp->cellview_index () == mp_private->cv_index && ! lp->has_children () && (mp_private->all_layers || lp->layer_index () >= 0) && lp->source (true).layer_props () != db::LayerProperties ()) {
|
|
std::pair <db::LayerProperties, int> k (lp->source (true).layer_props (), lp->layer_index ());
|
|
name_for_layer.insert (std::make_pair (k, lp->display_string (mp_private->view, true, true /*always show source*/)));
|
|
mp_private->layers.push_back (k);
|
|
}
|
|
++lp;
|
|
}
|
|
|
|
size_t nk = mp_private->layers.size ();
|
|
|
|
for (unsigned int l = 0; l < mp_private->layout->layers (); ++l) {
|
|
if (mp_private->layout->is_valid_layer (l)) {
|
|
std::pair <db::LayerProperties, int> k (mp_private->layout->get_properties (l), int (l));
|
|
if (name_for_layer.find (k) == name_for_layer.end ()) {
|
|
mp_private->layers.push_back (k);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort (mp_private->layers.begin () + nk, mp_private->layers.end ());
|
|
|
|
for (std::vector <std::pair <db::LayerProperties, int> >::iterator ll = mp_private->layers.begin (); ll != mp_private->layers.end (); ++ll) {
|
|
std::map<std::pair <db::LayerProperties, int>, std::string, LPIPairCompareOp>::const_iterator ln = name_for_layer.find (*ll);
|
|
if (ln != name_for_layer.end ()) {
|
|
addItem (tl::to_qstring (ln->second));
|
|
} else {
|
|
addItem (tl::to_qstring (ll->first.to_string ()));
|
|
}
|
|
}
|
|
|
|
if (mp_private->new_layer_enabled) {
|
|
addItem (QObject::tr ("New Layer .."));
|
|
}
|
|
|
|
set_current_layer (props);
|
|
|
|
} else if (mp_private->layout) {
|
|
|
|
size_t n = mp_private->layers.size ();
|
|
|
|
for (unsigned int l = 0; l < mp_private->layout->layers (); ++l) {
|
|
if (mp_private->layout->is_valid_layer (l)) {
|
|
mp_private->layers.push_back (std::make_pair (mp_private->layout->get_properties (l), int (l)));
|
|
}
|
|
}
|
|
|
|
std::sort (mp_private->layers.begin () + n, mp_private->layers.end ());
|
|
|
|
for (std::vector <std::pair <db::LayerProperties, int> >::iterator ll = mp_private->layers.begin (); ll != mp_private->layers.end (); ++ll) {
|
|
addItem (tl::to_qstring (ll->first.to_string ()));
|
|
}
|
|
|
|
set_current_layer (props);
|
|
|
|
} else {
|
|
set_current_layer (-1);
|
|
}
|
|
}
|
|
|
|
void
|
|
LayerSelectionComboBox::set_current_layer (const db::LayerProperties &props)
|
|
{
|
|
for (std::vector <std::pair <db::LayerProperties, int> >::iterator ll = mp_private->layers.begin (); ll != mp_private->layers.end (); ++ll) {
|
|
if (ll->first.log_equal (props)) {
|
|
setCurrentIndex (std::distance (mp_private->layers.begin (), ll));
|
|
return;
|
|
}
|
|
}
|
|
|
|
setCurrentIndex (-1);
|
|
}
|
|
|
|
void
|
|
LayerSelectionComboBox::set_current_layer (int l)
|
|
{
|
|
if (l < 0) {
|
|
setCurrentIndex (-1);
|
|
} else {
|
|
for (std::vector <std::pair <db::LayerProperties, int> >::iterator ll = mp_private->layers.begin (); ll != mp_private->layers.end (); ++ll) {
|
|
if (ll->second == l) {
|
|
setCurrentIndex (std::distance (mp_private->layers.begin (), ll));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int
|
|
LayerSelectionComboBox::current_layer () const
|
|
{
|
|
int i = currentIndex ();
|
|
if (i < 0 || i > int (mp_private->layers.size ())) {
|
|
return -1;
|
|
} else {
|
|
return mp_private->layers [i].second;
|
|
}
|
|
}
|
|
|
|
db::LayerProperties
|
|
LayerSelectionComboBox::current_layer_props () const
|
|
{
|
|
int i = currentIndex ();
|
|
if (i < 0 || i > int (mp_private->layers.size ())) {
|
|
return db::LayerProperties ();
|
|
} else {
|
|
return mp_private->layers [i].first;
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// LibrarySelectionComboBox implementation
|
|
|
|
LibrarySelectionComboBox::LibrarySelectionComboBox (QWidget * /*parent*/)
|
|
{
|
|
update_list ();
|
|
}
|
|
|
|
void
|
|
LibrarySelectionComboBox::update_list ()
|
|
{
|
|
blockSignals (true);
|
|
|
|
db::Library *lib = current_library ();
|
|
|
|
clear ();
|
|
|
|
addItem (QObject::tr ("Local (no library)"), QVariant ());
|
|
for (db::LibraryManager::iterator l = db::LibraryManager::instance ().begin (); l != db::LibraryManager::instance ().end (); ++l) {
|
|
db::Library *lib = db::LibraryManager::instance ().lib (l->second);
|
|
if (! lib->get_description ().empty ()) {
|
|
addItem (tl::to_qstring (lib->get_name () + " - " + lib->get_description ()), QVariant ((unsigned int) lib->get_id ()));
|
|
} else {
|
|
addItem (tl::to_qstring (lib->get_name ()), QVariant ((unsigned int) lib->get_id ()));
|
|
}
|
|
}
|
|
|
|
set_current_library (lib);
|
|
|
|
blockSignals (false);
|
|
}
|
|
|
|
LibrarySelectionComboBox::~LibrarySelectionComboBox ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
LibrarySelectionComboBox::set_current_library (db::Library *lib)
|
|
{
|
|
if (lib != current_library ()) {
|
|
|
|
for (int i = 0; i < count (); ++i) {
|
|
QVariant data = itemData (i);
|
|
db::Library *item_lib = 0;
|
|
if (! data.isNull ()) {
|
|
item_lib = db::LibraryManager::instance ().lib (data.value<db::lib_id_type> ());
|
|
}
|
|
if (item_lib == lib) {
|
|
setCurrentIndex (i);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// fallback: not a valid library pointer
|
|
setCurrentIndex (-1);
|
|
|
|
}
|
|
}
|
|
|
|
db::Library *
|
|
LibrarySelectionComboBox::current_library () const
|
|
{
|
|
QVariant data = itemData (currentIndex ());
|
|
if (data.isNull ()) {
|
|
return 0;
|
|
} else {
|
|
return db::LibraryManager::instance ().lib (data.value<db::lib_id_type> ());
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// SimpleColorButton implementation
|
|
|
|
SimpleColorButton::SimpleColorButton (QWidget *parent, const char *name)
|
|
: QPushButton (parent)
|
|
{
|
|
setObjectName (QString::fromUtf8 (name));
|
|
|
|
connect (this, SIGNAL (clicked ()), this, SLOT (selected ()));
|
|
}
|
|
|
|
SimpleColorButton::SimpleColorButton (QPushButton *&to_replace, const char *name)
|
|
: QPushButton (to_replace->parentWidget ())
|
|
{
|
|
setObjectName (QString::fromUtf8 (name));
|
|
|
|
// If the push button was part of a layout, replace it.
|
|
// This is somewhat tricky because there is no common method of
|
|
// the layout managers to replace a widget.
|
|
|
|
QLayout *ly = to_replace->parentWidget ()->layout ();
|
|
if (ly) {
|
|
|
|
QBoxLayout *bx_ly = dynamic_cast <QBoxLayout *> (ly);
|
|
if (bx_ly) {
|
|
int i = ly->indexOf (to_replace);
|
|
bx_ly->insertWidget (i, this);
|
|
}
|
|
|
|
QGridLayout *grid_ly = dynamic_cast <QGridLayout *> (ly);
|
|
if (grid_ly) {
|
|
int i = ly->indexOf (to_replace);
|
|
int row = 0, column = 0;
|
|
int row_span = 0, column_span = 0;
|
|
grid_ly->getItemPosition (i, &row, &column, &row_span, &column_span);
|
|
grid_ly->addWidget (this, row, column, row_span, column_span);
|
|
}
|
|
|
|
}
|
|
|
|
delete to_replace;
|
|
to_replace = 0;
|
|
|
|
connect (this, SIGNAL (clicked ()), this, SLOT (selected ()));
|
|
}
|
|
|
|
void
|
|
SimpleColorButton::set_color (QColor c)
|
|
{
|
|
set_color_internal (c);
|
|
}
|
|
|
|
void
|
|
SimpleColorButton::set_color_internal (QColor c)
|
|
{
|
|
m_color = c;
|
|
|
|
QFontMetrics fm (font (), this);
|
|
QRect rt (fm.boundingRect (QObject::tr ("Auto"))); // dummy text to be compliant with the other color button
|
|
QPixmap pxmp (rt.width () + 24, rt.height ());
|
|
|
|
QPainter pxpainter (&pxmp);
|
|
QColor text_color = palette ().color (QPalette::Active, QPalette::Text);
|
|
pxpainter.setPen (QPen (text_color));
|
|
pxpainter.setBrush (QBrush (c.isValid () ? c : QColor (128, 128, 128)));
|
|
QRect r (0, 0, pxmp.width () - 1, pxmp.height () - 1);
|
|
pxpainter.drawRect (r);
|
|
|
|
setIconSize (pxmp.size ());
|
|
setIcon (QIcon (pxmp));
|
|
}
|
|
|
|
QColor
|
|
SimpleColorButton::get_color () const
|
|
{
|
|
return m_color;
|
|
}
|
|
|
|
void
|
|
SimpleColorButton::selected ()
|
|
{
|
|
QColor c = QColorDialog::getColor (get_color (), this);
|
|
if (c.isValid ()) {
|
|
set_color (c);
|
|
emit color_changed (m_color);
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// ColorButton implementation
|
|
|
|
ColorButton::ColorButton (QWidget *parent, const char *name)
|
|
: QPushButton (parent)
|
|
{
|
|
setObjectName (QString::fromUtf8 (name));
|
|
|
|
setMenu (new QMenu (this));
|
|
connect (menu (), SIGNAL (aboutToShow ()), this, SLOT (menu_about_to_show ()));
|
|
}
|
|
|
|
ColorButton::ColorButton (QPushButton *&to_replace, const char *name)
|
|
: QPushButton (to_replace->parentWidget ())
|
|
{
|
|
setObjectName (QString::fromUtf8 (name));
|
|
|
|
setMenu (new QMenu (this));
|
|
connect (menu (), SIGNAL (aboutToShow ()), this, SLOT (menu_about_to_show ()));
|
|
|
|
// If the push button was part of a layout, replace it.
|
|
// This is somewhat tricky because there is no common method of
|
|
// the layout managers to replace a widget.
|
|
|
|
QLayout *ly = to_replace->parentWidget ()->layout ();
|
|
if (ly) {
|
|
|
|
QBoxLayout *bx_ly = dynamic_cast <QBoxLayout *> (ly);
|
|
if (bx_ly) {
|
|
int i = ly->indexOf (to_replace);
|
|
bx_ly->insertWidget (i, this);
|
|
}
|
|
|
|
QGridLayout *grid_ly = dynamic_cast <QGridLayout *> (ly);
|
|
if (grid_ly) {
|
|
int i = ly->indexOf (to_replace);
|
|
int row = 0, column = 0;
|
|
int row_span = 0, column_span = 0;
|
|
grid_ly->getItemPosition (i, &row, &column, &row_span, &column_span);
|
|
grid_ly->addWidget (this, row, column, row_span, column_span);
|
|
}
|
|
|
|
}
|
|
|
|
delete to_replace;
|
|
to_replace = 0;
|
|
}
|
|
|
|
const char *color_icon =
|
|
"xxxxxaaxxxxbbxxxxx"
|
|
"xxxxA00AxxB11Bxxxx"
|
|
"xxxa0000ab1111bxxx"
|
|
"xxxa0000ab1111bxxx"
|
|
"xxxxA00AxxB11Bxxxx"
|
|
"xxffxaaxxxxbbxccxx"
|
|
"xF55FxxxxxxxxC22Cx"
|
|
"f5555fxxxxxxc2222c"
|
|
"f5555fxxxxxxc2222c"
|
|
"xF55FxxxxxxxxC22Cx"
|
|
"xxffxeexxxxddxccxx"
|
|
"xxxxE44ExxD33Dxxxx"
|
|
"xxxe4444ed3333dxxx"
|
|
"xxxe4444ed3333dxxx"
|
|
"xxxxE44ExxD33Dxxxx"
|
|
"xxxxxeexxxxddxxxxx";
|
|
|
|
void
|
|
ColorButton::build_menu ()
|
|
{
|
|
menu ()->clear ();
|
|
|
|
menu ()->addAction (QObject::tr ("Automatic"), this, SLOT (menu_selected ()))->setData (QVariant (QColor ()));
|
|
menu ()->addAction (QObject::tr ("Choose ..."), this, SLOT (browse_selected ()));
|
|
menu ()->addSeparator ();
|
|
|
|
try {
|
|
|
|
std::string s;
|
|
lay::PluginRoot::instance ()->config_get (cfg_color_palette, s);
|
|
lay::ColorPalette palette;
|
|
palette.from_string (s);
|
|
|
|
QMenu *submenu = 0;
|
|
|
|
// fill the list of stipple palette items
|
|
for (unsigned int i = 0; i < palette.colors (); ++i) {
|
|
|
|
if ((i % 6) == 0) {
|
|
|
|
std::map<char, QColor> codes;
|
|
codes.insert (std::make_pair ('x', QColor (0, 0, 0, 0)));
|
|
for (int j = 0; j < 6; ++j) {
|
|
QColor c = palette.color_by_index (i + j);
|
|
codes.insert (std::make_pair ('0' + j, c));
|
|
c.setAlpha (128);
|
|
codes.insert (std::make_pair ('a' + j, c));
|
|
c.setAlpha (192);
|
|
codes.insert (std::make_pair ('A' + j, c));
|
|
}
|
|
|
|
QImage icon (18, 16, QImage::Format_ARGB32);
|
|
const char *cp = color_icon;
|
|
for (int y = 0; y < 16; ++y) {
|
|
for (int x = 0; x < 18; ++x) {
|
|
icon.setPixel (x, y, codes [*cp].rgba ());
|
|
++cp;
|
|
}
|
|
}
|
|
|
|
submenu = menu ()->addMenu (QPixmap::fromImage (icon), tl::to_qstring (tl::sprintf ("#%d .. %d", i + 1, std::min (i + 6, palette.colors ()))));
|
|
|
|
}
|
|
|
|
QColor color = QColor (palette.color_by_index (i));
|
|
std::string name = tl::sprintf ("#%d", i + 1);
|
|
|
|
QPixmap icon (16, 16);
|
|
icon.fill (color);
|
|
|
|
submenu->addAction (QIcon (icon), tl::to_qstring (name), this, SLOT (menu_selected ()))->setData (QVariant (color));
|
|
|
|
}
|
|
|
|
} catch (...) { }
|
|
}
|
|
|
|
void
|
|
ColorButton::set_color (QColor c)
|
|
{
|
|
set_color_internal (c);
|
|
}
|
|
|
|
void
|
|
ColorButton::set_color_internal (QColor c)
|
|
{
|
|
m_color = c;
|
|
|
|
QPushButton::setText (QString::fromUtf8 (" "));
|
|
|
|
QString text = QString::fromUtf8 ("XXXXXXX");
|
|
QFontMetrics fm (font (), this);
|
|
QRect rt (fm.boundingRect (text)); // dummy text to be compliant with the other color button
|
|
|
|
QPushButton::setIconSize (QSize (rt.width (), rt.height ()));
|
|
|
|
QPixmap pixmap (rt.width (), rt.height ());
|
|
pixmap.fill (QColor (0, 0, 0, 0));
|
|
|
|
QColor text_color = palette ().color (QPalette::Active, QPalette::Text);
|
|
QPainter pxpainter (&pixmap);
|
|
pxpainter.setPen (QPen (text_color));
|
|
|
|
if (! m_color.isValid ()) {
|
|
|
|
pxpainter.setFont (font ());
|
|
QRect r (0, 0, pixmap.width () - 1, pixmap.height () - 1);
|
|
pxpainter.drawText (r, Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextSingleLine, QObject::tr ("Auto"));
|
|
|
|
} else {
|
|
|
|
pxpainter.setBrush (QBrush (c));
|
|
QRect r (0, 0, pixmap.width () - 1, pixmap.height () - 1);
|
|
pxpainter.drawRect (r);
|
|
|
|
}
|
|
|
|
QPushButton::setIcon (QIcon (pixmap));
|
|
}
|
|
|
|
QColor
|
|
ColorButton::get_color () const
|
|
{
|
|
return m_color;
|
|
}
|
|
|
|
void
|
|
ColorButton::menu_about_to_show ()
|
|
{
|
|
build_menu ();
|
|
}
|
|
|
|
void
|
|
ColorButton::menu_selected ()
|
|
{
|
|
QAction *action = dynamic_cast<QAction *> (sender ());
|
|
if (action) {
|
|
set_color (action->data ().value<QColor> ());
|
|
emit color_changed (m_color);
|
|
}
|
|
}
|
|
|
|
void
|
|
ColorButton::browse_selected ()
|
|
{
|
|
QColor c = QColorDialog::getColor (get_color (), this);
|
|
if (c.isValid ()) {
|
|
set_color (c);
|
|
emit color_changed (m_color);
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// DecoratedLineEdit implementation
|
|
|
|
const int le_frame_width = 4; // TODO: obtain from style?
|
|
const int le_decoration_space = 2; // additional distance between decoration icons and text
|
|
|
|
DecoratedLineEdit::DecoratedLineEdit (QWidget *parent)
|
|
: QLineEdit (parent),
|
|
m_clear_button_enabled (false), m_options_button_enabled (false),
|
|
m_escape_signal_enabled (false), m_tab_signal_enabled (false),
|
|
mp_options_menu (0)
|
|
{
|
|
mp_options_label = new QLabel (this);
|
|
mp_options_label->hide ();
|
|
mp_options_label->setCursor (Qt::ArrowCursor);
|
|
mp_options_label->setPixmap (QString::fromUtf8 (":/options_edit.png"));
|
|
|
|
mp_clear_label = new QLabel (this);
|
|
mp_clear_label->hide ();
|
|
mp_clear_label->setCursor (Qt::ArrowCursor);
|
|
mp_clear_label->setPixmap (QString::fromUtf8 (":/clear_edit.png"));
|
|
|
|
int l = 0, t = 0, r = 0, b = 0;
|
|
getTextMargins (&l, &t, &r, &b);
|
|
m_default_left_margin = l;
|
|
m_default_right_margin = r;
|
|
}
|
|
|
|
DecoratedLineEdit::~DecoratedLineEdit ()
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void DecoratedLineEdit::set_escape_signal_enabled (bool en)
|
|
{
|
|
m_escape_signal_enabled = en;
|
|
}
|
|
|
|
void DecoratedLineEdit::set_tab_signal_enabled (bool en)
|
|
{
|
|
m_tab_signal_enabled = en;
|
|
}
|
|
|
|
bool DecoratedLineEdit::event (QEvent *event)
|
|
{
|
|
// Handling this event makes the widget receive all keystrokes
|
|
if (event->type () == QEvent::ShortcutOverride) {
|
|
QKeyEvent *ke = static_cast<QKeyEvent *> (event);
|
|
if (ke->key () == Qt::Key_Escape && m_escape_signal_enabled) {
|
|
ke->accept ();
|
|
} else if ((ke->key () == Qt::Key_Tab || ke->key () == Qt::Key_Backtab) && m_tab_signal_enabled) {
|
|
ke->accept ();
|
|
}
|
|
}
|
|
return QLineEdit::event (event);
|
|
}
|
|
|
|
void DecoratedLineEdit::keyPressEvent (QKeyEvent *event)
|
|
{
|
|
if (m_escape_signal_enabled && event->key () == Qt::Key_Escape) {
|
|
emit esc_pressed ();
|
|
event->accept ();
|
|
} else if (m_tab_signal_enabled && event->key () == Qt::Key_Tab) {
|
|
emit tab_pressed ();
|
|
event->accept ();
|
|
} else if (m_tab_signal_enabled && event->key () == Qt::Key_Backtab) {
|
|
emit backtab_pressed ();
|
|
event->accept ();
|
|
} else {
|
|
QLineEdit::keyPressEvent (event);
|
|
}
|
|
}
|
|
|
|
bool DecoratedLineEdit::focusNextPrevChild (bool next)
|
|
{
|
|
if (m_tab_signal_enabled && isEnabled ()) {
|
|
QKeyEvent event (QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
|
|
keyPressEvent (&event);
|
|
if (event.isAccepted ()) {
|
|
return true;
|
|
}
|
|
}
|
|
return QLineEdit::focusNextPrevChild (next);
|
|
}
|
|
|
|
void DecoratedLineEdit::set_clear_button_enabled (bool en)
|
|
{
|
|
if (en != m_clear_button_enabled) {
|
|
|
|
m_clear_button_enabled = en;
|
|
mp_clear_label->setVisible (en);
|
|
|
|
int l = 0, t = 0, r = 0, b = 0;
|
|
getTextMargins (&l, &t, &r, &b);
|
|
if (! en) {
|
|
r = m_default_right_margin;
|
|
} else {
|
|
r = m_default_right_margin + mp_clear_label->sizeHint ().width () + le_decoration_space;
|
|
}
|
|
setTextMargins (l, t, r, b);
|
|
|
|
resizeEvent (0);
|
|
|
|
}
|
|
}
|
|
|
|
void DecoratedLineEdit::set_options_button_enabled (bool en)
|
|
{
|
|
if (en != m_options_button_enabled) {
|
|
|
|
m_options_button_enabled = en;
|
|
mp_options_label->setVisible (en);
|
|
|
|
int l = 0, t = 0, r = 0, b = 0;
|
|
getTextMargins (&l, &t, &r, &b);
|
|
if (! en) {
|
|
l = m_default_left_margin;
|
|
} else {
|
|
l = m_default_left_margin + mp_options_label->sizeHint ().width () + le_decoration_space;
|
|
}
|
|
setTextMargins (l, t, r, b);
|
|
|
|
resizeEvent (0);
|
|
|
|
}
|
|
}
|
|
|
|
void DecoratedLineEdit::set_options_menu (QMenu *menu)
|
|
{
|
|
mp_options_menu = menu;
|
|
}
|
|
|
|
void DecoratedLineEdit::mouseReleaseEvent (QMouseEvent *event)
|
|
{
|
|
if (event->button () == Qt::LeftButton) {
|
|
|
|
QWidget *c = childAt (event->pos ());
|
|
if (c == mp_clear_label) {
|
|
|
|
clear ();
|
|
emit textEdited (text ());
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void DecoratedLineEdit::mousePressEvent (QMouseEvent *event)
|
|
{
|
|
if (event->button () == Qt::LeftButton) {
|
|
|
|
QWidget *c = childAt (event->pos ());
|
|
if (c == mp_options_label) {
|
|
|
|
if (mp_options_menu) {
|
|
mp_options_menu->popup (event->globalPos ());
|
|
} else {
|
|
emit options_button_clicked ();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void DecoratedLineEdit::resizeEvent (QResizeEvent * /*event*/)
|
|
{
|
|
int fw = hasFrame () ? le_frame_width : 0;
|
|
|
|
if (m_clear_button_enabled) {
|
|
QSize label_size = mp_clear_label->sizeHint ();
|
|
QRect r = geometry ();
|
|
mp_clear_label->setGeometry (r.width () - fw - label_size.width (), 0, label_size.width (), r.height ());
|
|
}
|
|
|
|
if (m_options_button_enabled) {
|
|
QSize label_size = mp_options_label->sizeHint ();
|
|
QRect r = geometry ();
|
|
mp_options_label->setGeometry (fw, 0, label_size.width (), r.height ());
|
|
}
|
|
}
|
|
|
|
// -------------------------------------------------------------
|
|
// BackgroundAwareTreeStyle implementation
|
|
|
|
BackgroundAwareTreeStyle::BackgroundAwareTreeStyle (QStyle *org_style)
|
|
: QProxyStyle (org_style)
|
|
{
|
|
// .. nothing yet ..
|
|
}
|
|
|
|
void
|
|
BackgroundAwareTreeStyle::drawPrimitive (QStyle::PrimitiveElement pe, const QStyleOption *opt, QPainter *p, const QWidget *w) const
|
|
{
|
|
if (pe == PE_IndicatorBranch) {
|
|
|
|
static const int sz = 9;
|
|
|
|
int mid_h = opt->rect.x () + opt->rect.width () / 2;
|
|
int mid_v = opt->rect.y () + opt->rect.height () / 2;
|
|
|
|
if (opt->state & State_Children) {
|
|
|
|
QColor c;
|
|
|
|
QPalette::ColorGroup cg = QPalette::Disabled;
|
|
if ((w && w->isEnabled ()) || (!w && (opt->state & State_Enabled))) {
|
|
if ((w && w->hasFocus ()) || (!w && (opt->state & State_HasFocus))) {
|
|
cg = QPalette::Normal;
|
|
} else {
|
|
cg = QPalette::Inactive;
|
|
}
|
|
}
|
|
if (opt->state & State_Selected) {
|
|
c = opt->palette.color (cg, QPalette::HighlightedText);
|
|
} else {
|
|
c = opt->palette.color (cg, QPalette::Text);
|
|
}
|
|
if (! (opt->state & State_MouseOver)) {
|
|
if (c.green () < 128) {
|
|
c = QColor ((c.red () * 3 + 255) / 4, (c.green () * 3 + 255) / 4, (c.blue () * 3 + 255) / 4);
|
|
} else {
|
|
c = QColor ((c.red () * 8) / 9, (c.green () * 8) / 9, (c.blue () * 8) / 9);
|
|
}
|
|
}
|
|
|
|
QPen old_pen = p->pen ();
|
|
p->setPen (Qt::NoPen);
|
|
QBrush old_brush = p->brush ();
|
|
p->setBrush (c);
|
|
QPainter::RenderHints old_rh = p->renderHints ();
|
|
p->setRenderHints (QPainter::Antialiasing);
|
|
|
|
if (opt->state & State_Open) {
|
|
QPoint points[] = {
|
|
QPoint (mid_h - sz / 2, mid_v - sz / 3),
|
|
QPoint (mid_h + sz / 2, mid_v - sz / 3),
|
|
QPoint (mid_h, mid_v + sz / 3)
|
|
};
|
|
p->drawPolygon (points, sizeof (points) / sizeof (points[0]));
|
|
} else {
|
|
QPoint points[] = {
|
|
QPoint (mid_h - sz / 3, mid_v - sz / 2),
|
|
QPoint (mid_h + sz / 3, mid_v),
|
|
QPoint (mid_h - sz / 3, mid_v + sz / 2)
|
|
};
|
|
p->drawPolygon (points, sizeof (points) / sizeof (points[0]));
|
|
}
|
|
|
|
p->setPen (old_pen);
|
|
p->setBrush (old_brush);
|
|
p->setRenderHints (old_rh);
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
QProxyStyle::drawPrimitive (pe, opt, p, w);
|
|
}
|
|
|
|
}
|
|
|