]> Shamusworld >> Repos - architektonas/blob - src/forms/librarywidget.cpp
8ca7ca1b5f361d746ead6144ad3231f1f9cec3af
[architektonas] / src / forms / librarywidget.cpp
1 // librarywidget.cpp
2 //
3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
9 //
10 // JLH = James L. Hammons <jlhamm@acm.org>
11 //
12 // Who  When        What
13 // ---  ----------  -----------------------------------------------------------
14 // JLH  05/10/2010  Created this file. :-)
15 // JLH  08/28/2010  Restored functionality to library browser
16 // JLH  09/06/2010  Partially fixed thumbnail rendering
17 //
18
19 /*
20 BUGS:
21
22 - Picks up thumbnail directories twice--only once if thumbnail dir doesn't exist
23 */
24
25 #include "librarywidget.h"
26
27 #include "actionhandler.h"
28 #include "actionlibraryinsert.h"
29 #include "drawing.h"
30 #include "rs_staticgraphicview.h"
31 #include "rs_system.h"
32 #include "paintinterface.h"
33
34 LibraryWidget::LibraryWidget(QWidget * parent/*= 0*/, Qt::WindowFlags flags/*= 0*/):
35         QWidget(parent, flags), actionHandler(NULL)
36 {
37 #if 0
38 std::cout << "LibraryWidget::LibraryWidget()" << std::endl;
39 #endif
40         ui.setupUi(this);
41         ui.lvDirectory->setColumnCount(1);
42
43         QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
44 #if 0
45 std::cout << "directorySize = " << directoryList.size() << std::endl;
46
47 for(int i=0; i<directoryList.size(); i++)
48         std::cout << directoryList.at(i).toLocal8Bit().constData() << std::endl;
49
50 std::cout.flush();
51 #endif
52
53         for(QStringList::Iterator it=directoryList.begin(); it!=directoryList.end(); it++)
54                 appendTree(NULL, (*it));
55 }
56
57 LibraryWidget::~LibraryWidget()
58 {
59 }
60
61 void LibraryWidget::setActionHandler(ActionHandler * ah)
62 {
63         actionHandler = ah;
64 }
65
66 /**
67  * Escape releases focus.
68  */
69 void LibraryWidget::keyPressEvent(QKeyEvent * e)
70 {
71         switch (e->key())
72         {
73         case Qt::Key_Escape:
74                 emit escape();
75                 break;
76
77         default:
78                 QWidget::keyPressEvent(e);
79                 break;
80         }
81 }
82
83 /**
84  * Insert.
85  */
86 void LibraryWidget::insert()
87 {
88         QListWidgetItem * item = ui.ivPreview->currentItem();
89         QString dxfPath = getItemPath(item);
90
91         if (QFileInfo(dxfPath).isReadable())
92         {
93                 if (actionHandler)
94                 {
95                         ActionInterface * a = actionHandler->setCurrentAction(RS2::ActionLibraryInsert);
96
97                         if (a)
98                         {
99                                 ActionLibraryInsert * action = (ActionLibraryInsert *)a;
100                                 action->setFile(dxfPath);
101                         }
102                         else
103                         {
104                                 RS_DEBUG->print(RS_Debug::D_ERROR, "LibraryWidget::insert:"
105                                         "Cannot create action ActionLibraryInsert");
106                         }
107                 }
108         }
109         else
110         {
111                 RS_DEBUG->print(RS_Debug::D_ERROR,
112                         "LibraryWidget::insert: Can't read file: '%s'", dxfPath.toLatin1().data());
113         }
114 }
115
116 /**
117  * Appends the given directory to the given list view item. Called recursively until all
118  * library directories are appended.
119  */
120 void LibraryWidget::appendTree(QTreeWidgetItem * item, QString directory)
121 {
122         QDir dir(directory);
123
124         if (!dir.exists())
125                 return;
126
127         // read subdirectories of this directory:
128         QStringList lDirectoryList = dir.entryList(QDir::Dirs, QDir::Name);
129
130         for(QStringList::Iterator it=lDirectoryList.begin(); it!=lDirectoryList.end(); it++)
131         {
132                 if ((*it) != "." && (*it) != "..")
133                 {
134                         // Look for an item already existing and take this instead of
135                         // making a new one:
136                         QTreeWidgetItem * newItem = NULL;
137                         QTreeWidgetItem * searchItem =
138                                 (item ? item->child(0) : ui.lvDirectory->topLevelItem(0));
139
140                         if (searchItem)
141                         {
142                                 for(int i=0; i<searchItem->childCount(); i++)
143                                 {
144                                         if (searchItem->child(i)->text(0) == (*it))
145                                         {
146                                                 newItem = searchItem->child(i);
147                                                 break;
148                                         }
149                                 }
150                         }
151
152                         // Create new item if no existing was found:
153                         if (!newItem)
154                         {
155                                 QStringList list;
156                                 list << (*it);
157                                 newItem = (item ? new QTreeWidgetItem(item, list) : new QTreeWidgetItem(ui.lvDirectory, list));
158                         }
159
160                         appendTree(newItem, directory + "/" + (*it));
161                 }
162         }
163 }
164
165 /**
166  * Updates the icon preview.
167  */
168 void LibraryWidget::updatePreview(QTreeWidgetItem * item, int /*column*/)
169 {
170         if (!item)
171                 return;
172
173         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
174
175         // dir from the point of view of the library browser (e.g. /mechanical/screws)
176         QString directory = getItemDir(item);
177         ui.ivPreview->clear();
178
179         // List of all directories that contain part libraries:
180         QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
181         QStringList::Iterator it;
182         QDir itemDir;
183         QStringList itemPathList;
184         QStringList filter;
185         filter << "*.dxf";
186
187         // Look in all possible system directories for DXF files in the current library path:
188         for(it=directoryList.begin(); it!=directoryList.end(); ++it)
189         {
190                 itemDir.setPath((*it) + directory);
191
192                 if (itemDir.exists())
193                 {
194                         QStringList itemNameList = itemDir.entryList(filter, QDir::Files, QDir::Name);
195                         QStringList::Iterator it2;
196
197                         for(it2=itemNameList.begin(); it2!=itemNameList.end(); ++it2)
198                                 itemPathList += itemDir.path() + "/" + (*it2);
199                 }
200         }
201
202         // Sort entries:
203         itemPathList.sort();
204
205         // Fill items into icon view:
206         QListWidgetItem * newItem;
207
208         for(it=itemPathList.begin(); it!=itemPathList.end(); ++it)
209         {
210 //              QString label = QFileInfo(*it).baseName(true);
211                 QString label = QFileInfo(*it).completeBaseName();
212                 QPixmap pixmap = getPixmap(directory, QFileInfo(*it).fileName(), (*it));
213 //              newItem = new QListWidgetItem(ui.ivPreview, label, pixmap);
214                 newItem = new QListWidgetItem(QIcon(pixmap), label, ui.ivPreview);
215 //Doesn't do what we want...
216 //              newItem->setSizeHint(QSize(64, 64));
217         }
218
219         QApplication::restoreOverrideCursor();
220 }
221
222 /**
223  * @return Directory (in terms of the List view) to the given item (e.g. /mechanical/screws)
224  * (called recursively)
225  */
226 QString LibraryWidget::getItemDir(QTreeWidgetItem * item)
227 {
228         if (!item)
229                 return "";
230
231         QTreeWidgetItem * parent = item->parent();
232
233         return getItemDir(parent) + QString("/%1").arg(item->text(0));
234 }
235
236 /**
237  * @return Path of the DXF file that is represented by the given item.
238  */
239 QString LibraryWidget::getItemPath(QListWidgetItem * item)
240 {
241         QString dir = getItemDir(ui.lvDirectory->currentItem());
242
243         if (item)
244         {
245                 // List of all directories that contain part libraries:
246                 QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
247                 QStringList::Iterator it;
248                 QDir itemDir;
249
250                 // Look in all possible system directories for DXF files in the current library path:
251                 for(it=directoryList.begin(); it!=directoryList.end(); ++it)
252                 {
253                         itemDir.setPath((*it) + dir);
254
255                         if (itemDir.exists())
256                         {
257                                 QString f = (*it) + dir + "/" + item->text() + ".dxf";
258
259                                 if (QFileInfo(f).isReadable())
260                                         return f;
261                         }
262                 }
263         }
264
265         return "";
266 }
267
268 /**
269  * @return Pixmap that serves as icon for the given DXF File.
270  * The existing PNG file is returned or created and returned..
271  *
272  * @param dir Library directory (e.g. "/mechanical/screws")
273  * @param dxfFile File name (e.g. "screw1.dxf")
274  * @param dxfPath Full path to the existing DXF file on disk
275  *                          (e.g. /home/tux/.qcad/library/mechanical/screws/screw1.dxf)
276  */
277 QPixmap LibraryWidget::getPixmap(const QString & dir, const QString & dxfFile,
278         const QString & dxfPath)
279 {
280         QString pngFile = getPathToPixmap(dir, dxfFile, dxfPath);
281         QFileInfo fiPng(pngFile);
282
283         // Found existing thumbnail:
284         if (fiPng.isFile())
285                 return QPixmap(pngFile);
286         // Default thumbnail:
287         else
288                 return QPixmap(64, 64);
289 }
290
291 /**
292  * @return Path to the thumbnail of the given DXF file. If no thumbnail exists, one is
293  * created in the user's home. If no thumbnail can be created, an empty string is returned.
294  */
295 QString LibraryWidget::getPathToPixmap(const QString & dir, const QString & dxfFile,
296         const QString & dxfPath)
297 {
298         RS_DEBUG->print("LibraryWidget::getPathToPixmap: dir: '%s' dxfFile: '%s' dxfPath: '%s'",
299                 dir.toLatin1().data(), dxfFile.toLatin1().data(), dxfPath.toLatin1().data());
300
301         // List of all directories that contain part libraries:
302         QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
303         directoryList.prepend(RS_SYSTEM->getHomeDir() + "/.architektonas/library");
304         QStringList::Iterator it;
305
306         QFileInfo fiDxf(dxfPath);
307         QString itemDir;
308         QString pngPath;
309
310         // look in all possible system directories for PNG files
311         //  in the current library path:
312         for(it=directoryList.begin(); it!=directoryList.end(); ++it)
313         {
314                 itemDir = (*it) + dir;
315 //              pngPath = itemDir + "/" + fiDxf.baseName(true) + ".png";
316                 pngPath = itemDir + "/" + fiDxf.completeBaseName() + ".png";
317                 RS_DEBUG->print("LibraryWidget::getPathToPixmap: checking: '%s'",
318                         pngPath.toLatin1().data());
319                 QFileInfo fiPng(pngPath);
320
321                 // the thumbnail exists:
322                 if (fiPng.isFile())
323                 {
324                         RS_DEBUG->print("LibraryWidget::getPathToPixmap: dxf date: %s, png date: %s",
325                                 fiDxf.lastModified().toString().toLatin1().data(), fiPng.lastModified().toString().toLatin1().data());
326
327                         if (fiPng.lastModified() > fiDxf.lastModified())
328                         {
329                                 RS_DEBUG->print("LibraryWidget::getPathToPixmap: thumbnail found: '%s'",
330                                         pngPath.toLatin1().data());
331                                 return pngPath;
332                         }
333                         else
334                         {
335                                 RS_DEBUG->print("LibraryWidget::getPathToPixmap: thumbnail needs to be updated: '%s'",
336                                         pngPath.toLatin1().data());
337                         }
338                 }
339         }
340
341         // The thumbnail must be created in the user's home.
342         // So, create all directories needed:
343         RS_SYSTEM->createHomePath("/.architektonas/library" + dir);
344
345         QString d = RS_SYSTEM->getHomeDir() + "/.architektonas/library" + dir;
346 //printf("librarywidget: d = \"%s\"\n", d.toAscii().data());
347
348 //      pngPath = d + "/" + fiDxf.baseName(true) + ".png";
349         pngPath = d + "/" + fiDxf.completeBaseName() + ".png";
350 //printf("librarywidget: pngPath = \"%s\"\n", pngPath.toAscii().data());
351
352         // Ugh. This is a mess and doesn't work right anyway...
353         // Problem is StaticGraphicView doesn't follow a standard rendering path,
354         // so all this crap just ends in failure. Not sure how to fix.
355 /**
356 I suppose one way to do it would be to make a CreateImage() function in GraphicView,
357 then we wouldn't have all this messiness...
358 I think the way the redraw works is like that--it creates its own PaintInterface
359 and QPainter and sets "painter" equal to it... That being the case, it would be
360 a simple matter to modify StaticGraphicView to utilize that... Let's see...
361 Which it should be doing, but, for some reason, it fails. Dunno why.
362
363 It only fails for certain DXFs. Dunno why.
364 */
365
366         QPixmap buffer(128, 128);
367         QPainter qpntr(&buffer);
368         PaintInterface * painter = new PaintInterface(&qpntr);
369         qpntr.setBackground(Qt::white);
370         qpntr.eraseRect(0, 0, 128, 128);
371
372         RS_StaticGraphicView gv(128, 128, painter);
373         Drawing drawing;
374
375         if (drawing.open(dxfPath, RS2::FormatUnknown))
376         {
377                 RS_Color black(0, 0, 0);
378
379                 // Set all pens in the drawing to BLACK
380                 for(RS_Entity * e=drawing.firstEntity(RS2::ResolveAll); e!=NULL; e=drawing.nextEntity(RS2::ResolveAll))
381                 {
382                         RS_Pen pen = e->getPen();
383                         pen.setColor(black);
384                         e->setPen(pen);
385                 }
386
387                 gv.setContainer(&drawing);
388                 gv.zoomAuto(false);
389 //This works, but somehow doesn't work ALL the time
390                 gv.drawEntity(&drawing, true);
391
392                 QImageWriter writer;
393                 QImage image = buffer.toImage();
394                 image.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
395                 writer.setFileName(pngPath);
396                 writer.setFormat("png");
397
398                 if (!writer.write(image))
399                 {
400                         RS_DEBUG->print(RS_Debug::D_ERROR,
401                                 "LibraryWidget::getPathToPixmap: Cannot write thumbnail: '%s'",
402                                 pngPath.toLatin1().data());
403                         pngPath = "";
404                 }
405         }
406         else
407         {
408                 RS_DEBUG->print(RS_Debug::D_ERROR, "LibraryWidget::getPathToPixmap: Cannot open file: '%s'", dxfPath.toLatin1().data());
409         }
410
411         delete painter;
412
413         return pngPath;
414 }
415
416 #if 0
417
418 QWidget::setMinimumSize: (/QMdi::ControlLabel) Negative sizes (-1,-1) are not possible
419 QPaintDevice: Cannot destroy paint device that is being painted
420
421 Program received signal SIGSEGV, Segmentation fault.
422 0x085cd068 in ?? ()
423 (gdb) bt
424 #0  0x085cd068 in ?? ()
425 #1  0xb7818bd0 in QPainter::~QPainter() () from /usr/lib/qt4/libQtGui.so.4
426 #2  0x081c1e8e in LibraryWidget::getPathToPixmap(QString const&, QString const&, QString const&) ()
427 #3  0x081c0fa6 in LibraryWidget::getPixmap(QString const&, QString const&, QString const&) ()
428 #4  0x081c0775 in LibraryWidget::updatePreview(QTreeWidgetItem*, int) ()
429 #5  0x08237d5e in LibraryWidget::qt_metacall(QMetaObject::Call, int, void**) ()
430
431 #endif