magstructfact.cpp 71.5 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
/**
 * magnetic structure factor tool
 * @author Tobias Weber <tweber@ill.fr>
 * @date Jan-2019
 * @license GPLv3, see 'LICENSE' file
 * @desc The present version was forked on 28-Dec-2018 from the privately developed "misc" project (https://github.com/t-weber/misc).
 */

#include "magstructfact.h"

#include <QtWidgets/QApplication>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHeaderView>
#include <QtWidgets/QTabWidget>
#include <QtWidgets/QLabel>
#include <QtWidgets/QToolButton>
#include <QtWidgets/QFileDialog>
#include <QtWidgets/QMessageBox>

#include <iostream>
#include <fstream>
#include <random>
#include <chrono>
#include <tuple>

#include <boost/version.hpp>
#include <boost/config.hpp>
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/units/systems/si/codata/electron_constants.hpp>
#include <boost/units/systems/si/codata/neutron_constants.hpp>
#include <boost/units/systems/si/codata/electromagnetic_constants.hpp>
namespace algo = boost::algorithm;
namespace pt = boost::property_tree;
namespace si = boost::units::si;
namespace consts = si::constants;

#include "../structfact/loadcif.h"
#include "libs/algos.h"
#include "libs/helper.h"


//using namespace m;
using namespace m_ops;


constexpr t_real g_eps = 1e-6;
constexpr int g_prec = 6;


53
// columns of Fourier components table
54
55
56
enum : int
{
	COL_NAME = 0,
Tobias WEBER's avatar
Tobias WEBER committed
57
58
59
	COL_X, COL_Y, COL_Z,				// position
	COL_M_MAG,							// scale factor of FC
	COL_ReM_X, COL_ReM_Y, COL_ReM_Z,	// fourier components
60
	COL_ImM_X, COL_ImM_Y, COL_ImM_Z,
Tobias WEBER's avatar
Tobias WEBER committed
61
62
	COL_RAD,							// drawing radius
	COL_COL,							// colour
63
64
65
66
67

	NUM_COLS
};


68
69
70
71
// columns of propagation vectors table
enum : int
{
	PROP_COL_NAME = 0,
Tobias WEBER's avatar
Tobias WEBER committed
72
73
	PROP_COL_X, PROP_COL_Y, PROP_COL_Z,	// propagation direction
	PROP_COL_CONJ,						// conjugate fourier component for this propagation vector?
74
75
76
77
78

	PROP_NUM_COLS
};


79
80
struct PowderLine
{
81
82
83
	t_real Q{};
	t_real I{};
	std::size_t num_peaks = 0;
84
85
86
87
88
89
90
91
92
93
94
95
96
97
	std::string peaks;
};


// ----------------------------------------------------------------------------
MagStructFactDlg::MagStructFactDlg(QWidget* pParent) : QDialog{pParent},
	m_sett{new QSettings{"tobis_stuff", "magstructfact"}}
{
	setWindowTitle("Magnetic Structure Factors");
	setSizeGripEnabled(true);
	setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));


	auto tabs = new QTabWidget(this);
98
99
100


	{	// fourier components panel
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
		m_nucleipanel = new QWidget(this);

		m_nuclei = new QTableWidget(m_nucleipanel);
		m_nuclei->setShowGrid(true);
		m_nuclei->setSortingEnabled(true);
		m_nuclei->setMouseTracking(true);
		m_nuclei->setSelectionBehavior(QTableWidget::SelectRows);
		m_nuclei->setSelectionMode(QTableWidget::ContiguousSelection);
		m_nuclei->setContextMenuPolicy(Qt::CustomContextMenu);

		m_nuclei->verticalHeader()->setDefaultSectionSize(fontMetrics().lineSpacing() + 4);
		m_nuclei->verticalHeader()->setVisible(false);

		m_nuclei->setColumnCount(NUM_COLS);
		m_nuclei->setHorizontalHeaderItem(COL_NAME, new QTableWidgetItem{"Name"});
		m_nuclei->setHorizontalHeaderItem(COL_X, new QTableWidgetItem{"x (frac.)"});
		m_nuclei->setHorizontalHeaderItem(COL_Y, new QTableWidgetItem{"y (frac.)"});
		m_nuclei->setHorizontalHeaderItem(COL_Z, new QTableWidgetItem{"z (frac.)"});
119
		m_nuclei->setHorizontalHeaderItem(COL_M_MAG, new QTableWidgetItem{"|M|"});
120
121
122
123
124
125
		m_nuclei->setHorizontalHeaderItem(COL_ReM_X, new QTableWidgetItem{"Re{FC_x}"});
		m_nuclei->setHorizontalHeaderItem(COL_ReM_Y, new QTableWidgetItem{"Re{FC_y}"});
		m_nuclei->setHorizontalHeaderItem(COL_ReM_Z, new QTableWidgetItem{"Re{FC_z}"});
		m_nuclei->setHorizontalHeaderItem(COL_ImM_X, new QTableWidgetItem{"Im{FC_x}"});
		m_nuclei->setHorizontalHeaderItem(COL_ImM_Y, new QTableWidgetItem{"Im{FC_y}"});
		m_nuclei->setHorizontalHeaderItem(COL_ImM_Z, new QTableWidgetItem{"Im{FC_z}"});
Tobias WEBER's avatar
Tobias WEBER committed
126
		m_nuclei->setHorizontalHeaderItem(COL_RAD, new QTableWidgetItem{"Scale"});
127
128
129
130
131
132
		m_nuclei->setHorizontalHeaderItem(COL_COL, new QTableWidgetItem{"Colour"});

		m_nuclei->setColumnWidth(COL_NAME, 90);
		m_nuclei->setColumnWidth(COL_X, 75);
		m_nuclei->setColumnWidth(COL_Y, 75);
		m_nuclei->setColumnWidth(COL_Z, 75);
133
		m_nuclei->setColumnWidth(COL_M_MAG, 75);
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
		m_nuclei->setColumnWidth(COL_ReM_X, 75);
		m_nuclei->setColumnWidth(COL_ReM_Y, 75);
		m_nuclei->setColumnWidth(COL_ReM_Z, 75);
		m_nuclei->setColumnWidth(COL_ImM_X, 75);
		m_nuclei->setColumnWidth(COL_ImM_Y, 75);
		m_nuclei->setColumnWidth(COL_ImM_Z, 75);
		m_nuclei->setColumnWidth(COL_RAD, 75);
		m_nuclei->setColumnWidth(COL_COL, 75);

		QToolButton *pTabBtnAdd = new QToolButton(m_nucleipanel);
		QToolButton *pTabBtnDel = new QToolButton(m_nucleipanel);
		QToolButton *pTabBtnUp = new QToolButton(m_nucleipanel);
		QToolButton *pTabBtnDown = new QToolButton(m_nucleipanel);
		QToolButton *pTabBtnSG = new QToolButton(m_nucleipanel);

		m_nuclei->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});
		pTabBtnAdd->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnDel->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnUp->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnDown->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnSG->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});

