]> Shamusworld >> Repos - architektonas/blob - src/forms/librarywidget.cpp
edacdc1131266cdd4844a0d2ba45dac5d4388638
[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 // JLH  09/07/2010  Fully fixed thumbnail rendering
18 //
19
20 /*
21 Note that this is basically just a way to get a block from a file; it's unclear
22 that it does so and the doco (such as it was) didn't mention it either. So what
23 we need is to make it very clear that inserting is to the BLOCK list and not
24 the document--perhaps we need to fold it into the block list, and make the
25 insert function insert into the block list only...
26 */
27
28 #include "librarywidget.h"
29
30 #include "actionhandler.h"
31 #include "actionlibraryinsert.h"
32 #include "drawing.h"
33 #include "rs_staticgraphicview.h"
34 #include "rs_system.h"
35 #include "paintinterface.h"
36
37 LibraryWidget::LibraryWidget(QWidget * parent/*= 0*/, Qt::WindowFlags flags/*= 0*/):
38         QWidget(parent, flags), actionHandler(NULL)
39 {
40 #if 0
41 std::cout << "LibraryWidget::LibraryWidget()" << std::endl;
42 #endif
43         ui.setupUi(this);
44         ui.lvDirectory->setColumnCount(1);
45
46         QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
47 #if 0
48 std::cout << "directorySize = " << directoryList.size() << std::endl;
49
50 for(int i=0; i<directoryList.size(); i++)
51         std::cout << directoryList.at(i).toLocal8Bit().constData() << std::endl;
52
53 std::cout.flush();
54 #endif
55
56         for(QStringList::Iterator it=directoryList.begin(); it!=directoryList.end(); it++)
57                 appendTree(NULL, (*it));
58 }
59
60 LibraryWidget::~LibraryWidget()
61 {
62 }
63
64 void LibraryWidget::setActionHandler(ActionHandler * ah)
65 {
66         actionHandler = ah;
67 }
68
69 /**
70  * Escape releases focus.
71  */
72 void LibraryWidget::keyPressEvent(QKeyEvent * e)
73 {
74         switch (e->key())
75         {
76         case Qt::Key_Escape:
77                 emit escape();
78                 break;
79
80         default:
81                 QWidget::keyPressEvent(e);
82                 break;
83         }
84 }
85
86 /**
87  * Insert.
88  */
89 void LibraryWidget::insert()
90 {
91         QListWidgetItem * item = ui.ivPreview->currentItem();
92         QString dxfPath = getItemPath(item);
93
94         if (QFileInfo(dxfPath).isReadable())
95         {
96                 if (actionHandler)
97                 {
98                         ActionInterface * a = actionHandler->setCurrentAction(RS2::ActionLibraryInsert);
99
100                         if (a)
101                         {
102                                 ActionLibraryInsert * action = (ActionLibraryInsert *)a;
103                                 action->setFile(dxfPath);
104                         }
105                         else
106                         {
107                                 RS_DEBUG->print(RS_Debug::D_ERROR, "LibraryWidget::insert:"
108                                         "Cannot create action ActionLibraryInsert");
109                         }
110                 }
111         }
112         else
113         {
114                 RS_DEBUG->print(RS_Debug::D_ERROR,
115                         "LibraryWidget::insert: Can't read file: '%s'", dxfPath.toLatin1().data());
116         }
117 }
118
119 /**
120  * Appends the given directory to the given list view item. Called recursively until all
121  * library directories are appended.
122  */
123 void LibraryWidget::appendTree(QTreeWidgetItem * item, QString directory)
124 {
125         QDir dir(directory);
126
127         if (!dir.exists())
128                 return;
129
130         // read subdirectories of this directory:
131         QStringList lDirectoryList = dir.entryList(QDir::Dirs, QDir::Name);
132
133         for(QStringList::Iterator it=lDirectoryList.begin(); it!=lDirectoryList.end(); it++)
134         {
135                 if ((*it) != "." && (*it) != "..")
136                 {
137                         // Look for an item already existing and take this instead of
138                         // making a new one:
139                         QTreeWidgetItem * newItem = NULL;
140                         QTreeWidgetItem * searchItem =
141                                 (item ? item->child(0) : ui.lvDirectory->topLevelItem(0));
142
143                         if (searchItem)
144                         {
145                                 for(int i=0; i<searchItem->childCount(); i++)
146                                 {
147                                         if (searchItem->child(i)->text(0) == (*it))
148                                         {
149                                                 newItem = searchItem->child(i);
150                                                 break;
151                                         }
152                                 }
153                         }
154
155                         // Create new item if no existing was found:
156                         if (!newItem)
157                         {
158                                 QStringList list;
159                                 list << (*it);
160                                 newItem = (item ? new QTreeWidgetItem(item, list) : new QTreeWidgetItem(ui.lvDirectory, list));
161                         }
162
163 //This is picking up the directory tree TWICE, but ONLY if there are no thumbnails!
164 //Actually, only if there is no corresponding thumbnail DIRECTORY under .architektonas...
165 //Dunno why...
166 //It was picking up .architektonas from the home directory in rs_system.cpp, that's why!
167 //printf("LibraryWidget::appendTree: *it=\"%s\"\n", (*it).toAscii().data());
168                         appendTree(newItem, directory + "/" + (*it));
169                 }
170         }
171 }
172
173 /**
174  * Updates the icon preview.
175  */
176 void LibraryWidget::updatePreview(QTreeWidgetItem * item, int /*column*/)
177 {
178         if (!item)
179                 return;
180
181         QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
182
183         // dir from the point of view of the library browser (e.g. /mechanical/screws)
184         QString directory = getItemDir(item);
185         ui.ivPreview->clear();
186
187         // List of all directories that contain part libraries:
188         QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
189         QStringList::Iterator it;
190         QDir itemDir;
191         QStringList itemPathList;
192         QStringList filter;
193         filter << "*.dxf";
194
195         // Look in all possible system directories for DXF files in the current library path:
196         for(it=directoryList.begin(); it!=directoryList.end(); ++it)
197         {
198                 itemDir.setPath((*it) + directory);
199
200                 if (itemDir.exists())
201                 {
202                         QStringList itemNameList = itemDir.entryList(filter, QDir::Files, QDir::Name);
203                         QStringList::Iterator it2;
204
205                         for(it2=itemNameList.begin(); it2!=itemNameList.end(); ++it2)
206                                 itemPathList += itemDir.path() + "/" + (*it2);
207                 }
208         }
209
210         // Sort entries:
211         itemPathList.sort();
212
213         // Fill items into icon view:
214         QListWidgetItem * newItem;
215
216         for(it=itemPathList.begin(); it!=itemPathList.end(); ++it)
217         {
218 //              QString label = QFileInfo(*it).baseName(true);
219                 QString label = QFileInfo(*it).completeBaseName();
220                 QPixmap pixmap = getPixmap(directory, QFileInfo(*it).fileName(), (*it));
221 //              newItem = new QListWidgetItem(ui.ivPreview, label, pixmap);
222                 newItem = new QListWidgetItem(QIcon(pixmap), label, ui.ivPreview);
223 //Doesn't do what we want...
224 //              newItem->setSizeHint(QSize(64, 64));
225 //printf("LibraryWidget: label = \"%s\"\n", label.toAscii().data());
226         }
227
228         QApplication::restoreOverrideCursor();
229 }
230
231 /**
232  * @return Directory (in terms of the List view) to the given item (e.g. /mechanical/screws)
233  * (called recursively)
234  */
235 QString LibraryWidget::getItemDir(QTreeWidgetItem * item)
236 {
237         if (!item)
238                 return "";
239
240         QTreeWidgetItem * parent = item->parent();
241
242         return getItemDir(parent) + QString("/%1").arg(item->text(0));
243 }
244
245 /**
246  * @return Path of the DXF file that is represented by the given item.
247  */
248 QString LibraryWidget::getItemPath(QListWidgetItem * item)
249 {
250         QString dir = getItemDir(ui.lvDirectory->currentItem());
251
252         if (item)
253         {
254                 // List of all directories that contain part libraries:
255                 QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
256                 QStringList::Iterator it;
257                 QDir itemDir;
258
259                 // Look in all possible system directories for DXF files in the current library path:
260                 for(it=directoryList.begin(); it!=directoryList.end(); ++it)
261                 {
262                         itemDir.setPath((*it) + dir);
263
264                         if (itemDir.exists())
265                         {
266                                 QString f = (*it) + dir + "/" + item->text() + ".dxf";
267
268                                 if (QFileInfo(f).isReadable())
269                                         return f;
270                         }
271                 }
272         }
273
274         return "";
275 }
276
277 /**
278  * @return Pixmap that serves as icon for the given DXF File.
279  * The existing PNG file is returned or created and returned..
280  *
281  * @param dir Library directory (e.g. "/mechanical/screws")
282  * @param dxfFile File name (e.g. "screw1.dxf")
283  * @param dxfPath Full path to the existing DXF file on disk
284  *                (e.g. /home/tux/.architektonas/library/mechanical/screws/screw1.dxf)
285  */
286 QPixmap LibraryWidget::getPixmap(const QString & dir, const QString & dxfFile,
287         const QString & dxfPath)
288 {
289         QString pngFile = getPathToPixmap(dir, dxfFile, dxfPath);
290         QFileInfo fiPng(pngFile);
291
292         // Found existing thumbnail:
293         if (fiPng.isFile())
294                 return QPixmap(pngFile);
295         // Default thumbnail:
296         else
297                 return QPixmap(64, 64);
298 }
299
300 /**
301  * @return Path to the thumbnail of the given DXF file. If no thumbnail exists, one is
302  * created in the user's home. If no thumbnail can be created, an empty string is returned.
303  */
304 QString LibraryWidget::getPathToPixmap(const QString & dir, const QString & dxfFile,
305         const QString & dxfPath)
306 {
307         RS_DEBUG->print("LibraryWidget::getPathToPixmap: dir: '%s' dxfFile: '%s' dxfPath: '%s'",
308                 dir.toLatin1().data(), dxfFile.toLatin1().data(), dxfPath.toLatin1().data());
309
310         // List of all directories that contain part libraries:
311         QStringList directoryList = RS_SYSTEM->getDirectoryList("library");
312         directoryList.prepend(RS_SYSTEM->getHomeDir() + "/.architektonas/library");
313         QStringList::Iterator it;
314
315         QFileInfo fiDxf(dxfPath);
316         QString itemDir;
317         QString pngPath;
318
319         // look in all possible system directories for PNG files
320         //  in the current library path:
321         for(it=directoryList.begin(); it!=directoryList.end(); ++it)
322         {
323                 itemDir = (*it) + dir;
324                 pngPath = itemDir + "/" + fiDxf.completeBaseName() + ".png";
325                 RS_DEBUG->print("LibraryWidget::getPathToPixmap: checking: '%s'",
326                         pngPath.toLatin1().data());
327                 QFileInfo fiPng(pngPath);
328
329                 // the thumbnail exists:
330                 if (fiPng.isFile())
331                 {
332                         RS_DEBUG->print("LibraryWidget::getPathToPixmap: dxf date: %s, png date: %s",
333                                 fiDxf.lastModified().toString().toLatin1().data(), fiPng.lastModified().toString().toLatin1().data());
334
335                         if (fiPng.lastModified() > fiDxf.lastModified())
336                         {
337                                 RS_DEBUG->print("LibraryWidget::getPathToPixmap: thumbnail found: '%s'",
338                                         pngPath.toLatin1().data());
339                                 return pngPath;
340                         }
341                         else
342                         {
343                                 RS_DEBUG->print("LibraryWidget::getPathToPixmap: thumbnail needs to be updated: '%s'",
344                                         pngPath.toLatin1().data());
345                         }
346                 }
347         }
348
349         // The thumbnail must be created in the user's home.
350         // So, create all directories needed:
351         RS_SYSTEM->createHomePath("/.architektonas/library" + dir);
352         QString d = RS_SYSTEM->getHomeDir() + "/.architektonas/library" + dir;
353         pngPath = d + "/" + fiDxf.completeBaseName() + ".png";
354
355         QPixmap buffer(128, 128);
356         QPainter qpntr(&buffer);
357         PaintInterface * painter = new PaintInterface(&qpntr);
358         qpntr.setBackground(Qt::white);
359         qpntr.eraseRect(0, 0, 128, 128);
360
361         RS_StaticGraphicView view(128, 128, painter);
362         Drawing drawing;
363
364         if (drawing.open(dxfPath, RS2::FormatUnknown))
365         {
366                 RS_Color black(0, 0, 0);
367
368                 // Set all pens in the drawing to BLACK
369                 for(RS_Entity * e=drawing.firstEntity(RS2::ResolveAll); e!=NULL; e=drawing.nextEntity(RS2::ResolveAll))
370                 {
371                         RS_Pen pen = e->getPen();
372                         pen.setColor(black);
373                         e->setPen(pen);
374                 }
375
376                 view.setContainer(&drawing);
377                 view.zoomAuto(false);
378                 view.drawEntity(&drawing, true);
379
380                 QImageWriter writer;
381                 QImage image = buffer.toImage();
382                 image.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
383                 writer.setFileName(pngPath);
384                 writer.setFormat("png");
385
386                 if (!writer.write(image))
387                 {
388                         RS_DEBUG->print(RS_Debug::D_ERROR,
389                                 "LibraryWidget::getPathToPixmap: Cannot write thumbnail: '%s'",
390                                 pngPath.toLatin1().data());
391                         pngPath = "";
392                 }
393         }
394         else
395         {
396                 RS_DEBUG->print(RS_Debug::D_ERROR, "LibraryWidget::getPathToPixmap: Cannot open file: '%s'", dxfPath.toLatin1().data());
397         }
398
399         delete painter;
400
401         return pngPath;
402 }