2014-01-10 23:27:44 +00:00
/*
* ZeroTier One - Global Peer to Peer Ethernet
2014-02-16 20:40:22 +00:00
* Copyright ( C ) 2011 - 2014 ZeroTier Networks LLC
2014-01-10 23:27:44 +00:00
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation , either version 3 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*
* - -
*
* ZeroTier may be used and distributed under the terms of the GPLv3 , which
* are available at : http : //www.gnu.org/licenses/gpl-3.0.html
*
* If you would like to embed ZeroTier into a commercial application or
* redistribute it in a modified binary form , please contact ZeroTier Networks
* LLC . Start here : http : //www.zerotier.com/
*/
2013-11-18 20:06:05 +00:00
# include <string>
# include <map>
2013-11-20 23:29:02 +00:00
# include <set>
2013-11-18 20:06:05 +00:00
# include <vector>
2013-11-19 20:05:14 +00:00
# include <stdexcept>
2013-11-21 18:45:44 +00:00
# include <utility>
2013-11-18 20:06:05 +00:00
2013-11-15 22:04:32 +00:00
# include <QClipboard>
2013-11-18 20:06:05 +00:00
# include <QMutex>
2013-11-19 20:05:14 +00:00
# include <QCoreApplication>
# include <QDir>
# include <QFile>
# include <QMessageBox>
# include <QDebug>
2013-11-20 17:19:37 +00:00
# include <QProcess>
# include <QStringList>
2013-11-20 23:29:02 +00:00
# include <QVBoxLayout>
2013-12-20 00:23:41 +00:00
# include <QScrollBar>
2013-12-21 00:07:20 +00:00
# include <QEventLoop>
2014-01-27 06:24:29 +00:00
# include <QFont>
2013-11-18 20:06:05 +00:00
2014-01-17 18:36:58 +00:00
# include "main.h"
# include "mainwindow.h"
# include "aboutwindow.h"
# include "networkwidget.h"
# include "ui_mainwindow.h"
2014-02-02 05:55:32 +00:00
# include "ui_quickstartdialog.h"
2014-01-17 18:36:58 +00:00
2014-01-16 01:00:53 +00:00
# ifdef __APPLE__
# include <stdio.h>
# include <string.h>
# include <unistd.h>
# include <sys/types.h>
# include <sys/stat.h>
# include "mac_doprivileged.h"
# endif
2013-11-20 19:10:33 +00:00
// Globally visible
2014-04-03 00:12:04 +00:00
ZeroTier : : Node : : NodeControlClient * zeroTierClient = ( ZeroTier : : Node : : NodeControlClient * ) 0 ;
2013-11-20 19:10:33 +00:00
// Main window instance for app
2014-02-05 20:38:37 +00:00
QMainWindow * mainWindow = ( MainWindow * ) 0 ;
2013-11-20 19:10:33 +00:00
2014-01-17 18:36:58 +00:00
// Handles message from ZeroTier One service
2014-04-03 00:12:04 +00:00
static void handleZTMessage ( void * arg , const char * line )
2013-11-18 20:06:05 +00:00
{
2014-04-03 00:12:04 +00:00
static std : : vector < std : : string > ztReplies ;
2013-11-20 19:10:33 +00:00
static QMutex ztReplies_m ;
2013-11-18 20:06:05 +00:00
ztReplies_m . lock ( ) ;
2014-04-03 00:12:04 +00:00
if ( line ) {
if ( ( line [ 0 ] = = ' . ' ) & & ( line [ 1 ] = = ( char ) 0 ) ) {
2014-01-17 18:36:58 +00:00
// The message is packed into an event and sent to the main window where
// the actual parsing code lives.
2014-04-03 00:12:04 +00:00
MainWindow : : ZTMessageEvent * event = new MainWindow : : ZTMessageEvent ( ztReplies ) ;
ztReplies . clear ( ) ;
2013-11-20 19:10:33 +00:00
QCoreApplication : : postEvent ( mainWindow , event ) ; // must post since this may be another thread
2014-04-03 00:12:04 +00:00
} else if ( line [ 0 ] ) {
ztReplies . push_back ( std : : string ( line ) ) ;
}
2013-11-18 20:06:05 +00:00
}
2014-04-03 00:12:04 +00:00
ztReplies_m . unlock ( ) ;
2013-11-18 20:06:05 +00:00
}
2013-11-13 21:50:49 +00:00
MainWindow : : MainWindow ( QWidget * parent ) :
QMainWindow ( parent ) ,
2014-01-03 23:39:09 +00:00
ui ( new Ui : : MainWindow ) ,
2014-01-11 01:31:10 +00:00
pollServiceTimerId ( - 1 )
2013-11-13 21:50:49 +00:00
{
2014-01-17 18:36:58 +00:00
mainWindow = this ;
2013-11-13 21:50:49 +00:00
ui - > setupUi ( this ) ;
2014-01-11 00:41:44 +00:00
if ( ui - > networkListWidget - > verticalScrollBar ( ) )
ui - > networkListWidget - > verticalScrollBar ( ) - > setSingleStep ( 8 ) ;
2014-01-25 07:15:14 +00:00
# ifdef __APPLE__
2014-01-11 00:41:44 +00:00
QWidgetList widgets = this - > findChildren < QWidget * > ( ) ;
foreach ( QWidget * widget , widgets )
widget - > setAttribute ( Qt : : WA_MacShowFocusRect , false ) ;
2014-01-25 07:15:14 +00:00
# endif
2014-01-27 06:24:29 +00:00
# ifdef __WINDOWS__
2014-03-06 22:06:31 +00:00
// Windows operates at a different DPI, so we have to rescale the default Qt
// font sizes so everything isn't huge. Yeah.
2014-01-27 06:24:29 +00:00
QWidgetList widgets = this - > findChildren < QWidget * > ( ) ;
foreach ( QWidget * widget , widgets ) {
2014-03-06 22:06:31 +00:00
if ( typeid ( * widget ) ! = typeid ( * ui - > menuFile ) ) { // menus don't need the DPI shift apparently
2014-02-28 05:56:57 +00:00
QFont font ( widget - > font ( ) ) ;
font . setPointSizeF ( font . pointSizeF ( ) * 0.75 ) ;
widget - > setFont ( font ) ;
}
2014-01-27 06:24:29 +00:00
}
2014-03-07 23:20:37 +00:00
this - > raise ( ) ;
2014-01-27 06:24:29 +00:00
# endif
2014-01-25 07:15:14 +00:00
ui - > noNetworksLabel - > setVisible ( true ) ;
ui - > noNetworksLabel - > setText ( " Connecting to Service... " ) ;
ui - > bottomContainerWidget - > setVisible ( false ) ;
ui - > networkListWidget - > setVisible ( false ) ;
2014-01-09 07:12:03 +00:00
2014-01-28 07:13:36 +00:00
this - > firstTimerTick = true ;
this - > pollServiceTimerId = this - > startTimer ( 200 ) ;
2014-01-10 06:58:31 +00:00
this - > cyclesSinceResponseFromService = 0 ;
2013-11-13 21:50:49 +00:00
}
MainWindow : : ~ MainWindow ( )
{
delete ui ;
2013-11-20 17:19:37 +00:00
delete zeroTierClient ;
2014-04-03 00:12:04 +00:00
zeroTierClient = ( ZeroTier : : Node : : NodeControlClient * ) 0 ;
2013-11-20 19:10:33 +00:00
mainWindow = ( MainWindow * ) 0 ;
2013-11-13 21:50:49 +00:00
}
2013-11-15 22:04:32 +00:00
2014-01-28 07:13:36 +00:00
void MainWindow : : timerEvent ( QTimerEvent * event ) // event can be null since code also calls this directly
2013-11-19 20:05:14 +00:00
{
2014-01-04 06:14:30 +00:00
if ( this - > isHidden ( ) )
return ;
2014-01-28 07:13:36 +00:00
if ( this - > pollServiceTimerId < 0 )
2014-01-11 01:31:10 +00:00
return ;
2014-01-04 06:14:30 +00:00
2014-04-03 00:12:04 +00:00
// Show quick start dialog on first launch, then reset timer to normal rate
2014-01-28 07:13:36 +00:00
if ( this - > firstTimerTick ) {
this - > firstTimerTick = false ;
this - > killTimer ( this - > pollServiceTimerId ) ;
2014-02-02 05:55:32 +00:00
if ( ! settings - > value ( " shown_quickStart " , false ) . toBool ( ) ) {
on_actionQuick_Start_triggered ( ) ;
settings - > setValue ( " shown_quickStart " , true ) ;
settings - > sync ( ) ;
}
2014-04-03 00:12:04 +00:00
this - > pollServiceTimerId = this - > startTimer ( 2000 ) ;
2014-01-28 07:13:36 +00:00
}
2013-11-19 20:05:14 +00:00
if ( ! zeroTierClient ) {
# ifdef __APPLE__
2014-04-03 00:12:04 +00:00
if ( ( ! QFile : : exists ( ZeroTier : : Node : : NodeControlClient : : authTokenDefaultUserPath ( ) ) ) & & ( QFile : : exists ( " /Library/Application Support/ZeroTier/One/zerotier-one " ) ) ) {
// Authorize user by copying auth token into local home directory
QMessageBox : : information ( this , " Authorization Needed " , " Administrator privileges are required to allow the current user to control ZeroTier One on this computer. (You only have to do this once.) " , QMessageBox : : Ok , QMessageBox : : NoButton ) ;
std : : string homePath ( QDir : : homePath ( ) . toStdString ( ) ) ;
QString zt1Caches ( QDir : : homePath ( ) + " /Library/Caches/ZeroTier/One " ) ;
QDir : : root ( ) . mkpath ( zt1Caches ) ;
std : : string tmpPath ( ( zt1Caches + " /auth.sh " ) . toStdString ( ) ) ;
FILE * scr = fopen ( tmpPath . c_str ( ) , " w " ) ;
if ( ! scr ) {
QMessageBox : : critical ( this , " Cannot Authorize " , " Unable to authorize this user to administrate ZeroTier One. (Cannot write to temporary Library/Caches/ZeroTier/One folder.) " , QMessageBox : : Ok , QMessageBox : : NoButton ) ;
QApplication : : exit ( 1 ) ;
return ;
2013-11-19 20:05:14 +00:00
}
2013-11-20 17:19:37 +00:00
2014-04-03 00:12:04 +00:00
fprintf ( scr , " #!/bin/bash \n " ) ;
fprintf ( scr , " export PATH= \" /bin:/usr/bin:/sbin:/usr/sbin \" \n " ) ;
fprintf ( scr , " if [ -f '/Library/Application Support/ZeroTier/One/authtoken.secret' ]; then \n " ) ;
fprintf ( scr , " mkdir -p '%s/Library/Application Support/ZeroTier/One' \n " , homePath . c_str ( ) ) ;
fprintf ( scr , " chown %d '%s/Library/Application Support/ZeroTier' \n " , ( int ) getuid ( ) , homePath . c_str ( ) ) ;
fprintf ( scr , " chgrp %d '%s/Library/Application Support/ZeroTier' \n " , ( int ) getgid ( ) , homePath . c_str ( ) ) ;
fprintf ( scr , " chmod 0700 '%s/Library/Application Support/ZeroTier' \n " , homePath . c_str ( ) ) ;
fprintf ( scr , " chown %d '%s/Library/Application Support/ZeroTier/One' \n " , ( int ) getuid ( ) , homePath . c_str ( ) ) ;
fprintf ( scr , " chgrp %d '%s/Library/Application Support/ZeroTier/One' \n " , ( int ) getgid ( ) , homePath . c_str ( ) ) ;
fprintf ( scr , " chmod 0700 '%s/Library/Application Support/ZeroTier/One' \n " , homePath . c_str ( ) ) ;
fprintf ( scr , " cp -f '/Library/Application Support/ZeroTier/One/authtoken.secret' '%s/Library/Application Support/ZeroTier/One/authtoken.secret' \n " , homePath . c_str ( ) ) ;
fprintf ( scr , " chown %d '%s/Library/Application Support/ZeroTier/One/authtoken.secret' \n " , ( int ) getuid ( ) , homePath . c_str ( ) ) ;
fprintf ( scr , " chgrp %d '%s/Library/Application Support/ZeroTier/One/authtoken.secret' \n " , ( int ) getgid ( ) , homePath . c_str ( ) ) ;
fprintf ( scr , " chmod 0600 '%s/Library/Application Support/ZeroTier/One/authtoken.secret' \n " , homePath . c_str ( ) ) ;
fprintf ( scr , " fi \n " ) ;
fprintf ( scr , " exit 0 \n " ) ;
fclose ( scr ) ;
chmod ( tmpPath . c_str ( ) , 0755 ) ;
macExecutePrivilegedShellCommand ( ( std : : string ( " ' " ) + tmpPath + " ' >>/dev/null 2>&1 " ) . c_str ( ) ) ;
unlink ( tmpPath . c_str ( ) ) ;
}
# endif // __APPLE__
try {
zeroTierClient = new ZeroTier : : Node : : NodeControlClient ( ( const char * ) 0 , & handleZTMessage , this ) ;
const char * err = zeroTierClient - > error ( ) ;
if ( err ) {
delete zeroTierClient ;
zeroTierClient = ( ZeroTier : : Node : : NodeControlClient * ) 0 ;
2013-11-20 17:19:37 +00:00
}
2014-04-03 00:12:04 +00:00
} catch ( . . . ) {
zeroTierClient = ( ZeroTier : : Node : : NodeControlClient * ) 0 ;
2013-11-19 20:05:14 +00:00
}
}
2013-11-20 19:10:33 +00:00
2014-01-26 18:59:33 +00:00
if ( + + this - > cyclesSinceResponseFromService > = 3 ) {
if ( this - > cyclesSinceResponseFromService = = 3 )
QMessageBox : : warning ( this , " Service Not Running " , " Can't connect to the ZeroTier One service. Is it running? " , QMessageBox : : Ok ) ;
2014-01-25 07:15:14 +00:00
ui - > noNetworksLabel - > setVisible ( true ) ;
ui - > noNetworksLabel - > setText ( " Connecting to Service... " ) ;
ui - > bottomContainerWidget - > setVisible ( false ) ;
ui - > networkListWidget - > setVisible ( false ) ;
}
2013-11-21 20:11:22 +00:00
2014-04-03 00:12:04 +00:00
if ( zeroTierClient ) {
zeroTierClient - > send ( " info " ) ;
zeroTierClient - > send ( " listnetworks " ) ;
zeroTierClient - > send ( " listpeers " ) ;
}
2013-11-20 19:10:33 +00:00
}
void MainWindow : : customEvent ( QEvent * event )
{
ZTMessageEvent * m = ( ZTMessageEvent * ) event ; // only one custom event type so far
if ( m - > ztMessage . size ( ) = = 0 )
return ;
2014-04-03 00:12:04 +00:00
std : : vector < std : : string > hdr ( ZeroTier : : Node : : NodeControlClient : : splitLine ( m - > ztMessage [ 0 ] ) ) ;
2013-11-20 19:10:33 +00:00
if ( hdr . size ( ) < 2 )
return ;
if ( hdr [ 0 ] ! = " 200 " )
return ;
2014-04-03 00:12:04 +00:00
this - > cyclesSinceResponseFromService = 0 ;
2013-11-20 19:10:33 +00:00
if ( hdr [ 1 ] = = " info " ) {
if ( hdr . size ( ) > = 3 )
this - > myAddress = hdr [ 2 ] . c_str ( ) ;
if ( hdr . size ( ) > = 4 )
this - > myStatus = hdr [ 3 ] . c_str ( ) ;
if ( hdr . size ( ) > = 5 )
this - > myVersion = hdr [ 4 ] . c_str ( ) ;
} else if ( hdr [ 1 ] = = " listnetworks " ) {
2013-12-21 00:07:20 +00:00
std : : map < std : : string , std : : vector < std : : string > > newNetworks ;
2013-11-20 21:16:30 +00:00
for ( unsigned long i = 1 ; i < m - > ztMessage . size ( ) ; + + i ) {
2014-04-03 00:12:04 +00:00
std : : vector < std : : string > l ( ZeroTier : : Node : : NodeControlClient : : splitLine ( m - > ztMessage [ i ] ) ) ;
2013-11-21 20:11:22 +00:00
// 200 listnetworks <nwid> <name> <status> <config age> <type> <dev> <ips>
if ( ( l . size ( ) = = 9 ) & & ( l [ 2 ] . length ( ) = = 16 ) )
2013-12-21 00:07:20 +00:00
newNetworks [ l [ 2 ] ] = l ;
2013-11-21 18:45:44 +00:00
}
2013-12-21 00:07:20 +00:00
if ( newNetworks ! = networks ) {
networks = newNetworks ;
for ( bool removed = true ; removed ; ) {
removed = false ;
for ( int r = 0 ; r < ui - > networkListWidget - > count ( ) ; + + r ) {
NetworkWidget * nw = ( NetworkWidget * ) ui - > networkListWidget - > itemWidget ( ui - > networkListWidget - > item ( r ) ) ;
if ( ! networks . count ( nw - > networkId ( ) ) ) {
ui - > networkListWidget - > setVisible ( false ) ; // HACK to prevent an occasional crash here, discovered through hours of shotgun debugging... :P
delete ui - > networkListWidget - > takeItem ( r ) ;
removed = true ;
break ;
}
}
2013-11-21 18:45:44 +00:00
}
2013-12-21 00:07:20 +00:00
ui - > networkListWidget - > setVisible ( true ) ;
std : : set < std : : string > alreadyDisplayed ;
for ( int r = 0 ; r < ui - > networkListWidget - > count ( ) ; + + r ) {
NetworkWidget * nw = ( NetworkWidget * ) ui - > networkListWidget - > itemWidget ( ui - > networkListWidget - > item ( r ) ) ;
if ( networks . count ( nw - > networkId ( ) ) > 0 ) {
alreadyDisplayed . insert ( nw - > networkId ( ) ) ;
std : : vector < std : : string > & l = networks [ nw - > networkId ( ) ] ;
nw - > setNetworkName ( l [ 3 ] ) ;
nw - > setStatus ( l [ 4 ] , l [ 5 ] ) ;
nw - > setNetworkType ( l [ 6 ] ) ;
nw - > setNetworkDeviceName ( l [ 7 ] ) ;
nw - > setIps ( l [ 8 ] ) ;
}
}
for ( std : : map < std : : string , std : : vector < std : : string > > : : iterator nwdata ( networks . begin ( ) ) ; nwdata ! = networks . end ( ) ; + + nwdata ) {
if ( alreadyDisplayed . count ( nwdata - > first ) = = 0 ) {
std : : vector < std : : string > & l = nwdata - > second ;
NetworkWidget * nw = new NetworkWidget ( ( QWidget * ) 0 , nwdata - > first ) ;
nw - > setNetworkName ( l [ 3 ] ) ;
nw - > setStatus ( l [ 4 ] , l [ 5 ] ) ;
nw - > setNetworkType ( l [ 6 ] ) ;
nw - > setNetworkDeviceName ( l [ 7 ] ) ;
nw - > setIps ( l [ 8 ] ) ;
QListWidgetItem * item = new QListWidgetItem ( ) ;
item - > setSizeHint ( nw - > sizeHint ( ) ) ;
ui - > networkListWidget - > addItem ( item ) ;
ui - > networkListWidget - > setItemWidget ( item , nw ) ;
}
2013-11-20 23:29:02 +00:00
}
2013-11-20 21:16:30 +00:00
}
2013-11-20 19:10:33 +00:00
} else if ( hdr [ 1 ] = = " listpeers " ) {
this - > numPeers = 0 ;
2014-04-03 00:12:04 +00:00
for ( unsigned long i = 1 ; i < m - > ztMessage . size ( ) ; + + i )
+ + this - > numPeers ;
} else
return ;
2013-11-20 19:10:33 +00:00
2014-01-13 19:16:38 +00:00
if ( ! ui - > networkListWidget - > count ( ) ) {
ui - > noNetworksLabel - > setText ( " You Have Not Joined Any Networks " ) ;
ui - > noNetworksLabel - > setVisible ( true ) ;
} else ui - > noNetworksLabel - > setVisible ( false ) ;
2014-01-11 05:40:38 +00:00
2014-01-25 07:15:14 +00:00
if ( ! ui - > bottomContainerWidget - > isVisible ( ) )
ui - > bottomContainerWidget - > setVisible ( true ) ;
if ( ! ui - > networkListWidget - > isVisible ( ) )
ui - > networkListWidget - > setVisible ( true ) ;
2013-12-19 22:59:52 +00:00
if ( this - > myAddress . size ( ) )
ui - > addressButton - > setText ( this - > myAddress ) ;
2013-12-21 00:07:20 +00:00
else ui - > addressButton - > setText ( " " ) ;
2013-12-19 22:59:52 +00:00
QString st ( this - > myStatus ) ;
st + = " , v " ;
st + = this - > myVersion ;
st + = " , " ;
st + = QString : : number ( this - > numPeers ) ;
2014-04-03 00:12:04 +00:00
st + = " peers " ;
2013-12-19 22:59:52 +00:00
ui - > statusLabel - > setText ( st ) ;
2013-11-19 20:05:14 +00:00
}
2013-11-15 22:04:32 +00:00
void MainWindow : : on_joinNetworkButton_clicked ( )
{
2013-11-20 21:16:30 +00:00
QString toJoin ( ui - > networkIdLineEdit - > text ( ) ) ;
ui - > networkIdLineEdit - > setText ( QString ( ) ) ;
if ( ! zeroTierClient ) // sanity check
return ;
if ( toJoin . size ( ) ! = 16 ) {
QMessageBox : : information ( this , " Invalid Network ID " , " The network ID you entered was not valid. Enter a 16-digit hexadecimal network ID, like '8056c2e21c000001'. " , QMessageBox : : Ok , QMessageBox : : NoButton ) ;
return ;
}
zeroTierClient - > send ( ( QString ( " join " ) + toJoin ) . toStdString ( ) ) ;
2013-11-15 22:04:32 +00:00
}
void MainWindow : : on_actionAbout_triggered ( )
{
AboutWindow * about = new AboutWindow ( this ) ;
about - > show ( ) ;
}
void MainWindow : : on_networkIdLineEdit_textChanged ( const QString & text )
{
2013-11-18 17:01:33 +00:00
QString newText ;
for ( QString : : const_iterator i ( text . begin ( ) ) ; i ! = text . end ( ) ; + + i ) {
switch ( i - > toLatin1 ( ) ) {
case ' 0 ' : newText . append ( ' 0 ' ) ; break ;
case ' 1 ' : newText . append ( ' 1 ' ) ; break ;
case ' 2 ' : newText . append ( ' 2 ' ) ; break ;
case ' 3 ' : newText . append ( ' 3 ' ) ; break ;
case ' 4 ' : newText . append ( ' 4 ' ) ; break ;
case ' 5 ' : newText . append ( ' 5 ' ) ; break ;
case ' 6 ' : newText . append ( ' 6 ' ) ; break ;
case ' 7 ' : newText . append ( ' 7 ' ) ; break ;
case ' 8 ' : newText . append ( ' 8 ' ) ; break ;
case ' 9 ' : newText . append ( ' 9 ' ) ; break ;
case ' a ' : newText . append ( ' a ' ) ; break ;
case ' b ' : newText . append ( ' b ' ) ; break ;
case ' c ' : newText . append ( ' c ' ) ; break ;
case ' d ' : newText . append ( ' d ' ) ; break ;
case ' e ' : newText . append ( ' e ' ) ; break ;
case ' f ' : newText . append ( ' f ' ) ; break ;
case ' A ' : newText . append ( ' a ' ) ; break ;
case ' B ' : newText . append ( ' b ' ) ; break ;
case ' C ' : newText . append ( ' c ' ) ; break ;
case ' D ' : newText . append ( ' d ' ) ; break ;
case ' E ' : newText . append ( ' e ' ) ; break ;
case ' F ' : newText . append ( ' f ' ) ; break ;
default : break ;
}
}
2013-11-20 23:29:02 +00:00
if ( newText . size ( ) > 16 )
newText . truncate ( 16 ) ;
2013-11-18 17:01:33 +00:00
ui - > networkIdLineEdit - > setText ( newText ) ;
2013-11-15 22:04:32 +00:00
}
2013-12-19 22:59:52 +00:00
void MainWindow : : on_addressButton_clicked ( )
2013-11-15 22:04:32 +00:00
{
2013-11-20 19:10:33 +00:00
QApplication : : clipboard ( ) - > setText ( this - > myAddress ) ;
2013-11-15 22:04:32 +00:00
}
2014-02-02 05:55:32 +00:00
void MainWindow : : on_actionQuick_Start_triggered ( )
{
Ui : : QuickstartDialog qd ;
QDialog * qdd = new QDialog ( this ) ;
qd . setupUi ( qdd ) ;
qdd - > setModal ( false ) ;
qdd - > show ( ) ;
}