From 698569f8428ca088f764d704034a1330517b98c0 Mon Sep 17 00:00:00 2001 From: tpearson Date: Sun, 26 Jun 2011 00:41:16 +0000 Subject: Finish rebranding of Krita as Chalk git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1238363 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- chalk/ui/kis_layerbox.cc | 675 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 675 insertions(+) create mode 100644 chalk/ui/kis_layerbox.cc (limited to 'chalk/ui/kis_layerbox.cc') diff --git a/chalk/ui/kis_layerbox.cc b/chalk/ui/kis_layerbox.cc new file mode 100644 index 000000000..703fd3bec --- /dev/null +++ b/chalk/ui/kis_layerbox.cc @@ -0,0 +1,675 @@ +/* + * kis_layerbox.cc - part of Chalk aka Krayon aka KimageShop + * + * Copyright (c) 2002 Patrick Julien + * Copyright (C) 2006 Gábor Lehel + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 *tqparent, const char *name) + : super(tqparent, name), m_image(0) +{ + TQVBoxLayout *vbox = new TQVBoxLayout(this); + vbox->setAutoAdd(true); + + m_lst = new WdgLayerBox(this); + setMinimumSize(m_lst->tqminimumSizeHint()); + + 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")); + + KIconLoader il( "chalk" ); + + list()->setPreviewsShown(true); + + list()->setFoldersCanBeActive(true); + + list()->addProperty("visible", i18n("Visible"), loadPixmap("visible.png", il, KIcon::SizeSmallMedium), + loadPixmap("novisible.png", il, KIcon::SizeSmallMedium), true); + + list()->addProperty("locked", i18n("Locked"), loadPixmap("locked.png", il, KIcon::SizeSmallMedium), + loadPixmap("unlocked.png", il, KIcon::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 KPopupMenu(this); + m_lst->bnAdd->setPopup(m_newLayerMenu); + m_lst->bnAdd->setPopupDelay(1); + m_newLayerMenu->insertItem( SmallIconSet( "filenew" ), 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->tqparent() == m_image->rootLayer()) + { + KisPopulateVisitor visitor(list()); + layer->accept(visitor); + layersAdded = visitor.layersAdded(); + } + else + { + KisPopulateVisitor visitor(static_cast(list()->layer(layer->tqparent()->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 tqparentID = layer->tqparent()->id(); + if (layer->tqparent() == m_image->rootLayer()) + tqparentID = -1; + + int siblingID = -1; + if (layer->prevSibling()) + siblingID = layer->prevSibling()->id(); + + list()->moveLayer(layer->id(), tqparentID, siblingID); + + markModified(layer->tqparent()); + markModified(wasParent); + updateUI(); +} + +void KisLayerBox::slotLayerPropertiesChanged(KisLayerSP layer) +{ + if (KisLayerItem* item = dynamic_cast(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(*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 tqparent; + if( item->tqparent() ) + tqparent = dynamic_cast(m_image->findLayer(item->tqparent()->id()).data()); + if( !tqparent ) + tqparent = m_image->rootLayer(); + KisLayerSP above = 0; + if (item->nextSibling()) + above = m_image->findLayer(item->nextSibling()->id()); + if (layer) + m_image->moveLayer(layer, tqparent.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 tqparent = dynamic_cast(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 = tqparent->firstChild(); + else if (!p && m_image->rootLayer()->childCount()) + above = m_image->rootLayer()->firstChild(); + emit sigRequestLayer(tqparent, 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 tqparent = dynamic_cast(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 = tqparent->firstChild(); + else if (!p && m_image->rootLayer()->childCount()) + above = m_image->rootLayer()->firstChild(); + emit sigRequestGroupLayer(tqparent, 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 tqparent = dynamic_cast(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 = tqparent->firstChild(); + else if (!p && m_image->rootLayer()->childCount()) + above = m_image->rootLayer()->firstChild(); + emit sigRequestAdjustmentLayer(tqparent, 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 tqparent = dynamic_cast(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 = tqparent->firstChild(); + else if (!p && m_image->rootLayer()->childCount()) + above = m_image->rootLayer()->firstChild(); + emit sigRequestPartLayer(tqparent, 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()->tqparent())); + 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 tqparent; + KisLayerSP above; + if (KisLayerSP active = m_image->activeLayer()) + { + tqparent = root; + above = active; + if (active->tqparent()) + tqparent = active->tqparent(); + } + else + { + tqparent = root; + above = m_image->rootLayer()->firstChild(); + } + + switch (type) + { + case PAINT_LAYER: + emit sigRequestLayer(tqparent, above); + break; + case GROUP_LAYER: + emit sigRequestGroupLayer(tqparent, above); + break; + case ADJUSTMENT_LAYER: + emit sigRequestAdjustmentLayer(tqparent, above); + break; + case OBJECT_LAYER: + default: //goddamned TQt doesn't emit activated for default-assigned IDs, so this does nothing + emit sigRequestPartLayer(tqparent, above, m_partLayerAction->documentEntry()); + } +} + +void KisLayerBox::slotRmClicked() +{ + TQValueList l = list()->selectedLayerIDs(); + if (l.count() < 2 && list()->activeLayer() && !l.tqcontains(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 l = list()->selectedLayerIDs(); + if (l.count() < 2 && list()->activeLayer() && !l.tqcontains(list()->activeLayer()->id())) + { + l.clear(); + l.append(list()->activeLayer()->id()); + } + + KisLayerSP layer = m_image->findLayer(l.first()); + if( l.count() == 1 && layer == layer->tqparent()->firstChild() && layer->tqparent() != m_image->rootLayer()) + { + if (KisGroupLayerSP grandtqparent = layer->tqparent()->tqparent()) + m_image->moveLayer(layer, grandtqparent, layer->tqparent().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->tqparent(), li->prevSibling()); + } + + if( !l.isEmpty() ) + list()->ensureItemVisible( list()->layer( l.first() ) ); +} + +void KisLayerBox::slotLowerClicked() +{ + TQValueList l = list()->selectedLayers(); + if (l.count() < 2 && list()->activeLayer() && !l.tqcontains(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->tqparent(), 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(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 KIconLoader& + il, int size) +{ + TQPixmap pixmap = il.loadIcon(filename, KIcon::NoGroup, size); + + if (pixmap.isNull()) + KMessageBox::error(0, i18n("Cannot tqfind %1").tqarg(filename), + i18n("Canvas")); + + return pixmap; +} + +void KisLayerBox::markModified(KisLayer* layer) +{ + if( !layer ) + return; + + TQValueList v; + while (layer && layer != m_image->rootLayer().data()) + { + v.append(layer->id()); + layer = layer->tqparent(); + } + for (int i = v.count() - 1; i >= 0; --i) + if (!m_modified.tqcontains(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( root ) ) + s = TQString("[%1]").tqarg( 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->tqparent(); + } +} + +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->tqparent(); + } + return; + } + TQString s = root->displayName(); + if( root->isFolder() ) + s = TQString("[%1]").tqarg( 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->tqparent(); + } +} + +#include "kis_layerbox.moc" -- cgit v1.2.3