156
157
158
159
		pTabBtnAdd->setText("Add Fourier Comp.");
		pTabBtnDel->setText("Delete Fourier Comp.");
		pTabBtnUp->setText("Move Fourier Comp. Up");
		pTabBtnDown->setText("Move Fourier Comp. Down");
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
		pTabBtnSG->setText("Generate");

		m_editA = new QLineEdit("5", m_nucleipanel);
		m_editB = new QLineEdit("5", m_nucleipanel);
		m_editC = new QLineEdit("5", m_nucleipanel);
		m_editAlpha = new QLineEdit("90", m_nucleipanel);
		m_editBeta = new QLineEdit("90", m_nucleipanel);
		m_editGamma = new QLineEdit("90", m_nucleipanel);

		m_comboSG = new QComboBox(m_nucleipanel);


		// get space groups and symops
		for(auto [sgnum, descr, ops] : get_sgs<t_mat>())
		{
			m_comboSG->addItem(descr.c_str(), m_comboSG->count());
			m_SGops.emplace_back(std::move(ops));
		}


		auto pTabGrid = new QGridLayout(m_nucleipanel);
		pTabGrid->setSpacing(2);
		pTabGrid->setContentsMargins(4,4,4,4);
Tobias WEBER's avatar
Tobias WEBER committed
183
		int y = 0;
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
		//pTabGrid->addWidget(m_plot.get(), y,0,1,4);
		pTabGrid->addWidget(m_nuclei, y,0,1,4);
		pTabGrid->addWidget(pTabBtnAdd, ++y,0,1,1);
		pTabGrid->addWidget(pTabBtnDel, y,1,1,1);
		pTabGrid->addWidget(pTabBtnUp, y,2,1,1);
		pTabGrid->addWidget(pTabBtnDown, y,3,1,1);

		pTabGrid->addWidget(new QLabel("Space Groups:"), ++y,0,1,1);
		pTabGrid->addWidget(m_comboSG, y,1,1,2);
		pTabGrid->addWidget(pTabBtnSG, y,3,1,1);


		auto sep1 = new QFrame(m_nucleipanel); sep1->setFrameStyle(QFrame::HLine);
		pTabGrid->addWidget(sep1, ++y,0, 1,4);

		pTabGrid->addWidget(new QLabel("Lattice (A):"), ++y,0,1,1);
		pTabGrid->addWidget(m_editA, y,1,1,1);
		pTabGrid->addWidget(m_editB, y,2,1,1);
		pTabGrid->addWidget(m_editC, y,3,1,1);
		pTabGrid->addWidget(new QLabel("Angles (deg):"), ++y,0,1,1);
		pTabGrid->addWidget(m_editAlpha, y,1,1,1);
		pTabGrid->addWidget(m_editBeta, y,2,1,1);
		pTabGrid->addWidget(m_editGamma, y,3,1,1);


		// table CustomContextMenu
210
		QMenu *pTabContextMenu = new QMenu(m_nuclei);
211
212
213
214
		pTabContextMenu->addAction("Add Fourier Component Before", this, [this]() { this->AddTabItem(-2); });
		pTabContextMenu->addAction("Add Fourier Component After", this, [this]() { this->AddTabItem(-3); });
		pTabContextMenu->addAction("Clone Fourier Component", this, [this]() { this->AddTabItem(-4); });
		pTabContextMenu->addAction("Delete Fourier Component", this, [this]() { this->DelTabItem(); });
215
216
217


		// table CustomContextMenu in case nothing is selected
218
		QMenu *pTabContextMenuNoItem = new QMenu(m_nuclei);
219
220
		pTabContextMenuNoItem->addAction("Add Fourier Component", this, [this]() { this->AddTabItem(); });
		pTabContextMenuNoItem->addAction("Delete Fourier Component", this, [this]() { this->DelTabItem(); });
221
		//pTabContextMenuNoItem->addSeparator();
222
223
224
225
226
227
228


		// signals
		for(auto* edit : std::vector<QLineEdit*>{{ m_editA, m_editB, m_editC, m_editAlpha, m_editBeta, m_editGamma }})
			connect(edit, &QLineEdit::textEdited, this, [this]() { this->CalcB(); });

		connect(pTabBtnAdd, &QToolButton::clicked, this, [this]() { this->AddTabItem(-1); });
229
230
231
		connect(pTabBtnDel, &QToolButton::clicked, this, [this]() { this->DelTabItem(); });
		connect(pTabBtnUp, &QToolButton::clicked, this, [this]() { this->MoveTabItemUp(m_nuclei); });
		connect(pTabBtnDown, &QToolButton::clicked, this, [this]() { this->MoveTabItemDown(m_nuclei); });
232
233
234
235
236
		connect(pTabBtnSG, &QToolButton::clicked, this, &MagStructFactDlg::GenerateFromSG);

		connect(m_nuclei, &QTableWidget::currentCellChanged, this, &MagStructFactDlg::TableCurCellChanged);
		connect(m_nuclei, &QTableWidget::entered, this, &MagStructFactDlg::TableCellEntered);
		connect(m_nuclei, &QTableWidget::itemChanged, this, &MagStructFactDlg::TableItemChanged);
