1 /* Copyright 2009-2014 e-soul.org. All rights reserved.
2  *
3  * Redistribution and use in source and binary forms, with or without modification, are permitted provided
4  * that the following conditions are met:
5  *
6  *   1. Redistributions of source code must retain the above copyright notice, this list of conditions and
7  *      the following disclaimer.
8  *   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
9  *      and the following disclaimer in the documentation and/or other materials provided with the
10  *      distribution.
11  *
12  * THIS SOFTWARE IS PROVIDED BY E-SOUL.ORG ``AS IS'' AND ANY EXPRESS OR IMPLIED
13  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
14  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL E-SOUL.ORG
15  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
16  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
17  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
18  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
19  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
20  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21  *
22  * The views and conclusions contained in the software and documentation are those of the authors and
23  * should not be interpreted as representing official policies, either expressed or implied, of e-soul.org.
24  */
25 
26 #include <sstream>
27 
28 #include <QSystemTrayIcon>
29 #include <QTimer>
30 #include <QStringList>
31 #include <QStringListModel>
32 #include <QListView>
33 #include <QPushButton>
34 #include <QVBoxLayout>
35 #include <QAction>
36 #include <QMenu>
37 #include <QMenuBar>
38 #include <QCloseEvent>
39 #include <QApplication>
40 #include <QMenu>
41 
42 #include "Trac.hh"
43 #include "PortChecker.hh"
44 #include "PreferencesDialog.hh"
45 
Trac()46 Trac::Trac()
47 {
48 	// initialization
49 	oldList = NULL;
50 	model = new QStringListModel;
51 	prefs = new Preferences;
52 	prefs->load();
53 
54 	// UI initialization
55 	checkNowButton = new QPushButton(tr("Check Now"));
56 	QObject::connect(checkNowButton, SIGNAL(clicked()), this, SLOT(onCheckNow()));
57 
58 	QListView* listView = new QListView;
59 	listView->setModel(model);
60 
61 	QLabel* listLabel = new QLabel(tr("There are updates for the following ports:"));
62 	totalLabel = new QLabel(tr("Total: "));
63 
64 	QVBoxLayout *layout = new QVBoxLayout();
65 	layout->addWidget(checkNowButton);
66 	layout->addWidget(listLabel);
67 	layout->addWidget(listView);
68 	layout->addWidget(totalLabel);
69 
70 	QWidget* widget = new QWidget;
71 	setCentralWidget(widget);
72 	widget->setLayout(layout);
73 
74 	QMenu* programmeMenu = createProgrammeMenu();
75 	initMenuBar(programmeMenu);
76 	initTrayIcon(programmeMenu);
77 	initTimer();
78 }
79 
~Trac()80 Trac::~Trac()
81 {
82 }
83 
onCheckNow()84 void Trac::onCheckNow()
85 {
86 	check();
87 }
88 
check()89 void Trac::check()
90 {
91 	timer->stop();
92 	checkNowButton->setEnabled(false);
93 	checkNowButton->setText(tr("Checking, please wait..."));
94 	//FIXME is a new port checker really needed?
95 	pc = new PortChecker(prefs);
96 	QObject::connect(pc, SIGNAL(finished()), this, SLOT(getList()));
97 	pc->start(QThread::NormalPriority);
98 }
99 
exitProgramme()100 void Trac::exitProgramme()
101 {
102 	prefs->persist();
103 	QApplication::quit();
104 }
105 
closeEvent(QCloseEvent * event)106 void Trac::closeEvent(QCloseEvent *event)
107 {
108 	hide();
109 	event->ignore();
110 }
111 
getList()112 void Trac::getList()
113 {
114 	QStringList* list = pc->getList();
115 	delete pc;
116 	model->setStringList(*list);
117 	bool newList = false;
118 	if(oldList != NULL)
119 	{
120 		newList = !equal(list, oldList);
121 		delete oldList;
122 	}
123 	else
124 	{
125 		newList = true;
126 	}
127 	oldList = list;
128 	if(newList && !list->isEmpty())
129 	{
130 		trayIcon->showMessage(tr("Port updates"), tr("A new list of port updates is available."));
131 		//FIXME remove hardcoded path
132 		trayIcon->setIcon(QIcon(tr("/usr/local/share/portrac/updates-available.png")));
133 		trayIcon->setToolTip(tr("Portrac: updates available"));
134 	}
135 	else if(list->isEmpty())
136 	{
137 		//FIXME remove hardcoded path
138 		trayIcon->setIcon(QIcon(tr("/usr/local/share/portrac/up-to-date.png")));
139 		trayIcon->setToolTip(tr("Portrac: up to date"));
140 	}
141 	timer->start(determineCheckInterval());
142 	checkNowButton->setEnabled(true);
143 	checkNowButton->setText(tr("Check Now"));
144 
145 	updateTotalLabel(list);
146 }
147 
preferences()148 void Trac::preferences()
149 {
150 	PreferencesDialog* pd = new PreferencesDialog(this, prefs);
151 	pd->show();
152 	/*
153 	this dialog sets Qt::WA_DeleteOnClose
154 	therefore no delete call needed
155 	*/
156 }
157 
equal(QStringList * list1,QStringList * list2)158 bool Trac::equal(QStringList* list1, QStringList* list2)
159 {
160 	if(list1->size() == list2->size())
161 	{
162 		for(int i = 0; i < list1->size(); i++)
163 		{
164 			if(list1->at(i).compare(list2->at(i)) != 0)
165 			{
166 				return false;
167 			}
168 		}
169 		return true;
170 	}
171 	return false;
172 }
173 
determineCheckInterval()174 int Trac::determineCheckInterval()
175 {
176 	int multiplier = atoi(prefs->getPreference(CHECK_INTERVAL_PREF).c_str());
177 	return DEFAULT_TIMER_INTERVAL * multiplier;
178 }
179 
updateTotalLabel(QStringList * list)180 void Trac::updateTotalLabel(QStringList* list)
181 {
182 	ostringstream oss("Total: ", ios_base::app);
183 	oss << list->size();
184 	QString tl(oss.str().c_str());
185 	totalLabel->setText(tl);
186 }
187 
createProgrammeMenu()188 QMenu* Trac::createProgrammeMenu()
189 {
190 	QAction* preferencesAct = new QAction(tr("&Preferences"), this);
191 	preferencesAct->setStatusTip(tr("Choose your preferences."));
192 	QObject::connect(preferencesAct, SIGNAL(triggered()), this, SLOT(preferences()));
193 
194 	QAction* exitAct = new QAction(tr("&Exit"), this);
195 	exitAct->setStatusTip(tr("Exit this programme."));
196 	QObject::connect(exitAct, SIGNAL(triggered()), this, SLOT(exitProgramme()));
197 
198 	QMenu* programmeMenu = new QMenu(tr("&Programme"), menuBar());
199 	programmeMenu->addAction(preferencesAct);
200 	programmeMenu->addAction(exitAct);
201 
202 	return programmeMenu;
203 }
204 
initMenuBar(QMenu * programmeMenu)205 void Trac::initMenuBar(QMenu* programmeMenu)
206 {
207 	menuBar()->addMenu(programmeMenu);
208 }
209 
trayActivated(QSystemTrayIcon::ActivationReason reason)210 void Trac::trayActivated(QSystemTrayIcon::ActivationReason reason)
211 {
212 	if(reason == QSystemTrayIcon::Context)
213 	{
214 		trayIcon->contextMenu()->exec();
215 	}
216 	else
217 	{
218 		if(!isVisible()) {
219 			show();
220 			setWindowState(Qt::WindowActive);
221 		} else {
222 			hide();
223 		}
224 	}
225 }
226 
initTrayIcon(QMenu * programmeMenu)227 void Trac::initTrayIcon(QMenu* programmeMenu)
228 {
229 	trayIcon = new QSystemTrayIcon(this);
230 	//FIXME remove hardcoded path
231 	trayIcon->setIcon(QIcon(tr("/usr/local/share/portrac/portrac.png")));
232 	trayIcon->setToolTip(tr("Portrac"));
233 	trayIcon->setContextMenu(programmeMenu);
234 	trayIcon->show();
235 	QObject::connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(trayActivated(QSystemTrayIcon::ActivationReason)));
236 	QObject::connect(trayIcon, SIGNAL(messageClicked()), this, SLOT(show()));
237 }
238 
initTimer()239 void Trac::initTimer()
240 {
241 	timer = new QTimer(this);
242 	QObject::connect(timer, SIGNAL(timeout()), this, SLOT(check()));
243 	timer->start(determineCheckInterval());
244 }
245