1 | /*************************************************************************** |
2 | * Copyright (C) 2005-2014 by the Quassel Project * |
3 | * devel@quassel-irc.org * |
4 | * * |
5 | * This program is free software; you can redistribute it and/or modify * |
6 | * it under the terms of the GNU General Public License as published by * |
7 | * the Free Software Foundation; either version 2 of the License, or * |
8 | * (at your option) version 3. * |
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 * |
17 | * Free Software Foundation, Inc., * |
18 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * |
19 | ***************************************************************************/ |
20 | |
21 | #include "bufferview.h" |
22 | |
23 | #include <QApplication> |
24 | #include <QAction> |
25 | #include <QFlags> |
26 | #include <QHeaderView> |
27 | #include <QLineEdit> |
28 | #include <QMenu> |
29 | #include <QMessageBox> |
30 | #include <QSet> |
31 | |
32 | #include "action.h" |
33 | #include "buffermodel.h" |
34 | #include "bufferviewfilter.h" |
35 | #include "buffersettings.h" |
36 | #include "buffersyncer.h" |
37 | #include "client.h" |
38 | #include "contextmenuactionprovider.h" |
39 | #include "graphicalui.h" |
40 | #include "network.h" |
41 | #include "networkmodel.h" |
42 | #include "contextmenuactionprovider.h" |
43 | |
44 | /***************************************** |
45 | * The TreeView showing the Buffers |
46 | *****************************************/ |
47 | // Please be carefull when reimplementing methods which are used to inform the view about changes to the data |
48 | // to be on the safe side: call QTreeView's method aswell |
49 | BufferView::BufferView(QWidget *parent) |
50 | : QTreeView(parent) |
51 | { |
52 | connect(this, SIGNAL(collapsed(const QModelIndex &)), SLOT(storeExpandedState(const QModelIndex &))); |
53 | connect(this, SIGNAL(expanded(const QModelIndex &)), SLOT(storeExpandedState(const QModelIndex &))); |
54 | |
55 | setSelectionMode(QAbstractItemView::ExtendedSelection); |
56 | |
57 | QAbstractItemDelegate *oldDelegate = itemDelegate(); |
58 | BufferViewDelegate *tristateDelegate = new BufferViewDelegate(this); |
59 | setItemDelegate(tristateDelegate); |
60 | delete oldDelegate; |
61 | } |
62 | |
63 | |
64 | void BufferView::init() |
65 | { |
66 | header()->setContextMenuPolicy(Qt::ActionsContextMenu); |
67 | hideColumn(1); |
68 | hideColumn(2); |
69 | setIndentation(10); |
70 | |
71 | expandAll(); |
72 | |
73 | header()->hide(); // nobody seems to use this anyway |
74 | |
75 | // breaks with Qt 4.8 |
76 | if (QString("4.8.0" ) > qVersion()) // FIXME breaks with Qt versions >= 4.10! |
77 | setAnimated(true); |
78 | |
79 | // FIXME This is to workaround bug #663 |
80 | setUniformRowHeights(true); |
81 | |
82 | #ifndef QT_NO_DRAGANDDROP |
83 | setDragEnabled(true); |
84 | setAcceptDrops(true); |
85 | setDropIndicatorShown(true); |
86 | #endif |
87 | |
88 | setSortingEnabled(true); |
89 | sortByColumn(0, Qt::AscendingOrder); |
90 | |
91 | // activated() fails on X11 and Qtopia at least |
92 | #if defined Q_WS_QWS || defined Q_WS_X11 |
93 | disconnect(this, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(joinChannel(QModelIndex))); |
94 | connect(this, SIGNAL(doubleClicked(QModelIndex)), SLOT(joinChannel(QModelIndex))); |
95 | #else |
96 | // afaik this is better on Mac and Windows |
97 | disconnect(this, SIGNAL(activated(QModelIndex)), this, SLOT(joinChannel(QModelIndex))); |
98 | connect(this, SIGNAL(activated(QModelIndex)), SLOT(joinChannel(QModelIndex))); |
99 | #endif |
100 | } |
101 | |
102 | |
103 | void BufferView::setModel(QAbstractItemModel *model) |
104 | { |
105 | delete selectionModel(); |
106 | |
107 | QTreeView::setModel(model); |
108 | init(); |
109 | // remove old Actions |
110 | QList<QAction *> oldactions = header()->actions(); |
111 | foreach(QAction *action, oldactions) { |
112 | header()->removeAction(action); |
113 | action->deleteLater(); |
114 | } |
115 | |
116 | if (!model) |
117 | return; |
118 | |
119 | QString sectionName; |
120 | QAction *showSection; |
121 | for (int i = 1; i < model->columnCount(); i++) { |
122 | sectionName = (model->headerData(i, Qt::Horizontal, Qt::DisplayRole)).toString(); |
123 | showSection = new QAction(sectionName, header()); |
124 | showSection->setCheckable(true); |
125 | showSection->setChecked(!isColumnHidden(i)); |
126 | showSection->setProperty("column" , i); |
127 | connect(showSection, SIGNAL(toggled(bool)), this, SLOT(toggleHeader(bool))); |
128 | header()->addAction(showSection); |
129 | } |
130 | |
131 | connect(model, SIGNAL(layoutChanged()), this, SLOT(on_layoutChanged())); |
132 | } |
133 | |
134 | |
135 | void BufferView::setFilteredModel(QAbstractItemModel *model_, BufferViewConfig *config) |
136 | { |
137 | BufferViewFilter *filter = qobject_cast<BufferViewFilter *>(model()); |
138 | if (filter) { |
139 | filter->setConfig(config); |
140 | setConfig(config); |
141 | return; |
142 | } |
143 | |
144 | if (model()) { |
145 | disconnect(this, 0, model(), 0); |
146 | disconnect(model(), 0, this, 0); |
147 | } |
148 | |
149 | if (!model_) { |
150 | setModel(model_); |
151 | } |
152 | else { |
153 | BufferViewFilter *filter = new BufferViewFilter(model_, config); |
154 | setModel(filter); |
155 | connect(filter, SIGNAL(configChanged()), this, SLOT(on_configChanged())); |
156 | } |
157 | setConfig(config); |
158 | } |
159 | |
160 | |
161 | void BufferView::setSelectionModel(QItemSelectionModel *selectionModel) |
162 | { |
163 | if (QTreeView::selectionModel()) |
164 | disconnect(selectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), |
165 | model(), SIGNAL(checkPreviousCurrentForRemoval(QModelIndex, QModelIndex))); |
166 | |
167 | QTreeView::setSelectionModel(selectionModel); |
168 | BufferViewFilter *filter = qobject_cast<BufferViewFilter *>(model()); |
169 | if (filter) { |
170 | connect(selectionModel, SIGNAL(currentChanged(QModelIndex, QModelIndex)), |
171 | filter, SLOT(checkPreviousCurrentForRemoval(QModelIndex, QModelIndex))); |
172 | } |
173 | } |
174 | |
175 | |
176 | void BufferView::setConfig(BufferViewConfig *config) |
177 | { |
178 | if (_config == config) |
179 | return; |
180 | |
181 | if (_config) { |
182 | disconnect(_config, 0, this, 0); |
183 | } |
184 | |
185 | _config = config; |
186 | if (config) { |
187 | connect(config, SIGNAL(networkIdSet(const NetworkId &)), this, SLOT(setRootIndexForNetworkId(const NetworkId &))); |
188 | setRootIndexForNetworkId(config->networkId()); |
189 | } |
190 | else { |
191 | setIndentation(10); |
192 | setRootIndex(QModelIndex()); |
193 | } |
194 | } |
195 | |
196 | |
197 | void BufferView::setRootIndexForNetworkId(const NetworkId &networkId) |
198 | { |
199 | if (!networkId.isValid() || !model()) { |
200 | setIndentation(10); |
201 | setRootIndex(QModelIndex()); |
202 | } |
203 | else { |
204 | setIndentation(5); |
205 | int networkCount = model()->rowCount(); |
206 | QModelIndex child; |
207 | for (int i = 0; i < networkCount; i++) { |
208 | child = model()->index(i, 0); |
209 | if (networkId == model()->data(child, NetworkModel::NetworkIdRole).value<NetworkId>()) |
210 | setRootIndex(child); |
211 | } |
212 | } |
213 | } |
214 | |
215 | |
216 | void BufferView::joinChannel(const QModelIndex &index) |
217 | { |
218 | BufferInfo::Type bufferType = (BufferInfo::Type)index.data(NetworkModel::BufferTypeRole).value<int>(); |
219 | |
220 | if (bufferType != BufferInfo::ChannelBuffer) |
221 | return; |
222 | |
223 | BufferInfo bufferInfo = index.data(NetworkModel::BufferInfoRole).value<BufferInfo>(); |
224 | |
225 | Client::userInput(bufferInfo, QString("/JOIN %1" ).arg(bufferInfo.bufferName())); |
226 | } |
227 | |
228 | |
229 | void BufferView::keyPressEvent(QKeyEvent *event) |
230 | { |
231 | if (event->key() == Qt::Key_Backspace || event->key() == Qt::Key_Delete) { |
232 | event->accept(); |
233 | removeSelectedBuffers(); |
234 | } |
235 | QTreeView::keyPressEvent(event); |
236 | } |
237 | |
238 | |
239 | void BufferView::dropEvent(QDropEvent *event) |
240 | { |
241 | QModelIndex index = indexAt(event->pos()); |
242 | |
243 | QRect indexRect = visualRect(index); |
244 | QPoint cursorPos = event->pos(); |
245 | |
246 | // check if we're really _on_ the item and not indicating a move to just above or below the item |
247 | const int margin = 2; |
248 | if (cursorPos.y() - indexRect.top() < margin |
249 | || indexRect.bottom() - cursorPos.y() < margin) |
250 | return QTreeView::dropEvent(event); |
251 | |
252 | QList<QPair<NetworkId, BufferId> > bufferList = Client::networkModel()->mimeDataToBufferList(event->mimeData()); |
253 | if (bufferList.count() != 1) |
254 | return QTreeView::dropEvent(event); |
255 | |
256 | NetworkId networkId = bufferList[0].first; |
257 | BufferId bufferId2 = bufferList[0].second; |
258 | |
259 | if (index.data(NetworkModel::ItemTypeRole) != NetworkModel::BufferItemType) |
260 | return QTreeView::dropEvent(event); |
261 | |
262 | if (index.data(NetworkModel::BufferTypeRole) != BufferInfo::QueryBuffer) |
263 | return QTreeView::dropEvent(event); |
264 | |
265 | if (index.data(NetworkModel::NetworkIdRole).value<NetworkId>() != networkId) |
266 | return QTreeView::dropEvent(event); |
267 | |
268 | BufferId bufferId1 = index.data(NetworkModel::BufferIdRole).value<BufferId>(); |
269 | if (bufferId1 == bufferId2) |
270 | return QTreeView::dropEvent(event); |
271 | |
272 | int res = QMessageBox::question(0, tr("Merge buffers permanently?" ), |
273 | tr("Do you want to merge the buffer \"%1\" permanently into buffer \"%2\"?\n This cannot be reversed!" ).arg(Client::networkModel()->bufferName(bufferId2)).arg(Client::networkModel()->bufferName(bufferId1)), |
274 | QMessageBox::Yes|QMessageBox::No, QMessageBox::No); |
275 | if (res == QMessageBox::Yes) { |
276 | Client::mergeBuffersPermanently(bufferId1, bufferId2); |
277 | } |
278 | } |
279 | |
280 | |
281 | void BufferView::removeSelectedBuffers(bool permanently) |
282 | { |
283 | if (!config()) |
284 | return; |
285 | |
286 | BufferId bufferId; |
287 | QSet<BufferId> removedRows; |
288 | foreach(QModelIndex index, selectionModel()->selectedIndexes()) { |
289 | if (index.data(NetworkModel::ItemTypeRole) != NetworkModel::BufferItemType) |
290 | continue; |
291 | |
292 | bufferId = index.data(NetworkModel::BufferIdRole).value<BufferId>(); |
293 | if (removedRows.contains(bufferId)) |
294 | continue; |
295 | |
296 | removedRows << bufferId; |
297 | } |
298 | |
299 | foreach(BufferId bufferId, removedRows) { |
300 | if (permanently) |
301 | config()->requestRemoveBufferPermanently(bufferId); |
302 | else |
303 | config()->requestRemoveBuffer(bufferId); |
304 | } |
305 | } |
306 | |
307 | |
308 | void BufferView::rowsInserted(const QModelIndex &parent, int start, int end) |
309 | { |
310 | QTreeView::rowsInserted(parent, start, end); |
311 | |
312 | // ensure that newly inserted network nodes are expanded per default |
313 | if (parent.data(NetworkModel::ItemTypeRole) != NetworkModel::NetworkItemType) |
314 | return; |
315 | |
316 | setExpandedState(parent); |
317 | } |
318 | |
319 | |
320 | void BufferView::on_layoutChanged() |
321 | { |
322 | int numNets = model()->rowCount(QModelIndex()); |
323 | for (int row = 0; row < numNets; row++) { |
324 | QModelIndex networkIdx = model()->index(row, 0, QModelIndex()); |
325 | setExpandedState(networkIdx); |
326 | } |
327 | } |
328 | |
329 | |
330 | void BufferView::on_configChanged() |
331 | { |
332 | Q_ASSERT(model()); |
333 | |
334 | // expand all active networks... collapse inactive ones... unless manually changed |
335 | QModelIndex networkIdx; |
336 | NetworkId networkId; |
337 | for (int row = 0; row < model()->rowCount(); row++) { |
338 | networkIdx = model()->index(row, 0); |
339 | if (model()->rowCount(networkIdx) == 0) |
340 | continue; |
341 | |
342 | networkId = model()->data(networkIdx, NetworkModel::NetworkIdRole).value<NetworkId>(); |
343 | if (!networkId.isValid()) |
344 | continue; |
345 | |
346 | setExpandedState(networkIdx); |
347 | } |
348 | |
349 | if (config()) { |
350 | // update selection to current one |
351 | Client::bufferModel()->synchronizeView(this); |
352 | } |
353 | } |
354 | |
355 | |
356 | void BufferView::storeExpandedState(const QModelIndex &networkIdx) |
357 | { |
358 | NetworkId networkId = model()->data(networkIdx, NetworkModel::NetworkIdRole).value<NetworkId>(); |
359 | |
360 | int oldState = 0; |
361 | if (isExpanded(networkIdx)) |
362 | oldState |= WasExpanded; |
363 | if (model()->data(networkIdx, NetworkModel::ItemActiveRole).toBool()) |
364 | oldState |= WasActive; |
365 | |
366 | _expandedState[networkId] = oldState; |
367 | } |
368 | |
369 | |
370 | void BufferView::setExpandedState(const QModelIndex &networkIdx) |
371 | { |
372 | if (model()->data(networkIdx, NetworkModel::ItemTypeRole) != NetworkModel::NetworkItemType) |
373 | return; |
374 | |
375 | if (model()->rowCount(networkIdx) == 0) |
376 | return; |
377 | |
378 | NetworkId networkId = model()->data(networkIdx, NetworkModel::NetworkIdRole).value<NetworkId>(); |
379 | |
380 | bool networkActive = model()->data(networkIdx, NetworkModel::ItemActiveRole).toBool(); |
381 | bool expandNetwork = networkActive; |
382 | if (_expandedState.contains(networkId)) { |
383 | int oldState = _expandedState[networkId]; |
384 | if ((bool)(oldState & WasActive) == networkActive) |
385 | expandNetwork = (bool)(oldState & WasExpanded); |
386 | } |
387 | |
388 | if (expandNetwork != isExpanded(networkIdx)) { |
389 | update(networkIdx); |
390 | setExpanded(networkIdx, expandNetwork); |
391 | } |
392 | storeExpandedState(networkIdx); // this call is needed to keep track of the isActive state |
393 | } |
394 | |
395 | #if QT_VERSION < 0x050000 |
396 | void BufferView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) |
397 | { |
398 | QTreeView::dataChanged(topLeft, bottomRight); |
399 | #else |
400 | void BufferView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles) |
401 | { |
402 | QTreeView::dataChanged(topLeft, bottomRight, roles); |
403 | #endif |
404 | |
405 | // determine how many items have been changed and if any of them is a networkitem |
406 | // which just swichted from active to inactive or vice versa |
407 | if (topLeft.data(NetworkModel::ItemTypeRole) != NetworkModel::NetworkItemType) |
408 | return; |
409 | |
410 | for (int i = topLeft.row(); i <= bottomRight.row(); i++) { |
411 | QModelIndex networkIdx = topLeft.sibling(i, 0); |
412 | setExpandedState(networkIdx); |
413 | } |
414 | } |
415 | |
416 | |
417 | void BufferView::(bool checked) |
418 | { |
419 | QAction *action = qobject_cast<QAction *>(sender()); |
420 | header()->setSectionHidden((action->property("column" )).toInt(), !checked); |
421 | } |
422 | |
423 | |
424 | void BufferView::(QContextMenuEvent *event) |
425 | { |
426 | QModelIndex index = indexAt(event->pos()); |
427 | if (!index.isValid()) |
428 | index = rootIndex(); |
429 | |
430 | QMenu (this); |
431 | |
432 | if (index.isValid()) { |
433 | addActionsToMenu(&contextMenu, index); |
434 | } |
435 | |
436 | addFilterActions(&contextMenu, index); |
437 | |
438 | if (!contextMenu.actions().isEmpty()) |
439 | contextMenu.exec(QCursor::pos()); |
440 | } |
441 | |
442 | |
443 | void BufferView::(QMenu *, const QModelIndex &index) |
444 | { |
445 | QModelIndexList indexList = selectedIndexes(); |
446 | // make sure the item we clicked on is first |
447 | indexList.removeAll(index); |
448 | indexList.prepend(index); |
449 | |
450 | GraphicalUi::contextMenuActionProvider()->addActions(contextMenu, indexList, this, "menuActionTriggered" , (bool)config()); |
451 | } |
452 | |
453 | |
454 | void BufferView::(QMenu *, const QModelIndex &index) |
455 | { |
456 | BufferViewFilter *filter = qobject_cast<BufferViewFilter *>(model()); |
457 | if (filter) { |
458 | QList<QAction *> filterActions = filter->actions(index); |
459 | if (!filterActions.isEmpty()) { |
460 | contextMenu->addSeparator(); |
461 | foreach(QAction *action, filterActions) { |
462 | contextMenu->addAction(action); |
463 | } |
464 | } |
465 | } |
466 | } |
467 | |
468 | |
469 | void BufferView::(QAction *result) |
470 | { |
471 | ContextMenuActionProvider::ActionType type = (ContextMenuActionProvider::ActionType)result->data().toInt(); |
472 | switch (type) { |
473 | case ContextMenuActionProvider::HideBufferTemporarily: |
474 | removeSelectedBuffers(); |
475 | break; |
476 | case ContextMenuActionProvider::HideBufferPermanently: |
477 | removeSelectedBuffers(true); |
478 | break; |
479 | default: |
480 | return; |
481 | } |
482 | } |
483 | |
484 | |
485 | void BufferView::nextBuffer() |
486 | { |
487 | changeBuffer(Forward); |
488 | } |
489 | |
490 | |
491 | void BufferView::previousBuffer() |
492 | { |
493 | changeBuffer(Backward); |
494 | } |
495 | |
496 | |
497 | void BufferView::changeBuffer(Direction direction) |
498 | { |
499 | QModelIndex currentIndex = selectionModel()->currentIndex(); |
500 | QModelIndex resultingIndex; |
501 | |
502 | if (currentIndex.parent().isValid()) { |
503 | //If we are a child node just switch among siblings unless it's the first/last child |
504 | resultingIndex = currentIndex.sibling(currentIndex.row() + direction, 0); |
505 | |
506 | if (!resultingIndex.isValid()) { |
507 | QModelIndex parent = currentIndex.parent(); |
508 | if (direction == Backward) |
509 | resultingIndex = parent; |
510 | else |
511 | resultingIndex = parent.sibling(parent.row() + direction, 0); |
512 | } |
513 | } |
514 | else { |
515 | //If we have a toplevel node, try and get an adjacent child |
516 | if (direction == Backward) { |
517 | QModelIndex newParent = currentIndex.sibling(currentIndex.row() - 1, 0); |
518 | if (model()->hasChildren(newParent)) |
519 | resultingIndex = newParent.child(model()->rowCount(newParent) - 1, 0); |
520 | else |
521 | resultingIndex = newParent; |
522 | } |
523 | else { |
524 | if (model()->hasChildren(currentIndex)) |
525 | resultingIndex = currentIndex.child(0, 0); |
526 | else |
527 | resultingIndex = currentIndex.sibling(currentIndex.row() + 1, 0); |
528 | } |
529 | } |
530 | |
531 | if (!resultingIndex.isValid()) |
532 | return; |
533 | |
534 | selectionModel()->setCurrentIndex(resultingIndex, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); |
535 | selectionModel()->select(resultingIndex, QItemSelectionModel::ClearAndSelect); |
536 | } |
537 | |
538 | |
539 | void BufferView::wheelEvent(QWheelEvent *event) |
540 | { |
541 | if (ItemViewSettings().mouseWheelChangesBuffer() == (bool)(event->modifiers() & Qt::AltModifier)) |
542 | return QTreeView::wheelEvent(event); |
543 | |
544 | int rowDelta = (event->delta() > 0) ? -1 : 1; |
545 | changeBuffer((Direction)rowDelta); |
546 | } |
547 | |
548 | |
549 | void BufferView::hideCurrentBuffer() |
550 | { |
551 | QModelIndex index = selectionModel()->currentIndex(); |
552 | if (index.data(NetworkModel::ItemTypeRole) != NetworkModel::BufferItemType) |
553 | return; |
554 | |
555 | BufferId bufferId = index.data(NetworkModel::BufferIdRole).value<BufferId>(); |
556 | |
557 | //The check above means we won't be looking at a network, which should always be the first row, so we can just go backwards. |
558 | changeBuffer(Backward); |
559 | |
560 | /*if(removedRows.contains(bufferId)) |
561 | continue; |
562 | |
563 | removedRows << bufferId;*/ |
564 | /*if(permanently) |
565 | config()->requestRemoveBufferPermanently(bufferId); |
566 | else*/ |
567 | config()->requestRemoveBuffer(bufferId); |
568 | } |
569 | |
570 | |
571 | QSize BufferView::sizeHint() const |
572 | { |
573 | return QTreeView::sizeHint(); |
574 | |
575 | if (!model()) |
576 | return QTreeView::sizeHint(); |
577 | |
578 | if (model()->rowCount() == 0) |
579 | return QSize(120, 50); |
580 | |
581 | int columnSize = 0; |
582 | for (int i = 0; i < model()->columnCount(); i++) { |
583 | if (!isColumnHidden(i)) |
584 | columnSize += sizeHintForColumn(i); |
585 | } |
586 | return QSize(columnSize, 50); |
587 | } |
588 | |
589 | |
590 | // **************************************** |
591 | // BufferViewDelgate |
592 | // **************************************** |
593 | class ColorsChangedEvent : public QEvent |
594 | { |
595 | public: |
596 | ColorsChangedEvent() : QEvent(QEvent::User) {}; |
597 | }; |
598 | |
599 | |
600 | BufferViewDelegate::BufferViewDelegate(QObject *parent) |
601 | : QStyledItemDelegate(parent) |
602 | { |
603 | } |
604 | |
605 | |
606 | void BufferViewDelegate::customEvent(QEvent *event) |
607 | { |
608 | if (event->type() != QEvent::User) |
609 | return; |
610 | |
611 | event->accept(); |
612 | } |
613 | |
614 | |
615 | bool BufferViewDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) |
616 | { |
617 | if (event->type() != QEvent::MouseButtonRelease) |
618 | return QStyledItemDelegate::editorEvent(event, model, option, index); |
619 | |
620 | if (!(model->flags(index) & Qt::ItemIsUserCheckable)) |
621 | return QStyledItemDelegate::editorEvent(event, model, option, index); |
622 | |
623 | QVariant value = index.data(Qt::CheckStateRole); |
624 | if (!value.isValid()) |
625 | return QStyledItemDelegate::editorEvent(event, model, option, index); |
626 | |
627 | QStyleOptionViewItemV4 viewOpt(option); |
628 | initStyleOption(&viewOpt, index); |
629 | |
630 | QRect checkRect = viewOpt.widget->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &viewOpt, viewOpt.widget); |
631 | QMouseEvent *me = static_cast<QMouseEvent *>(event); |
632 | |
633 | if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos())) |
634 | return QStyledItemDelegate::editorEvent(event, model, option, index); |
635 | |
636 | Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt()); |
637 | if (state == Qt::Unchecked) |
638 | state = Qt::PartiallyChecked; |
639 | else if (state == Qt::PartiallyChecked) |
640 | state = Qt::Checked; |
641 | else |
642 | state = Qt::Unchecked; |
643 | model->setData(index, state, Qt::CheckStateRole); |
644 | return true; |
645 | } |
646 | |
647 | |
648 | // ============================== |
649 | // BufferView Dock |
650 | // ============================== |
651 | BufferViewDock::BufferViewDock(BufferViewConfig *config, QWidget *parent) |
652 | : QDockWidget(parent), |
653 | _active(false), |
654 | _title(config->bufferViewName()) |
655 | { |
656 | setObjectName("BufferViewDock-" + QString::number(config->bufferViewId())); |
657 | toggleViewAction()->setData(config->bufferViewId()); |
658 | setAllowedAreas(Qt::RightDockWidgetArea|Qt::LeftDockWidgetArea); |
659 | connect(config, SIGNAL(bufferViewNameSet(const QString &)), this, SLOT(bufferViewRenamed(const QString &))); |
660 | updateTitle(); |
661 | } |
662 | |
663 | |
664 | void BufferViewDock::updateTitle() |
665 | { |
666 | QString title = _title; |
667 | if (isActive()) |
668 | title.prepend(QString::fromUtf8("• " )); |
669 | setWindowTitle(title); |
670 | } |
671 | |
672 | |
673 | void BufferViewDock::setActive(bool active) |
674 | { |
675 | if (active != isActive()) { |
676 | _active = active; |
677 | updateTitle(); |
678 | if (active) |
679 | raise(); // for tabbed docks |
680 | } |
681 | } |
682 | |
683 | |
684 | void BufferViewDock::bufferViewRenamed(const QString &newName) |
685 | { |
686 | _title = newName; |
687 | updateTitle(); |
688 | toggleViewAction()->setText(newName); |
689 | } |
690 | |
691 | |
692 | int BufferViewDock::bufferViewId() const |
693 | { |
694 | BufferView *view = bufferView(); |
695 | if (!view) |
696 | return 0; |
697 | |
698 | if (view->config()) |
699 | return view->config()->bufferViewId(); |
700 | else |
701 | return 0; |
702 | } |
703 | |
704 | |
705 | BufferViewConfig *BufferViewDock::config() const |
706 | { |
707 | BufferView *view = bufferView(); |
708 | if (!view) |
709 | return 0; |
710 | else |
711 | return view->config(); |
712 | } |
713 | |