237
238
239
240
241
242
243
244
		connect(m_nuclei, &QTableWidget::customContextMenuRequested, this, 
			[this, pTabContextMenu, pTabContextMenuNoItem](const QPoint& pt)
			{ this->ShowTableContextMenu(m_nuclei, pTabContextMenu, pTabContextMenuNoItem, pt); });

		tabs->addTab(m_nucleipanel, "Fourier Components");
	}


245
	{	// propagation vectors panel
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
		m_propvecpanel = new QWidget(this);

		m_propvecs = new QTableWidget(m_propvecpanel);
		m_propvecs->setShowGrid(true);
		m_propvecs->setSortingEnabled(true);
		m_propvecs->setMouseTracking(true);
		m_propvecs->setSelectionBehavior(QTableWidget::SelectRows);
		m_propvecs->setSelectionMode(QTableWidget::ContiguousSelection);
		m_propvecs->setContextMenuPolicy(Qt::CustomContextMenu);

		m_propvecs->verticalHeader()->setDefaultSectionSize(fontMetrics().lineSpacing() + 4);
		m_propvecs->verticalHeader()->setVisible(false);

		m_propvecs->setColumnCount(PROP_NUM_COLS);
		m_propvecs->setHorizontalHeaderItem(PROP_COL_NAME, new QTableWidgetItem{"Name"});
		m_propvecs->setHorizontalHeaderItem(PROP_COL_X, new QTableWidgetItem{"x (frac.)"});
		m_propvecs->setHorizontalHeaderItem(PROP_COL_Y, new QTableWidgetItem{"y (frac.)"});
		m_propvecs->setHorizontalHeaderItem(PROP_COL_Z, new QTableWidgetItem{"z (frac.)"});
Tobias WEBER's avatar
Tobias WEBER committed
264
		m_propvecs->setHorizontalHeaderItem(PROP_COL_CONJ, new QTableWidgetItem{"FC*"});
265
266
267
268
269

		m_propvecs->setColumnWidth(PROP_COL_NAME, 90);
		m_propvecs->setColumnWidth(PROP_COL_X, 90);
		m_propvecs->setColumnWidth(PROP_COL_Y, 90);
		m_propvecs->setColumnWidth(PROP_COL_Z, 90);
Tobias WEBER's avatar
Tobias WEBER committed
270
		m_propvecs->setColumnWidth(PROP_COL_CONJ, 75);
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287

		QToolButton *pTabBtnAdd = new QToolButton(m_propvecpanel);
		QToolButton *pTabBtnDel = new QToolButton(m_propvecpanel);
		QToolButton *pTabBtnUp = new QToolButton(m_propvecpanel);
		QToolButton *pTabBtnDown = new QToolButton(m_propvecpanel);

		m_propvecs->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});
		pTabBtnAdd->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnDel->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnUp->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});
		pTabBtnDown->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Fixed});

		pTabBtnAdd->setText("Add Vector");
		pTabBtnDel->setText("Delete Vector");
		pTabBtnUp->setText("Move Vector Up");
		pTabBtnDown->setText("Move Vector Down");

288

289
290
291
		auto pTabGrid = new QGridLayout(m_propvecpanel);
		pTabGrid->setSpacing(2);
		pTabGrid->setContentsMargins(4,4,4,4);
Tobias WEBER's avatar
Tobias WEBER committed
292
		int y = 0;
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
		pTabGrid->addWidget(m_propvecs, y,0,1,4);
		pTabGrid->addWidget(pTabBtnAdd, ++y,0,1,1);
		pTabGrid->addWidget(pTabBtnDel, y,1,1,1);
		pTabGrid->addWidget(pTabBtnUp, y,2,1,1);
		pTabGrid->addWidget(pTabBtnDown, y,3,1,1);


		// table CustomContextMenu
		QMenu *pPropContextMenu = new QMenu(m_propvecs);
		pPropContextMenu->addAction("Add Vector Before", this, [this]() { this->AddPropItem(-2); });
		pPropContextMenu->addAction("Add Vector After", this, [this]() { this->AddPropItem(-3); });
		pPropContextMenu->addAction("Clone Vector", this, [this]() { this->AddPropItem(-4); });
		pPropContextMenu->addAction("Delete Vector", this, [this]() { this->DelPropItem(); });


		// table CustomContextMenu in case nothing is selected
		QMenu *pPropContextMenuNoItem = new QMenu(m_propvecs);
		pPropContextMenuNoItem->addAction("Add Vector", this, [this]() { this->AddPropItem(); });
		pPropContextMenuNoItem->addAction("Delete Vector", this, [this]() { this->DelPropItem(); });



		connect(pTabBtnAdd, &QToolButton::clicked, this, [this]() { this->AddPropItem(-1); });
		connect(pTabBtnDel, &QToolButton::clicked, this, [this]() { this->DelPropItem(); });
		connect(pTabBtnUp, &QToolButton::clicked, this,  [this]() { this->MoveTabItemUp(m_propvecs); });
		connect(pTabBtnDown, &QToolButton::clicked, this,  [this]() { this->MoveTabItemUp(m_propvecs); });

		//connect(m_propvecs, &QTableWidget::currentCellChanged, this, &MagStructFactDlg::PropCurCellChanged);
		//connect(m_propvecs, &QTableWidget::entered, this, &MagStructFactDlg::PropCellEntered);
		connect(m_propvecs, &QTableWidget::itemChanged, this, &MagStructFactDlg::PropItemChanged);
		connect(m_propvecs, &QTableWidget::customContextMenuRequested, this, 
			[this, pPropContextMenu, pPropContextMenuNoItem](const QPoint& pt)
			{ this->ShowTableContextMenu(m_propvecs, pPropContextMenu, pPropContextMenuNoItem, pt); });

		tabs->addTab(m_propvecpanel, "Propagation Vectors");
