1 /**************************************************************************
2 ** This file is part of LiteIDE
3 **
4 ** Copyright (c) 2011-2017 LiteIDE. All rights reserved.
5 **
6 ** This library is free software; you can redistribute it and/or
7 ** modify it under the terms of the GNU Lesser General Public
8 ** License as published by the Free Software Foundation; either
9 ** version 2.1 of the License, or (at your option) any later version.
10 **
11 ** This library is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 ** Lesser General Public License for more details.
15 **
16 ** In addition, as a special exception,  that plugins developed for LiteIDE,
17 ** are allowed to remain closed sourced and can be distributed under any license .
18 ** These rights are included in the file LGPL_EXCEPTION.txt in this package.
19 **
20 **************************************************************************/
21 // Module: bookmarkmodel.cpp
22 // Creator: visualfc <visualfc@gmail.com>
23 
24 #include "bookmarkmodel.h"
25 #include <QPainter>
26 //lite_memory_check_begin
27 #if defined(WIN32) && defined(_MSC_VER) &&  defined(_DEBUG)
28      #define _CRTDBG_MAP_ALLOC
29      #include <stdlib.h>
30      #include <crtdbg.h>
31      #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__ )
32      #define new DEBUG_NEW
33 #endif
34 //lite_memory_check_end
35 
addNode(LiteApi::IEditorMark * mark,LiteApi::IEditorMarkNode * node)36 void BookmarkModel::addNode(LiteApi::IEditorMark *mark, LiteApi::IEditorMarkNode *node)
37 {
38     beginInsertRows(QModelIndex(), m_nodeList.size(), m_nodeList.size());
39 
40     BookmarkNode *bn = createBookmarkNode(mark,node);
41     m_nodeList.append(bn);
42     m_nodeMap.insert(node,bn);
43 
44     endInsertRows();
45     //selectionModel()->setCurrentIndex(index(m_bookmarksList.size()-1 , 0, QModelIndex()), QItemSelectionModel::Select | QItemSelectionModel::Clear);
46 }
47 
removeNode(LiteApi::IEditorMark * mark,LiteApi::IEditorMarkNode * node)48 void BookmarkModel::removeNode(LiteApi::IEditorMark *mark, LiteApi::IEditorMarkNode *node)
49 {
50     BookmarkNode *bn = findBookmarkNode(mark,node);
51     if (!bn) {
52         return;
53     }
54     int idx = m_nodeList.indexOf(bn);
55     beginRemoveRows(QModelIndex(), idx, idx);
56 
57     m_nodeMap.remove(node);
58 
59     delete bn;
60 
61     m_nodeList.removeAt(idx);
62     endRemoveRows();
63  //   if (selectionModel()->currentIndex().isValid())
64     //       selectionModel()->setCurrentIndex(selectionModel()->currentIndex(), QItemSelectionModel::Select | QItemSelectionModel::Clear);
65 }
66 
updateNode(LiteApi::IEditorMark * mark,LiteApi::IEditorMarkNode * node)67 void BookmarkModel::updateNode(LiteApi::IEditorMark *mark, LiteApi::IEditorMarkNode *node)
68 {
69     BookmarkNode *bn = findBookmarkNode(mark,node);
70     if (!bn) {
71         return;
72     }
73     bn->setLineNumber(node->blockNumber()+1);
74     bn->setLineText(node->block().text());
75     int idx = m_nodeList.indexOf(bn);
76     QModelIndex i = index(idx,0,QModelIndex());
77     emit dataChanged(i,i);
78 }
79 
createBookmarkNode(LiteApi::IEditorMark * mark,LiteApi::IEditorMarkNode * node) const80 BookmarkNode *BookmarkModel::createBookmarkNode(LiteApi::IEditorMark *mark, LiteApi::IEditorMarkNode *node) const
81 {
82     BookmarkNode *n = new BookmarkNode();
83     n->setFilePath(mark->filePath());
84     n->setLineNumber(node->blockNumber()+1);
85     n->setLineText(node->block().text());
86     return n;
87 }
88 
bookmarkNodeForIndex(const QModelIndex & index) const89 BookmarkNode *BookmarkModel::bookmarkNodeForIndex(const QModelIndex &index) const
90 {
91     if (!index.isValid() || index.row() >= m_nodeList.size())
92         return 0;
93     return m_nodeList.at(index.row());
94 }
95 
findBookmarkNode(LiteApi::IEditorMark *,LiteApi::IEditorMarkNode * node) const96 BookmarkNode *BookmarkModel::findBookmarkNode(LiteApi::IEditorMark */*mark*/, LiteApi::IEditorMarkNode *node) const
97 {
98     return m_nodeMap.value(node);
99 }
100 
BookmarkModel(QObject * parent)101 BookmarkModel::BookmarkModel(QObject *parent)
102     : QAbstractItemModel(parent)
103 {
104 }
105 
index(int row,int column,const QModelIndex & parent) const106 QModelIndex BookmarkModel::index(int row, int column, const QModelIndex &parent) const
107 {
108     if (parent.isValid())
109         return QModelIndex();
110     else
111         return createIndex(row, column);
112 }
113 
parent(const QModelIndex & index) const114 QModelIndex BookmarkModel::parent(const QModelIndex &index) const
115 {
116     return QModelIndex();
117 }
118 
rowCount(const QModelIndex & parent) const119 int BookmarkModel::rowCount(const QModelIndex &parent) const
120 {
121     if (parent.isValid())
122         return 0;
123 
124     return m_nodeList.size();
125 }
126 
columnCount(const QModelIndex & parent) const127 int BookmarkModel::columnCount(const QModelIndex &parent) const
128 {
129     if (parent.isValid())
130         return 0;
131 
132     return 1;
133 }
134 
data(const QModelIndex & index,int role) const135 QVariant BookmarkModel::data(const QModelIndex &index, int role) const
136 {
137     if (!index.isValid() || index.column() !=0 || index.row() < 0 || index.row() >= m_nodeList.count())
138         return QVariant();
139 
140     BookmarkNode *node = m_nodeList.at(index.row());
141     if (role == BookmarkModel::FileName)
142         return node->fileName();
143     if (role == BookmarkModel::LineNumber)
144         return node->lineNumber();
145     if (role == BookmarkModel::FilePath)
146         return node->filePath();
147     if (role == BookmarkModel::LineText)
148         return node->lineText();
149     if (role == BookmarkModel::Note)
150         return node->noteText();
151     if (role == Qt::ToolTipRole)
152         return QDir::toNativeSeparators(node->filePath());
153     return QVariant();
154 }
155 
BookmarkDelegate(QObject * parent)156 BookmarkDelegate::BookmarkDelegate(QObject *parent)
157     : QStyledItemDelegate(parent)
158 {
159 }
160 
sizeHint(const QStyleOptionViewItem & option,const QModelIndex & index) const161 QSize BookmarkDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
162 {
163     QStyleOptionViewItem opt = option;
164     initStyleOption(&opt, index);
165 
166     QFontMetrics fm(option.font);
167     QSize s;
168     s.setWidth(option.rect.width());
169     s.setHeight(fm.height() * 2 + 10);
170     return s;
171 }
172 
generateGradientPixmap(int width,int height,const QColor & color,bool selected) const173 void BookmarkDelegate::generateGradientPixmap(int width, int height, const QColor &color, bool selected) const
174 {
175     QColor c = color;
176     c.setAlpha(0);
177 
178     QPixmap pixmap(width+1, height);
179     pixmap.fill(c);
180 
181     QPainter painter(&pixmap);
182     painter.setPen(Qt::NoPen);
183 
184     QLinearGradient lg;
185     lg.setCoordinateMode(QGradient::ObjectBoundingMode);
186     lg.setFinalStop(1,0);
187 
188     lg.setColorAt(0, c);
189     lg.setColorAt(0.4, color);
190 
191     painter.setBrush(lg);
192     painter.drawRect(0, 0, width+1, height);
193 
194     if (selected)
195         m_selectedPixmap = pixmap;
196     else
197         m_normalPixmap = pixmap;
198 }
199 
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const200 void BookmarkDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
201 {
202     QStyleOptionViewItem opt = option;
203     initStyleOption(&opt, index);
204     painter->save();
205 
206     QFontMetrics fm(opt.font);
207     static int lwidth = fm.width(QLatin1String("8888")) + 18;
208 
209     QColor backgroundColor;
210     QColor textColor;
211 
212     bool selected = opt.state & QStyle::State_Selected;
213 
214     if (selected) {
215         painter->setBrush(opt.palette.highlight().color());
216         backgroundColor = opt.palette.highlight().color();
217         if (!m_selectedPixmap)
218             generateGradientPixmap(lwidth, fm.height()+1, backgroundColor, selected);
219     } else {
220         painter->setBrush(opt.palette.background().color());
221         backgroundColor = opt.palette.background().color();
222         if (!m_normalPixmap)
223             generateGradientPixmap(lwidth, fm.height(), backgroundColor, selected);
224     }
225     painter->setPen(Qt::NoPen);
226     painter->drawRect(opt.rect);
227 
228     // Set Text Color
229     if (opt.state & QStyle::State_Selected)
230         textColor = opt.palette.highlightedText().color();
231     else
232         textColor = opt.palette.text().color();
233 
234     painter->setPen(textColor);
235 
236 
237     // TopLeft
238     QString topLeft = index.data(BookmarkModel::FileName).toString();
239     //painter->drawText(6, 2 + opt.rect.top() + fm.ascent(), topLeft);
240 
241     QString topRight = index.data(BookmarkModel::LineNumber).toString();
242     // Check whether we need to be fancy and paint some background
243     int fwidth = fm.width(topLeft);
244     if (fwidth + lwidth > opt.rect.width()) {
245         int left = opt.rect.right() - lwidth;
246         painter->drawPixmap(left, opt.rect.top(), selected ? m_selectedPixmap : m_normalPixmap);
247     }
248     // topRight
249     painter->drawText(opt.rect.right() - fm.width(topRight) - 6 , 2 + opt.rect.top() + fm.ascent(), topRight);
250 
251     // Directory
252     QColor mix;
253     mix.setRgbF(0.7 * textColor.redF()   + 0.3 * backgroundColor.redF(),
254                 0.7 * textColor.greenF() + 0.3 * backgroundColor.greenF(),
255                 0.7 * textColor.blueF()  + 0.3 * backgroundColor.blueF());
256     painter->setPen(mix);
257 
258     QString directory = index.data(BookmarkModel::FilePath).toString();
259     int availableSpace = opt.rect.width() - fm.width("888");
260     if (fm.width(directory) > availableSpace) {
261         // We need a shorter directory
262         availableSpace -= fm.width("...");
263 
264         int pos = directory.size();
265         int idx;
266         forever {
267             idx = directory.lastIndexOf("/", pos-1);
268             if (idx == -1) {
269                 // Can't happen, this means the string did fit after all?
270                 break;
271             }
272             int width = fm.width(directory.mid(idx, pos-idx));
273             if (width > availableSpace) {
274                 directory = "..." + directory.mid(pos);
275                 break;
276             } else {
277                 pos = idx;
278                 availableSpace -= width;
279             }
280         }
281     }
282 
283     //painter->drawText(3, opt.rect.top() + fm.ascent() + fm.height() + 6, directory);
284     painter->drawText(6, 2 + opt.rect.top() + fm.ascent(), directory);
285 
286     QString lineText = index.data(BookmarkModel::Note).toString().trimmed();
287     if (lineText.isEmpty())
288         lineText = index.data(BookmarkModel::LineText).toString().trimmed();
289 
290     painter->drawText(6, opt.rect.top() + fm.ascent() + fm.height() + 6, lineText);
291 
292     // Separator lines
293     const QRectF innerRect = QRectF(opt.rect).adjusted(0.5, 0.5, -0.5, -0.5);
294     painter->setPen(QColor::fromRgb(150,150,150));
295     painter->drawLine(innerRect.bottomLeft(), innerRect.bottomRight());
296     painter->restore();
297 }
298