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: 6962 $:
22 $Author: irascibl@gmail.com $:
23 $Date: 2013-04-14 00:08:36 +0200 (So, 14. Apr 2013) $
24 
25 ********************************************************************/
26 
27 
28 #include <QVBoxLayout>
29 #include <QFileDialog>
30 #include <QMessageBox>
31 #include <QSettings>
32 #include <QtDebug>
33 #include <QInputDialog>
34 #include <QDropEvent>
35 #include <QMimeData>
36 
37 #include "binmanager.h"
38 #include "stacktabwidget.h"
39 #include "stacktabbar.h"
40 
41 #include "../../model/modelpart.h"
42 #include "../../mainwindow/mainwindow.h"
43 #include "../../model/palettemodel.h"
44 #include "../../waitpushundostack.h"
45 #include "../../debugdialog.h"
46 #include "../../utils/folderutils.h"
47 #include "../../utils/textutils.h"
48 #include "../../utils/fileprogressdialog.h"
49 #include "../../referencemodel/referencemodel.h"
50 #include "../../items/partfactory.h"
51 #include "../partsbinpalettewidget.h"
52 #include "../partsbinview.h"
53 
54 ///////////////////////////////////////////////////////////
55 
toString(BinLocation::Location location)56 QString BinLocation::toString(BinLocation::Location location) {
57 	switch (location) {
58 	case BinLocation::User:
59 			return "user";
60 	case BinLocation::More:
61 			return "more";
62 	case BinLocation::App:
63 			return "app";
64 	case BinLocation::Outside:
65 	default:
66 		return "outside";
67 	}
68 }
69 
fromString(const QString & locationString)70 BinLocation::Location BinLocation::fromString(const QString & locationString) {
71 	if (locationString.compare("user", Qt::CaseInsensitive) == 0) return BinLocation::User;
72 	if (locationString.compare("more", Qt::CaseInsensitive) == 0) return BinLocation::More;
73 	if (locationString.compare("app", Qt::CaseInsensitive) == 0) return BinLocation::App;
74 	return BinLocation::Outside;
75 }
76 
findLocation(const QString & filename)77 BinLocation::Location BinLocation::findLocation(const QString & filename)
78 {
79 
80 	if (filename.startsWith(FolderUtils::getUserDataStorePath("bins"))) {
81 		return BinLocation::User;
82 	}
83 	else if (filename.startsWith(FolderUtils::getApplicationSubFolderPath("bins") + "/more")) {
84 		return BinLocation::More;
85 	}
86 	else if (filename.startsWith(FolderUtils::getApplicationSubFolderPath("bins"))) {
87 		return BinLocation::App;
88 	}
89 
90 	return BinLocation::Outside;
91 }
92 ///////////////////////////////////////////////////////////
93 
94 QString BinManager::Title;
95 QString BinManager::MyPartsBinLocation;
96 QString BinManager::MyPartsBinTemplateLocation;
97 QString BinManager::SearchBinLocation;
98 QString BinManager::SearchBinTemplateLocation;
99 QString BinManager::ContribPartsBinLocation;
100 QString BinManager::TempPartsBinTemplateLocation;
101 QString BinManager::CorePartsBinLocation;
102 
103 QString BinManager::StandardBinStyle = "background-color: gray;";
104 QString BinManager::CurrentBinStyle = "background-color: black;";
105 
106 QHash<QString, QString> BinManager::StandardBinIcons;
107 
BinManager(class ReferenceModel * referenceModel,class HtmlInfoView * infoView,WaitPushUndoStack * undoStack,MainWindow * parent)108 BinManager::BinManager(class ReferenceModel *referenceModel, class HtmlInfoView *infoView, WaitPushUndoStack *undoStack, MainWindow* parent)
109 	: QFrame(parent)
110 {
111 	BinManager::Title = tr("Parts");
112 
113     m_combinedMenu = NULL;
114     m_showListViewAction = m_showIconViewAction = NULL;
115 
116 	m_referenceModel = referenceModel;
117 	m_infoView = infoView;
118 	m_undoStack = undoStack;
119 	m_defaultSaveFolder = FolderUtils::getUserDataStorePath("bins");
120 	m_mainWindow = parent;
121 	m_currentBin = NULL;
122 
123 	connect(this, SIGNAL(savePartAsBundled(const QString &)), m_mainWindow, SLOT(saveBundledPart(const QString &)));
124 
125 	m_unsavedBinsCount = 0;
126 
127 	QVBoxLayout *lo = new QVBoxLayout(this);
128 
129 	m_stackTabWidget = new StackTabWidget(this);
130 	m_stackTabWidget->setTabPosition(QTabWidget::West);
131 	lo->addWidget(m_stackTabWidget);
132 
133 	lo->setMargin(0);
134 	lo->setSpacing(0);
135     setMaximumHeight(500);
136 }
137 
~BinManager()138 BinManager::~BinManager() {
139 }
140 
initStandardBins()141 void BinManager::initStandardBins()
142 {
143 	createCombinedMenu();
144 	createContextMenus();
145 
146 	//DebugDialog::debug("init bin manager");
147 	QList<BinLocation *> actualLocations;
148 	findAllBins(actualLocations);
149 
150     hackLocalContrib(actualLocations);
151 
152 	restoreStateAndGeometry(actualLocations);
153 	foreach (BinLocation * location, actualLocations) {
154 		PartsBinPaletteWidget* bin = newBin();
155         bin->load(location->path, m_mainWindow->fileProgressDialog(), true);
156 		m_stackTabWidget->addTab(bin, bin->icon(), bin->title());
157 		m_stackTabWidget->stackTabBar()->setTabToolTip(m_stackTabWidget->count() - 1, bin->title());
158 		registerBin(bin);
159 		delete location;
160 	}
161     actualLocations.clear();
162 
163 	//DebugDialog::debug("open core bin");
164 	openCoreBinIn();
165 
166 	//DebugDialog::debug("after core bin");
167     currentChanged(m_stackTabWidget->currentIndex());
168 
169 	connectTabWidget();
170 }
171 
addBin(PartsBinPaletteWidget * bin)172 void BinManager::addBin(PartsBinPaletteWidget* bin) {
173 	m_stackTabWidget->addTab(bin, bin->icon(), bin->title());
174 	registerBin(bin);
175 	setAsCurrentBin(bin);
176 }
177 
registerBin(PartsBinPaletteWidget * bin)178 void BinManager::registerBin(PartsBinPaletteWidget* bin) {
179 
180 	if (!bin->fileName().isEmpty()) {
181 		m_openedBins[bin->fileName()] = bin;
182 
183 	    if (bin->fileName().compare(CorePartsBinLocation) == 0) {
184 		    bin->setAllowsChanges(false);
185 	    }
186 	    else if (bin->fileName().compare(SearchBinLocation) == 0) {
187 		    bin->setAllowsChanges(false);
188 	    }
189 	    else if (bin->fileName().compare(ContribPartsBinLocation) == 0) {
190 		    bin->setAllowsChanges(false);
191 	    }
192 	    else if (bin->fileName().compare(m_tempPartsBinLocation) == 0) {
193 		    bin->setAllowsChanges(false);
194 	    }
195 	    else if (bin->fileName().contains(FolderUtils::getApplicationSubFolderPath("bins"))) {
196 		    bin->setAllowsChanges(false);
197 	    }
198     }
199 }
200 
connectTabWidget()201 void BinManager::connectTabWidget() {
202 	connect(
203 		m_stackTabWidget, SIGNAL(currentChanged(int)),
204 		this, SLOT(currentChanged(int))
205 	);
206 	connect(
207 		m_stackTabWidget, SIGNAL(tabCloseRequested(int)),
208 		this, SLOT(tabCloseRequested(int))
209 	);
210 }
211 
insertBin(PartsBinPaletteWidget * bin,int index)212 void BinManager::insertBin(PartsBinPaletteWidget* bin, int index) {
213 	registerBin(bin);
214 	m_stackTabWidget->insertTab(index, bin, bin->icon(), bin->title());
215 	m_stackTabWidget->setCurrentIndex(index);
216 }
217 
beforeClosing()218 bool BinManager::beforeClosing() {
219 	bool retval = true;
220 
221 	for(int j = 0; j < m_stackTabWidget->count(); j++) {
222 		PartsBinPaletteWidget *bin = qobject_cast<PartsBinPaletteWidget*>(m_stackTabWidget->widget(j));
223 		if (bin && !bin->fastLoaded()) {
224 			setAsCurrentTab(bin);
225 			retval = retval && bin->beforeClosing();
226 			if(!retval) break;
227 		}
228 	}
229 
230 
231 	if(retval) {
232 		saveStateAndGeometry();
233 	}
234 
235 	return retval;
236 }
237 
setAsCurrentTab(PartsBinPaletteWidget * bin)238 void BinManager::setAsCurrentTab(PartsBinPaletteWidget* bin) {
239 	m_stackTabWidget->setCurrentWidget(bin);
240 }
241 
242 
hasAlienParts()243 bool BinManager::hasAlienParts() {
244 	return false;
245 
246 }
247 
setInfoViewOnHover(bool infoViewOnHover)248 void BinManager::setInfoViewOnHover(bool infoViewOnHover) {
249 	Q_UNUSED(infoViewOnHover);
250 }
251 
addNewPart(ModelPart * modelPart)252 void BinManager::addNewPart(ModelPart *modelPart) {
253 	PartsBinPaletteWidget* myPartsBin = getOrOpenMyPartsBin();
254 	myPartsBin->addPart(modelPart);
255 	setDirtyTab(myPartsBin);
256 }
257 
getOrOpenMyPartsBin()258 PartsBinPaletteWidget* BinManager::getOrOpenMyPartsBin() {
259     return getOrOpenBin(MyPartsBinLocation, MyPartsBinTemplateLocation);
260 }
261 
getOrOpenSearchBin()262 PartsBinPaletteWidget* BinManager::getOrOpenSearchBin() {
263     PartsBinPaletteWidget * bin = getOrOpenBin(SearchBinLocation, SearchBinTemplateLocation);
264 	if (bin) {
265 		bin->setSaveQuietly(true);
266 	}
267 	return bin;
268 }
269 
getOrOpenBin(const QString & binLocation,const QString & binTemplateLocation)270 PartsBinPaletteWidget* BinManager::getOrOpenBin(const QString & binLocation, const QString & binTemplateLocation) {
271 
272     PartsBinPaletteWidget* partsBin = findBin(binLocation);
273 
274     if(!partsBin) {
275         QString fileToOpen = QFileInfo(binLocation).exists() ? binLocation : createIfBinNotExists(binLocation, binTemplateLocation);
276         partsBin = openBinIn(fileToOpen, false);
277 	}
278 	if (partsBin != NULL && partsBin->fastLoaded()) {
279 		partsBin->load(partsBin->fileName(), partsBin, false);
280 	}
281 
282     return partsBin;
283 }
284 
findBin(const QString & binLocation)285 PartsBinPaletteWidget* BinManager::findBin(const QString & binLocation) {
286 	for (int i = 0; i < m_stackTabWidget->count(); i++) {
287 		PartsBinPaletteWidget* bin = (PartsBinPaletteWidget *) m_stackTabWidget->widget(i);
288         if(bin->fileName() == binLocation) {
289             return bin;
290 		}
291 	}
292 
293 	return NULL;
294 }
295 
createIfMyPartsNotExists()296 QString BinManager::createIfMyPartsNotExists() {
297     return createIfBinNotExists(MyPartsBinLocation, MyPartsBinTemplateLocation);
298 }
299 
createIfSearchNotExists()300 QString BinManager::createIfSearchNotExists() {
301     return createIfBinNotExists(SearchBinLocation, SearchBinTemplateLocation);
302 }
303 
createIfBinNotExists(const QString & dest,const QString & source)304 QString BinManager::createIfBinNotExists(const QString & dest, const QString & source)
305 {
306     QString binPath = dest;
307     QFile file(source);
308 	FolderUtils::slamCopy(file, binPath);
309 	return binPath;
310 }
311 
addPartToBin(ModelPart * modelPart,int position)312 void BinManager::addPartToBin(ModelPart *modelPart, int position) {
313 	PartsBinPaletteWidget *bin = m_currentBin? m_currentBin: getOrOpenMyPartsBin();
314 	addPartToBinAux(bin,modelPart,position);
315 }
316 
addToMyParts(ModelPart * modelPart)317 void BinManager::addToMyParts(ModelPart *modelPart) {
318 	PartsBinPaletteWidget *bin = getOrOpenMyPartsBin();
319 	if (bin) {
320 		addPartToBinAux(bin,modelPart);
321 		setAsCurrentTab(bin);
322 	}
323 }
324 
addToTempPartsBin(ModelPart * modelPart)325 void BinManager::addToTempPartsBin(ModelPart *modelPart) {
326 	PartsBinPaletteWidget *bin = getOrOpenBin(m_tempPartsBinLocation, TempPartsBinTemplateLocation);
327 	if (bin) {
328 		addPartToBinAux(bin,modelPart);
329 		setAsCurrentTab(bin);
330 		bin->setDirty(false);
331 	}
332 }
333 
hideTempPartsBin()334 void BinManager::hideTempPartsBin() {
335 	for (int i = 0; i < m_stackTabWidget->count(); i++) {
336 		PartsBinPaletteWidget* bin = (PartsBinPaletteWidget *) m_stackTabWidget->widget(i);
337         if (bin->fileName().compare(m_tempPartsBinLocation) == 0) {
338             m_stackTabWidget->removeTab(i);
339 			break;
340 		}
341 	}
342 }
343 
addPartToBinAux(PartsBinPaletteWidget * bin,ModelPart * modelPart,int position)344 void BinManager::addPartToBinAux(PartsBinPaletteWidget *bin, ModelPart *modelPart, int position) {
345 	if(bin) {
346 		if (bin->fastLoaded()) {
347 			bin->load(bin->fileName(), bin, false);
348 		}
349 		bin->addPart(modelPart, position);
350 		setDirtyTab(bin);
351 	}
352 }
353 
load(const QString & filename)354 void BinManager::load(const QString& filename) {
355 	openBin(filename);
356 }
357 
358 
setDirtyTab(PartsBinPaletteWidget * w,bool dirty)359 void BinManager::setDirtyTab(PartsBinPaletteWidget* w, bool dirty) {
360 	/*
361 	if (!w->windowTitle().contains(FritzingWindow::QtFunkyPlaceholder)) {
362 		// trying to deal with the warning in QWidget::setWindowModified
363 		// but setting the title here doesn't work
364 		QString t = w->windowTitle();
365 		if (t.isEmpty()) t = " ";
366 		w->setWindowTitle(t);
367 	}
368 	*/
369 	w->setWindowModified(dirty);
370 	if(m_stackTabWidget != NULL) {
371 		int tabIdx = m_stackTabWidget->indexOf(w);
372 		m_stackTabWidget->setTabText(tabIdx, w->title()+(dirty? " *": ""));
373 	} else {
374 		qWarning() << tr("BinManager::setDirtyTab: Couldn't set the bin '%1' as dirty").arg(w->title());
375 	}
376 }
377 
updateTitle(PartsBinPaletteWidget * w,const QString & newTitle)378 void BinManager::updateTitle(PartsBinPaletteWidget* w, const QString& newTitle) {
379 	if(m_stackTabWidget != NULL) {
380 		m_stackTabWidget->setTabText(m_stackTabWidget->indexOf(w), newTitle+" *");
381 		setDirtyTab(w);
382 	}
383 	else {
384 		qWarning() << tr("BinManager::updateTitle: Couldn't set the bin '%1' as dirty").arg(w->title());
385 	}
386 }
387 
newBinIn()388 PartsBinPaletteWidget* BinManager::newBinIn() {
389 	PartsBinPaletteWidget* bin = newBin();
390 	bin->setPaletteModel(new PaletteModel(true, false), true);
391 	bin->setTitle(tr("New bin (%1)").arg(++m_unsavedBinsCount));
392 	insertBin(bin, m_stackTabWidget->count());
393     bin->setReadOnly(false);
394 	renameBin();
395 	return bin;
396 }
397 
openBinIn(QString fileName,bool fastLoad)398 PartsBinPaletteWidget* BinManager::openBinIn(QString fileName, bool fastLoad) {
399 	if(fileName.isNull() || fileName.isEmpty()) {
400 		fileName = QFileDialog::getOpenFileName(
401 				this,
402 				tr("Select a Fritzing Parts Bin file to open"),
403 				m_defaultSaveFolder,
404 				tr("Fritzing Bin Files (*%1 *%2);;Fritzing Bin (*%1);;Fritzing Shareable Bin (*%2)")
405 				.arg(FritzingBinExtension).arg(FritzingBundledBinExtension)
406 		);
407         if (fileName.isNull()) return NULL;
408 	}
409 	PartsBinPaletteWidget* bin = NULL;
410 	bool createNewOne = false;
411 	if(m_openedBins.contains(fileName)) {
412 		bin = m_openedBins[fileName];
413 		if(m_stackTabWidget) {
414 			m_stackTabWidget->setCurrentWidget(bin);
415 		} else {
416 			m_openedBins.remove(fileName);
417 			createNewOne = true;
418 		}
419 	} else {
420 		createNewOne = true;
421 	}
422 
423 	if(createNewOne) {
424 		bin = newBin();
425 		if(bin->open(fileName, bin, fastLoad)) {
426 			m_openedBins[fileName] = bin;
427 			insertBin(bin, m_stackTabWidget->count());
428 
429 			// to force the user to take a decision of what to do with the imported parts
430 			if(fileName.endsWith(FritzingBundledBinExtension)) {
431 				setDirtyTab(bin);
432 			}
433 		}
434 	}
435 	if (!fastLoad) {
436 		setAsCurrentBin(bin);
437 	}
438 	return bin;
439 }
440 
openCoreBinIn()441 PartsBinPaletteWidget* BinManager::openCoreBinIn() {
442 	PartsBinPaletteWidget* bin = findBin(CorePartsBinLocation);
443 	if (bin != NULL) {
444 		setAsCurrentTab(bin);
445 	}
446 	else {
447 		bin = newBin();
448 		bin->setAllowsChanges(false);
449 		bin->load(BinManager::CorePartsBinLocation, bin, false);
450 		insertBin(bin, 0);
451 	}
452 	setAsCurrentBin(bin);
453 	return bin;
454 }
455 
newBin()456 PartsBinPaletteWidget* BinManager::newBin() {
457 	PartsBinPaletteWidget* bin = new PartsBinPaletteWidget(m_referenceModel, m_infoView, m_undoStack,this);
458 	connect(
459 		bin, SIGNAL(fileNameUpdated(PartsBinPaletteWidget*, const QString&, const QString&)),
460 		this, SLOT(updateFileName(PartsBinPaletteWidget*, const QString&, const QString&))
461 	);
462 	connect(
463 		bin, SIGNAL(focused(PartsBinPaletteWidget*)),
464 		this, SLOT(setAsCurrentBin(PartsBinPaletteWidget*))
465 	);
466 	connect(bin, SIGNAL(saved(bool)), m_mainWindow, SLOT(binSaved(bool)));
467 	connect(m_mainWindow, SIGNAL(alienPartsDismissed()), bin, SLOT(removeAlienParts()));
468 
469 	return bin;
470 }
471 
currentChanged(int index)472 void BinManager::currentChanged(int index) {
473 	for (int i = 0; i < m_stackTabWidget->count(); i++) {
474 		PartsBinPaletteWidget* bin = (PartsBinPaletteWidget *) m_stackTabWidget->widget(i);
475         if (bin == NULL) continue;
476         if (!bin->hasMonoIcon()) continue;
477 
478         if (i == index) {
479             m_stackTabWidget->setTabIcon(i, bin->icon());
480         }
481         else {
482             m_stackTabWidget->setTabIcon(i, bin->monoIcon());
483         }
484 
485     }
486 
487 
488 
489 
490 
491 	PartsBinPaletteWidget *bin = getBin(index);
492 	if (bin) setAsCurrentBin(bin);
493 }
494 
setAsCurrentBin(PartsBinPaletteWidget * bin)495 void BinManager::setAsCurrentBin(PartsBinPaletteWidget* bin) {
496 	if (bin == NULL) {
497 		qWarning() << tr("Cannot set a NULL bin as the current one");
498 		return;
499 	}
500 
501 	if (bin->fastLoaded()) {
502 		bin->load(bin->fileName(), bin, false);
503 	}
504 
505 	if (m_currentBin == bin) return;
506 
507 	if (bin->fileName().compare(SearchBinLocation) == 0) {
508 		bin->focusSearch();
509 	}
510 
511     /*
512 
513     // jrc 3-july-2013 commented out this stylesheet change:
514     //      it causes the tab bar to lose its scroll position
515     //      the stylesheet change is commented out in the qss file, so visually it's a no-op
516 
517 	QString style = m_mainWindow->styleSheet();
518 	if(m_currentBin && m_stackTabWidget) {
519 		StackTabBar *currTabBar = m_stackTabWidget->stackTabBar();
520 		currTabBar->setProperty("current","false");
521 		currTabBar->setStyleSheet("");
522 		currTabBar->setStyleSheet(style);
523 	}
524 	if(m_stackTabWidget) {
525 		m_currentBin = bin;
526 		StackTabBar *currTabBar = m_stackTabWidget->stackTabBar();
527 		currTabBar->setProperty("current","true");
528 		currTabBar->setStyleSheet("");
529 		currTabBar->setStyleSheet(style);
530 	}
531     */
532 }
533 
closeBinIn(int index)534 void BinManager::closeBinIn(int index) {
535 	if (m_stackTabWidget->count() == 1) return;
536 
537 	int realIndex = index == -1? m_stackTabWidget->currentIndex(): index;
538 	PartsBinPaletteWidget *w = getBin(realIndex);
539 	if(w && w->beforeClosing()) {
540 		m_stackTabWidget->removeTab(realIndex);
541 		m_openedBins.remove(w->fileName());
542 	}
543 }
544 
getBin(int index)545 PartsBinPaletteWidget* BinManager::getBin(int index) {
546 	return qobject_cast<PartsBinPaletteWidget*>(m_stackTabWidget->widget(index));
547 }
548 
currentBin()549 PartsBinPaletteWidget* BinManager::currentBin() {
550 	return qobject_cast<PartsBinPaletteWidget*>(m_stackTabWidget->currentWidget());
551 }
552 
updateFileName(PartsBinPaletteWidget * bin,const QString & newFileName,const QString & oldFilename)553 void BinManager::updateFileName(PartsBinPaletteWidget* bin, const QString &newFileName, const QString &oldFilename) {
554 	m_openedBins.remove(oldFilename);
555 	m_openedBins[newFileName] = bin;
556 }
557 
saveStateAndGeometry()558 void BinManager::saveStateAndGeometry() {
559 	QSettings settings;
560 	settings.remove("bins2"); // clean up previous state
561 	settings.beginGroup("bins2");
562 
563 	for(int j = m_stackTabWidget->count() - 1; j >= 0; j--) {
564 		PartsBinPaletteWidget *bin = qobject_cast<PartsBinPaletteWidget*>(m_stackTabWidget->widget(j));
565 		if (bin) {
566 			settings.beginGroup(QString::number(j));
567 			settings.setValue("location", BinLocation::toString(bin->location()));
568 			settings.setValue("title", bin->title());
569 			settings.setValue("path", bin->fileName());
570 			settings.endGroup();
571 		}
572 	}
573 
574 	settings.endGroup();
575 }
576 
restoreStateAndGeometry(QList<BinLocation * > & actualLocations)577 void BinManager::restoreStateAndGeometry(QList<BinLocation *> & actualLocations) {
578 	QList<BinLocation *> theoreticalLocations;
579 
580 	QSettings settings;
581 	settings.beginGroup("bins2");
582 	int size = settings.childGroups().size();
583 	if (size == 0) {
584 		// first time
585 		readTheoreticalLocations(theoreticalLocations);
586 	}
587 	else {
588 		for (int i = 0; i < size; ++i) {
589 			settings.beginGroup(QString::number(i));
590 			BinLocation  * location = new BinLocation;
591 			location->location = BinLocation::fromString(settings.value("location").toString());
592 			location->path = settings.value("path").toString();
593 			location->title = settings.value("title").toString();
594 			theoreticalLocations.append(location);
595 			settings.endGroup();
596 		}
597 	}
598 
599 	foreach (BinLocation * location, actualLocations) {
600 		location->marked = false;
601 	}
602 	foreach (BinLocation * tLocation, theoreticalLocations) {
603 		foreach (BinLocation * aLocation, actualLocations) {
604 			if (aLocation->title.compare(tLocation->title) == 0 && aLocation->location == tLocation->location) {
605 				aLocation->marked = true;
606 				break;
607 			}
608 		}
609 	}
610 
611 	QList<BinLocation *> tempLocations(actualLocations);
612 	actualLocations.clear();
613 
614 	foreach (BinLocation * tLocation, theoreticalLocations) {
615         tLocation->marked = false;
616         bool gotOne = false;
617 
618 		for (int ix = 0; ix < tempLocations.count(); ix++) {
619 			BinLocation  * aLocation = tempLocations[ix];
620 			if (aLocation->title.compare(tLocation->title) == 0 && aLocation->location == tLocation->location) {
621 				gotOne = true;
622 				actualLocations.append(aLocation);
623 				tempLocations.removeAt(ix);
624                 //DebugDialog::debug("adding loc 1 " + aLocation->path);
625 				break;
626 			}
627 		}
628 		if (gotOne) continue;
629 
630 		if (tLocation->title == "___*___") {
631 			for (int ix = 0; ix < tempLocations.count(); ix++) {
632 				BinLocation * aLocation = tempLocations[ix];
633 				if (!aLocation->marked && aLocation->location == tLocation->location) {
634 					gotOne = true;
635 					actualLocations.append(aLocation);
636 					tempLocations.removeAt(ix);
637                     //DebugDialog::debug("adding loc 2 " + aLocation->path);
638                     break;
639 				}
640 			}
641 		}
642 		if (gotOne) continue;
643 
644 		if (!tLocation->path.isEmpty()) {
645 			QFileInfo info(tLocation->path);
646 			if (info.exists()) {
647                 //DebugDialog::debug("adding loc 3 " + tLocation->path);
648 				actualLocations.append(tLocation);
649                 tLocation->marked = true;
650 			}
651 		}
652 	}
653 
654         foreach (BinLocation * tLocation, theoreticalLocations) {
655             if (!tLocation->marked) {
656                 delete tLocation;
657             }
658 	}
659 
660 	// catch the leftovers
661 	actualLocations.append(tempLocations);
662     //foreach (BinLocation * aLocation, tempLocations) {
663         //DebugDialog::debug("adding loc 4 " + aLocation->path);
664     //}
665 
666 
667 }
668 
readTheoreticalLocations(QList<BinLocation * > & theoreticalLocations)669 void BinManager::readTheoreticalLocations(QList<BinLocation *> & theoreticalLocations)
670 {
671 	QFile file(":/resources/bins/order.xml");
672 	QString errorStr;
673 	int errorLine;
674 	int errorColumn;
675 	QDomDocument domDocument;
676 
677 	if (!domDocument.setContent(&file, true, &errorStr, &errorLine, &errorColumn)) {
678 		DebugDialog::debug(QString("unable to parse order.xml: %1 %2 %3").arg(errorStr).arg(errorLine).arg(errorColumn));
679 		return;
680 	}
681 
682 	QDomElement bin = domDocument.documentElement().firstChildElement("bin");
683 	while (!bin.isNull()) {
684 		BinLocation * location = new BinLocation;
685 		location->title = bin.attribute("title", "");
686 		location->location = BinLocation::fromString(bin.attribute("location", ""));
687 		theoreticalLocations.append(location);
688 		bin = bin.nextSiblingElement("bin");
689 	}
690 }
691 
hackLocalContrib(QList<BinLocation * > & locations)692 void BinManager::hackLocalContrib(QList<BinLocation *> & locations)
693 {
694     // with release 0.7.12, there is no more local contrib bin
695     // so clear out existing local contrib bins by copying parts to mine bin
696 
697     BinLocation * localContrib = NULL;
698     BinLocation * myParts = NULL;
699     foreach (BinLocation * location, locations) {
700         if (location->location == BinLocation::User) {
701             if (location->title == "Contributed Parts") {
702                 localContrib = location;
703             }
704             else if (location->title == "My Parts") {
705                 myParts = location;
706             }
707         }
708     }
709 
710     if (localContrib == NULL) return;
711 
712     if (myParts == NULL) {
713         createIfBinNotExists(MyPartsBinLocation, MyPartsBinTemplateLocation);
714 	    myParts = new BinLocation;
715 	    myParts->location = BinLocation::User;
716 	    myParts->path = MyPartsBinLocation;
717 	    QString icon;
718 	    getBinTitle(myParts->path, myParts->title, icon);
719 	    locations.append(myParts);
720     }
721 
722 	QString errorStr;
723 	int errorLine;
724 	int errorColumn;
725 
726     QFile contribFile(localContrib->path);
727     QDomDocument contribDoc;
728     bool result = contribDoc.setContent(&contribFile, true, &errorStr, &errorLine, &errorColumn);
729     locations.removeOne(localContrib);
730     contribFile.close();
731     bool removed = contribFile.remove();
732     if (!removed) {
733         DebugDialog::debug("failed to remove contrib bin");
734     }
735 
736     if (!result) return;
737 
738     QFile myPartsFile(myParts->path);
739     QDomDocument myPartsDoc;
740     if (!myPartsDoc.setContent(&myPartsFile, true, &errorStr, &errorLine, &errorColumn)) {
741         return;
742     }
743 
744     QDomElement myPartsRoot = myPartsDoc.documentElement();
745     QDomElement myPartsInstances = myPartsRoot.firstChildElement("instances");
746 
747     bool changed = false;
748 
749     QDomElement contribRoot = contribDoc.documentElement();
750     QDomElement contribInstances = contribRoot.firstChildElement("instances");
751     QDomElement contribInstance = contribInstances.firstChildElement("instance");
752     while (!contribInstance.isNull()) {
753         QString moduleIDRef = contribInstance.attribute("moduleIdRef");
754         QDomElement myPartsInstance = myPartsInstances.firstChildElement("instance");
755         bool already = false;
756         while (!myPartsInstance.isNull()) {
757             QString midr = myPartsInstance.attribute("moduleIdRef");
758             if (midr == moduleIDRef) {
759                 already = true;
760                 break;
761             }
762             myPartsInstance = myPartsInstance.nextSiblingElement("instance");
763         }
764 
765         if (!already) {
766             QDomNode node = contribInstance.cloneNode(true);
767             myPartsInstances.appendChild(node);
768             changed = true;
769         }
770 
771         contribInstance = contribInstance.nextSiblingElement("instance");
772     }
773 
774     if (changed) {
775         TextUtils::writeUtf8(myParts->path, myPartsDoc.toString(0));
776     }
777 }
778 
findAllBins(QList<BinLocation * > & locations)779 void BinManager::findAllBins(QList<BinLocation *> & locations)
780 {
781 	BinLocation * location = new BinLocation;
782 	location->location = BinLocation::App;
783 	location->path = CorePartsBinLocation;
784 	QString icon;
785 	getBinTitle(location->path, location->title, icon);
786 	locations.append(location);
787 
788 	location = new BinLocation;
789 	location->location = BinLocation::App;
790 	location->path = ContribPartsBinLocation;
791 	getBinTitle(location->path, location->title, icon);
792 	locations.append(location);
793 
794 	QDir userBinsDir(FolderUtils::getUserDataStorePath("bins"));
795 	findBins(userBinsDir, locations, BinLocation::User);
796 
797 	QDir dir(FolderUtils::getApplicationSubFolderPath("bins"));
798 	dir.cd("more");
799 	findBins(dir, locations, BinLocation::More);
800 }
801 
findBins(QDir & dir,QList<BinLocation * > & locations,BinLocation::Location loc)802 void BinManager::findBins(QDir & dir, QList<BinLocation *> & locations, BinLocation::Location loc) {
803 
804 	QStringList filters;
805 	filters << "*"+FritzingBinExtension;
806 	QFileInfoList files = dir.entryInfoList(filters);
807 	foreach(QFileInfo info, files) {
808 		BinLocation * location = new BinLocation;
809 		location->path = info.absoluteFilePath();
810 		location->location = loc;
811 		QString icon;
812 		getBinTitle(location->path, location->title, icon);
813 		locations.append(location);
814 	}
815 }
816 
getBinTitle(const QString & filename,QString & binName,QString & iconName)817 bool BinManager::getBinTitle(const QString & filename, QString & binName, QString & iconName) {
818 	QFile file(filename);
819 	file.open(QFile::ReadOnly);
820 	QXmlStreamReader xml(&file);
821 	xml.setNamespaceProcessing(false);
822 
823 	while (!xml.atEnd()) {
824         switch (xml.readNext()) {
825         case QXmlStreamReader::StartElement:
826 			if (xml.name().toString().compare("module") == 0) {
827 				iconName = xml.attributes().value("icon").toString();
828 			}
829 			else if (xml.name().toString().compare("title") == 0) {
830 				binName = xml.readElementText();
831 				return true;
832 			}
833 			break;
834 
835 		default:
836 			break;
837 		}
838 	}
839 
840 	return false;
841 }
842 
tabCloseRequested(int index)843 void BinManager::tabCloseRequested(int index) {
844 	closeBinIn(index);
845 }
846 
addPartTo(PartsBinPaletteWidget * bin,ModelPart * mp,bool setDirty)847 void BinManager::addPartTo(PartsBinPaletteWidget* bin, ModelPart* mp, bool setDirty) {
848 	if(mp) {
849 		bool alreadyIn = bin->contains(mp->moduleID());
850 		bin->addPart(mp);
851 		if(!alreadyIn && setDirty) {
852 			setDirtyTab(bin,true);
853 		}
854 	}
855 }
856 
editSelectedPartNewFrom(PartsBinPaletteWidget * bin)857 void BinManager::editSelectedPartNewFrom(PartsBinPaletteWidget* bin) {
858 	ItemBase * itemBase = bin->selectedItemBase();
859 	m_mainWindow->getPartsEditorNewAnd(itemBase);
860 }
861 
isTabReorderingEvent(QDropEvent * event)862 bool BinManager::isTabReorderingEvent(QDropEvent* event) {
863 	const QMimeData *m = event->mimeData();
864 	QStringList formats = m->formats();
865 	return formats.contains("action") && (m->data("action") == "tab-reordering");
866 }
867 
getSelectedModuleIDFromSketch()868 const QString &BinManager::getSelectedModuleIDFromSketch() {
869 	return m_mainWindow->selectedModuleID();
870 }
871 
openedBinsActions(const QString & moduleId)872 QList<QAction*> BinManager::openedBinsActions(const QString &moduleId) {
873 	QMap<QString,QAction*> titlesAndActions; // QMap sorts values by key
874 
875 	for (int i = 0; i < m_stackTabWidget->count(); i++) {
876 		PartsBinPaletteWidget* pppw = (PartsBinPaletteWidget *) m_stackTabWidget->widget(i);
877         if (pppw->readOnly()) continue;
878 
879 		QAction *act = pppw->addPartToMeAction();
880 		act->setEnabled(!pppw->contains(moduleId));
881 		titlesAndActions[pppw->title()] = act;
882 	}
883 
884 	return titlesAndActions.values();
885 }
886 
openBin(const QString & filename)887 void BinManager::openBin(const QString &filename) {
888 	openBinIn(filename, false);
889 }
890 
mainWindow()891 MainWindow* BinManager::mainWindow() {
892 	return m_mainWindow;
893 }
894 
initNames()895 void BinManager::initNames() {
896     BinManager::MyPartsBinLocation = FolderUtils::getUserDataStorePath("bins")+"/my_parts.fzb";
897     BinManager::MyPartsBinTemplateLocation =":/resources/bins/my_parts.fzb";
898     BinManager::SearchBinLocation = FolderUtils::getUserDataStorePath("bins")+"/search.fzb";
899     BinManager::SearchBinTemplateLocation =":/resources/bins/search.fzb";
900 	BinManager::ContribPartsBinLocation = FolderUtils::getApplicationSubFolderPath("bins")+"/contribParts.fzb";
901     BinManager::CorePartsBinLocation = FolderUtils::getApplicationSubFolderPath("bins")+"/core.fzb";
902     BinManager::TempPartsBinTemplateLocation =":/resources/bins/temp.fzb";
903 
904 	StandardBinIcons.insert(BinManager::MyPartsBinLocation, "Mine.png");
905 	StandardBinIcons.insert(BinManager::SearchBinLocation, "Search.png");
906 	StandardBinIcons.insert(BinManager::ContribPartsBinLocation, "Contrib.png");
907 	StandardBinIcons.insert(BinManager::CorePartsBinLocation, "Core.png");
908 }
909 
search(const QString & searchText)910 void BinManager::search(const QString & searchText) {
911     PartsBinPaletteWidget * searchBin = getOrOpenSearchBin();
912     if (searchBin == NULL) return;
913 
914     FileProgressDialog progress(tr("Searching..."), 0, this);
915     progress.setIncValueMod(10);
916     connect(m_referenceModel, SIGNAL(addSearchMaximum(int)), &progress, SLOT(addMaximum(int)));
917     connect(m_referenceModel, SIGNAL(incSearch()), &progress, SLOT(incValue()));
918 
919     QList<ModelPart *> modelParts = m_referenceModel->search(searchText, false);
920 
921     progress.setIncValueMod(1);
922     searchBin->removeParts();
923     foreach (ModelPart * modelPart, modelParts) {
924         //DebugDialog::debug(modelPart->title());
925         if (modelPart->itemType() == ModelPart::SchematicSubpart) {
926         }
927         else if (modelPart->moduleID().contains(PartFactory::OldSchematicPrefix)) {
928         }
929         else {
930             this->addPartTo(searchBin, modelPart, false);
931         }
932         progress.incValue();
933     }
934 
935     setDirtyTab(searchBin);
936 }
937 
currentViewIsIconView()938 bool BinManager::currentViewIsIconView() {
939 	PartsBinPaletteWidget * bin = currentBin();
940 	if (bin == NULL) return true;
941 
942 	return bin->currentViewIsIconView();
943 }
944 
toIconView()945 void BinManager::toIconView() {
946 	PartsBinPaletteWidget * bin = currentBin();
947 	if (bin == NULL) return;
948 
949 	bin->toIconView();
950 }
951 
toListView()952 void BinManager::toListView() {
953 	PartsBinPaletteWidget * bin = currentBin();
954 	if (bin == NULL) return;
955 
956 	bin->toListView();
957 }
958 
updateBinCombinedMenuCurrent()959 void BinManager::updateBinCombinedMenuCurrent() {
960 	PartsBinPaletteWidget * bin = currentBin();
961 	if (bin == NULL) return;
962 
963 	updateBinCombinedMenu(bin);
964 }
965 
updateBinCombinedMenu(PartsBinPaletteWidget * bin)966 void BinManager::updateBinCombinedMenu(PartsBinPaletteWidget * bin) {
967     if (m_combinedMenu == NULL) return;
968 
969 	m_saveBinAction->setEnabled(bin->allowsChanges());
970 	m_renameBinAction->setEnabled(bin->canClose());
971 	m_closeBinAction->setEnabled(bin->canClose());
972 	m_deleteBinAction->setEnabled(bin->canClose());
973 	ItemBase *itemBase = bin->selectedItemBase();
974 	bool enabled = (itemBase != NULL);
975 	m_editPartNewAction->setEnabled(enabled && itemBase->canEditPart());
976 
977     bool enableAnyway = false;
978 #ifndef QT_NO_DEBUG
979     enableAnyway = true;
980 #endif
981 	m_exportPartAction->setEnabled(enabled && (!itemBase->modelPart()->isCore() || enableAnyway));
982 	m_removePartAction->setEnabled(enabled && bin->allowsChanges());
983 	m_findPartAction->setEnabled(enabled);
984 }
985 
createCombinedMenu()986 void BinManager::createCombinedMenu()
987 {
988 	m_combinedMenu = new QMenu(tr("Bin"), this);
989 
990     m_openAction = new QAction(tr("Import..."), this);
991 	m_openAction->setStatusTip(tr("Load a Fritzing part (.fzpz), or a Fritzing parts bin (.fzb, .fzbz)"));
992 	connect(m_openAction, SIGNAL(triggered()), this, SLOT(mainLoad()));
993 
994 	m_newBinAction = new QAction(tr("New Bin..."), this);
995 	m_newBinAction->setStatusTip(tr("Create a new parts bin"));
996 	connect(m_newBinAction, SIGNAL(triggered()),this, SLOT(newBinIn()));
997 
998 	m_closeBinAction = new QAction(tr("Close Bin"), this);
999 	m_closeBinAction->setStatusTip(tr("Close parts bin"));
1000 	connect(m_closeBinAction, SIGNAL(triggered()),this, SLOT(closeBin()));
1001 
1002 	m_deleteBinAction = new QAction(tr("Delete Bin"), this);
1003 	m_deleteBinAction->setStatusTip(tr("Delete parts bin"));
1004 	connect(m_deleteBinAction, SIGNAL(triggered()),this, SLOT(deleteBin()));
1005 
1006 	m_saveBinAction = new QAction(tr("Save Bin"), this);
1007 	m_saveBinAction->setStatusTip(tr("Save parts bin"));
1008 	connect(m_saveBinAction, SIGNAL(triggered()),this, SLOT(saveBin()));
1009 
1010 	m_saveBinAsAction = new QAction(tr("Save Bin As..."), this);
1011 	m_saveBinAsAction->setStatusTip(tr("Save parts bin as..."));
1012 	connect(m_saveBinAsAction, SIGNAL(triggered()),this, SLOT(saveBinAs()));
1013 
1014 	m_saveBinAsBundledAction = new QAction(tr("Export Bin..."), this);
1015 	m_saveBinAsBundledAction->setStatusTip(tr("Save parts bin in compressed format..."));
1016 	connect(m_saveBinAsBundledAction, SIGNAL(triggered()),this, SLOT(saveBundledBin()));
1017 
1018 	m_renameBinAction = new QAction(tr("Rename Bin..."), this);
1019 	m_renameBinAction->setStatusTip(tr("Rename parts bin..."));
1020 	connect(m_renameBinAction, SIGNAL(triggered()),this, SLOT(renameBin()));
1021 
1022 	m_copyToSketchAction = new QAction(tr("Copy to Sketch"), this);
1023 	m_copyToSketchAction->setStatusTip(tr("Copy all the parts in the bin to a sketch"));
1024 	connect(m_copyToSketchAction, SIGNAL(triggered()),this, SLOT(copyToSketch()));
1025 
1026 	m_copyAllToSketchAction = new QAction(tr("Copy all to Sketch"), this);
1027 	m_copyAllToSketchAction->setStatusTip(tr("Copy all loaded parts to the sketch"));
1028 	connect(m_copyAllToSketchAction, SIGNAL(triggered()),this, SLOT(copyAllToSketch()));
1029 
1030 	m_showListViewAction = new QAction(tr("Show Bin in List View"), this);
1031 	m_showListViewAction->setCheckable(true);
1032 	m_showListViewAction->setStatusTip(tr("Display parts as a list"));
1033 	connect(m_showListViewAction, SIGNAL(triggered()),this, SLOT(toListView()));
1034 
1035 	m_showIconViewAction = new QAction(tr("Show Bin in Icon View"), this);
1036 	m_showIconViewAction->setCheckable(true);
1037 	m_showIconViewAction->setStatusTip(tr("Display parts as icons"));
1038 	connect(m_showIconViewAction, SIGNAL(triggered()),this, SLOT(toIconView()));
1039 
1040     m_combinedMenu->addAction(m_openAction);
1041 	m_combinedMenu->addSeparator();
1042 
1043 	m_combinedMenu->addAction(m_newBinAction);
1044 	m_combinedMenu->addAction(m_closeBinAction);
1045 	m_combinedMenu->addAction(m_deleteBinAction);
1046 	m_combinedMenu->addAction(m_saveBinAction);
1047 	m_combinedMenu->addAction(m_saveBinAsAction);
1048 	m_combinedMenu->addAction(m_saveBinAsBundledAction);
1049 	m_combinedMenu->addAction(m_renameBinAction);
1050 #ifndef QT_NO_DEBUG
1051 	m_combinedMenu->addAction(m_copyToSketchAction);
1052 	m_combinedMenu->addAction(m_copyAllToSketchAction);
1053 #endif
1054 	m_combinedMenu->addSeparator();
1055 	m_combinedMenu->addAction(m_showIconViewAction);
1056 	m_combinedMenu->addAction(m_showListViewAction);
1057 
1058 	m_editPartNewAction = new QAction(tr("Edit Part (new parts editor)..."),this);
1059 	m_exportPartAction = new QAction(tr("Export Part..."),this);
1060 	m_removePartAction = new QAction(tr("Remove Part"),this);
1061 	m_findPartAction = new QAction(tr("Find Part in Sketch"),this);
1062 
1063 	connect(m_editPartNewAction, SIGNAL(triggered()),this, SLOT(editSelectedNew()));
1064 	connect(m_exportPartAction, SIGNAL(triggered()),this, SLOT(exportSelected()));
1065 	connect(m_removePartAction, SIGNAL(triggered()),this, SLOT(removeSelected()));
1066 	connect(m_findPartAction, SIGNAL(triggered()),this, SLOT(findSelected()));
1067 
1068 	connect(m_combinedMenu, SIGNAL(aboutToShow()), this, SLOT(updateBinCombinedMenuCurrent()));
1069 
1070 	m_combinedMenu->addSeparator();
1071 	m_combinedMenu->addAction(m_editPartNewAction);
1072 	m_combinedMenu->addAction(m_exportPartAction);
1073 	m_combinedMenu->addAction(m_removePartAction);
1074 	m_combinedMenu->addAction(m_findPartAction);
1075 
1076 }
1077 
createContextMenus()1078 void BinManager::createContextMenus() {
1079 	m_binContextMenu = new QMenu(this);
1080 	m_binContextMenu->addAction(m_closeBinAction);
1081 	m_binContextMenu->addAction(m_deleteBinAction);
1082 	m_binContextMenu->addAction(m_saveBinAction);
1083 	m_binContextMenu->addAction(m_saveBinAsAction);
1084 	m_binContextMenu->addAction(m_saveBinAsBundledAction);
1085 	m_binContextMenu->addAction(m_renameBinAction);
1086 
1087 	m_partContextMenu = new QMenu(this);
1088 	m_partContextMenu->addAction(m_editPartNewAction);
1089 	m_partContextMenu->addAction(m_exportPartAction);
1090 	m_partContextMenu->addAction(m_removePartAction);
1091 	m_partContextMenu->addAction(m_findPartAction);
1092 }
1093 
closeBin()1094 void BinManager::closeBin() {
1095 	closeBinIn(-1);
1096 }
1097 
deleteBin()1098 void BinManager::deleteBin() {
1099 	PartsBinPaletteWidget * bin = currentBin();
1100 	if (bin == NULL) return;
1101 
1102 	QMessageBox::StandardButton answer = QMessageBox::question(
1103 		this,
1104 		tr("Delete bin"),
1105 		tr("Do you really want to delete bin '%1'?  This action cannot be undone.").arg(bin->title()),
1106 		QMessageBox::Yes | QMessageBox::No,
1107 		QMessageBox::No
1108 	);
1109 	// TODO: make button texts translatable
1110 	if (answer != QMessageBox::Yes) return;
1111 
1112 	QString filename = bin->fileName();
1113 	closeBinIn(-1);
1114 	QFile::remove(filename);
1115 }
1116 
importPartToMineBin(const QString & filename)1117 void BinManager::importPartToMineBin(const QString & filename) {
1118 
1119 	if (!filename.isEmpty() && !filename.isNull()) {
1120         PartsBinPaletteWidget * bin = getOrOpenBin(MyPartsBinLocation, MyPartsBinTemplateLocation);
1121         if (bin == NULL) return;
1122 
1123         setAsCurrentTab(bin);
1124         importPart(filename, bin);
1125     }
1126 }
1127 
importPartToCurrentBin(const QString & filename)1128 void BinManager::importPartToCurrentBin(const QString & filename) {
1129 
1130 	if (!filename.isEmpty() && !filename.isNull()) {
1131         PartsBinPaletteWidget * bin = currentBin();
1132         if (bin == NULL) return;
1133 
1134         importPart(filename, bin);
1135 	}
1136 }
1137 
importPart(const QString & filename,PartsBinPaletteWidget * bin)1138 void BinManager::importPart(const QString & filename, PartsBinPaletteWidget * bin) {
1139 	ModelPart *mp = m_mainWindow->loadBundledPart(filename, !bin->allowsChanges());
1140 	if (bin->allowsChanges()) {
1141 		addPartTo(bin, mp, true);
1142 	}
1143 }
1144 
editSelectedNew()1145 void BinManager::editSelectedNew() {
1146 	editSelectedPartNewFrom(currentBin());
1147 }
1148 
renameBin()1149 void BinManager::renameBin() {
1150 	PartsBinPaletteWidget * bin = currentBin();
1151 	if (bin == NULL) return;
1152 
1153 	if (!currentBin()->allowsChanges()) {
1154 		// TODO: disable menu item instead
1155 		QMessageBox::warning(this, tr("Read-only bin"), tr("This bin cannot be renamed."));
1156 		return;
1157 	}
1158 
1159 	bool ok;
1160 	QString newTitle = QInputDialog::getText(
1161 		this,
1162 		tr("Rename bin"),
1163 		tr("Please choose a name for the bin:"),
1164 		QLineEdit::Normal,
1165 		bin->title(),
1166 		&ok
1167 	);
1168 	if(ok) {
1169 		bin->setTitle(newTitle);
1170 		m_stackTabWidget->stackTabBar()->setTabToolTip(m_stackTabWidget->currentIndex(), newTitle);
1171 		bin->addPartToMeAction()->setText(newTitle);
1172 		updateTitle(bin, newTitle);
1173 	}
1174 }
1175 
saveBin()1176 void BinManager::saveBin() {
1177 	PartsBinPaletteWidget * bin = currentBin();
1178 	if (bin == NULL) return;
1179 
1180 	bool result = bin->save();
1181 	if (result) setDirtyTab(currentBin(),false);
1182 }
1183 
saveBinAs()1184 void BinManager::saveBinAs() {
1185 	PartsBinPaletteWidget * bin = currentBin();
1186 	if (bin == NULL) return;
1187 
1188 	bin->saveAs();
1189 }
1190 
1191 
updateViewChecks(bool iconView)1192 void BinManager::updateViewChecks(bool iconView) {
1193     if (m_showListViewAction == NULL) return;
1194     if (m_showIconViewAction == NULL) return;
1195 
1196 	if (iconView) {
1197 		m_showListViewAction->setChecked(false);
1198 		m_showIconViewAction->setChecked(true);
1199 	}
1200 	else {
1201 		m_showListViewAction->setChecked(true);
1202 		m_showIconViewAction->setChecked(false);
1203 	}
1204 }
1205 
binContextMenu(PartsBinPaletteWidget * bin)1206 QMenu * BinManager::binContextMenu(PartsBinPaletteWidget * bin) {
1207 	updateBinCombinedMenu(bin);
1208 	return m_binContextMenu;
1209 }
1210 
partContextMenu(PartsBinPaletteWidget * bin)1211 QMenu * BinManager::partContextMenu(PartsBinPaletteWidget * bin) {
1212 	updateBinCombinedMenu(bin);
1213 	return m_partContextMenu;
1214 }
1215 
combinedMenu(PartsBinPaletteWidget * bin)1216 QMenu * BinManager::combinedMenu(PartsBinPaletteWidget * bin) {
1217 	updateBinCombinedMenu(bin);
1218 	return m_combinedMenu;
1219 }
1220 
combinedMenu()1221 QMenu * BinManager::combinedMenu() {
1222 	return m_combinedMenu;
1223 }
1224 
removeSelected()1225 bool BinManager::removeSelected() {
1226 	PartsBinPaletteWidget * bin = currentBin();
1227 	if (bin == NULL) return false;
1228 
1229 	ModelPart * mp = bin->selectedModelPart();
1230 	if (mp == NULL) return false;
1231 
1232     if (m_mainWindow->anyUsePart(mp->moduleID())) {
1233         QMessageBox::warning(this, tr("Remove from Bin"), tr("Unable to remove part '%1'--it is in use in a sketch").arg(mp->title()));
1234         return false;
1235     }
1236 
1237 	QMessageBox::StandardButton answer = QMessageBox::question(
1238 		this,
1239 		tr("Remove from bin"),
1240 		tr("Do you really want to remove '%1' from the bin? This operation cannot be undone.").arg(mp->title()),
1241 		QMessageBox::Yes | QMessageBox::No,
1242 		QMessageBox::No
1243 	);
1244 	// TODO: make button texts translatable
1245 	if(answer != QMessageBox::Yes) return false;
1246 
1247 
1248 	m_undoStack->push(new QUndoCommand("Parts bin: part removed"));
1249 	bin->removePart(mp->moduleID(), mp->path());
1250 	bin->setDirty();
1251 
1252 	return true;
1253 }
1254 
findSelected()1255 void BinManager::findSelected() {
1256 	PartsBinPaletteWidget * bin = currentBin();
1257 	if (bin == NULL) return;
1258 
1259 	ModelPart * mp = bin->selectedModelPart();
1260 	if (mp == NULL) return;
1261 
1262     m_mainWindow->selectPartsWithModuleID(mp);
1263 }
1264 
1265 
exportSelected()1266 void BinManager::exportSelected() {
1267 	PartsBinPaletteWidget * bin = currentBin();
1268 	if (bin == NULL) return;
1269 
1270 	ModelPart * mp = bin->selectedModelPart();
1271 	if (mp == NULL) return;
1272 
1273 	emit savePartAsBundled(mp->moduleID());
1274 }
1275 
saveBundledBin()1276 void BinManager::saveBundledBin() {
1277 	PartsBinPaletteWidget * bin = currentBin();
1278 	if (bin == NULL) return;
1279 
1280 	bin->saveBundledBin();
1281 }
1282 
setTabIcon(PartsBinPaletteWidget * w,QIcon * icon)1283 void BinManager::setTabIcon(PartsBinPaletteWidget* w, QIcon * icon)
1284 {
1285 	if (m_stackTabWidget != NULL) {
1286 		int tabIdx = m_stackTabWidget->indexOf(w);
1287 		m_stackTabWidget->setTabIcon(tabIdx, *icon);
1288 	}
1289 }
1290 
copyFilesToContrib(ModelPart * mp,QWidget * originator)1291 void BinManager::copyFilesToContrib(ModelPart * mp, QWidget * originator) {
1292 	PartsBinPaletteWidget * bin = qobject_cast<PartsBinPaletteWidget *>(originator);
1293 	if (bin == NULL) return;
1294 
1295 	if (bin->fileName().compare(m_tempPartsBinLocation) != 0) return;				// only copy from temp bin
1296 
1297 	QString path = mp->path();
1298 	if (path.isEmpty()) return;
1299 
1300 	QFileInfo info(path);
1301 	QFile fzp(path);
1302 
1303 	QString parts = FolderUtils::getUserDataStorePath("parts");
1304     FolderUtils::slamCopy(fzp, parts + "/contrib/" + info.fileName());
1305 	QString prefix = parts + "/svg/contrib/";
1306 
1307 	QDir dir = info.absoluteDir();
1308 	dir.cdUp();
1309 	dir.cd("svg");
1310 	dir.cd("contrib");
1311 
1312 	QList<ViewLayer::ViewID> viewIDs;
1313 	viewIDs << ViewLayer::IconView << ViewLayer::BreadboardView << ViewLayer::SchematicView << ViewLayer::PCBView;
1314 	foreach (ViewLayer::ViewID viewID, viewIDs) {
1315 		QString fn = mp->hasBaseNameFor(viewID);
1316 		if (!fn.isEmpty()) {
1317 			QFile svg(dir.absoluteFilePath(fn));
1318             FolderUtils::slamCopy(svg, prefix + fn);
1319 		}
1320 	}
1321 }
1322 
isTempPartsBin(PartsBinPaletteWidget * bin)1323 bool BinManager::isTempPartsBin(PartsBinPaletteWidget * bin) {
1324     return bin->fileName().compare(m_tempPartsBinLocation) == 0;
1325 }
1326 
setTempPartsBinLocation(const QString & name)1327 void BinManager::setTempPartsBinLocation(const QString & name) {
1328     m_tempPartsBinLocation = name;
1329     //StandardBinIcons.insert(m_tempPartsBinLocation, "Temp.png");
1330 }
1331 
mainLoad()1332 void BinManager::mainLoad() {
1333 	QString path = m_defaultSaveFolder;
1334 
1335 	QString fileName = FolderUtils::getOpenFileName(
1336 			this,
1337 			tr("Select a Fritzing File to Open"),
1338 			path,
1339 			tr("Fritzing Files (*%1 *%2 *%3);;Fritzing Part (*%1);;Fritzing Bin (*%2);;Fritzing Shareable Bin (*%3)")
1340                 .arg(FritzingBundledPartExtension)
1341                 .arg(FritzingBinExtension)
1342                 .arg(FritzingBundledBinExtension)
1343 		);
1344 
1345     if (fileName.isEmpty()) return;
1346 
1347     if (fileName.endsWith(FritzingBundledPartExtension)) {
1348         importPartToCurrentBin(fileName);
1349         return;
1350     }
1351 
1352     if (fileName.endsWith(FritzingBinExtension) || fileName.endsWith(FritzingBundledBinExtension)) {
1353         openBinIn(fileName, false);
1354     }
1355 }
1356 
hideTabBar()1357 void BinManager::hideTabBar()
1358 {
1359     m_stackTabWidget->stackTabBar()->hide();
1360 }
1361 
reloadPart(const QString & moduleID)1362 void BinManager::reloadPart(const QString & moduleID) {
1363 	PartsBinView::removePartReference(moduleID);
1364 	for(int j = 0; j < m_stackTabWidget->count(); j++) {
1365 		PartsBinPaletteWidget *bin = qobject_cast<PartsBinPaletteWidget*>(m_stackTabWidget->widget(j));
1366 		if (bin == NULL) continue;
1367 
1368 		bin->reloadPart(moduleID);
1369 	}
1370 }
1371 
copyToSketch()1372 void BinManager::copyToSketch() {
1373 	PartsBinPaletteWidget * bin = currentBin();
1374 	if (bin == NULL) return;
1375 
1376     QList<ModelPart *> modelParts = bin->getAllParts();
1377     if (modelParts.count() == 0) return;
1378 
1379     if (m_mainWindow) {
1380         m_mainWindow->addToSketch(modelParts);
1381     }
1382 }
1383 
copyAllToSketch()1384 void BinManager::copyAllToSketch() {
1385     QList<ModelPart *> modelParts;
1386 
1387     if (m_mainWindow) {
1388         m_mainWindow->addToSketch(modelParts);
1389     }
1390 }
1391 
1392