328
329
330
331
332
333
334
335
336
337
338
339
	}


	{	// structure factors panel
		auto sfactpanel = new QWidget(this);
		auto pGrid = new QGridLayout(sfactpanel);
		pGrid->setSpacing(4);
		pGrid->setContentsMargins(4,4,4,4);

		m_structfacts = new QPlainTextEdit(sfactpanel);
		m_structfacts->setReadOnly(true);
		m_structfacts->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
340
		m_structfacts->setLineWrapMode(QPlainTextEdit::NoWrap);
341
342
343
344
345
346

		m_maxBZ = new QSpinBox(sfactpanel);
		m_maxBZ->setMinimum(0);
		m_maxBZ->setMaximum(99);
		m_maxBZ->setValue(4);

347
348
349
350
		m_RemoveZeroes = new QCheckBox("Remove Zeroes", sfactpanel);
		m_RemoveZeroes->setChecked(true);


351
		pGrid->addWidget(m_structfacts, 0,0, 1,4);
352
		pGrid->addWidget(new QLabel("Max. Order:"), 1,0,1,1);
353
		pGrid->addWidget(m_maxBZ, 1,1, 1,1);
354
355
		pGrid->addWidget(m_RemoveZeroes, 1,2, 1,2);

356
357
358

		// signals
		connect(m_maxBZ, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this]() { this->Calc(); });
359
		connect(m_RemoveZeroes, static_cast<void (QCheckBox::*)(int)>(&QCheckBox::stateChanged), this, [this]() { this->Calc(); });
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381

		tabs->addTab(sfactpanel, "Structure Factors");
	}


	{	// powder lines panel
		auto powderpanel = new QWidget(this);
		auto pGrid = new QGridLayout(powderpanel);
		pGrid->setSpacing(4);
		pGrid->setContentsMargins(4,4,4,4);

		m_powderlines = new QPlainTextEdit(powderpanel);
		m_powderlines->setReadOnly(true);
		m_powderlines->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
		m_powderlines->setLineWrapMode(QPlainTextEdit::NoWrap);

		pGrid->addWidget(m_powderlines, 0,0, 1,4);

		tabs->addTab(powderpanel, "Powder Lines");
	}


382
383
384
385
386
387
388
389
390
391
392
	{	// real magnetic moments
		auto mmpanel = new QWidget(this);
		auto pGrid = new QGridLayout(mmpanel);
		pGrid->setSpacing(4);
		pGrid->setContentsMargins(4,4,4,4);

		m_moments = new QPlainTextEdit(mmpanel);
		m_moments->setReadOnly(true);
		m_moments->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont));
		m_moments->setLineWrapMode(QPlainTextEdit::NoWrap);

Tobias WEBER's avatar
Tobias WEBER committed
393
394
395
396
397
398
399
400
401
402
403
		for(auto*& spin : m_maxSC)
		{
			spin = new QSpinBox(mmpanel);
			spin->setMinimum(0);
			spin->setMaximum(999);
			spin->setValue(4);
		}

		m_maxSC[0]->setPrefix("x = ");
		m_maxSC[1]->setPrefix("y = ");
		m_maxSC[2]->setPrefix("z = ");
404
405
406

		pGrid->addWidget(m_moments, 0,0, 1,4);
		pGrid->addWidget(new QLabel("Max. Supercell Order:"), 1,0,1,1);
Tobias WEBER's avatar
Tobias WEBER committed
407
408
409
		pGrid->addWidget(m_maxSC[0], 1,1, 1,1);
		pGrid->addWidget(m_maxSC[1], 1,2, 1,1);
		pGrid->addWidget(m_maxSC[2], 1,3, 1,1);
410
411
412


		// signals
Tobias WEBER's avatar
Tobias WEBER committed
413
414
		for(auto* spin : m_maxSC)
			connect(spin, static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged), this, [this]() { this->Calc(); });
415
416
417
418
419

		tabs->addTab(mmpanel, "Magnetic Moments");
	}


