/* * 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(), TQT_SIGNAL(aboutToShow()), TQT_SLOT(slotAboutToShow())); connect(list(), TQT_SIGNAL(activated(LayerItem*)), TQT_SLOT(slotLayerActivated(LayerItem*))); connect(list(), TQT_SIGNAL(displayNameChanged(LayerItem*, const TQString&)), TQT_SLOT(slotLayerDisplayNameChanged(LayerItem*, const TQString&))); connect(list(), TQT_SIGNAL(propertyChanged(LayerItem*, const TQString&, bool)), TQT_SLOT(slotLayerPropertyChanged(LayerItem*, const TQString&, bool))); connect(list(), TQT_SIGNAL(layerMoved(LayerItem*, LayerItem*, LayerItem*)), TQT_SLOT(slotLayerMoved(LayerItem*, LayerItem*, LayerItem*))); connect(list(), TQT_SIGNAL(requestNewLayer(LayerItem*, LayerItem*)), TQT_SLOT(slotRequestNewLayer(LayerItem*, LayerItem*))); connect(list(), TQT_SIGNAL(requestNewFolder(LayerItem*, LayerItem*)), TQT_SLOT(slotRequestNewFolder(LayerItem*, LayerItem*))); connect(list(), TQT_SIGNAL(requestNewAdjustmentLayer(LayerItem*, LayerItem*)), TQT_SLOT(slotRequestNewAdjustmentLayer(LayerItem*, LayerItem*))); connect(list(), TQT_SIGNAL(requestNewObjectLayer(LayerItem*, LayerItem*, const KoDocumentEntry&)), TQT_SLOT(slotRequestNewObjectLayer(LayerItem*, LayerItem*, const KoDocumentEntry&))); connect(list(), TQT_SIGNAL(requestRemoveLayer(LayerItem*)), TQT_SLOT(slotRequestRemoveLayer(LayerItem*))); connect(list(), TQT_SIGNAL(requestLayerProperties(LayerItem*)), TQT_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", TQT_TQOBJECT(this) ); m_partLayerAction->plug( m_newLayerMenu ); connect(m_partLayerAction, TQT_SIGNAL(activated()), this, TQT_SLOT(slotAddMenuActivated())); connect(m_newLayerMenu, TQT_SIGNAL(activated(int)), this, TQT_SLOT(slotAddMenuActivated(int))); connect(m_lst->bnDelete, TQT_SIGNAL(clicked()), TQT_SLOT(slotRmClicked())); connect(m_lst->bnRaise, TQT_SIGNAL(clicked()), TQT_SLOT(slotRaiseClicked())); connect(m_lst->bnLower, TQT_SIGNAL(clicked()), TQT_SLOT(slotLowerClicked())); connect(m_lst->bnProperties, TQT_SIGNAL(clicked()), TQT_SLOT(slotPropertiesClicked())); connect(m_lst->intOpacity, TQT_SIGNAL(valueChanged(int, bool)), TQT_SIGNAL(sigOpacityChanged(int, bool))); connect(m_lst->intOpacity, TQT_SIGNAL(finishedChanging(int, int)), TQT_SIGNAL(sigOpacityFinishedChanging(int, int))); connect(m_lst->cmbComposite, TQT_SIGNAL(activated(const KisCompositeOp&)), TQT_SIGNAL(sigItemComposite(const KisCompositeOp&))); Q_ASSERT(subject->document() != 0); if (subject->document()) { connect(subject->document(), TQT_SIGNAL(sigCommandExecuted()), TQT_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, TQT_SIGNAL(sigLayerActivated(KisLayerSP)), this, TQT_SLOT(slotLayerActivated(KisLayerSP))); connect(img, TQT_SIGNAL(sigLayerAdded(KisLayerSP)), this, TQT_SLOT(slotLayerAdded(KisLayerSP))); connect(img, TQT_SIGNAL(sigLayerRemoved(KisLayerSP, KisGroupLayerSP, KisLayerSP)), this, TQT_SLOT(slotLayerRemoved(KisLayerSP, KisGroupLayerSP, KisLayerSP))); connect(img, TQT_SIGNAL(sigLayerPropertiesChanged(KisLayerSP)), this, TQT_SLOT(slotLayerPropertiesChanged(KisLayerSP))); connect(img, TQT_SIGNAL(sigLayerMoved(KisLayerSP, KisGroupLayerSP, KisLayerSP)), this, TQT_SLOT(slotLayerMoved(KisLayerSP, KisGroupLayerSP, KisLayerSP))); connect(img, TQT_SIGNAL(sigLayersChanged(KisGroupLayerSP)), this, TQT_SLOT(slotLayersChanged(KisGroupLayerSP))); connect(img, TQT_SIGNAL(sigLayerUpdated(KisLayerSP, TQRect)), this, TQT_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"