1 /***************************************************************************
2  *   Copyright (C) 2007 by Pierre Marchand   *
3  *   pierre@oep-h.com   *
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) 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                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 #include "fmpreviewlist.h"
21 
22 #include "typotek.h"
23 #include "fontitem.h"
24 #include "mainviewwidget.h"
25 #include "fmfloatingpreview.h"
26 #include "fmfontdb.h"
27 
28 #include <QImage>
29 #include <QDebug>
30 #include <QSettings>
31 #include <QScrollBar>
32 //#include <QDrag>
33 //#include <QMimeData>
34 #include <QApplication>
35 #include <QLabel>
36 #include <QPainter>
37 #include <QBrush>
38 
39 #define FM_MINIMUM_PREVIEW_WIDTH 280
40 
41 bool FMPreviewIconEngine::initState = false;
42 QPen FMPreviewIconEngine::pen = QPen();
43 QVector<QRgb> FMPreviewIconEngine::m_selPalette;
44 QRgb FMPreviewIconEngine::activatedColor =  qRgb (9,223,11);
45 QRgb FMPreviewIconEngine::deactivatedColor =  qRgb (190,190,190);
46 QRgb FMPreviewIconEngine::partlyActivatedColor =  qRgb (166,220,220);
47 
48 
FMPreviewIconEngine()49 FMPreviewIconEngine::FMPreviewIconEngine()
50 	:QIconEngine(),
51 	activatedFont(NotActivated)
52 {
53 	if(!initState)
54 	{
55 		// setup palette
56 		QColor sColor( QApplication::palette().color(QPalette::Highlight) );
57 		QColor tColor( QApplication::palette().color(QPalette::HighlightedText) );
58 		m_selPalette.clear();
59 		int sr(sColor.red());
60 		int sg(sColor.green());
61 		int sb(sColor.blue());
62 		int tr(tColor.red());
63 		int tg(tColor.green());
64 		int tb(tColor.blue());
65 		int cpal(256);
66 		for ( int aa = 0; aa < cpal ; ++aa )
67 		{
68 			int sn(cpal - aa);
69 			int tn(aa);
70 			m_selPalette << qRgb (((sr*sn) + (tr*tn)) /cpal,
71 					      ((sg*sn) + (tg*tn)) /cpal,
72 					      ((sb*sn) + (tb*tn)) /cpal );
73 		}
74 
75 		// setup "writing" pen
76 //		pen.setColor(QColor(m_selPalette.at(128)));
77 		pen.setColor(QColor(220,220,220));
78 		pen.setWidth(1);
79 
80 		initState = true;
81 	}
82 
83 }
84 
clone() const85 QIconEngine *FMPreviewIconEngine::clone() const
86 {
87 	// TODO Implement this function
88 	return 0;
89 }
90 
actualSelPalette(const QVector<QRgb> & orig)91 QVector<QRgb> FMPreviewIconEngine::actualSelPalette(const QVector<QRgb>& orig)
92 {
93 	QRgb r(QApplication::palette().color(QPalette::Highlight).rgb());
94 	QVector<QRgb> ret;
95 	for(int i(0); i<256; ++i)
96 		ret << r;
97 	QColor bgColor(QApplication::palette().color(QPalette::Base));
98 	QColor fgColor(QApplication::palette().color(QPalette::Text));
99 
100 	// In m_selPalette, background is at the begining of the vector
101 
102 	bool DarkOnLight(fgColor.rgb() < bgColor.rgb());
103 	// order is dark first, light last
104 	QMap<QRgb, int> order;
105 	for(int i(0); i < orig.count(); ++i)
106 		order[orig[i]] = i;
107 	QList<int> oIdx(order.values());
108 	if(DarkOnLight)
109 	{
110 		// oIdx has background values at the end
111 		int v(0);
112 		for(int c(oIdx.count() - 1); c >= 0 ; --c)
113 		{
114 			ret [oIdx[c]] =  m_selPalette[v];
115 			++v;
116 		}
117 	}
118 	else
119 	{
120 		int v(0);
121 		for(int c(0); c < oIdx.count() ; c++)
122 		{
123 			ret [oIdx[c]] =  m_selPalette[v];
124 			++v;
125 		}
126 	}
127 	return ret;
128 }
129 
130 
~FMPreviewIconEngine()131 FMPreviewIconEngine::~FMPreviewIconEngine()
132 {
133 	//	if(m_p)
134 	//		delete m_p;
135 }
136 
paint(QPainter * painter,const QRect & rect,QIcon::Mode mode,QIcon::State state)137 void FMPreviewIconEngine::paint ( QPainter * painter, const QRect & rect, QIcon::Mode mode, QIcon::State state )
138 {
139 	if(!m_p.isNull())
140 	{
141 		painter->save();
142 		painter->translate(rect.x(),rect.y());
143 		QRect r(0 , 0 , rect.width(), rect.height());
144 		QPainterPath pp;
145 		double rr(double(r.height()) / 5.0);
146 		pp.addRoundedRect(r,rr,rr);
147 		painter->setRenderHint(QPainter::Antialiasing, true);
148 		// draw background
149 		painter->save();
150 		painter->setPen(Qt::NoPen);
151 		if(mode == QIcon::Selected)
152 			painter->setBrush(QApplication::palette().color(QPalette::Highlight));
153 		else
154 			painter->setBrush(QApplication::palette().color(QPalette::Base));
155 		painter->drawPath(pp);
156 		painter->restore();
157 		// end of bg
158 		painter->setPen(pen);
159 		QRect tr(r);
160 		tr.translate(0, (r.height() - m_p.height()) / 2);
161 		if(mode == QIcon::Selected)
162 		{
163 			QImage hm(m_p.toImage().convertToFormat(QImage::Format_Indexed8));
164 			hm.setColorTable(actualSelPalette(hm.colorTable()));
165 			painter->setClipPath(pp);
166 			painter->drawPixmap(tr, QPixmap::fromImage(hm) , r);
167 			painter->drawPath(pp);
168 		}
169 		else
170 		{
171 			painter->drawPixmap(tr, m_p , r);
172 			painter->drawPath(pp);
173 
174 		}
175 		if(activatedFont != NotActivated)
176 		{
177 			painter->setPen(Qt::NoPen);
178 			QPainterPath activationPath;
179 			double rr2(rr/2.0);
180 			activationPath.moveTo(rr, 0);
181 			activationPath.cubicTo(rr2,0,
182 					       0,rr2,
183 					       0,rr);
184 			activationPath.lineTo(0,rect.height() - rr);
185 			activationPath.cubicTo(0,rect.height() -rr2,
186 					       rr2,rect.height(),
187 					       rr,rect.height());
188 			activationPath.closeSubpath();
189 			if(activatedFont == Activated)
190 				painter->setBrush(QBrush(activatedColor));
191 			else if(activatedFont == PartlyActivated)
192 				painter->setBrush(QBrush(partlyActivatedColor));
193 			painter->drawPath(activationPath);
194 		}
195 		painter->restore();
196 	}
197 }
198 
addPixmap(const QPixmap & pixmap,QIcon::Mode mode,QIcon::State state)199 void FMPreviewIconEngine::addPixmap ( const QPixmap & pixmap, QIcon::Mode mode, QIcon::State state )
200 {
201 	m_p = pixmap;
202 }
203 
204 
FMPreviewModel(QObject * pa,FMPreviewView * wPa,QList<FontItem * > db)205 FMPreviewModel::FMPreviewModel( QObject * pa , FMPreviewView * wPa,  QList<FontItem*> db )
206 	: QAbstractListModel(pa) , m_view(wPa), base(db)
207 {
208 	familyMode = false;
209 	QSettings settings;
210 	styleTooltipName = settings.value("Preview/StyleTooltipName","font-weight:bold;").toString();
211 	styleTooltipPath = settings.value("Preview/StyleTooltipPath","font-weight:normal;font-size:small;").toString();
212 	styleTooltipTags = settings.value("Preview/StyleTooltipTags","text-align:right;font-weight:normal;font-size:small;font-style:italic;").toString();
213 
214 	settings.setValue("Preview/StyleTooltipName", styleTooltipName);
215 	settings.setValue("Preview/StyleTooltipPath", styleTooltipPath);
216 	settings.setValue("Preview/StyleTooltipTags", styleTooltipTags);
217 }
218 
data(const QModelIndex & index,int role) const219 QVariant FMPreviewModel::data(const QModelIndex & index, int role) const
220 {
221 	if(!index.isValid())
222 		return QVariant();
223 
224 	int row = index.row();
225 	// 	qDebug()<<"D"<<row;
226 	FontItem *fit;
227 	if(base.isEmpty())
228 		fit = FMFontDb::DB()->getFilteredFonts(true).at(row);
229 	else
230 		fit = base.at(row);
231 	if(!fit)
232 		return QVariant();
233 
234 	QColor bgColor(QApplication::palette().color(QPalette::Base));
235 	QColor fgColor(QApplication::palette().color(QPalette::Text));
236 
237 	int width(m_view->getUsedWidth());
238 
239 	if(role == Qt::DisplayRole)
240 	{
241 		if( typotek::getInstance()->getPreviewSubtitled() )
242 			return fit->fancyName() ;
243 		else
244 			return QVariant();
245 	}
246 	else if(role == Qt::DecorationRole)
247 	{
248 		QString word;
249 		if(specString.isEmpty())
250 			word = typotek::getInstance()->word(fit);
251 		else
252 			word = typotek::getInstance()->word(fit, specString);
253 		QPixmap im(fit->oneLinePreviewPixmap(word,fgColor, bgColor, width ) );
254 		FMPreviewIconEngine * pie(new FMPreviewIconEngine);
255 		if(!familyMode)
256 			pie->setActivation(fit->isActivated() ? FMPreviewIconEngine::Activated : FMPreviewIconEngine::NotActivated);
257 		else
258 		{
259 			bool hasActive(false);
260 			bool hasNotActive(false);
261 			foreach(FontItem * f, FMFontDb::DB()->FamilySet(fit->family()))
262 			{
263 				if(f->isActivated())
264 					hasActive = true;
265 				else
266 					hasNotActive = true;
267 				if(hasActive && hasNotActive)
268 					break;
269 			}
270 			if(hasNotActive && hasActive)
271 				pie->setActivation(FMPreviewIconEngine::PartlyActivated);
272 			else
273 			{
274 				pie->setActivation(hasActive ? FMPreviewIconEngine::Activated : FMPreviewIconEngine::NotActivated);
275 			}
276 		}
277 		QIcon ic( pie );
278 		ic.addPixmap(im);
279 
280 		return ic;
281 	}
282 	else if(role == Qt::ToolTipRole)
283 	{
284 		if(familyMode)
285 		{
286 			QList<FontItem*> fam(FMFontDb::DB()->FamilySet(fit->family()));
287 			QString sRet;
288 			sRet+= "<div style=\"" + styleTooltipName + "\">" + fit->family() + " ("+QString::number(fam.count())+")</div>";
289 			sRet+= "<div style=\"" + styleTooltipTags + "\">" + fit->tags().join(QString(", ")) + "</div>";
290 
291 			foreach(FontItem* ffi, fam)
292 			{
293 				sRet += "<div style=\"" + styleTooltipPath + "\">" + ffi->variant() + "</div>";
294 			}
295 			return sRet;
296 		}
297 		if(typotek::getInstance()->getPreviewSubtitled())
298 		{
299 			return QString("<div style=\"" + styleTooltipPath + "\">" + fit->path() + "</div>");
300 		}
301 		else
302 		{
303 			QString complete;
304 			complete += "<div style=\"" + styleTooltipName + "\">" + fit->fancyName() + "</div>";
305 			complete += "\n";
306 			complete += "<div style=\"" + styleTooltipPath + "\">" + fit->path() + "</div>";
307 			return complete;
308 		}
309 	}
310 	else if(role == PathRole)
311 	{
312 		return fit->path();
313 	}
314 
315 	// fall back
316 	return QVariant();
317 
318 }
319 
flags(const QModelIndex & index) const320 Qt::ItemFlags FMPreviewModel::flags(const QModelIndex & index) const
321 {
322 	return (Qt::ItemIsEnabled | Qt::ItemIsSelectable);
323 }
324 
rowCount(const QModelIndex & parent) const325 int FMPreviewModel::rowCount(const QModelIndex & parent) const
326 {
327 	if(parent.isValid() || !typotek::getInstance()->getTheMainView())
328 		return 0;
329 	int cl(0);
330 	if(base.isEmpty())
331 		cl = FMFontDb::DB()->getFilteredFonts(true).count();
332 	else
333 		cl = base.count();
334 	return cl;
335 }
336 
dataChanged()337 void FMPreviewModel::dataChanged()
338 {
339 	QAbstractItemModel::dataChanged(index(0),index(rowCount(QModelIndex()) - 1));
340 	m_view->updateLayout();
341 	emit layoutChanged ();
342 }
343 
resetBase(QList<FontItem * > db)344 void FMPreviewModel::resetBase(QList<FontItem *>db)
345 {
346 	base = db;
347 	dataChanged();
348 }
349 
getBase()350 QList<FontItem *> FMPreviewModel::getBase()
351 {
352 	if(base.isEmpty())
353 		return FMFontDb::DB()->getFilteredFonts(true);
354 	else
355 		return base;
356 }
357 
358 
FMPreviewView(QWidget * parent)359 FMPreviewView::FMPreviewView(QWidget * parent):
360 		QListView(parent),
361 		columns(1)
362 {
363 	dragFlag = false;
364 	setDragEnabled(true);
365 	setDragDropMode(QAbstractItemView::DragDrop);
366 	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
367         setSelectionRectVisible(false);
368 
369 }
370 
moveTo(const QString & fname)371 bool FMPreviewView::moveTo(const QString &fname)
372 {
373 	QList<FontItem*> fl(reinterpret_cast<FMPreviewModel*>(model())->getBase());
374 
375 	QString uname(fname.toUpper());
376 	const int fl_count(fl.count());
377 	int rFont(fl_count);
378 	for(int i(0); i < fl_count ; ++i)
379 	{
380 		QString pname(fl[i]->fancyName().toUpper());
381 		pname.truncate(uname.count());
382 		if(uname == pname)
383 		{
384 			rFont = i;
385 			break;
386 		}
387 	}
388 
389 	if(rFont != fl_count)
390 	{
391 		QAbstractListModel *mod(reinterpret_cast<QAbstractListModel*>(model()));
392 		QModelIndex mi(mod->index(rFont));
393 		if(mi.isValid())
394 		{
395 			selectionModel()->setCurrentIndex(mi, QItemSelectionModel::ClearAndSelect);
396 			// scrollTo ( mi );
397 			return true;
398 		}
399 	}
400 
401 	return false;
402 }
403 
resizeEvent(QResizeEvent * event)404 void FMPreviewView::resizeEvent(QResizeEvent * event)
405 {
406         int actualWidth(width() - 20); // if we use the viewport size, it becomes funny when a resize shows/hides the scrollbar
407         setSpacing(0);
408         int gHeight(2.0 * typotek::getInstance()->getPreviewSize() * typotek::getInstance()->getDpiY() / 72.0);
409         qDebug()<< "VW" << actualWidth<<verticalScrollBar()->width()<< "S" <<spacing();
410         double cNr(1);
411 
412 	if(columns == 1)
413                 usedWidth = qRound((double(actualWidth)  / columns));
414 	else
415         {
416             int minCellWidth(FM_MINIMUM_PREVIEW_WIDTH + 6);
417             cNr = qRound(double(actualWidth) / minCellWidth);
418             minCellWidth =  qRound((double(actualWidth)  / cNr) - 6);
419             qDebug()<< "C" << cNr << "U" << minCellWidth ;
420             setGridSize(QSize(minCellWidth, gHeight + 12));
421             usedWidth = minCellWidth - 6;
422         }
423         setIconSize(QSize(usedWidth, gHeight + 6));
424 	QListView::resizeEvent(event);
425 }
426 
mousePressEvent(QMouseEvent * event)427 void FMPreviewView::mousePressEvent(QMouseEvent * event)
428 {
429 	if (event->button() == Qt::LeftButton)
430 	{
431 		startDragPoint = event->pos();
432 		dragFlag = false;
433 		//		const QModelIndex idx ( indexAt(startDragPoint) );
434 		//		if(idx.isValid())
435 		//			emit pressed(idx);
436 	}
437 	QListView::mousePressEvent(event);
438 }
439 
mouseMoveEvent(QMouseEvent * event)440 void FMPreviewView::mouseMoveEvent(QMouseEvent * event)
441 {
442 	if(!(event->modifiers().testFlag( Qt::ControlModifier )))
443 		return;
444 	if (!(event->buttons() & Qt::LeftButton))
445 		return;
446 	if ((event->pos() - startDragPoint).manhattanLength() < QApplication::startDragDistance())
447 		return;
448 
449 	FMPreviewModel * m(reinterpret_cast<FMPreviewModel*>(model()));
450 	if(m && m->getFamilyMode())
451 		return;
452 	// Create a window with the current preview
453 	if(currentIndex().isValid() && (!dragFlag))
454 	{
455 		dragFlag = true;
456 		//		FontItem * sf(typotek::getInstance()->getSelectedFont());
457 		QModelIndex idx = indexAt(startDragPoint);
458 		QString fname = idx.data(FMPreviewModel::PathRole).toString();
459 		if(!fname.isEmpty())
460 		{
461 			FontItem * sf(FMFontDb::DB()->Font(fname));
462 			if(sf)
463 				FMFloatingPreview::create(sf, QRect(event->globalPos(), QSize(width() ,1) ));
464 		}
465 	}
466 
467 }
468 
keyPressEvent(QKeyEvent * event)469 void FMPreviewView::keyPressEvent(QKeyEvent *event)
470 {
471 	qDebug()<<"FMPreviewView::keyPressEvent"<<event;
472 	if((!event->text().isEmpty()) && (event->text().at(0).isLetterOrNumber()))
473 		emit keyPressed(event->text());
474 	else
475 		QListView::keyPressEvent(event);
476 }
477 
updateLayout()478 void FMPreviewView::updateLayout()
479 {
480 	update();
481 }
482 
setCurrentFont(const QString & name)483 void FMPreviewView::setCurrentFont(const QString & name)
484 {
485 	QList<FontItem*> fl(reinterpret_cast<FMPreviewModel*>(model())->getBase());
486 
487 	const int fl_count(fl.count());
488 	int rFont(fl_count);
489 	for(int i(0); i < fl_count ; ++i)
490 	{
491 		if(fl[i]->path() == name)
492 		{
493 			rFont = i;
494 			break;
495 		}
496 	}
497 
498 	if(rFont != fl_count)
499 	{
500 		QAbstractListModel *mod(reinterpret_cast<QAbstractListModel*>(model()));
501 		QModelIndex mi(mod->index(rFont));
502 		if(mi.isValid())
503 		{
504 			selectionModel()->setCurrentIndex(mi, QItemSelectionModel::ClearAndSelect);
505 			// scrollTo ( mi );
506 		}
507 	}
508 }
509