420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
	{	// info panel
		auto infopanel = new QWidget(this);
		auto pGrid = new QGridLayout(infopanel);
		pGrid->setSpacing(4);
		pGrid->setContentsMargins(4,4,4,4);

		// table grid
		for(int i=0; i<4; ++i)
		{
			m_labelGlInfos[i] = new QLabel("", infopanel);
			m_labelGlInfos[i]->setSizePolicy(QSizePolicy::Ignored, m_labelGlInfos[i]->sizePolicy().verticalPolicy());
		}

		auto sep1 = new QFrame(infopanel); sep1->setFrameStyle(QFrame::HLine);
		auto sep2 = new QFrame(infopanel); sep2->setFrameStyle(QFrame::HLine);
		auto sep3 = new QFrame(infopanel); sep3->setFrameStyle(QFrame::HLine);

		std::string strBoost = BOOST_LIB_VERSION;
		algo::replace_all(strBoost, "_", ".");

		auto labelTitle = new QLabel("Magnetic Structure Factor Calculator", infopanel);
		auto fontTitle = labelTitle->font();
		fontTitle.setBold(true);
		labelTitle->setFont(fontTitle);
		labelTitle->setAlignment(Qt::AlignHCenter);

		auto labelAuthor = new QLabel("Written by Tobias Weber <tweber@ill.fr>.", infopanel);
		labelAuthor->setAlignment(Qt::AlignHCenter);

		auto labelDate = new QLabel("January 2019.", infopanel);
		labelDate->setAlignment(Qt::AlignHCenter);

		int y = 0;
		pGrid->addWidget(labelTitle, y++,0, 1,1);
		pGrid->addWidget(labelAuthor, y++,0, 1,1);
		pGrid->addWidget(labelDate, y++,0, 1,1);
		pGrid->addItem(new QSpacerItem(16,16, QSizePolicy::Minimum, QSizePolicy::Fixed), y++,0, 1,1);
		pGrid->addWidget(sep1, y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("Compiler: ") + QString(BOOST_COMPILER) + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("C++ Library: ") + QString(BOOST_STDLIB) + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("Build Date: ") + QString(__DATE__) + ", " + QString(__TIME__) + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(sep2, y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("Qt Version: ") + QString(QT_VERSION_STR) + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(new QLabel(QString("Boost Version: ") + strBoost.c_str() + ".", infopanel), y++,0, 1,1);
		pGrid->addWidget(sep3, y++,0, 1,1);
		for(int i=0; i<4; ++i)
			pGrid->addWidget(m_labelGlInfos[i], y++,0, 1,1);
		pGrid->addItem(new QSpacerItem(16,16, QSizePolicy::Minimum, QSizePolicy::Expanding), y++,0, 1,1);

		tabs->addTab(infopanel, "Infos");
	}


	// main grid
	auto pmainGrid = new QGridLayout(this);
	pmainGrid->setSpacing(4);
	pmainGrid->setContentsMargins(4,4,4,4);
	pmainGrid->addWidget(tabs, 0,0, 1,1);


	// menu bar
	{
		m_menu = new QMenuBar(this);
		m_menu->setNativeMenuBar(m_sett ? m_sett->value("native_gui", false).toBool() : false);

		auto menuFile = new QMenu("File", m_menu);
Tobias WEBER's avatar
Tobias WEBER committed
486
		auto menuView = new QMenu("3D View", m_menu);
487

Tobias WEBER's avatar
Tobias WEBER committed
488
		auto acNew = new QAction("New", menuFile);
489
490
491
492
		auto acLoad = new QAction("Load...", menuFile);
		auto acSave = new QAction("Save...", menuFile);
		auto acImportCIF = new QAction("Import CIF...", menuFile);
		auto acExit = new QAction("Exit", menuFile);
Tobias WEBER's avatar
Tobias WEBER committed
493
494
		auto ac3DView = new QAction("Unit Cell / Fourier Components...", menuFile);
		auto ac3DViewSC = new QAction("Super Cell / Magnetic Moments...", menuFile);
495

Tobias WEBER's avatar
Tobias WEBER committed
496
497
		menuFile->addAction(acNew);
		menuFile->addSeparator();
498
499
500
501
502
503
504
		menuFile->addAction(acLoad);
		menuFile->addAction(acSave);
		menuFile->addSeparator();
		menuFile->addAction(acImportCIF);
		menuFile->addSeparator();
		menuFile->addAction(acExit);
		menuView->addAction(ac3DView);
Tobias WEBER's avatar
Tobias WEBER committed
505
		menuView->addAction(ac3DViewSC);
506

Tobias WEBER's avatar
Tobias WEBER committed
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
		connect(acNew, &QAction::triggered, this,  [this]()
		{
			// clear old tables
			DelTabItem(-1);
			DelPropItem(-1);

			// set some defaults
			m_comboSG->setCurrentIndex(0);
			m_editA->setText("5");
			m_editB->setText("5");
			m_editC->setText("5");
			m_editAlpha->setText("90");
			m_editBeta->setText("90");
			m_editGamma->setText("90");
		});
522
523
524
525
		connect(acLoad, &QAction::triggered, this, &MagStructFactDlg::Load);
		connect(acSave, &QAction::triggered, this, &MagStructFactDlg::Save);
		connect(acImportCIF, &QAction::triggered, this, &MagStructFactDlg::ImportCIF);
		connect(acExit, &QAction::triggered, this, &QDialog::close);
Tobias WEBER's avatar
Tobias WEBER committed
526
527
528
	

		// unit cell view
529
530
531
532
533
534
		connect(ac3DView, &QAction::triggered, this, [this]()
		{
			// plot widget
			if(!m_dlgPlot)
			{
				m_dlgPlot = new QDialog(this);
Tobias WEBER's avatar
Tobias WEBER committed
535
				m_dlgPlot->setWindowTitle("Unit Cell");
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567

				m_plot = std::make_shared<GlPlot>(this);
				m_plot->GetImpl()->SetLight(0, m::create<t_vec3_gl>({ 5, 5, 5 }));
				m_plot->GetImpl()->SetLight(1, m::create<t_vec3_gl>({ -5, -5, -5 }));
				m_plot->GetImpl()->SetCoordMax(1.);
				m_plot->GetImpl()->SetCamBase(m::create<t_mat_gl>({1,0,0,0,  0,0,1,0,  0,-1,0,-1.5,  0,0,0,1}),
					m::create<t_vec_gl>({1,0,0,0}), m::create<t_vec_gl>({0,0,1,0}));


				auto labCoordSys = new QLabel("Coordinate System:", /*m_dlgPlot*/ this);
				auto comboCoordSys = new QComboBox(/*m_dlgPlot*/ this);
				m_status3D = new QLabel(/*m_dlgPlot*/ this);

				comboCoordSys->addItem("Fractional Units (rlu)");
				comboCoordSys->addItem("Lab Units (A)");


				m_plot->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});
				labCoordSys->setSizePolicy(QSizePolicy{QSizePolicy::Fixed, QSizePolicy::Fixed});

				auto grid = new QGridLayout(m_dlgPlot);
				grid->setSpacing(2);
				grid->setContentsMargins(4,4,4,4);
				grid->addWidget(m_plot.get(), 0,0,1,2);
				grid->addWidget(labCoordSys, 1,0,1,1);
				grid->addWidget(comboCoordSys, 1,1,1,1);
				grid->addWidget(m_status3D, 2,0,1,2);


				connect(m_plot.get(), &GlPlot::AfterGLInitialisation, this, &MagStructFactDlg::AfterGLInitialisation);
				connect(m_plot->GetImpl(), &GlPlot_impl::PickerIntersection, this, &MagStructFactDlg::PickerIntersection);
				connect(m_plot.get(), &GlPlot::MouseDown, this, &MagStructFactDlg::PlotMouseDown);
Tobias WEBER's avatar
Tobias WEBER committed
568
				//connect(m_plot.get(), &GlPlot::MouseUp, this, [this](bool left, bool mid, bool right) {});
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
				connect(comboCoordSys, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int val)
				{
					if(this->m_plot)
						this->m_plot->GetImpl()->SetCoordSys(val);
				});


				if(m_sett && m_sett->contains("geo_3dview"))
					m_dlgPlot->restoreGeometry(m_sett->value("geo_3dview").toByteArray());
				else
					m_dlgPlot->resize(500,500);
			}

			m_dlgPlot->show();
			m_dlgPlot->raise();
			m_dlgPlot->focusWidget();
		});

