You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
koffice/chalk/ui/kis_layerbox.cpp

676 lines
22 KiB

/*
* kis_layerbox.cpp - part of Chalk aka Krayon aka KimageShop
*
* Copyright (c) 2002 Patrick Julien <freak@codepimps.org>
* Copyright (C) 2006 Gábor Lehel <illissius@gmail.com>
*
* 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <tqbutton.h>
#include <tqtoolbutton.h>
#include <tqbrush.h>
#include <tqfont.h>
#include <tqfontmetrics.h>
#include <tqhbox.h>
#include <tqlayout.h>
#include <tqpainter.h>
#include <tqpoint.h>
#include <tqrect.h>
#include <tqstring.h>
#include <tqstyle.h>
#include <tqtooltip.h>
#include <tqwidget.h>
#include <tqcombobox.h>
#include <tqcheckbox.h>
#include <kdebug.h>
#include <tdeglobal.h>
#include <tdepopupmenu.h>
#include <tdemessagebox.h>
#include <kpushbutton.h>
#include <kiconloader.h>
#include <kicontheme.h>
#include <tdelocale.h>
#include <KoPartSelectAction.h>
#include "kis_layerlist.h"
#include "kis_cmb_composite.h"
#include "kis_int_spinbox.h"
#include "wdglayerbox.h"
#include "kis_colorspace.h"
#include "kis_paint_device.h"
#include "kis_layer.h"
#include "kis_group_layer.h"
#include "kis_image.h"
#include "kis_populate_visitor.h"
#include "kis_layerbox.h"
KisLayerBox::KisLayerBox(KisCanvasSubject *subject, TQWidget *parent, const char *name)
: super(parent, name), m_image(0)
{
TQVBoxLayout *vbox = new TQVBoxLayout(this);
vbox->setAutoAdd(true);
m_lst = new WdgLayerBox(this);
setMinimumSize(m_lst->minimumSizeHint());
TQToolTip::add(m_lst->bnAdd, i18n("Create new layer"));
TQToolTip::add(m_lst->bnDelete, i18n("Remove current layer"));
TQToolTip::add(m_lst->bnRaise, i18n("Raise current layer"));
m_lst->bnRaise->setEnabled(false);
m_lst->bnLower->setEnabled(false);
TQToolTip::add(m_lst->bnLower, i18n("Lower current layer"));
TQToolTip::add(m_lst->bnProperties, i18n("Properties for layer"));
TDEIconLoader il( "chalk" );
list()->setPreviewsShown(true);
list()->setFoldersCanBeActive(true);
list()->addProperty("visible", i18n("Visible"), loadPixmap("visible.png", il, TDEIcon::SizeSmallMedium),
loadPixmap("novisible.png", il, TDEIcon::SizeSmallMedium), true);
list()->addProperty("locked", i18n("Locked"), loadPixmap("locked.png", il, TDEIcon::SizeSmallMedium),
loadPixmap("unlocked.png", il, TDEIcon::SizeSmallMedium));
connect(list()->contextMenu(), TQ_SIGNAL(aboutToShow()), TQ_SLOT(slotAboutToShow()));
connect(list(), TQ_SIGNAL(activated(LayerItem*)),
TQ_SLOT(slotLayerActivated(LayerItem*)));
connect(list(), TQ_SIGNAL(displayNameChanged(LayerItem*, const TQString&)),
TQ_SLOT(slotLayerDisplayNameChanged(LayerItem*, const TQString&)));
connect(list(), TQ_SIGNAL(propertyChanged(LayerItem*, const TQString&, bool)),
TQ_SLOT(slotLayerPropertyChanged(LayerItem*, const TQString&, bool)));
connect(list(), TQ_SIGNAL(layerMoved(LayerItem*, LayerItem*, LayerItem*)),
TQ_SLOT(slotLayerMoved(LayerItem*, LayerItem*, LayerItem*)));
connect(list(), TQ_SIGNAL(requestNewLayer(LayerItem*, LayerItem*)),
TQ_SLOT(slotRequestNewLayer(LayerItem*, LayerItem*)));
connect(list(), TQ_SIGNAL(requestNewFolder(LayerItem*, LayerItem*)),
TQ_SLOT(slotRequestNewFolder(LayerItem*, LayerItem*)));
connect(list(), TQ_SIGNAL(requestNewAdjustmentLayer(LayerItem*, LayerItem*)),
TQ_SLOT(slotRequestNewAdjustmentLayer(LayerItem*, LayerItem*)));
connect(list(), TQ_SIGNAL(requestNewObjectLayer(LayerItem*, LayerItem*, const KoDocumentEntry&)),
TQ_SLOT(slotRequestNewObjectLayer(LayerItem*, LayerItem*, const KoDocumentEntry&)));
connect(list(), TQ_SIGNAL(requestRemoveLayer(LayerItem*)),
TQ_SLOT(slotRequestRemoveLayer(LayerItem*)));
connect(list(), TQ_SIGNAL(requestLayerProperties(LayerItem*)),
TQ_SLOT(slotRequestLayerProperties(LayerItem*)));
m_newLayerMenu = new TDEPopupMenu(this);
m_lst->bnAdd->setPopup(m_newLayerMenu);
m_lst->bnAdd->setPopupDelay(1);
m_newLayerMenu->insertItem( SmallIconSet( "document-new" ), i18n( "&New Layer..." ), PAINT_LAYER );
m_newLayerMenu->insertItem( SmallIconSet( "folder" ), i18n( "New &Group Layer..." ), GROUP_LAYER );
m_newLayerMenu->insertItem( SmallIconSet( "tool_filter" ), i18n( "New &Adjustment Layer..." ), ADJUSTMENT_LAYER );
m_partLayerAction = new KoPartSelectAction( i18n( "New &Object Layer" ), "gear", this );
m_partLayerAction->plug( m_newLayerMenu );
connect(m_partLayerAction, TQ_SIGNAL(activated()), this, TQ_SLOT(slotAddMenuActivated()));
connect(m_newLayerMenu, TQ_SIGNAL(activated(int)), this, TQ_SLOT(slotAddMenuActivated(int)));
connect(m_lst->bnDelete, TQ_SIGNAL(clicked()), TQ_SLOT(slotRmClicked()));
connect(m_lst->bnRaise, TQ_SIGNAL(clicked()), TQ_SLOT(slotRaiseClicked()));
connect(m_lst->bnLower, TQ_SIGNAL(clicked()), TQ_SLOT(slotLowerClicked()));
connect(m_lst->bnProperties, TQ_SIGNAL(clicked()), TQ_SLOT(slotPropertiesClicked()));
connect(m_lst->intOpacity, TQ_SIGNAL(valueChanged(int, bool)), TQ_SIGNAL(sigOpacityChanged(int, bool)));
connect(m_lst->intOpacity, TQ_SIGNAL(finishedChanging(int, int)), TQ_SIGNAL(sigOpacityFinishedChanging(int, int)));
connect(m_lst->cmbComposite, TQ_SIGNAL(activated(const KisCompositeOp&)), TQ_SIGNAL(sigItemComposite(const KisCompositeOp&)));
Q_ASSERT(subject->document() != 0);
if (subject->document()) {
connect(subject->document(), TQ_SIGNAL(sigCommandExecuted()), TQ_SLOT(updateThumbnails()));
}
}
KisLayerBox::~KisLayerBox()
{
}
KisLayerList* KisLayerBox::list() const
{
return m_lst->listLayers;
}
void KisLayerBox::setImage(KisImageSP img)
{
if (m_image == img)
return;
if (m_image)
m_image->disconnect(this);
m_image = img;
if (img)
{
connect(img, TQ_SIGNAL(sigLayerActivated(KisLayerSP)), this, TQ_SLOT(slotLayerActivated(KisLayerSP)));
connect(img, TQ_SIGNAL(sigLayerAdded(KisLayerSP)), this, TQ_SLOT(slotLayerAdded(KisLayerSP)));
connect(img, TQ_SIGNAL(sigLayerRemoved(KisLayerSP, KisGroupLayerSP, KisLayerSP)),
this, TQ_SLOT(slotLayerRemoved(KisLayerSP, KisGroupLayerSP, KisLayerSP)));
connect(img, TQ_SIGNAL(sigLayerPropertiesChanged(KisLayerSP)),
this, TQ_SLOT(slotLayerPropertiesChanged(KisLayerSP)));
connect(img, TQ_SIGNAL(sigLayerMoved(KisLayerSP, KisGroupLayerSP, KisLayerSP)),
this, TQ_SLOT(slotLayerMoved(KisLayerSP, KisGroupLayerSP, KisLayerSP)));
connect(img, TQ_SIGNAL(sigLayersChanged(KisGroupLayerSP)), this, TQ_SLOT(slotLayersChanged(KisGroupLayerSP)));
connect(img, TQ_SIGNAL(sigLayerUpdated(KisLayerSP, TQRect)), this, TQ_SLOT(slotLayerUpdated(KisLayerSP, TQRect)));
slotLayersChanged(img->rootLayer());
updateThumbnails();
}
else
{
clear();
}
}
void KisLayerBox::slotLayerActivated(KisLayerSP layer)
{
if (layer)
list()->setActiveLayer(layer->id());
else
list()->setActiveLayer(-1);
updateUI();
}
void KisLayerBox::slotLayerAdded(KisLayerSP layer)
{
if (layer.data() == m_image->rootLayer().data() || list()->layer(layer->id()))
return;
vKisLayerSP layersAdded;
if (layer->parent() == m_image->rootLayer())
{
KisPopulateVisitor visitor(list());
layer->accept(visitor);
layersAdded = visitor.layersAdded();
}
else
{
KisPopulateVisitor visitor(static_cast<KisLayerItem*>(list()->layer(layer->parent()->id())));
layer->accept(visitor);
layersAdded = visitor.layersAdded();
}
for (vKisLayerSP::iterator it = layersAdded.begin(); it != layersAdded.end(); ++it) {
markModified(*it);
}
updateUI();
}
void KisLayerBox::slotLayerRemoved(KisLayerSP layer, KisGroupLayerSP wasParent, KisLayerSP)
{
list()->removeLayer(layer->id());
m_modified.remove(layer->id());
markModified(wasParent);
updateUI();
}
void KisLayerBox::slotLayerMoved(KisLayerSP layer, KisGroupLayerSP wasParent, KisLayerSP)
{
int parentID = layer->parent()->id();
if (layer->parent() == m_image->rootLayer())
parentID = -1;
int siblingID = -1;
if (layer->prevSibling())
siblingID = layer->prevSibling()->id();
list()->moveLayer(layer->id(), parentID, siblingID);
markModified(layer->parent());
markModified(wasParent);
updateUI();
}
void KisLayerBox::slotLayerPropertiesChanged(KisLayerSP layer)
{
if (KisLayerItem* item = dynamic_cast<KisLayerItem*>(list()->layer(layer->id())))
{
Q_ASSERT(item->layer() == layer.data());
item->sync();
updateUI();
markModified(layer);
}
}
void KisLayerBox::slotLayersChanged(KisGroupLayerSP rootLayer)
{
list()->clear();
KisPopulateVisitor visitor(list());
for (KisLayerSP layer = rootLayer->firstChild(); layer; layer = layer->nextSibling())
layer->accept(visitor);
m_modified.clear();
for (TQListViewItemIterator it(list()->lastItem()); *it; --it)
m_modified.append(static_cast<LayerItem*>(*it)->id());
updateUI();
}
void KisLayerBox::slotLayerUpdated(KisLayerSP layer, TQRect)
{
markModified(layer);
}
void KisLayerBox::slotLayerActivated(LayerItem* item)
{
if (item)
m_image->activate(m_image->findLayer(item->id()));
else
m_image->activate(0);
updateUI();
}
void KisLayerBox::slotLayerDisplayNameChanged(LayerItem* item, const TQString& displayName)
{
if(KisLayerSP layer = m_image->findLayer(item->id()))
layer->setName(displayName);
updateUI();
}
void KisLayerBox::slotLayerPropertyChanged(LayerItem* item, const TQString& name, bool on)
{
if (KisLayerSP layer = m_image->findLayer(item->id()))
{
if (name == "visible")
layer->setVisible(on);
else if (name == "locked")
layer->setLocked(on);
}
}
void KisLayerBox::slotLayerMoved(LayerItem* item, LayerItem*, LayerItem*)
{
KisLayerSP layer = m_image->findLayer(item->id());
KisGroupLayerSP parent;
if( item->parent() )
parent = dynamic_cast<KisGroupLayer*>(m_image->findLayer(item->parent()->id()).data());
if( !parent )
parent = m_image->rootLayer();
KisLayerSP above = 0;
if (item->nextSibling())
above = m_image->findLayer(item->nextSibling()->id());
if (layer)
m_image->moveLayer(layer, parent.data(), above);
updateUI();
}
void KisLayerBox::slotRequestNewLayer(LayerItem* p, LayerItem* after)
{
KisLayer* l = m_image->rootLayer().data();
if (p)
l = m_image->findLayer(p->id()).data();
KisGroupLayerSP parent = dynamic_cast<KisGroupLayer*>(l);
KisLayerSP above = 0;
if (after && after->nextSibling())
above = m_image->findLayer(after->nextSibling()->id());
else if (after)
above = 0;
else if (p && p->firstChild())
above = parent->firstChild();
else if (!p && m_image->rootLayer()->childCount())
above = m_image->rootLayer()->firstChild();
emit sigRequestLayer(parent, above);
}
void KisLayerBox::slotRequestNewFolder(LayerItem* p, LayerItem* after)
{
KisLayer* l = m_image->rootLayer().data(); //FIXME I hate copy-pasting like this.
if (p)
l = m_image->findLayer(p->id()).data();
KisGroupLayerSP parent = dynamic_cast<KisGroupLayer*>(l);
KisLayerSP above = 0;
if (after && after->nextSibling())
above = m_image->findLayer(after->nextSibling()->id());
else if (after)
above = 0;
else if (p && p->firstChild())
above = parent->firstChild();
else if (!p && m_image->rootLayer()->childCount())
above = m_image->rootLayer()->firstChild();
emit sigRequestGroupLayer(parent, above);
}
void KisLayerBox::slotRequestNewAdjustmentLayer(LayerItem* p, LayerItem* after)
{
KisLayer* l = m_image->rootLayer().data(); //FIXME here too.
if (p)
l = m_image->findLayer(p->id()).data();
KisGroupLayerSP parent = dynamic_cast<KisGroupLayer*>(l);
KisLayerSP above = 0;
if (after && after->nextSibling())
above = m_image->findLayer(after->nextSibling()->id());
else if (after)
above = 0;
else if (p && p->firstChild())
above = parent->firstChild();
else if (!p && m_image->rootLayer()->childCount())
above = m_image->rootLayer()->firstChild();
emit sigRequestAdjustmentLayer(parent, above);
}
void KisLayerBox::slotRequestNewObjectLayer(LayerItem* p, LayerItem* after, const KoDocumentEntry& entry)
{
KisLayer* l = m_image->rootLayer().data(); //FIXME and here.
if (p)
l = m_image->findLayer(p->id()).data();
KisGroupLayerSP parent = dynamic_cast<KisGroupLayer*>(l);
KisLayerSP above = 0;
if (after && after->nextSibling())
above = m_image->findLayer(after->nextSibling()->id());
else if (after)
above = 0;
else if (p && p->firstChild())
above = parent->firstChild();
else if (!p && m_image->rootLayer()->childCount())
above = m_image->rootLayer()->firstChild();
emit sigRequestPartLayer(parent, above, entry);
}
void KisLayerBox::slotRequestRemoveLayer(LayerItem* item)
{
if (KisLayerSP layer = m_image->findLayer(item->id())) {
m_image->removeLayer(layer);
}
updateUI();
}
void KisLayerBox::slotRequestLayerProperties(LayerItem* item)
{
if (KisLayerSP layer = m_image->findLayer(item->id()))
{
emit sigRequestLayerProperties(layer);
}
}
void KisLayerBox::updateUI()
{
m_lst->bnDelete->setEnabled(list()->activeLayer());
m_lst->bnRaise->setEnabled(list()->activeLayer() && (list()->activeLayer()->prevSibling() || list()->activeLayer()->parent()));
m_lst->bnLower->setEnabled(list()->activeLayer() && list()->activeLayer()->nextSibling());
m_lst->intOpacity->setEnabled(list()->activeLayer());
m_lst->cmbComposite->setEnabled(list()->activeLayer());
if (m_image)
if (KisLayerSP active = m_image->activeLayer())
{
if (m_image->activeDevice())
slotSetColorSpace(m_image->activeDevice()->colorSpace());
else
slotSetColorSpace(m_image->colorSpace());
slotSetOpacity(int(float(active->opacity() * 100) / 255 + 0.5));
slotSetCompositeOp(active->compositeOp());
}
}
void KisLayerBox::slotAboutToShow()
{
}
void KisLayerBox::slotSetCompositeOp(const KisCompositeOp& compositeOp)
{
m_lst->cmbComposite->blockSignals(true);
m_lst->cmbComposite->setCurrentItem(compositeOp);
m_lst->cmbComposite->blockSignals(false);
}
void KisLayerBox::slotSetColorSpace(const KisColorSpace * colorSpace)
{
m_lst->cmbComposite->blockSignals(true);
m_lst->cmbComposite->setCompositeOpList(colorSpace->userVisiblecompositeOps());
m_lst->cmbComposite->blockSignals(false);
}
// range: 0-100
void KisLayerBox::slotSetOpacity(int opacity)
{
m_lst->intOpacity->blockSignals(true);
m_lst->intOpacity->setValue(opacity);
m_lst->intOpacity->blockSignals(false);
}
void KisLayerBox::clear()
{
list()->clear();
updateUI();
}
void KisLayerBox::slotAddMenuActivated(int type)
{
if(type == -1)
return;
KisGroupLayerSP root = m_image->rootLayer();
KisGroupLayerSP parent;
KisLayerSP above;
if (KisLayerSP active = m_image->activeLayer())
{
parent = root;
above = active;
if (active->parent())
parent = active->parent();
}
else
{
parent = root;
above = m_image->rootLayer()->firstChild();
}
switch (type)
{
case PAINT_LAYER:
emit sigRequestLayer(parent, above);
break;
case GROUP_LAYER:
emit sigRequestGroupLayer(parent, above);
break;
case ADJUSTMENT_LAYER:
emit sigRequestAdjustmentLayer(parent, above);
break;
case OBJECT_LAYER:
default: //TQt doesn't emit activated for default-assigned IDs, so this does nothing
emit sigRequestPartLayer(parent, above, m_partLayerAction->documentEntry());
}
}
void KisLayerBox::slotRmClicked()
{
TQValueList<int> l = list()->selectedLayerIDs();
if (l.count() < 2 && list()->activeLayer() && !l.contains(list()->activeLayer()->id()))
{
l.clear();
l.append(list()->activeLayer()->id());
}
for (int i = 0, n = l.count(); i < n; ++i)
{
m_modified.remove(l[i]);
m_image->removeLayer(m_image->findLayer(l[i]));
}
}
void KisLayerBox::slotRaiseClicked()
{
TQValueList<int> l = list()->selectedLayerIDs();
if (l.count() < 2 && list()->activeLayer() && !l.contains(list()->activeLayer()->id()))
{
l.clear();
l.append(list()->activeLayer()->id());
}
KisLayerSP layer = m_image->findLayer(l.first());
if( l.count() == 1 && layer == layer->parent()->firstChild() && layer->parent() != m_image->rootLayer())
{
if (KisGroupLayerSP grandparent = layer->parent()->parent())
m_image->moveLayer(layer, grandparent, layer->parent().data());
}
else
{
for (int i = 0, n = l.count(); i < n; ++i)
if (KisLayerSP li = m_image->findLayer(l[i]))
if (li->prevSibling())
m_image->moveLayer(li, li->parent(), li->prevSibling());
}
if( !l.isEmpty() )
list()->ensureItemVisible( list()->layer( l.first() ) );
}
void KisLayerBox::slotLowerClicked()
{
TQValueList<LayerItem*> l = list()->selectedLayers();
if (l.count() < 2 && list()->activeLayer() && !l.contains(list()->activeLayer()))
{
l.clear();
l.append(list()->activeLayer());
}
for (int i = l.count() - 1; i >= 0; --i)
if (LayerItem *layer = l[i])
if (layer->nextSibling())
list()->moveLayer(layer, layer->parent(), layer->nextSibling());
if( !l.isEmpty() )
list()->ensureItemVisible( l.last() );
}
void KisLayerBox::slotPropertiesClicked()
{
if (KisLayerSP active = m_image->activeLayer())
emit sigRequestLayerProperties(active);
}
void KisLayerBox::updateThumbnails()
{
bool again = true;
while (m_modified.count() && again)
{
//again = false;
KisLayerItem* item = static_cast<KisLayerItem*>(list()->layer(m_modified.last()));
m_modified.pop_back();
if (!item || !item->updatePreview())
again = true;
}
}
void KisLayerBox::setUpdatesAndSignalsEnabled(bool enable)
{
setUpdatesEnabled(enable);
m_lst->intOpacity->setUpdatesEnabled(enable);
m_lst->cmbComposite->setUpdatesEnabled(enable);
list()->blockSignals(!enable);
m_lst->intOpacity->blockSignals(!enable);
m_lst->cmbComposite->blockSignals(!enable);
}
TQPixmap KisLayerBox::loadPixmap(const TQString& filename, const TDEIconLoader&
il, int size)
{
TQPixmap pixmap = il.loadIcon(filename, TDEIcon::NoGroup, size);
if (pixmap.isNull())
KMessageBox::error(0, i18n("Cannot find %1").arg(filename),
i18n("Canvas"));
return pixmap;
}
void KisLayerBox::markModified(KisLayer* layer)
{
if( !layer )
return;
TQValueList<int> v;
while (layer && layer != m_image->rootLayer().data())
{
v.append(layer->id());
layer = layer->parent();
}
for (int i = v.count() - 1; i >= 0; --i)
if (!m_modified.contains(v[i]))
m_modified.append(v[i]);
}
void KisLayerBox::printChalkLayers() const
{
static int indent = 0;
static KisLayer *root = 0;
if( !root )
root = m_image->rootLayer();
if( !root )
return;
TQString s = root->name();
if( dynamic_cast<KisGroupLayer*>( root ) )
s = TQString("[%1]").arg( s );
if( m_image->activeLayer().data() == root )
s.prepend("*");
kdDebug() << (TQString().fill(' ', indent) + s) << endl;
for (KisLayer* layer = root->firstChild(); layer; layer = layer->nextSibling())
{
indent += 2;
root = layer;
printChalkLayers();
indent -= 2;
root = layer->parent();
}
}
void KisLayerBox::printLayerboxLayers() const
{
static int indent = 0;
static LayerItem *root = 0;
if( !root )
{
for (LayerItem* layer = list()->firstChild(); layer; layer = layer->nextSibling())
{
indent += 2;
root = layer;
printLayerboxLayers();
indent -= 2;
root = layer->parent();
}
return;
}
TQString s = root->displayName();
if( root->isFolder() )
s = TQString("[%1]").arg( s );
if( list()->activeLayer() == root )
s.prepend("*");
kdDebug() << (TQString().fill(' ', indent) + s) << endl;
for (LayerItem* layer = root->firstChild(); layer; layer = layer->nextSibling())
{
indent += 2;
root = layer;
printLayerboxLayers();
indent -= 2;
root = layer->parent();
}
}
#include "kis_layerbox.moc"