1 /*******************************************************************
2
3 Part of the Fritzing project - http://fritzing.org
4 Copyright (c) 2007-2014 Fachhochschule Potsdam - http://fh-potsdam.de
5
6 Fritzing is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 Fritzing 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
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Fritzing. If not, see <http://www.gnu.org/licenses/>.
18
19 ********************************************************************
20
21 $Revision: 6904 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-02-26 16:26:03 +0100 (Di, 26. Feb 2013) $
24
25 ********************************************************************/
26
27
28 #include <QUndoStack>
29 #include <QHBoxLayout>
30 #include <QVBoxLayout>
31 #include <QFileDialog>
32 #include <QMessageBox>
33 #include <QSettings>
34 #include <QWidgetAction>
35 #include <QColorDialog>
36 #include <QBuffer>
37 #include <QSvgGenerator>
38
39 #include "partsbinpalettewidget.h"
40 #include "partsbincommands.h"
41 #include "partsbiniconview.h"
42 #include "partsbinlistview.h"
43 #include "searchlineedit.h"
44 #include "../utils/misc.h"
45 #include "../debugdialog.h"
46 #include "../infoview/htmlinfoview.h"
47 #include "../utils/fileprogressdialog.h"
48 #include "../utils/folderutils.h"
49 #include "../utils/textutils.h"
50 #include "../mainwindow/mainwindow.h" // TODO: PartsBinPaletteWidget should not call MainWindow functions
51
52
53 static QString CustomIconName = "Custom1.png";
54 static QString CustomIconTitle = "Fritzing Custom Icon";
55
isCustomSvg(const QString & string)56 inline bool isCustomSvg(const QString & string) {
57 return string.startsWith("<?xml") && string.contains(CustomIconTitle);
58 }
59
60 static QHash<QString, PaletteModel *> PaletteBinModels;
61
62 static QIcon EmptyIcon;
63
64 //////////////////////////////////////////////
65
PartsBinPaletteWidget(ReferenceModel * referenceModel,HtmlInfoView * infoView,WaitPushUndoStack * undoStack,BinManager * manager)66 PartsBinPaletteWidget::PartsBinPaletteWidget(ReferenceModel *referenceModel, HtmlInfoView *infoView, WaitPushUndoStack *undoStack, BinManager* manager) :
67 QFrame(manager)
68 {
69 m_binLabel = NULL;
70 m_monoIcon = m_icon = NULL;
71 m_searchLineEdit = NULL;
72 m_saveQuietly = false;
73 m_fastLoaded = false;
74 m_model = NULL;
75
76 m_loadingProgressDialog = NULL;
77
78 setAcceptDrops(true);
79 setAllowsChanges(true);
80
81 m_manager = manager;
82
83 m_referenceModel = referenceModel;
84 m_canDeleteModel = false;
85 m_orderHasChanged = false;
86
87 Q_UNUSED(undoStack);
88
89 m_undoStack = new WaitPushUndoStack(this);
90 connect(m_undoStack, SIGNAL(cleanChanged(bool)), this, SLOT(undoStackCleanChanged(bool)) );
91
92 m_iconView = new PartsBinIconView(m_referenceModel, this);
93 m_iconView->setInfoView(infoView);
94
95 m_listView = new PartsBinListView(m_referenceModel, this);
96 m_listView->setInfoView(infoView);
97
98 m_stackedWidget = new QStackedWidget(this);
99 m_stackedWidget->addWidget(m_iconView);
100 m_stackedWidget->addWidget(m_listView);
101
102 QVBoxLayout * vbl = new QVBoxLayout(this);
103 vbl->setMargin(3);
104 vbl->setSpacing(0);
105
106 m_header = NULL;
107 setupHeader();
108 if (m_header) {
109 vbl->addWidget(m_header);
110
111 QFrame * separator = new QFrame();
112 separator->setMaximumHeight(1);
113 separator->setObjectName("partsBinHeaderSeparator");
114 separator->setFrameShape(QFrame::HLine);
115 separator->setFrameShadow(QFrame::Plain);
116 vbl->addWidget(separator);
117 }
118
119 vbl->addWidget(m_stackedWidget);
120 this->setLayout(vbl);
121
122 setObjectName("partsBinContainer");
123 toIconView();
124
125 m_defaultSaveFolder = FolderUtils::getUserDataStorePath("bins");
126 m_untitledFileName = tr("Untitled Bin");
127
128 connect(m_listView, SIGNAL(currentRowChanged(int)), m_iconView, SLOT(setSelected(int)));
129 connect(m_iconView, SIGNAL(selectionChanged(int)), m_listView, SLOT(setSelected(int)));
130
131 connect(m_listView, SIGNAL(currentRowChanged(int)), m_manager, SLOT(updateBinCombinedMenuCurrent()));
132 connect(m_iconView, SIGNAL(selectionChanged(int)), m_manager, SLOT(updateBinCombinedMenuCurrent()));
133
134 connect(m_listView, SIGNAL(informItemMoved(int,int)), m_iconView, SLOT(itemMoved(int,int)));
135 connect(m_iconView, SIGNAL(informItemMoved(int,int)), m_listView, SLOT(itemMoved(int,int)));
136 connect(m_listView, SIGNAL(informItemMoved(int,int)), this, SLOT(itemMoved()));
137 connect(m_iconView, SIGNAL(informItemMoved(int,int)), this, SLOT(itemMoved()));
138
139 if (m_binLabel) m_binLabel->setText(m_title);
140
141 m_addPartToMeAction = new QAction(m_title,this);
142 connect(m_addPartToMeAction, SIGNAL(triggered()),this, SLOT(addSketchPartToMe()));
143
144 installEventFilter(this);
145 }
146
~PartsBinPaletteWidget()147 PartsBinPaletteWidget::~PartsBinPaletteWidget() {
148 if (m_canDeleteModel && m_model != NULL) {
149 delete m_model;
150 m_model = NULL;
151 }
152
153 if (m_icon) delete m_icon;
154 if (m_monoIcon) delete m_monoIcon;
155 }
156
cleanup()157 void PartsBinPaletteWidget::cleanup() {
158 foreach (PaletteModel * paletteModel, PaletteBinModels) {
159 delete paletteModel;
160 }
161 PaletteBinModels.clear();
162 }
163
164
sizeHint() const165 QSize PartsBinPaletteWidget::sizeHint() const {
166 return QSize(DockWidthDefault, PartsBinHeightDefault);
167 }
168
title() const169 QString PartsBinPaletteWidget::title() const {
170 return m_title;
171 }
172
setTitle(const QString & title)173 void PartsBinPaletteWidget::setTitle(const QString &title) {
174 if(m_title != title) {
175 m_title = title;
176 if (m_binLabel) m_binLabel->setText(title);
177 }
178 }
179
setupHeader()180 void PartsBinPaletteWidget::setupHeader()
181 {
182 QMenu * combinedMenu = m_manager->combinedMenu();
183 if (combinedMenu == NULL) return;
184
185 m_combinedBinMenuButton = newToolButton("partsBinCombinedMenuButton", ___emptyString___, ___emptyString___);
186 m_combinedBinMenuButton->setMenu(combinedMenu);
187
188 m_binLabel = new QLabel(this);
189 m_binLabel->setObjectName("partsBinLabel");
190 m_binLabel->setWordWrap(false);
191
192 m_searchLineEdit = new SearchLineEdit(this);
193 m_searchLineEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
194 connect(m_searchLineEdit, SIGNAL(returnPressed()), this, SLOT(search()));
195
196 m_searchStackedWidget = new QStackedWidget(this);
197 m_searchStackedWidget->setObjectName("searchStackedWidget");
198
199 m_searchStackedWidget->addWidget(m_binLabel);
200 m_searchStackedWidget->addWidget(m_searchLineEdit);
201
202 m_header = new QFrame(this);
203 m_header->setObjectName("partsBinHeader");
204 QHBoxLayout * hbl = new QHBoxLayout();
205 hbl->setSpacing(0);
206 hbl->setMargin(0);
207
208 hbl->addWidget(m_searchStackedWidget);
209 hbl->addWidget(m_combinedBinMenuButton);
210
211 m_header->setLayout(hbl);
212 }
213
setView(PartsBinView * view)214 void PartsBinPaletteWidget::setView(PartsBinView *view) {
215 m_currentView = view;
216 if(m_currentView == m_iconView) {
217 m_stackedWidget->setCurrentIndex(0);
218 m_manager->updateViewChecks(true);
219 }
220 else {
221 m_stackedWidget->setCurrentIndex(1);
222 m_manager->updateViewChecks(false);
223 }
224
225 }
226
toIconView()227 void PartsBinPaletteWidget::toIconView() {
228 setView(m_iconView);
229 }
230
toListView()231 void PartsBinPaletteWidget::toListView() {
232 disconnect(m_listView, SIGNAL(currentRowChanged(int)), m_iconView, SLOT(setSelected(int)));
233 setView(m_listView);
234 connect(m_listView, SIGNAL(currentRowChanged(int)), m_iconView, SLOT(setSelected(int)));
235 }
236
saveAsAux(const QString & filename)237 bool PartsBinPaletteWidget::saveAsAux(const QString &filename) {
238 FileProgressDialog progress("Saving...", 0, this);
239
240 QString oldFilename = m_fileName;
241 setFilename(filename);
242 QString title = this->title();
243 if(!title.isNull() && !title.isEmpty()) {
244 ModelPartSharedRoot * root = m_model->rootModelPartShared();
245 if (root) root->setTitle(title);
246 }
247
248 if(m_orderHasChanged) {
249 m_model->setOrdererChildren(m_iconView->orderedChildren());
250 }
251 m_model->save(filename, false);
252 if(m_orderHasChanged) {
253 m_model->setOrdererChildren(QList<QObject*>());
254 m_orderHasChanged = false;
255 }
256
257 m_undoStack->setClean();
258
259 m_location = BinLocation::findLocation(filename);
260
261 if(oldFilename != m_fileName) {
262 emit fileNameUpdated(this,m_fileName,oldFilename);
263 }
264 emit saved(hasAlienParts());
265
266 foreach (QString path, m_removed) {
267 bool result = QFile::remove(path);
268 if (!result) {
269 DebugDialog::debug("unable to delete '" + path + "' from bin");
270 }
271 }
272 m_removed.clear();
273
274 return true;
275 }
276
loadFromModel(PaletteModel * model)277 void PartsBinPaletteWidget::loadFromModel(PaletteModel *model) {
278 m_iconView->loadFromModel(model);
279 m_listView->setPaletteModel(model);
280 afterModelSetted(model);
281 m_canDeleteModel = false; // FApplication is holding this model, so don't delete it
282 setFilename(model->loadedFrom());
283 }
284
setPaletteModel(PaletteModel * model,bool clear)285 void PartsBinPaletteWidget::setPaletteModel(PaletteModel *model, bool clear) {
286 m_iconView->setPaletteModel(model, clear);
287 m_listView->setPaletteModel(model, clear);
288 afterModelSetted(model);
289 }
290
afterModelSetted(PaletteModel * model)291 void PartsBinPaletteWidget::afterModelSetted(PaletteModel *model) {
292 grabTitle(model);
293 m_model = model;
294 m_undoStack->setClean();
295 setFilename(model->loadedFrom());
296 }
297
grabTitle(PaletteModel * model)298 void PartsBinPaletteWidget::grabTitle(PaletteModel *model) {
299 ModelPartSharedRoot * root = model->rootModelPartShared();
300 if (!root) return;
301
302 QString iconFilename = root->icon();
303 grabTitle(root->title(), iconFilename);
304 root->setIcon(iconFilename);
305
306 if (m_searchLineEdit) m_searchLineEdit->setText(root->searchTerm());
307 }
308
grabTitle(const QString & title,QString & iconFilename)309 void PartsBinPaletteWidget::grabTitle(const QString & title, QString & iconFilename)
310 {
311 m_title = title;
312 m_addPartToMeAction->setText(m_title);
313 if (m_binLabel) m_binLabel->setText(m_title);
314
315 QString temp = BinManager::StandardBinIcons.value(m_fileName, "");
316 if (!temp.isEmpty()) {
317 iconFilename = temp;
318 }
319 else if (iconFilename.isEmpty()) {
320 iconFilename = CustomIconName;
321 }
322
323 if (isCustomSvg(iconFilename)) {
324 // convert to image
325 int w = TextUtils::getViewBoxCoord(iconFilename, 2);
326 int h = TextUtils::getViewBoxCoord(iconFilename, 3);
327 QImage image(w, h, QImage::Format_ARGB32);
328 image.fill(0);
329 QRectF target(0, 0, w, h);
330 QSvgRenderer renderer(iconFilename.toUtf8());
331 QPainter painter;
332 painter.begin(&image);
333 renderer.render(&painter, target);
334 painter.end();
335 //image.save(FolderUtils::getUserDataStorePath("") + "/test icon.png");
336 m_icon = new QIcon(QPixmap::fromImage(image));
337 m_monoIcon = new QIcon(":resources/bins/icons/Custom1-mono.png");
338
339 // TODO: hack svg to make a mono icon
340 }
341 else {
342 QFileInfo info(m_fileName);
343 QDir dir = info.absoluteDir();
344 QString path = dir.absoluteFilePath(iconFilename);
345 QFile file1(path);
346 if (file1.exists()) {
347 m_icon = new QIcon(path);
348 }
349 else {
350 path = ":resources/bins/icons/" + iconFilename;
351 QFile file2(path);
352 if (file2.exists()) {
353 m_icon = new QIcon(path);
354 }
355 }
356
357 if (m_icon) {
358 int ix = path.lastIndexOf(".");
359 path.insert(ix, "-mono");
360 QFile file3(path);
361 if (file3.exists()) {
362 m_monoIcon = new QIcon(path);
363 }
364 }
365 }
366 }
367
addPart(ModelPart * modelPart,int position)368 void PartsBinPaletteWidget::addPart(ModelPart *modelPart, int position) {
369 if (m_model == NULL) {
370 return;
371 }
372
373 ModelPart *mp = m_model->addModelPart(m_model->root(),modelPart);
374
375 m_iconView->addPart(mp, position);
376 m_listView->addPart(mp, position);
377
378 if(modelPart->isAlien()) {
379 m_alienParts << mp->moduleID();
380 }
381 }
382
newToolButton(const QString & btnObjName,const QString & imgPath,const QString & text)383 QToolButton* PartsBinPaletteWidget::newToolButton(const QString& btnObjName, const QString& imgPath, const QString &text) {
384 QToolButton *toolBtn = new QToolButton(this);
385 toolBtn->setObjectName(btnObjName);
386 toolBtn->setToolButtonStyle(Qt::ToolButtonIconOnly);
387 toolBtn->setArrowType(Qt::NoArrow);
388 if (!text.isEmpty()) {
389 toolBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
390 toolBtn->setText(text);
391 }
392 toolBtn->setPopupMode(QToolButton::InstantPopup);
393 if (!imgPath.isEmpty()) {
394 toolBtn->setIcon(QIcon(imgPath));
395 }
396 toolBtn->setArrowType(Qt::NoArrow);
397 return toolBtn;
398 }
399
save()400 bool PartsBinPaletteWidget::save() {
401 bool result = true;
402 if (FolderUtils::isEmptyFileName(m_fileName,m_untitledFileName) || currentBinIsCore()) {
403 result = saveAs();
404 } else {
405 saveAsAux(m_fileName);
406 }
407 return result;
408 }
409
saveAs()410 bool PartsBinPaletteWidget::saveAs() {
411 QString fileExt;
412 QString fileName = QFileDialog::getSaveFileName(
413 this,
414 tr("Specify a file name"),
415 (m_fileName.isNull() || m_fileName.isEmpty() || /* it's a resource */ m_fileName.startsWith(":"))?
416 m_defaultSaveFolder+"/"+title()+FritzingBinExtension:
417 m_fileName,
418 tr("Fritzing Bin (*%1)").arg(FritzingBinExtension),
419 &fileExt
420 );
421
422 if (fileName.isEmpty()) return false; // Cancel pressed
423
424 if(!FritzingWindow::alreadyHasExtension(fileName, FritzingBinExtension)) {
425 fileName += FritzingBinExtension;
426 }
427 saveAsAux(fileName);
428 return true;
429 }
430
saveBundledBin()431 void PartsBinPaletteWidget::saveBundledBin() {
432 bool wasModified = m_isDirty;
433 m_manager->mainWindow()->saveBundledNonAtomicEntity(
434 m_fileName, FritzingBundledBinExtension, this,
435 m_model->root()->getAllNonCoreParts(), true, "", true, false
436 );
437 setDirty(wasModified);
438 }
439
loadBundledAux(QDir & unzipDir,QList<ModelPart * > mps)440 bool PartsBinPaletteWidget::loadBundledAux(QDir &unzipDir, QList<ModelPart*> mps) {
441 QStringList namefilters;
442 namefilters << "*"+FritzingBinExtension;
443
444 this->load(unzipDir.entryInfoList(namefilters)[0].filePath(), this, false);
445 foreach(ModelPart* mp, mps) {
446 if(mp->isAlien()) { // double check
447 m_alienParts << mp->moduleID();
448 }
449 }
450 setFilename(___emptyString___);
451
452 return true;
453 }
454
455
open(QString fileName,QWidget * progressTarget,bool fastLoad)456 bool PartsBinPaletteWidget::open(QString fileName, QWidget * progressTarget, bool fastLoad) {
457 QFile file(fileName);
458 if (!file.exists()) {
459 QMessageBox::warning(NULL, tr("Fritzing"),
460 tr("Cannot find file %1.")
461 .arg(fileName));
462 return false;
463 }
464
465 if (!file.open(QFile::ReadOnly | QFile::Text)) {
466 QMessageBox::warning(NULL, tr("Fritzing"),
467 tr("Cannot read file %1:\n%2.")
468 .arg(fileName)
469 .arg(file.errorString()));
470 return false;
471 }
472
473 file.close();
474
475 if(fileName.endsWith(FritzingBinExtension)) {
476 load(fileName, progressTarget, fastLoad);
477 m_isDirty = false;
478 } else if(fileName.endsWith(FritzingBundledBinExtension)) {
479 return m_manager->mainWindow()->loadBundledNonAtomicEntity(fileName,this,false, false);
480 }
481
482 return true;
483 }
484
load(const QString & filename,QWidget * progressTarget,bool fastLoad)485 void PartsBinPaletteWidget::load(const QString &filename, QWidget * progressTarget, bool fastLoad) {
486 // TODO deleting this local palette reference model deletes modelPartShared held by the palette bin modelParts
487 //PaletteModel * paletteReferenceModel = new PaletteModel(true, true);
488
489 m_location = BinLocation::findLocation(filename);
490
491 //DebugDialog::debug("loading bin");
492
493 if (fastLoad) {
494 QString binName, iconName;
495 if (BinManager::getBinTitle(filename, binName, iconName)) {
496 m_fileName = filename;
497 grabTitle(binName, iconName);
498 m_fastLoaded = true;
499 }
500 return;
501 }
502
503 m_fastLoaded = false;
504 PaletteModel * paletteBinModel = PaletteBinModels.value(filename);
505 if (paletteBinModel == NULL) {
506 paletteBinModel = new PaletteModel(true, false);
507 //DebugDialog::debug("after palette model");
508
509 QString name = m_title;
510 if (name.isEmpty()) name = QFileInfo(filename).completeBaseName();
511
512 bool deleteWhenDone = false;
513 if (progressTarget != NULL) {
514 //DebugDialog::debug("open progress " + filename);
515 deleteWhenDone = true;
516 progressTarget = m_loadingProgressDialog = new FileProgressDialog(tr("Loading..."), 200, progressTarget);
517 m_loadingProgressDialog->setBinLoadingChunk(200);
518 m_loadingProgressDialog->setBinLoadingCount(1);
519 m_loadingProgressDialog->setMessage(tr("loading bin '%1'").arg(name));
520 m_loadingProgressDialog->show();
521 }
522
523 if (progressTarget) {
524 connect(paletteBinModel, SIGNAL(loadingInstances(ModelBase *, QDomElement &)), progressTarget, SLOT(loadingInstancesSlot(ModelBase *, QDomElement &)));
525 connect(paletteBinModel, SIGNAL(loadingInstance(ModelBase *, QDomElement &)), progressTarget, SLOT(loadingInstanceSlot(ModelBase *, QDomElement &)));
526 connect(m_iconView, SIGNAL(settingItem()), progressTarget, SLOT(settingItemSlot()));
527 connect(m_listView, SIGNAL(settingItem()), progressTarget, SLOT(settingItemSlot()));
528 }
529 DebugDialog::debug(QString("loading bin '%1'").arg(name));
530 bool result = paletteBinModel->loadFromFile(filename, m_referenceModel, false);
531 //DebugDialog::debug(QString("done loading bin '%1'").arg(name));
532
533 if (!result) {
534 QMessageBox::warning(NULL, QObject::tr("Fritzing"), QObject::tr("Fritzing cannot load the parts bin"));
535 }
536 else {
537 m_fileName = filename;
538 setPaletteModel(paletteBinModel,true);
539 PaletteBinModels.insert(filename, paletteBinModel);
540 }
541
542 if (progressTarget) {
543 //DebugDialog::debug("close progress " + filename);
544 disconnect(paletteBinModel, SIGNAL(loadingInstances(ModelBase *, QDomElement &)), progressTarget, SLOT(loadingInstancesSlot(ModelBase *, QDomElement &)));
545 disconnect(paletteBinModel, SIGNAL(loadingInstance(ModelBase *, QDomElement &)), progressTarget, SLOT(loadingInstanceSlot(ModelBase *, QDomElement &)));
546 disconnect(m_iconView, SIGNAL(settingItem()), progressTarget, SLOT(settingItemSlot()));
547 disconnect(m_listView, SIGNAL(settingItem()), progressTarget, SLOT(settingItemSlot()));
548 if (deleteWhenDone) {
549 m_loadingProgressDialog->close();
550 delete m_loadingProgressDialog;
551 }
552 m_loadingProgressDialog = NULL;
553 }
554 }
555 else {
556 m_fileName = filename;
557 setPaletteModel(paletteBinModel,true);
558 }
559
560
561 //DebugDialog::debug("done loading bin");
562 //delete paletteReferenceModel;
563 }
564
undoStackCleanChanged(bool isClean)565 void PartsBinPaletteWidget::undoStackCleanChanged(bool isClean) {
566 if(!isClean && currentBinIsCore()) {
567 setFilename(QString::null);
568 }
569 setWindowModified(!isClean);
570 m_manager->setDirtyTab(this,isClean);
571 }
572
currentBinIsCore()573 bool PartsBinPaletteWidget::currentBinIsCore() {
574 return m_fileName == BinManager::CorePartsBinLocation;
575 }
576
beforeClosing()577 bool PartsBinPaletteWidget::beforeClosing() {
578 bool retval;
579 if (this->isWindowModified()) {
580 QMessageBox::StandardButton reply;
581 if (m_saveQuietly) {
582 reply = QMessageBox::Save;
583 }
584 else {
585 QMessageBox messageBox(this);
586 messageBox.setWindowTitle(tr("Save bin \"%1\"").arg(title()));
587 messageBox.setText(tr("Do you want to save the changes you made in the bin \"%1\"?").arg(title()));
588 messageBox.setInformativeText(tr("Your changes will be lost if you don't save them."));
589 messageBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel);
590 messageBox.setDefaultButton(QMessageBox::Save);
591 messageBox.setIcon(QMessageBox::Warning);
592 messageBox.setWindowModality(Qt::WindowModal);
593 messageBox.setButtonText(QMessageBox::Save, tr("Save"));
594 messageBox.setButtonText(QMessageBox::Discard, tr("Don't Save"));
595 messageBox.button(QMessageBox::Discard)->setShortcut(tr("Ctrl+D"));
596 messageBox.setButtonText(QMessageBox::Cancel, tr("Cancel"));
597
598 reply = (QMessageBox::StandardButton)messageBox.exec();
599 }
600
601 if (reply == QMessageBox::Save) {
602 retval = save();
603 } else if (reply == QMessageBox::Discard) {
604 retval = true;
605 } else {
606 retval = false;
607 }
608 } else {
609 retval = true;
610 }
611 return retval;
612 }
613
closeEvent(QCloseEvent * event)614 void PartsBinPaletteWidget::closeEvent(QCloseEvent* event) {
615 QFrame::closeEvent(event);
616 }
617
mousePressEvent(QMouseEvent * event)618 void PartsBinPaletteWidget::mousePressEvent(QMouseEvent* event) {
619 emit focused(this);
620 QFrame::mousePressEvent(event);
621 }
622
selectedModelPart()623 ModelPart * PartsBinPaletteWidget::selectedModelPart() {
624 return m_currentView->selectedModelPart();
625 }
626
selectedItemBase()627 ItemBase * PartsBinPaletteWidget::selectedItemBase() {
628 return m_currentView->selectedItemBase();
629 }
630
contains(const QString & moduleID)631 bool PartsBinPaletteWidget::contains(const QString &moduleID) {
632 return m_iconView->contains(moduleID);
633 }
634
hasAlienParts()635 bool PartsBinPaletteWidget::hasAlienParts() {
636 return m_alienParts.size() > 0;
637 }
638
addPart(const QString & moduleID,int position)639 void PartsBinPaletteWidget::addPart(const QString& moduleID, int position) {
640 ModelPart *modelPart = m_referenceModel->retrieveModelPart(moduleID);
641 addPart(modelPart, position);
642 }
643
removePart(const QString & moduleID,const QString & path)644 void PartsBinPaletteWidget::removePart(const QString & moduleID, const QString & path) {
645 m_iconView->removePart(moduleID);
646 m_listView->removePart(moduleID);
647
648 // remove the model part from the model last, as this deletes it,
649 // and the removePart calls above still need the modelpart
650 m_model->removePart(moduleID);
651 if (path.contains(FolderUtils::getUserDataStorePath())) {
652 m_removed << path;
653 }
654 }
655
656
removeParts()657 void PartsBinPaletteWidget::removeParts() {
658 m_iconView->removeParts();
659 m_listView->removeParts();
660
661 // remove the model part from the model last, as this deletes it,
662 // and the removePart calls above still need the modelpart
663 m_model->removeParts();
664 }
665
removeAlienParts()666 void PartsBinPaletteWidget::removeAlienParts() {
667 foreach(QString moduleID, m_alienParts) {
668 removePart(moduleID, "");
669 }
670 m_alienParts.clear();
671 }
672
setInfoViewOnHover(bool infoViewOnHover)673 void PartsBinPaletteWidget::setInfoViewOnHover(bool infoViewOnHover) {
674 if(m_iconView) m_iconView->setInfoViewOnHover(infoViewOnHover);
675 if(m_listView) m_listView->setInfoViewOnHover(infoViewOnHover);
676 }
677
addPartCommand(const QString & moduleID)678 void PartsBinPaletteWidget::addPartCommand(const QString& moduleID) {
679 /*bool updating = alreadyIn(moduleID);
680
681 QString partTitle = m_referenceModel->partTitle(moduleID);
682 if(partTitle.isEmpty()) partTitle = moduleID;
683
684 QString undoStackMsg;
685
686 if(!updating) {
687 undoStackMsg = tr("\"%1\" added to bin").arg(partTitle);
688 } else {
689 undoStackMsg = tr("\"%1\" updated in bin").arg(partTitle);
690 }
691 QUndoCommand *parentCmd = new QUndoCommand(undoStackMsg);
692
693 int index = m_listView->position(moduleID);
694 new PartsBinAddCommand(this, moduleID, index, parentCmd);
695 m_undoStack->push(parentCmd);*/
696
697 QMessageBox::StandardButton answer = QMessageBox::question(
698 this,
699 tr("Add to bin"),
700 tr("Do you really want to add the selected part to the bin?"),
701 QMessageBox::Yes | QMessageBox::No,
702 QMessageBox::Yes
703 );
704 // TODO: make button texts translatable
705 if(answer == QMessageBox::Yes) {
706 int index = m_listView->position(moduleID);
707 m_undoStack->push(new QUndoCommand("Parts bin: part added"));
708 addPart(moduleID, index);
709 }
710 }
711
itemMoved()712 void PartsBinPaletteWidget::itemMoved() {
713 m_orderHasChanged = true;
714 m_manager->setDirtyTab(this);
715 }
716
setDirty(bool dirty)717 void PartsBinPaletteWidget::setDirty(bool dirty) {
718 m_manager->setDirtyTab(this, dirty);
719 m_isDirty = dirty;
720 }
721
fileName()722 const QString &PartsBinPaletteWidget::fileName() {
723 return m_fileName;
724 }
725
eventFilter(QObject * obj,QEvent * event)726 bool PartsBinPaletteWidget::eventFilter(QObject *obj, QEvent *event) {
727 if (obj == this) {
728 if (event->type() == QEvent::MouseButtonPress ||
729 event->type() == QEvent::GraphicsSceneDragMove ||
730 event->type() == QEvent::GraphicsSceneDrop ||
731 event->type() == QEvent::GraphicsSceneMousePress
732 ) {
733 emit focused(this);
734 }
735 }
736 return QFrame::eventFilter(obj, event);
737 }
738
currentView()739 PartsBinView *PartsBinPaletteWidget::currentView() {
740 return m_currentView;
741 }
742
dragEnterEvent(QDragEnterEvent * event)743 void PartsBinPaletteWidget::dragEnterEvent(QDragEnterEvent *event) {
744 QFrame::dragEnterEvent(event);
745 }
746
dragLeaveEvent(QDragLeaveEvent * event)747 void PartsBinPaletteWidget::dragLeaveEvent(QDragLeaveEvent *event) {
748 QFrame::dragLeaveEvent(event);
749 }
750
dragMoveEvent(QDragMoveEvent * event)751 void PartsBinPaletteWidget::dragMoveEvent(QDragMoveEvent *event) {
752 QFrame::dragMoveEvent(event);
753 }
754
dropEvent(QDropEvent * event)755 void PartsBinPaletteWidget::dropEvent(QDropEvent *event) {
756 QFrame::dropEvent(event);
757 }
758
759
addPartToMeAction()760 QAction *PartsBinPaletteWidget::addPartToMeAction() {
761 return m_addPartToMeAction;
762 }
763
addSketchPartToMe()764 void PartsBinPaletteWidget::addSketchPartToMe() {
765 m_manager->openBinIn(this->m_fileName, false);
766 QString moduleID = m_manager->getSelectedModuleIDFromSketch();
767 if (moduleID.isEmpty()) return;
768
769 bool wasAlreadyIn = contains(moduleID);
770 addPart(moduleID, -1);
771 if(!wasAlreadyIn) {
772 setDirty();
773 }
774 }
775
setFilename(const QString & filename)776 void PartsBinPaletteWidget::setFilename(const QString &filename) {
777 m_fileName = filename;
778 if (m_fileName.compare(BinManager::SearchBinLocation) == 0) {
779 m_searchStackedWidget->setCurrentIndex(1);
780 }
781 bool acceptIt = !currentBinIsCore();
782 setAcceptDrops(acceptIt);
783 m_iconView->setAcceptDrops(acceptIt);
784 m_listView->setAcceptDrops(acceptIt);
785 }
786
search()787 void PartsBinPaletteWidget::search() {
788 SearchLineEdit * edit = qobject_cast<SearchLineEdit *>(sender());
789 if (edit == NULL) return;
790
791 QString searchText = edit->text();
792 if (searchText.isEmpty()) return;
793
794 ModelPartSharedRoot * root = m_model->rootModelPartShared();
795 if (root) {
796 root->setSearchTerm(searchText);
797 }
798
799 m_manager->search(searchText);
800 }
801
allowsChanges()802 bool PartsBinPaletteWidget::allowsChanges() {
803 return m_allowsChanges;
804 }
805
readOnly()806 bool PartsBinPaletteWidget::readOnly() {
807 return !allowsChanges();
808 }
809
setAllowsChanges(bool allowsChanges)810 void PartsBinPaletteWidget::setAllowsChanges(bool allowsChanges) {
811 m_allowsChanges = allowsChanges;
812 }
813
setReadOnly(bool readOnly)814 void PartsBinPaletteWidget::setReadOnly(bool readOnly) {
815 setAllowsChanges(!readOnly);
816 }
817
focusSearch()818 void PartsBinPaletteWidget::focusSearch() {
819 if (m_searchLineEdit) {
820 QTimer::singleShot(20, this, SLOT(focusSearchAfter()));
821 }
822 }
823
focusSearchAfter()824 void PartsBinPaletteWidget::focusSearchAfter() {
825 //DebugDialog::debug("focus search after");
826 if (m_searchLineEdit->decoy()) {
827 m_searchLineEdit->setDecoy(false);
828 }
829 m_searchLineEdit->setFocus(Qt::OtherFocusReason);
830 }
831
setSaveQuietly(bool saveQuietly)832 void PartsBinPaletteWidget::setSaveQuietly(bool saveQuietly) {
833 m_saveQuietly = saveQuietly;
834 }
835
currentViewIsIconView()836 bool PartsBinPaletteWidget::currentViewIsIconView() {
837 if (m_currentView == NULL) return true;
838
839 return (m_currentView == m_iconView);
840 }
841
icon()842 QIcon PartsBinPaletteWidget::icon() {
843 if (m_icon) return *m_icon;
844
845 return EmptyIcon;
846 }
847
hasMonoIcon()848 bool PartsBinPaletteWidget::hasMonoIcon() {
849 return m_monoIcon != NULL;
850 }
851
monoIcon()852 QIcon PartsBinPaletteWidget::monoIcon() {
853 if (m_monoIcon) return *m_monoIcon;
854
855 return EmptyIcon;
856 }
857
combinedMenu()858 QMenu * PartsBinPaletteWidget::combinedMenu()
859 {
860 return m_manager->combinedMenu(this);
861 }
862
partContextMenu()863 QMenu * PartsBinPaletteWidget::partContextMenu()
864 {
865 return m_manager->partContextMenu(this);
866 }
867
binContextMenu()868 QMenu * PartsBinPaletteWidget::binContextMenu()
869 {
870 QMenu * menu = m_manager->binContextMenu(this);
871 if (menu == NULL) return NULL;
872
873 QMenu * newMenu = new QMenu();
874 foreach (QAction * action, menu->actions()) {
875 newMenu->addAction(action);
876 }
877
878 // TODO: need to enable/disable actions based on this bin
879
880 ModelPartSharedRoot * root = (m_model == NULL) ? NULL : m_model->rootModelPartShared();
881 if (root) {
882 QString iconFilename = root->icon();
883 if (iconFilename.compare(CustomIconName) == 0 || isCustomSvg(iconFilename)) {
884 newMenu->addSeparator();
885 QAction * action = new QAction(tr("Change icon color..."), newMenu);
886 action->setToolTip(tr("Change the color of the icon for this bin."));
887 connect(action, SIGNAL(triggered()), this, SLOT(changeIconColor()));
888 newMenu->addAction(action);
889 }
890 }
891 return newMenu;
892 }
893
changeIconColor()894 void PartsBinPaletteWidget::changeIconColor() {
895 QImage image(":resources/bins/icons/" + CustomIconName);
896 QColor initial(image.pixel(image.width() / 2, image.height() / 2));
897 QColor color = QColorDialog::getColor(initial, this, tr("Select a color for this icon"), 0 );
898 if (!color.isValid()) return;
899
900 QRgb match = initial.rgba();
901 for (int y = 0; y < image.height(); y++) {
902 for (int x = 0; x < image.width(); x++) {
903 QRgb rgb = image.pixel(x, y);
904 if (qRed(rgb) == qRed(match) && qBlue(rgb) == qBlue(match) && qGreen(rgb) == qGreen(match)) {
905 image.setPixel(x, y, (color.rgb() & 0xffffff) | (qAlpha(rgb) << 24));
906 }
907 }
908 }
909
910 #ifndef QT_NO_DEBUG
911 //QFileInfo info(m_fileName);
912 //image.save(FolderUtils::getUserDataStorePath("") + "/" + info.completeBaseName() + ".png");
913 #endif
914
915 delete m_icon;
916 m_icon = new QIcon(QPixmap::fromImage(image));
917
918 m_manager->setTabIcon(this, m_icon);
919 setDirty();
920
921 ModelPartSharedRoot * root = m_model->rootModelPartShared();
922
923 if (root) {
924 QSvgGenerator svgGenerator;
925 svgGenerator.setResolution(90);
926 svgGenerator.setTitle(CustomIconTitle);
927 QBuffer buffer;
928 svgGenerator.setOutputDevice(&buffer);
929 QSize sz = image.size();
930 svgGenerator.setSize(sz);
931 svgGenerator.setViewBox(QRect(0, 0, sz.width(), sz.height()));
932 QPainter svgPainter(&svgGenerator);
933 svgPainter.drawImage(QPoint(0,0), image);
934 svgPainter.end();
935 QString svg(buffer.buffer());
936 root->setIcon(svg);
937 }
938 }
939
fastLoaded()940 bool PartsBinPaletteWidget::fastLoaded() {
941 return m_fastLoaded;
942 }
943
location()944 BinLocation::Location PartsBinPaletteWidget::location() {
945 return m_location;
946 }
947
canClose()948 bool PartsBinPaletteWidget::canClose() {
949 switch (m_location) {
950 case BinLocation::User:
951 if (m_fileName.compare(BinManager::SearchBinLocation) == 0) return false;
952 if (m_fileName.compare(BinManager::ContribPartsBinLocation) == 0) return false;
953 if (m_fileName.compare(BinManager::MyPartsBinLocation) == 0) return false;
954 if (m_manager->isTempPartsBin(this)) return false;
955 return true;
956 case BinLocation::More:
957 return false;
958 case BinLocation::App:
959 return false;
960 case BinLocation::Outside:
961 default:
962 return true;
963 }
964 }
965
copyFilesToContrib(ModelPart * mp,QWidget * originator)966 void PartsBinPaletteWidget::copyFilesToContrib(ModelPart * mp, QWidget * originator) {
967 m_manager->copyFilesToContrib(mp, originator);
968 }
969
root()970 ModelPart * PartsBinPaletteWidget::root() {
971 if (m_model == NULL) return NULL;
972
973 return m_model->root();
974 }
975
isTempPartsBin()976 bool PartsBinPaletteWidget::isTempPartsBin() {
977 return m_manager->isTempPartsBin(this);
978 }
979
reloadPart(const QString & moduleID)980 void PartsBinPaletteWidget::reloadPart(const QString & moduleID) {
981 m_iconView->reloadPart(moduleID);
982 m_listView->reloadPart(moduleID);
983 }
984
getAllParts()985 QList<ModelPart *> PartsBinPaletteWidget::getAllParts() {
986 QList<ModelPart*> empty;
987 if (m_model == NULL) return empty;
988
989 if (m_model->root() == NULL) return empty;
990
991 return m_model->root()->getAllParts();
992 }
993
994
995