Tobias WEBER's avatar
Tobias WEBER committed
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646

		// super cell view
		connect(ac3DViewSC, &QAction::triggered, this, [this]()
		{
			// plot widget
			if(!m_dlgPlotSC)
			{
				m_dlgPlotSC = new QDialog(this);
				m_dlgPlotSC->setWindowTitle("Super Cell");

				m_plotSC = std::make_shared<GlPlot>(this);
				m_plotSC->GetImpl()->SetLight(0, m::create<t_vec3_gl>({ 5, 5, 5 }));
				m_plotSC->GetImpl()->SetLight(1, m::create<t_vec3_gl>({ -5, -5, -5 }));
				m_plotSC->GetImpl()->SetCoordMax(1.);
				m_plotSC->GetImpl()->SetCamBase(m::create<t_mat_gl>({1,0,0,0,  0,0,1,0,  0,-1,0,-1.5,  0,0,0,1}),
					m::create<t_vec_gl>({1,0,0,0}), m::create<t_vec_gl>({0,0,1,0}));


				auto labCoordSys = new QLabel("Coordinate System:", /*m_dlgPlotSC*/ this);
				auto comboCoordSys = new QComboBox(/*m_dlgPlotSC*/ this);
				m_status3D = new QLabel(/*m_dlgPlotSC*/ this);

				comboCoordSys->addItem("Fractional Units (rlu)");
				comboCoordSys->addItem("Lab Units (A)");


				m_plotSC->setSizePolicy(QSizePolicy{QSizePolicy::Expanding, QSizePolicy::Expanding});
				labCoordSys->setSizePolicy(QSizePolicy{QSizePolicy::Fixed, QSizePolicy::Fixed});

				auto grid = new QGridLayout(m_dlgPlotSC);
				grid->setSpacing(2);
				grid->setContentsMargins(4,4,4,4);
				grid->addWidget(m_plotSC.get(), 0,0,1,2);
				grid->addWidget(labCoordSys, 1,0,1,1);
				grid->addWidget(comboCoordSys, 1,1,1,1);
				grid->addWidget(m_status3D, 2,0,1,2);


				connect(m_plotSC.get(), &GlPlot::AfterGLInitialisation, this, &MagStructFactDlg::AfterGLInitialisationSC);
				//connect(m_plotSC->GetImpl(), &GlPlot_impl::PickerIntersection, this, &MagStructFactDlg::PickerIntersectionSC);
				//connect(m_plotSC.get(), &GlPlot::MouseDown, this, [this](bool left, bool mid, bool right) {});
				//connect(m_plotSC.get(), &GlPlot::MouseUp, this, [this](bool left, bool mid, bool right) {});
				connect(comboCoordSys, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, [this](int val)
				{
					if(this->m_plotSC)
						this->m_plotSC->GetImpl()->SetCoordSys(val);
				});


				if(m_sett && m_sett->contains("geo_3dview_sc"))
					m_dlgPlotSC->restoreGeometry(m_sett->value("geo_3dview_sc").toByteArray());
				else
					m_dlgPlotSC->resize(500,500);
			}

			m_dlgPlotSC->show();
			m_dlgPlotSC->raise();
			m_dlgPlotSC->focusWidget();
		});

647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
		m_menu->addMenu(menuFile);
		m_menu->addMenu(menuView);
		pmainGrid->setMenuBar(m_menu);
	}


	// restory window size and position
	if(m_sett && m_sett->contains("geo"))
		restoreGeometry(m_sett->value("geo").toByteArray());
	else
		resize(600, 500);


	m_ignoreChanges = 0;
}



// ----------------------------------------------------------------------------
void MagStructFactDlg::AddTabItem(int row, 
	const std::string& name, t_real MMag, t_real x, t_real y, t_real z, 
	t_real ReMx, t_real ReMy, t_real ReMz, t_real ImMx, t_real ImMy, t_real ImMz, 
	t_real scale, const std::string& col)
{
	bool bclone = 0;
	m_ignoreChanges = 1;

	if(row == -1)	// append to end of table
		row = m_nuclei->rowCount();
	else if(row == -2 && m_iCursorRow >= 0)	// use row from member variable
		row = m_iCursorRow;
	else if(row == -3 && m_iCursorRow >= 0)	// use row from member variable +1
		row = m_iCursorRow + 1;
	else if(row == -4 && m_iCursorRow >= 0)	// use row from member variable +1
	{
		row = m_iCursorRow + 1;
		bclone = 1;
	}

	//bool sorting = m_nuclei->isSortingEnabled();
	m_nuclei->setSortingEnabled(false);
	m_nuclei->insertRow(row);

	if(bclone)
	{
		for(int thecol=0; thecol<NUM_COLS; ++thecol)
			m_nuclei->setItem(row, thecol, m_nuclei->item(m_iCursorRow, thecol)->clone());
	}
	else
	{
		m_nuclei->setItem(row, COL_NAME, new QTableWidgetItem(name.c_str()));
		m_nuclei->setItem(row, COL_M_MAG, new NumericTableWidgetItem<t_real>(MMag));
		m_nuclei->setItem(row, COL_X, new NumericTableWidgetItem<t_real>(x));
		m_nuclei->setItem(row, COL_Y, new NumericTableWidgetItem<t_real>(y));
		m_nuclei->setItem(row, COL_Z, new NumericTableWidgetItem<t_real>(z));
		m_nuclei->setItem(row, COL_ReM_X, new NumericTableWidgetItem<t_real>(ReMx));
		m_nuclei->setItem(row, COL_ReM_Y, new NumericTableWidgetItem<t_real>(ReMy));
		m_nuclei->setItem(row, COL_ReM_Z, new NumericTableWidgetItem<t_real>(ReMz));
		m_nuclei->setItem(row, COL_ImM_X, new NumericTableWidgetItem<t_real>(ImMx));
		m_nuclei->setItem(row, COL_ImM_Y, new NumericTableWidgetItem<t_real>(ImMy));
		m_nuclei->setItem(row, COL_ImM_Z, new NumericTableWidgetItem<t_real>(ImMz));
		m_nuclei->setItem(row, COL_RAD, new NumericTableWidgetItem<t_real>(scale));
		m_nuclei->setItem(row, COL_COL, new QTableWidgetItem(col.c_str()));
	}

	Add3DItem(row);

	m_nuclei->scrollToItem(m_nuclei->item(row, 0));
	m_nuclei->setCurrentCell(row, 0);

	m_nuclei->setSortingEnabled(/*sorting*/ true);

	m_ignoreChanges = 0;
	Calc();
}


