124 | (&MainWindow::checkCurrentIndex));
125 | connect(ui->connectionView, &QTableView::doubleClicked,
126 | this, &MainWindow::onEdit);
127 |
128 | /* set custom context menu */
129 | ui->connectionView->setContextMenuPolicy(Qt::CustomContextMenu);
130 | connect(ui->connectionView, &QTableView::customContextMenuRequested,
131 | this, &MainWindow::onCustomContextMenuRequested);
132 |
133 | checkCurrentIndex();
134 |
135 | // Restore mainWindow's geometry and state
136 | restoreGeometry(configHelper->getMainWindowGeometry());
137 | restoreState(configHelper->getMainWindowState());
138 | ui->connectionView->horizontalHeader()->restoreGeometry(configHelper->getTableGeometry());
139 | ui->connectionView->horizontalHeader()->restoreState(configHelper->getTableState());
140 | }
141 |
142 | MainWindow::~MainWindow()
143 | {
144 | configHelper->save(*model);
145 | configHelper->setTableGeometry(ui->connectionView->horizontalHeader()->saveGeometry());
146 | configHelper->setTableState(ui->connectionView->horizontalHeader()->saveState());
147 | configHelper->setMainWindowGeometry(saveGeometry());
148 | configHelper->setMainWindowState(saveState());
149 |
150 | // delete ui after everything in case it's deleted while still needed for
151 | // the functions written above
152 | delete ui;
153 | }
154 |
155 | const QUrl MainWindow::issueUrl =
156 | QUrl("https://github.com/shadowsocks/shadowsocks-qt5/issues");
157 |
158 | void MainWindow::startAutoStartConnections()
159 | {
160 | configHelper->startAllAutoStart(*model);
161 | }
162 |
163 | void MainWindow::onImportGuiJson()
164 | {
165 | QString file = QFileDialog::getOpenFileName(
166 | this,
167 | tr("Import Connections from gui-config.json"),
168 | QString(),
169 | "GUI Configuration (gui-config.json)");
170 | if (!file.isNull()) {
171 | configHelper->importGuiConfigJson(model, file);
172 | }
173 | }
174 |
175 | void MainWindow::onExportGuiJson()
176 | {
177 | QString file = QFileDialog::getSaveFileName(
178 | this,
179 | tr("Export Connections as gui-config.json"),
180 | QString("gui-config.json"),
181 | "GUI Configuration (gui-config.json)");
182 | if (!file.isNull()) {
183 | configHelper->exportGuiConfigJson(*model, file);
184 | }
185 | }
186 |
187 | void MainWindow::onSaveManually()
188 | {
189 | configHelper->save(*model);
190 | }
191 |
192 | void MainWindow::onAddManually()
193 | {
194 | Connection *newCon = new Connection;
195 | newProfile(newCon);
196 | }
197 |
198 | void MainWindow::onAddScreenQRCode()
199 | {
200 | QString uri = QRCodeCapturer::scanEntireScreen();
201 | if (uri.isNull()) {
202 | QMessageBox::critical(
203 | this,
204 | tr("QR Code Not Found"),
205 | tr("Can't find any QR code image that contains "
206 | "valid URI on your screen(s)."));
207 | } else {
208 | Connection *newCon = new Connection(uri, this);
209 | newProfile(newCon);
210 | }
211 | }
212 |
213 | void MainWindow::onAddScreenQRCodeCapturer()
214 | {
215 | QRCodeCapturer *capturer = new QRCodeCapturer(this);
216 | connect(capturer, &QRCodeCapturer::closed,
217 | capturer, &QRCodeCapturer::deleteLater);
218 | connect(capturer, &QRCodeCapturer::qrCodeFound,
219 | this, &MainWindow::onQRCodeCapturerResultFound,
220 | Qt::DirectConnection);
221 | capturer->show();
222 | }
223 |
224 | void MainWindow::onAddQRCodeFile()
225 | {
226 | QString qrFile =
227 | QFileDialog::getOpenFileName(this,
228 | tr("Open QR Code Image File"),
229 | QString(),
230 | "Images (*.png *jpg *jpeg *xpm)");
231 | if (!qrFile.isNull()) {
232 | QImage img(qrFile);
233 | QString uri = URIHelper::decodeImage(img);
234 | if (uri.isNull()) {
235 | QMessageBox::critical(this,
236 | tr("QR Code Not Found"),
237 | tr("Can't find any QR code image that "
238 | "contains valid URI on your screen(s)."));
239 | } else {
240 | Connection *newCon = new Connection(uri, this);
241 | newProfile(newCon);
242 | }
243 | }
244 | }
245 |
246 | void MainWindow::onAddFromURI()
247 | {
248 | URIInputDialog *inputDlg = new URIInputDialog(this);
249 | connect(inputDlg, &URIInputDialog::finished,
250 | inputDlg, &URIInputDialog::deleteLater);
251 | connect(inputDlg, &URIInputDialog::acceptedURI, [&](const QString &uri){
252 | Connection *newCon = new Connection(uri, this);
253 | newProfile(newCon);
254 | });
255 | inputDlg->exec();
256 | }
257 |
258 | void MainWindow::onAddFromConfigJSON()
259 | {
260 | QString file = QFileDialog::getOpenFileName(this, tr("Open config.json"),
261 | QString(), "JSON (*.json)");
262 | if (!file.isNull()) {
263 | Connection *con = configHelper->configJsonToConnection(file);
264 | if (con) {
265 | newProfile(con);
266 | }
267 | }
268 | }
269 |
270 | void MainWindow::onDelete()
271 | {
272 | if (model->removeRow(proxyModel->mapToSource(
273 | ui->connectionView->currentIndex()).row())) {
274 | configHelper->save(*model);
275 | }
276 | checkCurrentIndex();
277 | }
278 |
279 | void MainWindow::onEdit()
280 | {
281 | editRow(proxyModel->mapToSource(ui->connectionView->currentIndex()).row());
282 | }
283 |
284 | void MainWindow::onShare()
285 | {
286 | QByteArray uri = model->getItem(
287 | proxyModel->mapToSource(ui->connectionView->currentIndex()).
288 | row())->getConnection()->getURI();
289 | ShareDialog *shareDlg = new ShareDialog(uri, this);
290 | connect(shareDlg, &ShareDialog::finished,
291 | shareDlg, &ShareDialog::deleteLater);
292 | shareDlg->exec();
293 | }
294 |
295 | void MainWindow::onConnect()
296 | {
297 | int row = proxyModel->mapToSource(ui->connectionView->currentIndex()).row();
298 | Connection *con = model->getItem(row)->getConnection();
299 | if (con->isValid()) {
300 | con->start();
301 | } else {
302 | QMessageBox::critical(this, tr("Invalid"),
303 | tr("The connection's profile is invalid!"));
304 | }
305 | }
306 |
307 | void MainWindow::onForceConnect()
308 | {
309 | int row = proxyModel->mapToSource(ui->connectionView->currentIndex()).row();
310 | Connection *con = model->getItem(row)->getConnection();
311 | if (con->isValid()) {
312 | model->disconnectConnectionsAt(con->getProfile().localAddress,
313 | con->getProfile().localPort);
314 | con->start();
315 | } else {
316 | QMessageBox::critical(this, tr("Invalid"),
317 | tr("The connection's profile is invalid!"));
318 | }
319 | }
320 |
321 | void MainWindow::onDisconnect()
322 | {
323 | int row = proxyModel->mapToSource(ui->connectionView->currentIndex()).row();
324 | model->getItem(row)->getConnection()->stop();
325 | }
326 |
327 | void MainWindow::onConnectionStatusChanged(const int row, const bool running)
328 | {
329 | if (proxyModel->mapToSource(
330 | ui->connectionView->currentIndex()).row() == row) {
331 | ui->actionConnect->setEnabled(!running);
332 | ui->actionDisconnect->setEnabled(running);
333 | }
334 | }
335 |
336 | void MainWindow::onLatencyTest()
337 | {
338 | model->getItem(proxyModel->mapToSource(ui->connectionView->currentIndex()).
339 | row())->testLatency();
340 | }
341 |
342 | void MainWindow::onMoveUp()
343 | {
344 | QModelIndex proxyIndex = ui->connectionView->currentIndex();
345 | int currentRow = proxyModel->mapToSource(proxyIndex).row();
346 | int targetRow = proxyModel->mapToSource(
347 | proxyModel->index(proxyIndex.row() - 1,
348 | proxyIndex.column(),
349 | proxyIndex.parent())
350 | ).row();
351 | model->move(currentRow, targetRow);
352 | checkCurrentIndex();
353 | }
354 |
355 | void MainWindow::onMoveDown()
356 | {
357 | QModelIndex proxyIndex = ui->connectionView->currentIndex();
358 | int currentRow = proxyModel->mapToSource(proxyIndex).row();
359 | int targetRow = proxyModel->mapToSource(
360 | proxyModel->index(proxyIndex.row() + 1,
361 | proxyIndex.column(),
362 | proxyIndex.parent())
363 | ).row();
364 | model->move(currentRow, targetRow);
365 | checkCurrentIndex();
366 | }
367 |
368 | void MainWindow::onGeneralSettings()
369 | {
370 | SettingsDialog *sDlg = new SettingsDialog(configHelper, this);
371 | connect(sDlg, &SettingsDialog::finished,
372 | sDlg, &SettingsDialog::deleteLater);
373 | if (sDlg->exec()) {
374 | configHelper->save(*model);
375 | configHelper->setStartAtLogin();
376 | }
377 | }
378 |
379 | void MainWindow::newProfile(Connection *newCon)
380 | {
381 | EditDialog *editDlg = new EditDialog(newCon, this);
382 | connect(editDlg, &EditDialog::finished, editDlg, &EditDialog::deleteLater);
383 | if (editDlg->exec()) {//accepted
384 | model->appendConnection(newCon);
385 | configHelper->save(*model);
386 | } else {
387 | newCon->deleteLater();
388 | }
389 | }
390 |
391 | void MainWindow::editRow(int row)
392 | {
393 | Connection *con = model->getItem(row)->getConnection();
394 | EditDialog *editDlg = new EditDialog(con, this);
395 | connect(editDlg, &EditDialog::finished, editDlg, &EditDialog::deleteLater);
396 | if (editDlg->exec()) {
397 | configHelper->save(*model);
398 | }
399 | }
400 |
401 | void MainWindow::checkCurrentIndex()
402 | {
403 | checkCurrentIndex(ui->connectionView->currentIndex());
404 | }
405 |
406 | void MainWindow::checkCurrentIndex(const QModelIndex &_index)
407 | {
408 | QModelIndex index = proxyModel->mapToSource(_index);
409 | const bool valid = index.isValid();
410 | ui->actionTestLatency->setEnabled(valid);
411 | ui->actionEdit->setEnabled(valid);
412 | ui->actionDelete->setEnabled(valid);
413 | ui->actionShare->setEnabled(valid);
414 | ui->actionMoveUp->setEnabled(valid ? _index.row() > 0 : false);
415 | ui->actionMoveDown->setEnabled(valid ?
416 | _index.row() < model->rowCount() - 1 :
417 | false);
418 |
419 | if (valid) {
420 | const bool &running =
421 | model->getItem(index.row())->getConnection()->isRunning();
422 | ui->actionConnect->setEnabled(!running);
423 | ui->actionForceConnect->setEnabled(!running);
424 | ui->actionDisconnect->setEnabled(running);
425 | } else {
426 | ui->actionConnect->setEnabled(false);
427 | ui->actionForceConnect->setEnabled(false);
428 | ui->actionDisconnect->setEnabled(false);
429 | }
430 | }
431 |
432 | void MainWindow::onAbout()
433 | {
434 | QString text = QString("Shadowsocks-Qt5
Version %1
"
435 | "Using libQtShadowsocks %2
"
436 | "Copyright © 2014-2018 Symeon Huang "
437 | "("
438 | "@librehat)
"
439 | "License: "
440 | "GNU Lesser General Public License Version 3
"
441 | "Project Hosted at "
442 | ""
443 | "GitHub
")
444 | .arg(QStringLiteral(APP_VERSION))
445 | .arg(QSS::Common::version());
446 | QMessageBox::about(this, tr("About"), text);
447 | }
448 |
449 | void MainWindow::onReportBug()
450 | {
451 | QDesktopServices::openUrl(issueUrl);
452 | }
453 |
454 | void MainWindow::onCustomContextMenuRequested(const QPoint &pos)
455 | {
456 | this->checkCurrentIndex(ui->connectionView->indexAt(pos));
457 | ui->menuConnection->popup(ui->connectionView->viewport()->mapToGlobal(pos));
458 | }
459 |
460 | void MainWindow::onFilterToggled(bool show)
461 | {
462 | if (show) {
463 | ui->filterLineEdit->setFocus();
464 | }
465 | }
466 |
467 | void MainWindow::onFilterTextChanged(const QString &text)
468 | {
469 | proxyModel->setFilterWildcard(text);
470 | }
471 |
472 | void MainWindow::onQRCodeCapturerResultFound(const QString &uri)
473 | {
474 | QRCodeCapturer* capturer = qobject_cast(sender());
475 | // Disconnect immediately to avoid duplicate signals
476 | disconnect(capturer, &QRCodeCapturer::qrCodeFound,
477 | this, &MainWindow::onQRCodeCapturerResultFound);
478 | Connection *newCon = new Connection(uri, this);
479 | newProfile(newCon);
480 | }
481 |
482 | void MainWindow::hideEvent(QHideEvent *e)
483 | {
484 | QMainWindow::hideEvent(e);
485 | notifier->onWindowVisibleChanged(false);
486 | }
487 |
488 | void MainWindow::showEvent(QShowEvent *e)
489 | {
490 | QMainWindow::showEvent(e);
491 | notifier->onWindowVisibleChanged(true);
492 | this->setFocus();
493 | }
494 |
495 | void MainWindow::closeEvent(QCloseEvent *e)
496 | {
497 | if (e->spontaneous()) {
498 | e->ignore();
499 | hide();
500 | } else {
501 | QMainWindow::closeEvent(e);
502 | }
503 | }
504 |
505 | void MainWindow::setupActionIcon()
506 | {
507 | ui->actionConnect->setIcon(QIcon::fromTheme("network-connect",
508 | QIcon::fromTheme("network-vpn")));
509 | ui->actionDisconnect->setIcon(QIcon::fromTheme("network-disconnect",
510 | QIcon::fromTheme("network-offline")));
511 | ui->actionEdit->setIcon(QIcon::fromTheme("document-edit",
512 | QIcon::fromTheme("accessories-text-editor")));
513 | ui->actionShare->setIcon(QIcon::fromTheme("document-share",
514 | QIcon::fromTheme("preferences-system-sharing")));
515 | ui->actionTestLatency->setIcon(QIcon::fromTheme("flag",
516 | QIcon::fromTheme("starred")));
517 | ui->actionImportGUIJson->setIcon(QIcon::fromTheme("document-import",
518 | QIcon::fromTheme("insert-text")));
519 | ui->actionExportGUIJson->setIcon(QIcon::fromTheme("document-export",
520 | QIcon::fromTheme("document-save-as")));
521 | ui->actionManually->setIcon(QIcon::fromTheme("edit-guides",
522 | QIcon::fromTheme("accessories-text-editor")));
523 | ui->actionURI->setIcon(QIcon::fromTheme("text-field",
524 | QIcon::fromTheme("insert-link")));
525 | ui->actionQRCode->setIcon(QIcon::fromTheme("edit-image-face-recognize",
526 | QIcon::fromTheme("insert-image")));
527 | ui->actionScanQRCodeCapturer->setIcon(ui->actionQRCode->icon());
528 | ui->actionGeneralSettings->setIcon(QIcon::fromTheme("configure",
529 | QIcon::fromTheme("preferences-desktop")));
530 | ui->actionReportBug->setIcon(QIcon::fromTheme("tools-report-bug",
531 | QIcon::fromTheme("help-faq")));
532 | }
533 |
534 | bool MainWindow::isInstanceRunning() const
535 | {
536 | return instanceRunning;
537 | }
538 |
539 | void MainWindow::initSingleInstance()
540 | {
541 | const QString serverName = QCoreApplication::applicationName();
542 | QLocalSocket socket;
543 | socket.connectToServer(serverName);
544 | if (socket.waitForConnected(500)) {
545 | instanceRunning = true;
546 | if (configHelper->isOnlyOneInstance()) {
547 | qWarning() << "An instance of ss-qt5 is already running";
548 | }
549 | QByteArray username = qgetenv("USER");
550 | if (username.isEmpty()) {
551 | username = qgetenv("USERNAME");
552 | }
553 | socket.write(username);
554 | socket.waitForBytesWritten();
555 | return;
556 | }
557 |
558 | /* Can't connect to server, indicating it's the first instance of the user */
559 | instanceServer = new QLocalServer(this);
560 | instanceServer->setSocketOptions(QLocalServer::WorldAccessOption);
561 | connect(instanceServer, &QLocalServer::newConnection,
562 | this, &MainWindow::onSingleInstanceConnect);
563 | if (instanceServer->listen(serverName)) {
564 | /* Remove server in case of crashes */
565 | if (instanceServer->serverError() == QAbstractSocket::AddressInUseError &&
566 | QFile::exists(instanceServer->serverName())) {
567 | QFile::remove(instanceServer->serverName());
568 | instanceServer->listen(serverName);
569 | }
570 | }
571 | }
572 |
573 | void MainWindow::onSingleInstanceConnect()
574 | {
575 | QLocalSocket *socket = instanceServer->nextPendingConnection();
576 | if (!socket) {
577 | return;
578 | }
579 |
580 | if (socket->waitForReadyRead(1000)) {
581 | QByteArray username = qgetenv("USER");
582 | if (username.isEmpty()) {
583 | username = qgetenv("USERNAME");
584 | }
585 |
586 | QByteArray recvUsername = socket->readAll();
587 | if (recvUsername == username) {
588 | // Only show the window if it's the same user
589 | show();
590 | } else {
591 | qWarning("Another user is trying to run another instance of ss-qt5");
592 | }
593 | }
594 | socket->deleteLater();
595 | }
596 |
--------------------------------------------------------------------------------
/src/mainwindow.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef MAINWINDOW_H
20 | #define MAINWINDOW_H
21 |
22 | #include
23 | #include
24 | #include
25 | #include "connectiontablemodel.h"
26 | #include "confighelper.h"
27 | #include "statusnotifier.h"
28 |
29 | namespace Ui {
30 | class MainWindow;
31 | }
32 |
33 | class MainWindow : public QMainWindow
34 | {
35 | Q_OBJECT
36 |
37 | public:
38 | explicit MainWindow(ConfigHelper *confHelper, QWidget *parent = 0);
39 | ~MainWindow();
40 |
41 | void startAutoStartConnections();
42 | bool isInstanceRunning() const;
43 |
44 | private:
45 | Ui::MainWindow *ui;
46 |
47 | ConnectionTableModel *model;
48 | QSortFilterProxyModel *proxyModel;
49 | ConfigHelper *configHelper;
50 | StatusNotifier *notifier;
51 |
52 | QLocalServer* instanceServer;
53 | bool instanceRunning;
54 | void initSingleInstance();
55 |
56 | void newProfile(Connection *);
57 | void editRow(int row);
58 | void blockChildrenSignals(bool);
59 | void checkCurrentIndex();
60 | void setupActionIcon();
61 |
62 | static const QUrl issueUrl;
63 |
64 | private slots:
65 | void onImportGuiJson();
66 | void onExportGuiJson();
67 | void onSaveManually();
68 | void onAddManually();
69 | void onAddScreenQRCode();
70 | void onAddScreenQRCodeCapturer();
71 | void onAddQRCodeFile();
72 | void onAddFromURI();
73 | void onAddFromConfigJSON();
74 | void onDelete();
75 | void onEdit();
76 | void onShare();
77 | void onConnect();
78 | void onForceConnect();
79 | void onDisconnect();
80 | void onConnectionStatusChanged(const int row, const bool running);
81 | void onLatencyTest();
82 | void onMoveUp();
83 | void onMoveDown();
84 | void onGeneralSettings();
85 | void checkCurrentIndex(const QModelIndex &index);
86 | void onAbout();
87 | void onReportBug();
88 | void onCustomContextMenuRequested(const QPoint &pos);
89 | void onFilterToggled(bool);
90 | void onFilterTextChanged(const QString &text);
91 | void onQRCodeCapturerResultFound(const QString &uri);
92 | void onSingleInstanceConnect();
93 |
94 | protected slots:
95 | void hideEvent(QHideEvent *e);
96 | void showEvent(QShowEvent *e);
97 | void closeEvent(QCloseEvent *e);
98 | };
99 |
100 | #endif // MAINWINDOW_H
101 |
--------------------------------------------------------------------------------
/src/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 480
10 | 480
11 |
12 |
13 |
14 |
15 | 400
16 | 400
17 |
18 |
19 |
20 | Connection Manager
21 |
22 |
23 |
24 | :/icons/icons/shadowsocks-qt5.png:/icons/icons/shadowsocks-qt5.png
25 |
26 |
27 | Qt::ToolButtonFollowStyle
28 |
29 |
30 |
31 | -
32 |
33 |
34 | Input to filter
35 |
36 |
37 | true
38 |
39 |
40 |
41 | -
42 |
43 |
44 | QAbstractItemView::NoEditTriggers
45 |
46 |
47 | false
48 |
49 |
50 | QAbstractItemView::SingleSelection
51 |
52 |
53 | QAbstractItemView::SelectRows
54 |
55 |
56 | false
57 |
58 |
59 | true
60 |
61 |
62 | false
63 |
64 |
65 | false
66 |
67 |
68 | false
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | Show Toolbar
77 |
78 |
79 | false
80 |
81 |
82 | Qt::AllToolBarAreas
83 |
84 |
85 | Qt::ToolButtonFollowStyle
86 |
87 |
88 | false
89 |
90 |
91 | TopToolBarArea
92 |
93 |
94 | false
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
182 |
183 |
184 | &Manually
185 |
186 |
187 | Add connection manually
188 |
189 |
190 |
191 |
192 | &Scan QR Code on Screen
193 |
194 |
195 |
196 |
197 |
198 | ..
199 |
200 |
201 | &From QR Code Image File
202 |
203 |
204 | From QR code image file
205 |
206 |
207 |
208 |
209 | &URI
210 |
211 |
212 | Add connection from URI
213 |
214 |
215 |
216 |
217 |
218 | ..
219 |
220 |
221 | &Delete
222 |
223 |
224 |
225 |
226 | &Edit
227 |
228 |
229 |
230 |
231 | &Connect
232 |
233 |
234 |
235 |
236 | D&isconnect
237 |
238 |
239 |
240 |
241 |
242 | ..
243 |
244 |
245 | &Quit
246 |
247 |
248 | Ctrl+Q
249 |
250 |
251 |
252 |
253 |
254 | ..
255 |
256 |
257 | &About
258 |
259 |
260 |
261 |
262 | About &Qt
263 |
264 |
265 |
266 |
267 | &General Settings
268 |
269 |
270 |
271 |
272 | &Share
273 |
274 |
275 |
276 |
277 | &Report Bug
278 |
279 |
280 |
281 |
282 | &Test Latency
283 |
284 |
285 | Test the latency of selected connection
286 |
287 |
288 |
289 |
290 | Test All C&onnections Latency
291 |
292 |
293 |
294 |
295 | &Import Connections from gui-config.json
296 |
297 |
298 | Import connections from old version configuration file
299 |
300 |
301 |
302 |
303 |
304 | ..
305 |
306 |
307 | From &config.json
308 |
309 |
310 |
311 |
312 |
313 | ..
314 |
315 |
316 | &Save Manually
317 |
318 |
319 | Ctrl+Shift+S
320 |
321 |
322 |
323 |
324 |
325 | ..
326 |
327 |
328 | &Move Up
329 |
330 |
331 |
332 |
333 |
334 | ..
335 |
336 |
337 | Mo&ve Down
338 |
339 |
340 |
341 |
342 | true
343 |
344 |
345 | true
346 |
347 |
348 | &Show Filter Bar
349 |
350 |
351 | Ctrl+F
352 |
353 |
354 |
355 |
356 | &Export as gui-config.json
357 |
358 |
359 |
360 |
361 | Scan &QR Code using Capturer
362 |
363 |
364 | Scan QR Code using Capturer
365 |
366 |
367 |
368 |
369 | &Force Connect
370 |
371 |
372 | Connect to this connection and disconnect any connections currently using the same local port
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 | actionShowFilterBar
383 | toggled(bool)
384 | filterLineEdit
385 | setVisible(bool)
386 |
387 |
388 | -1
389 | -1
390 |
391 |
392 | 239
393 | 88
394 |
395 |
396 |
397 |
398 |
399 |
--------------------------------------------------------------------------------
/src/portvalidator.cpp:
--------------------------------------------------------------------------------
1 | #include "portvalidator.h"
2 | #include "ssvalidator.h"
3 |
4 | PortValidator::PortValidator(QObject *parent)
5 | : QValidator(parent)
6 | {}
7 |
8 | QValidator::State PortValidator::validate(QString &input, int &) const
9 | {
10 | if (SSValidator::validatePort(input)) {
11 | return Acceptable;
12 | }
13 | else
14 | return Invalid;
15 | }
16 |
--------------------------------------------------------------------------------
/src/portvalidator.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef PORTVALIDATOR_H
20 | #define PORTVALIDATOR_H
21 |
22 | #include
23 |
24 | class PortValidator : public QValidator
25 | {
26 | public:
27 | PortValidator(QObject *parent = 0);
28 | State validate(QString &input, int &) const;
29 | };
30 |
31 | #endif // PORTVALIDATOR_H
32 |
--------------------------------------------------------------------------------
/src/qrcodecapturer.cpp:
--------------------------------------------------------------------------------
1 | #include "qrcodecapturer.h"
2 | #include "urihelper.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | QRCodeCapturer::QRCodeCapturer(QWidget *parent) :
10 | QMainWindow(parent)
11 | {
12 | #ifdef Q_OS_WIN
13 | /*
14 | * On Windows, it requires Qt::FramelessWindowHint to be set to make
15 | * translucent background work, but we need a window with borders.
16 | * Therefore, we set the entire window semi-transparent so that
17 | * users are still able to see the region below while moving the
18 | * capturer above the QR code image.
19 | */
20 | this->setWindowOpacity(0.75);
21 | #else
22 | this->setAttribute(Qt::WA_TranslucentBackground, true);
23 | #endif
24 | this->setWindowTitle(tr("QR Capturer"));
25 | this->setMinimumSize(400, 400);
26 | }
27 |
28 | QRCodeCapturer::~QRCodeCapturer()
29 | {}
30 |
31 | QString QRCodeCapturer::scanEntireScreen()
32 | {
33 | QString uri;
34 | QList screens = qApp->screens();
35 | for (QList::iterator sc = screens.begin();
36 | sc != screens.end();
37 | ++sc) {
38 | QImage raw_sc = (*sc)->grabWindow(qApp->desktop()->winId()).toImage();
39 | QString result = URIHelper::decodeImage(raw_sc);
40 | if (!result.isNull()) {
41 | uri = result;
42 | }
43 | }
44 | return uri;
45 | }
46 |
47 | void QRCodeCapturer::moveEvent(QMoveEvent *e)
48 | {
49 | QMainWindow::moveEvent(e);
50 | decodeCurrentRegion();
51 | }
52 |
53 | void QRCodeCapturer::resizeEvent(QResizeEvent *e)
54 | {
55 | QMainWindow::resizeEvent(e);
56 | decodeCurrentRegion();
57 | }
58 |
59 | void QRCodeCapturer::closeEvent(QCloseEvent *e)
60 | {
61 | QMainWindow::closeEvent(e);
62 | emit closed();
63 | }
64 |
65 | void QRCodeCapturer::decodeCurrentRegion()
66 | {
67 | QScreen *sc = qApp->screens().at(qApp->desktop()->screenNumber(this));
68 | QRect geometry = this->geometry();
69 | QImage raw_sc = sc->grabWindow(qApp->desktop()->winId(),
70 | geometry.x(),
71 | geometry.y(),
72 | geometry.width(),
73 | geometry.height()).toImage();
74 | QString result = URIHelper::decodeImage(raw_sc);
75 | if (!result.isNull()) {
76 | this->close();
77 | // moveEvent and resizeEvent both happen quite frequent
78 | // it's very likely this signal would be emitted multiple times
79 | // the solution is to use Qt::DirectConnection signal-slot connection
80 | // and disconnect such a connection in the slot function
81 | emit qrCodeFound(result);
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/qrcodecapturer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef QRCODECAPTURER_H
20 | #define QRCODECAPTURER_H
21 |
22 | #include
23 |
24 | class QRCodeCapturer : public QMainWindow
25 | {
26 | Q_OBJECT
27 |
28 | public:
29 | explicit QRCodeCapturer(QWidget *parent = 0);
30 | ~QRCodeCapturer();
31 |
32 | static QString scanEntireScreen();
33 |
34 | signals:
35 | void qrCodeFound(const QString &result);
36 | void closed();
37 |
38 | protected slots:
39 | void moveEvent(QMoveEvent *e);
40 | void resizeEvent(QResizeEvent *e);
41 | void closeEvent(QCloseEvent *e);
42 |
43 | private:
44 | void decodeCurrentRegion();
45 | };
46 |
47 | #endif // QRCODECAPTURER_H
48 |
--------------------------------------------------------------------------------
/src/qrwidget.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include "qrwidget.h"
6 |
7 | QRWidget::QRWidget(QWidget *parent) :
8 | QWidget(parent)
9 | {}
10 |
11 | void QRWidget::setQRData(const QByteArray &data)
12 | {
13 | qrImage = QImage(512, 512, QImage::Format_Mono);
14 | QPainter painter(&qrImage);
15 | QRcode *qrcode = QRcode_encodeString(data.constData(), 1, QR_ECLEVEL_L, QR_MODE_8, 1);
16 | if (qrcode) {
17 | QColor fg(Qt::black);
18 | QColor bg(Qt::white);
19 | painter.setBrush(bg);
20 | painter.setPen(Qt::NoPen);
21 | painter.drawRect(0, 0, 512, 512);
22 | painter.setBrush(fg);
23 | const int s = qrcode->width > 0 ? qrcode->width : 1;
24 | const qreal scale = 512.0 / s;
25 | for(int y = 0; y < s; y++){
26 | for(int x = 0; x < s; x++){
27 | if(qrcode->data[y * s + x] & 0x01){
28 | const qreal rx1 = x * scale, ry1 = y * scale;
29 | QRectF r(rx1, ry1, scale, scale);
30 | painter.drawRects(&r,1);
31 | }
32 | }
33 | }
34 | QRcode_free(qrcode);
35 | }
36 | else {
37 | qWarning() << tr("Generating QR code failed.");
38 | }
39 | }
40 |
41 | void QRWidget::paintEvent(QPaintEvent *e)
42 | {
43 | QWidget::paintEvent(e);
44 |
45 | QStyleOption opt;
46 | opt.init(this);
47 | QPainter painter(this);
48 | style()->drawPrimitive(QStyle::PE_Widget, &opt, &painter, this);
49 |
50 | QSizeF nSize = qrImage.size().scaled(this->size(), Qt::KeepAspectRatio);
51 | painter.translate((this->width() - nSize.width()) / 2, (this->height() - nSize.height()) / 2);
52 | painter.scale(nSize.width() / qrImage.width(), nSize.height() / qrImage.height());
53 | painter.drawImage(0, 0, qrImage);
54 | }
55 |
56 | const QImage& QRWidget::getQRImage() const
57 | {
58 | return qrImage;
59 | }
60 |
--------------------------------------------------------------------------------
/src/qrwidget.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef QRWIDGET_H
20 | #define QRWIDGET_H
21 |
22 | #include
23 | #include
24 |
25 | class QRWidget : public QWidget
26 | {
27 | Q_OBJECT
28 | public:
29 | explicit QRWidget(QWidget *parent = 0);
30 | void setQRData(const QByteArray &data);
31 | const QImage& getQRImage() const;
32 |
33 | private:
34 | QImage qrImage;
35 |
36 | protected:
37 | void paintEvent(QPaintEvent *);
38 | };
39 |
40 | #endif // QRWIDGET_H
41 |
--------------------------------------------------------------------------------
/src/settingsdialog.cpp:
--------------------------------------------------------------------------------
1 | #include "settingsdialog.h"
2 | #include "ui_settingsdialog.h"
3 | #include
4 |
5 | SettingsDialog::SettingsDialog(ConfigHelper *ch, QWidget *parent) :
6 | QDialog(parent),
7 | ui(new Ui::SettingsDialog),
8 | helper(ch)
9 | {
10 | ui->setupUi(this);
11 |
12 | ui->toolbarStyleComboBox->setCurrentIndex(helper->getToolbarStyle());
13 | ui->hideCheckBox->setChecked(helper->isHideWindowOnStartup());
14 | ui->startAtLoginCheckbox->setChecked(helper->isStartAtLogin());
15 | ui->oneInstanceCheckBox->setChecked(helper->isOnlyOneInstance());
16 | ui->nativeMenuBarCheckBox->setChecked(helper->isNativeMenuBar());
17 |
18 | connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &SettingsDialog::onAccepted);
19 | connect(ui->toolbarStyleComboBox, &QComboBox::currentTextChanged, this, &SettingsDialog::onChanged);
20 | connect(ui->hideCheckBox, &QCheckBox::stateChanged, this, &SettingsDialog::onChanged);
21 | connect(ui->startAtLoginCheckbox, &QCheckBox::stateChanged, this, &SettingsDialog::onChanged);
22 | connect(ui->oneInstanceCheckBox, &QCheckBox::stateChanged, this, &SettingsDialog::onChanged);
23 | connect(ui->nativeMenuBarCheckBox, &QCheckBox::stateChanged, this, &SettingsDialog::onChanged);
24 |
25 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
26 |
27 | this->adjustSize();
28 | }
29 |
30 | SettingsDialog::~SettingsDialog()
31 | {
32 | delete ui;
33 | }
34 |
35 | void SettingsDialog::onAccepted()
36 | {
37 | helper->setGeneralSettings(ui->toolbarStyleComboBox->currentIndex(),
38 | ui->hideCheckBox->isChecked(),
39 | ui->startAtLoginCheckbox->isChecked(),
40 | ui->oneInstanceCheckBox->isChecked(),
41 | ui->nativeMenuBarCheckBox->isChecked());
42 | this->accept();
43 | }
44 |
45 | void SettingsDialog::onChanged()
46 | {
47 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
48 | }
49 |
--------------------------------------------------------------------------------
/src/settingsdialog.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef SETTINGSDIALOG_H
20 | #define SETTINGSDIALOG_H
21 |
22 | #include
23 | #include "confighelper.h"
24 |
25 | namespace Ui {
26 | class SettingsDialog;
27 | }
28 |
29 | class SettingsDialog : public QDialog
30 | {
31 | Q_OBJECT
32 |
33 | public:
34 | explicit SettingsDialog(ConfigHelper *ch, QWidget *parent = 0);
35 | ~SettingsDialog();
36 |
37 | private:
38 | Ui::SettingsDialog *ui;
39 | ConfigHelper *helper;
40 |
41 | private slots:
42 | void onAccepted();
43 | void onChanged();
44 | };
45 |
46 | #endif // SETTINGSDIALOG_H
47 |
--------------------------------------------------------------------------------
/src/settingsdialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | SettingsDialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 340
10 | 217
11 |
12 |
13 |
14 | General Settings
15 |
16 |
17 | -
18 |
19 |
20 | Qt::Horizontal
21 |
22 |
23 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok
24 |
25 |
26 |
27 | -
28 |
29 |
30 | Toolbar Style
31 |
32 |
33 |
34 | -
35 |
36 |
37 |
38 | 0
39 | 0
40 |
41 |
42 |
-
43 |
44 | Icons Only
45 |
46 |
47 | -
48 |
49 | Text Only
50 |
51 |
52 | -
53 |
54 | Text Alongside Icons
55 |
56 |
57 | -
58 |
59 | Text Under Icons
60 |
61 |
62 | -
63 |
64 | System Style
65 |
66 |
67 |
68 |
69 | -
70 |
71 |
72 | Allow only one instance running
73 |
74 |
75 |
76 | -
77 |
78 |
79 | Hide window on startup
80 |
81 |
82 |
83 | -
84 |
85 |
86 | Need to restart the application for this change to take effect
87 |
88 |
89 | Use native menu bar
90 |
91 |
92 |
93 | -
94 |
95 |
96 | Start at login
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | buttonBox
106 | rejected()
107 | SettingsDialog
108 | reject()
109 |
110 |
111 | 316
112 | 260
113 |
114 |
115 | 286
116 | 274
117 |
118 |
119 |
120 |
121 |
122 |
--------------------------------------------------------------------------------
/src/shadowsocks-qt5.desktop:
--------------------------------------------------------------------------------
1 | [Desktop Entry]
2 | Name=Shadowsocks-Qt5
3 | GenericName=Shadowsocks-Qt5
4 | Comment=Shadowsocks GUI client
5 | Exec=ss-qt5
6 | Icon=shadowsocks-qt5
7 | Terminal=false
8 | Type=Application
9 | Categories=Network;
10 | StartupNotify=true
11 |
--------------------------------------------------------------------------------
/src/sharedialog.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "qrwidget.h"
3 | #include "sharedialog.h"
4 | #include "ui_sharedialog.h"
5 |
6 | ShareDialog::ShareDialog(const QByteArray &ssUrl, QWidget *parent) :
7 | QDialog(parent),
8 | ui(new Ui::ShareDialog)
9 | {
10 | ui->setupUi(this);
11 | ui->qrWidget->setQRData(ssUrl);
12 | ui->ssUrlEdit->setText(QString(ssUrl));
13 | ui->ssUrlEdit->setCursorPosition(0);
14 |
15 | connect(ui->saveButton, &QPushButton::clicked, this, &ShareDialog::onSaveButtonClicked);
16 |
17 | this->adjustSize();
18 | }
19 |
20 | ShareDialog::~ShareDialog()
21 | {
22 | delete ui;
23 | }
24 |
25 | void ShareDialog::onSaveButtonClicked()
26 | {
27 | QString filename = QFileDialog::getSaveFileName(this, tr("Save QR Code"), QString(), "PNG (*.png)");
28 | if (!filename.isEmpty()) {
29 | ui->qrWidget->getQRImage().save(filename);
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/sharedialog.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef SHAREDIALOG_H
20 | #define SHAREDIALOG_H
21 |
22 | #include
23 |
24 | namespace Ui {
25 | class ShareDialog;
26 | }
27 |
28 | class ShareDialog : public QDialog
29 | {
30 | Q_OBJECT
31 |
32 | public:
33 | explicit ShareDialog(const QByteArray &ssUrl, QWidget *parent = 0);
34 | ~ShareDialog();
35 |
36 | private:
37 | Ui::ShareDialog *ui;
38 |
39 | private slots:
40 | void onSaveButtonClicked();
41 | };
42 |
43 | #endif // SHAREDIALOG_H
44 |
--------------------------------------------------------------------------------
/src/sharedialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | ShareDialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 300
10 | 360
11 |
12 |
13 |
14 |
15 | 300
16 | 360
17 |
18 |
19 |
20 | Share Profile
21 |
22 |
23 | true
24 |
25 |
26 | -
27 |
28 |
29 |
30 | 0
31 | 10
32 |
33 |
34 |
35 |
36 | 256
37 | 256
38 |
39 |
40 |
41 |
42 | -
43 |
44 |
45 | background-color: "transparent"
46 |
47 |
48 | false
49 |
50 |
51 | true
52 |
53 |
54 |
55 | -
56 |
57 |
58 | Save QR code as an Image file
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | QRWidget
67 | QWidget
68 |
69 | 1
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/src/sqprofile.cpp:
--------------------------------------------------------------------------------
1 | #include "sqprofile.h"
2 |
3 | SQProfile::SQProfile()
4 | {
5 | autoStart = false;
6 | debug = false;
7 | serverPort = 8388;
8 | localPort = 1080;
9 | name = QObject::tr("Unnamed Profile");
10 | localAddress = QString("127.0.0.1");
11 | method = QString("RC4-MD5");
12 | timeout = 600;
13 | latency = LATENCY_UNKNOWN;
14 | currentUsage = 0;
15 | totalUsage = 0;
16 | QDate currentDate = QDate::currentDate();
17 | nextResetDate = QDate(currentDate.year(), currentDate.month() + 1, 1);
18 | httpMode = false;
19 | }
20 |
21 | SQProfile::SQProfile(const QSS::Profile &profile) : SQProfile()
22 | {
23 | name = QString::fromStdString(profile.name());
24 | localAddress = QString::fromStdString(profile.localAddress());
25 | localPort = profile.localPort();
26 | serverPort = profile.serverPort();
27 | serverAddress = QString::fromStdString(profile.serverAddress());
28 | method = QString::fromStdString(profile.method()).toUpper();
29 | password = QString::fromStdString(profile.password());
30 | timeout = profile.timeout();
31 | httpMode = profile.httpProxy();
32 | debug = profile.debug();
33 | }
34 |
35 | SQProfile::SQProfile(const QString &uri)
36 | {
37 | *this = SQProfile(QSS::Profile::fromUri(uri.toStdString()));
38 | }
39 |
40 | QSS::Profile SQProfile::toProfile() const
41 | {
42 | QSS::Profile qssprofile;
43 | qssprofile.setName(name.toStdString());
44 | qssprofile.setServerAddress(serverAddress.toStdString());
45 | qssprofile.setServerPort(serverPort);
46 | qssprofile.setLocalAddress(localAddress.toStdString());
47 | qssprofile.setLocalPort(localPort);
48 | qssprofile.setMethod(method.toLower().toStdString());
49 | qssprofile.setPassword(password.toStdString());
50 | qssprofile.setTimeout(timeout);
51 | qssprofile.setHttpProxy(httpMode);
52 | if (debug) {
53 | qssprofile.enableDebug();
54 | } else {
55 | qssprofile.disableDebug();
56 | }
57 | return qssprofile;
58 | }
59 |
60 | QDataStream& operator << (QDataStream &out, const SQProfile &p)
61 | {
62 | out << p.autoStart << p.debug << p.serverPort << p.localPort << p.name << p.serverAddress << p.localAddress << p.method << p.password << p.timeout << p.latency << p.currentUsage << p.totalUsage << p.lastTime << p.nextResetDate << p.httpMode;
63 | return out;
64 | }
65 |
66 | QDataStream& operator >> (QDataStream &in, SQProfile &p)
67 | {
68 | in >> p.autoStart >> p.debug >> p.serverPort >> p.localPort >> p.name >> p.serverAddress >> p.localAddress >> p.method >> p.password >> p.timeout >> p.latency >> p.currentUsage >> p.totalUsage >> p.lastTime >> p.nextResetDate >> p.httpMode;
69 | return in;
70 | }
71 |
--------------------------------------------------------------------------------
/src/sqprofile.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef SQPROFILE_H
20 | #define SQPROFILE_H
21 |
22 | #include
23 | #include
24 | #include
25 | #include
26 |
27 | struct SQProfile
28 | {
29 | SQProfile();
30 | SQProfile(const QSS::Profile& profile); // Copy values from QSS Profile
31 | SQProfile(const QString& uri); // Construct it using ss protocol
32 |
33 | QSS::Profile toProfile() const; // Convert it into a QSS Profile
34 |
35 | bool autoStart;
36 | bool debug;
37 | quint16 serverPort;
38 | quint16 localPort;
39 | QString name;
40 | QString serverAddress;
41 | QString localAddress;
42 | QString method;
43 | QString password;
44 | int timeout;
45 | int latency;
46 | quint64 currentUsage;
47 | quint64 totalUsage;
48 | QDateTime lastTime;//last time this connection is used
49 | QDate nextResetDate;//next scheduled date to reset data usage
50 | bool httpMode;
51 |
52 | static const int LATENCY_TIMEOUT = -1;
53 | static const int LATENCY_ERROR = -2;
54 | static const int LATENCY_UNKNOWN = -3;
55 | };
56 | Q_DECLARE_METATYPE(SQProfile)
57 |
58 | QDataStream& operator << (QDataStream &out, const SQProfile &p);
59 | QDataStream& operator >> (QDataStream &in, SQProfile &p);
60 |
61 | #endif // SQPROFILE_H
62 |
--------------------------------------------------------------------------------
/src/ss-qt5.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shadowsocks/shadowsocks-qt5/2692abea80435c16cb24f14cc3dd336b398a3c19/src/ss-qt5.ico
--------------------------------------------------------------------------------
/src/ss-qt5.rc:
--------------------------------------------------------------------------------
1 | IDI_ICON1 ICON DISCARDABLE "ss-qt5.ico"
2 |
--------------------------------------------------------------------------------
/src/ssvalidator.cpp:
--------------------------------------------------------------------------------
1 | #include "ssvalidator.h"
2 | #include
3 |
4 | QStringList SSValidator::supportedMethodList()
5 | {
6 | std::vector methodBA = QSS::Cipher::supportedMethods();
7 | std::sort(methodBA.begin(), methodBA.end());
8 | QStringList methodList;
9 | for (const std::string& method : methodBA) {
10 | methodList.push_back(QString::fromStdString(method).toUpper());
11 | }
12 | return methodList;
13 | }
14 |
15 | bool SSValidator::validate(const QString &input)
16 | {
17 | bool valid = true;
18 | try {
19 | QSS::Profile::fromUri(input.toStdString());
20 | } catch(const std::exception&) {
21 | valid = false;
22 | }
23 | return valid;
24 | }
25 |
26 | bool SSValidator::validatePort(const QString &port)
27 | {
28 | bool ok;
29 | port.toUShort(&ok);
30 | return ok;
31 | }
32 |
33 | bool SSValidator::validateMethod(const QString &method)
34 | {
35 | static const QStringList validMethodList = supportedMethodList();
36 | return validMethodList.contains(method, Qt::CaseInsensitive);
37 | }
38 |
--------------------------------------------------------------------------------
/src/ssvalidator.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2014-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef SSVALIDATOR_H
20 | #define SSVALIDATOR_H
21 | #include
22 | #include
23 |
24 | class SSValidator
25 | {
26 | public:
27 | static bool validate(const QString &input);
28 | static bool validatePort(const QString &port);
29 | static bool validateMethod(const QString &method);
30 |
31 | /*
32 | * Return supported encryption method list at run-time
33 | * To avoid repetitive query, please store return result as static.
34 | */
35 | static QStringList supportedMethodList();
36 | };
37 |
38 | #endif // SSVALIDATOR_H
39 |
--------------------------------------------------------------------------------
/src/statusnotifier.cpp:
--------------------------------------------------------------------------------
1 | #include "statusnotifier.h"
2 | #include "mainwindow.h"
3 | #include
4 | #ifdef Q_OS_LINUX
5 | #include
6 | #include
7 | #include
8 | #endif
9 |
10 | StatusNotifier::StatusNotifier(MainWindow *w, bool startHiden, QObject *parent) :
11 | QObject(parent),
12 | window(w)
13 | {
14 | systray.setIcon(QIcon(":/icons/icons/shadowsocks-qt5.png"));
15 | systray.setToolTip(QString("Shadowsocks-Qt5"));
16 | connect(&systray, &QSystemTrayIcon::activated, [this](QSystemTrayIcon::ActivationReason r) {
17 | if (r != QSystemTrayIcon::Context) {
18 | this->activate();
19 | }
20 | });
21 | minimiseRestoreAction = new QAction(startHiden ? tr("Restore") : tr("Minimise"), this);
22 | connect(minimiseRestoreAction, &QAction::triggered, this, &StatusNotifier::activate);
23 | systrayMenu.addAction(minimiseRestoreAction);
24 | systrayMenu.addAction(QIcon::fromTheme("application-exit", QIcon::fromTheme("exit")), tr("Quit"), qApp, SLOT(quit()));
25 | systray.setContextMenu(&systrayMenu);
26 | systray.show();
27 | }
28 |
29 | void StatusNotifier::activate()
30 | {
31 | if (!window->isVisible() || window->isMinimized()) {
32 | window->showNormal();
33 | window->activateWindow();
34 | window->raise();
35 | } else {
36 | window->hide();
37 | }
38 | }
39 |
40 | void StatusNotifier::showNotification(const QString &msg)
41 | {
42 | #ifdef Q_OS_LINUX
43 | //Using DBus to send message.
44 | QDBusMessage method = QDBusMessage::createMethodCall("org.freedesktop.Notifications","/org/freedesktop/Notifications", "org.freedesktop.Notifications", "Notify");
45 | QVariantList args;
46 | args << QCoreApplication::applicationName() << quint32(0) << "shadowsocks-qt5" << "Shadowsocks-Qt5" << msg << QStringList () << QVariantMap() << qint32(-1);
47 | method.setArguments(args);
48 | QDBusConnection::sessionBus().asyncCall(method);
49 | #else
50 | systray.showMessage("Shadowsocks-Qt5", msg);
51 | #endif
52 | }
53 |
54 | void StatusNotifier::onWindowVisibleChanged(bool visible)
55 | {
56 | minimiseRestoreAction->setText(visible ? tr("Minimise") : tr("Restore"));
57 | }
58 |
--------------------------------------------------------------------------------
/src/statusnotifier.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015-2017 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef STATUSNOTIFIER_H
20 | #define STATUSNOTIFIER_H
21 |
22 | #include
23 | #include
24 | #include
25 |
26 | class MainWindow;
27 |
28 | class StatusNotifier : public QObject
29 | {
30 | Q_OBJECT
31 | public:
32 | StatusNotifier(MainWindow *w, bool startHiden, QObject *parent = 0);
33 |
34 | public slots:
35 | void activate();
36 | void showNotification(const QString &);
37 | void onWindowVisibleChanged(bool visible);
38 |
39 | private:
40 | QMenu systrayMenu;
41 | QAction *minimiseRestoreAction;
42 | QSystemTrayIcon systray;
43 | MainWindow *window;
44 | };
45 |
46 | #endif // STATUSNOTIFIER_H
47 |
--------------------------------------------------------------------------------
/src/translations.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | i18n/ss-qt5_zh_CN.qm
4 | i18n/ss-qt5_zh_TW.qm
5 |
6 |
7 |
--------------------------------------------------------------------------------
/src/urihelper.cpp:
--------------------------------------------------------------------------------
1 | #include "urihelper.h"
2 | #include
3 |
4 | QImage URIHelper::convertToGrey(const QImage &input)
5 | {
6 | if (input.isNull()) {
7 | return QImage();
8 | }
9 | QImage ret(input.width(), input.height(), QImage::Format_Indexed8);
10 | QVector gtable(256);
11 | for (int i = 0; i < 256; ++i) {
12 | gtable[i] = qRgb(i, i, i);
13 | }
14 | ret.setColorTable(gtable);
15 | for (int i = 0; i < input.width(); ++i) {
16 | for (int j = 0; j < input.height(); ++j) {
17 | QRgb val = input.pixel(i, j);
18 | ret.setPixel(i, j, qGray(val));
19 | }
20 | }
21 | return ret;
22 | }
23 |
24 | QString URIHelper::decodeImage(const QImage &img)
25 | {
26 | QString uri;
27 | QImage gimg = convertToGrey(img);
28 |
29 | //use zbar to decode the QR code
30 | zbar::ImageScanner scanner;
31 | zbar::Image image(gimg.bytesPerLine(), gimg.height(), "Y800", gimg.bits(), gimg.byteCount());
32 | scanner.scan(image);
33 | zbar::SymbolSet res_set = scanner.get_results();
34 | for (zbar::SymbolIterator it = res_set.symbol_begin(); it != res_set.symbol_end(); ++it) {
35 | if (it->get_type() == zbar::ZBAR_QRCODE) {
36 | /*
37 | * uri will be overwritten if the result is valid
38 | * this means always the last uri gets used
39 | * therefore, please only leave one QR code for the sake of accuracy
40 | */
41 | QString result = QString::fromStdString(it->get_data());
42 | if (result.left(5).compare("ss://", Qt::CaseInsensitive) == 0) {
43 | uri = result;
44 | }
45 | }
46 | }
47 |
48 | return uri;
49 | }
50 |
--------------------------------------------------------------------------------
/src/urihelper.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef URIHELPER_H
20 | #define URIHELPER_H
21 |
22 | #include
23 | #include
24 |
25 | class URIHelper
26 | {
27 | public:
28 | virtual ~URIHelper() = 0;
29 |
30 | static QImage convertToGrey(const QImage &input);
31 | static QString decodeImage(const QImage &img);
32 | };
33 |
34 | #endif // URIHELPER_H
35 |
--------------------------------------------------------------------------------
/src/uriinputdialog.cpp:
--------------------------------------------------------------------------------
1 | #include "uriinputdialog.h"
2 | #include "ui_uriinputdialog.h"
3 | #include "ssvalidator.h"
4 | #include
5 |
6 | URIInputDialog::URIInputDialog(QWidget *parent) :
7 | QDialog(parent),
8 | ui(new Ui::URIInputDialog)
9 | {
10 | ui->setupUi(this);
11 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
12 | connect(ui->uriEdit, &QLineEdit::textChanged, this, &URIInputDialog::onURIChanged);
13 | connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &URIInputDialog::onAccepted);
14 |
15 | this->adjustSize();
16 | }
17 |
18 | URIInputDialog::~URIInputDialog()
19 | {
20 | delete ui;
21 | }
22 |
23 | void URIInputDialog::onURIChanged(const QString &str)
24 | {
25 | if (!SSValidator::validate(str)) {
26 | ui->uriEdit->setStyleSheet("background: pink");
27 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
28 | }
29 | else {
30 | ui->uriEdit->setStyleSheet("background: #81F279");
31 | ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
32 | }
33 | }
34 |
35 | void URIInputDialog::onAccepted()
36 | {
37 | emit acceptedURI(ui->uriEdit->text());
38 | this->accept();
39 | }
40 |
--------------------------------------------------------------------------------
/src/uriinputdialog.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2015-2016 Symeon Huang
3 | *
4 | * shadowsocks-qt5 is free software; you can redistribute it and/or modify
5 | * it under the terms of the GNU Lesser General Public License as published
6 | * by the Free Software Foundation; either version 3 of the License, or
7 | * (at your option) any later version.
8 | *
9 | * shadowsocks-qt5 is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU Lesser General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU Lesser General Public License
15 | * along with libQtShadowsocks; see the file LICENSE. If not, see
16 | * .
17 | */
18 |
19 | #ifndef URIINPUTDIALOG_H
20 | #define URIINPUTDIALOG_H
21 |
22 | #include
23 |
24 | namespace Ui {
25 | class URIInputDialog;
26 | }
27 |
28 | class URIInputDialog : public QDialog
29 | {
30 | Q_OBJECT
31 |
32 | signals:
33 | void acceptedURI(const QString &uri);
34 |
35 | public:
36 | explicit URIInputDialog(QWidget *parent = 0);
37 | ~URIInputDialog();
38 |
39 | private:
40 | Ui::URIInputDialog *ui;
41 |
42 | private slots:
43 | void onAccepted();
44 | void onURIChanged(const QString &);
45 | };
46 |
47 | #endif // URIINPUTDIALOG_H
48 |
--------------------------------------------------------------------------------
/src/uriinputdialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | URIInputDialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 400
10 | 159
11 |
12 |
13 |
14 | URI Input Dialog
15 |
16 |
17 | -
18 |
19 |
20 | Please input ss:// URI
21 |
22 |
23 |
24 | -
25 |
26 |
27 |
28 | 300
29 | 0
30 |
31 |
32 |
33 |
34 | -
35 |
36 |
37 | Qt::Vertical
38 |
39 |
40 |
41 | 20
42 | 0
43 |
44 |
45 |
46 |
47 | -
48 |
49 |
50 | Qt::Horizontal
51 |
52 |
53 | QDialogButtonBox::Cancel|QDialogButtonBox::Ok
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | buttonBox
63 | rejected()
64 | URIInputDialog
65 | reject()
66 |
67 |
68 | 316
69 | 260
70 |
71 |
72 | 286
73 | 274
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------