Chris@392
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@392
|
2
|
Chris@392
|
3 /*
|
Chris@392
|
4 Sonic Visualiser
|
Chris@392
|
5 An audio file viewer and annotation editor.
|
Chris@392
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@392
|
7 This file copyright 2008 QMUL.
|
Chris@392
|
8
|
Chris@392
|
9 This program is free software; you can redistribute it and/or
|
Chris@392
|
10 modify it under the terms of the GNU General Public License as
|
Chris@392
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@392
|
12 License, or (at your option) any later version. See the file
|
Chris@392
|
13 COPYING included with this distribution for more information.
|
Chris@392
|
14 */
|
Chris@392
|
15
|
Chris@392
|
16 #include "ModelDataTableDialog.h"
|
Chris@392
|
17
|
Chris@392
|
18 #include "data/model/ModelDataTableModel.h"
|
Chris@395
|
19 #include "data/model/TabularModel.h"
|
Chris@398
|
20 #include "data/model/Model.h"
|
Chris@392
|
21
|
Chris@393
|
22 #include "CommandHistory.h"
|
Chris@399
|
23 #include "IconLoader.h"
|
Chris@393
|
24
|
Chris@392
|
25 #include <QTableView>
|
Chris@552
|
26 #include <QLineEdit>
|
Chris@392
|
27 #include <QGridLayout>
|
Chris@552
|
28 #include <QLabel>
|
Chris@392
|
29 #include <QGroupBox>
|
Chris@392
|
30 #include <QDialogButtonBox>
|
Chris@392
|
31 #include <QHeaderView>
|
Chris@392
|
32 #include <QApplication>
|
Chris@1478
|
33 #include <QScreen>
|
Chris@399
|
34 #include <QAction>
|
Chris@399
|
35 #include <QToolBar>
|
Chris@392
|
36
|
Chris@393
|
37 #include <iostream>
|
Chris@393
|
38
|
Chris@1536
|
39 //#define DEBUG_MODEL_DATA_TABLE_DIALOG 1
|
Chris@1536
|
40
|
Chris@1479
|
41 ModelDataTableDialog::ModelDataTableDialog(ModelId tabularModelId,
|
Chris@404
|
42 QString title, QWidget *parent) :
|
Chris@400
|
43 QMainWindow(parent),
|
Chris@401
|
44 m_currentRow(0),
|
Chris@404
|
45 m_trackPlayback(true)
|
Chris@392
|
46 {
|
Chris@392
|
47 setWindowTitle(tr("Data Editor"));
|
Chris@392
|
48
|
Chris@404
|
49 QToolBar *toolbar;
|
Chris@399
|
50
|
Chris@404
|
51 toolbar = addToolBar(tr("Playback Toolbar"));
|
Chris@404
|
52 m_playToolbar = toolbar;
|
Chris@404
|
53 toolbar = addToolBar(tr("Play Mode Toolbar"));
|
Chris@404
|
54
|
Chris@399
|
55 IconLoader il;
|
Chris@399
|
56
|
Chris@402
|
57 QAction *action = new QAction(il.load("playfollow"), tr("Track Playback"), this);
|
Chris@402
|
58 action->setStatusTip(tr("Toggle tracking of playback position"));
|
Chris@402
|
59 action->setCheckable(true);
|
Chris@404
|
60 action->setChecked(m_trackPlayback);
|
Chris@402
|
61 connect(action, SIGNAL(triggered()), this, SLOT(togglePlayTracking()));
|
Chris@402
|
62 toolbar->addAction(action);
|
Chris@402
|
63
|
Chris@404
|
64 toolbar = addToolBar(tr("Edit Toolbar"));
|
Chris@402
|
65
|
Chris@1175
|
66 action = new QAction(il.load("draw"), tr("Insert New Item"), this);
|
Chris@399
|
67 action->setShortcut(tr("Insert"));
|
Chris@399
|
68 action->setStatusTip(tr("Insert a new item"));
|
Chris@399
|
69 connect(action, SIGNAL(triggered()), this, SLOT(insertRow()));
|
Chris@399
|
70 toolbar->addAction(action);
|
Chris@399
|
71
|
Chris@399
|
72 action = new QAction(il.load("datadelete"), tr("Delete Selected Items"), this);
|
Chris@399
|
73 action->setShortcut(tr("Delete"));
|
Chris@399
|
74 action->setStatusTip(tr("Delete the selected item or items"));
|
Chris@400
|
75 connect(action, SIGNAL(triggered()), this, SLOT(deleteRows()));
|
Chris@399
|
76 toolbar->addAction(action);
|
Chris@399
|
77
|
Chris@404
|
78 CommandHistory::getInstance()->registerToolbar(toolbar);
|
Chris@404
|
79
|
Chris@396
|
80 QFrame *mainFrame = new QFrame;
|
Chris@396
|
81 setCentralWidget(mainFrame);
|
Chris@396
|
82
|
Chris@392
|
83 QGridLayout *grid = new QGridLayout;
|
Chris@396
|
84 mainFrame->setLayout(grid);
|
Chris@392
|
85
|
Chris@392
|
86 QGroupBox *box = new QGroupBox;
|
Chris@398
|
87 if (title != "") {
|
Chris@398
|
88 box->setTitle(title);
|
Chris@398
|
89 } else {
|
Chris@398
|
90 box->setTitle(tr("Data in Layer"));
|
Chris@398
|
91 }
|
Chris@392
|
92 grid->addWidget(box, 0, 0);
|
Chris@392
|
93 grid->setRowStretch(0, 15);
|
Chris@392
|
94
|
Chris@392
|
95 QGridLayout *subgrid = new QGridLayout;
|
Chris@392
|
96 box->setLayout(subgrid);
|
Chris@392
|
97
|
Chris@392
|
98 subgrid->setSpacing(0);
|
Chris@392
|
99 subgrid->setMargin(5);
|
Chris@392
|
100
|
Chris@552
|
101 subgrid->addWidget(new QLabel(tr("Find:")), 1, 0);
|
Chris@552
|
102 subgrid->addWidget(new QLabel(tr(" ")), 1, 1);
|
Chris@552
|
103 m_find = new QLineEdit;
|
Chris@552
|
104 subgrid->addWidget(m_find, 1, 2);
|
Chris@552
|
105 connect(m_find, SIGNAL(textChanged(const QString &)),
|
Chris@552
|
106 this, SLOT(searchTextChanged(const QString &)));
|
Chris@552
|
107 connect(m_find, SIGNAL(returnPressed()),
|
Chris@552
|
108 this, SLOT(searchRepeated()));
|
Chris@552
|
109
|
Chris@392
|
110 m_tableView = new QTableView;
|
Chris@552
|
111 subgrid->addWidget(m_tableView, 0, 0, 1, 3);
|
Chris@392
|
112
|
Chris@395
|
113 m_tableView->setSortingEnabled(true);
|
Chris@396
|
114 m_tableView->sortByColumn(0, Qt::AscendingOrder);
|
Chris@392
|
115
|
Chris@1479
|
116 m_table = new ModelDataTableModel(tabularModelId);
|
Chris@392
|
117 m_tableView->setModel(m_table);
|
Chris@392
|
118
|
Chris@552
|
119 m_tableView->horizontalHeader()->setStretchLastSection(true);
|
Chris@552
|
120
|
Chris@393
|
121 connect(m_tableView, SIGNAL(clicked(const QModelIndex &)),
|
Chris@393
|
122 this, SLOT(viewClicked(const QModelIndex &)));
|
Chris@393
|
123 connect(m_tableView, SIGNAL(pressed(const QModelIndex &)),
|
Chris@393
|
124 this, SLOT(viewPressed(const QModelIndex &)));
|
Chris@400
|
125 connect(m_tableView->selectionModel(),
|
Chris@400
|
126 SIGNAL(currentChanged(const QModelIndex &, const QModelIndex &)),
|
Chris@400
|
127 this,
|
Chris@400
|
128 SLOT(currentChanged(const QModelIndex &, const QModelIndex &)));
|
Chris@400
|
129 connect(m_table, SIGNAL(addCommand(Command *)),
|
Chris@400
|
130 this, SLOT(addCommand(Command *)));
|
Chris@401
|
131 connect(m_table, SIGNAL(currentChanged(const QModelIndex &)),
|
Chris@401
|
132 this, SLOT(currentChangedThroughResort(const QModelIndex &)));
|
Chris@428
|
133 connect(m_table, SIGNAL(modelRemoved()),
|
Chris@428
|
134 this, SLOT(modelRemoved()));
|
Chris@393
|
135
|
Chris@392
|
136 QDialogButtonBox *bb = new QDialogButtonBox(QDialogButtonBox::Close);
|
Chris@396
|
137 connect(bb, SIGNAL(rejected()), this, SLOT(close()));
|
Chris@392
|
138 grid->addWidget(bb, 2, 0);
|
Chris@392
|
139 grid->setRowStretch(2, 0);
|
Chris@392
|
140
|
Chris@1478
|
141 QScreen *screen = QGuiApplication::primaryScreen();
|
Chris@1478
|
142 QRect available = screen->availableGeometry();
|
Chris@392
|
143
|
Chris@392
|
144 int width = available.width() / 3;
|
Chris@392
|
145 int height = available.height() / 2;
|
Chris@392
|
146 if (height < 370) {
|
Chris@392
|
147 if (available.height() > 500) height = 370;
|
Chris@392
|
148 }
|
Chris@552
|
149 if (width < 650) {
|
Chris@552
|
150 if (available.width() > 750) width = 650;
|
Chris@552
|
151 else if (width < 500) {
|
Chris@552
|
152 if (available.width() > 650) width = 500;
|
Chris@552
|
153 }
|
Chris@392
|
154 }
|
Chris@392
|
155
|
Chris@392
|
156 resize(width, height);
|
Chris@392
|
157 }
|
Chris@392
|
158
|
Chris@392
|
159 ModelDataTableDialog::~ModelDataTableDialog()
|
Chris@392
|
160 {
|
Chris@392
|
161 delete m_table;
|
Chris@392
|
162 }
|
Chris@392
|
163
|
Chris@393
|
164 void
|
Chris@908
|
165 ModelDataTableDialog::userScrolledToFrame(sv_frame_t frame)
|
Chris@393
|
166 {
|
Chris@1536
|
167 #ifdef DEBUG_MODEL_DATA_TABLE_DIALOG
|
Chris@1536
|
168 SVDEBUG << "ModelDataTableDialog::userScrolledToFrame " << frame << endl;
|
Chris@1536
|
169 #endif
|
Chris@1536
|
170
|
Chris@1536
|
171 // The table may contain more than one row with the same frame. If
|
Chris@1536
|
172 // our current row has the same frame as the one passed in, we
|
Chris@1536
|
173 // should do nothing - this avoids e.g. the situation where the
|
Chris@1536
|
174 // user clicks on the second of two equal-framed rows, we fire
|
Chris@1536
|
175 // scrollToFrame() from viewClicked(), that calls back here, and
|
Chris@1536
|
176 // we end up switching the selection to the first of the two rows
|
Chris@1536
|
177 // instead of the one the user clicked on.
|
Chris@1536
|
178
|
Chris@1536
|
179 if (m_table->getFrameForModelIndex(m_table->index(m_currentRow, 0)) ==
|
Chris@1536
|
180 frame) {
|
Chris@1536
|
181 #ifdef DEBUG_MODEL_DATA_TABLE_DIALOG
|
Chris@1536
|
182 SVDEBUG << "ModelDataTableDialog::userScrolledToFrame: Already have this frame current; calling makeCurrent" << endl;
|
Chris@1536
|
183 #endif
|
Chris@1536
|
184 return;
|
Chris@1536
|
185 }
|
Chris@1536
|
186
|
Chris@401
|
187 QModelIndex index = m_table->getModelIndexForFrame(frame);
|
Chris@401
|
188 makeCurrent(index.row());
|
Chris@401
|
189 }
|
Chris@401
|
190
|
Chris@401
|
191 void
|
Chris@908
|
192 ModelDataTableDialog::playbackScrolledToFrame(sv_frame_t frame)
|
Chris@401
|
193 {
|
Chris@401
|
194 if (m_trackPlayback) {
|
Chris@401
|
195 QModelIndex index = m_table->getModelIndexForFrame(frame);
|
Chris@401
|
196 makeCurrent(index.row());
|
Chris@401
|
197 }
|
Chris@401
|
198 }
|
Chris@401
|
199
|
Chris@401
|
200 void
|
Chris@552
|
201 ModelDataTableDialog::searchTextChanged(const QString &text)
|
Chris@552
|
202 {
|
Chris@552
|
203 QModelIndex mi = m_table->findText(text);
|
Chris@552
|
204 if (mi.isValid()) {
|
Chris@552
|
205 makeCurrent(mi.row());
|
Chris@552
|
206 m_tableView->selectionModel()->setCurrentIndex
|
Chris@552
|
207 (mi, QItemSelectionModel::ClearAndSelect);
|
Chris@552
|
208 }
|
Chris@552
|
209 }
|
Chris@552
|
210
|
Chris@552
|
211 void
|
Chris@552
|
212 ModelDataTableDialog::searchRepeated()
|
Chris@552
|
213 {
|
Chris@552
|
214 QModelIndex mi = m_table->findText(m_find->text());
|
Chris@552
|
215 if (mi.isValid()) {
|
Chris@552
|
216 makeCurrent(mi.row());
|
Chris@552
|
217 m_tableView->selectionModel()->setCurrentIndex
|
Chris@552
|
218 (mi, QItemSelectionModel::ClearAndSelect);
|
Chris@552
|
219 }
|
Chris@552
|
220 }
|
Chris@552
|
221
|
Chris@552
|
222 void
|
Chris@401
|
223 ModelDataTableDialog::makeCurrent(int row)
|
Chris@401
|
224 {
|
Chris@1271
|
225 if (m_table->rowCount() == 0 ||
|
Chris@1271
|
226 row >= m_table->rowCount() ||
|
Chris@1271
|
227 row < 0) {
|
Chris@1271
|
228 return;
|
Chris@1271
|
229 }
|
Chris@1271
|
230
|
Chris@401
|
231 int rh = m_tableView->height() / m_tableView->rowHeight(0);
|
Chris@404
|
232 int topRow = row - rh/4;
|
Chris@401
|
233 if (topRow < 0) topRow = 0;
|
Chris@404
|
234
|
Chris@404
|
235 // should only scroll if the desired row is not currently visible
|
Chris@404
|
236
|
Chris@404
|
237 // should only select if no part of the desired row is currently selected
|
Chris@404
|
238
|
Chris@682
|
239 // cerr << "rh = " << rh << ", row = " << row << ", scrolling to "
|
Chris@682
|
240 // << topRow << endl;
|
Chris@404
|
241
|
Chris@404
|
242 int pos = m_tableView->rowViewportPosition(row);
|
Chris@404
|
243
|
Chris@404
|
244 if (pos < 0 || pos >= m_tableView->height() - rh) {
|
Chris@404
|
245 m_tableView->scrollTo(m_table->index(topRow, 0));
|
Chris@404
|
246 }
|
Chris@404
|
247
|
Chris@404
|
248 bool haveRowSelected = false;
|
Chris@404
|
249 for (int i = 0; i < m_table->columnCount(); ++i) {
|
Chris@404
|
250 if (m_tableView->selectionModel()->isSelected(m_table->index(row, i))) {
|
Chris@404
|
251 haveRowSelected = true;
|
Chris@404
|
252 break;
|
Chris@404
|
253 }
|
Chris@404
|
254 }
|
Chris@404
|
255
|
Chris@404
|
256 if (!haveRowSelected) {
|
Chris@404
|
257 m_tableView->selectionModel()->setCurrentIndex
|
Chris@404
|
258 (m_table->index(row, 0),
|
Chris@404
|
259 QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
|
Chris@404
|
260 }
|
Chris@393
|
261 }
|
Chris@393
|
262
|
Chris@393
|
263 void
|
Chris@393
|
264 ModelDataTableDialog::viewClicked(const QModelIndex &index)
|
Chris@393
|
265 {
|
Chris@1536
|
266 #ifdef DEBUG_MODEL_DATA_TABLE_DIALOG
|
Chris@1536
|
267 SVDEBUG << "ModelDataTableDialog::viewClicked: " << index.row() << ", " << index.column() << endl;
|
Chris@1536
|
268 #endif
|
Chris@1536
|
269
|
Chris@394
|
270 emit scrollToFrame(m_table->getFrameForModelIndex(index));
|
Chris@393
|
271 }
|
Chris@393
|
272
|
Chris@393
|
273 void
|
Chris@807
|
274 ModelDataTableDialog::viewPressed(const QModelIndex &)
|
Chris@393
|
275 {
|
Chris@1536
|
276 #ifdef DEBUG_MODEL_DATA_TABLE_DIALOG
|
Chris@1536
|
277 SVDEBUG << "ModelDataTableDialog::viewPressed: " << index.row() << ", " << index.column() << endl;
|
Chris@1536
|
278 #endif
|
Chris@393
|
279 }
|
Chris@393
|
280
|
Chris@393
|
281 void
|
Chris@400
|
282 ModelDataTableDialog::currentChanged(const QModelIndex ¤t,
|
Chris@1272
|
283 const QModelIndex &previous)
|
Chris@399
|
284 {
|
Chris@1536
|
285 #ifdef DEBUG_MODEL_DATA_TABLE_DIALOG
|
Chris@1272
|
286 SVDEBUG << "ModelDataTableDialog::currentChanged: from "
|
Chris@1272
|
287 << previous.row() << ", " << previous.column()
|
Chris@1272
|
288 << " to " << current.row() << ", " << current.column()
|
Chris@1272
|
289 << endl;
|
Chris@1538
|
290 #else
|
Chris@1538
|
291 (void)previous; // unused
|
Chris@1536
|
292 #endif
|
Chris@400
|
293 m_currentRow = current.row();
|
Chris@401
|
294 m_table->setCurrentRow(m_currentRow);
|
Chris@399
|
295 }
|
Chris@399
|
296
|
Chris@399
|
297 void
|
Chris@400
|
298 ModelDataTableDialog::insertRow()
|
Chris@399
|
299 {
|
Chris@400
|
300 m_table->insertRow(m_currentRow);
|
Chris@400
|
301 }
|
Chris@400
|
302
|
Chris@400
|
303 void
|
Chris@400
|
304 ModelDataTableDialog::deleteRows()
|
Chris@400
|
305 {
|
Chris@1272
|
306 std::set<int> selectedRows;
|
Chris@1272
|
307 if (m_tableView->selectionModel()->hasSelection()) {
|
Chris@1272
|
308 for (const auto &ix: m_tableView->selectionModel()->selectedIndexes()) {
|
Chris@1272
|
309 selectedRows.insert(ix.row());
|
Chris@1272
|
310 }
|
Chris@1272
|
311 }
|
Chris@1272
|
312 // Remove rows in reverse order, so as not to pull the rug from
|
Chris@1272
|
313 // under our own feet
|
Chris@1272
|
314 for (auto ri = selectedRows.rbegin(); ri != selectedRows.rend(); ++ri) {
|
Chris@1536
|
315 #ifdef DEBUG_MODEL_DATA_TABLE_DIALOG
|
Chris@1272
|
316 SVDEBUG << "ModelDataTableDialog: removing row " << *ri << endl;
|
Chris@1536
|
317 #endif
|
Chris@1272
|
318 m_table->removeRow(*ri);
|
Chris@400
|
319 }
|
Chris@399
|
320 }
|
Chris@399
|
321
|
Chris@399
|
322 void
|
Chris@399
|
323 ModelDataTableDialog::editRow()
|
Chris@399
|
324 {
|
Chris@399
|
325 }
|
Chris@399
|
326
|
Chris@399
|
327 void
|
Chris@400
|
328 ModelDataTableDialog::addCommand(Command *command)
|
Chris@393
|
329 {
|
Chris@397
|
330 CommandHistory::getInstance()->addCommand(command, false, true);
|
Chris@393
|
331 }
|
Chris@393
|
332
|
Chris@401
|
333 void
|
Chris@402
|
334 ModelDataTableDialog::togglePlayTracking()
|
Chris@402
|
335 {
|
Chris@402
|
336 m_trackPlayback = !m_trackPlayback;
|
Chris@402
|
337 }
|
Chris@402
|
338
|
Chris@402
|
339 void
|
Chris@401
|
340 ModelDataTableDialog::currentChangedThroughResort(const QModelIndex &index)
|
Chris@401
|
341 {
|
Chris@1536
|
342 #ifdef DEBUG_MODEL_DATA_TABLE_DIALOG
|
Chris@1536
|
343 SVDEBUG << "ModelDataTableDialog::currentChangedThroughResort: row = " << index.row() << endl;
|
Chris@1536
|
344 #endif
|
Chris@401
|
345 makeCurrent(index.row());
|
Chris@401
|
346 }
|
Chris@401
|
347
|
Chris@428
|
348 void
|
Chris@428
|
349 ModelDataTableDialog::modelRemoved()
|
Chris@428
|
350 {
|
Chris@428
|
351 close();
|
Chris@428
|
352 }
|
Chris@401
|
353
|
Chris@401
|
354
|