/**
 * add 3d object
 */
void MagStructFactDlg::Add3DItem(int row)
{
	if(!m_plot) return;

	// add all items
	if(row < 0)
	{
		for(int row=0; row<m_nuclei->rowCount(); ++row)
			Add3DItem(row);
		return;
	}

739
740
	auto objSphere = m_plot->GetImpl()->AddLinkedObject(m_sphere, 0,0,0, 1,1,1,1);
	//auto obj = m_plot->GetImpl()->AddSphere(0.05, 0,0,0, 1,1,1,1);
741
742
	auto objArrowRe = m_plot->GetImpl()->AddLinkedObject(m_arrow, 0,0,0, 1,1,1,1);
	auto objArrowIm = m_plot->GetImpl()->AddLinkedObject(m_arrow, 0,0,0, 1,1,1,1);
743

744
745
746
	m_nuclei->item(row, COL_NAME)->setData(Qt::UserRole+0, unsigned(objSphere));	// atomic position
	m_nuclei->item(row, COL_NAME)->setData(Qt::UserRole+1, unsigned(objArrowRe));	// real part of Fourier comp
	m_nuclei->item(row, COL_NAME)->setData(Qt::UserRole+2, unsigned(objArrowIm));	// imag part of Fourier comp
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767

	Sync3DItem(row);
}


/**
 * sync the properties of a 3d object
 */
void MagStructFactDlg::Sync3DItem(int row)
{
	if(!m_plot) return;

	// sync all items
	if(row < 0)
	{
		for(int row=0; row<m_nuclei->rowCount(); ++row)
			Sync3DItem(row);
		return;
	}

	std::size_t objSphere = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+0).toUInt();
768
769
770
	std::size_t objArrowRe = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+1).toUInt();
	std::size_t objArrowIm = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+2).toUInt();
	if(!objSphere || !objArrowRe || !objArrowIm)
771
772
		return;

773
774
775
776
	auto *itemName = m_nuclei->item(row, COL_NAME);
	auto *itemx = m_nuclei->item(row, COL_X);
	auto *itemy = m_nuclei->item(row, COL_Y);
	auto *itemz = m_nuclei->item(row, COL_Z);
777
778
779
780
	auto *itemM = m_nuclei->item(row, COL_M_MAG);
	auto *itemReMX = m_nuclei->item(row, COL_ReM_X);
	auto *itemReMY = m_nuclei->item(row, COL_ReM_Y);
	auto *itemReMZ = m_nuclei->item(row, COL_ReM_Z);
781
782
783
	auto *itemImMX = m_nuclei->item(row, COL_ImM_X);
	auto *itemImMY = m_nuclei->item(row, COL_ImM_Y);
	auto *itemImMZ = m_nuclei->item(row, COL_ImM_Z);
784
785
786
	auto *itemsc = m_nuclei->item(row, COL_RAD);
	auto *itemCol = m_nuclei->item(row, COL_COL);

787
	t_real_gl posx=0, posy=0, posz=0, M=1, ReMX=0, ReMY=0, ReMZ=1, ImMX=0, ImMY=0, ImMZ=1, scale=1;
788
789
790
	std::istringstream{itemx->text().toStdString()} >> posx;
	std::istringstream{itemy->text().toStdString()} >> posy;
	std::istringstream{itemz->text().toStdString()} >> posz;
791
792
793
794
	std::istringstream{itemM->text().toStdString()} >> M;
	std::istringstream{itemReMX->text().toStdString()} >> ReMX;
	std::istringstream{itemReMY->text().toStdString()} >> ReMY;
	std::istringstream{itemReMZ->text().toStdString()} >> ReMZ;
795
796
797
	std::istringstream{itemImMX->text().toStdString()} >> ImMX;
	std::istringstream{itemImMY->text().toStdString()} >> ImMY;
	std::istringstream{itemImMZ->text().toStdString()} >> ImMZ;
798
799
800
801
802
803
	std::istringstream{itemsc->text().toStdString()} >> scale;

	qreal r=1, g=1, b=1;
	QColor col{itemCol->text()};
	col.getRgbF(&r, &g, &b);

804
805
	t_mat_gl matSphere = m::hom_translation<t_mat_gl>(posx, posy, posz) * 
		m::hom_scaling<t_mat_gl>(M*scale, M*scale, M*scale);
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822

	auto vecReM = m::create<t_vec_gl>({t_real_gl(ReMX), t_real_gl(ReMY), t_real_gl(ReMZ)});
	auto vecImM = m::create<t_vec_gl>({t_real_gl(ImMX), t_real_gl(ImMY), t_real_gl(ImMZ)});
	auto normReM = m::norm<t_vec_gl>(vecReM);
	auto normImM = m::norm<t_vec_gl>(vecImM);

	t_mat_gl matArrowRe = GlPlot_impl::GetArrowMatrix(
		vecReM, 									// to
		1, 											// post-scale
		m::create<t_vec_gl>({0, 0, 0}),				// post-translate 
		m::create<t_vec_gl>({0, 0, 1}),				// from
		M*scale, 									// pre-scale
		m::create<t_vec_gl>({posx, posy, posz})		// pre-translate 
	);

	t_mat_gl matArrowIm = GlPlot_impl::GetArrowMatrix(
		vecImM, 									// to
823
824
825
826
827
828
829
830
		1, 											// post-scale
		m::create<t_vec_gl>({0, 0, 0}),				// post-translate 
		m::create<t_vec_gl>({0, 0, 1}),				// from
		M*scale, 									// pre-scale
		m::create<t_vec_gl>({posx, posy, posz})		// pre-translate 
	);

	m_plot->GetImpl()->SetObjectMatrix(objSphere, matSphere);
831
832
	m_plot->GetImpl()->SetObjectMatrix(objArrowRe, matArrowRe);
	m_plot->GetImpl()->SetObjectMatrix(objArrowIm, matArrowIm);
