1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 
8 #include "propertywidget_distance.h"
9 
10 #include <QSignalBlocker>
11 
12 #include "appmodehelper.h"
13 #include "appmodes.h"
14 #include "localemgr.h"
15 #include "pageitem_table.h"
16 #include "pageitem_textframe.h"
17 #include "scribus.h"
18 #include "scribusapp.h"
19 #include "scribusdoc.h"
20 #include "selection.h"
21 #include "tabmanager.h"
22 
PropertyWidget_Distance(QWidget * parent)23 PropertyWidget_Distance::PropertyWidget_Distance(QWidget* parent) : QFrame(parent)
24 {
25 	setupUi(this);
26 
27 	layout()->setAlignment(Qt::AlignTop);
28 
29 	columnsLabel->setBuddy(columns);
30 	columnGap->setValues(0, 300, 2, 0);
31 
32 	topDistance->setValues(0, 300, 2, 0);
33 	topLabel->setBuddy(topDistance);
34 
35 	bottomDistance->setValues(0, 300, 2, 0);
36 	bottomLabel->setBuddy(bottomDistance);
37 
38 	leftDistance->setValues(0, 300, 2, 0);
39 	leftLabel->setBuddy(leftDistance);
40 
41 	rightDistance->setValues(0, 300, 2, 0);
42 	rightLabel->setBuddy(rightDistance);
43 
44 	columns->setDecimals(0);
45 	columns->setSuffix("");
46 
47 	languageChange();
48 
49 	columnGapLabel->setCurrentIndex(0);
50 
51 	connect(ScQApp, SIGNAL(localeChanged()), this, SLOT(localeChange()));
52 }
53 
setMainWindow(ScribusMainWindow * mw)54 void PropertyWidget_Distance::setMainWindow(ScribusMainWindow* mw)
55 {
56 	m_ScMW = mw;
57 
58 	connect(m_ScMW->appModeHelper, SIGNAL(AppModeChanged(int, int)), this, SLOT(handleAppModeChanged(int, int)));
59 	connect(m_ScMW, SIGNAL(UpdateRequest(int))      , this, SLOT(handleUpdateRequest(int)));
60 }
61 
setDoc(ScribusDoc * d)62 void PropertyWidget_Distance::setDoc(ScribusDoc *d)
63 {
64 	if(d == (ScribusDoc*) m_doc)
65 		return;
66 
67 	if (m_doc)
68 	{
69 		disconnect(m_doc->m_Selection, SIGNAL(selectionChanged()), this, SLOT(handleSelectionChanged()));
70 		disconnect(m_doc             , SIGNAL(docChanged())      , this, SLOT(handleSelectionChanged()));
71 	}
72 
73 	m_doc  = d;
74 	m_item = nullptr;
75 
76 	if (m_doc.isNull())
77 	{
78 		disconnectSignals();
79 		return;
80 	}
81 
82 	m_unitRatio   = m_doc->unitRatio();
83 	m_unitIndex   = m_doc->unitIndex();
84 
85 	columns->setDecimals(0);
86 	columnGap->setDecimals(2);
87 	topDistance->setDecimals(2);
88 	leftDistance->setDecimals(2);
89 	bottomDistance->setDecimals(2);
90 	rightDistance->setDecimals(2);
91 
92 	connect(m_doc->m_Selection, SIGNAL(selectionChanged()), this, SLOT(handleSelectionChanged()));
93 	connect(m_doc             , SIGNAL(docChanged())      , this, SLOT(handleSelectionChanged()));
94 }
95 
setCurrentItem(PageItem * item)96 void PropertyWidget_Distance::setCurrentItem(PageItem *item)
97 {
98 	if (!m_ScMW || m_ScMW->scriptIsRunning())
99 		return;
100 	//CB We shouldn't really need to process this if our item is the same one
101 	//maybe we do if the item has been changed by scripter.. but that should probably
102 	//set some status if so.
103 	//FIXME: This won't work until when a canvas deselect happens, m_item must be nullptr.
104 	//if (m_item == i)
105 	//	return;
106 
107 	if (item && m_doc.isNull())
108 		setDoc(item->doc());
109 
110 	m_item = item;
111 
112 	disconnectSignals();
113 	configureWidgets();
114 
115 	if (!m_item) return;
116 
117 	PageItem_TextFrame *textItem = m_item->asTextFrame();
118 	if (m_doc->appMode == modeEditTable)
119 		textItem = m_item->asTable()->activeCell().textFrame();
120 	if (!textItem) return;
121 
122 	//#14427: columns->setMaximum(qMax(qRound(textItem->width() / qMax(textItem->ColGap, 10.0)), 1));
123 	columns->setMinimum(1);
124 	columns->setValue(textItem->m_columns);
125 	columnGap->setMinimum(0);
126 	if (columnGapLabel->currentIndex() == 0)
127 	{
128 		columnGap->setMaximum(qMax((textItem->width() / textItem->m_columns - textItem->textToFrameDistLeft() - textItem->textToFrameDistRight()) * m_unitRatio, 0.0));
129 		columnGap->setValue(textItem->m_columnGap * m_unitRatio);
130 	}
131 	else
132 	{
133 		columnGap->setMaximum(qMax((textItem->width() / textItem->m_columns) * m_unitRatio, 0.0));
134 		columnGap->setValue(textItem->columnWidth() * m_unitRatio);
135 	}
136 	leftDistance->setValue(textItem->textToFrameDistLeft()*m_unitRatio);
137 	topDistance->setValue(textItem->textToFrameDistTop()*m_unitRatio);
138 	bottomDistance->setValue(textItem->textToFrameDistBottom()*m_unitRatio);
139 	rightDistance->setValue(textItem->textToFrameDistRight()*m_unitRatio);
140 	if (columns->value() == 1)
141 	{
142 		columnGap->setEnabled(false);
143 		columnGapLabel->setEnabled(false);
144 	}
145 	else
146 	{
147 		columnGap->setEnabled(true);
148 		columnGapLabel->setEnabled(true);
149 	}
150 
151 	showTextDistances(textItem->textToFrameDistLeft(), textItem->textToFrameDistTop(), textItem->textToFrameDistBottom(), textItem->textToFrameDistRight());
152 	verticalAlign->setCurrentIndex(textItem->verticalAlignment());
153 	connectSignals();
154 }
155 
connectSignals()156 void PropertyWidget_Distance::connectSignals()
157 {
158 	connect(columns       , SIGNAL(valueChanged(double))   , this, SLOT(handleColumns()), Qt::UniqueConnection);
159 	connect(columnGap     , SIGNAL(valueChanged(double)), this, SLOT(handleColumnGap()), Qt::UniqueConnection);
160 	connect(columnGapLabel, SIGNAL(activated(int))      , this, SLOT(handleGapSwitch()), Qt::UniqueConnection);
161 	connect(topDistance   , SIGNAL(valueChanged(double)), this, SLOT(handleTextDistances()), Qt::UniqueConnection);
162 	connect(leftDistance  , SIGNAL(valueChanged(double)), this, SLOT(handleTextDistances()), Qt::UniqueConnection);
163 	connect(rightDistance , SIGNAL(valueChanged(double)), this, SLOT(handleTextDistances()), Qt::UniqueConnection);
164 	connect(bottomDistance, SIGNAL(valueChanged(double)), this, SLOT(handleTextDistances()), Qt::UniqueConnection);
165 	connect(tabsButton    , SIGNAL(clicked())           , this, SLOT(handleTabs()), Qt::UniqueConnection);
166 	connect(verticalAlign , SIGNAL(activated(int))      , this, SLOT(handleVAlign()), Qt::UniqueConnection);
167 }
168 
disconnectSignals()169 void PropertyWidget_Distance::disconnectSignals()
170 {
171 	disconnect(columns       , SIGNAL(valueChanged(double))   , this, SLOT(handleColumns()));
172 	disconnect(columnGap     , SIGNAL(valueChanged(double)), this, SLOT(handleColumnGap()));
173 	disconnect(columnGapLabel, SIGNAL(activated(int))      , this, SLOT(handleGapSwitch()));
174 	disconnect(topDistance   , SIGNAL(valueChanged(double)), this, SLOT(handleTextDistances()));
175 	disconnect(leftDistance  , SIGNAL(valueChanged(double)), this, SLOT(handleTextDistances()));
176 	disconnect(rightDistance , SIGNAL(valueChanged(double)), this, SLOT(handleTextDistances()));
177 	disconnect(bottomDistance, SIGNAL(valueChanged(double)), this, SLOT(handleTextDistances()));
178 	disconnect(tabsButton    , SIGNAL(clicked())           , this, SLOT(handleTabs()));
179 	disconnect(verticalAlign , SIGNAL(activated(int))      , this, SLOT(handleVAlign()));
180 }
181 
configureWidgets()182 void PropertyWidget_Distance::configureWidgets()
183 {
184 	bool enabled = false;
185 	if (m_item && m_doc)
186 	{
187 		PageItem_TextFrame *textItem = m_item->asTextFrame();
188 		if (m_doc->appMode == modeEditTable)
189 			textItem = m_item->asTable()->activeCell().textFrame();
190 
191 		enabled  = (textItem != nullptr);
192 		enabled &= (m_doc->m_Selection->count() == 1);
193 
194 		if (textItem)
195 		{
196 			int numCols = textItem->m_columns;
197 
198 			columnGap->setEnabled(numCols != 1);
199 			columnGapLabel->setEnabled(numCols != 1);
200 		}
201 	}
202 	setEnabled(enabled);
203 }
204 
handleAppModeChanged(int oldMode,int mode)205 void PropertyWidget_Distance::handleAppModeChanged(int oldMode, int mode)
206 {
207 	if (oldMode == modeEditTable || mode == modeEditTable)
208 	{
209 		setCurrentItem(m_item);
210 	}
211 }
212 
handleSelectionChanged()213 void PropertyWidget_Distance::handleSelectionChanged()
214 {
215 	if (!m_doc || !m_ScMW || m_ScMW->scriptIsRunning())
216 		return;
217 
218 	PageItem* currItem = currentItemFromSelection();
219 	setCurrentItem(currItem);
220 	updateGeometry();
221 }
222 
handleUpdateRequest(int)223 void PropertyWidget_Distance::handleUpdateRequest(int /*updateFlags*/)
224 {
225 	// Nothing to do in this widget
226 }
227 
showColumns(int r,double g)228 void PropertyWidget_Distance::showColumns(int r, double g)
229 {
230 	if (!m_ScMW || m_ScMW->scriptIsRunning())
231 		return;
232 
233 	bool cSigWasBlocked    = columns->blockSignals(true);
234 	bool cGapSigWasBlocked = columnGap->blockSignals(true);
235 
236 	columns->setValue(r);
237 	columnGap->setValue(g * m_unitRatio);
238 	if (m_item)
239 	{
240 		PageItem_TextFrame *textItem = m_item->asTextFrame();
241 		if (m_doc->appMode == modeEditTable)
242 			textItem = m_item->asTable()->activeCell().textFrame();
243 
244 		if (textItem != nullptr)
245 		{
246 //#14427: columns->setMaximum(qMax(qRound(textItem->width() / qMax(textItem->ColGap, 10.0)), 1));
247 			if (columnGapLabel->currentIndex() == 0)
248 			{
249 				columnGap->setMaximum(qMax((textItem->width() / textItem->m_columns - textItem->textToFrameDistLeft() - textItem->textToFrameDistRight()) * m_unitRatio, 0.0));
250 				columnGap->setValue(textItem->m_columnGap * m_unitRatio);
251 			}
252 			else
253 			{
254 				columnGap->setMaximum(qMax((textItem->width() / textItem->m_columns) * m_unitRatio, 0.0));
255 				columnGap->setValue(textItem->columnWidth() * m_unitRatio);
256 			}
257 		}
258 	}
259 	columns->setMinimum(1);
260 	columnGap->setMinimum(0);
261 	columnGap->setEnabled(columns->value() != 1);
262 	columnGapLabel->setEnabled(columns->value() != 1);
263 
264 	columns->blockSignals(cSigWasBlocked);
265 	columnGap->blockSignals(cGapSigWasBlocked);
266 }
267 
showTextDistances(double left,double top,double bottom,double right)268 void PropertyWidget_Distance::showTextDistances(double left, double top, double bottom, double right)
269 {
270 	leftDistance->showValue(left * m_unitRatio);
271 	topDistance->showValue(top * m_unitRatio);
272 	bottomDistance->showValue(bottom * m_unitRatio);
273 	rightDistance->showValue(right * m_unitRatio);
274 }
275 
handleColumns()276 void PropertyWidget_Distance::handleColumns()
277 {
278 	if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
279 		return;
280 
281 	PageItem *textItem = m_item;
282 	if (m_doc->appMode == modeEditTable)
283 		textItem = m_item->asTable()->activeCell().textFrame();
284 
285 	if (textItem)
286 	{
287 		textItem->setColumns(static_cast<int>(columns->value()));
288 		showColumns(textItem->m_columns, textItem->m_columnGap);
289 		//this is already done in showColumns()
290 		/*if (static_cast<int>(columns->value()) == 1)
291 		{
292 			columnGap->setEnabled(false);
293 			columnGapLabel->setEnabled(false);
294 		}
295 		else
296 		{
297 			columnGap->setEnabled(true);
298 			columnGapLabel->setEnabled(true);
299 		}*/
300 		textItem->update();
301 		if (m_doc->appMode == modeEditTable)
302 			m_item->asTable()->update();
303 		m_doc->regionsChanged()->update(QRect());
304 	}
305 }
306 
handleColumnGap()307 void PropertyWidget_Distance::handleColumnGap()
308 {
309 	if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
310 		return;
311 
312 	PageItem *textItem = m_item;
313 	if (m_doc->appMode == modeEditTable)
314 		textItem = m_item->asTable()->activeCell().textFrame();
315 	if (!textItem)
316 		return;
317 
318 	if (columnGapLabel->currentIndex() == 0)
319 		textItem->setColumnGap(columnGap->value() / m_unitRatio);
320 	else
321 	{
322 		double lineCorr=0.0;
323 		if ((textItem->lineColor() != CommonStrings::None) || (!textItem->strokePattern().isEmpty()))
324 			lineCorr = textItem->lineWidth();
325 		double newWidth = columnGap->value() / m_unitRatio;
326 		double newGap = qMax(((textItem->width() - textItem->textToFrameDistLeft() - textItem->textToFrameDistRight() - lineCorr) - (newWidth * textItem->m_columns)) / (textItem->m_columns - 1), 0.0);
327 		textItem->setColumnGap(newGap);
328 	}
329 	textItem->update();
330 	if (m_doc->appMode == modeEditTable)
331 		m_item->asTable()->update();
332 	m_doc->regionsChanged()->update(QRect());
333 }
334 
handleGapSwitch()335 void PropertyWidget_Distance::handleGapSwitch()
336 {
337 	if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
338 		return;
339 	PageItem *textItem = m_item;
340 	if (m_doc->appMode == modeEditTable)
341 		textItem = m_item->asTable()->activeCell().textFrame();
342 	if (textItem != nullptr)
343 		showColumns(textItem->m_columns, textItem->m_columnGap);
344 
345 	int index = columnGapLabel->currentIndex();
346 	columnGap->setToolTip((index == 0) ? tr( "Distance between columns" ) : tr( "Column width" ));
347 }
348 
handleVAlign()349 void PropertyWidget_Distance::handleVAlign()
350 {
351 	if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
352 		return;
353 	PageItem *textItem = m_item;
354 	if (m_doc->appMode == modeEditTable)
355 		textItem = m_item->asTable()->activeCell().textFrame();
356 	if (textItem != nullptr)
357 	{
358 		textItem->setVerticalAlignment(verticalAlign->currentIndex());
359 		textItem->update();
360 		if (m_doc->appMode == modeEditTable)
361 			m_item->asTable()->update();
362 		m_doc->regionsChanged()->update(QRect());
363 	}
364 }
365 
handleTabs()366 void PropertyWidget_Distance::handleTabs()
367 {
368 	if (!m_doc || !m_item)
369 		return;
370 
371 	PageItem_TextFrame *tItem = m_item->asTextFrame();
372 	if (tItem == nullptr)
373 		return;
374 	const ParagraphStyle& style(m_doc->appMode == modeEdit ? tItem->currentStyle() : tItem->itemText.defaultStyle());
375 	TabManager *dia = new TabManager(this, m_doc->unitIndex(), style.tabValues(), tItem->columnWidth());
376 	if (dia->exec())
377 	{
378 		if (m_doc->appMode != modeEdit)
379 		{
380 			ParagraphStyle newStyle(m_item->itemText.defaultStyle());
381 			newStyle.setTabValues(dia->tabList());
382 			Selection tempSelection(this, false);
383 			tempSelection.addItem(m_item, true);
384 			m_doc->itemSelection_ApplyParagraphStyle(newStyle, &tempSelection);
385 		}
386 		else
387 		{
388 			ParagraphStyle newStyle;
389 			newStyle.setTabValues(dia->tabList());
390 			m_doc->itemSelection_ApplyParagraphStyle(newStyle);
391 		}
392 		m_item->update();
393 	}
394 	delete dia;
395 }
396 
handleTextDistances()397 void PropertyWidget_Distance::handleTextDistances()
398 {
399 	if (!m_doc || !m_item || !m_ScMW || m_ScMW->scriptIsRunning())
400 		return;
401 
402 	PageItem *textItem = m_item;
403 	if (m_doc->appMode == modeEditTable)
404 		textItem = m_item->asTable()->activeCell().textFrame();
405 	if (!textItem) return;
406 
407 	double left   = leftDistance->value() / m_unitRatio;
408 	double right  = rightDistance->value() / m_unitRatio;
409 	double top    = topDistance->value() / m_unitRatio;
410 	double bottom = bottomDistance->value() / m_unitRatio;
411 	textItem->setTextToFrameDist(left, right, top, bottom);
412 	showColumns(textItem->m_columns, textItem->m_columnGap);
413 
414 	textItem->update();
415 	if (m_doc->appMode == modeEditTable)
416 		m_item->asTable()->update();
417 	m_doc->regionsChanged()->update(QRect());
418 }
419 
changeEvent(QEvent * e)420 void PropertyWidget_Distance::changeEvent(QEvent *e)
421 {
422 	if (e->type() == QEvent::LanguageChange)
423 	{
424 		languageChange();
425 		return;
426 	}
427 	QWidget::changeEvent(e);
428 }
429 
languageChange()430 void PropertyWidget_Distance::languageChange()
431 {
432 	retranslateUi(this);
433 
434 	QSignalBlocker verticalAlignBlocker(verticalAlign);
435 	int oldAlignIndex = verticalAlign->currentIndex();
436 	verticalAlign->clear();
437 	verticalAlign->addItem( tr("Top"));
438 	verticalAlign->addItem( tr("Middle"));
439 	verticalAlign->addItem( tr("Bottom"));
440 	verticalAlign->setCurrentIndex(oldAlignIndex);
441 
442 	QSignalBlocker columnGapLabelBlocker(columnGapLabel);
443 	int oldColGapLabel = columnGapLabel->currentIndex();
444 	columnGapLabel->clear();
445 	columnGapLabel->addItem( tr("Gap:"));
446 	columnGapLabel->addItem( tr("Width:"));
447 	columnGapLabel->setCurrentIndex(oldColGapLabel);
448 
449 	QString ptSuffix = tr(" pt");
450 
451 	QString suffix = (m_doc) ? unitGetSuffixFromIndex(m_doc->unitIndex()) : ptSuffix;
452 
453 	columnGap->setSuffix(suffix);
454 	leftDistance->setSuffix(suffix);
455 	topDistance->setSuffix(suffix);
456 	bottomDistance->setSuffix(suffix);
457 	rightDistance->setSuffix(suffix);
458 }
459 
unitChange()460 void PropertyWidget_Distance::unitChange()
461 {
462 	if (!m_doc)
463 		return;
464 
465 	QSignalBlocker columnGapBlocker(columnGap);
466 	QSignalBlocker leftDistanceBlocker(leftDistance);
467 	QSignalBlocker topDistanceBlocker(topDistance);
468 	QSignalBlocker bottomDistanceBlocker(bottomDistance);
469 	QSignalBlocker rightDistanceBlocker(rightDistance);
470 
471 	m_unitRatio = m_doc->unitRatio();
472 	m_unitIndex = m_doc->unitIndex();
473 
474 	columnGap->setNewUnit(m_unitIndex);
475 	leftDistance->setNewUnit(m_unitIndex);
476 	topDistance->setNewUnit(m_unitIndex);
477 	bottomDistance->setNewUnit(m_unitIndex);
478 	rightDistance->setNewUnit(m_unitIndex);
479 }
480 
localeChange()481 void PropertyWidget_Distance::localeChange()
482 {
483 	const QLocale& l(LocaleManager::instance().userPreferredLocale());
484 	columnGap->setLocale(l);
485 	topDistance->setLocale(l);
486 	bottomDistance->setLocale(l);
487 	leftDistance->setLocale(l);
488 	rightDistance->setLocale(l);
489 }
490