1 /*
2  *  Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License
15  *  along with this program; if not, write to the Free Software
16  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18 
19 #include "kis_selection_options.h"
20 
21 #include <QWidget>
22 #include <QRadioButton>
23 #include <QComboBox>
24 #include <QVBoxLayout>
25 #include <QLayout>
26 #include <QButtonGroup>
27 
28 #include <kis_icon.h>
29 #include "kis_types.h"
30 #include "kis_layer.h"
31 #include "kis_image.h"
32 #include "kis_selection.h"
33 #include "kis_paint_device.h"
34 #include "canvas/kis_canvas2.h"
35 #include "KisViewManager.h"
36 #include "kis_signal_compressor.h"
37 #include "kis_shape_controller.h"
38 #include "kis_canvas2.h"
39 #include "KisDocument.h"
40 #include "kis_dummies_facade_base.h"
41 
42 #include <ksharedconfig.h>
43 #include <kconfiggroup.h>
44 
KisSelectionOptions(KisCanvas2 *)45 KisSelectionOptions::KisSelectionOptions(KisCanvas2 * /*canvas*/)
46     : m_colorLabelsCompressor(900, KisSignalCompressor::FIRST_INACTIVE)
47 {
48     m_page = new WdgSelectionOptions(this);
49     Q_CHECK_PTR(m_page);
50 
51     QVBoxLayout * l = new QVBoxLayout(this);
52     l->addWidget(m_page);
53     l->addSpacerItem(new QSpacerItem(0,0, QSizePolicy::Preferred, QSizePolicy::Expanding));
54     l->setContentsMargins(0,0,0,0);
55 
56     m_mode = new QButtonGroup(this);
57     m_mode->addButton(m_page->pixel, PIXEL_SELECTION);
58     m_mode->addButton(m_page->shape, SHAPE_PROTECTION);
59 
60     m_action = new QButtonGroup(this);
61     m_action->addButton(m_page->add, SELECTION_ADD);
62     m_action->addButton(m_page->subtract, SELECTION_SUBTRACT);
63     m_action->addButton(m_page->replace, SELECTION_REPLACE);
64     m_action->addButton(m_page->intersect, SELECTION_INTERSECT);
65     m_action->addButton(m_page->symmetricdifference, SELECTION_SYMMETRICDIFFERENCE);
66 
67     m_page->pixel->setGroupPosition(KoGroupButton::GroupLeft);
68     m_page->shape->setGroupPosition(KoGroupButton::GroupRight);
69     m_page->pixel->setIcon(KisIconUtils::loadIcon("select_pixel"));
70     m_page->shape->setIcon(KisIconUtils::loadIcon("select_shape"));
71 
72     m_page->add->setGroupPosition(KoGroupButton::GroupCenter);
73     m_page->subtract->setGroupPosition(KoGroupButton::GroupCenter);
74     m_page->replace->setGroupPosition(KoGroupButton::GroupLeft);
75     m_page->intersect->setGroupPosition(KoGroupButton::GroupCenter);
76     m_page->symmetricdifference->setGroupPosition(KoGroupButton::GroupRight);
77     m_page->add->setIcon(KisIconUtils::loadIcon("selection_add"));
78     m_page->subtract->setIcon(KisIconUtils::loadIcon("selection_subtract"));
79     m_page->replace->setIcon(KisIconUtils::loadIcon("selection_replace"));
80     m_page->intersect->setIcon(KisIconUtils::loadIcon("selection_intersect"));
81     m_page->symmetricdifference->setIcon(KisIconUtils::loadIcon("selection_symmetric_difference"));
82 
83     m_page->cmbSampleLayersMode->addItem(sampleLayerModeToUserString(SAMPLE_LAYERS_MODE_CURRENT), SAMPLE_LAYERS_MODE_CURRENT);
84     m_page->cmbSampleLayersMode->addItem(sampleLayerModeToUserString(SAMPLE_LAYERS_MODE_ALL), SAMPLE_LAYERS_MODE_ALL);
85     m_page->cmbSampleLayersMode->addItem(sampleLayerModeToUserString(SAMPLE_LAYERS_MODE_COLOR_LABELED), SAMPLE_LAYERS_MODE_COLOR_LABELED);
86     m_page->cmbSampleLayersMode->setEditable(false);
87 
88     m_page->cmbColorLabels->setModes(false, false);
89 
90     connect(m_mode, SIGNAL(buttonClicked(int)), this, SIGNAL(modeChanged(int)));
91     connect(m_action, SIGNAL(buttonClicked(int)), this, SIGNAL(actionChanged(int)));
92     connect(m_mode, SIGNAL(buttonClicked(int)), this, SLOT(hideActionsForSelectionMode(int)));
93     connect(m_page->chkAntiAliasing, SIGNAL(toggled(bool)), this, SIGNAL(antiAliasSelectionChanged(bool)));
94     connect(m_page->cmbColorLabels, SIGNAL(selectedColorsChanged()), this, SIGNAL(selectedColorLabelsChanged()));
95     connect(m_page->cmbSampleLayersMode, SIGNAL(currentIndexChanged(int)), this, SLOT(slotSampleLayersModeChanged(int)));
96 
97     KConfigGroup cfg = KSharedConfig::openConfig()->group("KisToolSelectBase");
98     m_page->chkAntiAliasing->setChecked(cfg.readEntry("antiAliasSelection", true));
99 
100     connect(&m_colorLabelsCompressor, SIGNAL(timeout()), this, SLOT(slotUpdateAvailableColorLabels()));
101 }
102 
~KisSelectionOptions()103 KisSelectionOptions::~KisSelectionOptions()
104 {
105 }
106 
action()107 int KisSelectionOptions::action()
108 {
109     return m_action->checkedId();
110 }
111 
setAction(int action)112 void KisSelectionOptions::setAction(int action) {
113     QAbstractButton* button = m_action->button(action);
114     KIS_SAFE_ASSERT_RECOVER_RETURN(button);
115 
116     button->setChecked(true);
117 }
118 
setMode(int mode)119 void KisSelectionOptions::setMode(int mode) {
120     QAbstractButton* button = m_mode->button(mode);
121     KIS_SAFE_ASSERT_RECOVER_RETURN(button);
122 
123     button->setChecked(true);
124     hideActionsForSelectionMode(mode);
125 }
126 
setAntiAliasSelection(bool value)127 void KisSelectionOptions::setAntiAliasSelection(bool value)
128 {
129     m_page->chkAntiAliasing->setChecked(value);
130 }
131 
setSampleLayersMode(QString mode)132 void KisSelectionOptions::setSampleLayersMode(QString mode)
133 {
134     if (mode != SAMPLE_LAYERS_MODE_ALL && mode != SAMPLE_LAYERS_MODE_COLOR_LABELED && mode != SAMPLE_LAYERS_MODE_CURRENT) {
135         mode = SAMPLE_LAYERS_MODE_CURRENT;
136     }
137     setCmbSampleLayersMode(mode);
138 }
139 
enablePixelOnlySelectionMode()140 void KisSelectionOptions::enablePixelOnlySelectionMode()
141 {
142     setMode(PIXEL_SELECTION);
143     disableSelectionModeOption();
144 }
145 
setColorLabelsEnabled(bool enabled)146 void KisSelectionOptions::setColorLabelsEnabled(bool enabled)
147 {
148     if (enabled) {
149         m_page->cmbColorLabels->show();
150         m_page->cmbSampleLayersMode->show();
151     } else {
152         m_page->cmbColorLabels->hide();
153         m_page->cmbSampleLayersMode->hide();
154     }
155 }
156 
updateActionButtonToolTip(int action,const QKeySequence & shortcut)157 void KisSelectionOptions::updateActionButtonToolTip(int action, const QKeySequence &shortcut)
158 {
159     const QString shortcutString = shortcut.toString(QKeySequence::NativeText);
160     QString toolTipText;
161     switch ((SelectionAction)action) {
162     case SELECTION_DEFAULT:
163     case SELECTION_REPLACE:
164         toolTipText = shortcutString.isEmpty() ?
165             i18nc("@info:tooltip", "Replace") :
166             i18nc("@info:tooltip", "Replace (%1)", shortcutString);
167 
168         m_action->button(SELECTION_REPLACE)->setToolTip(toolTipText);
169         break;
170     case SELECTION_ADD:
171         toolTipText = shortcutString.isEmpty() ?
172             i18nc("@info:tooltip", "Add") :
173             i18nc("@info:tooltip", "Add (%1)", shortcutString);
174 
175         m_action->button(SELECTION_ADD)->setToolTip(toolTipText);
176         break;
177     case SELECTION_SUBTRACT:
178         toolTipText = shortcutString.isEmpty() ?
179             i18nc("@info:tooltip", "Subtract") :
180             i18nc("@info:tooltip", "Subtract (%1)", shortcutString);
181 
182         m_action->button(SELECTION_SUBTRACT)->setToolTip(toolTipText);
183 
184         break;
185     case SELECTION_INTERSECT:
186         toolTipText = shortcutString.isEmpty() ?
187             i18nc("@info:tooltip", "Intersect") :
188             i18nc("@info:tooltip", "Intersect (%1)", shortcutString);
189 
190         m_action->button(SELECTION_INTERSECT)->setToolTip(toolTipText);
191 
192         break;
193 
194     case SELECTION_SYMMETRICDIFFERENCE:
195         toolTipText = shortcutString.isEmpty() ?
196             i18nc("@info:tooltip", "Symmetric Difference") :
197             i18nc("@info:tooltip", "Symmetric Difference (%1)", shortcutString);
198 
199         m_action->button(SELECTION_SYMMETRICDIFFERENCE)->setToolTip(toolTipText);
200 
201         break;
202     }
203 }
204 
attachToImage(KisImageSP image,KisCanvas2 * canvas)205 void KisSelectionOptions::attachToImage(KisImageSP image, KisCanvas2* canvas)
206 {
207     m_image = image;
208     m_canvas = canvas;
209     activateConnectionToImage();
210 }
211 
activateConnectionToImage()212 void KisSelectionOptions::activateConnectionToImage()
213 {
214     if (m_image && m_canvas) {
215         m_page->cmbColorLabels->updateAvailableLabels(m_image->root());
216         KIS_SAFE_ASSERT_RECOVER_RETURN(m_canvas);
217         KisDocument *doc = m_canvas->imageView()->document();
218 
219         KisShapeController *kritaShapeController =
220                 dynamic_cast<KisShapeController*>(doc->shapeController());
221         KisDummiesFacadeBase* m_dummiesFacade = static_cast<KisDummiesFacadeBase*>(kritaShapeController);
222         if (m_dummiesFacade) {
223             m_nodesUpdatesConnectionsStore.addConnection(m_dummiesFacade, SIGNAL(sigEndInsertDummy(KisNodeDummy*)),
224                                                          &m_colorLabelsCompressor, SLOT(start()));
225             m_nodesUpdatesConnectionsStore.addConnection(m_dummiesFacade, SIGNAL(sigEndRemoveDummy()),
226                                                          &m_colorLabelsCompressor, SLOT(start()));
227             m_nodesUpdatesConnectionsStore.addConnection(m_dummiesFacade, SIGNAL(sigDummyChanged(KisNodeDummy*)),
228                                                          &m_colorLabelsCompressor, SLOT(start()));
229         }
230     }
231 }
232 
deactivateConnectionToImage()233 void KisSelectionOptions::deactivateConnectionToImage()
234 {
235     m_nodesUpdatesConnectionsStore.clear();
236 }
237 
238 //hide action buttons and antialiasing, if shape selection is active (actions currently don't work on shape selection)
hideActionsForSelectionMode(int mode)239 void KisSelectionOptions::hideActionsForSelectionMode(int mode) {
240     const bool isPixelSelection = (mode == (int)PIXEL_SELECTION);
241 
242     m_page->chkAntiAliasing->setVisible(isPixelSelection);
243 }
244 
slotUpdateAvailableColorLabels()245 void KisSelectionOptions::slotUpdateAvailableColorLabels()
246 {
247     if (m_image) {
248         m_page->cmbColorLabels->updateAvailableLabels(m_image->root());
249     }
250 }
251 
slotSampleLayersModeChanged(int index)252 void KisSelectionOptions::slotSampleLayersModeChanged(int index)
253 {
254     QString newSampleLayersMode = m_page->cmbSampleLayersMode->itemData(index).toString();
255     m_page->cmbColorLabels->setEnabled(newSampleLayersMode == SAMPLE_LAYERS_MODE_COLOR_LABELED);
256     emit sampleLayersModeChanged(newSampleLayersMode);
257 }
258 
sampleLayerModeToUserString(QString sampleLayersModeId)259 QString KisSelectionOptions::sampleLayerModeToUserString(QString sampleLayersModeId)
260 {
261     QString currentLayer = i18nc("Option in selection tool: take only the current layer into account when calculating the selection", "Current Layer");
262     if (sampleLayersModeId == SAMPLE_LAYERS_MODE_CURRENT) {
263         return currentLayer;
264     } else if (sampleLayersModeId == SAMPLE_LAYERS_MODE_ALL) {
265         return i18nc("Option in selection tool: take all layers (merged) into account when calculating the selection", "All Layers");
266     } else if (sampleLayersModeId == SAMPLE_LAYERS_MODE_COLOR_LABELED) {
267         return i18nc("Option in selection tool: take all layers that were marked with specific color labels (more precisely, all of them merged) "
268                      "into account when calculating the selection", "Color Labeled Layers");
269     }
270 
271     KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(false, currentLayer);
272     return currentLayer;
273 }
274 
setCmbSampleLayersMode(QString sampleLayersModeId)275 void KisSelectionOptions::setCmbSampleLayersMode(QString sampleLayersModeId)
276 {
277     for (int i = 0; i < m_page->cmbSampleLayersMode->count(); i++) {
278         if (m_page->cmbSampleLayersMode->itemData(i).toString() == sampleLayersModeId)
279         {
280             m_page->cmbSampleLayersMode->setCurrentIndex(i);
281             break;
282         }
283     }
284     m_page->cmbColorLabels->setEnabled(sampleLayersModeId == SAMPLE_LAYERS_MODE_COLOR_LABELED);
285 }
286 
antiAliasSelection()287 bool KisSelectionOptions::antiAliasSelection()
288 {
289     return m_page->chkAntiAliasing->isChecked();
290 }
291 
colorLabelsSelected()292 QList<int> KisSelectionOptions::colorLabelsSelected()
293 {
294     return m_page->cmbColorLabels->selectedColors();
295 }
296 
sampleLayersMode()297 QString KisSelectionOptions::sampleLayersMode()
298 {
299     return m_page->cmbSampleLayersMode->currentData().toString();
300 }
301 
disableAntiAliasSelectionOption()302 void KisSelectionOptions::disableAntiAliasSelectionOption()
303 {
304     m_page->chkAntiAliasing->hide();
305     disconnect(m_page->pixel, SIGNAL(clicked()), m_page->chkAntiAliasing, SLOT(show()));
306 }
307 
disableSelectionModeOption()308 void KisSelectionOptions::disableSelectionModeOption()
309 {
310     m_page->lblMode->hide();
311     m_page->pixel->hide();
312     m_page->shape->hide();
313 }
314 
315