]> Shamusworld >> Repos - architektonas/blob - src/forms/librarywidget.cpp
3f8d67ecc89450329b289b14e417733d75e75820
[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 //This is picking up the directory tree TWICE, but ONLY if there are no thumbnails!
161 //Actually, only if there is no corresponding thumbnail DIRECTORY under .architektonas...
162 //Dunno why...
163 //It was picking up .architektonas from the home directory in rs_system.cpp, that's why!
164 //printf("LibraryWidget::appendTree: *it=\"%s\"\n", (*it).toAscii().data());
165                         appendTree(newItem, directory + "/" + (*it));
166                 }
167         }
168 }
169
170 /**
171  * Updates the icon preview.
172  */
173 void LibraryWidget::updatePreview(QTreeWidgetItem * item, int /*column*/)
174 {
175         if (!item)
176                 return;
177
178         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
179
180         // dir from the point of view of the library browser (e.g. /mechanical/screws)
181         QString directory = getItemDir(item);
182         ui.ivPreview->clear();
183
184         // List of all directories that contain part libraries:
185         QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
186         QStringList::Iterator it;
187         QDir itemDir;
188         QStringList itemPathList;
189         QStringList filter;
190         filter << "*.dxf";
191
192         // Look in all possible system directories for DXF files in the current library path:
193         for(it=directoryList.begin(); it!=directoryList.end(); ++it)
194         {
195                 itemDir.setPath((*it) + directory);
196
197                 if (itemDir.exists())
198                 {
199                         QStringList itemNameList = itemDir.entryList(filter, QDir::Files, QDir::Name);
200                         QStringList::Iterator it2;
201
202                         for(it2=itemNameList.begin(); it2!=itemNameList.end(); ++it2)
203                                 itemPathList += itemDir.path() + "/" + (*it2);
204                 }
205         }
206
207         // Sort entries:
208         itemPathList.sort();
209
210         // Fill items into icon view:
211         QListWidgetItem * newItem;
212
213         for(it=itemPathList.begin(); it!=itemPathList.end(); ++it)
214         {
215 //              QString label = QFileInfo(*it).baseName(true);
216                 QString label = QFileInfo(*it).completeBaseName();
217                 QPixmap pixmap = getPixmap(directory, QFileInfo(*it).fileName(), (*it));
218 //              newItem = new QListWidgetItem(ui.ivPreview, label, pixmap);
219                 newItem = new QListWidgetItem(QIcon(pixmap), label, ui.ivPreview);
220 //Doesn't do what we want...
221 //              newItem->setSizeHint(QSize(64, 64));
222 //printf("LibraryWidget: label = \"%s\"\n", label.toAscii().data());
223         }
224
225         QApplication::restoreOverrideCursor();
226 }
227
228 /**
229  * @return Directory (in terms of the List view) to the given item (e.g. /mechanical/screws)
230  * (called recursively)
231  */
232 QString LibraryWidget::getItemDir(QTreeWidgetItem * item)
233 {
234         if (!item)
235                 return "";
236
237         QTreeWidgetItem * parent = item->parent();
238
239         return getItemDir(parent) + QString("/%1").arg(item->text(0));
240 }
241
242 /**
243  * @return Path of the DXF file that is represented by the given item.
244  */
245 QString LibraryWidget::getItemPath(QListWidgetItem * item)
246 {
247         QString dir = getItemDir(ui.lvDirectory->currentItem());
248
249         if (item)
250         {
251                 // List of all directories that contain part libraries:
252                 QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
253                 QStringList::Iterator it;
254                 QDir itemDir;
255
256                 // Look in all possible system directories for DXF files in the current library path:
257                 for(it=directoryList.begin(); it!=directoryList.end(); ++it)
258                 {
259                         itemDir.setPath((*it) + dir);
260
261                         if (itemDir.exists())
262                         {
263                                 QString f = (*it) + dir + "/" + item->text() + ".dxf";
264
265                                 if (QFileInfo(f).isReadable())
266                                         return f;
267                         }
268                 }
269         }
270
271         return "";
272 }
273
274 /**
275  * @return Pixmap that serves as icon for the given DXF File.
276  * The existing PNG file is returned or created and returned..
277  *
278  * @param dir Library directory (e.g. "/mechanical/screws")
279  * @param dxfFile File name (e.g. "screw1.dxf")
280  * @param dxfPath Full path to the existing DXF file on disk
281  *                          (e.g. /home/tux/.qcad/library/mechanical/screws/screw1.dxf)
282  */
283 QPixmap LibraryWidget::getPixmap(const QString & dir, const QString & dxfFile,
284         const QString & dxfPath)
285 {
286         QString pngFile = getPathToPixmap(dir, dxfFile, dxfPath);
287         QFileInfo fiPng(pngFile);
288
289         // Found existing thumbnail:
290         if (fiPng.isFile())
291                 return QPixmap(pngFile);
292         // Default thumbnail:
293         else
294                 return QPixmap(64, 64);
295 }
296
297 /**
298  * @return Path to the thumbnail of the given DXF file. If no thumbnail exists, one is
299  * created in the user's home. If no thumbnail can be created, an empty string is returned.
300  */
301 QString LibraryWidget::getPathToPixmap(const QString & dir, const QString & dxfFile,
302         const QString & dxfPath)
303 {
304         RS_DEBUG->print("LibraryWidget::getPathToPixmap: dir: '%s' dxfFile: '%s' dxfPath: '%s'",
305                 dir.toLatin1().data(), dxfFile.toLatin1().data(), dxfPath.toLatin1().data());
306
307         // List of all directories that contain part libraries:
308         QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
309         directoryList.prepend(RS_SYSTEM->getHomeDir() + "/.architektonas/library");
310         QStringList::Iterator it;
311
312         QFileInfo fiDxf(dxfPath);
313         QString itemDir;
314         QString pngPath;
315
316         // look in all possible system directories for PNG files
317         //  in the current library path:
318         for(it=directoryList.begin(); it!=directoryList.end(); ++it)
319         {
320                 itemDir = (*it) + dir;
321 //              pngPath = itemDir + "/" + fiDxf.baseName(true) + ".png";
322                 pngPath = itemDir + "/" + fiDxf.completeBaseName() + ".png";
323                 RS_DEBUG->print("LibraryWidget::getPathToPixmap: checking: '%s'",
324                         pngPath.toLatin1().data());
325                 QFileInfo fiPng(pngPath);
326
327                 // the thumbnail exists:
328                 if (fiPng.isFile())
329                 {
330                         RS_DEBUG->print("LibraryWidget::getPathToPixmap: dxf date: %s, png date: %s",
331                                 fiDxf.lastModified().toString().toLatin1().data(), fiPng.lastModified().toString().toLatin1().data());
332
333                         if (fiPng.lastModified() > fiDxf.lastModified())
334                         {
335                                 RS_DEBUG->print("LibraryWidget::getPathToPixmap: thumbnail found: '%s'",
336                                         pngPath.toLatin1().data());
337                                 return pngPath;
338                         }
339                         else
340                         {
341                                 RS_DEBUG->print("LibraryWidget::getPathToPixmap: thumbnail needs to be updated: '%s'",
342                                         pngPath.toLatin1().data());
343                         }
344                 }
345         }
346
347         // The thumbnail must be created in the user's home.
348         // So, create all directories needed:
349         RS_SYSTEM->createHomePath("/.architektonas/library" + dir);
350
351         QString d = RS_SYSTEM->getHomeDir() + "/.architektonas/library" + dir;
352 //printf("librarywidget: d = \"%s\"\n", d.toAscii().data());
353
354 //      pngPath = d + "/" + fiDxf.baseName(true) + ".png";
355         pngPath = d + "/" + fiDxf.completeBaseName() + ".png";
356 //printf("librarywidget: pngPath = \"%s\"\n", pngPath.toAscii().data());
357
358         // Ugh. This is a mess and doesn't work right anyway...
359         // Problem is StaticGraphicView doesn't follow a standard rendering path,
360         // so all this crap just ends in failure. Not sure how to fix.
361 /**
362 I suppose one way to do it would be to make a CreateImage() function in GraphicView,
363 then we wouldn't have all this messiness...
364 I think the way the redraw works is like that--it creates its own PaintInterface
365 and QPainter and sets "painter" equal to it... That being the case, it would be
366 a simple matter to modify StaticGraphicView to utilize that... Let's see...
367 Which it should be doing, but, for some reason, it fails. Dunno why.
368
369 It only fails for certain DXFs. Dunno why.
370 */
371
372         QPixmap buffer(128, 128);
373         QPainter qpntr(&buffer);
374         PaintInterface * painter = new PaintInterface(&qpntr);
375         qpntr.setBackground(Qt::white);
376         qpntr.eraseRect(0, 0, 128, 128);
377
378         RS_StaticGraphicView gv(128, 128, painter);
379         Drawing drawing;
380
381         if (drawing.open(dxfPath, RS2::FormatUnknown))
382         {
383                 RS_Color black(0, 0, 0);
384
385                 // Set all pens in the drawing to BLACK
386                 for(RS_Entity * e=drawing.firstEntity(RS2::ResolveAll); e!=NULL; e=drawing.nextEntity(RS2::ResolveAll))
387                 {
388                         RS_Pen pen = e->getPen();
389                         pen.setColor(black);
390                         e->setPen(pen);
391                 }
392
393                 gv.setContainer(&drawing);
394                 gv.zoomAuto(false);
395 //This works, but somehow doesn't work ALL the time
396                 gv.drawEntity(&drawing, true);
397
398                 QImageWriter writer;
399                 QImage image = buffer.toImage();
400                 image.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
401                 writer.setFileName(pngPath);
402                 writer.setFormat("png");
403
404                 if (!writer.write(image))
405                 {
406                         RS_DEBUG->print(RS_Debug::D_ERROR,
407                                 "LibraryWidget::getPathToPixmap: Cannot write thumbnail: '%s'",
408                                 pngPath.toLatin1().data());
409                         pngPath = "";
410                 }
411         }
412         else
413         {
414                 RS_DEBUG->print(RS_Debug::D_ERROR, "LibraryWidget::getPathToPixmap: Cannot open file: '%s'", dxfPath.toLatin1().data());
415         }
416
417         delete painter;
418
419         return pngPath;
420 }
421
422 #if 0
423
424 QWidget::setMinimumSize: (/QMdi::ControlLabel) Negative sizes (-1,-1) are not possible
425 QPaintDevice: Cannot destroy paint device that is being painted
426
427 Program received signal SIGSEGV, Segmentation fault.
428 0x085cd068 in ?? ()
429 (gdb) bt
430 #0  0x085cd068 in ?? ()
431 #1  0xb7818bd0 in QPainter::~QPainter() () from /usr/lib/qt4/libQtGui.so.4
432 #2  0x081c1e8e in LibraryWidget::getPathToPixmap(QString const&, QString const&, QString const&) ()
433 #3  0x081c0fa6 in LibraryWidget::getPixmap(QString const&, QString const&, QString const&) ()
434 #4  0x081c0775 in LibraryWidget::updatePreview(QTreeWidgetItem*, int) ()
435 #5  0x08237d5e in LibraryWidget::qt_metacall(QMetaObject::Call, int, void**) ()
436
437 #endif