833
834
	m_plot->GetImpl()->SetObjectLabel(objSphere, itemName->text().toStdString());
	m_plot->GetImpl()->SetObjectCol(objSphere, r, g, b, 1);
835
836
837
838
	m_plot->GetImpl()->SetObjectCol(objArrowRe, r, g, b, 1);
	m_plot->GetImpl()->SetObjectCol(objArrowIm, 1.-r, 1.-g, 1.-b, 1);
	m_plot->GetImpl()->SetObjectVisible(objArrowRe, !m::equals<t_real_gl>(normReM, 0, g_eps));
	m_plot->GetImpl()->SetObjectVisible(objArrowIm, !m::equals<t_real_gl>(normImM, 0, g_eps));
839
840
841
842
843
844
845
846
847
848
849
850
851
852
	m_plot->update();
}


void MagStructFactDlg::DelTabItem(int begin, int end)
{
	m_ignoreChanges = 1;

	// if nothing is selected, clear all items
	if(begin == -1 || m_nuclei->selectedItems().count() == 0)
	{
		if(m_plot)
		{
			for(int row=0; row<m_nuclei->rowCount(); ++row)
853
854
855
856
			{
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+0).toUInt(); obj)
					m_plot->GetImpl()->RemoveObject(obj);
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+1).toUInt(); obj)
857
					m_plot->GetImpl()->RemoveObject(obj);
858
859
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+2).toUInt(); obj)
					m_plot->GetImpl()->RemoveObject(obj);
860
			}
861
862
863
864
865
866
867
868
			m_plot->update();
		}

		m_nuclei->clearContents();
		m_nuclei->setRowCount(0);
	}
	else if(begin == -2)	// clear selected
	{
869
		for(int row : GetSelectedRows(m_nuclei, true))
870
871
872
873
		{
			// remove 3d object
			if(m_plot)
			{
874
875
876
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+0).toUInt(); obj)
					m_plot->GetImpl()->RemoveObject(obj);
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+1).toUInt(); obj)
877
					m_plot->GetImpl()->RemoveObject(obj);
878
879
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+2).toUInt(); obj)
					m_plot->GetImpl()->RemoveObject(obj);
880
881
882
883
884
885
886
887
888
889
890
891
892
				m_plot->update();
			}

			m_nuclei->removeRow(row);
		}
	}
	else if(begin >= 0 && end >= 0)		// clear given range
	{
		for(int row=end-1; row>=begin; --row)
		{
			// remove 3d object
			if(m_plot)
			{
893
894
895
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+0).toUInt(); obj)
					m_plot->GetImpl()->RemoveObject(obj);
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+1).toUInt(); obj)
896
					m_plot->GetImpl()->RemoveObject(obj);
897
898
				if(std::size_t obj = m_nuclei->item(row, COL_NAME)->data(Qt::UserRole+2).toUInt(); obj)
					m_plot->GetImpl()->RemoveObject(obj);
899
900
901
902
903
904
905
906
907
908
909
910
				m_plot->update();
			}

			m_nuclei->removeRow(row);
		}
	}

	m_ignoreChanges = 0;
	Calc();
}


911
void MagStructFactDlg::MoveTabItemUp(QTableWidget *pTab)
912
913
{
	m_ignoreChanges = 1;
914
	pTab->setSortingEnabled(false);
915

916
	auto selected = GetSelectedRows(pTab, false);
917
918
919
920
921
	for(int row : selected)
	{
		if(row == 0)
			continue;

922
		auto *item = pTab->item(row, 0);
923
924
925
		if(!item || !item->isSelected())
			continue;

926
927
928
929
		pTab->insertRow(row-1);
		for(int col=0; col<pTab->columnCount(); ++col)
			pTab->setItem(row-1, col, pTab->item(row+1, col)->clone());
		pTab->removeRow(row+1);
930
931
	}

932
	for(int row=0; row<pTab->rowCount(); ++row)
933
	{
934
		if(auto *item = pTab->item(row, 0);
935
936
			item && std::find(selected.begin(), selected.end(), row+1) != selected.end())
		{
937
938
			for(int col=0; col<pTab->columnCount(); ++col)
				pTab->item(row, col)->setSelected(true);
939
940
941
942
943
944
945
		}
	}

	m_ignoreChanges = 0;
}


946
void MagStructFactDlg::MoveTabItemDown(QTableWidget *pTab)
947
948
{
	m_ignoreChanges = 1;
949
	pTab->setSortingEnabled(false);
950

951
	auto selected = GetSelectedRows(pTab, true);
952
953
	for(int row : selected)
	{
954
		if(row == pTab->rowCount()-1)
955
956
			continue;

957
		auto *item = pTab->item(row, 0);
958
959
960
		if(!item || !item->isSelected())
			continue;

961
962
963
964
		pTab->insertRow(row+2);
		for(int col=0; col<pTab->columnCount(); ++col)
			pTab->setItem(row+2, col, pTab->item(row, col)->clone());
		pTab->removeRow(row);
965
966
	}

967
	for(int row=0; row<pTab->rowCount(); ++row)
968
	{
969
		if(auto *item = pTab->item(row, 0);
970
971
			item && std::find(selected.begin(), selected.end(), row-1) != selected.end())
		{
972
973
			for(int col=0; col<pTab->columnCount(); ++col)
				pTab->item(row, col)->setSelected(true);
974
975
976
977
978
979
980
981
982
983
984
		}
	}

	m_ignoreChanges = 0;
}
// ----------------------------------------------------------------------------




// ----------------------------------------------------------------------------
985
std::vector<int> MagStructFactDlg::GetSelectedRows(QTableWidget *pTab, bool sort_reversed) const
986
987
{
	std::vector<int> vec;
988
	vec.reserve(pTab->selectedItems().size());
989

990
	for(int row=0; row<pTab->rowCount(); ++row)
991
	{
992
		if(auto *item = pTab->item(row, 0); item && item->isSelected())
993
994
995
996
997
998
999
1000
			vec.push_back(row);
	}

	if(sort_reversed)
	{
		std::stable_sort(vec.begin(), vec.end(), [](int row1, int row2)
		{ return row1 > row2; });
	}
For faster browsing, not all history is shown. View entire blame