OSDN Git Service

M src/cueplot.qrc: add icons
[cueplot/cueplot.git] / src / gui / main / PlotDataWidget.cpp
1 /*
2  * Cueplot: a GUI front-end to gnuplot
3  * Copyright (C) 2007, 2008 Muneyuki Noguchi
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software Foundation, 
17  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18  */
19 #include "PlotDataWidget.h"
20 #include "Plot.h"
21 #include "PlotListModel.h"
22 #include "PlotListView.h"
23 #include "PlotOptionDialog.h"
24
25 #include <QtCore/QMimeData>
26 #include <QtGui/QApplication>
27 #include <QtGui/QClipboard>
28 #include <QtGui/QGridLayout>
29 #include <QtGui/QPushButton>
30
31 PlotDataWidget::PlotDataWidget(QWidget *parent)
32 : QWidget(parent)
33 {
34         addPushButton = new QPushButton;
35         addPushButton->setText(tr("&Add"));
36         addPushButton->setEnabled(false);
37         addPushButton->setIcon(QIcon(":/images/add.png"));
38         connect(addPushButton, SIGNAL(clicked()),
39                         this, SIGNAL(dataAdded()));
40
41         optionPushButton = new QPushButton;
42         optionPushButton->setText(tr("O&ption..."));
43         optionPushButton->setEnabled(false);
44         optionPushButton->setSizePolicy(
45                         QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
46         connect(optionPushButton, SIGNAL(clicked()),
47                         this, SLOT(setPlotOptions()));
48
49         upPushButton = new QPushButton;
50         upPushButton->setText(tr("&Up"));
51         upPushButton->setEnabled(false);
52         upPushButton->setIcon(QIcon(":/images/up.png"));
53         connect(upPushButton, SIGNAL(clicked()),
54                         this, SLOT(setDataUp()));
55
56         downPushButton = new QPushButton;
57         downPushButton->setText(tr("&Down"));
58         downPushButton->setEnabled(false);
59         downPushButton->setIcon(QIcon(":/images/down.png"));
60         connect(downPushButton, SIGNAL(clicked()),
61                         this, SLOT(setDataDown()));
62
63         deletePushButton = new QPushButton;
64         deletePushButton->setText(tr("D&elete"));
65         deletePushButton->setEnabled(false);
66         deletePushButton->setIcon(QIcon(":/images/remove.png"));
67         deletePushButton->setSizePolicy(
68                         QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
69         connect(deletePushButton, SIGNAL(clicked()),
70                         this, SLOT(deleteData()));
71
72         plotListModel = new PlotListModel;
73
74         plotListView = new PlotListView;
75         plotListView->setModel(plotListModel);
76         // Task Tracker at trolltech.com
77         // #182440 items are removed when using InternalMove drag drop mode
78 #if !defined(Q_OS_DARWIN) || QT_VERSION >= 0x040400
79         plotListView->setDragEnabled(true);
80 #endif
81         plotListView->setAcceptDrops(true);
82         plotListView->setDropIndicatorShown(true);
83         plotListView->setSelectionMode(QAbstractItemView::ExtendedSelection);
84         connect(plotListModel, 
85                         SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)),
86                         this, SLOT(changeDialog(const QModelIndex &, const QModelIndex &)));
87         connect(plotListModel, SIGNAL(rowsInserted(const QModelIndex &, int, int)),
88                         this, SLOT(insertDialog(const QModelIndex &, int, int)));
89         connect(plotListModel, SIGNAL(rowsRemoved(const QModelIndex &, int, int)),
90                         this, SLOT(removeDialog(const QModelIndex &, int, int)));
91         connect(plotListModel, SIGNAL(rowsMoved(int, int)),
92                         this, SLOT(moveDialog(int, int)));
93
94         selectionModel = plotListView->selectionModel();
95         connect(selectionModel,
96                         SIGNAL(selectionChanged(const QItemSelection &,
97                                         const QItemSelection &)), this, SLOT(setPushButton()));
98
99         // ----------- assemble layouts and widgets
100         // ------------------------ create layouts
101
102         QGridLayout *mainLayout = new QGridLayout;
103         mainLayout->setMargin(0);
104         mainLayout->addWidget(addPushButton, 0, 0);
105         mainLayout->addWidget(optionPushButton, 1, 0);
106         mainLayout->addWidget(upPushButton, 2, 0);
107         mainLayout->addWidget(downPushButton, 3, 0);
108         // ウィンドウを最大化したときのことを考えて、QSpacerItem を入れる
109         mainLayout->addItem(new QSpacerItem(0, 0, 
110                                 QSizePolicy::Minimum, QSizePolicy::Expanding), 4, 0);
111         mainLayout->addItem(new QSpacerItem(0, 0, 
112                                 QSizePolicy::Expanding, QSizePolicy::Minimum), 5, 0, 1, 2);
113         mainLayout->addWidget(deletePushButton, 5, 2);
114         mainLayout->addWidget(plotListView, 0, 1, 5, 2);
115
116         setLayout(mainLayout);
117 }
118
119 void PlotDataWidget::addData(const Plot &plot)
120 {
121         // 挿入する場所
122         QModelIndexList list = selectionModel->selectedIndexes();
123         int row = list.size() ? list.last().row() + 1 : plotListModel->rowCount();
124
125         // 項目を挿入する
126         plotListModel->insertRow(row);
127
128         // 項目を設定する
129         plotListModel->setPlot(plotListModel->index(row), plot);
130 }
131
132 void PlotDataWidget::setDataUp()
133 {
134         moveData(true);
135 }
136
137 void PlotDataWidget::setDataDown()
138 {
139         moveData(false);
140 }
141
142 void PlotDataWidget::moveData(bool up)
143 {
144         QModelIndexList list = selectionModel->selectedIndexes();
145         int size = list.size();
146         if (!size) {
147                 return;
148         }
149         qSort(list);
150         if (up) {
151                 for (int i = 0; i < size; i++) {
152                         int row = list[i].row();
153                         // 一番上にある項目はこれ以上、上に動かせない
154                         if (row > 0) {
155                                 plotListModel->moveRow(row, row - 1);
156                         }
157                 }
158         } else {
159                 for (int i = size - 1; i >= 0; i--) {
160                         int row = list[i].row();
161                         // 一番下にある項目はこれ以上、下に動かせない
162                         if (row < plotListModel->rowCount() - 1) {
163                                 plotListModel->moveRow(row, row + 1);
164                         }
165                 }
166
167         }
168 }
169
170 void PlotDataWidget::deleteData()
171 {
172         for (int i = 0, j = selectionModel->selectedIndexes().size(); i < j; i++) {
173                 // 項目が削除されるとselectionModel->selectedIndexes()の中身が変わる
174                 plotListModel->removeRow(
175                                 selectionModel->selectedIndexes().first().row());
176         }
177 }
178
179 void PlotDataWidget::setPushButton()
180 {
181         // 項目が一つでも選択されていたら、
182         // オプションボタンと削除ボタンを有効にする
183         QModelIndexList list = selectionModel->selectedIndexes();
184         bool isSelected = list.size();
185         optionPushButton->setEnabled(isSelected);
186         deletePushButton->setEnabled(isSelected);
187
188         // 選択された項目があり、複数の項目がリスト中にあれば、
189         // 上ボタンと下ボタンを有効にする
190         int count = plotListModel->rowCount();
191         bool isMulti = isSelected && (count > 1);
192         // 一番上の項目
193         int min = count;
194         // 一番下の項目
195         int max = -1;
196         if (isMulti) {
197                 for (int i = 0, j = list.size(); i < j; i++) {
198                         int row = list.at(i).row();
199                         if (min > row) {
200                                 min = row;
201                         }
202                         if (max < row) {
203                                 max = row;
204                         }
205                 }
206         }
207         // 選択された項目のうち一番上の項目が一番上にあるときは上ボタンを無効にする
208         upPushButton->setEnabled(isMulti && min != 0);
209         // 選択された項目のうち一番下の項目が一番下にあるときは下ボタンを無効にする
210         downPushButton->setEnabled(isMulti && max != count - 1);
211 }
212
213 void PlotDataWidget::changeOption(const PlotOptionDialog &dialog,
214                 const QList<Plot> &list)
215 {
216         int idx = 0;
217         // オプションを変えたダイアログをリストの中から探す
218         for (int i = 0, j = dialogList.size(); i < j; i++) {
219                 const PlotOptionDialog *d = dialogList.at(i);
220                 if (d == &dialog) {
221                         plotListModel->setPlot(plotListModel->index(i), list[idx]);
222                         idx++;
223                 }
224         }
225 }
226
227 void PlotDataWidget::setAdditionEnabled(bool enable)
228 {
229         addPushButton->setEnabled(enable);
230 }
231
232 void PlotDataWidget::setPlotOptions()
233 {
234         QModelIndexList list = selectionModel->selectedIndexes();
235         int size = list.size();
236         if (!size) {
237                 return;
238         }
239         PlotOptionDialog *plotOptionDialog = dialogList.at(list[0].row());
240         // 新しいダイアログウィンドウを開くかどうかを示すフラグ
241         // i) 対応ダイアログを開いたことのない項目がある
242         // ii) 既存のダイアログの対応項目の数と選択項目の数が一致しない
243         bool flag = (!plotOptionDialog
244                         || (size != dialogList.count(plotOptionDialog)));
245         if (!flag) {
246                 for (int i = 0; i < size; i++) {
247                         // iii) 既存のダイアログの対応項目と選択項目が一致しない
248                         PlotOptionDialog *d = dialogList.at(list[i].row());
249                         if (d != plotOptionDialog) {
250                                 flag = true;
251                                 break;
252                         }
253                 }
254         }
255         if (flag) {
256                 // 選択項目に対応する既存のダイアログウィンドウをすべて閉じる
257                 for (int i = 0; i < size; i++) {
258                         PlotOptionDialog *dialog = dialogList.at(list[i].row());
259                         if (!dialog) {
260                                 continue;
261                         }
262                         if (dialog->canClose()) {
263                                 deleteDialog(*dialog);
264                         } else {
265                                 return;
266                         }
267                 }
268
269                 plotOptionDialog = new PlotOptionDialog;
270                 QList<Plot> plot;
271                 for (int i = 0; i < size; i++) {
272                         plot.append(plotListModel->plot(list[i]));
273                         dialogList[list[i].row()] = plotOptionDialog;
274                 }
275                 plotOptionDialog->setPlot(plot);
276                 connect(plotOptionDialog, 
277                                 SIGNAL(optionChanged(const PlotOptionDialog &,
278                                                 const QList<Plot> &)),
279                                 this,
280                                 SLOT(changeOption(const PlotOptionDialog &,
281                                                 const QList<Plot> &)));
282         }
283         plotOptionDialog->show();
284         plotOptionDialog->raise();
285         plotOptionDialog->activateWindow();
286 }
287
288 void PlotDataWidget::changeDialog(const QModelIndex &topLeft, 
289                 const QModelIndex &bottomRight)
290 {
291         for (int i = topLeft.row(), j = bottomRight.row(); i <= j; i++) {
292                 PlotOptionDialog *plotOptionDialog = dialogList.at(i);
293                 if (plotOptionDialog) {
294                         QList<Plot> list;
295                         int idx = 0;
296                         while ((idx = dialogList.indexOf(plotOptionDialog, idx)) >= 0) {
297                                 list.append(plotListModel->plot(plotListModel->index(idx)));
298                                 idx++;
299                         }
300                         plotOptionDialog->setPlot(list);
301                 }
302         }
303         setAction();
304 }
305
306 void PlotDataWidget::insertDialog(const QModelIndex &/*parent*/,
307                 int start, int end)
308 {
309         selectionModel->clear();
310         for (int i = start; i <= end; i++) {
311                 dialogList.insert(i, 0);
312                 selectionModel->select(plotListModel->index(i),
313                                 QItemSelectionModel::Select);
314         }
315         selectionModel->setCurrentIndex(plotListModel->index(end), 
316                         QItemSelectionModel::Select);
317         setPushButton();
318 }
319
320 void PlotDataWidget::removeDialog(const QModelIndex &/*parent*/,
321                 int start, int end)
322 {
323         for (int i = start; i <= end; i++) {
324                 deleteDialog(*dialogList.takeAt(start));
325         }
326         setPushButton();
327         setAction();
328 }
329
330 void PlotDataWidget::moveDialog(int from, int to)
331 {
332         dialogList.move(from, to);
333
334         selectionModel->select(plotListModel->index(from), 
335                         QItemSelectionModel::Deselect);
336         // 動かした項目を選択状態にする
337         QModelIndex modelIndex = plotListModel->index(to); 
338         selectionModel->select(modelIndex, QItemSelectionModel::Select);
339         selectionModel->setCurrentIndex(modelIndex, 
340                         QItemSelectionModel::Select);
341 }
342
343 void PlotDataWidget::setAction()
344 {
345         // チェックされている項目があるか調べる
346         bool isChecked = false;
347         for (int i = 0, j = plotListModel->rowCount(); i < j; i++) {
348                 if (plotListModel->data(plotListModel->index(i),
349                                         Qt::CheckStateRole) == Qt::Checked) {
350                         isChecked = true;
351                         break;
352                 }
353         }
354         emit setPlotEnabled(isChecked);
355 }
356
357 bool PlotDataWidget::hasItem() const
358 {
359         return plotListModel->rowCount() > 0;
360 }
361
362 QStringList PlotDataWidget::commandList() const
363 {
364         QStringList commandList;
365         for (int i = 0, j = plotListModel->rowCount(); i < j; i++) {
366                 QModelIndex modelIndex = plotListModel->index(i);
367                 if (plotListModel->data(modelIndex, 
368                                         Qt::CheckStateRole) == Qt::Checked) {
369                         commandList << plotListModel->plot(modelIndex).command();
370                 }
371         }
372         return commandList;
373 }
374
375 void PlotDataWidget::closeDialog()
376 {
377         while (!dialogList.isEmpty()) {
378                 deleteDialog(*dialogList.takeFirst());
379         }
380 }
381
382 void PlotDataWidget::deleteDialog(PlotOptionDialog &plotOptionDialog)
383 {
384         if (!(&plotOptionDialog)) {
385                 return;
386         }
387         // 二重deleteを防ぐ
388         int idx = 0;
389         while ((idx = dialogList.indexOf(&plotOptionDialog, idx)) >= 0) {
390                 dialogList[idx] = 0;
391                 idx++;
392         }
393         delete &plotOptionDialog;
394 }
395
396 void PlotDataWidget::copy()
397 {
398         if (QWidget::focusWidget() != plotListView) {
399                 return;
400         }
401         QApplication::clipboard()->setMimeData(plotListModel->mimeData(
402                                 selectionModel->selectedIndexes()));
403 }
404
405 void PlotDataWidget::cut()
406 {
407         if (QWidget::focusWidget() != plotListView) {
408                 return;
409         }
410         copy();
411         deleteData();
412 }
413
414 void PlotDataWidget::paste()
415 {
416         const QMimeData *data = QApplication::clipboard()->mimeData();
417         if (!data) {
418                 return;
419         }
420         QWidget *widget = QWidget::focusWidget();
421         if (widget != plotListView) {
422                 return;
423         }
424         QModelIndexList list = selectionModel->selectedIndexes();
425         int row = list.size() ? list.last().row() + 1 : plotListModel->rowCount();
426         if (data->hasFormat("application/vnd.row.list")) {
427                 plotListModel->decodePlot(data, row);
428         } else if (data->hasUrls()) {
429                 plotListModel->decodeUrl(data, row);
430         } else if (data->hasText()) {
431                 plotListModel->decodeText(data, row);
432         }
433 }