Merge branch 'master' into chatlog_v3_1
16
INSTALL.md
|
@ -7,14 +7,14 @@
|
|||
<a name="dependencies" />
|
||||
##Dependencies
|
||||
|
||||
| Name | Version | Modules |
|
||||
|--------------|-------------|------------------------------------- |
|
||||
| Qt | >= 5.2.0 | core, gui, network, widget, xml, sql |
|
||||
| GCC/MinGW | >= 4.8 | C++11 enabled |
|
||||
| Tox Core | most recent | core, av |
|
||||
| OpenCV | >= 2.4.9 | core, highgui, imgproc |
|
||||
| OpenAL Soft | >= 1.16.0 | |
|
||||
| filter_audio | most recent | |
|
||||
| Name | Version | Modules |
|
||||
|--------------|-------------|------------------------------------------ |
|
||||
| Qt | >= 5.2.0 | core, gui, network, sql, svg, widget, xml |
|
||||
| GCC/MinGW | >= 4.8 | C++11 enabled |
|
||||
| Tox Core | most recent | core, av |
|
||||
| OpenCV | >= 2.4.9 | core, highgui, imgproc |
|
||||
| OpenAL Soft | >= 1.16.0 | |
|
||||
| filter_audio | most recent | |
|
||||
|
||||
|
||||
<a name="linux" />
|
||||
|
|
|
@ -29,7 +29,7 @@ This client runs on Windows, Linux and Mac natively.<br/>
|
|||
|
||||
<h3>Screenshots</h3>
|
||||
<h5>Note: The screenshots may not always be up to date, but they should give a good idea of the general look and features</h5>
|
||||
<img src="https://wiki.tox.im/images/5/5d/ToxGUI_tux3.png"/>
|
||||
<img src="https://i.imgur.com/5wSLGSX.png"/>
|
||||
<img src="https://i.imgur.com/0XWcqBc.jpg"/>
|
||||
|
||||
##Documentation:
|
||||
|
@ -38,6 +38,7 @@ This client runs on Windows, Linux and Mac natively.<br/>
|
|||
|
||||
##Developer overview:
|
||||
|
||||
[Contributing](https://github.com/tux3/qTox/wiki#contributing)<br/>
|
||||
[GitStats](https://tux3-dev.tox.im/)<br/>
|
||||
[Mac & Linux jenkins](https://jenkins.libtoxcore.so/user/tux3/my-views/view/qTox/)<br/>
|
||||
[Windows jenkins](https://tux3-dev.tox.im/jenkins)<br/>
|
||||
|
|
56
android/AndroidManifest.xml
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?xml version="1.0"?>
|
||||
<manifest package="im.tox.qtox" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto">
|
||||
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="qtox">
|
||||
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:label="qTox" android:screenOrientation="unspecified" android:launchMode="singleTop">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<meta-data android:name="android.app.lib_name" android:value="qtox"/>
|
||||
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
|
||||
<meta-data android:name="android.app.repository" android:value="default"/>
|
||||
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
|
||||
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
|
||||
<!-- Deploy Qt libs as part of package -->
|
||||
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
|
||||
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
|
||||
<!-- Run with local libs -->
|
||||
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
|
||||
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
|
||||
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
|
||||
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
|
||||
<!-- Messages maps -->
|
||||
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
|
||||
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
|
||||
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
|
||||
<!-- Messages maps -->
|
||||
|
||||
<!-- Splash screen -->
|
||||
<!--
|
||||
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
|
||||
-->
|
||||
<!-- Splash screen -->
|
||||
|
||||
<!-- Background running -->
|
||||
<!-- Warning: changing this value to true may cause unexpected crashes if the
|
||||
application still try to draw after
|
||||
"applicationStateChanged(Qt::ApplicationSuspended)"
|
||||
signal is sent! -->
|
||||
<meta-data android:name="android.app.background_running" android:value="false"/>
|
||||
<!-- Background running -->
|
||||
</activity>
|
||||
</application>
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="20"/>
|
||||
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default permissions. -->
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
|
||||
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
|
||||
Remove the comment if you do not require these default features. -->
|
||||
<!-- %%INSERT_FEATURES -->
|
||||
|
||||
</manifest>
|
Before Width: | Height: | Size: 367 B After Width: | Height: | Size: 228 B |
Before Width: | Height: | Size: 411 B After Width: | Height: | Size: 375 B |
Before Width: | Height: | Size: 878 B After Width: | Height: | Size: 390 B |
Before Width: | Height: | Size: 308 B After Width: | Height: | Size: 237 B |
Before Width: | Height: | Size: 947 B After Width: | Height: | Size: 363 B |
Before Width: | Height: | Size: 791 B After Width: | Height: | Size: 410 B |
Before Width: | Height: | Size: 360 B After Width: | Height: | Size: 231 B |
Before Width: | Height: | Size: 376 B After Width: | Height: | Size: 351 B |
Before Width: | Height: | Size: 825 B After Width: | Height: | Size: 405 B |
Before Width: | Height: | Size: 277 B After Width: | Height: | Size: 201 B |
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 278 B |
Before Width: | Height: | Size: 790 B After Width: | Height: | Size: 351 B |
11
img/taskbar/dark/taskbar_away.svg
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16.1 16" enable-background="new 0 0 16.1 16" xml:space="preserve">
|
||||
<path fill="#252325" d="M8,0C6.1,0,4.2,1.3,4.2,3.9v1.9h-1c-1.1,0-1.8,0.8-1.8,1.9v6.5c0,1.1,0.7,1.9,1.9,1.9l9.6,0
|
||||
c1.1,0,1.9-0.8,1.9-1.9V7.6c0-1.1-0.8-1.9-1.9-1.9h-0.9l0-1.9C11.9,1.3,10,0,8,0z M8,1.6c1.3,0,2.2,1,2.2,2.2C10.3,5,9.8,6.2,8,6.9
|
||||
c0.3-0.5,0.4-0.7,0.5-1.2C6.7,5.9,5.8,4.9,5.8,3.8S6.7,1.6,8,1.6z"/>
|
||||
<path fill="#CDBE41" d="M8,7.6c-1.8,0-3.3,1.5-3.3,3.3c0,1.8,1.5,3.3,3.3,3.3s3.3-1.5,3.3-3.3C11.3,9.1,9.8,7.6,8,7.6z M5.6,11
|
||||
c0-1.3,1.1-2.4,2.4-2.4s2.4,1.1,2.4,2.4H5.6z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 929 B |
Before Width: | Height: | Size: 420 B |
72
img/taskbar/dark/taskbar_busy.svg
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 16.1 16"
|
||||
id="Layer_1"
|
||||
xml:space="preserve">
|
||||
<metadata
|
||||
id="metadata19">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs17" />
|
||||
<path
|
||||
d="M 8,0 C 6.1,0 4.2,1.3 4.2,3.9 v 1.9 h -1 C 2.1,5.8 1.4,6.6 1.4,7.7 v 6.5 c 0,1.1 0.7,1.9 1.9,1.9 l 9.6,0 c 1.1,0 1.9,-0.8 1.9,-1.9 V 7.6 C 14.8,6.5 14,5.7 12.9,5.7 H 12 L 12,3.8 C 11.9,1.3 10,0 8,0 z m 0,1.6 c 1.3,0 2.2,1 2.2,2.2 C 10.3,5 9.8,6.2 8,6.9 8.3,6.4 8.4,6.2 8.5,5.7 6.7,5.9 5.8,4.9 5.8,3.8 5.8,2.7 6.7,1.6 8,1.6 z"
|
||||
id="path3"
|
||||
style="fill:#252325" />
|
||||
<path
|
||||
d="m 7.9927197,7.71875 c -1.8159871,0 -3.28125,1.4652629 -3.28125,3.28125 0,1.815987 1.4652629,3.28125 3.28125,3.28125 1.8159871,0 3.3125003,-1.465263 3.3125003,-3.28125 0,-1.8159871 -1.4965132,-3.28125 -3.3125003,-3.28125 z m 0,0.90625 c 1.3198669,0 2.4062503,1.0551331 2.4062503,2.375 0,1.319867 -1.0863834,2.375 -2.4062503,2.375 -1.3198669,0 -2.375,-1.055133 -2.375,-2.375 0,-1.3198669 1.0551331,-2.375 2.375,-2.375 z"
|
||||
id="path3000"
|
||||
style="fill:#c94f50;fill-opacity:1" />
|
||||
<path
|
||||
d="m -6.5762711,11.932203 a 5.3220339,6.237288 0 1 1 -10.6440679,0 5.3220339,6.237288 0 1 1 10.6440679,0 z"
|
||||
id="path2996"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<rect
|
||||
width="1.1830783"
|
||||
height="5.3462563"
|
||||
rx="0"
|
||||
ry="0.89710045"
|
||||
x="12.866886"
|
||||
y="-0.54217148"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
|
||||
id="rect3937"
|
||||
style="fill:#c94f50;fill-opacity:1;fill-rule:nonzero" />
|
||||
<g
|
||||
transform="translate(-12.523729,-0.08389831)"
|
||||
id="g3893">
|
||||
<g
|
||||
id="g3877">
|
||||
<g
|
||||
id="g3879">
|
||||
<path
|
||||
d="m 5.6,8.7 c -1.3,1.3 -1.3,3.4 0,4.7 1.3,1.3 3.4,1.3 4.7,0 1.3,-1.3 1.3,-3.4 0,-4.7 C 9,7.4 6.9,7.4 5.6,8.7 z m 4.1,4 c -0.9,0.9 -2.4,0.9 -3.4,0 -0.5,-0.5 -0.7,-1.1 -0.7,-1.8 0,0 0,0 0,0 0,0 0,-0.1 0,-0.1 0,0 0,0 0,0 0,0 0,-0.1 0,-0.1 0,0 0,0 0,0 0,-0.1 0,-0.2 0.1,-0.4 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,-0.1 0,0 0,0 0,-0.1 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0.1,-0.1 0.1,-0.1 0,0 0,0 0,0 l 0,0 0,0 c 0,0 0.1,-0.1 0.1,-0.1 0,0 0,0 0,0 0,0 0,0 0.1,-0.1 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0.1,0 0.1,-0.1 0,0 0,0 0,0 0,0 0.1,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0.1,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0.1,0 0.1,0 0.2,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0.1,0 0.2,0.1 0.2,0.1 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 l 0,0 c 0.2,0.1 0.3,0.2 0.4,0.3 1.3,1.1 1.3,2.6 0.4,3.5 z"
|
||||
id="path3881"
|
||||
style="fill:#ffffff" />
|
||||
<g
|
||||
id="g3883">
|
||||
<polyline
|
||||
id="polyline3885"
|
||||
points="7.5,8.5 7.5,13.6 8.5,13.6 8.5,8.5 "
|
||||
style="fill:#ffffff" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 716 B |
10
img/taskbar/dark/taskbar_event.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16.1 16" enable-background="new 0 0 16.1 16" xml:space="preserve">
|
||||
<path fill="#252325" d="M8,0C6.1,0,4.2,1.3,4.2,3.9v1.9h-1c-1.1,0-1.8,0.8-1.8,1.9v6.5c0,1.1,0.7,1.9,1.9,1.9l9.6,0
|
||||
c1.1,0,1.9-0.8,1.9-1.9V7.6c0-1.1-0.8-1.9-1.9-1.9h-0.9l0-1.9C11.9,1.3,10,0,8,0z M8,1.6c1.3,0,2.2,1,2.2,2.2C10.3,5,9.8,6.2,8,6.9
|
||||
c0.3-0.5,0.4-0.7,0.5-1.2C6.7,5.9,5.8,4.9,5.8,3.8S6.7,1.6,8,1.6z"/>
|
||||
<path fill="#ff9400" d="M11.4,11c0,1.9-1.5,3.4-3.4,3.4c-1.9,0-3.4-1.5-3.4-3.4c0-1.9,1.5-3.4,3.4-3.4C9.9,7.6,11.4,9.1,11.4,11"/>
|
||||
</svg>
|
After Width: | Height: | Size: 885 B |
Before Width: | Height: | Size: 410 B |
Before Width: | Height: | Size: 719 B |
Before Width: | Height: | Size: 478 B |
71
img/taskbar/dark/taskbar_invisible.svg
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 16.1 16"
|
||||
id="Layer_1"
|
||||
xml:space="preserve">
|
||||
<metadata
|
||||
id="metadata19">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs17" />
|
||||
<path
|
||||
d="M 8,0 C 6.1,0 4.2,1.3 4.2,3.9 v 1.9 h -1 C 2.1,5.8 1.4,6.6 1.4,7.7 v 6.5 c 0,1.1 0.7,1.9 1.9,1.9 l 9.6,0 c 1.1,0 1.9,-0.8 1.9,-1.9 V 7.6 C 14.8,6.5 14,5.7 12.9,5.7 H 12 L 12,3.8 C 11.9,1.3 10,0 8,0 z m 0,1.6 c 1.3,0 2.2,1 2.2,2.2 C 10.3,5 9.8,6.2 8,6.9 8.3,6.4 8.4,6.2 8.5,5.7 6.7,5.9 5.8,4.9 5.8,3.8 5.8,2.7 6.7,1.6 8,1.6 z"
|
||||
id="path3"
|
||||
style="fill:#252325" />
|
||||
<path
|
||||
d="m 7.9927197,7.71875 c -1.8159871,0 -3.28125,1.4652629 -3.28125,3.28125 0,1.815987 1.4652629,3.28125 3.28125,3.28125 1.8159871,0 3.3125003,-1.465263 3.3125003,-3.28125 0,-1.8159871 -1.4965132,-3.28125 -3.3125003,-3.28125 z m 0,0.90625 c 1.3198669,0 2.4062503,1.0551331 2.4062503,2.375 0,1.319867 -1.0863834,2.375 -2.4062503,2.375 -1.3198669,0 -2.375,-1.055133 -2.375,-2.375 0,-1.3198669 1.0551331,-2.375 2.375,-2.375 z"
|
||||
id="path3000"
|
||||
style="fill:#dedede;fill-opacity:1" />
|
||||
<path
|
||||
d="m -6.5762711,11.932203 a 5.3220339,6.237288 0 1 1 -10.6440679,0 5.3220339,6.237288 0 1 1 10.6440679,0 z"
|
||||
id="path2996"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<rect
|
||||
width="1.1830783"
|
||||
height="5.3462563"
|
||||
rx="0"
|
||||
ry="0.89710045"
|
||||
x="7.4181905"
|
||||
y="8.3502293"
|
||||
id="rect3937"
|
||||
style="fill:#dedede;fill-opacity:1;fill-rule:nonzero" />
|
||||
<g
|
||||
transform="translate(-12.523729,-0.08389831)"
|
||||
id="g3893">
|
||||
<g
|
||||
id="g3877">
|
||||
<g
|
||||
id="g3879">
|
||||
<path
|
||||
d="m 5.6,8.7 c -1.3,1.3 -1.3,3.4 0,4.7 1.3,1.3 3.4,1.3 4.7,0 1.3,-1.3 1.3,-3.4 0,-4.7 C 9,7.4 6.9,7.4 5.6,8.7 z m 4.1,4 c -0.9,0.9 -2.4,0.9 -3.4,0 -0.5,-0.5 -0.7,-1.1 -0.7,-1.8 0,0 0,0 0,0 0,0 0,-0.1 0,-0.1 0,0 0,0 0,0 0,0 0,-0.1 0,-0.1 0,0 0,0 0,0 0,-0.1 0,-0.2 0.1,-0.4 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,-0.1 0,0 0,0 0,-0.1 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0.1,-0.1 0.1,-0.1 0,0 0,0 0,0 l 0,0 0,0 c 0,0 0.1,-0.1 0.1,-0.1 0,0 0,0 0,0 0,0 0,0 0.1,-0.1 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0.1,0 0.1,-0.1 0,0 0,0 0,0 0,0 0.1,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0.1,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0.1,0 0.1,0 0.2,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0.1,0 0.2,0.1 0.2,0.1 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 l 0,0 c 0.2,0.1 0.3,0.2 0.4,0.3 1.3,1.1 1.3,2.6 0.4,3.5 z"
|
||||
id="path3881"
|
||||
style="fill:#ffffff" />
|
||||
<g
|
||||
id="g3883">
|
||||
<polyline
|
||||
id="polyline3885"
|
||||
points="7.5,8.5 7.5,13.6 8.5,13.6 8.5,8.5 "
|
||||
style="fill:#ffffff" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 3.9 KiB |
Before Width: | Height: | Size: 704 B |
Before Width: | Height: | Size: 395 B |
22
img/taskbar/dark/taskbar_offline.svg
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16.1 16" enable-background="new 0 0 16.1 16" xml:space="preserve">
|
||||
<path fill="#252325" d="M8,0C6.1,0,4.2,1.3,4.2,3.9v1.9h-1c-1.1,0-1.8,0.8-1.8,1.9v6.5c0,1.1,0.7,1.9,1.9,1.9l9.6,0
|
||||
c1.1,0,1.9-0.8,1.9-1.9V7.6c0-1.1-0.8-1.9-1.9-1.9h-0.9l0-1.9C11.9,1.3,10,0,8,0z M8,1.6c1.3,0,2.2,1,2.2,2.2C10.3,5,9.8,6.2,8,6.9
|
||||
c0.3-0.5,0.4-0.7,0.5-1.2C6.7,5.9,5.8,4.9,5.8,3.8S6.7,1.6,8,1.6z"/>
|
||||
<g>
|
||||
<path fill="#C94F50" d="M8,7.7c-1.8,0-3.3,1.5-3.3,3.3c0,1.8,1.5,3.3,3.3,3.3c1.8,0,3.3-1.5,3.3-3.3C11.3,9.2,9.8,7.7,8,7.7z
|
||||
M8,13.4c-1.3,0-2.4-1.1-2.4-2.4c0-0.7,0.3-1.3,0.7-1.7c0,0,0,0,0,0c0,0,0,0,0.1-0.1c0,0,0,0,0,0c0,0,0,0,0.1-0.1c0,0,0,0,0,0
|
||||
C6.6,9.1,6.7,9,6.8,8.9c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0
|
||||
c0,0,0,0,0.1,0c0,0,0,0,0.1,0c0,0,0.1,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0
|
||||
c0,0,0,0,0,0c0,0,0.1,0,0.1,0c0,0,0,0,0,0c0,0,0.1,0,0.1,0c0,0,0,0,0,0l0,0l0,0c0,0,0.1,0,0.1,0c0,0,0,0,0,0c0,0,0.1,0,0.1,0
|
||||
c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0.1,0,0.1,0c0,0,0,0,0,0c0,0,0.1,0,0.1,0c0,0,0,0,0,0
|
||||
c0,0,0.1,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0.1,0
|
||||
c0,0,0,0,0,0c0,0,0.1,0,0.1,0.1c0,0,0,0,0,0c0,0,0,0,0.1,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0
|
||||
c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0,0,0,0c0,0,0.1,0.1,0.1,0.1c0,0,0,0,0,0c0,0,0,0,0,0.1c0,0,0,0,0,0
|
||||
c0,0,0,0,0,0.1c0,0,0,0,0,0c0,0,0,0,0,0.1c0,0,0,0,0,0c0,0,0,0,0,0.1c0,0,0,0,0,0c0,0.1,0.1,0.2,0.1,0.2c0,0,0,0,0,0c0,0,0,0,0,0.1
|
||||
c0,0,0,0,0,0c0,0,0,0,0,0.1c0,0,0,0,0,0c0,0,0,0,0,0.1c0,0,0,0,0,0l0,0c0,0.2,0.1,0.4,0.1,0.6C10.4,12.3,9.3,13.4,8,13.4z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 711 B |
Before Width: | Height: | Size: 374 B |
10
img/taskbar/dark/taskbar_online.svg
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16.1 16" enable-background="new 0 0 16.1 16" xml:space="preserve">
|
||||
<path fill="#252325" d="M8,0C6.1,0,4.2,1.3,4.2,3.9v1.9h-1c-1.1,0-1.8,0.8-1.8,1.9v6.5c0,1.1,0.7,1.9,1.9,1.9l9.6,0
|
||||
c1.1,0,1.9-0.8,1.9-1.9V7.6c0-1.1-0.8-1.9-1.9-1.9h-0.9l0-1.9C11.9,1.3,10,0,8,0z M8,1.6c1.3,0,2.2,1,2.2,2.2C10.3,5,9.8,6.2,8,6.9
|
||||
c0.3-0.5,0.4-0.7,0.5-1.2C6.7,5.9,5.8,4.9,5.8,3.8S6.7,1.6,8,1.6z"/>
|
||||
<path fill="#6FC062" d="M11.4,11c0,1.9-1.5,3.4-3.4,3.4c-1.9,0-3.4-1.5-3.4-3.4c0-1.9,1.5-3.4,3.4-3.4C9.9,7.6,11.4,9.1,11.4,11"/>
|
||||
</svg>
|
After Width: | Height: | Size: 885 B |
Before Width: | Height: | Size: 637 B |
13
img/taskbar/light/taskbar_away.svg
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M8,0C6.1,0,4.1,1.3,4.1,3.9v1.9h-1C2,5.7,1.3,6.5,1.3,7.6v6.5c0,1.1,0.7,1.9,1.9,1.9l9.6,0
|
||||
c1.1,0,1.9-0.8,1.9-1.9V7.6c0-1.1-0.8-1.9-1.9-1.9h-0.9l0-1.9C11.9,1.3,9.9,0,8,0z M8,1.6c1.3,0,2.2,1,2.2,2.2
|
||||
C10.2,5,9.7,6.2,8,6.9c0.3-0.5,0.4-0.7,0.5-1.2C6.7,5.9,5.8,4.9,5.8,3.8S6.7,1.6,8,1.6z"/>
|
||||
<path fill="#CDBE41" d="M7.9,7.6c-1.8,0-3.3,1.5-3.3,3.3c0,1.8,1.5,3.3,3.3,3.3s3.3-1.5,3.3-3.3C11.3,9.1,9.8,7.6,7.9,7.6z M5.6,11
|
||||
c0-1.3,1.1-2.4,2.4-2.4s2.4,1.1,2.4,2.4H5.6z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 943 B |
Before Width: | Height: | Size: 386 B |
72
img/taskbar/light/taskbar_busy.svg
Normal file
|
@ -0,0 +1,72 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 16.1 16"
|
||||
id="Layer_1"
|
||||
xml:space="preserve">
|
||||
<metadata
|
||||
id="metadata19">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs17" />
|
||||
<path
|
||||
d="M 8,0 C 6.1,0 4.2,1.3 4.2,3.9 v 1.9 h -1 C 2.1,5.8 1.4,6.6 1.4,7.7 v 6.5 c 0,1.1 0.7,1.9 1.9,1.9 l 9.6,0 c 1.1,0 1.9,-0.8 1.9,-1.9 V 7.6 C 14.8,6.5 14,5.7 12.9,5.7 H 12 L 12,3.8 C 11.9,1.3 10,0 8,0 z m 0,1.6 c 1.3,0 2.2,1 2.2,2.2 C 10.3,5 9.8,6.2 8,6.9 8.3,6.4 8.4,6.2 8.5,5.7 6.7,5.9 5.8,4.9 5.8,3.8 5.8,2.7 6.7,1.6 8,1.6 z"
|
||||
id="path3"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
d="m 7.9927197,7.71875 c -1.8159871,0 -3.28125,1.4652629 -3.28125,3.28125 0,1.815987 1.4652629,3.28125 3.28125,3.28125 1.8159871,0 3.3125003,-1.465263 3.3125003,-3.28125 0,-1.8159871 -1.4965132,-3.28125 -3.3125003,-3.28125 z m 0,0.90625 c 1.3198669,0 2.4062503,1.0551331 2.4062503,2.375 0,1.319867 -1.0863834,2.375 -2.4062503,2.375 -1.3198669,0 -2.375,-1.055133 -2.375,-2.375 0,-1.3198669 1.0551331,-2.375 2.375,-2.375 z"
|
||||
id="path3000"
|
||||
style="fill:#c94f50;fill-opacity:1" />
|
||||
<path
|
||||
d="m -6.5762711,11.932203 a 5.3220339,6.237288 0 1 1 -10.6440679,0 5.3220339,6.237288 0 1 1 10.6440679,0 z"
|
||||
id="path2996"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
<rect
|
||||
width="1.1830783"
|
||||
height="5.3462563"
|
||||
rx="0"
|
||||
ry="0.89710045"
|
||||
x="12.866886"
|
||||
y="-0.54217148"
|
||||
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
|
||||
id="rect3937"
|
||||
style="fill:#c94f50;fill-opacity:1;fill-rule:nonzero" />
|
||||
<g
|
||||
transform="translate(-12.523729,-0.08389831)"
|
||||
id="g3893">
|
||||
<g
|
||||
id="g3877">
|
||||
<g
|
||||
id="g3879">
|
||||
<path
|
||||
d="m 5.6,8.7 c -1.3,1.3 -1.3,3.4 0,4.7 1.3,1.3 3.4,1.3 4.7,0 1.3,-1.3 1.3,-3.4 0,-4.7 C 9,7.4 6.9,7.4 5.6,8.7 z m 4.1,4 c -0.9,0.9 -2.4,0.9 -3.4,0 -0.5,-0.5 -0.7,-1.1 -0.7,-1.8 0,0 0,0 0,0 0,0 0,-0.1 0,-0.1 0,0 0,0 0,0 0,0 0,-0.1 0,-0.1 0,0 0,0 0,0 0,-0.1 0,-0.2 0.1,-0.4 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,-0.1 0,0 0,0 0,-0.1 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0,0 0,-0.1 0,0 0,0 0,0 0,0 0.1,-0.1 0.1,-0.1 0,0 0,0 0,0 l 0,0 0,0 c 0,0 0.1,-0.1 0.1,-0.1 0,0 0,0 0,0 0,0 0,0 0.1,-0.1 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0.1,0 0.1,-0.1 0,0 0,0 0,0 0,0 0.1,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0.1,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0.1,0 0.1,0 0.2,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0.1,0 0.2,0.1 0.2,0.1 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 0,0 0,0 0.1,0 0,0 0,0 0,0 l 0,0 c 0.2,0.1 0.3,0.2 0.4,0.3 1.3,1.1 1.3,2.6 0.4,3.5 z"
|
||||
id="path3881"
|
||||
style="fill:#ffffff" />
|
||||
<g
|
||||
id="g3883">
|
||||
<polyline
|
||||
id="polyline3885"
|
||||
points="7.5,8.5 7.5,13.6 8.5,13.6 8.5,8.5 "
|
||||
style="fill:#ffffff" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 4.0 KiB |
Before Width: | Height: | Size: 685 B |
12
img/taskbar/light/taskbar_event.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M8,0C6.1,0,4.1,1.3,4.1,3.9v1.9h-1C2,5.7,1.3,6.5,1.3,7.6v6.5c0,1.1,0.7,1.9,1.9,1.9l9.6,0
|
||||
c1.1,0,1.9-0.8,1.9-1.9V7.6c0-1.1-0.8-1.9-1.9-1.9h-0.9l0-1.9C11.9,1.3,9.9,0,8,0z M8,1.6c1.3,0,2.2,1,2.2,2.2
|
||||
C10.2,5,9.7,6.2,8,6.9c0.3-0.5,0.4-0.7,0.5-1.2C6.7,5.9,5.8,4.9,5.8,3.8S6.7,1.6,8,1.6z"/>
|
||||
<path fill="#ff9400" d="M11.4,11c0,1.9-1.5,3.4-3.4,3.4c-1.9,0-3.4-1.5-3.4-3.4c0-1.9,1.5-3.4,3.4-3.4C9.9,7.6,11.4,9.1,11.4,11"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 894 B |
Before Width: | Height: | Size: 363 B |
Before Width: | Height: | Size: 635 B |
Before Width: | Height: | Size: 366 B |
46
img/taskbar/light/taskbar_invisible.svg
Normal file
|
@ -0,0 +1,46 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
version="1.1"
|
||||
width="100%"
|
||||
height="100%"
|
||||
viewBox="0 0 16.1 16"
|
||||
id="Layer_1"
|
||||
xml:space="preserve">
|
||||
<metadata
|
||||
id="metadata19">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs17" />
|
||||
<path
|
||||
d="M 8,0 C 6.1,0 4.2,1.3 4.2,3.9 v 1.9 h -1 C 2.1,5.8 1.4,6.6 1.4,7.7 v 6.5 c 0,1.1 0.7,1.9 1.9,1.9 l 9.6,0 c 1.1,0 1.9,-0.8 1.9,-1.9 V 7.6 C 14.8,6.5 14,5.7 12.9,5.7 H 12 L 12,3.8 C 11.9,1.3 10,0 8,0 z m 0,1.6 c 1.3,0 2.2,1 2.2,2.2 C 10.3,5 9.8,6.2 8,6.9 8.3,6.4 8.4,6.2 8.5,5.7 6.7,5.9 5.8,4.9 5.8,3.8 5.8,2.7 6.7,1.6 8,1.6 z"
|
||||
id="path3"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
d="m 7.9927197,7.71875 c -1.8159871,0 -3.28125,1.4652629 -3.28125,3.28125 0,1.815987 1.4652629,3.28125 3.28125,3.28125 1.8159871,0 3.3125003,-1.465263 3.3125003,-3.28125 0,-1.8159871 -1.4965132,-3.28125 -3.3125003,-3.28125 z m 0,0.90625 c 1.3198669,0 2.4062503,1.0551331 2.4062503,2.375 0,1.319867 -1.0863834,2.375 -2.4062503,2.375 -1.3198669,0 -2.375,-1.055133 -2.375,-2.375 0,-1.3198669 1.0551331,-2.375 2.375,-2.375 z"
|
||||
id="path3000"
|
||||
style="fill:#4e4e4e;fill-opacity:1" />
|
||||
<rect
|
||||
width="1.1830783"
|
||||
height="5.3462563"
|
||||
rx="0"
|
||||
ry="0.89710045"
|
||||
x="7.4181905"
|
||||
y="8.3502293"
|
||||
id="rect3937"
|
||||
style="fill:#4e4e4e;fill-opacity:1;fill-rule:nonzero" />
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 670 B |
Before Width: | Height: | Size: 378 B |
15
img/taskbar/light/taskbar_offline.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M8,0C6.1,0,4.1,1.3,4.1,3.9v1.9h-1C2,5.7,1.3,6.5,1.3,7.6v6.5c0,1.1,0.7,1.9,1.9,1.9l9.6,0
|
||||
c1.1,0,1.9-0.8,1.9-1.9V7.6c0-1.1-0.8-1.9-1.9-1.9h-0.9l0-1.9C11.9,1.3,9.9,0,8,0z M8,1.6c1.3,0,2.2,1,2.2,2.2
|
||||
C10.2,5,9.7,6.2,8,6.9c0.3-0.5,0.4-0.7,0.5-1.2C6.7,5.9,5.8,4.9,5.8,3.8S6.7,1.6,8,1.6z"/>
|
||||
<g>
|
||||
<path fill="#C94F50" d="M7.9,7.7c-1.8,0-3.3,1.5-3.3,3.3c0,1.8,1.5,3.3,3.3,3.3c1.8,0,3.3-1.4,3.3-3.2C11.3,9.3,9.8,7.7,7.9,7.7z
|
||||
M7.9,13.4c-2.5,0-3.2-3.4-1.1-4.5C7.4,8.6,8,8.6,8.6,8.7C11.1,9.4,10.8,13.4,7.9,13.4z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 995 B |
Before Width: | Height: | Size: 677 B |
Before Width: | Height: | Size: 320 B |
12
img/taskbar/light/taskbar_online.svg
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 18.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
|
||||
<g>
|
||||
<path fill="#FFFFFF" d="M8,0C6.1,0,4.1,1.3,4.1,3.9v1.9h-1C2,5.7,1.3,6.5,1.3,7.6v6.5c0,1.1,0.7,1.9,1.9,1.9l9.6,0
|
||||
c1.1,0,1.9-0.8,1.9-1.9V7.6c0-1.1-0.8-1.9-1.9-1.9h-0.9l0-1.9C11.9,1.3,9.9,0,8,0z M8,1.6c1.3,0,2.2,1,2.2,2.2
|
||||
C10.2,5,9.7,6.2,8,6.9c0.3-0.5,0.4-0.7,0.5-1.2C6.7,5.9,5.8,4.9,5.8,3.8S6.7,1.6,8,1.6z"/>
|
||||
<path fill="#6FC062" d="M11.4,11c0,1.9-1.5,3.4-3.4,3.4c-1.9,0-3.4-1.5-3.4-3.4c0-1.9,1.5-3.4,3.4-3.4C9.9,7.6,11.4,9.1,11.4,11"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 894 B |
Before Width: | Height: | Size: 578 B |
84
qtox.pro
|
@ -32,10 +32,10 @@ FORMS += \
|
|||
src/widget/form/settings/identitysettings.ui \
|
||||
src/widget/form/settings/privacysettings.ui \
|
||||
src/widget/form/loadhistorydialog.ui \
|
||||
src/widget/form/inputpassworddialog.ui \
|
||||
src/widget/form/setpassworddialog.ui \
|
||||
src/chatlog/content/filetransferwidget.ui \
|
||||
src/widget/form/settings/advancedsettings.ui
|
||||
src/widget/form/settings/advancedsettings.ui \
|
||||
src/android.ui
|
||||
|
||||
CONFIG += c++11
|
||||
|
||||
|
@ -71,6 +71,23 @@ contains(ENABLE_SYSTRAY_UNITY_BACKEND, YES) {
|
|||
LIBS += -lgobject-2.0 -lappindicator -lgtk-x11-2.0
|
||||
}
|
||||
|
||||
android {
|
||||
ANDROID_TOOLCHAIN=/opt/android/toolchain-r9d-17/
|
||||
INCLUDEPATH += $$ANDROID_TOOLCHAIN/include/
|
||||
LIBS += -L$$ANDROID_TOOLCHAIN/lib
|
||||
|
||||
DISABLE_PLATFORM_EXT=YES
|
||||
DISABLE_FILTER_AUDIO=YES
|
||||
|
||||
ANDROID_PACKAGE_SOURCE_DIR = $$PWD/android
|
||||
contains(ANDROID_TARGET_ARCH,armeabi) {
|
||||
ANDROID_EXTRA_LIBS = \
|
||||
$$ANDROID_TOOLCHAIN/lib/libopenal.so \
|
||||
$$ANDROID_TOOLCHAIN/lib/libsodium.so
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
contains(DISABLE_PLATFORM_EXT, YES) {
|
||||
|
||||
} else {
|
||||
|
@ -92,7 +109,7 @@ contains(JENKINS,YES) {
|
|||
# Rules for Windows, Mac OSX, and Linux
|
||||
win32 {
|
||||
RC_FILE = windows/qtox.rc
|
||||
LIBS += -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lsodium -lvpx -lpthread
|
||||
LIBS += -L$$PWD/libs/lib -ltoxav -ltoxcore -ltoxencryptsave -ltoxdns -lsodium -lvpx -lpthread
|
||||
LIBS += -L$$PWD/libs/lib -lopencv_core249 -lopencv_highgui249 -lopencv_imgproc249 -lOpenAL32 -lopus
|
||||
LIBS += -lopengl32 -lole32 -loleaut32 -luuid -lvfw32 -lws2_32 -liphlpapi -lz
|
||||
|
||||
|
@ -112,31 +129,38 @@ win32 {
|
|||
contains(DEFINES, QTOX_PLATFORM_EXT) { LIBS += -framework IOKit -framework CoreFoundation }
|
||||
contains(DEFINES, QTOX_FILTER_AUDIO) { LIBS += -lfilteraudio }
|
||||
} else {
|
||||
# If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
|
||||
contains(STATICPKG, YES) {
|
||||
target.path = /usr/bin
|
||||
INSTALLS += target
|
||||
LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic
|
||||
LIBS += -Wl,-Bstatic -ljpeg -ltiff -lpng -ljasper -lIlmImf -lIlmThread -lIex -ldc1394 -lraw1394 -lHalf -lz -llzma -ljbig
|
||||
LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0
|
||||
android {
|
||||
LIBS += -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns
|
||||
LIBS += -lopencv_videoio -lopencv_imgcodecs -lopencv_highgui -lopencv_imgproc -lopencv_androidcamera
|
||||
LIBS += -llibjpeg -llibwebp -llibpng -llibtiff -llibjasper -lIlmImf -lopencv_core
|
||||
LIBS += -lopus -lvpx -lsodium -lopenal
|
||||
} else {
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lvpx -lsodium -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
|
||||
}
|
||||
|
||||
contains(DEFINES, QTOX_PLATFORM_EXT) {
|
||||
LIBS += -lX11 -lXss
|
||||
}
|
||||
|
||||
contains(DEFINES, QTOX_FILTER_AUDIO) {
|
||||
# If we're building a package, static link libtox[core,av] and libsodium, since they are not provided by any package
|
||||
contains(STATICPKG, YES) {
|
||||
LIBS += -Wl,-Bstatic -lfilteraudio
|
||||
target.path = /usr/bin
|
||||
INSTALLS += target
|
||||
LIBS += -L$$PWD/libs/lib/ -lopus -lvpx -lopenal -Wl,-Bstatic -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lsodium -lopencv_highgui -lopencv_imgproc -lopencv_core -lz -Wl,-Bdynamic
|
||||
LIBS += -Wl,-Bstatic -ljpeg -ltiff -lpng -ljasper -lIlmImf -lIlmThread -lIex -ldc1394 -lraw1394 -lHalf -lz -llzma -ljbig
|
||||
LIBS += -Wl,-Bdynamic -lv4l1 -lv4l2 -lavformat -lavcodec -lavutil -lswscale -lusb-1.0
|
||||
} else {
|
||||
LIBS += -lfilteraudio
|
||||
LIBS += -L$$PWD/libs/lib/ -ltoxcore -ltoxav -ltoxencryptsave -ltoxdns -lvpx -lsodium -lopenal -lopencv_core -lopencv_highgui -lopencv_imgproc
|
||||
}
|
||||
}
|
||||
|
||||
contains(JENKINS, YES) {
|
||||
LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxdns.a ./libs/lib/libtoxencryptsave.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a ./libs/lib/libfilteraudio.a /usr/lib/libopencv_core.so /usr/lib/libopencv_highgui.so /usr/lib/libopencv_imgproc.so -lopenal -lX11 -lXss -s
|
||||
contains(DEFINES, QTOX_PLATFORM_EXT) {
|
||||
LIBS += -lX11 -lXss
|
||||
}
|
||||
|
||||
contains(DEFINES, QTOX_FILTER_AUDIO) {
|
||||
contains(STATICPKG, YES) {
|
||||
LIBS += -Wl,-Bstatic -lfilteraudio
|
||||
} else {
|
||||
LIBS += -lfilteraudio
|
||||
}
|
||||
}
|
||||
|
||||
contains(JENKINS, YES) {
|
||||
LIBS = ./libs/lib/libtoxav.a ./libs/lib/libvpx.a ./libs/lib/libopus.a ./libs/lib/libtoxdns.a ./libs/lib/libtoxencryptsave.a ./libs/lib/libtoxcore.a ./libs/lib/libsodium.a ./libs/lib/libfilteraudio.a /usr/lib/libopencv_core.so /usr/lib/libopencv_highgui.so /usr/lib/libopencv_imgproc.so -lopenal -lX11 -lXss -s
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -186,7 +210,6 @@ HEADERS += src/widget/form/addfriendform.h \
|
|||
src/misc/db/genericddinterface.h \
|
||||
src/misc/db/plaindb.h \
|
||||
src/misc/db/encrypteddb.h \
|
||||
src/widget/form/inputpassworddialog.h \
|
||||
src/widget/form/setpassworddialog.h \
|
||||
src/widget/form/tabcompleter.h \
|
||||
src/video/videoframe.h \
|
||||
|
@ -215,6 +238,11 @@ HEADERS += src/widget/form/addfriendform.h \
|
|||
src/chatlog/pixmapcache.h \
|
||||
src/widget/callconfirmwidget.h \
|
||||
src/widget/systemtrayicon.h \
|
||||
src/widget/systemtrayicon_private.h \
|
||||
src/nexus.h \
|
||||
src/widget/gui.h \
|
||||
src/widget/androidgui.h \
|
||||
src/offlinemsgengine.h
|
||||
|
||||
SOURCES += \
|
||||
src/widget/form/addfriendform.cpp \
|
||||
|
@ -232,6 +260,7 @@ SOURCES += \
|
|||
src/widget/groupwidget.cpp \
|
||||
src/widget/widget.cpp \
|
||||
src/core.cpp \
|
||||
src/coreencryption.cpp \
|
||||
src/friend.cpp \
|
||||
src/friendlist.cpp \
|
||||
src/group.cpp \
|
||||
|
@ -260,7 +289,6 @@ SOURCES += \
|
|||
src/misc/db/genericddinterface.cpp \
|
||||
src/misc/db/plaindb.cpp \
|
||||
src/misc/db/encrypteddb.cpp \
|
||||
src/widget/form/inputpassworddialog.cpp \
|
||||
src/widget/form/setpassworddialog.cpp \
|
||||
src/video/netvideosource.cpp \
|
||||
src/widget/form/tabcompleter.cpp \
|
||||
|
@ -289,7 +317,11 @@ SOURCES += \
|
|||
src/chatlog/documentcache.cpp \
|
||||
src/chatlog/pixmapcache.cpp \
|
||||
src/widget/callconfirmwidget.cpp \
|
||||
src/widget/systemtrayicon.cpp
|
||||
src/widget/systemtrayicon.cpp \
|
||||
src/nexus.cpp \
|
||||
src/widget/gui.cpp \
|
||||
src/widget/androidgui.cpp \
|
||||
src/offlinemsgengine.cpp
|
||||
|
||||
contains(DEFINES, QTOX_FILTER_AUDIO) {
|
||||
HEADERS += src/audiofilterer.h
|
||||
|
|
22
res.qrc
|
@ -37,14 +37,16 @@
|
|||
<file>img/status/dot_online.png</file>
|
||||
<file>img/status/dot_online_2x.png</file>
|
||||
<file>img/status/dot_online_notification.png</file>
|
||||
<file>img/taskbar/dark/taskbar_online_2x.png</file>
|
||||
<file>img/taskbar/dark/taskbar_idle_2x.png</file>
|
||||
<file>img/taskbar/dark/taskbar_busy_2x.png</file>
|
||||
<file>img/taskbar/dark/taskbar_offline_2x.png</file>
|
||||
<file>img/taskbar/light/taskbar_online_2x.png</file>
|
||||
<file>img/taskbar/light/taskbar_idle_2x.png</file>
|
||||
<file>img/taskbar/light/taskbar_busy_2x.png</file>
|
||||
<file>img/taskbar/light/taskbar_offline_2x.png</file>
|
||||
<file>img/taskbar/dark/taskbar_online.svg</file>
|
||||
<file>img/taskbar/dark/taskbar_away.svg</file>
|
||||
<file>img/taskbar/dark/taskbar_busy.svg</file>
|
||||
<file>img/taskbar/dark/taskbar_offline.svg</file>
|
||||
<file>img/taskbar/dark/taskbar_event.svg</file>
|
||||
<file>img/taskbar/light/taskbar_online.svg</file>
|
||||
<file>img/taskbar/light/taskbar_away.svg</file>
|
||||
<file>img/taskbar/light/taskbar_busy.svg</file>
|
||||
<file>img/taskbar/light/taskbar_offline.svg</file>
|
||||
<file>img/taskbar/light/taskbar_event.svg</file>
|
||||
<file>img/transfer.png</file>
|
||||
<file>smileys/cylgom/angel.png</file>
|
||||
<file>smileys/cylgom/angry.png</file>
|
||||
|
@ -189,10 +191,6 @@
|
|||
<file>ui/sendButton/sendButtonPressed.png</file>
|
||||
<file>ui/settings/mainContent.css</file>
|
||||
<file>ui/settings/mainHead.css</file>
|
||||
<file>ui/statusButton/dot_away.png</file>
|
||||
<file>ui/statusButton/dot_busy.png</file>
|
||||
<file>ui/statusButton/dot_idle.png</file>
|
||||
<file>ui/statusButton/dot_online.png</file>
|
||||
<file>ui/statusButton/menu_indicator.png</file>
|
||||
<file>ui/statusButton/statusButton.css</file>
|
||||
<file>ui/stopFileButton/default.png</file>
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
if which apt-get; then
|
||||
sudo apt-get install build-essential qt5-qmake qt5-default libopenal-dev libopencv-dev \
|
||||
libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev qttools5-dev-tools qtchooser libxss-dev
|
||||
libtool autotools-dev automake checkinstall check libopus-dev libvpx-dev qttools5-dev-tools qtchooser libxss-dev libqt5svg5*
|
||||
elif which pacman; then
|
||||
sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss
|
||||
sudo pacman -S --needed base-devel qt5 opencv openal opus libvpx libxss qt5-svg
|
||||
elif which yum; then
|
||||
sudo yum groupinstall "Development Tools"
|
||||
sudo yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel libXScrnSaver-devel
|
||||
sudo yum install qt-devel qt-doc qt-creator opencv-devel openal-soft-devel libtool autoconf automake check check-devel libXScrnSaver-devel qt5-qtsvg
|
||||
else
|
||||
echo "Unknown package manager, attempting to compile anyways"
|
||||
fi
|
||||
|
|
909
src/android.ui
Normal file
|
@ -0,0 +1,909 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Android</class>
|
||||
<widget class="QWidget" name="Android">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<height>480</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QFrame" name="statusPanel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QWidget" name="statusHead" native="true">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="myProfile">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Maximum</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>5</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="1,1">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="CroppingLabel" name="nameLabel">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Button">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Light">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Midlight">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Dark">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Mid">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Text">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="ButtonText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Shadow">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Highlight">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="AlternateBase">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="NoRole">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="ToolTipText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Button">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Light">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Midlight">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Dark">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Mid">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Text">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="ButtonText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Shadow">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Highlight">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="AlternateBase">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="NoRole">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="ToolTipText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Button">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Light">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Midlight">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Dark">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Mid">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Text">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="ButtonText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Base">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Window">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Shadow">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Highlight">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>240</red>
|
||||
<green>240</green>
|
||||
<blue>240</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="AlternateBase">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="NoRole">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="ToolTipText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>255</red>
|
||||
<green>255</green>
|
||||
<blue>255</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>11</pointsize>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhNoPredictiveText|Qt::ImhPreferLatin</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Your name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="CroppingLabel" name="statusLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>1</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="palette">
|
||||
<palette>
|
||||
<active>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>193</red>
|
||||
<green>193</green>
|
||||
<blue>193</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Text">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>193</red>
|
||||
<green>193</green>
|
||||
<blue>193</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</active>
|
||||
<inactive>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>193</red>
|
||||
<green>193</green>
|
||||
<blue>193</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Text">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>193</red>
|
||||
<green>193</green>
|
||||
<blue>193</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</inactive>
|
||||
<disabled>
|
||||
<colorrole role="WindowText">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>14</red>
|
||||
<green>14</green>
|
||||
<blue>14</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
<colorrole role="Text">
|
||||
<brush brushstyle="SolidPattern">
|
||||
<color alpha="255">
|
||||
<red>14</red>
|
||||
<green>14</green>
|
||||
<blue>14</blue>
|
||||
</color>
|
||||
</brush>
|
||||
</colorrole>
|
||||
</disabled>
|
||||
</palette>
|
||||
</property>
|
||||
<property name="font">
|
||||
<font>
|
||||
<pointsize>8</pointsize>
|
||||
</font>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhNoPredictiveText|Qt::ImhPreferLatin</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Your status</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="statusButton">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
<height>10</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="autoExclusive">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="default">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="AdjustingScrollArea" name="friendList">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
<property name="widgetResizable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="scrollAreaWidgetContents">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>320</width>
|
||||
<height>385</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5"/>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
<item alignment="Qt::AlignHCenter">
|
||||
<widget class="QWidget" name="tooliconsZone" native="true">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="addButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Add friends</string>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/img/add.png</normaloff>:/img/add.png</iconset>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="autoDefault">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="groupButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Create a group chat</string>
|
||||
</property>
|
||||
<property name="autoFillBackground">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/img/group_button.png</normaloff>:/img/group_button.png</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="transferButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>View completed file transfers</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/img/transfer.png</normaloff>:/img/transfer.png</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="settingsButton">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>55</width>
|
||||
<height>35</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::NoFocus</enum>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Change your settings</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>:/img/settings.png</normaloff>:/img/settings.png</iconset>
|
||||
</property>
|
||||
<property name="flat">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
<class>AdjustingScrollArea</class>
|
||||
<extends>QScrollArea</extends>
|
||||
<header>src/widget/adjustingscrollarea.h</header>
|
||||
<container>1</container>
|
||||
</customwidget>
|
||||
<customwidget>
|
||||
<class>CroppingLabel</class>
|
||||
<extends>QLabel</extends>
|
||||
<header>src/widget/croppinglabel.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -19,6 +19,7 @@
|
|||
#include "src/misc/serialize.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include "src/widget/widget.h"
|
||||
#include "src/widget/gui.h"
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QCoreApplication>
|
||||
|
@ -490,8 +491,8 @@ void AutoUpdater::checkUpdatesAsyncInteractiveWorker()
|
|||
QDir updateDir(updateDirStr);
|
||||
|
||||
if ((updateDir.exists() && QFile(updateDirStr+"flist").exists())
|
||||
|| Widget::getInstance()->askMsgboxQuestion(QObject::tr("Update", "The title of a message box"),
|
||||
QObject::tr("An update is available, do you want to download it now?\nIt will be installed when qTox restarts.")))
|
||||
|| GUI::askQuestion(QObject::tr("Update", "The title of a message box"),
|
||||
QObject::tr("An update is available, do you want to download it now?\nIt will be installed when qTox restarts."), true, false))
|
||||
{
|
||||
downloadUpdate();
|
||||
}
|
||||
|
|
|
@ -197,7 +197,7 @@ QString ChatMessage::detectQuotes(const QString& str)
|
|||
QString quotedText;
|
||||
for (int i=0;i<messageLines.size();++i)
|
||||
{
|
||||
if (QRegExp("^>( |>|[^_\\d\\W]).*").exactMatch(messageLines[i]))
|
||||
if (QRegExp("^>( |[[]|>|[^_\\d\\W]).*").exactMatch(messageLines[i]))
|
||||
quotedText += "<span class=quote>" + messageLines[i] + "</span>";
|
||||
else
|
||||
quotedText += messageLines[i];
|
||||
|
|
327
src/core.cpp
|
@ -15,15 +15,15 @@
|
|||
*/
|
||||
|
||||
#include "core.h"
|
||||
#include "nexus.h"
|
||||
#include "misc/cdata.h"
|
||||
#include "misc/cstring.h"
|
||||
#include "misc/settings.h"
|
||||
#include "widget/widget.h"
|
||||
#include "widget/gui.h"
|
||||
#include "historykeeper.h"
|
||||
#include "src/audio.h"
|
||||
|
||||
#include <tox/tox.h>
|
||||
#include <tox/toxencryptsave.h>
|
||||
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
|
@ -39,7 +39,6 @@
|
|||
#include <QDateTime>
|
||||
#include <QList>
|
||||
#include <QBuffer>
|
||||
#include <QMessageBox>
|
||||
#include <QMutexLocker>
|
||||
|
||||
const QString Core::CONFIG_FILE_NAME = "data";
|
||||
|
@ -90,13 +89,25 @@ Core::Core(Camera* cam, QThread *CoreThread, QString loadPath) :
|
|||
Audio::openInput(inDevDescr);
|
||||
}
|
||||
|
||||
void Core::deadifyTox()
|
||||
{
|
||||
if (toxav)
|
||||
{
|
||||
toxav_kill(toxav);
|
||||
toxav = nullptr;
|
||||
}
|
||||
if (tox)
|
||||
{
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Core::~Core()
|
||||
{
|
||||
qDebug() << "Deleting Core";
|
||||
|
||||
clearPassword(Core::ptMain);
|
||||
clearPassword(Core::ptHistory);
|
||||
|
||||
saveConfiguration();
|
||||
toxTimer->stop();
|
||||
coreThread->exit(0);
|
||||
while (coreThread->isRunning())
|
||||
|
@ -105,13 +116,7 @@ Core::~Core()
|
|||
coreThread->wait(500);
|
||||
}
|
||||
|
||||
if (tox)
|
||||
{
|
||||
toxav_kill(toxav);
|
||||
toxav = nullptr;
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
}
|
||||
deadifyTox();
|
||||
|
||||
if (videobuf)
|
||||
{
|
||||
|
@ -125,7 +130,7 @@ Core::~Core()
|
|||
|
||||
Core* Core::getInstance()
|
||||
{
|
||||
return Widget::getInstance()->getCore();
|
||||
return Nexus::getCore();
|
||||
}
|
||||
|
||||
void Core::make_tox()
|
||||
|
@ -183,9 +188,6 @@ void Core::make_tox()
|
|||
{
|
||||
if (toxOptions.proxy_type != TOX_PROXY_NONE)
|
||||
{
|
||||
//QMessageBox::critical(Widget::getInstance(), tr("Proxy failure", "popup title"),
|
||||
//tr("toxcore failed to start with your proxy settings. qTox cannot run; please modify your "
|
||||
//"settings and restart.", "popup text"));
|
||||
qCritical() << "Core: bad proxy! no toxcore!";
|
||||
emit badProxy();
|
||||
}
|
||||
|
@ -223,27 +225,40 @@ void Core::make_tox()
|
|||
|
||||
void Core::start()
|
||||
{
|
||||
qDebug() << "Core: Starting up";
|
||||
|
||||
make_tox();
|
||||
|
||||
qsrand(time(nullptr));
|
||||
|
||||
if (loadPath != "")
|
||||
{
|
||||
if (!loadConfiguration(loadPath)) // loadPath is meaningless after this
|
||||
{
|
||||
qCritical() << "Core: loadConfiguration failed, exiting now";
|
||||
emit failedToStart();
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
return;
|
||||
while (!loadConfiguration(loadPath))
|
||||
{
|
||||
if (loadPath.isEmpty())
|
||||
{
|
||||
QString profile;
|
||||
if ((profile = loadOldInformation()).isEmpty())
|
||||
{
|
||||
qCritical() << "Core: loadConfiguration failed, exiting now";
|
||||
emit failedToStart();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
HistoryKeeper::resetInstance(); // I'm not actually sure if this is necessary
|
||||
}
|
||||
}
|
||||
}
|
||||
// loadPath is meaningless after this
|
||||
loadPath = "";
|
||||
}
|
||||
else // new ID
|
||||
{
|
||||
setStatusMessage(tr("Toxing on qTox")); // this also solves the not updating issue
|
||||
setUsername(tr("qTox User"));
|
||||
QMetaObject::invokeMethod(Widget::getInstance(), "onSettingsClicked"); // update ui with new profile
|
||||
}
|
||||
|
||||
tox_callback_friend_request(tox, onFriendRequest, this);
|
||||
|
@ -309,7 +324,7 @@ void Core::start()
|
|||
|
||||
void Core::process()
|
||||
{
|
||||
if (!tox)
|
||||
if (!isReady())
|
||||
return;
|
||||
|
||||
static int tolerance = CORE_DISCONNECT_TOLERANCE;
|
||||
|
@ -501,7 +516,6 @@ void Core::onGroupInvite(Tox*, int friendnumber, uint8_t type, const uint8_t *da
|
|||
void Core::onGroupMessage(Tox*, int groupnumber, int peernumber, const uint8_t * message, uint16_t length, void *_core)
|
||||
{
|
||||
Core* core = static_cast<Core*>(_core);
|
||||
|
||||
emit core->groupMessageReceived(groupnumber, peernumber, CString::toString(message, length), false);
|
||||
}
|
||||
|
||||
|
@ -1011,7 +1025,7 @@ void Core::acceptFileRecvRequest(int friendId, int fileNum, QString path)
|
|||
|
||||
void Core::removeFriend(int friendId, bool fake)
|
||||
{
|
||||
if (!tox || fake)
|
||||
if (!isReady() || fake)
|
||||
return;
|
||||
if (tox_del_friend(tox, friendId) == -1) {
|
||||
emit failedToRemoveFriend(friendId);
|
||||
|
@ -1023,7 +1037,7 @@ void Core::removeFriend(int friendId, bool fake)
|
|||
|
||||
void Core::removeGroup(int groupId, bool fake)
|
||||
{
|
||||
if (!tox || fake)
|
||||
if (!isReady() || fake)
|
||||
return;
|
||||
tox_del_groupchat(tox, groupId);
|
||||
|
||||
|
@ -1161,7 +1175,8 @@ QString Core::sanitize(QString name)
|
|||
|
||||
bool Core::loadConfiguration(QString path)
|
||||
{
|
||||
// setting the profile is now the responsibility of the caller
|
||||
loadPath = ""; // if not empty upon return, then user forgot a password and is switching
|
||||
|
||||
QFile configurationFile(path);
|
||||
qDebug() << "Core::loadConfiguration: reading from " << path;
|
||||
|
||||
|
@ -1185,51 +1200,20 @@ bool Core::loadConfiguration(QString path)
|
|||
}
|
||||
else if (error == 1) // Encrypted data save
|
||||
{
|
||||
if (!Settings::getInstance().getEncryptTox())
|
||||
Widget::getInstance()->showWarningMsgBox(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless."));
|
||||
uint8_t salt[tox_pass_salt_length()];
|
||||
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
||||
do
|
||||
if (!loadEncryptedSave(data))
|
||||
{
|
||||
while (!pwsaltedkeys[ptMain])
|
||||
configurationFile.close();
|
||||
|
||||
QString profile = Settings::getInstance().askProfiles();
|
||||
|
||||
if (!profile.isEmpty())
|
||||
{
|
||||
emit blockingGetPassword(tr("Tox datafile decryption password"), ptMain, salt);
|
||||
if (!pwsaltedkeys[ptMain])
|
||||
Widget::getInstance()->showWarningMsgBox(tr("Password error"), tr("Failed to setup password.\nEmpty password."));
|
||||
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
HistoryKeeper::resetInstance();
|
||||
}
|
||||
|
||||
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
|
||||
if (error != 0)
|
||||
{
|
||||
QMessageBox msgb;
|
||||
msgb.moveToThread(qApp->thread());
|
||||
QPushButton *tryAgain = msgb.addButton(tr("Try Again"), QMessageBox::AcceptRole);
|
||||
QPushButton *cancel = msgb.addButton(tr("Change profile"), QMessageBox::RejectRole);
|
||||
QPushButton *wipe = msgb.addButton(tr("Reinit current profile"), QMessageBox::ActionRole);
|
||||
msgb.setDefaultButton(tryAgain);
|
||||
msgb.setWindowTitle(tr("Password error"));
|
||||
msgb.setText(tr("Wrong password has been entered"));
|
||||
// msgb.setInformativeText(tr(""));
|
||||
|
||||
msgb.exec();
|
||||
|
||||
if (msgb.clickedButton() == tryAgain)
|
||||
clearPassword(ptMain);
|
||||
else if (msgb.clickedButton() == cancel)
|
||||
{
|
||||
configurationFile.close();
|
||||
return false;
|
||||
}
|
||||
else if (msgb.clickedButton() == wipe)
|
||||
{
|
||||
clearPassword(ptMain);
|
||||
Settings::getInstance().setEncryptTox(false);
|
||||
error = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
Settings::getInstance().setEncryptTox(true);
|
||||
} while (error != 0);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
configurationFile.close();
|
||||
|
@ -1249,53 +1233,7 @@ bool Core::loadConfiguration(QString path)
|
|||
|
||||
// tox core is already decrypted
|
||||
if (Settings::getInstance().getEnableLogging() && Settings::getInstance().getEncryptLogs())
|
||||
{
|
||||
// get salt
|
||||
QFile file(HistoryKeeper::getHistoryPath());
|
||||
file.open(QIODevice::ReadOnly);
|
||||
QByteArray data = file.read(tox_pass_encryption_extra_length());
|
||||
file.close();
|
||||
uint8_t salt[tox_pass_salt_length()];
|
||||
int err = tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
||||
if (err)
|
||||
{ // maybe we should handle this better
|
||||
qWarning() << "Core: history db isn't encrypted, but encryption is set!! No history loaded...";
|
||||
}
|
||||
else
|
||||
{
|
||||
bool error = true;
|
||||
do
|
||||
{
|
||||
while (!pwsaltedkeys[ptHistory])
|
||||
{
|
||||
emit blockingGetPassword(tr("History Log decryption password"), Core::ptHistory, salt);
|
||||
if (!pwsaltedkeys[ptHistory])
|
||||
Widget::getInstance()->showWarningMsgBox(tr("Password error"), tr("Failed to setup password.\nEmpty password."));
|
||||
}
|
||||
|
||||
if (!HistoryKeeper::checkPassword())
|
||||
{
|
||||
if (QMessageBox::Ok == Widget::getInstance()->showWarningMsgBox(tr("Encrypted log"),
|
||||
tr("Your history is encrypted with different password.\nDo you want to try another password?"),
|
||||
QMessageBox::Ok | QMessageBox::Cancel))
|
||||
{
|
||||
error = true;
|
||||
clearPassword(ptHistory);
|
||||
}
|
||||
else
|
||||
{
|
||||
error = false;
|
||||
clearPassword(ptHistory);
|
||||
Widget::getInstance()->showWarningMsgBox(tr("History"), tr("Due to incorret password history will be disabled."));
|
||||
Settings::getInstance().setEncryptLogs(false);
|
||||
Settings::getInstance().setEnableLogging(false);
|
||||
}
|
||||
} else {
|
||||
error = false;
|
||||
}
|
||||
} while (error);
|
||||
}
|
||||
}
|
||||
checkEncryptedHistory();
|
||||
|
||||
loadFriends();
|
||||
return true;
|
||||
|
@ -1306,6 +1244,9 @@ void Core::saveConfiguration()
|
|||
if (QThread::currentThread() != coreThread)
|
||||
return (void) QMetaObject::invokeMethod(this, "saveConfiguration");
|
||||
|
||||
if (!isReady())
|
||||
return;
|
||||
|
||||
QString dir = Settings::getSettingsDirPath();
|
||||
QDir directory(dir);
|
||||
if (!directory.exists() && !directory.mkpath(directory.absolutePath())) {
|
||||
|
@ -1322,7 +1263,7 @@ void Core::saveConfiguration()
|
|||
if (profile == "") // happens on creation of a new Tox ID
|
||||
profile = getIDString();
|
||||
|
||||
Settings::getInstance().setCurrentProfile(profile);
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
}
|
||||
|
||||
QString path = directory.filePath(profile + TOX_EXT);
|
||||
|
@ -1330,66 +1271,6 @@ void Core::saveConfiguration()
|
|||
saveConfiguration(path);
|
||||
}
|
||||
|
||||
void Core::saveConfiguration(const QString& path)
|
||||
{
|
||||
if (QThread::currentThread() != coreThread)
|
||||
return (void) QMetaObject::invokeMethod(this, "saveConfiguration", Q_ARG(const QString&, path));
|
||||
|
||||
if (!tox)
|
||||
{
|
||||
qWarning() << "Core::saveConfiguration: Tox not started, aborting!";
|
||||
return;
|
||||
}
|
||||
|
||||
Settings::getInstance().save();
|
||||
|
||||
QSaveFile configurationFile(path);
|
||||
if (!configurationFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
qCritical() << "File " << path << " cannot be opened";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Core: writing tox_save to " << path;
|
||||
|
||||
uint32_t fileSize;
|
||||
bool encrypt = Settings::getInstance().getEncryptTox();
|
||||
if (encrypt)
|
||||
fileSize = tox_encrypted_size(tox);
|
||||
else
|
||||
fileSize = tox_size(tox);
|
||||
|
||||
if (fileSize > 0 && fileSize <= INT32_MAX)
|
||||
{
|
||||
uint8_t *data = new uint8_t[fileSize];
|
||||
if (encrypt)
|
||||
{
|
||||
if (!pwsaltedkeys[ptMain])
|
||||
{
|
||||
Widget::getInstance()->showWarningMsgBox(tr("NO Password"), tr("Will be saved without encryption!"));
|
||||
tox_save(tox, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = tox_encrypted_key_save(tox, data, pwsaltedkeys[ptMain]);
|
||||
if (ret == -1)
|
||||
{
|
||||
qCritical() << "Core::saveConfiguration: encryption of save file failed!!!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tox_save(tox, data);
|
||||
}
|
||||
|
||||
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
|
||||
configurationFile.commit();
|
||||
delete[] data;
|
||||
}
|
||||
}
|
||||
|
||||
void Core::switchConfiguration(const QString& profile)
|
||||
{
|
||||
if (profile.isEmpty())
|
||||
|
@ -1398,19 +1279,15 @@ void Core::switchConfiguration(const QString& profile)
|
|||
qDebug() << "Core: switching from" << Settings::getInstance().getCurrentProfile() << "to" << profile;
|
||||
|
||||
saveConfiguration();
|
||||
saveCurrentInformation(); // part of a hack, see core.h
|
||||
|
||||
ready = false;
|
||||
GUI::setEnabled(false);
|
||||
clearPassword(ptMain);
|
||||
clearPassword(ptHistory);
|
||||
|
||||
toxTimer->stop();
|
||||
Widget::getInstance()->setEnabledThreadsafe(false);
|
||||
if (tox) {
|
||||
toxav_kill(toxav);
|
||||
toxav = nullptr;
|
||||
tox_kill(tox);
|
||||
tox = nullptr;
|
||||
}
|
||||
deadifyTox();
|
||||
|
||||
emit selfAvatarChanged(QPixmap(":/img/contact_dark.png"));
|
||||
emit blockingClearContacts(); // we need this to block, but signals are required for thread safety
|
||||
|
@ -1420,15 +1297,12 @@ void Core::switchConfiguration(const QString& profile)
|
|||
else
|
||||
loadPath = QDir(Settings::getSettingsDirPath()).filePath(profile + TOX_EXT);
|
||||
|
||||
// the new profile needs to be set before resetting the settings, so that
|
||||
// we don't load the old profile's profile.ini
|
||||
Settings::getInstance().setCurrentProfile(profile);
|
||||
Settings::getInstance().save(false); // save new profile, but don't write old profile info to newprofile.ini
|
||||
Settings::resetInstance();
|
||||
HistoryKeeper::getInstance()->resetInstance();
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
HistoryKeeper::resetInstance();
|
||||
|
||||
start();
|
||||
Widget::getInstance()->setEnabledThreadsafe(true);
|
||||
if (isReady())
|
||||
GUI::setEnabled(true);
|
||||
}
|
||||
|
||||
void Core::loadFriends()
|
||||
|
@ -1793,69 +1667,6 @@ QList<CString> Core::splitMessage(const QString &message, int maxLen)
|
|||
return splittedMsgs;
|
||||
}
|
||||
|
||||
void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt)
|
||||
{
|
||||
if (password.isEmpty())
|
||||
{
|
||||
clearPassword(passtype);
|
||||
return;
|
||||
}
|
||||
if (!pwsaltedkeys[passtype])
|
||||
pwsaltedkeys[passtype] = new uint8_t[tox_pass_key_length()];
|
||||
|
||||
CString str(password);
|
||||
if (salt)
|
||||
tox_derive_key_with_salt(str.data(), str.size(), salt, pwsaltedkeys[passtype]);
|
||||
else
|
||||
tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype]);
|
||||
|
||||
password.clear();
|
||||
}
|
||||
|
||||
void Core::clearPassword(PasswordType passtype)
|
||||
{
|
||||
if (pwsaltedkeys[passtype])
|
||||
{
|
||||
delete[] pwsaltedkeys[passtype];
|
||||
pwsaltedkeys[passtype] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype)
|
||||
{
|
||||
if (!pwsaltedkeys[passtype])
|
||||
return QByteArray();
|
||||
uint8_t encrypted[data.size() + tox_pass_encryption_extra_length()];
|
||||
if (tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], encrypted) == -1)
|
||||
{
|
||||
qWarning() << "Core::encryptData: encryption failed";
|
||||
return QByteArray();
|
||||
}
|
||||
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + tox_pass_encryption_extra_length());
|
||||
}
|
||||
|
||||
QByteArray Core::decryptData(const QByteArray& data, PasswordType passtype)
|
||||
{
|
||||
if (!pwsaltedkeys[passtype])
|
||||
return QByteArray();
|
||||
int sz = data.size() - tox_pass_encryption_extra_length();
|
||||
uint8_t decrypted[sz];
|
||||
if (tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], decrypted) != sz)
|
||||
{
|
||||
qWarning() << "Core::decryptData: decryption failed";
|
||||
return QByteArray();
|
||||
}
|
||||
return QByteArray(reinterpret_cast<char*>(decrypted), sz);
|
||||
}
|
||||
|
||||
bool Core::isPasswordSet(PasswordType passtype)
|
||||
{
|
||||
if (pwsaltedkeys[passtype])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QString Core::getPeerName(const ToxID& id) const
|
||||
{
|
||||
QString name;
|
||||
|
@ -1890,7 +1701,7 @@ QString Core::getPeerName(const ToxID& id) const
|
|||
|
||||
bool Core::isReady()
|
||||
{
|
||||
return ready;
|
||||
return toxav && tox && ready;
|
||||
}
|
||||
|
||||
void Core::setNospam(uint32_t nospam)
|
||||
|
|
22
src/core.h
|
@ -52,6 +52,8 @@ public:
|
|||
static QString sanitize(QString name);
|
||||
static QList<CString> splitMessage(const QString &message, int maxLen);
|
||||
|
||||
static QByteArray getSaltFromFile(QString filename);
|
||||
|
||||
QString getPeerName(const ToxID& id) const;
|
||||
|
||||
int getGroupNumberPeers(int groupId) const; ///< Return the number of peers in the group chat on success, or -1 on failure
|
||||
|
@ -83,10 +85,10 @@ public slots:
|
|||
void start(); ///< Initializes the core, must be called before anything else
|
||||
void process(); ///< Processes toxcore events and ensure we stay connected, called by its own timer
|
||||
void bootstrapDht(); ///< Connects us to the Tox network
|
||||
void switchConfiguration(const QString& profile); ///< Load a different profile and restart the core
|
||||
|
||||
void saveConfiguration();
|
||||
void saveConfiguration(const QString& path);
|
||||
void switchConfiguration(const QString& profile); ///< Load a different profile and restart the core
|
||||
|
||||
void acceptFriendRequest(const QString& userId);
|
||||
void requestFriendship(const QString& friendAddress, const QString& message);
|
||||
|
@ -137,6 +139,7 @@ public slots:
|
|||
static bool isGroupCallVolEnabled(int groupId);
|
||||
|
||||
void setPassword(QString& password, PasswordType passtype, uint8_t* salt = nullptr);
|
||||
void useOtherPassword(PasswordType type);
|
||||
void clearPassword(PasswordType passtype);
|
||||
QByteArray encryptData(const QByteArray& data, PasswordType passtype);
|
||||
QByteArray decryptData(const QByteArray& data, PasswordType passtype);
|
||||
|
@ -145,7 +148,6 @@ signals:
|
|||
void connected();
|
||||
void disconnected();
|
||||
void blockingClearContacts();
|
||||
void blockingGetPassword(QString info, int passtype, uint8_t* salt = nullptr);
|
||||
|
||||
void friendRequestReceived(const QString& userId, const QString& message);
|
||||
void friendMessageReceived(int friendId, const QString& message, bool isAction);
|
||||
|
@ -266,6 +268,8 @@ private:
|
|||
bool checkConnection();
|
||||
|
||||
bool loadConfiguration(QString path); // Returns false for a critical error, true otherwise
|
||||
bool loadEncryptedSave(QByteArray& data);
|
||||
void checkEncryptedHistory();
|
||||
void make_tox();
|
||||
void loadFriends();
|
||||
|
||||
|
@ -274,6 +278,8 @@ private:
|
|||
|
||||
void checkLastOnline(int friendId);
|
||||
|
||||
void deadifyTox();
|
||||
|
||||
private slots:
|
||||
void onFileTransferFinished(ToxFile file);
|
||||
|
||||
|
@ -294,7 +300,17 @@ private:
|
|||
QMutex fileSendMutex, messageSendMutex;
|
||||
bool ready;
|
||||
|
||||
uint8_t* pwsaltedkeys[PasswordType::ptCounter]; // use the pw's hash as the "pw"
|
||||
uint8_t* pwsaltedkeys[PasswordType::ptCounter] = {nullptr}; // use the pw's hash as the "pw"
|
||||
|
||||
// Hack for reloading current profile if switching to an encrypted one fails.
|
||||
// Testing the passwords before killing the current profile is perfectly doable,
|
||||
// however it would require major refactoring;
|
||||
// the Core class as a whole also requires major refactoring (especially to support multiple IDs at once),
|
||||
// so I'm punting on this until then, when it would get fixed anyways
|
||||
uint8_t* backupkeys[PasswordType::ptCounter] = {nullptr};
|
||||
QString* backupProfile = nullptr;
|
||||
void saveCurrentInformation();
|
||||
QString loadOldInformation();
|
||||
|
||||
static const int videobufsize;
|
||||
static uint8_t* videobuf;
|
||||
|
|
329
src/coreencryption.cpp
Normal file
|
@ -0,0 +1,329 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre 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 COPYING file for more details.
|
||||
*/
|
||||
|
||||
/* This file is part of the Core class, but was separated for my sanity */
|
||||
/* The load function delegates to loadEncrypted here, and the save function */
|
||||
/* was permanently moved here to handle encryption */
|
||||
|
||||
#include "core.h"
|
||||
#include "src/widget/gui.h"
|
||||
#include <tox/tox.h>
|
||||
#include <tox/toxencryptsave.h>
|
||||
#include "src/misc/settings.h"
|
||||
#include "misc/cstring.h"
|
||||
#include "historykeeper.h"
|
||||
#include <QApplication>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QSaveFile>
|
||||
#include <QFile>
|
||||
#include <QThread>
|
||||
|
||||
void Core::setPassword(QString& password, PasswordType passtype, uint8_t* salt)
|
||||
{
|
||||
clearPassword(passtype);
|
||||
if (password.isEmpty())
|
||||
return;
|
||||
|
||||
pwsaltedkeys[passtype] = new uint8_t[tox_pass_key_length()];
|
||||
|
||||
CString str(password);
|
||||
if (salt)
|
||||
tox_derive_key_with_salt(str.data(), str.size(), salt, pwsaltedkeys[passtype]);
|
||||
else
|
||||
tox_derive_key_from_pass(str.data(), str.size(), pwsaltedkeys[passtype]);
|
||||
|
||||
password.clear();
|
||||
}
|
||||
|
||||
#include <algorithm>
|
||||
void Core::useOtherPassword(PasswordType type)
|
||||
{
|
||||
clearPassword(type);
|
||||
pwsaltedkeys[type] = new uint8_t[tox_pass_key_length()];
|
||||
|
||||
PasswordType other = (type == ptMain) ? ptHistory : ptMain;
|
||||
|
||||
std::copy(pwsaltedkeys[other], pwsaltedkeys[other]+tox_pass_key_length(), pwsaltedkeys[type]);
|
||||
}
|
||||
|
||||
void Core::clearPassword(PasswordType passtype)
|
||||
{
|
||||
delete[] pwsaltedkeys[passtype];
|
||||
pwsaltedkeys[passtype] = nullptr;
|
||||
}
|
||||
|
||||
// part of a hack, see core.h
|
||||
void Core::saveCurrentInformation()
|
||||
{
|
||||
if (pwsaltedkeys[ptMain])
|
||||
{
|
||||
backupkeys[ptMain] = new uint8_t[tox_pass_key_length()];
|
||||
std::copy(pwsaltedkeys[ptMain], pwsaltedkeys[ptMain]+tox_pass_key_length(), backupkeys[ptMain]);
|
||||
}
|
||||
if (pwsaltedkeys[ptHistory])
|
||||
{
|
||||
backupkeys[ptHistory] = new uint8_t[tox_pass_key_length()];
|
||||
std::copy(pwsaltedkeys[ptHistory], pwsaltedkeys[ptHistory]+tox_pass_key_length(), backupkeys[ptHistory]);
|
||||
}
|
||||
backupProfile = new QString(Settings::getInstance().getCurrentProfile());
|
||||
}
|
||||
|
||||
QString Core::loadOldInformation()
|
||||
{
|
||||
QString out;
|
||||
if (backupProfile)
|
||||
{
|
||||
out = *backupProfile;
|
||||
delete backupProfile;
|
||||
backupProfile = nullptr;
|
||||
}
|
||||
backupProfile = nullptr;
|
||||
clearPassword(ptMain);
|
||||
clearPassword(ptHistory);
|
||||
// we can just copy the pointer, as long as we null out backupkeys
|
||||
// (if backupkeys was null anyways, then this is a null-op)
|
||||
pwsaltedkeys[ptMain] = backupkeys[ptMain];
|
||||
pwsaltedkeys[ptHistory] = backupkeys[ptHistory];
|
||||
backupkeys[ptMain] = nullptr;
|
||||
backupkeys[ptHistory] = nullptr;
|
||||
return out;
|
||||
}
|
||||
|
||||
QByteArray Core::encryptData(const QByteArray& data, PasswordType passtype)
|
||||
{
|
||||
if (!pwsaltedkeys[passtype])
|
||||
return QByteArray();
|
||||
uint8_t encrypted[data.size() + tox_pass_encryption_extra_length()];
|
||||
if (tox_pass_key_encrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], encrypted) == -1)
|
||||
{
|
||||
qWarning() << "Core::encryptData: encryption failed";
|
||||
return QByteArray();
|
||||
}
|
||||
return QByteArray(reinterpret_cast<char*>(encrypted), data.size() + tox_pass_encryption_extra_length());
|
||||
}
|
||||
|
||||
QByteArray Core::decryptData(const QByteArray& data, PasswordType passtype)
|
||||
{
|
||||
if (!pwsaltedkeys[passtype])
|
||||
return QByteArray();
|
||||
int sz = data.size() - tox_pass_encryption_extra_length();
|
||||
uint8_t decrypted[sz];
|
||||
int decr_size = tox_pass_key_decrypt(reinterpret_cast<const uint8_t*>(data.data()), data.size(), pwsaltedkeys[passtype], decrypted);
|
||||
if (decr_size != sz)
|
||||
{
|
||||
qWarning() << "Core::decryptData: decryption failed";
|
||||
return QByteArray();
|
||||
}
|
||||
return QByteArray(reinterpret_cast<char*>(decrypted), sz);
|
||||
}
|
||||
|
||||
bool Core::isPasswordSet(PasswordType passtype)
|
||||
{
|
||||
if (pwsaltedkeys[passtype])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray Core::getSaltFromFile(QString filename)
|
||||
{
|
||||
QFile file(filename);
|
||||
if (!file.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qWarning() << "Core: file" << filename << "doesn't exist";
|
||||
return QByteArray();
|
||||
}
|
||||
QByteArray data = file.read(tox_pass_encryption_extra_length());
|
||||
file.close();
|
||||
|
||||
uint8_t *salt = new uint8_t[tox_pass_salt_length()];
|
||||
int err = tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
||||
if (err)
|
||||
{
|
||||
qWarning() << "Core: can't get salt from" << filename << "header";
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray res = QByteArray::fromRawData(reinterpret_cast<const char*>(salt), tox_pass_salt_length());
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Core::loadEncryptedSave(QByteArray& data)
|
||||
{
|
||||
if (!Settings::getInstance().getEncryptTox())
|
||||
GUI::showWarning(tr("Encryption error"), tr("The .tox file is encrypted, but encryption was not checked, continuing regardless."));
|
||||
|
||||
int error = -1;
|
||||
QString a(tr("Please enter the password for the %1 profile.", "used in load() when no pw is already set").arg(Settings::getInstance().getCurrentProfile()));
|
||||
QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()"));
|
||||
QString dialogtxt;
|
||||
|
||||
if (pwsaltedkeys[ptMain]) // password set, try it
|
||||
{
|
||||
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
|
||||
if (!error)
|
||||
{
|
||||
Settings::getInstance().setEncryptTox(true);
|
||||
return true;
|
||||
}
|
||||
dialogtxt = tr("The profile password failed. Please try another?", "used only when pw set before load() doesn't work");
|
||||
}
|
||||
else
|
||||
dialogtxt = a;
|
||||
|
||||
uint8_t salt[tox_pass_salt_length()];
|
||||
tox_get_salt(reinterpret_cast<uint8_t *>(data.data()), salt);
|
||||
|
||||
do
|
||||
{
|
||||
QString pw = GUI::passwordDialog(tr("Change profile"), dialogtxt);
|
||||
|
||||
if (pw.isEmpty())
|
||||
{
|
||||
clearPassword(ptMain);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
setPassword(pw, ptMain, salt);
|
||||
|
||||
error = tox_encrypted_key_load(tox, reinterpret_cast<uint8_t *>(data.data()), data.size(), pwsaltedkeys[ptMain]);
|
||||
dialogtxt = a + "\n" + b;
|
||||
} while (error != 0);
|
||||
|
||||
Settings::getInstance().setEncryptTox(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Core::checkEncryptedHistory()
|
||||
{
|
||||
QString path = HistoryKeeper::getHistoryPath();
|
||||
bool exists = QFile::exists(path);
|
||||
|
||||
QByteArray salt = getSaltFromFile(path);
|
||||
if (exists && salt.size() == 0)
|
||||
{ // maybe we should handle this better
|
||||
GUI::showWarning(tr("Encrypted chat history"), tr("No encrypted chat history file found, or it was corrupted.\nHistory will be disabled!"));
|
||||
Settings::getInstance().setEncryptLogs(false);
|
||||
Settings::getInstance().setEnableLogging(false);
|
||||
HistoryKeeper::resetInstance();
|
||||
return;
|
||||
}
|
||||
|
||||
QString a(tr("Please enter the password for the chat history for the %1 profile.", "used in load() when no hist pw set").arg(Settings::getInstance().getCurrentProfile()));
|
||||
QString b(tr("The previous password is incorrect; please try again:", "used on retries in load()"));
|
||||
QString c(tr("\nDisabling chat history now will leave the encrypted history intact (but not usable); if you later remember the password, you may re-enable encryption from the Privacy tab with the correct password to use the history.", "part of history password dialog"));
|
||||
QString dialogtxt;
|
||||
|
||||
if (pwsaltedkeys[ptHistory])
|
||||
{
|
||||
if (!exists || HistoryKeeper::checkPassword())
|
||||
return;
|
||||
dialogtxt = tr("The chat history password failed. Please try another?", "used only when pw set before load() doesn't work");
|
||||
}
|
||||
else
|
||||
dialogtxt = a;
|
||||
dialogtxt += "\n" + c;
|
||||
|
||||
if (pwsaltedkeys[ptMain])
|
||||
{
|
||||
useOtherPassword(ptHistory);
|
||||
if (!exists || HistoryKeeper::checkPassword())
|
||||
{
|
||||
qDebug() << "Core: using main password for chat history";
|
||||
return;
|
||||
}
|
||||
clearPassword(ptHistory);
|
||||
}
|
||||
|
||||
bool error = true;
|
||||
do
|
||||
{
|
||||
QString pw = GUI::passwordDialog(tr("Disable chat history"), dialogtxt);
|
||||
|
||||
if (pw.isEmpty())
|
||||
{
|
||||
clearPassword(ptHistory);
|
||||
Settings::getInstance().setEncryptLogs(false);
|
||||
Settings::getInstance().setEnableLogging(false);
|
||||
HistoryKeeper::resetInstance();
|
||||
return;
|
||||
}
|
||||
else
|
||||
setPassword(pw, ptHistory, reinterpret_cast<uint8_t*>(salt.data()));
|
||||
|
||||
error = exists && !HistoryKeeper::checkPassword();
|
||||
dialogtxt = a + "\n" + c + "\n" + b;
|
||||
} while (error);
|
||||
}
|
||||
|
||||
void Core::saveConfiguration(const QString& path)
|
||||
{
|
||||
if (QThread::currentThread() != coreThread)
|
||||
return (void) QMetaObject::invokeMethod(this, "saveConfiguration", Q_ARG(const QString&, path));
|
||||
|
||||
if (!isReady())
|
||||
{
|
||||
qWarning() << "Core::saveConfiguration: Tox not started, aborting!";
|
||||
return;
|
||||
}
|
||||
|
||||
QSaveFile configurationFile(path);
|
||||
if (!configurationFile.open(QIODevice::WriteOnly)) {
|
||||
qCritical() << "File " << path << " cannot be opened";
|
||||
return;
|
||||
}
|
||||
|
||||
qDebug() << "Core: writing tox_save to " << path;
|
||||
|
||||
uint32_t fileSize; bool encrypt = Settings::getInstance().getEncryptTox();
|
||||
if (encrypt)
|
||||
fileSize = tox_encrypted_size(tox);
|
||||
else
|
||||
fileSize = tox_size(tox);
|
||||
|
||||
if (fileSize > 0 && fileSize <= std::numeric_limits<int32_t>::max()) {
|
||||
uint8_t *data = new uint8_t[fileSize];
|
||||
|
||||
if (encrypt)
|
||||
{
|
||||
if (!pwsaltedkeys[ptMain])
|
||||
{
|
||||
// probably zero chance event
|
||||
GUI::showWarning(tr("NO Password"), tr("Encryption is enabled, but there is no password! Encryption will be disabled."));
|
||||
Settings::getInstance().setEncryptTox(false);
|
||||
tox_save(tox, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
int ret = tox_encrypted_key_save(tox, data, pwsaltedkeys[ptMain]);
|
||||
if (ret == -1)
|
||||
{
|
||||
qCritical() << "Core::saveConfiguration: encryption of save file failed!!!";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
tox_save(tox, data);
|
||||
|
||||
configurationFile.write(reinterpret_cast<char *>(data), fileSize);
|
||||
configurationFile.commit();
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
Settings::getInstance().save();
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
#include "friendlist.h"
|
||||
#include "widget/friendwidget.h"
|
||||
#include "widget/form/chatform.h"
|
||||
#include "widget/widget.h"
|
||||
#include "widget/gui.h"
|
||||
#include "src/core.h"
|
||||
#include "src/misc/settings.h"
|
||||
|
||||
|
@ -52,7 +52,7 @@ void Friend::setName(QString name)
|
|||
chatForm->setName(name);
|
||||
|
||||
if (widget->isActive())
|
||||
Widget::getInstance()->setWindowTitle(name);
|
||||
GUI::setWindowTitle(name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ void Friend::setAlias(QString name)
|
|||
chatForm->setName(dispName);
|
||||
|
||||
if (widget->isActive())
|
||||
Widget::getInstance()->setWindowTitle(dispName);
|
||||
GUI::setWindowTitle(dispName);
|
||||
}
|
||||
|
||||
void Friend::setStatusMessage(QString message)
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "friendlist.h"
|
||||
#include "friend.h"
|
||||
#include "core.h"
|
||||
#include "widget/widget.h"
|
||||
#include "widget/gui.h"
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
|
||||
|
@ -90,7 +90,7 @@ void Group::setName(const QString& name)
|
|||
chatForm->setName(name);
|
||||
|
||||
if (widget->isActive())
|
||||
Widget::getInstance()->setWindowTitle(name);
|
||||
GUI::setWindowTitle(name);
|
||||
}
|
||||
|
||||
void Group::regeneratePeerList()
|
||||
|
|
|
@ -68,20 +68,15 @@ HistoryKeeper *HistoryKeeper::getInstance()
|
|||
return historyInstance;
|
||||
}
|
||||
|
||||
bool HistoryKeeper::checkPassword()
|
||||
bool HistoryKeeper::checkPassword(int encrypted)
|
||||
{
|
||||
if (Settings::getInstance().getEnableLogging())
|
||||
{
|
||||
if (Settings::getInstance().getEncryptLogs())
|
||||
{
|
||||
QString dbpath = getHistoryPath();
|
||||
return EncryptedDb::check(dbpath);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!Settings::getInstance().getEnableLogging() && (encrypted == -1))
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((encrypted == 1) || (encrypted == -1 && Settings::getInstance().getEncryptLogs()))
|
||||
return EncryptedDb::check(getHistoryPath(Settings::getInstance().getCurrentProfile(), encrypted));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
||||
|
@ -132,9 +127,10 @@ HistoryKeeper::HistoryKeeper(GenericDdInterface *db_) :
|
|||
|
||||
setSyncType(Settings::getInstance().getDbSyncType());
|
||||
|
||||
messageID = 0;
|
||||
QSqlQuery sqlAnswer = db->exec("select seq from sqlite_sequence where name=\"history\";");
|
||||
sqlAnswer.first();
|
||||
messageID = sqlAnswer.value(0).toInt();
|
||||
if (sqlAnswer.first())
|
||||
messageID = sqlAnswer.value(0).toLongLong();
|
||||
}
|
||||
|
||||
HistoryKeeper::~HistoryKeeper()
|
||||
|
@ -142,16 +138,13 @@ HistoryKeeper::~HistoryKeeper()
|
|||
delete db;
|
||||
}
|
||||
|
||||
int HistoryKeeper::addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent)
|
||||
qint64 HistoryKeeper::addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent)
|
||||
{
|
||||
int chat_id = getChatID(chat, ctSingle).first;
|
||||
int sender_id = getAliasID(sender);
|
||||
QList<QString> cmds = generateAddChatEntryCmd(chat, message, sender, dt, isSent);
|
||||
|
||||
db->exec("BEGIN TRANSACTION;");
|
||||
db->exec(QString("INSERT INTO history (timestamp, chat_id, sender, message) ") +
|
||||
QString("VALUES (%1, %2, %3, '%4');")
|
||||
.arg(dt.toMSecsSinceEpoch()).arg(chat_id).arg(sender_id).arg(wrapMessage(message)));
|
||||
db->exec(QString("INSERT INTO sent_status (status) VALUES (%1);").arg(isSent));
|
||||
for (auto &it : cmds)
|
||||
db->exec(it);
|
||||
db->exec("COMMIT TRANSACTION;");
|
||||
|
||||
messageID++;
|
||||
|
@ -190,12 +183,66 @@ QList<HistoryKeeper::HistMessage> HistoryKeeper::getChatHistory(HistoryKeeper::C
|
|||
|
||||
QDateTime time = QDateTime::fromMSecsSinceEpoch(timeInt);
|
||||
|
||||
res.push_back({id, sender,message,time,isSent});
|
||||
res.push_back(HistMessage(id, "", sender, message, time, isSent));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
QList<HistoryKeeper::HistMessage> HistoryKeeper::exportMessages()
|
||||
{
|
||||
QSqlQuery dbAnswer;
|
||||
dbAnswer = db->exec(QString("SELECT history.id, timestamp, user_id, message, status, name FROM history LEFT JOIN sent_status ON history.id = sent_status.id ") +
|
||||
QString("INNER JOIN aliases ON history.sender = aliases.id INNER JOIN chats ON history.chat_id = chats.id;"));
|
||||
|
||||
QList<HistMessage> res;
|
||||
|
||||
while (dbAnswer.next())
|
||||
{
|
||||
qint64 id = dbAnswer.value(0).toLongLong();
|
||||
qint64 timeInt = dbAnswer.value(1).toLongLong();
|
||||
QString sender = dbAnswer.value(2).toString();
|
||||
QString message = unWrapMessage(dbAnswer.value(3).toString());
|
||||
bool isSent = true;
|
||||
if (!dbAnswer.value(4).isNull())
|
||||
isSent = dbAnswer.value(4).toBool();
|
||||
QString chat = dbAnswer.value(5).toString();
|
||||
QDateTime time = QDateTime::fromMSecsSinceEpoch(timeInt);
|
||||
|
||||
res.push_back(HistMessage(id, chat, sender, message, time, isSent));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void HistoryKeeper::importMessages(const QList<HistoryKeeper::HistMessage> &lst)
|
||||
{
|
||||
db->exec("BEGIN TRANSACTION;");
|
||||
for (const HistMessage &msg : lst)
|
||||
{
|
||||
QList<QString> cmds = generateAddChatEntryCmd(msg.chat, msg.message, msg.sender, msg.timestamp, msg.isSent);
|
||||
for (auto &it : cmds)
|
||||
db->exec(it);
|
||||
|
||||
messageID++;
|
||||
}
|
||||
db->exec("COMMIT TRANSACTION;");
|
||||
}
|
||||
|
||||
QList<QString> HistoryKeeper::generateAddChatEntryCmd(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent)
|
||||
{
|
||||
QList<QString> cmds;
|
||||
|
||||
int chat_id = getChatID(chat, ctSingle).first;
|
||||
int sender_id = getAliasID(sender);
|
||||
|
||||
cmds.push_back(QString("INSERT INTO history (timestamp, chat_id, sender, message) VALUES (%1, %2, %3, '%4');")
|
||||
.arg(dt.toMSecsSinceEpoch()).arg(chat_id).arg(sender_id).arg(wrapMessage(message)));
|
||||
cmds.push_back(QString("INSERT INTO sent_status (status) VALUES (%1);").arg(isSent));
|
||||
|
||||
return cmds;
|
||||
}
|
||||
|
||||
QString HistoryKeeper::wrapMessage(const QString &str)
|
||||
{
|
||||
QString wrappedMessage(str);
|
||||
|
@ -272,7 +319,7 @@ void HistoryKeeper::resetInstance()
|
|||
historyInstance = nullptr;
|
||||
}
|
||||
|
||||
int HistoryKeeper::addGroupChatEntry(const QString &chat, const QString &message, const QString &sender, const QDateTime &dt)
|
||||
qint64 HistoryKeeper::addGroupChatEntry(const QString &chat, const QString &message, const QString &sender, const QDateTime &dt)
|
||||
{
|
||||
Q_UNUSED(chat)
|
||||
Q_UNUSED(message)
|
||||
|
@ -342,3 +389,29 @@ void HistoryKeeper::setSyncType(Db::syncType sType)
|
|||
|
||||
db->exec(QString("PRAGMA synchronous=%1;").arg(syncCmd));
|
||||
}
|
||||
|
||||
bool HistoryKeeper::isFileExist()
|
||||
{
|
||||
QString path = getHistoryPath();
|
||||
QFile file(path);
|
||||
|
||||
return file.exists();
|
||||
}
|
||||
|
||||
bool HistoryKeeper::removeHistory(int encrypted)
|
||||
{
|
||||
resetInstance();
|
||||
|
||||
QString path = getHistoryPath(QString(), encrypted);
|
||||
QFile DbFile(path);
|
||||
return DbFile.remove();
|
||||
}
|
||||
|
||||
QList<HistoryKeeper::HistMessage> HistoryKeeper::exportMessagesDeleteFile(int encrypted)
|
||||
{
|
||||
auto msgs = getInstance()->exportMessages();
|
||||
qDebug() << "HistoryKeeper: count" << msgs.size() << "messages exported";
|
||||
if (!removeHistory(encrypted))
|
||||
qWarning() << "HistoryKeeper: couldn't delete old log file!";
|
||||
return msgs;
|
||||
}
|
||||
|
|
|
@ -31,7 +31,11 @@ public:
|
|||
|
||||
struct HistMessage
|
||||
{
|
||||
HistMessage(qint64 id, QString chat, QString sender, QString message, QDateTime timestamp, bool isSent) :
|
||||
id(id), chat(chat), sender(sender), message(message), timestamp(timestamp), isSent(isSent) {}
|
||||
|
||||
qint64 id;
|
||||
QString chat;
|
||||
QString sender;
|
||||
QString message;
|
||||
QDateTime timestamp;
|
||||
|
@ -44,14 +48,20 @@ public:
|
|||
static void resetInstance();
|
||||
|
||||
static QString getHistoryPath(QString currentProfile = QString(), int encrypted = -1); // -1 defaults to checking settings, 0 or 1 to specify
|
||||
static bool checkPassword();
|
||||
static bool checkPassword(int encrypted = -1);
|
||||
static bool isFileExist();
|
||||
static void renameHistory(QString from, QString to);
|
||||
static bool removeHistory(int encrypted = -1);
|
||||
static QList<HistMessage> exportMessagesDeleteFile(int encrypted = -1);
|
||||
|
||||
int addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent);
|
||||
int addGroupChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt);
|
||||
qint64 addChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent);
|
||||
qint64 addGroupChatEntry(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt);
|
||||
QList<HistMessage> getChatHistory(ChatType ct, const QString &chat, const QDateTime &time_from, const QDateTime &time_to);
|
||||
void markAsSent(int m_id);
|
||||
|
||||
QList<HistMessage> exportMessages();
|
||||
void importMessages(const QList<HistoryKeeper::HistMessage> &lst);
|
||||
|
||||
void setSyncType(Db::syncType sType);
|
||||
|
||||
private:
|
||||
|
@ -63,15 +73,16 @@ private:
|
|||
void updateAliases();
|
||||
QPair<int, ChatType> getChatID(const QString &id_str, ChatType ct);
|
||||
int getAliasID(const QString &id_str);
|
||||
QString wrapMessage(const QString &str);
|
||||
QString unWrapMessage(const QString &str);
|
||||
QList<QString> generateAddChatEntryCmd(const QString& chat, const QString& message, const QString& sender, const QDateTime &dt, bool isSent);
|
||||
|
||||
static QString wrapMessage(const QString &str);
|
||||
static QString unWrapMessage(const QString &str);
|
||||
static ChatType convertToChatType(int);
|
||||
ChatType convertToChatType(int);
|
||||
|
||||
GenericDdInterface *db;
|
||||
QMap<QString, int> aliases;
|
||||
QMap<QString, QPair<int, ChatType>> chats;
|
||||
int messageID;
|
||||
qint64 messageID;
|
||||
};
|
||||
|
||||
#endif // HISTORYKEEPER_H
|
||||
|
|
12
src/main.cpp
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include "widget/widget.h"
|
||||
#include "misc/settings.h"
|
||||
#include "src/nexus.h"
|
||||
#include "src/ipc.h"
|
||||
#include "src/widget/toxuri.h"
|
||||
#include "src/widget/toxsave.h"
|
||||
|
@ -70,13 +71,14 @@ int main(int argc, char *argv[])
|
|||
parser.process(a);
|
||||
|
||||
Settings::getInstance(); // Build our Settings singleton as soon as QApplication is ready, not before
|
||||
|
||||
if (parser.isSet("p"))
|
||||
{
|
||||
QString profile = parser.value("p");
|
||||
if (QDir(Settings::getSettingsDirPath()).exists(profile + ".tox"))
|
||||
{
|
||||
qDebug() << "Setting profile to" << profile;
|
||||
Settings::getInstance().setCurrentProfile(profile);
|
||||
Settings::getInstance().switchProfile(profile);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -121,6 +123,9 @@ int main(int argc, char *argv[])
|
|||
AutoUpdater::installLocalUpdate(); ///< NORETURN
|
||||
#endif
|
||||
|
||||
Nexus::getInstance().start();
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
// Inter-process communication
|
||||
IPC ipc;
|
||||
ipc.registerEventHandler(&toxURIEventHandler);
|
||||
|
@ -175,17 +180,18 @@ int main(int argc, char *argv[])
|
|||
if (!ipc.isCurrentOwner())
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Run
|
||||
a.setQuitOnLastWindowClosed(false);
|
||||
Widget* w = Widget::getInstance();
|
||||
int errorcode = a.exec();
|
||||
|
||||
delete w;
|
||||
#ifdef LOG_TO_FILE
|
||||
delete logFile;
|
||||
logFile = nullptr;
|
||||
#endif
|
||||
|
||||
Nexus::destroyInstance();
|
||||
|
||||
return errorcode;
|
||||
}
|
||||
|
|
|
@ -980,6 +980,10 @@ QSplitter:handle{
|
|||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../res.qrc">
|
||||
<normaloff>:/img/status/dot_away.png</normaloff>:/img/status/dot_away.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>10</width>
|
||||
|
@ -1775,7 +1779,7 @@ QSplitter:handle{
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>775</width>
|
||||
<height>21</height>
|
||||
<height>20</height>
|
||||
</rect>
|
||||
</property>
|
||||
</widget>
|
||||
|
|
|
@ -24,28 +24,36 @@
|
|||
#include <QDebug>
|
||||
#include <QSqlError>
|
||||
|
||||
qint64 EncryptedDb::plainChunkSize = 4096;
|
||||
qint64 EncryptedDb::encryptedChunkSize = EncryptedDb::plainChunkSize + tox_pass_encryption_extra_length();
|
||||
qint64 EncryptedDb::encryptedChunkSize = 4096;
|
||||
qint64 EncryptedDb::plainChunkSize = EncryptedDb::encryptedChunkSize - tox_pass_encryption_extra_length();
|
||||
|
||||
EncryptedDb::EncryptedDb(const QString &fname, QList<QString> initList) :
|
||||
PlainDb(":memory:", initList), encrFile(fname)
|
||||
PlainDb(":memory:", initList), fileName(fname)
|
||||
{
|
||||
QByteArray fileContent;
|
||||
if (pullFileContent())
|
||||
if (pullFileContent(fileName, buffer))
|
||||
{
|
||||
chunkPosition = encrFile.size() / encryptedChunkSize;
|
||||
|
||||
encrFile.seek(0);
|
||||
qDebug() << "EncryptedDb: writing old data";
|
||||
encrFile.setFileName(fileName);
|
||||
encrFile.open(QIODevice::ReadOnly);
|
||||
fileContent = encrFile.readAll();
|
||||
} else {
|
||||
qWarning() << "corrupted history log file will be wiped!";
|
||||
chunkPosition = 0;
|
||||
chunkPosition = encrFile.size() / encryptedChunkSize;
|
||||
encrFile.close();
|
||||
}
|
||||
else
|
||||
chunkPosition = 0;
|
||||
|
||||
encrFile.close();
|
||||
encrFile.open(QIODevice::WriteOnly);
|
||||
encrFile.write(fileContent);
|
||||
encrFile.flush();
|
||||
encrFile.setFileName(fileName);
|
||||
|
||||
if (!encrFile.open(QIODevice::WriteOnly))
|
||||
{
|
||||
qWarning() << "can't open file:" << fileName;
|
||||
}
|
||||
else
|
||||
{
|
||||
encrFile.write(fileContent);
|
||||
encrFile.flush();
|
||||
}
|
||||
}
|
||||
|
||||
EncryptedDb::~EncryptedDb()
|
||||
|
@ -57,27 +65,34 @@ EncryptedDb::~EncryptedDb()
|
|||
QSqlQuery EncryptedDb::exec(const QString &query)
|
||||
{
|
||||
QSqlQuery retQSqlQuery = PlainDb::exec(query);
|
||||
if (query.startsWith("INSERT", Qt::CaseInsensitive))
|
||||
if (checkCmd(query))
|
||||
appendToEncrypted(query);
|
||||
|
||||
return retQSqlQuery;
|
||||
}
|
||||
|
||||
bool EncryptedDb::pullFileContent()
|
||||
bool EncryptedDb::pullFileContent(const QString &fname, QByteArray &buf)
|
||||
{
|
||||
encrFile.open(QIODevice::ReadOnly);
|
||||
QFile dbFile(fname);
|
||||
if (!dbFile.open(QIODevice::ReadOnly))
|
||||
{
|
||||
qDebug() << "EncryptedDb::pullFileContent: file doesn't exist";
|
||||
buf = QByteArray();
|
||||
return false;
|
||||
}
|
||||
QByteArray fileContent;
|
||||
|
||||
while (!encrFile.atEnd())
|
||||
while (!dbFile.atEnd())
|
||||
{
|
||||
QByteArray encrChunk = encrFile.read(encryptedChunkSize);
|
||||
buffer = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
|
||||
if (buffer.size() > 0)
|
||||
QByteArray encrChunk = dbFile.read(encryptedChunkSize);
|
||||
qDebug() << "EncryptedDb::pullFileContent: got chunk:" << encrChunk.size();
|
||||
buf = Core::getInstance()->decryptData(encrChunk, Core::ptHistory);
|
||||
if (buf.size() > 0)
|
||||
{
|
||||
fileContent += buffer;
|
||||
fileContent += buf;
|
||||
} else {
|
||||
qWarning() << "Encrypted history log is corrupted: can't decrypt";
|
||||
buffer = QByteArray();
|
||||
qWarning() << "EncryptedDb::pullFileContent: Encrypted history log is corrupted: can't decrypt, will be deleted";
|
||||
buf = QByteArray();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -88,31 +103,17 @@ bool EncryptedDb::pullFileContent()
|
|||
for (auto ba_line : splittedBA)
|
||||
{
|
||||
QString line = QByteArray::fromBase64(ba_line);
|
||||
if (line.size() == 0)
|
||||
continue;
|
||||
|
||||
bool isGoodLine = false;
|
||||
if (line.startsWith("CREATE", Qt::CaseInsensitive) || line.startsWith("INSERT", Qt::CaseInsensitive))
|
||||
{
|
||||
if (line.endsWith(");"))
|
||||
{
|
||||
sqlCmds.append(line);
|
||||
isGoodLine = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isGoodLine)
|
||||
{
|
||||
qWarning() << "Encrypted history log is corrupted: errors in content";
|
||||
buffer = QByteArray();
|
||||
return false;
|
||||
}
|
||||
sqlCmds.append(line);
|
||||
}
|
||||
|
||||
PlainDb::exec("BEGIN TRANSACTION;");
|
||||
for (auto line : sqlCmds)
|
||||
{
|
||||
QSqlQuery r = PlainDb::exec(line);
|
||||
}
|
||||
PlainDb::exec("COMMIT TRANSACTION;");
|
||||
|
||||
dbFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -133,6 +134,7 @@ void EncryptedDb::appendToEncrypted(const QString &sql)
|
|||
if (encr.size() > 0)
|
||||
{
|
||||
encrFile.write(encr);
|
||||
encrFile.flush();
|
||||
}
|
||||
|
||||
buffer = buffer.right(buffer.size() - plainChunkSize);
|
||||
|
@ -170,3 +172,14 @@ bool EncryptedDb::check(const QString &fname)
|
|||
file.close();
|
||||
return state;
|
||||
}
|
||||
|
||||
bool EncryptedDb::checkCmd(const QString &cmd)
|
||||
{
|
||||
if (cmd.startsWith("INSERT", Qt::CaseInsensitive) || cmd.startsWith("UPDATE", Qt::CaseInsensitive)
|
||||
|| cmd.startsWith("DELETE", Qt::CaseInsensitive))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -32,10 +32,12 @@ public:
|
|||
static bool check(const QString &fname);
|
||||
|
||||
private:
|
||||
bool pullFileContent();
|
||||
bool pullFileContent(const QString& fname, QByteArray &buf);
|
||||
void appendToEncrypted(const QString &sql);
|
||||
bool checkCmd(const QString &cmd);
|
||||
|
||||
QFile encrFile;
|
||||
QString fileName;
|
||||
|
||||
static qint64 plainChunkSize;
|
||||
static qint64 encryptedChunkSize;
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
#include "smileypack.h"
|
||||
#include "src/corestructs.h"
|
||||
#include "src/misc/db/plaindb.h"
|
||||
#include "src/core.h"
|
||||
#include "src/widget/gui.h"
|
||||
|
||||
#include <QFont>
|
||||
#include <QApplication>
|
||||
|
@ -58,6 +60,77 @@ void Settings::resetInstance()
|
|||
}
|
||||
}
|
||||
|
||||
void Settings::switchProfile(const QString& profile)
|
||||
{
|
||||
setCurrentProfile(profile);
|
||||
save(false);
|
||||
resetInstance();
|
||||
}
|
||||
|
||||
QString Settings::detectProfile()
|
||||
{
|
||||
QDir dir(getSettingsDirPath());
|
||||
QString path, profile = getCurrentProfile();
|
||||
path = dir.filePath(profile + Core::TOX_EXT);
|
||||
QFile file(path);
|
||||
if (profile.isEmpty() || !file.exists())
|
||||
{
|
||||
setCurrentProfile("");
|
||||
#if 1 // deprecation attempt
|
||||
// if the last profile doesn't exist, fall back to old "data"
|
||||
path = dir.filePath(Core::CONFIG_FILE_NAME);
|
||||
QFile file(path);
|
||||
if (file.exists())
|
||||
return path;
|
||||
else if (QFile(path = dir.filePath("tox_save")).exists()) // also import tox_save if no data
|
||||
return path;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
profile = askProfiles();
|
||||
if (profile.isEmpty())
|
||||
return "";
|
||||
else
|
||||
{
|
||||
switchProfile(profile);
|
||||
return dir.filePath(profile + Core::TOX_EXT);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
return path;
|
||||
}
|
||||
|
||||
QList<QString> Settings::searchProfiles()
|
||||
{
|
||||
QList<QString> out;
|
||||
QDir dir(getSettingsDirPath());
|
||||
dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
|
||||
dir.setNameFilters(QStringList("*.tox"));
|
||||
for (QFileInfo file : dir.entryInfoList())
|
||||
out += file.completeBaseName();
|
||||
return out;
|
||||
}
|
||||
|
||||
QString Settings::askProfiles()
|
||||
{ // TODO: allow user to create new Tox ID, even if a profile already exists
|
||||
QList<QString> profiles = searchProfiles();
|
||||
if (profiles.empty()) return "";
|
||||
bool ok;
|
||||
QString profile = GUI::itemInputDialog(nullptr,
|
||||
tr("Choose a profile"),
|
||||
tr("Please choose which identity to use"),
|
||||
profiles,
|
||||
0, // which slot to start on
|
||||
false, // if the user can enter their own input
|
||||
&ok);
|
||||
if (!ok) // user cancelled
|
||||
return "";
|
||||
else
|
||||
return profile;
|
||||
}
|
||||
|
||||
|
||||
void Settings::load()
|
||||
{
|
||||
if (loaded)
|
||||
|
@ -186,13 +259,6 @@ void Settings::load()
|
|||
splitterState = s.value("splitterState", QByteArray()).toByteArray();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Privacy");
|
||||
typingNotification = s.value("typingNotification", true).toBool();
|
||||
enableLogging = s.value("enableLogging", false).toBool();
|
||||
encryptLogs = s.value("encryptLogs", false).toBool();
|
||||
encryptTox = s.value("encryptTox", false).toBool();
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Audio");
|
||||
inDev = s.value("inDev", "").toString();
|
||||
outDev = s.value("outDev", "").toString();
|
||||
|
@ -225,38 +291,45 @@ void Settings::load()
|
|||
|
||||
loaded = true;
|
||||
|
||||
if (currentProfile.isEmpty()) // new profile in Core::switchConfiguration
|
||||
return;
|
||||
if (!currentProfile.isEmpty()) // new profile in Core::switchConfiguration
|
||||
{
|
||||
// load from a profile specific friend data list if possible
|
||||
QString tmp = dir.filePath(currentProfile + ".ini");
|
||||
if (QFile(tmp).exists()) // otherwise, filePath remains the global file
|
||||
filePath = tmp;
|
||||
|
||||
// load from a profile specific friend data list if possible
|
||||
QString tmp = dir.filePath(currentProfile + ".ini");
|
||||
if (QFile(tmp).exists())
|
||||
filePath = tmp;
|
||||
QSettings ps(filePath, QSettings::IniFormat);
|
||||
friendLst.clear();
|
||||
ps.beginGroup("Friends");
|
||||
int size = ps.beginReadArray("Friend");
|
||||
for (int i = 0; i < size; i ++)
|
||||
{
|
||||
ps.setArrayIndex(i);
|
||||
friendProp fp;
|
||||
fp.addr = ps.value("addr").toString();
|
||||
fp.alias = ps.value("alias").toString();
|
||||
fp.autoAcceptDir = ps.value("autoAcceptDir").toString();
|
||||
friendLst[ToxID::fromString(fp.addr).publicKey] = fp;
|
||||
}
|
||||
ps.endArray();
|
||||
ps.endGroup();
|
||||
|
||||
QSettings fs(filePath, QSettings::IniFormat);
|
||||
friendLst.clear();
|
||||
fs.beginGroup("Friends");
|
||||
int size = fs.beginReadArray("Friend");
|
||||
for (int i = 0; i < size; i ++)
|
||||
{
|
||||
fs.setArrayIndex(i);
|
||||
friendProp fp;
|
||||
fp.addr = fs.value("addr").toString();
|
||||
fp.alias = fs.value("alias").toString();
|
||||
fp.autoAcceptDir = fs.value("autoAcceptDir").toString();
|
||||
friendLst[ToxID::fromString(fp.addr).publicKey] = fp;
|
||||
}
|
||||
fs.endArray();
|
||||
fs.endGroup();
|
||||
ps.beginGroup("Privacy");
|
||||
typingNotification = ps.value("typingNotification", false).toBool();
|
||||
enableLogging = ps.value("enableLogging", false).toBool();
|
||||
encryptLogs = ps.value("encryptLogs", false).toBool();
|
||||
encryptTox = ps.value("encryptTox", false).toBool();
|
||||
ps.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void Settings::save(bool writeFriends)
|
||||
void Settings::save(bool writePersonal)
|
||||
{
|
||||
QString filePath = QDir(getSettingsDirPath()).filePath(FILENAME);
|
||||
save(filePath, writeFriends);
|
||||
save(filePath, writePersonal);
|
||||
}
|
||||
|
||||
void Settings::save(QString path, bool writeFriends)
|
||||
void Settings::save(QString path, bool writePersonal)
|
||||
{
|
||||
qDebug() << "Settings: Saving in "<<path;
|
||||
|
||||
|
@ -336,13 +409,6 @@ void Settings::save(QString path, bool writeFriends)
|
|||
s.setValue("splitterState", splitterState);
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Privacy");
|
||||
s.setValue("typingNotification", typingNotification);
|
||||
s.setValue("enableLogging", enableLogging);
|
||||
s.setValue("encryptLogs", encryptLogs);
|
||||
s.setValue("encryptTox", encryptTox);
|
||||
s.endGroup();
|
||||
|
||||
s.beginGroup("Audio");
|
||||
s.setValue("inDev", inDev);
|
||||
s.setValue("outDev", outDev);
|
||||
|
@ -353,23 +419,30 @@ void Settings::save(QString path, bool writeFriends)
|
|||
s.setValue("camVideoRes",camVideoRes);
|
||||
s.endGroup();
|
||||
|
||||
if (!writeFriends || currentProfile.isEmpty()) // Core::switchConfiguration
|
||||
return;
|
||||
if (writePersonal && !currentProfile.isEmpty()) // Core::switchConfiguration
|
||||
{
|
||||
QSettings ps(QFileInfo(path).dir().filePath(currentProfile + ".ini"), QSettings::IniFormat);
|
||||
ps.beginGroup("Friends");
|
||||
ps.beginWriteArray("Friend", friendLst.size());
|
||||
int index = 0;
|
||||
for (auto& frnd : friendLst)
|
||||
{
|
||||
ps.setArrayIndex(index);
|
||||
ps.setValue("addr", frnd.addr);
|
||||
ps.setValue("alias", frnd.alias);
|
||||
ps.setValue("autoAcceptDir", frnd.autoAcceptDir);
|
||||
index++;
|
||||
}
|
||||
ps.endArray();
|
||||
ps.endGroup();
|
||||
|
||||
QSettings fs(QFileInfo(path).dir().filePath(currentProfile + ".ini"), QSettings::IniFormat);
|
||||
fs.beginGroup("Friends");
|
||||
fs.beginWriteArray("Friend", friendLst.size());
|
||||
int index = 0;
|
||||
for (auto& frnd : friendLst)
|
||||
{
|
||||
fs.setArrayIndex(index);
|
||||
fs.setValue("addr", frnd.addr);
|
||||
fs.setValue("alias", frnd.alias);
|
||||
fs.setValue("autoAcceptDir", frnd.autoAcceptDir);
|
||||
index++;
|
||||
}
|
||||
fs.endArray();
|
||||
fs.endGroup();
|
||||
ps.beginGroup("Privacy");
|
||||
ps.setValue("typingNotification", typingNotification);
|
||||
ps.setValue("enableLogging", enableLogging);
|
||||
ps.setValue("encryptLogs", encryptLogs);
|
||||
ps.setValue("encryptTox", encryptTox);
|
||||
ps.endGroup();
|
||||
}
|
||||
}
|
||||
|
||||
QString Settings::getSettingsDirPath()
|
||||
|
|
|
@ -32,6 +32,10 @@ class Settings : public QObject
|
|||
public:
|
||||
static Settings& getInstance();
|
||||
static void resetInstance();
|
||||
void switchProfile(const QString& profile);
|
||||
QString detectProfile();
|
||||
QList<QString> searchProfiles();
|
||||
QString askProfiles();
|
||||
~Settings() = default;
|
||||
|
||||
void executeSettingsDialog(QWidget* parent);
|
||||
|
@ -236,8 +240,8 @@ public:
|
|||
void setCompactLayout(bool compact);
|
||||
|
||||
public:
|
||||
void save(bool writeFriends = true);
|
||||
void save(QString path, bool writeFriends = true);
|
||||
void save(bool writePersonal = true);
|
||||
void save(QString path, bool writePersonal = true);
|
||||
void load();
|
||||
|
||||
private:
|
||||
|
|
|
@ -16,10 +16,7 @@
|
|||
|
||||
#include "style.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "src/widget/widget.h"
|
||||
#include "ui_mainwindow.h"
|
||||
#include "src/widget/genericchatroomwidget.h"
|
||||
#include "src/widget/gui.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
@ -198,11 +195,9 @@ void Style::setThemeColor(QColor color)
|
|||
dict["@themeMediumDark"] = getColor(ThemeMediumDark).name();
|
||||
dict["@themeMedium"] = getColor(ThemeMedium).name();
|
||||
dict["@themeLight"] = getColor(ThemeLight).name();
|
||||
|
||||
applyTheme();
|
||||
}
|
||||
|
||||
void Style::applyTheme()
|
||||
{
|
||||
Widget::getInstance()->reloadTheme();
|
||||
GUI::reloadTheme();
|
||||
}
|
||||
|
|
156
src/nexus.cpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
#include "nexus.h"
|
||||
#include "core.h"
|
||||
#include "misc/settings.h"
|
||||
#include "video/camera.h"
|
||||
#include "widget/gui.h"
|
||||
#include <QThread>
|
||||
#include <QDebug>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <src/widget/androidgui.h>
|
||||
#else
|
||||
#include <src/widget/widget.h>
|
||||
#endif
|
||||
|
||||
static Nexus* nexus{nullptr};
|
||||
|
||||
Nexus::Nexus(QObject *parent) :
|
||||
QObject(parent),
|
||||
core{nullptr},
|
||||
coreThread{nullptr},
|
||||
widget{nullptr},
|
||||
androidgui{nullptr},
|
||||
started{false}
|
||||
{
|
||||
}
|
||||
|
||||
Nexus::~Nexus()
|
||||
{
|
||||
delete core;
|
||||
delete coreThread;
|
||||
#ifdef Q_OS_ANDROID
|
||||
delete androidgui;
|
||||
#else
|
||||
delete widget;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Nexus::start()
|
||||
{
|
||||
if (started)
|
||||
return;
|
||||
qDebug() << "Nexus: Starting up";
|
||||
|
||||
// Setup the environment
|
||||
qRegisterMetaType<Status>("Status");
|
||||
qRegisterMetaType<vpx_image>("vpx_image");
|
||||
qRegisterMetaType<uint8_t>("uint8_t");
|
||||
qRegisterMetaType<uint16_t>("uint16_t");
|
||||
qRegisterMetaType<const int16_t*>("const int16_t*");
|
||||
qRegisterMetaType<int32_t>("int32_t");
|
||||
qRegisterMetaType<int64_t>("int64_t");
|
||||
qRegisterMetaType<QPixmap>("QPixmap");
|
||||
qRegisterMetaType<ToxFile>("ToxFile");
|
||||
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
||||
qRegisterMetaType<Core::PasswordType>("Core::PasswordType");
|
||||
|
||||
// Create Core
|
||||
QString profilePath = Settings::getInstance().detectProfile();
|
||||
coreThread = new QThread(this);
|
||||
coreThread->setObjectName("qTox Core");
|
||||
core = new Core(Camera::getInstance(), coreThread, profilePath);
|
||||
core->moveToThread(coreThread);
|
||||
connect(coreThread, &QThread::started, core, &Core::start);
|
||||
|
||||
// Start GUI
|
||||
#ifdef Q_OS_ANDROID
|
||||
androidgui = new AndroidGUI;
|
||||
androidgui->show();
|
||||
#else
|
||||
widget = Widget::getInstance();
|
||||
#endif
|
||||
GUI::getInstance();
|
||||
|
||||
// Connections
|
||||
#ifdef Q_OS_ANDROID
|
||||
connect(core, &Core::connected, androidgui, &AndroidGUI::onConnected);
|
||||
connect(core, &Core::disconnected, androidgui, &AndroidGUI::onDisconnected);
|
||||
//connect(core, &Core::failedToStart, androidgui, &AndroidGUI::onFailedToStartCore);
|
||||
//connect(core, &Core::badProxy, androidgui, &AndroidGUI::onBadProxyCore);
|
||||
connect(core, &Core::statusSet, androidgui, &AndroidGUI::onStatusSet);
|
||||
connect(core, &Core::usernameSet, androidgui, &AndroidGUI::setUsername);
|
||||
connect(core, &Core::statusMessageSet, androidgui, &AndroidGUI::setStatusMessage);
|
||||
connect(core, &Core::selfAvatarChanged, androidgui, &AndroidGUI::onSelfAvatarLoaded);
|
||||
|
||||
connect(androidgui, &AndroidGUI::statusSet, core, &Core::setStatus);
|
||||
//connect(androidgui, &AndroidGUI::friendRequested, core, &Core::requestFriendship);
|
||||
//connect(androidgui, &AndroidGUI::friendRequestAccepted, core, &Core::acceptFriendRequest);
|
||||
//connect(androidgui, &AndroidGUI::changeProfile, core, &Core::switchConfiguration);
|
||||
#else
|
||||
connect(core, &Core::connected, widget, &Widget::onConnected);
|
||||
connect(core, &Core::disconnected, widget, &Widget::onDisconnected);
|
||||
connect(core, &Core::failedToStart, widget, &Widget::onFailedToStartCore);
|
||||
connect(core, &Core::badProxy, widget, &Widget::onBadProxyCore);
|
||||
connect(core, &Core::statusSet, widget, &Widget::onStatusSet);
|
||||
connect(core, &Core::usernameSet, widget, &Widget::setUsername);
|
||||
connect(core, &Core::statusMessageSet, widget, &Widget::setStatusMessage);
|
||||
connect(core, &Core::selfAvatarChanged, widget, &Widget::onSelfAvatarLoaded);
|
||||
connect(core, &Core::friendAdded, widget, &Widget::addFriend);
|
||||
connect(core, &Core::failedToAddFriend, widget, &Widget::addFriendFailed);
|
||||
connect(core, &Core::friendUsernameChanged, widget, &Widget::onFriendUsernameChanged);
|
||||
connect(core, &Core::friendStatusChanged, widget, &Widget::onFriendStatusChanged);
|
||||
connect(core, &Core::friendStatusMessageChanged, widget, &Widget::onFriendStatusMessageChanged);
|
||||
connect(core, &Core::friendRequestReceived, widget, &Widget::onFriendRequestReceived);
|
||||
connect(core, &Core::friendMessageReceived, widget, &Widget::onFriendMessageReceived);
|
||||
connect(core, &Core::receiptRecieved, widget, &Widget::onReceiptRecieved);
|
||||
connect(core, &Core::groupInviteReceived, widget, &Widget::onGroupInviteReceived);
|
||||
connect(core, &Core::groupMessageReceived, widget, &Widget::onGroupMessageReceived);
|
||||
connect(core, &Core::groupNamelistChanged, widget, &Widget::onGroupNamelistChanged);
|
||||
connect(core, &Core::groupTitleChanged, widget, &Widget::onGroupTitleChanged);
|
||||
connect(core, &Core::emptyGroupCreated, widget, &Widget::onEmptyGroupCreated);
|
||||
connect(core, &Core::avInvite, widget, &Widget::playRingtone);
|
||||
connect(core, &Core::blockingClearContacts, widget, &Widget::clearContactsList, Qt::BlockingQueuedConnection);
|
||||
connect(core, &Core::friendTypingChanged, widget, &Widget::onFriendTypingChanged);
|
||||
|
||||
connect(core, SIGNAL(messageSentResult(int,QString,int)), widget, SLOT(onMessageSendResult(int,QString,int)));
|
||||
connect(core, SIGNAL(groupSentResult(int,QString,int)), widget, SLOT(onGroupSendResult(int,QString,int)));
|
||||
|
||||
connect(widget, &Widget::statusSet, core, &Core::setStatus);
|
||||
connect(widget, &Widget::friendRequested, core, &Core::requestFriendship);
|
||||
connect(widget, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest);
|
||||
connect(widget, &Widget::changeProfile, core, &Core::switchConfiguration);
|
||||
#endif
|
||||
|
||||
// Start Core
|
||||
coreThread->start();
|
||||
|
||||
started = true;
|
||||
}
|
||||
|
||||
Nexus& Nexus::getInstance()
|
||||
{
|
||||
if (!nexus)
|
||||
nexus = new Nexus;
|
||||
return *nexus;
|
||||
}
|
||||
|
||||
void Nexus::destroyInstance()
|
||||
{
|
||||
delete nexus;
|
||||
nexus = nullptr;
|
||||
}
|
||||
|
||||
Core* Nexus::getCore()
|
||||
{
|
||||
return getInstance().core;
|
||||
}
|
||||
|
||||
AndroidGUI* Nexus::getAndroidGUI()
|
||||
{
|
||||
return getInstance().androidgui;
|
||||
}
|
||||
|
||||
Widget* Nexus::getDesktopGUI()
|
||||
{
|
||||
return getInstance().widget;
|
||||
}
|
38
src/nexus.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
#ifndef NEXUS_H
|
||||
#define NEXUS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QThread;
|
||||
class Core;
|
||||
class Widget;
|
||||
class AndroidGUI;
|
||||
|
||||
/// This class is in charge of connecting various systems together
|
||||
/// and forwarding signals appropriately to the right objects
|
||||
/// It is in charge of starting the GUI and the Core
|
||||
class Nexus : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
void start(); ///< Will initialise the systems (GUI, Core, ...)
|
||||
|
||||
static Nexus& getInstance();
|
||||
static void destroyInstance();
|
||||
static Core* getCore(); ///< Will return 0 if not started
|
||||
static AndroidGUI* getAndroidGUI(); ///< Will return 0 if not started
|
||||
static Widget* getDesktopGUI(); ///< Will return 0 if not started
|
||||
|
||||
private:
|
||||
explicit Nexus(QObject *parent = 0);
|
||||
~Nexus();
|
||||
|
||||
private:
|
||||
Core* core;
|
||||
QThread* coreThread;
|
||||
Widget* widget;
|
||||
AndroidGUI* androidgui;
|
||||
bool started;
|
||||
};
|
||||
|
||||
#endif // NEXUS_H
|
121
src/offlinemsgengine.cpp
Normal file
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
Copyright (C) 2015 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre 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 COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "offlinemsgengine.h"
|
||||
#include "src/friend.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include "src/core.h"
|
||||
#include <QMutexLocker>
|
||||
#include <QTimer>
|
||||
|
||||
const int OfflineMsgEngine::offlineTimeout = 2000;
|
||||
QSet<OfflineMsgEngine*> OfflineMsgEngine::engines;
|
||||
QMutex OfflineMsgEngine::globalMutex;
|
||||
|
||||
OfflineMsgEngine::OfflineMsgEngine(Friend *frnd) :
|
||||
mutex(QMutex::Recursive),
|
||||
f(frnd)
|
||||
{
|
||||
engines.insert(this);
|
||||
}
|
||||
|
||||
OfflineMsgEngine::~OfflineMsgEngine()
|
||||
{
|
||||
engines.remove(this);
|
||||
}
|
||||
|
||||
void OfflineMsgEngine::dischargeReceipt(int receipt)
|
||||
{
|
||||
QMutexLocker ml(&mutex);
|
||||
|
||||
auto it = receipts.find(receipt);
|
||||
if (it != receipts.end())
|
||||
{
|
||||
int mID = it.value();
|
||||
auto msgIt = undeliveredMsgs.find(mID);
|
||||
if (msgIt != undeliveredMsgs.end())
|
||||
{
|
||||
HistoryKeeper::getInstance()->markAsSent(mID);
|
||||
msgIt.value().msg->markAsSent();
|
||||
msgIt.value().msg->featureUpdate();
|
||||
undeliveredMsgs.erase(msgIt);
|
||||
}
|
||||
receipts.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void OfflineMsgEngine::registerReceipt(int receipt, int messageID, MessageActionPtr msg, const QDateTime ×tamp)
|
||||
{
|
||||
QMutexLocker ml(&mutex);
|
||||
|
||||
receipts[receipt] = messageID;
|
||||
undeliveredMsgs[messageID] = {msg, timestamp, receipt};
|
||||
}
|
||||
|
||||
void OfflineMsgEngine::deliverOfflineMsgs()
|
||||
{
|
||||
QMutexLocker ml(&mutex);
|
||||
|
||||
if (!Settings::getInstance().getFauxOfflineMessaging())
|
||||
return;
|
||||
|
||||
if (f->getStatus() == Status::Offline)
|
||||
return;
|
||||
|
||||
if (undeliveredMsgs.size() == 0)
|
||||
return;
|
||||
|
||||
QMap<int, MsgPtr> msgs = undeliveredMsgs;
|
||||
removeAllReciepts();
|
||||
|
||||
for (auto iter = msgs.begin(); iter != msgs.end(); iter++)
|
||||
{
|
||||
if (iter.value().timestamp.msecsTo(QDateTime::currentDateTime()) < offlineTimeout)
|
||||
{
|
||||
registerReceipt(iter.value().receipt, iter.key(), iter.value().msg, iter.value().timestamp);
|
||||
continue;
|
||||
}
|
||||
QString messageText = iter.value().msg->getRawMessage();
|
||||
int rec;
|
||||
if (iter.value().msg->isAction())
|
||||
rec = Core::getInstance()->sendAction(f->getFriendID(), messageText);
|
||||
else
|
||||
rec = Core::getInstance()->sendMessage(f->getFriendID(), messageText);
|
||||
registerReceipt(rec, iter.key(), iter.value().msg);
|
||||
}
|
||||
}
|
||||
|
||||
void OfflineMsgEngine::removeAllReciepts()
|
||||
{
|
||||
QMutexLocker ml(&mutex);
|
||||
|
||||
receipts.clear();
|
||||
undeliveredMsgs.clear();
|
||||
}
|
||||
|
||||
void OfflineMsgEngine::processAllMsgs()
|
||||
{
|
||||
if (globalMutex.tryLock())
|
||||
{
|
||||
for (auto &it : engines)
|
||||
{
|
||||
it->deliverOfflineMsgs();
|
||||
}
|
||||
|
||||
globalMutex.unlock();
|
||||
}
|
||||
}
|
61
src/offlinemsgengine.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
Copyright (C) 2015 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre 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 COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef OFFLINEMSGENGINE_H
|
||||
#define OFFLINEMSGENGINE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QMutex>
|
||||
#include <QDateTime>
|
||||
#include "src/widget/tool/chatactions/messageaction.h"
|
||||
|
||||
struct Friend;
|
||||
class QTimer;
|
||||
|
||||
class OfflineMsgEngine : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
OfflineMsgEngine(Friend *);
|
||||
virtual ~OfflineMsgEngine();
|
||||
|
||||
void dischargeReceipt(int receipt);
|
||||
void registerReceipt(int receipt, int messageID, MessageActionPtr msg, const QDateTime ×tamp = QDateTime::currentDateTime());
|
||||
|
||||
public slots:
|
||||
void deliverOfflineMsgs();
|
||||
void removeAllReciepts();
|
||||
static void processAllMsgs();
|
||||
|
||||
private:
|
||||
struct MsgPtr {
|
||||
MessageActionPtr msg;
|
||||
QDateTime timestamp;
|
||||
int receipt;
|
||||
};
|
||||
|
||||
QMutex mutex;
|
||||
Friend* f;
|
||||
QHash<int, int> receipts;
|
||||
QMap<int, MsgPtr> undeliveredMsgs;
|
||||
|
||||
static QSet<OfflineMsgEngine*> engines;
|
||||
static const int offlineTimeout;
|
||||
static QMutex globalMutex;
|
||||
};
|
||||
|
||||
#endif // OFFLINEMSGENGINE_H
|
|
@ -220,8 +220,10 @@ fallbackOnTox1:
|
|||
#if TOX1_SILENT_FALLBACK
|
||||
toxIdStr = queryTox1(record, silent);
|
||||
#elif TOX1_ASK_FALLBACK
|
||||
QMessageBox::StandardButton btn = QMessageBox::warning(nullptr, "qTox", tr("It appears that qTox has to use the old tox1 protocol.\n\
|
||||
Unfortunately tox1 is not secure. Should it be used anyway?"), QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
|
||||
QMessageBox::StandardButton btn = QMessageBox::warning(nullptr, "qTox", tr("It appears that qTox has to use the old tox1 protocol to access DNS record of your friend's Tox ID.\n\
|
||||
Unfortunately tox1 is not secure, and you are at risk of someone hijacking what is sent between you and ToxDNS service.\n\
|
||||
Should tox1 be used anyway?\n\
|
||||
If unsure, press “No”, so that request to ToxDNS service will not be made using unsecure protocol."), QMessageBox::Yes|QMessageBox::No, QMessageBox::No);
|
||||
if (btn == QMessageBox::Yes)
|
||||
queryTox1(record, silent);
|
||||
#endif
|
||||
|
@ -264,8 +266,10 @@ ToxID ToxDNS::resolveToxAddress(const QString &address, bool silent)
|
|||
#if TOX1_SILENT_FALLBACK
|
||||
toxId = ToxID::fromString(queryTox1(address, silent));
|
||||
#elif TOX1_ASK_FALLBACK
|
||||
QMessageBox::StandardButton btn = QMessageBox::warning(nullptr, "qTox", tr("It appears that qTox has to use the old tox1 protocol.\n\
|
||||
Unfortunately tox1 is not secure. Should it be used anyway?"), QMessageBox::Ok|QMessageBox::No, QMessageBox::No);
|
||||
QMessageBox::StandardButton btn = QMessageBox::warning(nullptr, "qTox", tr("It appears that qTox has to use the old tox1 protocol to access DNS record of your friend's Tox ID.\n\
|
||||
Unfortunately tox1 is not secure, and you are at risk of someone hijacking what is sent between you and ToxDNS service.\n\
|
||||
Should tox1 be used anyway?\n\
|
||||
If unsure, press “No”, so that request to ToxDNS service will not be made using unsecure protocol."), QMessageBox::Ok|QMessageBox::No, QMessageBox::No);
|
||||
if (btn == QMessageBox::Ok)
|
||||
toxId = ToxID::fromString(queryTox1(address, silent));
|
||||
#else
|
||||
|
|
169
src/widget/androidgui.cpp
Normal file
|
@ -0,0 +1,169 @@
|
|||
#include "androidgui.h"
|
||||
#include "ui_android.h"
|
||||
#include "friendlistwidget.h"
|
||||
#include "maskablepixmapwidget.h"
|
||||
#include "src/core.h"
|
||||
#include "src/friend.h"
|
||||
#include "src/friendlist.h"
|
||||
#include "src/group.h"
|
||||
#include "src/grouplist.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include "src/misc/style.h"
|
||||
#include "src/nexus.h"
|
||||
#include "src/widget/friendwidget.h"
|
||||
#include "src/widget/groupwidget.h"
|
||||
#include <QLabel>
|
||||
#include <QMenu>
|
||||
|
||||
AndroidGUI::AndroidGUI(QWidget *parent) :
|
||||
QWidget(parent),
|
||||
ui{new Ui::Android}
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->friendList->setStyleSheet(Style::resolve(Style::getStylesheet(":ui/friendList/friendList.css")));
|
||||
|
||||
profilePicture = new MaskablePixmapWidget(this, QSize(40, 40), ":/img/avatar_mask.png");
|
||||
profilePicture->setPixmap(QPixmap(":/img/contact_dark.png"));
|
||||
profilePicture->setClickable(true);
|
||||
ui->myProfile->insertWidget(0, profilePicture);
|
||||
ui->myProfile->insertSpacing(1, 7);
|
||||
|
||||
ui->tooliconsZone->setStyleSheet(Style::resolve("QPushButton{background-color:@themeDark;border:none;}QPushButton:hover{background-color:@themeMediumDark;border:none;}"));
|
||||
ui->statusHead->setStyleSheet(Style::getStylesheet(":/ui/window/statusPanel.css"));
|
||||
|
||||
contactListWidget = new FriendListWidget();
|
||||
ui->friendList->setWidget(contactListWidget);
|
||||
ui->friendList->setLayoutDirection(Qt::RightToLeft);
|
||||
|
||||
ui->nameLabel->setEditable(true);
|
||||
ui->statusLabel->setEditable(true);
|
||||
|
||||
ui->statusPanel->setStyleSheet(Style::getStylesheet(":/ui/window/statusPanel.css"));
|
||||
|
||||
QMenu *statusButtonMenu = new QMenu(ui->statusButton);
|
||||
QAction* setStatusOnline = statusButtonMenu->addAction(AndroidGUI::tr("Online","Button to set your status to 'Online'"));
|
||||
setStatusOnline->setIcon(QIcon(":ui/statusButton/dot_online.png"));
|
||||
QAction* setStatusAway = statusButtonMenu->addAction(AndroidGUI::tr("Away","Button to set your status to 'Away'"));
|
||||
setStatusAway->setIcon(QIcon(":ui/statusButton/dot_idle.png"));
|
||||
QAction* setStatusBusy = statusButtonMenu->addAction(AndroidGUI::tr("Busy","Button to set your status to 'Busy'"));
|
||||
setStatusBusy->setIcon(QIcon(":ui/statusButton/dot_busy.png"));
|
||||
ui->statusButton->setMenu(statusButtonMenu);
|
||||
|
||||
ui->statusButton->setProperty("status", "offline");
|
||||
Style::repolish(ui->statusButton);
|
||||
|
||||
// Disable some widgets until we're connected to the DHT
|
||||
ui->statusButton->setEnabled(false);
|
||||
|
||||
Style::setThemeColor(Settings::getInstance().getThemeColor());
|
||||
Style::setThemeColor(1);
|
||||
reloadTheme();
|
||||
|
||||
connect(ui->nameLabel, &CroppingLabel::textChanged, this, &AndroidGUI::onUsernameChanged);
|
||||
connect(ui->statusLabel, &CroppingLabel::textChanged, this, &AndroidGUI::onStatusMessageChanged);
|
||||
}
|
||||
|
||||
AndroidGUI::~AndroidGUI()
|
||||
{
|
||||
delete profilePicture;
|
||||
delete contactListWidget;
|
||||
}
|
||||
|
||||
void AndroidGUI::reloadTheme()
|
||||
{
|
||||
QString statusPanelStyle = Style::getStylesheet(":/ui/window/statusPanel.css");
|
||||
ui->tooliconsZone->setStyleSheet(Style::resolve("QPushButton{background-color:@themeDark;border:none;}QPushButton:hover{background-color:@themeMediumDark;border:none;}"));
|
||||
ui->statusPanel->setStyleSheet(statusPanelStyle);
|
||||
ui->statusHead->setStyleSheet(statusPanelStyle);
|
||||
ui->friendList->setStyleSheet(Style::getStylesheet(":ui/friendList/friendList.css"));
|
||||
ui->statusButton->setStyleSheet(Style::getStylesheet(":ui/statusButton/statusButton.css"));
|
||||
|
||||
for (Friend* f : FriendList::getAllFriends())
|
||||
f->getFriendWidget()->reloadTheme();
|
||||
|
||||
for (Group* g : GroupList::getAllGroups())
|
||||
g->getGroupWidget()->reloadTheme();
|
||||
}
|
||||
|
||||
void AndroidGUI::onSelfAvatarLoaded(const QPixmap& pic)
|
||||
{
|
||||
profilePicture->setPixmap(pic);
|
||||
}
|
||||
|
||||
void AndroidGUI::onConnected()
|
||||
{
|
||||
ui->statusButton->setEnabled(true);
|
||||
if (beforeDisconnect == Status::Offline)
|
||||
emit statusSet(Status::Online);
|
||||
else
|
||||
emit statusSet(beforeDisconnect);
|
||||
}
|
||||
|
||||
void AndroidGUI::onDisconnected()
|
||||
{
|
||||
QString stat = ui->statusButton->property("status").toString();
|
||||
if (stat == "online")
|
||||
beforeDisconnect = Status::Online;
|
||||
else if (stat == "busy")
|
||||
beforeDisconnect = Status::Busy;
|
||||
else if (stat == "away")
|
||||
beforeDisconnect = Status::Away;
|
||||
else
|
||||
beforeDisconnect = Status::Offline;
|
||||
|
||||
ui->statusButton->setEnabled(false);
|
||||
emit statusSet(Status::Offline);
|
||||
}
|
||||
|
||||
void AndroidGUI::onUsernameChanged(const QString& newUsername, const QString& oldUsername)
|
||||
{
|
||||
setUsername(oldUsername); // restore old username until Core tells us to set it
|
||||
Nexus::getCore()->setUsername(newUsername);
|
||||
}
|
||||
|
||||
void AndroidGUI::setUsername(const QString& username)
|
||||
{
|
||||
ui->nameLabel->setText(username);
|
||||
ui->nameLabel->setToolTip(username); // for overlength names
|
||||
QString sanename = username;
|
||||
sanename.remove(QRegExp("[\\t\\n\\v\\f\\r\\x0000]"));
|
||||
nameMention = QRegExp("\\b" + QRegExp::escape(username) + "\\b", Qt::CaseInsensitive);
|
||||
sanitizedNameMention = QRegExp("\\b" + QRegExp::escape(sanename) + "\\b", Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
void AndroidGUI::onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage)
|
||||
{
|
||||
ui->statusLabel->setText(oldStatusMessage); // restore old status message until Core tells us to set it
|
||||
ui->statusLabel->setToolTip(oldStatusMessage); // for overlength messsages
|
||||
Nexus::getCore()->setStatusMessage(newStatusMessage);
|
||||
}
|
||||
|
||||
void AndroidGUI::setStatusMessage(const QString &statusMessage)
|
||||
{
|
||||
ui->statusLabel->setText(statusMessage);
|
||||
ui->statusLabel->setToolTip(statusMessage); // for overlength messsages
|
||||
}
|
||||
|
||||
void AndroidGUI::onStatusSet(Status status)
|
||||
{
|
||||
//We have to use stylesheets here, there's no way to
|
||||
//prevent the button icon from moving when pressed otherwise
|
||||
switch (status)
|
||||
{
|
||||
case Status::Online:
|
||||
ui->statusButton->setProperty("status" ,"online");
|
||||
break;
|
||||
case Status::Away:
|
||||
ui->statusButton->setProperty("status" ,"away");
|
||||
break;
|
||||
case Status::Busy:
|
||||
ui->statusButton->setProperty("status" ,"busy");
|
||||
break;
|
||||
case Status::Offline:
|
||||
ui->statusButton->setProperty("status" ,"offline");
|
||||
break;
|
||||
}
|
||||
Style::repolish(ui->statusButton);
|
||||
}
|
||||
|
52
src/widget/androidgui.h
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef ANDROIDGUI_H
|
||||
#define ANDROIDGUI_H
|
||||
|
||||
#include "src/corestructs.h"
|
||||
#include <QWidget>
|
||||
|
||||
class MaskablePixmapWidget;
|
||||
class FriendListWidget;
|
||||
|
||||
namespace Ui {
|
||||
class Android;
|
||||
}
|
||||
|
||||
class AndroidGUI : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AndroidGUI(QWidget *parent = 0);
|
||||
~AndroidGUI();
|
||||
|
||||
public slots:
|
||||
void onConnected();
|
||||
void onDisconnected();
|
||||
void onStatusSet(Status status);
|
||||
void onSelfAvatarLoaded(const QPixmap &pic);
|
||||
void setUsername(const QString& username);
|
||||
void setStatusMessage(const QString &statusMessage);
|
||||
|
||||
signals:
|
||||
void friendRequestAccepted(const QString& userId);
|
||||
void friendRequested(const QString& friendAddress, const QString& message);
|
||||
void statusSet(Status status);
|
||||
void statusSelected(Status status);
|
||||
void usernameChanged(const QString& username);
|
||||
void statusMessageChanged(const QString& statusMessage);
|
||||
|
||||
private:
|
||||
void reloadTheme();
|
||||
|
||||
private slots:
|
||||
void onUsernameChanged(const QString& newUsername, const QString& oldUsername);
|
||||
void onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage);
|
||||
|
||||
private:
|
||||
Ui::Android* ui;
|
||||
MaskablePixmapWidget* profilePicture;
|
||||
FriendListWidget* contactListWidget;
|
||||
Status beforeDisconnect = Status::Offline;
|
||||
QRegExp nameMention, sanitizedNameMention;
|
||||
};
|
||||
|
||||
#endif // ANDROIDGUI_H
|
|
@ -1,5 +1,6 @@
|
|||
#include "callconfirmwidget.h"
|
||||
#include "widget.h"
|
||||
#include "gui.h"
|
||||
#include <assert.h>
|
||||
#include <QVBoxLayout>
|
||||
#include <QHBoxLayout>
|
||||
#include <QLabel>
|
||||
|
@ -11,7 +12,7 @@
|
|||
#include <QPalette>
|
||||
|
||||
CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor) :
|
||||
QWidget(Widget::getInstance()), anchor(Anchor),
|
||||
QWidget(GUI::getMainWidget()), anchor(Anchor),
|
||||
rectW{120}, rectH{85},
|
||||
spikeW{30}, spikeH{15},
|
||||
roundedFactor{20},
|
||||
|
@ -43,7 +44,7 @@ CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor) :
|
|||
connect(buttonBox, &QDialogButtonBox::accepted, this, &CallConfirmWidget::accepted);
|
||||
connect(buttonBox, &QDialogButtonBox::rejected, this, &CallConfirmWidget::rejected);
|
||||
|
||||
connect(Widget::getInstance(), &Widget::resized, this, &CallConfirmWidget::reposition);
|
||||
connect(&GUI::getInstance(), &GUI::resized, this, &CallConfirmWidget::reposition);
|
||||
|
||||
layout->setMargin(12);
|
||||
layout->addSpacing(spikeH);
|
||||
|
@ -56,7 +57,7 @@ CallConfirmWidget::CallConfirmWidget(const QWidget *Anchor) :
|
|||
|
||||
void CallConfirmWidget::reposition()
|
||||
{
|
||||
Widget* w = Widget::getInstance();
|
||||
QWidget* w = GUI::getMainWidget();
|
||||
QPoint pos = anchor->mapToGlobal({(anchor->width()-rectW)/2,anchor->height()})-w->mapToGlobal({0,0});
|
||||
|
||||
// We don't want the widget to overflow past the right of the screen
|
||||
|
|
|
@ -28,6 +28,9 @@ CroppingLabel::CroppingLabel(QWidget* parent)
|
|||
|
||||
textEdit = new QLineEdit(this);
|
||||
textEdit->hide();
|
||||
textEdit->setInputMethodHints(Qt::ImhNoAutoUppercase
|
||||
| Qt::ImhNoPredictiveText
|
||||
| Qt::ImhPreferLatin);
|
||||
|
||||
installEventFilter(this);
|
||||
textEdit->installEventFilter(this);
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "src/chatlog/chatlinecontentproxy.h"
|
||||
#include "src/chatlog/content/text.h"
|
||||
#include "src/chatlog/chatlog.h"
|
||||
#include "src/offlinemsgengine.h"
|
||||
|
||||
ChatForm::ChatForm(Friend* chatFriend)
|
||||
: f(chatFriend)
|
||||
|
@ -58,6 +59,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
statusMessageLabel->setMinimumHeight(Style::getFont(Style::Medium).pixelSize());
|
||||
|
||||
callConfirm = nullptr;
|
||||
offlineEngine = new OfflineMsgEngine(f);
|
||||
|
||||
typingTimer.setSingleShot(true);
|
||||
|
||||
|
@ -73,7 +75,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
headTextLayout->addWidget(callDuration, 1, Qt::AlignCenter);
|
||||
callDuration->hide();
|
||||
|
||||
menu.addAction(tr("Load History..."), this, SLOT(onLoadHistory()));
|
||||
menu.addAction(tr("Load chat history..."), this, SLOT(onLoadHistory()));
|
||||
|
||||
connect(Core::getInstance(), &Core::fileSendStarted, this, &ChatForm::startFileSend);
|
||||
connect(sendButton, &QPushButton::clicked, this, &ChatForm::onSendTriggered);
|
||||
|
@ -85,7 +87,7 @@ ChatForm::ChatForm(Friend* chatFriend)
|
|||
connect(micButton, SIGNAL(clicked()), this, SLOT(onMicMuteToggle()));
|
||||
connect(volButton, SIGNAL(clicked()), this, SLOT(onVolMuteToggle()));
|
||||
connect(Core::getInstance(), &Core::fileSendFailed, this, &ChatForm::onFileSendFailed);
|
||||
connect(this, SIGNAL(chatAreaCleared()), this, SLOT(clearReciepts()));
|
||||
connect(this, SIGNAL(chatAreaCleared()), getOfflineMsgEngine(), SLOT(removeAllReciepts()));
|
||||
connect(&typingTimer, &QTimer::timeout, this, [=]{Core::getInstance()->sendTyping(f->getFriendID(), false);});
|
||||
connect(nameLabel, &CroppingLabel::textChanged, this, [=](QString text, QString orig) {
|
||||
if (text != orig) emit aliasChanged(text);
|
||||
|
@ -139,7 +141,7 @@ void ChatForm::onSendTriggered()
|
|||
else
|
||||
rec = Core::getInstance()->sendMessage(f->getFriendID(), qt_msg);
|
||||
|
||||
registerReceipt(rec, id, ma);
|
||||
getOfflineMsgEngine()->registerReceipt(rec, id, ma);
|
||||
|
||||
msgEdit->setLastMessage(msg); //set last message only when sending it
|
||||
}
|
||||
|
@ -806,7 +808,8 @@ void ChatForm::loadHistory(QDateTime since, bool processUndelivered)
|
|||
rec = Core::getInstance()->sendMessage(f->getFriendID(), msg->toString());
|
||||
else
|
||||
rec = Core::getInstance()->sendAction(f->getFriendID(), msg->toString());
|
||||
registerReceipt(rec, it.id, msg);
|
||||
|
||||
getOfflineMsgEngine()->registerReceipt(rec, it.id, msg);
|
||||
}
|
||||
}
|
||||
historyMessages.prepend(msg);
|
||||
|
@ -889,29 +892,6 @@ QString ChatForm::secondsToDHMS(quint32 duration)
|
|||
return cD + res.sprintf("%dd%02dh %02dm %02ds", days, hours, minutes, seconds);
|
||||
}
|
||||
|
||||
void ChatForm::registerReceipt(int receipt, int messageID, ChatMessage::Ptr msg)
|
||||
{
|
||||
receipts[receipt] = messageID;
|
||||
undeliveredMsgs[messageID] = msg;
|
||||
}
|
||||
|
||||
void ChatForm::dischargeReceipt(int receipt)
|
||||
{
|
||||
auto it = receipts.find(receipt);
|
||||
if (it != receipts.end())
|
||||
{
|
||||
int mID = it.value();
|
||||
auto msgIt = undeliveredMsgs.find(mID);
|
||||
if (msgIt != undeliveredMsgs.end())
|
||||
{
|
||||
HistoryKeeper::getInstance()->markAsSent(mID);
|
||||
msgIt.value()->markAsSent(QDateTime::currentDateTime());
|
||||
undeliveredMsgs.erase(msgIt);
|
||||
}
|
||||
receipts.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::setFriendTyping(bool isTyping)
|
||||
{
|
||||
chatWidget->setTypingNotificationVisible(isTyping);
|
||||
|
@ -922,32 +902,6 @@ void ChatForm::setFriendTyping(bool isTyping)
|
|||
text->setText("<div class=typing>" + QString("%1 is typing").arg(f->getDisplayedName()) + "</div>");
|
||||
}
|
||||
|
||||
void ChatForm::clearReciepts()
|
||||
{
|
||||
receipts.clear();
|
||||
undeliveredMsgs.clear();
|
||||
}
|
||||
|
||||
void ChatForm::deliverOfflineMsgs()
|
||||
{
|
||||
if (!Settings::getInstance().getFauxOfflineMessaging())
|
||||
return;
|
||||
|
||||
QMap<int, ChatMessage::Ptr> msgs = undeliveredMsgs;
|
||||
clearReciepts();
|
||||
|
||||
for (auto iter = msgs.begin(); iter != msgs.end(); iter++)
|
||||
{
|
||||
QString messageText = iter.value()->toString();
|
||||
int rec;
|
||||
if (iter.value()->isAction())
|
||||
rec = Core::getInstance()->sendAction(f->getFriendID(), messageText);
|
||||
else
|
||||
rec = Core::getInstance()->sendMessage(f->getFriendID(), messageText);
|
||||
registerReceipt(rec, iter.key(), iter.value());
|
||||
}
|
||||
}
|
||||
|
||||
void ChatForm::show(Ui::MainWindow &ui)
|
||||
{
|
||||
GenericChatForm::show(ui);
|
||||
|
@ -961,3 +915,8 @@ void ChatForm::hideEvent(QHideEvent*)
|
|||
if (callConfirm)
|
||||
callConfirm->hide();
|
||||
}
|
||||
|
||||
OfflineMsgEngine *ChatForm::getOfflineMsgEngine()
|
||||
{
|
||||
return offlineEngine;
|
||||
}
|
||||
|
|
|
@ -32,6 +32,7 @@ class QPixmap;
|
|||
class CallConfirmWidget;
|
||||
class QHideEvent;
|
||||
class QMoveEvent;
|
||||
class OfflineMsgEngine;
|
||||
|
||||
class ChatForm : public GenericChatForm
|
||||
{
|
||||
|
@ -44,6 +45,7 @@ public:
|
|||
|
||||
void dischargeReceipt(int receipt);
|
||||
void setFriendTyping(bool isTyping);
|
||||
OfflineMsgEngine* getOfflineMsgEngine();
|
||||
|
||||
virtual void show(Ui::MainWindow &ui);
|
||||
|
||||
|
@ -60,8 +62,6 @@ signals:
|
|||
void aliasChanged(const QString& alias);
|
||||
|
||||
public slots:
|
||||
void deliverOfflineMsgs();
|
||||
void clearReciepts();
|
||||
void startFileSend(ToxFile file);
|
||||
void onFileRecvRequest(ToxFile file);
|
||||
void onAvInvite(int FriendId, int CallId, bool video);
|
||||
|
@ -113,14 +113,12 @@ private:
|
|||
QTimer typingTimer;
|
||||
QTimer *disableCallButtonsTimer;
|
||||
QElapsedTimer timeElapsed;
|
||||
OfflineMsgEngine *offlineEngine;
|
||||
|
||||
QHash<uint, FileTransferInstance*> ftransWidgets;
|
||||
void startCounter();
|
||||
void stopCounter();
|
||||
QString secondsToDHMS(quint32 duration);
|
||||
QHash<int, int> receipts;
|
||||
QMap<int, ChatMessage::Ptr> undeliveredMsgs;
|
||||
bool isTyping;
|
||||
CallConfirmWidget *callConfirm;
|
||||
void enableCallButtons();
|
||||
};
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre 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 COPYING file for more details.
|
||||
*/
|
||||
|
||||
#include "inputpassworddialog.h"
|
||||
#include "ui_inputpassworddialog.h"
|
||||
|
||||
InputPasswordDialog::InputPasswordDialog(QString title, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::InputPasswordDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
if (title != QString())
|
||||
setWindowTitle(title);
|
||||
}
|
||||
|
||||
InputPasswordDialog::~InputPasswordDialog()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
QString InputPasswordDialog::getPassword()
|
||||
{
|
||||
return ui->passwordLineEdit->text();
|
||||
}
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2014 by Project Tox <https://tox.im>
|
||||
|
||||
This file is part of qTox, a Qt-based graphical interface for Tox.
|
||||
|
||||
This program is libre 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 COPYING file for more details.
|
||||
*/
|
||||
|
||||
#ifndef INPUTPASSWORDDIALOG_H
|
||||
#define INPUTPASSWORDDIALOG_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class InputPasswordDialog;
|
||||
}
|
||||
|
||||
class InputPasswordDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit InputPasswordDialog(QString title = QString(), QWidget *parent = 0);
|
||||
~InputPasswordDialog();
|
||||
|
||||
QString getPassword();
|
||||
|
||||
private:
|
||||
Ui::InputPasswordDialog *ui;
|
||||
};
|
||||
|
||||
#endif // INPUTPASSWORDDIALOG_H
|
|
@ -1,97 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>InputPasswordDialog</class>
|
||||
<widget class="QDialog" name="InputPasswordDialog">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>123</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Password Dialog</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Input password:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="passwordLineEdit">
|
||||
<property name="cursor">
|
||||
<cursorShape>IBeamCursor</cursorShape>
|
||||
</property>
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QDialogButtonBox" name="buttonBox">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="standardButtons">
|
||||
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>accepted()</signal>
|
||||
<receiver>InputPasswordDialog</receiver>
|
||||
<slot>accept()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>248</x>
|
||||
<y>254</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>157</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
<connection>
|
||||
<sender>buttonBox</sender>
|
||||
<signal>rejected()</signal>
|
||||
<receiver>InputPasswordDialog</receiver>
|
||||
<slot>reject()</slot>
|
||||
<hints>
|
||||
<hint type="sourcelabel">
|
||||
<x>316</x>
|
||||
<y>260</y>
|
||||
</hint>
|
||||
<hint type="destinationlabel">
|
||||
<x>286</x>
|
||||
<y>274</y>
|
||||
</hint>
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
</ui>
|
|
@ -18,14 +18,27 @@
|
|||
#include "ui_setpassworddialog.h"
|
||||
#include <QPushButton>
|
||||
|
||||
SetPasswordDialog::SetPasswordDialog(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::SetPasswordDialog)
|
||||
const double SetPasswordDialog::reasonablePasswordLength = 8.;
|
||||
|
||||
SetPasswordDialog::SetPasswordDialog(QString body, QString extraButton, QWidget* parent)
|
||||
: QDialog(parent)
|
||||
, ui(new Ui::SetPasswordDialog)
|
||||
, body(body+"\n")
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
connect(ui->passwordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit()));
|
||||
connect(ui-> passwordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit()));
|
||||
connect(ui->repasswordlineEdit, SIGNAL(textChanged(QString)), this, SLOT(onPasswordEdit()));
|
||||
|
||||
ui->body->setText(body + "\n" + tr("The passwords don't match."));
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
|
||||
if (!extraButton.isEmpty())
|
||||
{
|
||||
QPushButton* third = new QPushButton(extraButton);
|
||||
ui->buttonBox->addButton(third, QDialogButtonBox::YesRole);
|
||||
connect(third, &QPushButton::clicked, this, [&](){this->done(Tertiary);});
|
||||
}
|
||||
}
|
||||
|
||||
SetPasswordDialog::~SetPasswordDialog()
|
||||
|
@ -35,10 +48,53 @@ SetPasswordDialog::~SetPasswordDialog()
|
|||
|
||||
void SetPasswordDialog::onPasswordEdit()
|
||||
{
|
||||
if (ui->passwordlineEdit->text() == ui->repasswordlineEdit->text())
|
||||
QString pswd = ui->passwordlineEdit->text();
|
||||
|
||||
if (pswd == ui->repasswordlineEdit->text() && pswd.length() > 0)
|
||||
{
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);
|
||||
ui->body->setText(body);
|
||||
}
|
||||
else
|
||||
{
|
||||
ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
||||
ui->body->setText(body + tr("The passwords don't match."));
|
||||
}
|
||||
|
||||
// Password strength calculator
|
||||
// Based on code in the Master Password dialog in Firefox
|
||||
// (pref-masterpass.js)
|
||||
// Original code triple-licensed under the MPL, GPL, and LGPL
|
||||
// so is license-compatible with this file
|
||||
|
||||
const double lengthFactor = reasonablePasswordLength / 8.0;
|
||||
int pwlength = (int)(pswd.length() / lengthFactor);
|
||||
if (pwlength > 5)
|
||||
pwlength = 5;
|
||||
|
||||
const QRegExp numRxp("[0-9]", Qt::CaseSensitive, QRegExp::RegExp);
|
||||
int numeric = (int)(pswd.count(numRxp) / lengthFactor);
|
||||
if (numeric > 3)
|
||||
numeric = 3;
|
||||
|
||||
const QRegExp symbRxp("\\W", Qt::CaseInsensitive, QRegExp::RegExp);
|
||||
int numsymbols = (int)(pswd.count(symbRxp) / lengthFactor);
|
||||
if (numsymbols > 3)
|
||||
numsymbols = 3;
|
||||
|
||||
const QRegExp upperRxp("[A-Z]", Qt::CaseSensitive, QRegExp::RegExp);
|
||||
int upper = (int)(pswd.count(upperRxp) / lengthFactor);
|
||||
if (upper > 3)
|
||||
upper = 3;
|
||||
|
||||
int pwstrength=((pwlength*10)-20) + (numeric*10) + (numsymbols*15) + (upper*10);
|
||||
if (pwstrength < 0)
|
||||
pwstrength = 0;
|
||||
|
||||
if (pwstrength > 100)
|
||||
pwstrength = 100;
|
||||
|
||||
ui->strengthBar->setValue(pwstrength);
|
||||
}
|
||||
|
||||
QString SetPasswordDialog::getPassword()
|
||||
|
|
|
@ -28,7 +28,8 @@ class SetPasswordDialog : public QDialog
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit SetPasswordDialog(QWidget *parent = 0);
|
||||
enum ReturnCode {Rejected=QDialog::Rejected, Accepted=QDialog::Accepted, Tertiary};
|
||||
explicit SetPasswordDialog(QString body, QString extraButton, QWidget* parent = 0);
|
||||
~SetPasswordDialog();
|
||||
QString getPassword();
|
||||
|
||||
|
@ -37,6 +38,8 @@ private slots:
|
|||
|
||||
private:
|
||||
Ui::SetPasswordDialog *ui;
|
||||
QString body;
|
||||
static const double reasonablePasswordLength;
|
||||
};
|
||||
|
||||
#endif // SETPASSWORDDIALOG_H
|
||||
|
|
|
@ -6,44 +6,77 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>434</width>
|
||||
<height>175</height>
|
||||
<width>325</width>
|
||||
<height>160</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Dialog</string>
|
||||
<string>Set your password</string>
|
||||
</property>
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Type Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLabel" name="body"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="passwordlineEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>Repeat Password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="repasswordlineEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QGridLayout" name="pswdsLayout">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Repeat password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Type password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QLineEdit" name="repasswordlineEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QLineEdit" name="passwordlineEdit">
|
||||
<property name="echoMode">
|
||||
<enum>QLineEdit::Password</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight</set>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Password strength</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QProgressBar" name="strengthBar">
|
||||
<property name="value">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="format">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
|
@ -73,6 +106,10 @@
|
|||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<tabstops>
|
||||
<tabstop>passwordlineEdit</tabstop>
|
||||
<tabstop>repasswordlineEdit</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
|
|
|
@ -65,7 +65,7 @@
|
|||
<item alignment="Qt::AlignTop">
|
||||
<widget class="QGroupBox" name="historyGroup">
|
||||
<property name="title">
|
||||
<string>History</string>
|
||||
<string>Chat history</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
|
|
|
@ -27,6 +27,10 @@
|
|||
#include <AL/al.h>
|
||||
#endif
|
||||
|
||||
#ifndef ALC_ALL_DEVICES_SPECIFIER
|
||||
#define ALC_ALL_DEVICES_SPECIFIER ALC_DEVICE_SPECIFIER
|
||||
#endif
|
||||
|
||||
AVForm::AVForm() :
|
||||
GenericForm(tr("Audio/Video"), QPixmap(":/img/settings/av.png"))
|
||||
{
|
||||
|
|
|
@ -359,4 +359,5 @@ void GeneralForm::onThemeColorChanged(int)
|
|||
int index = bodyUI->themeColorCBox->currentIndex();
|
||||
Settings::getInstance().setThemeColor(index);
|
||||
Style::setThemeColor(index);
|
||||
Style::applyTheme();
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "src/misc/settings.h"
|
||||
#include "src/widget/croppinglabel.h"
|
||||
#include "src/widget/widget.h"
|
||||
#include "src/widget/gui.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "src/misc/style.h"
|
||||
#include <QLabel>
|
||||
|
@ -29,7 +30,6 @@
|
|||
#include <QClipboard>
|
||||
#include <QInputDialog>
|
||||
#include <QFileDialog>
|
||||
#include <QMessageBox>
|
||||
|
||||
IdentityForm::IdentityForm() :
|
||||
GenericForm(tr("Identity"), QPixmap(":/img/settings/identity.png"))
|
||||
|
@ -112,7 +112,7 @@ void IdentityForm::present()
|
|||
toxId->setText(Core::getInstance()->getSelfId().toString());
|
||||
toxId->setCursorPosition(0);
|
||||
bodyUI->profiles->clear();
|
||||
for (QString profile : Widget::searchProfiles())
|
||||
for (QString profile : Settings::getInstance().searchProfiles())
|
||||
bodyUI->profiles->addItem(profile);
|
||||
QString current = Settings::getInstance().getCurrentProfile();
|
||||
if (current != "")
|
||||
|
@ -152,10 +152,11 @@ void IdentityForm::onRenameClicked()
|
|||
name = Core::sanitize(name);
|
||||
QDir dir(Settings::getSettingsDirPath());
|
||||
QString file = dir.filePath(name+Core::TOX_EXT);
|
||||
if (!QFile::exists(file) || checkContinue(tr("Profile already exists", "rename confirm title"),
|
||||
if (!QFile::exists(file) || GUI::askQuestion(tr("Profile already exists", "rename confirm title"),
|
||||
tr("A profile named \"%1\" already exists. Do you want to erase it?", "rename confirm text").arg(cur)))
|
||||
{
|
||||
QFile::rename(dir.filePath(cur+Core::TOX_EXT), file);
|
||||
QFile::rename(dir.filePath(cur+".ini"), dir.filePath(name+".ini"));
|
||||
bodyUI->profiles->setItemText(bodyUI->profiles->currentIndex(), name);
|
||||
HistoryKeeper::renameHistory(cur, name);
|
||||
Settings::getInstance().setCurrentProfile(name);
|
||||
|
@ -176,8 +177,6 @@ void IdentityForm::onExportClicked()
|
|||
if (QFile::exists(path))
|
||||
{
|
||||
// should we popup a warning?
|
||||
// if (!checkContinue(tr("Overwriting a file"), tr("Are you sure you want to overwrite %1?").arg(path)))
|
||||
// return;
|
||||
success = QFile::remove(path);
|
||||
if (!success)
|
||||
{
|
||||
|
@ -199,8 +198,8 @@ void IdentityForm::onDeleteClicked()
|
|||
}
|
||||
else
|
||||
{
|
||||
if (checkContinue(tr("Deletion imminent!","deletion confirmation title"),
|
||||
tr("Are you sure you want to delete this profile?\nAssociated friend information and chat logs will be deleted as well.","deletion confirmation text")))
|
||||
if (GUI::askQuestion(tr("Deletion imminent!","deletion confirmation title"),
|
||||
tr("Are you sure you want to delete this profile?","deletion confirmation text")))
|
||||
{
|
||||
QString profile = bodyUI->profiles->currentText();
|
||||
QDir dir(Settings::getSettingsDirPath());
|
||||
|
@ -238,7 +237,7 @@ void IdentityForm::onImportClicked()
|
|||
|
||||
QString profilePath = QDir(Settings::getSettingsDirPath()).filePath(profile + Core::TOX_EXT);
|
||||
|
||||
if (QFileInfo(profilePath).exists() && !checkContinue(tr("Profile already exists", "import confirm title"),
|
||||
if (QFileInfo(profilePath).exists() && !GUI::askQuestion(tr("Profile already exists", "import confirm title"),
|
||||
tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile)))
|
||||
return;
|
||||
|
||||
|
@ -251,12 +250,6 @@ void IdentityForm::onNewClicked()
|
|||
emit Widget::getInstance()->changeProfile(QString());
|
||||
}
|
||||
|
||||
bool IdentityForm::checkContinue(const QString& title, const QString& msg)
|
||||
{
|
||||
QMessageBox::StandardButton resp = QMessageBox::question(this, title, msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
return resp == QMessageBox::Yes;
|
||||
}
|
||||
|
||||
void IdentityForm::disableSwitching()
|
||||
{
|
||||
bodyUI->loadButton->setEnabled(false);
|
||||
|
|
|
@ -66,7 +66,6 @@ private slots:
|
|||
void onDeleteClicked();
|
||||
void onImportClicked();
|
||||
void onNewClicked();
|
||||
bool checkContinue(const QString& title, const QString& msg);
|
||||
void disableSwitching();
|
||||
void enableSwitching();
|
||||
|
||||
|
|
|
@ -21,8 +21,11 @@
|
|||
#include "src/historykeeper.h"
|
||||
#include "src/core.h"
|
||||
#include "src/widget/widget.h"
|
||||
#include "src/widget/gui.h"
|
||||
#include "src/widget/form/setpassworddialog.h"
|
||||
#include <QMessageBox>
|
||||
#include <QFile>
|
||||
#include <QDebug>
|
||||
|
||||
PrivacyForm::PrivacyForm() :
|
||||
GenericForm(tr("Privacy"), QPixmap(":/img/settings/privacy.png"))
|
||||
|
@ -30,10 +33,15 @@ PrivacyForm::PrivacyForm() :
|
|||
bodyUI = new Ui::PrivacySettings;
|
||||
bodyUI->setupUi(this);
|
||||
|
||||
bodyUI->encryptToxHLayout->addStretch();
|
||||
bodyUI->encryptLogsHLayout->addStretch();
|
||||
|
||||
connect(bodyUI->cbTypingNotification, SIGNAL(stateChanged(int)), this, SLOT(onTypingNotificationEnabledUpdated()));
|
||||
connect(bodyUI->cbKeepHistory, SIGNAL(stateChanged(int)), this, SLOT(onEnableLoggingUpdated()));
|
||||
connect(bodyUI->cbEncryptHistory, SIGNAL(clicked()), this, SLOT(onEncryptLogsUpdated()));
|
||||
connect(bodyUI->changeLogsPwButton, &QPushButton::clicked, this, &PrivacyForm::setChatLogsPassword);
|
||||
connect(bodyUI->cbEncryptTox, SIGNAL(clicked()), this, SLOT(onEncryptToxUpdated()));
|
||||
connect(bodyUI->changeToxPwButton, &QPushButton::clicked, this, &PrivacyForm::setToxPassword);
|
||||
connect(bodyUI->nospamLineEdit, SIGNAL(editingFinished()), this, SLOT(setNospam()));
|
||||
connect(bodyUI->randomNosapamButton, SIGNAL(clicked()), this, SLOT(generateRandomNospam()));
|
||||
connect(bodyUI->nospamLineEdit, SIGNAL(textChanged(QString)), this, SLOT(onNospamEdit()));
|
||||
|
@ -48,7 +56,7 @@ void PrivacyForm::onEnableLoggingUpdated()
|
|||
{
|
||||
Settings::getInstance().setEnableLogging(bodyUI->cbKeepHistory->isChecked());
|
||||
bodyUI->cbEncryptHistory->setEnabled(bodyUI->cbKeepHistory->isChecked());
|
||||
HistoryKeeper::getInstance()->resetInstance();
|
||||
HistoryKeeper::resetInstance();
|
||||
Widget::getInstance()->clearAllReceipts();
|
||||
}
|
||||
|
||||
|
@ -57,79 +65,191 @@ void PrivacyForm::onTypingNotificationEnabledUpdated()
|
|||
Settings::getInstance().setTypingNotification(bodyUI->cbTypingNotification->isChecked());
|
||||
}
|
||||
|
||||
bool PrivacyForm::setChatLogsPassword()
|
||||
{
|
||||
Core* core = Core::getInstance();
|
||||
SetPasswordDialog* dialog;
|
||||
|
||||
// check if an encrypted history exists because it was disabled earlier, and use it if possible
|
||||
QString path = HistoryKeeper::getHistoryPath(QString(), 1);
|
||||
QByteArray salt = core->getSaltFromFile(path);
|
||||
bool haveEncHist = salt.size() > 0;
|
||||
|
||||
QString body = tr("Please set your new chat history password.");
|
||||
if (haveEncHist)
|
||||
body += "\n\n" + tr("It appears you have an unused encrypted chat history; if the password matches, it will be added to your current history.");
|
||||
|
||||
if (core->isPasswordSet(Core::ptMain))
|
||||
dialog = new SetPasswordDialog(body, tr("Use data file password", "pushbutton text"), this);
|
||||
else
|
||||
dialog = new SetPasswordDialog(body, QString(), this);
|
||||
|
||||
do {
|
||||
int r = dialog->exec();
|
||||
if (r == QDialog::Rejected)
|
||||
break;
|
||||
|
||||
QList<HistoryKeeper::HistMessage> oldMessages = HistoryKeeper::exportMessagesDeleteFile();
|
||||
|
||||
QString newpw = dialog->getPassword();
|
||||
|
||||
if (r == SetPasswordDialog::Tertiary)
|
||||
core->useOtherPassword(Core::ptHistory);
|
||||
else if (haveEncHist)
|
||||
core->setPassword(newpw, Core::ptHistory, reinterpret_cast<uint8_t*>(salt.data()));
|
||||
else
|
||||
core->setPassword(newpw, Core::ptHistory);
|
||||
|
||||
if (!haveEncHist || HistoryKeeper::checkPassword(1))
|
||||
{
|
||||
Settings::getInstance().setEncryptLogs(true);
|
||||
HistoryKeeper::getInstance()->importMessages(oldMessages);
|
||||
if (haveEncHist)
|
||||
{
|
||||
Widget::getInstance()->reloadHistory();
|
||||
GUI::showWarning(tr("Successfully decrypted old chat history","popup title"), tr("You have succesfully decrypted the old chat history, and it has been added to your current history and re-encrypted.", "popup text"));
|
||||
}
|
||||
delete dialog;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!GUI::askQuestion(tr("Old encrypted chat history", "popup title"), tr("There is currently an unused encrypted chat history, but the password you just entered doesn't match.\nWould you like to try again?\nCanceling will delete the old history and set the password to what you just entered.", "This happens when enabling encryption after previously \"Disabling History\""), true, true))
|
||||
haveEncHist = false; // logically this is really just a `break`, but conceptually this is more accurate
|
||||
}
|
||||
} while (haveEncHist);
|
||||
|
||||
delete dialog;
|
||||
return false;
|
||||
}
|
||||
|
||||
void PrivacyForm::onEncryptLogsUpdated()
|
||||
{
|
||||
bool encrytionState = bodyUI->cbEncryptHistory->isChecked();
|
||||
Core* core = Core::getInstance();
|
||||
|
||||
if (encrytionState)
|
||||
if (bodyUI->cbEncryptHistory->isChecked())
|
||||
{
|
||||
if (!Core::getInstance()->isPasswordSet(Core::ptHistory))
|
||||
if (!core->isPasswordSet(Core::ptHistory))
|
||||
{
|
||||
SetPasswordDialog dialog;
|
||||
if (dialog.exec())
|
||||
if (setChatLogsPassword())
|
||||
{
|
||||
QString pswd = dialog.getPassword();
|
||||
if (pswd.size() == 0)
|
||||
encrytionState = false;
|
||||
|
||||
Core::getInstance()->setPassword(pswd, Core::ptHistory);
|
||||
} else {
|
||||
encrytionState = false;
|
||||
Core::getInstance()->clearPassword(Core::ptHistory);
|
||||
bodyUI->cbEncryptHistory->setChecked(true);
|
||||
bodyUI->changeLogsPwButton->setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Settings::getInstance().setEncryptLogs(encrytionState);
|
||||
if (encrytionState && !HistoryKeeper::checkPassword())
|
||||
else
|
||||
{
|
||||
if (QMessageBox::Ok != QMessageBox::warning(nullptr, tr("Encrypted log"),
|
||||
tr("You already have history log file encrypted with different password\nDo you want to delete old history file?"),
|
||||
QMessageBox::Ok | QMessageBox::Cancel))
|
||||
QMessageBox::StandardButton button = QMessageBox::warning(
|
||||
Widget::getInstance(),
|
||||
tr("Old encrypted chat history", "title"),
|
||||
tr("Would you like to decrypt your chat history?\nOtherwise it will be deleted."),
|
||||
QMessageBox::Ok | QMessageBox::No | QMessageBox::Cancel,
|
||||
QMessageBox::Cancel
|
||||
);
|
||||
|
||||
if (button == QMessageBox::Ok)
|
||||
{
|
||||
// TODO: ask user about reencryption with new password
|
||||
encrytionState = false;
|
||||
QList<HistoryKeeper::HistMessage> oldMessages = HistoryKeeper::exportMessagesDeleteFile(true);
|
||||
core->clearPassword(Core::ptHistory);
|
||||
Settings::getInstance().setEncryptLogs(false);
|
||||
HistoryKeeper::getInstance()->importMessages(oldMessages);
|
||||
}
|
||||
else if (button == QMessageBox::No)
|
||||
{
|
||||
if (QMessageBox::critical(
|
||||
Widget::getInstance(),
|
||||
tr("Old encrypted chat history", "title"),
|
||||
tr("Are you sure you want to lose your entire chat history?"),
|
||||
QMessageBox::Yes | QMessageBox::Cancel,
|
||||
QMessageBox::Cancel
|
||||
)
|
||||
== QMessageBox::Yes)
|
||||
{
|
||||
HistoryKeeper::removeHistory(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyUI->cbEncryptHistory->setChecked(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bodyUI->cbEncryptHistory->setChecked(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Settings::getInstance().setEncryptLogs(encrytionState);
|
||||
bodyUI->cbEncryptHistory->setChecked(encrytionState);
|
||||
core->clearPassword(Core::ptHistory);
|
||||
Settings::getInstance().setEncryptLogs(false);
|
||||
bodyUI->cbEncryptHistory->setChecked(false);
|
||||
bodyUI->changeLogsPwButton->setEnabled(false);
|
||||
HistoryKeeper::resetInstance();
|
||||
}
|
||||
|
||||
if (encrytionState)
|
||||
HistoryKeeper::resetInstance();
|
||||
bool PrivacyForm::setToxPassword()
|
||||
{
|
||||
Core* core = Core::getInstance();
|
||||
SetPasswordDialog* dialog;
|
||||
QString body = tr("Please set your new data file password.");
|
||||
if (core->isPasswordSet(Core::ptHistory))
|
||||
dialog = new SetPasswordDialog(body, tr("Use chat history password", "pushbutton text"), this);
|
||||
else
|
||||
dialog = new SetPasswordDialog(body, QString(), this);
|
||||
|
||||
if (!Settings::getInstance().getEncryptLogs())
|
||||
Core::getInstance()->clearPassword(Core::ptHistory);
|
||||
if (int r = dialog->exec())
|
||||
{
|
||||
QString newpw = dialog->getPassword();
|
||||
delete dialog;
|
||||
|
||||
if (r == SetPasswordDialog::Tertiary)
|
||||
core->useOtherPassword(Core::ptMain);
|
||||
else
|
||||
core->setPassword(newpw, Core::ptMain);
|
||||
|
||||
Settings::getInstance().setEncryptTox(true);
|
||||
core->saveConfiguration();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete dialog;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void PrivacyForm::onEncryptToxUpdated()
|
||||
{
|
||||
bool encrytionState = bodyUI->cbEncryptTox->isChecked();
|
||||
Core* core = Core::getInstance();
|
||||
|
||||
if (encrytionState)
|
||||
if (bodyUI->cbEncryptTox->isChecked())
|
||||
{
|
||||
if (!Core::getInstance()->isPasswordSet(Core::ptMain))
|
||||
if (!core->isPasswordSet(Core::ptMain))
|
||||
{
|
||||
SetPasswordDialog dialog;
|
||||
if (dialog.exec())
|
||||
if (setToxPassword())
|
||||
{
|
||||
QString pswd = dialog.getPassword();
|
||||
if (pswd.size() == 0)
|
||||
encrytionState = false;
|
||||
|
||||
Core::getInstance()->setPassword(pswd, Core::ptMain);
|
||||
} else {
|
||||
encrytionState = false;
|
||||
Core::getInstance()->clearPassword(Core::ptMain);
|
||||
bodyUI->cbEncryptTox->setChecked(true);
|
||||
bodyUI->changeToxPwButton->setEnabled(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!GUI::askQuestion(tr("Decrypt your data file", "title"), tr("Would you like to decrypt your data file?")))
|
||||
{
|
||||
bodyUI->cbEncryptTox->setChecked(true);
|
||||
return;
|
||||
}
|
||||
// affirmative answer falls through to the catch all below
|
||||
}
|
||||
|
||||
bodyUI->cbEncryptTox->setChecked(encrytionState);
|
||||
Settings::getInstance().setEncryptTox(encrytionState);
|
||||
|
||||
if (!Settings::getInstance().getEncryptTox())
|
||||
Core::getInstance()->clearPassword(Core::ptMain);
|
||||
bodyUI->cbEncryptTox->setChecked(false);
|
||||
Settings::getInstance().setEncryptTox(false);
|
||||
bodyUI->changeToxPwButton->setEnabled(false);
|
||||
core->clearPassword(Core::ptMain);
|
||||
}
|
||||
|
||||
void PrivacyForm::setNospam()
|
||||
|
@ -148,8 +268,10 @@ void PrivacyForm::present()
|
|||
bodyUI->cbTypingNotification->setChecked(Settings::getInstance().isTypingNotificationEnabled());
|
||||
bodyUI->cbKeepHistory->setChecked(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptHistory->setChecked(Settings::getInstance().getEncryptLogs());
|
||||
bodyUI->changeLogsPwButton->setEnabled(Settings::getInstance().getEncryptLogs());
|
||||
bodyUI->cbEncryptHistory->setEnabled(Settings::getInstance().getEnableLogging());
|
||||
bodyUI->cbEncryptTox->setChecked(Settings::getInstance().getEncryptTox());
|
||||
bodyUI->changeToxPwButton->setEnabled(Settings::getInstance().getEncryptTox());
|
||||
}
|
||||
|
||||
void PrivacyForm::generateRandomNospam()
|
||||
|
|
|
@ -39,7 +39,9 @@ private slots:
|
|||
void generateRandomNospam();
|
||||
void onNospamEdit();
|
||||
void onEncryptLogsUpdated();
|
||||
bool setChatLogsPassword();
|
||||
void onEncryptToxUpdated();
|
||||
bool setToxPassword();
|
||||
|
||||
private:
|
||||
Ui::PrivacySettings* bodyUI;
|
||||
|
|
|
@ -54,11 +54,11 @@
|
|||
<item>
|
||||
<widget class="QCheckBox" name="cbKeepHistory">
|
||||
<property name="toolTip">
|
||||
<string comment="toolTip for Keep History setting">History keeping is still in development.
|
||||
<string comment="toolTip for Keep History setting">Chat history keeping is still in development.
|
||||
Save format changes are possible, which may result in data loss.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Keep History (unstable)</string>
|
||||
<string>Keep chat history (mostly stable)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -68,31 +68,63 @@ Save format changes are possible, which may result in data loss.</string>
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Encryption</string>
|
||||
<string>Local file encryption</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbEncryptTox">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<widget class="QLabel" name="encryptStatement">
|
||||
<property name="text">
|
||||
<string>Encrypt Tox datafile</string>
|
||||
<string>All Tox communications over the internet are encrypted, and this cannot be disabled. However, you may optionally password protect your local Tox files.</string>
|
||||
</property>
|
||||
<property name="wordWrap">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbEncryptHistory">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Encrypt History</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="encryptToxHLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbEncryptTox">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Encrypt Tox data file</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="changeToxPwButton">
|
||||
<property name="text">
|
||||
<string>Change password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="encryptLogsHLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="cbEncryptHistory">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Encrypt chat history</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="changeLogsPwButton">
|
||||
<property name="text">
|
||||
<string>Change password</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
|
|
308
src/widget/gui.cpp
Normal file
|
@ -0,0 +1,308 @@
|
|||
#include "gui.h"
|
||||
#include "src/nexus.h"
|
||||
#include <assert.h>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QInputDialog>
|
||||
#include <QLabel>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QThread>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include "androidgui.h"
|
||||
#else
|
||||
#include "widget.h"
|
||||
#endif
|
||||
|
||||
GUI::GUI(QObject *parent) :
|
||||
QObject(parent)
|
||||
{
|
||||
assert(QThread::currentThread() == qApp->thread());
|
||||
|
||||
#ifndef Q_OS_ANDROID
|
||||
assert(Nexus::getDesktopGUI());
|
||||
connect(Nexus::getDesktopGUI(), &Widget::resized, this, &GUI::resized);
|
||||
#endif
|
||||
}
|
||||
|
||||
GUI& GUI::getInstance()
|
||||
{
|
||||
static GUI gui;
|
||||
return gui;
|
||||
}
|
||||
|
||||
// Implementation of the public clean interface
|
||||
|
||||
void GUI::setEnabled(bool state)
|
||||
{
|
||||
if (QThread::currentThread() == qApp->thread())
|
||||
{
|
||||
getInstance()._setEnabled(state);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(&getInstance(), "_setEnabled", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(bool, state));
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::setWindowTitle(const QString& title)
|
||||
{
|
||||
if (QThread::currentThread() == qApp->thread())
|
||||
{
|
||||
getInstance()._setWindowTitle(title);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(&getInstance(), "_setWindowTitle", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, title));
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::reloadTheme()
|
||||
{
|
||||
if (QThread::currentThread() == qApp->thread())
|
||||
{
|
||||
getInstance()._reloadTheme();
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(&getInstance(), "_reloadTheme", Qt::BlockingQueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::showInfo(const QString& title, const QString& msg)
|
||||
{
|
||||
if (QThread::currentThread() == qApp->thread())
|
||||
{
|
||||
getInstance()._showInfo(title, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(&getInstance(), "_showInfo", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg));
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::showWarning(const QString& title, const QString& msg)
|
||||
{
|
||||
if (QThread::currentThread() == qApp->thread())
|
||||
{
|
||||
getInstance()._showWarning(title, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(&getInstance(), "_showWarning", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg));
|
||||
}
|
||||
}
|
||||
|
||||
void GUI::showError(const QString& title, const QString& msg)
|
||||
{
|
||||
if (QThread::currentThread() == qApp->thread())
|
||||
{
|
||||
getInstance()._showError(title, msg);
|
||||
}
|
||||
else
|
||||
{
|
||||
QMetaObject::invokeMethod(&getInstance(), "_showError", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg));
|
||||
}
|
||||
}
|
||||
|
||||
bool GUI::askQuestion(const QString& title, const QString& msg,
|
||||
bool defaultAns, bool warning)
|
||||
{
|
||||
if (QThread::currentThread() == qApp->thread())
|
||||
{
|
||||
return getInstance()._askQuestion(title, msg, defaultAns, warning);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool ret;
|
||||
QMetaObject::invokeMethod(&getInstance(), "_askQuestion", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, ret),
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg),
|
||||
Q_ARG(bool, defaultAns), Q_ARG(bool, warning));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
QString GUI::itemInputDialog(QWidget * parent, const QString & title,
|
||||
const QString & label, const QStringList & items,
|
||||
int current, bool editable, bool * ok,
|
||||
Qt::WindowFlags flags,
|
||||
Qt::InputMethodHints hints)
|
||||
{
|
||||
if (QThread::currentThread() == qApp->thread())
|
||||
{
|
||||
return getInstance()._itemInputDialog(parent, title, label, items, current, editable, ok, flags, hints);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString r;
|
||||
QMetaObject::invokeMethod(&getInstance(), "_itemInputDialog", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QString, r),
|
||||
Q_ARG(QWidget*, parent), Q_ARG(const QString&, title),
|
||||
Q_ARG(const QString&,label), Q_ARG(const QStringList&, items),
|
||||
Q_ARG(int, current), Q_ARG(bool, editable), Q_ARG(bool*, ok),
|
||||
Q_ARG(Qt::WindowFlags, flags), Q_ARG(Qt::InputMethodHints, hints));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
QString GUI::passwordDialog(const QString& cancel, const QString& body)
|
||||
{
|
||||
if (QThread::currentThread() == qApp->thread())
|
||||
{
|
||||
return getInstance()._passwordDialog(cancel, body);
|
||||
}
|
||||
else
|
||||
{
|
||||
QString r;
|
||||
QMetaObject::invokeMethod(&getInstance(), "_passwordDialog", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QString, r),
|
||||
Q_ARG(const QString&, cancel), Q_ARG(const QString&, body));
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
// Private implementations
|
||||
|
||||
void GUI::_setEnabled(bool state)
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
Nexus::getAndroidGUI()->setEnabled(state);
|
||||
#else
|
||||
Nexus::getDesktopGUI()->setEnabled(state);
|
||||
#endif
|
||||
}
|
||||
|
||||
void GUI::_setWindowTitle(const QString& title)
|
||||
{
|
||||
if (title.isEmpty())
|
||||
getMainWidget()->setWindowTitle("qTox");
|
||||
else
|
||||
getMainWidget()->setWindowTitle("qTox - " +title);
|
||||
}
|
||||
|
||||
void GUI::_reloadTheme()
|
||||
{
|
||||
#ifndef Q_OS_ANDROID
|
||||
Nexus::getDesktopGUI()->reloadTheme();
|
||||
#endif
|
||||
}
|
||||
|
||||
void GUI::_showInfo(const QString& title, const QString& msg)
|
||||
{
|
||||
QMessageBox::information(getMainWidget(), title, msg);
|
||||
}
|
||||
|
||||
void GUI::_showWarning(const QString& title, const QString& msg)
|
||||
{
|
||||
QMessageBox::warning(getMainWidget(), title, msg);
|
||||
}
|
||||
|
||||
void GUI::_showError(const QString& title, const QString& msg)
|
||||
{
|
||||
QMessageBox::critical(getMainWidget(), title, msg);
|
||||
}
|
||||
|
||||
bool GUI::_askQuestion(const QString& title, const QString& msg,
|
||||
bool defaultAns, bool warning)
|
||||
{
|
||||
if (warning)
|
||||
{
|
||||
QMessageBox::StandardButton def = QMessageBox::Cancel;
|
||||
if (defaultAns)
|
||||
def = QMessageBox::Ok;
|
||||
return QMessageBox::warning(getMainWidget(), title, msg, QMessageBox::Ok | QMessageBox::Cancel, def) == QMessageBox::Ok;
|
||||
}
|
||||
else
|
||||
{
|
||||
QMessageBox::StandardButton def = QMessageBox::No;
|
||||
if (defaultAns)
|
||||
def = QMessageBox::Yes;
|
||||
return QMessageBox::question(getMainWidget(), title, msg, QMessageBox::Yes | QMessageBox::No, def) == QMessageBox::Yes;
|
||||
}
|
||||
}
|
||||
|
||||
QString GUI::_itemInputDialog(QWidget * parent, const QString & title,
|
||||
const QString & label, const QStringList & items,
|
||||
int current, bool editable, bool * ok,
|
||||
Qt::WindowFlags flags,
|
||||
Qt::InputMethodHints hints)
|
||||
{
|
||||
return QInputDialog::getItem(parent, title, label, items, current, editable, ok, flags, hints);
|
||||
}
|
||||
|
||||
QString GUI::_passwordDialog(const QString& cancel, const QString& body)
|
||||
{
|
||||
// we use a hack. It is considered that closing the dialog without explicitly clicking
|
||||
// disable history is confusing. But we can't distinguish between clicking the cancel
|
||||
// button and closing the dialog. So instead, we reverse the Ok and Cancel roles,
|
||||
// so that nothing but explicitly clicking disable history closes the dialog
|
||||
QString ret;
|
||||
QInputDialog dialog;
|
||||
dialog.setWindowTitle(tr("Enter your password"));
|
||||
dialog.setOkButtonText(cancel);
|
||||
dialog.setCancelButtonText(tr("Decrypt"));
|
||||
dialog.setInputMode(QInputDialog::TextInput);
|
||||
dialog.setTextEchoMode(QLineEdit::Password);
|
||||
dialog.setLabelText(body);
|
||||
|
||||
// problem with previous hack: the default button is disable history, not decrypt.
|
||||
// use another hack to reverse the default buttons.
|
||||
// http://www.qtcentre.org/threads/49924-Change-property-of-QInputDialog-button
|
||||
QList<QDialogButtonBox*> l = dialog.findChildren<QDialogButtonBox*>();
|
||||
if (!l.isEmpty())
|
||||
{
|
||||
QPushButton* ok = l.first()->button(QDialogButtonBox::Ok);
|
||||
QPushButton* cancel = l.first()->button(QDialogButtonBox::Cancel);
|
||||
if (ok && cancel)
|
||||
{
|
||||
ok->setAutoDefault(false);
|
||||
ok->setDefault(false);
|
||||
cancel->setAutoDefault(true);
|
||||
cancel->setDefault(true);
|
||||
}
|
||||
else
|
||||
qWarning() << "PasswordDialog: Missing button!";
|
||||
}
|
||||
else
|
||||
qWarning() << "PasswordDialog: No QDialogButtonBox!";
|
||||
|
||||
// using similar code, set QLabels to wrap
|
||||
for (auto* label : dialog.findChildren<QLabel*>())
|
||||
label->setWordWrap(true);
|
||||
|
||||
while (true)
|
||||
{
|
||||
int val = dialog.exec();
|
||||
if (val == QDialog::Accepted)
|
||||
return QString();
|
||||
else
|
||||
{
|
||||
ret = dialog.textValue();
|
||||
if (!ret.isEmpty())
|
||||
return ret;
|
||||
}
|
||||
dialog.setTextValue("");
|
||||
dialog.setLabelText(body + "\n\n" + tr("You must enter a non-empty password:"));
|
||||
}
|
||||
}
|
||||
|
||||
// Other
|
||||
|
||||
QWidget* GUI::getMainWidget()
|
||||
{
|
||||
QWidget* maingui{nullptr};
|
||||
#ifdef Q_OS_ANDROID
|
||||
maingui = Nexus::getAndroidGUI();
|
||||
#else
|
||||
maingui = Nexus::getDesktopGUI();
|
||||
#endif
|
||||
return maingui;
|
||||
}
|
75
src/widget/gui.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
#ifndef GUI_H
|
||||
#define GUI_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class QWidget;
|
||||
|
||||
/// Abstracts the GUI from the target backend (AndroidGUI, DesktopGUI, ...)
|
||||
/// All the functions exposed here are thread-safe
|
||||
/// Prefer calling this class to calling a GUI backend directly
|
||||
class GUI : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static GUI& getInstance();
|
||||
/// Returns the main QWidget* of the application
|
||||
static QWidget* getMainWidget();
|
||||
/// Will enable or disable the GUI.
|
||||
/// A disabled GUI can't be interacted with by the user
|
||||
static void setEnabled(bool state);
|
||||
/// Change the title of the main window
|
||||
/// This is usually always visible to the user
|
||||
static void setWindowTitle(const QString& title);
|
||||
/// Reloads the application theme and redraw the window
|
||||
static void reloadTheme();
|
||||
/// Show some text to the user, for example in a message box
|
||||
static void showInfo(const QString& title, const QString& msg);
|
||||
/// Show a warning to the user, for example in a message box
|
||||
static void showWarning(const QString& title, const QString& msg);
|
||||
/// Show an error to the user, for example in a message box
|
||||
static void showError(const QString& title, const QString& msg);
|
||||
/// Asks the user a question, for example in a message box.
|
||||
/// If warning is true, we will use a special warning style.
|
||||
/// Returns the answer.
|
||||
static bool askQuestion(const QString& title, const QString& msg,
|
||||
bool defaultAns = false, bool warning = true);
|
||||
/// Asks the user to input text and returns the answer.
|
||||
/// The interface is equivalent to QInputDialog::getItem()
|
||||
static QString itemInputDialog(QWidget * parent, const QString & title,
|
||||
const QString & label, const QStringList & items,
|
||||
int current = 0, bool editable = true, bool * ok = 0,
|
||||
Qt::WindowFlags flags = 0,
|
||||
Qt::InputMethodHints hints = Qt::ImhNone);
|
||||
/// Asks the user to answer a password
|
||||
/// cancel is the text on the cancel button and body
|
||||
/// is descriptive text that will be shown to the user
|
||||
static QString passwordDialog(const QString& cancel, const QString& body);
|
||||
|
||||
signals:
|
||||
/// Emitted when the GUI is resized on supported platforms
|
||||
/// Guaranteed to work on desktop platforms
|
||||
void resized();
|
||||
|
||||
private:
|
||||
explicit GUI(QObject *parent = 0);
|
||||
|
||||
// Private implementation, those must be called from the GUI thread
|
||||
private slots:
|
||||
void _setEnabled(bool state);
|
||||
void _setWindowTitle(const QString& title);
|
||||
void _reloadTheme();
|
||||
void _showInfo(const QString& title, const QString& msg);
|
||||
void _showWarning(const QString& title, const QString& msg);
|
||||
void _showError(const QString& title, const QString& msg);
|
||||
bool _askQuestion(const QString& title, const QString& msg,
|
||||
bool defaultAns = false, bool warning = true);
|
||||
QString _itemInputDialog(QWidget * parent, const QString & title,
|
||||
const QString & label, const QStringList & items,
|
||||
int current = 0, bool editable = true, bool * ok = 0,
|
||||
Qt::WindowFlags flags = 0,
|
||||
Qt::InputMethodHints inputMethodHints = Qt::ImhNone);
|
||||
QString _passwordDialog(const QString& cancel, const QString& body);
|
||||
};
|
||||
|
||||
#endif // GUI_H
|
|
@ -42,6 +42,11 @@ SystemTrayIcon::SystemTrayIcon()
|
|||
}
|
||||
}
|
||||
|
||||
SystemTrayIcon::~SystemTrayIcon()
|
||||
{
|
||||
qDebug() << "Deleting SystemTrayIcon";
|
||||
}
|
||||
|
||||
QString SystemTrayIcon::extractIconToFile(QIcon icon, QString name)
|
||||
{
|
||||
QString iconPath;
|
||||
|
|
|
@ -12,6 +12,7 @@ class SystemTrayIcon : public QObject
|
|||
Q_OBJECT
|
||||
public:
|
||||
SystemTrayIcon();
|
||||
~SystemTrayIcon();
|
||||
void setContextMenu(QMenu* menu);
|
||||
void show();
|
||||
void hide();
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
*/
|
||||
|
||||
#include "toxsave.h"
|
||||
#include "widget.h"
|
||||
#include "gui.h"
|
||||
#include "src/core.h"
|
||||
#include "src/misc/settings.h"
|
||||
#include <QCoreApplication>
|
||||
|
@ -30,12 +30,6 @@ void toxSaveEventHandler(const QByteArray& eventData)
|
|||
handleToxSave(eventData);
|
||||
}
|
||||
|
||||
static bool checkContinue(const QString& title, const QString& msg)
|
||||
{
|
||||
QMessageBox::StandardButton resp = QMessageBox::question(Widget::getInstance(), title, msg, QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
|
||||
return resp == QMessageBox::Yes;
|
||||
}
|
||||
|
||||
void handleToxSave(const QString& path)
|
||||
{
|
||||
Core* core = Core::getInstance();
|
||||
|
@ -59,20 +53,19 @@ void handleToxSave(const QString& path)
|
|||
|
||||
if (info.suffix() != "tox")
|
||||
{
|
||||
QMessageBox::warning(Widget::getInstance(),
|
||||
QObject::tr("Ignoring non-Tox file", "popup title"),
|
||||
QObject::tr("Warning: you've chosen a file that is not a Tox save file; ignoring.", "popup text"));
|
||||
GUI::showWarning(QObject::tr("Ignoring non-Tox file", "popup title"),
|
||||
QObject::tr("Warning: you've chosen a file that is not a Tox save file; ignoring.", "popup text"));
|
||||
return;
|
||||
}
|
||||
|
||||
QString profilePath = QDir(Settings::getSettingsDirPath()).filePath(profile + Core::TOX_EXT);
|
||||
|
||||
if (QFileInfo(profilePath).exists() && !checkContinue(QObject::tr("Profile already exists", "import confirm title"),
|
||||
if (QFileInfo(profilePath).exists() && !GUI::askQuestion(QObject::tr("Profile already exists", "import confirm title"),
|
||||
QObject::tr("A profile named \"%1\" already exists. Do you want to erase it?", "import confirm text").arg(profile)))
|
||||
return;
|
||||
|
||||
QFile::copy(path, profilePath);
|
||||
// no good way to update the ui from here... maybe we need a Widget:refreshUi() function...
|
||||
// such a thing would simplify other code as well I believe
|
||||
QMessageBox::information(Widget::getInstance(), QObject::tr("Profile imported"), QObject::tr("%1.tox was successfully imported").arg(profile));
|
||||
GUI::showInfo(QObject::tr("Profile imported"), QObject::tr("%1.tox was successfully imported").arg(profile));
|
||||
}
|
||||
|
|
|
@ -32,11 +32,13 @@
|
|||
#include "form/chatform.h"
|
||||
#include "maskablepixmapwidget.h"
|
||||
#include "src/historykeeper.h"
|
||||
#include "form/inputpassworddialog.h"
|
||||
#include "src/autoupdate.h"
|
||||
#include "src/audio.h"
|
||||
#include "src/platform/timer.h"
|
||||
#include "systemtrayicon.h"
|
||||
#include "src/nexus.h"
|
||||
#include "src/offlinemsgengine.h"
|
||||
#include <cassert>
|
||||
#include <QMessageBox>
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
|
@ -48,11 +50,18 @@
|
|||
#include <QThread>
|
||||
#include <QFileDialog>
|
||||
#include <QInputDialog>
|
||||
#include <QDialogButtonBox>
|
||||
#include <QTimer>
|
||||
#include <QStyleFactory>
|
||||
#include <QTranslator>
|
||||
#include <tox/tox.h>
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
#define IS_ON_DESKTOP_GUI 0
|
||||
#else
|
||||
#define IS_ON_DESKTOP_GUI 1
|
||||
#endif
|
||||
|
||||
void toxActivateEventHandler(const QByteArray& data)
|
||||
{
|
||||
if (data != "$activate")
|
||||
|
@ -64,8 +73,11 @@ Widget *Widget::instance{nullptr};
|
|||
|
||||
Widget::Widget(QWidget *parent)
|
||||
: QMainWindow(parent),
|
||||
icon{nullptr},
|
||||
ui(new Ui::MainWindow),
|
||||
activeChatroomWidget{nullptr}
|
||||
activeChatroomWidget{nullptr},
|
||||
eventFlag(false),
|
||||
eventIcon(false)
|
||||
{
|
||||
translator = new QTranslator;
|
||||
setTranslation();
|
||||
|
@ -75,29 +87,32 @@ void Widget::init()
|
|||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
idleTimer = new QTimer();
|
||||
idleTimer->start(1000);
|
||||
timer = new QTimer();
|
||||
timer->start(1000);
|
||||
offlineMsgTimer = new QTimer();
|
||||
offlineMsgTimer->start(15000);
|
||||
|
||||
//restore window state
|
||||
restoreGeometry(Settings::getInstance().getWindowGeometry());
|
||||
restoreState(Settings::getInstance().getWindowState());
|
||||
ui->mainSplitter->restoreState(Settings::getInstance().getSplitterState());
|
||||
|
||||
statusOnline = new QAction(tr("Online", "Button to set your status to 'Online'"), this);
|
||||
statusOnline->setIcon(QIcon(":img/status/dot_online.png"));
|
||||
connect(statusOnline, SIGNAL(triggered()), this, SLOT(setStatusOnline()));
|
||||
statusAway = new QAction(tr("Away", "Button to set your status to 'Away'"), this);
|
||||
statusAway->setIcon(QIcon(":img/status/dot_idle.png"));
|
||||
connect(statusAway, SIGNAL(triggered()), this, SLOT(setStatusAway()));
|
||||
statusBusy = new QAction(tr("Busy", "Button to set your status to 'Busy'"), this);
|
||||
statusBusy->setIcon(QIcon(":img/status/dot_busy.png"));
|
||||
connect(statusBusy, SIGNAL(triggered()), this, SLOT(setStatusBusy()));
|
||||
|
||||
if (QSystemTrayIcon::isSystemTrayAvailable())
|
||||
{
|
||||
icon = new SystemTrayIcon;
|
||||
updateTrayIcon();
|
||||
trayMenu = new QMenu;
|
||||
|
||||
statusOnline = new QAction(tr("Online"), this);
|
||||
statusOnline->setIcon(QIcon(":ui/statusButton/dot_online.png"));
|
||||
connect(statusOnline, SIGNAL(triggered()), this, SLOT(setStatusOnline()));
|
||||
statusAway = new QAction(tr("Away"), this);
|
||||
statusAway->setIcon(QIcon(":ui/statusButton/dot_idle.png"));
|
||||
connect(statusAway, SIGNAL(triggered()), this, SLOT(setStatusAway()));
|
||||
statusBusy = new QAction(tr("Busy"), this);
|
||||
statusBusy->setIcon(QIcon(":ui/statusButton/dot_busy.png"));
|
||||
connect(statusBusy, SIGNAL(triggered()), this, SLOT(setStatusBusy()));
|
||||
actionQuit = new QAction(tr("&Quit"), this);
|
||||
connect(actionQuit, SIGNAL(triggered()), qApp, SLOT(quit()));
|
||||
|
||||
|
@ -113,6 +128,9 @@ void Widget::init()
|
|||
this,
|
||||
SLOT(onIconClick(QSystemTrayIcon::ActivationReason)));
|
||||
|
||||
icon->show();
|
||||
icon->hide();
|
||||
|
||||
if (Settings::getInstance().getShowSystemTray())
|
||||
{
|
||||
icon->show();
|
||||
|
@ -121,7 +139,6 @@ void Widget::init()
|
|||
}
|
||||
else
|
||||
this->show();
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -171,12 +188,9 @@ void Widget::init()
|
|||
ui->statusPanel->setStyleSheet(Style::getStylesheet(":/ui/window/statusPanel.css"));
|
||||
|
||||
QMenu *statusButtonMenu = new QMenu(ui->statusButton);
|
||||
QAction* setStatusOnline = statusButtonMenu->addAction(Widget::tr("Online","Button to set your status to 'Online'"));
|
||||
setStatusOnline->setIcon(QIcon(":ui/statusButton/dot_online.png"));
|
||||
QAction* setStatusAway = statusButtonMenu->addAction(Widget::tr("Away","Button to set your status to 'Away'"));
|
||||
setStatusAway->setIcon(QIcon(":ui/statusButton/dot_idle.png"));
|
||||
QAction* setStatusBusy = statusButtonMenu->addAction(Widget::tr("Busy","Button to set your status to 'Busy'"));
|
||||
setStatusBusy->setIcon(QIcon(":ui/statusButton/dot_busy.png"));
|
||||
statusButtonMenu->addAction(statusOnline);
|
||||
statusButtonMenu->addAction(statusAway);
|
||||
statusButtonMenu->addAction(statusBusy);
|
||||
ui->statusButton->setMenu(statusButtonMenu);
|
||||
|
||||
// disable proportional scaling
|
||||
|
@ -190,69 +204,16 @@ void Widget::init()
|
|||
ui->statusButton->setEnabled(false);
|
||||
|
||||
Style::setThemeColor(Settings::getInstance().getThemeColor());
|
||||
Style::applyTheme();
|
||||
|
||||
qRegisterMetaType<Status>("Status");
|
||||
qRegisterMetaType<vpx_image>("vpx_image");
|
||||
qRegisterMetaType<uint8_t>("uint8_t");
|
||||
qRegisterMetaType<uint16_t>("uint16_t");
|
||||
qRegisterMetaType<const int16_t*>("const int16_t*");
|
||||
qRegisterMetaType<int32_t>("int32_t");
|
||||
qRegisterMetaType<int64_t>("int64_t");
|
||||
qRegisterMetaType<QPixmap>("QPixmap");
|
||||
qRegisterMetaType<ToxFile>("ToxFile");
|
||||
qRegisterMetaType<ToxFile::FileDirection>("ToxFile::FileDirection");
|
||||
qRegisterMetaType<Core::PasswordType>("Core::PasswordType");
|
||||
|
||||
QString profilePath = detectProfile();
|
||||
coreThread = new QThread(this);
|
||||
coreThread->setObjectName("qTox Core");
|
||||
core = new Core(Camera::getInstance(), coreThread, profilePath);
|
||||
core->moveToThread(coreThread);
|
||||
connect(coreThread, &QThread::started, core, &Core::start);
|
||||
reloadTheme();
|
||||
|
||||
filesForm = new FilesForm();
|
||||
addFriendForm = new AddFriendForm;
|
||||
settingsWidget = new SettingsWidget();
|
||||
|
||||
connect(settingsWidget, &SettingsWidget::setShowSystemTray, this, &Widget::onSetShowSystemTray);
|
||||
|
||||
connect(core, &Core::connected, this, &Widget::onConnected);
|
||||
connect(core, &Core::disconnected, this, &Widget::onDisconnected);
|
||||
connect(core, &Core::failedToStart, this, &Widget::onFailedToStartCore);
|
||||
connect(core, &Core::badProxy, this, &Widget::onBadProxyCore);
|
||||
connect(core, &Core::statusSet, this, &Widget::onStatusSet);
|
||||
connect(core, &Core::usernameSet, this, &Widget::setUsername);
|
||||
connect(core, &Core::statusMessageSet, this, &Widget::setStatusMessage);
|
||||
connect(core, &Core::selfAvatarChanged, this, &Widget::onSelfAvatarLoaded);
|
||||
Core* core = Nexus::getCore();
|
||||
connect(core, SIGNAL(fileDownloadFinished(const QString&)), filesForm, SLOT(onFileDownloadComplete(const QString&)));
|
||||
connect(core, SIGNAL(fileUploadFinished(const QString&)), filesForm, SLOT(onFileUploadComplete(const QString&)));
|
||||
connect(core, &Core::friendAdded, this, &Widget::addFriend);
|
||||
connect(core, &Core::failedToAddFriend, this, &Widget::addFriendFailed);
|
||||
connect(core, &Core::friendUsernameChanged, this, &Widget::onFriendUsernameChanged);
|
||||
connect(core, &Core::friendStatusChanged, this, &Widget::onFriendStatusChanged);
|
||||
connect(core, &Core::friendStatusMessageChanged, this, &Widget::onFriendStatusMessageChanged);
|
||||
connect(core, &Core::friendRequestReceived, this, &Widget::onFriendRequestReceived);
|
||||
connect(core, &Core::friendMessageReceived, this, &Widget::onFriendMessageReceived);
|
||||
connect(core, &Core::receiptRecieved, this, &Widget::onReceiptRecieved);
|
||||
connect(core, &Core::groupInviteReceived, this, &Widget::onGroupInviteReceived);
|
||||
connect(core, &Core::groupMessageReceived, this, &Widget::onGroupMessageReceived);
|
||||
connect(core, &Core::groupNamelistChanged, this, &Widget::onGroupNamelistChanged);
|
||||
connect(core, &Core::groupTitleChanged, this, &Widget::onGroupTitleChanged);
|
||||
connect(core, &Core::emptyGroupCreated, this, &Widget::onEmptyGroupCreated);
|
||||
connect(core, &Core::avInvite, this, &Widget::playRingtone);
|
||||
connect(core, &Core::blockingClearContacts, this, &Widget::clearContactsList, Qt::BlockingQueuedConnection);
|
||||
connect(core, &Core::blockingGetPassword, this, &Widget::getPassword, Qt::BlockingQueuedConnection);
|
||||
connect(core, &Core::friendTypingChanged, this, &Widget::onFriendTypingChanged);
|
||||
|
||||
connect(core, SIGNAL(messageSentResult(int,QString,int)), this, SLOT(onMessageSendResult(int,QString,int)));
|
||||
connect(core, SIGNAL(groupSentResult(int,QString,int)), this, SLOT(onGroupSendResult(int,QString,int)));
|
||||
|
||||
connect(this, &Widget::statusSet, core, &Core::setStatus);
|
||||
connect(this, &Widget::friendRequested, core, &Core::requestFriendship);
|
||||
connect(this, &Widget::friendRequestAccepted, core, &Core::acceptFriendRequest);
|
||||
connect(this, &Widget::changeProfile, core, &Core::switchConfiguration);
|
||||
|
||||
connect(settingsWidget, &SettingsWidget::setShowSystemTray, this, &Widget::onSetShowSystemTray);
|
||||
connect(ui->addButton, SIGNAL(clicked()), this, SLOT(onAddClicked()));
|
||||
connect(ui->groupButton, SIGNAL(clicked()), this, SLOT(onGroupClicked()));
|
||||
connect(ui->transferButton, SIGNAL(clicked()), this, SLOT(onTransferClicked()));
|
||||
|
@ -261,13 +222,10 @@ void Widget::init()
|
|||
connect(ui->statusLabel, SIGNAL(textChanged(QString, QString)), this, SLOT(onStatusMessageChanged(QString, QString)));
|
||||
connect(ui->mainSplitter, &QSplitter::splitterMoved, this, &Widget::onSplitterMoved);
|
||||
connect(profilePicture, SIGNAL(clicked()), this, SLOT(onAvatarClicked()));
|
||||
connect(setStatusOnline, SIGNAL(triggered()), this, SLOT(setStatusOnline()));
|
||||
connect(setStatusAway, SIGNAL(triggered()), this, SLOT(setStatusAway()));
|
||||
connect(setStatusBusy, SIGNAL(triggered()), this, SLOT(setStatusBusy()));
|
||||
connect(addFriendForm, SIGNAL(friendRequested(QString, QString)), this, SIGNAL(friendRequested(QString, QString)));
|
||||
connect(idleTimer, &QTimer::timeout, this, &Widget::onUserAwayCheck);
|
||||
|
||||
coreThread->start();
|
||||
connect(timer, &QTimer::timeout, this, &Widget::onUserAwayCheck);
|
||||
connect(timer, &QTimer::timeout, this, &Widget::onEventIconTick);
|
||||
connect(offlineMsgTimer, &QTimer::timeout, &OfflineMsgEngine::processAllMsgs);
|
||||
|
||||
addFriendForm->show(*ui);
|
||||
|
||||
|
@ -297,34 +255,32 @@ void Widget::setTranslation()
|
|||
|
||||
void Widget::updateTrayIcon()
|
||||
{
|
||||
if (!icon)
|
||||
return;
|
||||
QString status = ui->statusButton->property("status").toString();
|
||||
QString pic;
|
||||
QString color = Settings::getInstance().getLightTrayIcon() ? "light" : "dark";
|
||||
if (status == "online")
|
||||
pic = ":img/taskbar/" + color + "/taskbar_online_2x.png";
|
||||
else if (status == "away")
|
||||
pic = ":img/taskbar/" + color + "/taskbar_idle_2x.png";
|
||||
else if (status == "busy")
|
||||
pic = ":img/taskbar/" + color + "/taskbar_busy_2x.png";
|
||||
QString status;
|
||||
if (eventIcon)
|
||||
status = "event";
|
||||
else
|
||||
pic = ":img/taskbar/" + color + "/taskbar_offline_2x.png";
|
||||
icon->setIcon(QIcon(pic));
|
||||
{
|
||||
status = ui->statusButton->property("status").toString();
|
||||
if (!status.length())
|
||||
status = "offline";
|
||||
}
|
||||
QString color = Settings::getInstance().getLightTrayIcon() ? "light" : "dark";
|
||||
QString pic = ":img/taskbar/" + color + "/taskbar_" + status + ".svg";
|
||||
if (icon)
|
||||
icon->setIcon(QIcon(pic));
|
||||
}
|
||||
|
||||
Widget::~Widget()
|
||||
{
|
||||
qDebug() << "Deleting Widget";
|
||||
core->saveConfiguration();
|
||||
qDebug() << "Widget: Deleting Widget";
|
||||
AutoUpdater::abortUpdates();
|
||||
delete core;
|
||||
icon->hide();
|
||||
hideMainForms();
|
||||
delete settingsWidget;
|
||||
delete addFriendForm;
|
||||
delete filesForm;
|
||||
delete idleTimer;
|
||||
delete timer;
|
||||
delete offlineMsgTimer;
|
||||
|
||||
FriendList::clear();
|
||||
GroupList::clear();
|
||||
|
@ -336,6 +292,8 @@ Widget::~Widget()
|
|||
|
||||
Widget* Widget::getInstance()
|
||||
{
|
||||
assert(IS_ON_DESKTOP_GUI); // Widget must only be used on Desktop platforms
|
||||
|
||||
if (!instance)
|
||||
{
|
||||
instance = new Widget();
|
||||
|
@ -344,11 +302,6 @@ Widget* Widget::getInstance()
|
|||
return instance;
|
||||
}
|
||||
|
||||
QThread* Widget::getCoreThread()
|
||||
{
|
||||
return coreThread;
|
||||
}
|
||||
|
||||
void Widget::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
if (Settings::getInstance().getShowSystemTray() && Settings::getInstance().getCloseToTray() == true)
|
||||
|
@ -384,72 +337,9 @@ void Widget::resizeEvent(QResizeEvent *event)
|
|||
emit resized();
|
||||
}
|
||||
|
||||
QString Widget::detectProfile()
|
||||
{
|
||||
QDir dir(Settings::getSettingsDirPath());
|
||||
QString path, profile = Settings::getInstance().getCurrentProfile();
|
||||
path = dir.filePath(profile + Core::TOX_EXT);
|
||||
QFile file(path);
|
||||
if (profile == "" || !file.exists())
|
||||
{
|
||||
Settings::getInstance().setCurrentProfile("");
|
||||
#if 1 // deprecation attempt
|
||||
// if the last profile doesn't exist, fall back to old "data"
|
||||
path = dir.filePath(Core::CONFIG_FILE_NAME);
|
||||
QFile file(path);
|
||||
if (file.exists())
|
||||
return path;
|
||||
else if (QFile(path = dir.filePath("tox_save")).exists()) // also import tox_save if no data
|
||||
return path;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
profile = askProfiles();
|
||||
if (profile != "")
|
||||
return dir.filePath(profile + Core::TOX_EXT);
|
||||
else
|
||||
return "";
|
||||
}
|
||||
}
|
||||
else
|
||||
return path;
|
||||
}
|
||||
|
||||
QList<QString> Widget::searchProfiles()
|
||||
{
|
||||
QList<QString> out;
|
||||
QDir dir(Settings::getSettingsDirPath());
|
||||
dir.setFilter(QDir::Files | QDir::NoDotAndDotDot);
|
||||
dir.setNameFilters(QStringList("*.tox"));
|
||||
for (QFileInfo file : dir.entryInfoList())
|
||||
out += file.completeBaseName();
|
||||
return out;
|
||||
}
|
||||
|
||||
QString Widget::askProfiles()
|
||||
{ // TODO: allow user to create new Tox ID, even if a profile already exists
|
||||
QList<QString> profiles = searchProfiles();
|
||||
if (profiles.empty()) return "";
|
||||
bool ok;
|
||||
QString profile = QInputDialog::getItem(this,
|
||||
tr("Choose a profile"),
|
||||
tr("Please choose which identity to use"),
|
||||
profiles,
|
||||
0, // which slot to start on
|
||||
false, // if the user can enter their own input
|
||||
&ok);
|
||||
if (!ok) // user cancelled
|
||||
{
|
||||
qApp->quit();
|
||||
return "";
|
||||
}
|
||||
else
|
||||
return profile;
|
||||
}
|
||||
|
||||
QString Widget::getUsername()
|
||||
{
|
||||
return core->getUsername();
|
||||
return Nexus::getCore()->getUsername();
|
||||
}
|
||||
|
||||
void Widget::onAvatarClicked()
|
||||
|
@ -493,7 +383,7 @@ void Widget::onAvatarClicked()
|
|||
return;
|
||||
}
|
||||
|
||||
core->setAvatar(TOX_AVATAR_FORMAT_PNG, bytes);
|
||||
Nexus::getCore()->setAvatar(TOX_AVATAR_FORMAT_PNG, bytes);
|
||||
}
|
||||
|
||||
void Widget::onSelfAvatarLoaded(const QPixmap& pic)
|
||||
|
@ -553,19 +443,22 @@ void Widget::onStatusSet(Status status)
|
|||
{
|
||||
case Status::Online:
|
||||
ui->statusButton->setProperty("status" ,"online");
|
||||
ui->statusButton->setIcon(QIcon(":img/status/dot_online.png"));
|
||||
break;
|
||||
case Status::Away:
|
||||
ui->statusButton->setProperty("status" ,"away");
|
||||
ui->statusButton->setIcon(QIcon(":img/status/dot_idle.png"));
|
||||
break;
|
||||
case Status::Busy:
|
||||
ui->statusButton->setProperty("status" ,"busy");
|
||||
ui->statusButton->setIcon(QIcon(":img/status/dot_busy.png"));
|
||||
break;
|
||||
case Status::Offline:
|
||||
ui->statusButton->setProperty("status" ,"offline");
|
||||
ui->statusButton->setIcon(QIcon(":img/status/dot_away.png"));
|
||||
break;
|
||||
}
|
||||
updateTrayIcon();
|
||||
Style::repolish(ui->statusButton);
|
||||
}
|
||||
|
||||
void Widget::setWindowTitle(const QString& title)
|
||||
|
@ -589,7 +482,7 @@ void Widget::onAddClicked()
|
|||
|
||||
void Widget::onGroupClicked()
|
||||
{
|
||||
core->createGroup();
|
||||
Nexus::getCore()->createGroup();
|
||||
}
|
||||
|
||||
void Widget::onTransferClicked()
|
||||
|
@ -662,7 +555,7 @@ void Widget::hideMainForms()
|
|||
void Widget::onUsernameChanged(const QString& newUsername, const QString& oldUsername)
|
||||
{
|
||||
setUsername(oldUsername); // restore old username until Core tells us to set it
|
||||
core->setUsername(newUsername);
|
||||
Nexus::getCore()->setUsername(newUsername);
|
||||
}
|
||||
|
||||
void Widget::setUsername(const QString& username)
|
||||
|
@ -679,7 +572,7 @@ void Widget::onStatusMessageChanged(const QString& newStatusMessage, const QStri
|
|||
{
|
||||
ui->statusLabel->setText(oldStatusMessage); // restore old status message until Core tells us to set it
|
||||
ui->statusLabel->setToolTip(oldStatusMessage); // for overlength messsages
|
||||
core->setStatusMessage(newStatusMessage);
|
||||
Nexus::getCore()->setStatusMessage(newStatusMessage);
|
||||
}
|
||||
|
||||
void Widget::setStatusMessage(const QString &statusMessage)
|
||||
|
@ -688,6 +581,12 @@ void Widget::setStatusMessage(const QString &statusMessage)
|
|||
ui->statusLabel->setToolTip(statusMessage); // for overlength messsages
|
||||
}
|
||||
|
||||
void Widget::reloadHistory()
|
||||
{
|
||||
for (auto f : FriendList::getAllFriends())
|
||||
f->getChatForm()->loadHistory(QDateTime::currentDateTime().addDays(-7), true);
|
||||
}
|
||||
|
||||
void Widget::addFriend(int friendId, const QString &userId)
|
||||
{
|
||||
//qDebug() << "Widget: Adding friend with id" << userId;
|
||||
|
@ -696,6 +595,7 @@ void Widget::addFriend(int friendId, const QString &userId)
|
|||
QLayout* layout = contactListWidget->getFriendLayout(Status::Offline);
|
||||
layout->addWidget(newfriend->getFriendWidget());
|
||||
|
||||
Core* core = Nexus::getCore();
|
||||
connect(settingsWidget, &SettingsWidget::compactToggled, newfriend->getFriendWidget(), &GenericChatroomWidget::onCompactChanged);
|
||||
connect(newfriend->getFriendWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*)));
|
||||
connect(newfriend->getFriendWidget(), SIGNAL(removeFriend(int)), this, SLOT(removeFriend(int)));
|
||||
|
@ -786,7 +686,7 @@ void Widget::onFriendStatusChanged(int friendId, Status status)
|
|||
|
||||
if (isActualChange && status != Status::Offline)
|
||||
{ // wait a little
|
||||
QTimer::singleShot(250, f->getChatForm(), SLOT(deliverOfflineMsgs()));
|
||||
QTimer::singleShot(250, f->getChatForm()->getOfflineMsgEngine(), SLOT(deliverOfflineMsgs()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -850,7 +750,7 @@ void Widget::onReceiptRecieved(int friendId, int receipt)
|
|||
if (!f)
|
||||
return;
|
||||
|
||||
f->getChatForm()->dischargeReceipt(receipt);
|
||||
f->getChatForm()->getOfflineMsgEngine()->dischargeReceipt(receipt);
|
||||
}
|
||||
|
||||
void Widget::newMessageAlert(GenericChatroomWidget* chat)
|
||||
|
@ -861,6 +761,9 @@ void Widget::newMessageAlert(GenericChatroomWidget* chat)
|
|||
|
||||
QApplication::alert(this);
|
||||
|
||||
if (inactiveWindow)
|
||||
eventFlag = true;
|
||||
|
||||
if (Settings::getInstance().getShowWindow())
|
||||
{
|
||||
show();
|
||||
|
@ -914,7 +817,7 @@ void Widget::removeFriend(Friend* f, bool fake)
|
|||
onAddClicked();
|
||||
}
|
||||
FriendList::removeFriend(f->getFriendID(), fake);
|
||||
core->removeFriend(f->getFriendID(), fake);
|
||||
Nexus::getCore()->removeFriend(f->getFriendID(), fake);
|
||||
delete f;
|
||||
if (ui->mainHead->layout()->isEmpty())
|
||||
onAddClicked();
|
||||
|
@ -945,7 +848,7 @@ void Widget::copyFriendIdToClipboard(int friendId)
|
|||
if (f != nullptr)
|
||||
{
|
||||
QClipboard *clipboard = QApplication::clipboard();
|
||||
clipboard->setText(core->getFriendAddress(f->getFriendID()), QClipboard::Clipboard);
|
||||
clipboard->setText(Nexus::getCore()->getFriendAddress(f->getFriendID()), QClipboard::Clipboard);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -953,7 +856,7 @@ void Widget::onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray in
|
|||
{
|
||||
if (type == TOX_GROUPCHAT_TYPE_TEXT || type == TOX_GROUPCHAT_TYPE_AV)
|
||||
{
|
||||
int groupId = core->joinGroupchat(friendId, type, (uint8_t*)invite.data(), invite.length());
|
||||
int groupId = Nexus::getCore()->joinGroupchat(friendId, type, (uint8_t*)invite.data(), invite.length());
|
||||
if (groupId < 0)
|
||||
{
|
||||
qWarning() << "Widget::onGroupInviteReceived: Unable to accept group invite";
|
||||
|
@ -1000,7 +903,7 @@ void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Cha
|
|||
g = createGroup(groupnumber);
|
||||
}
|
||||
|
||||
QString name = core->getGroupPeerName(groupnumber, peernumber);
|
||||
QString name = Nexus::getCore()->getGroupPeerName(groupnumber, peernumber);
|
||||
TOX_CHAT_CHANGE change = static_cast<TOX_CHAT_CHANGE>(Change);
|
||||
if (change == TOX_CHAT_CHANGE_PEER_ADD)
|
||||
{
|
||||
|
@ -1020,7 +923,7 @@ void Widget::onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t Cha
|
|||
// g->getChatForm()->addSystemInfoMessage(tr("%1 has left the chat").arg(name), "white", QDateTime::currentDateTime());
|
||||
}
|
||||
else if (change == TOX_CHAT_CHANGE_PEER_NAME) // core overwrites old name before telling us it changed...
|
||||
g->updatePeer(peernumber,core->getGroupPeerName(groupnumber, peernumber));
|
||||
g->updatePeer(peernumber,Nexus::getCore()->getGroupPeerName(groupnumber, peernumber));
|
||||
}
|
||||
|
||||
void Widget::onGroupTitleChanged(int groupnumber, const QString& author, const QString& title)
|
||||
|
@ -1043,7 +946,7 @@ void Widget::removeGroup(Group* g, bool fake)
|
|||
onAddClicked();
|
||||
}
|
||||
GroupList::removeGroup(g->getGroupId(), fake);
|
||||
core->removeGroup(g->getGroupId(), fake);
|
||||
Nexus::getCore()->removeGroup(g->getGroupId(), fake);
|
||||
delete g;
|
||||
if (ui->mainHead->layout()->isEmpty())
|
||||
onAddClicked();
|
||||
|
@ -1057,11 +960,6 @@ void Widget::removeGroup(int groupId)
|
|||
removeGroup(GroupList::findGroup(groupId));
|
||||
}
|
||||
|
||||
Core *Widget::getCore()
|
||||
{
|
||||
return core;
|
||||
}
|
||||
|
||||
Group *Widget::createGroup(int groupId)
|
||||
{
|
||||
Group* g = GroupList::findGroup(groupId);
|
||||
|
@ -1077,6 +975,7 @@ Group *Widget::createGroup(int groupId)
|
|||
layout->addWidget(newgroup->getGroupWidget());
|
||||
newgroup->getGroupWidget()->updateStatusLight();
|
||||
|
||||
Core* core = Nexus::getCore();
|
||||
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), this, SLOT(onChatroomWidgetClicked(GenericChatroomWidget*)));
|
||||
connect(newgroup->getGroupWidget(), SIGNAL(removeGroup(int)), this, SLOT(removeGroup(int)));
|
||||
connect(newgroup->getGroupWidget(), SIGNAL(chatroomWidgetClicked(GenericChatroomWidget*)), newgroup->getChatForm(), SLOT(focusInput()));
|
||||
|
@ -1101,20 +1000,20 @@ bool Widget::isFriendWidgetCurActiveWidget(Friend* f)
|
|||
|
||||
bool Widget::event(QEvent * e)
|
||||
{
|
||||
switch(e->type()) {
|
||||
switch(e->type())
|
||||
{
|
||||
case QEvent::WindowActivate:
|
||||
if (activeChatroomWidget != nullptr)
|
||||
{
|
||||
activeChatroomWidget->resetEventFlags();
|
||||
activeChatroomWidget->updateStatusLight();
|
||||
}
|
||||
case QEvent::MouseButtonPress:
|
||||
case QEvent::MouseButtonRelease:
|
||||
case QEvent::Wheel:
|
||||
case QEvent::KeyPress:
|
||||
case QEvent::KeyRelease:
|
||||
if (autoAwayActive)
|
||||
onUserAwayCheck(); // Just so we get back from away faster when interacting with app
|
||||
if (eventFlag)
|
||||
{
|
||||
eventFlag = false;
|
||||
eventIcon = false;
|
||||
updateTrayIcon();
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1150,19 +1049,28 @@ void Widget::onUserAwayCheck()
|
|||
#endif
|
||||
}
|
||||
|
||||
void Widget::onEventIconTick()
|
||||
{
|
||||
if (eventFlag)
|
||||
{
|
||||
eventIcon ^= true;
|
||||
updateTrayIcon();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setStatusOnline()
|
||||
{
|
||||
core->setStatus(Status::Online);
|
||||
Nexus::getCore()->setStatus(Status::Online);
|
||||
}
|
||||
|
||||
void Widget::setStatusAway()
|
||||
{
|
||||
core->setStatus(Status::Away);
|
||||
Nexus::getCore()->setStatus(Status::Away);
|
||||
}
|
||||
|
||||
void Widget::setStatusBusy()
|
||||
{
|
||||
core->setStatus(Status::Busy);
|
||||
Nexus::getCore()->setStatus(Status::Busy);
|
||||
}
|
||||
|
||||
void Widget::onMessageSendResult(int friendId, const QString& message, int messageId)
|
||||
|
@ -1185,20 +1093,6 @@ void Widget::onGroupSendResult(int groupId, const QString& message, int result)
|
|||
g->getChatForm()->addSystemInfoMessage(tr("Message failed to send"), ChatMessage::INFO, QDateTime::currentDateTime());
|
||||
}
|
||||
|
||||
void Widget::getPassword(QString info, int passtype, uint8_t* salt)
|
||||
{
|
||||
Core::PasswordType pt = static_cast<Core::PasswordType>(passtype);
|
||||
InputPasswordDialog dialog(info);
|
||||
if (dialog.exec())
|
||||
{
|
||||
QString pswd = dialog.getPassword();
|
||||
if (pswd.isEmpty())
|
||||
core->clearPassword(pt);
|
||||
else
|
||||
core->setPassword(pswd, pt, salt);
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::onFriendTypingChanged(int friendId, bool isTyping)
|
||||
{
|
||||
Friend* f = FriendList::findFriend(friendId);
|
||||
|
@ -1229,69 +1123,21 @@ void Widget::onSplitterMoved(int pos, int index)
|
|||
saveSplitterGeometry();
|
||||
}
|
||||
|
||||
QMessageBox::StandardButton Widget::showWarningMsgBox(const QString& title, const QString& msg, QMessageBox::StandardButtons buttons)
|
||||
{
|
||||
// We can only display widgets from the GUI thread
|
||||
if (QThread::currentThread() != qApp->thread())
|
||||
{
|
||||
QMessageBox::StandardButton ret;
|
||||
QMetaObject::invokeMethod(this, "showWarningMsgBox", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(QMessageBox::StandardButton, ret),
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg),
|
||||
Q_ARG(QMessageBox::StandardButtons, buttons));
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QMessageBox::warning(this, title, msg, buttons);
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::setEnabledThreadsafe(bool enabled)
|
||||
{
|
||||
// We can only do this from the GUI thread
|
||||
if (QThread::currentThread() != qApp->thread())
|
||||
{
|
||||
QMetaObject::invokeMethod(this, "setEnabledThreadsafe", Qt::BlockingQueuedConnection,
|
||||
Q_ARG(bool, enabled));
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
return setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
|
||||
bool Widget::askMsgboxQuestion(const QString& title, const QString& msg)
|
||||
{
|
||||
// We can only display widgets from the GUI thread
|
||||
if (QThread::currentThread() != qApp->thread())
|
||||
{
|
||||
bool ret;
|
||||
QMetaObject::invokeMethod(this, "askMsgboxQuestion", Qt::BlockingQueuedConnection,
|
||||
Q_RETURN_ARG(bool, ret),
|
||||
Q_ARG(const QString&, title), Q_ARG(const QString&, msg));
|
||||
return ret;
|
||||
}
|
||||
else
|
||||
{
|
||||
return QMessageBox::question(this, title, msg) == QMessageBox::StandardButton::Yes;
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::clearAllReceipts()
|
||||
{
|
||||
QList<Friend*> frnds = FriendList::getAllFriends();
|
||||
for (Friend *f : frnds)
|
||||
{
|
||||
f->getChatForm()->clearReciepts();
|
||||
f->getChatForm()->getOfflineMsgEngine()->removeAllReciepts();
|
||||
}
|
||||
}
|
||||
|
||||
void Widget::reloadTheme()
|
||||
{
|
||||
QString statusPanelStyle = Style::getStylesheet(":/ui/window/statusPanel.css");
|
||||
ui->tooliconsZone->setStyleSheet(Style::resolve("QPushButton{background-color:@themeDark;border:none;}QPushButton:hover{background-color:@themeMediumDark;border:none;}"));
|
||||
ui->statusPanel->setStyleSheet(Style::getStylesheet(":/ui/window/statusPanel.css"));
|
||||
ui->statusPanel->setStyleSheet(statusPanelStyle);
|
||||
ui->statusHead->setStyleSheet(statusPanelStyle);
|
||||
ui->friendList->setStyleSheet(Style::getStylesheet(":ui/friendList/friendList.css"));
|
||||
ui->statusButton->setStyleSheet(Style::getStylesheet(":ui/statusButton/statusButton.css"));
|
||||
|
||||
|
|
|
@ -54,21 +54,14 @@ public:
|
|||
explicit Widget(QWidget *parent = 0);
|
||||
void setCentralWidget(QWidget *widget, const QString &widgetName);
|
||||
QString getUsername();
|
||||
Core* getCore();
|
||||
QThread* getCoreThread();
|
||||
Camera* getCamera();
|
||||
static Widget* getInstance();
|
||||
void newMessageAlert(GenericChatroomWidget* chat);
|
||||
bool isFriendWidgetCurActiveWidget(Friend* f);
|
||||
bool getIsWindowMinimized();
|
||||
static QList<QString> searchProfiles();
|
||||
void clearContactsList();
|
||||
void setTranslation();
|
||||
void updateTrayIcon();
|
||||
Q_INVOKABLE QMessageBox::StandardButton showWarningMsgBox(const QString& title, const QString& msg,
|
||||
QMessageBox::StandardButtons buttonss = QMessageBox::Ok);
|
||||
Q_INVOKABLE void setEnabledThreadsafe(bool enabled);
|
||||
Q_INVOKABLE bool askMsgboxQuestion(const QString& title, const QString& msg);
|
||||
~Widget();
|
||||
|
||||
virtual void closeEvent(QCloseEvent *event);
|
||||
|
@ -76,6 +69,7 @@ public:
|
|||
virtual void resizeEvent(QResizeEvent *event);
|
||||
|
||||
void clearAllReceipts();
|
||||
void reloadHistory();
|
||||
|
||||
void reloadTheme();
|
||||
|
||||
|
@ -83,6 +77,29 @@ public slots:
|
|||
void onSettingsClicked();
|
||||
void setWindowTitle(const QString& title);
|
||||
void forceShow();
|
||||
void onConnected();
|
||||
void onDisconnected();
|
||||
void onStatusSet(Status status);
|
||||
void onFailedToStartCore();
|
||||
void onBadProxyCore();
|
||||
void onSelfAvatarLoaded(const QPixmap &pic);
|
||||
void setUsername(const QString& username);
|
||||
void setStatusMessage(const QString &statusMessage);
|
||||
void addFriend(int friendId, const QString& userId);
|
||||
void addFriendFailed(const QString& userId, const QString& errorInfo = QString());
|
||||
void onFriendStatusChanged(int friendId, Status status);
|
||||
void onFriendStatusMessageChanged(int friendId, const QString& message);
|
||||
void onFriendUsernameChanged(int friendId, const QString& username);
|
||||
void onFriendMessageReceived(int friendId, const QString& message, bool isAction);
|
||||
void onFriendRequestReceived(const QString& userId, const QString& message);
|
||||
void onReceiptRecieved(int friendId, int receipt);
|
||||
void onEmptyGroupCreated(int groupId);
|
||||
void onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray invite);
|
||||
void onGroupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
|
||||
void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
|
||||
void onGroupTitleChanged(int groupnumber, const QString& author, const QString& title);
|
||||
void playRingtone();
|
||||
void onFriendTypingChanged(int friendId, bool isTyping);
|
||||
|
||||
signals:
|
||||
void friendRequestAccepted(const QString& userId);
|
||||
|
@ -95,34 +112,13 @@ signals:
|
|||
void resized();
|
||||
|
||||
private slots:
|
||||
void onConnected();
|
||||
void onDisconnected();
|
||||
void onStatusSet(Status status);
|
||||
void onAddClicked();
|
||||
void onGroupClicked();
|
||||
void onTransferClicked();
|
||||
void onFailedToStartCore();
|
||||
void onBadProxyCore();
|
||||
void onAvatarClicked();
|
||||
void onSelfAvatarLoaded(const QPixmap &pic);
|
||||
void onUsernameChanged(const QString& newUsername, const QString& oldUsername);
|
||||
void onStatusMessageChanged(const QString& newStatusMessage, const QString& oldStatusMessage);
|
||||
void setUsername(const QString& username);
|
||||
void setStatusMessage(const QString &statusMessage);
|
||||
void addFriend(int friendId, const QString& userId);
|
||||
void addFriendFailed(const QString& userId, const QString& errorInfo = QString());
|
||||
void onFriendStatusChanged(int friendId, Status status);
|
||||
void onFriendStatusMessageChanged(int friendId, const QString& message);
|
||||
void onFriendUsernameChanged(int friendId, const QString& username);
|
||||
void onChatroomWidgetClicked(GenericChatroomWidget *);
|
||||
void onFriendMessageReceived(int friendId, const QString& message, bool isAction);
|
||||
void onFriendRequestReceived(const QString& userId, const QString& message);
|
||||
void onReceiptRecieved(int friendId, int receipt);
|
||||
void onEmptyGroupCreated(int groupId);
|
||||
void onGroupInviteReceived(int32_t friendId, uint8_t type, QByteArray invite);
|
||||
void onGroupMessageReceived(int groupnumber, int peernumber, const QString& message, bool isAction);
|
||||
void onGroupNamelistChanged(int groupnumber, int peernumber, uint8_t change);
|
||||
void onGroupTitleChanged(int groupnumber, const QString& author, const QString& title);
|
||||
void removeFriend(int friendId);
|
||||
void copyFriendIdToClipboard(int friendId);
|
||||
void removeGroup(int groupId);
|
||||
|
@ -131,11 +127,9 @@ private slots:
|
|||
void setStatusBusy();
|
||||
void onMessageSendResult(int friendId, const QString& message, int messageId);
|
||||
void onGroupSendResult(int groupId, const QString& message, int result);
|
||||
void playRingtone();
|
||||
void onIconClick(QSystemTrayIcon::ActivationReason);
|
||||
void onUserAwayCheck();
|
||||
void getPassword(QString info, int passtype, uint8_t* salt);
|
||||
void onFriendTypingChanged(int friendId, bool isTyping);
|
||||
void onEventIconTick();
|
||||
void onSetShowSystemTray(bool newValue);
|
||||
void onSplitterMoved(int pos, int index);
|
||||
|
||||
|
@ -148,8 +142,6 @@ private:
|
|||
void removeGroup(Group* g, bool fake = false);
|
||||
void saveWindowGeometry();
|
||||
void saveSplitterGeometry();
|
||||
QString askProfiles();
|
||||
QString detectProfile();
|
||||
SystemTrayIcon *icon;
|
||||
QMenu *trayMenu;
|
||||
QAction *statusOnline,
|
||||
|
@ -160,8 +152,6 @@ private:
|
|||
Ui::MainWindow *ui;
|
||||
QSplitter *centralLayout;
|
||||
QPoint dragPosition;
|
||||
Core* core;
|
||||
QThread* coreThread;
|
||||
AddFriendForm* addFriendForm;
|
||||
SettingsWidget* settingsWidget;
|
||||
FilesForm* filesForm;
|
||||
|
@ -172,9 +162,11 @@ private:
|
|||
bool notify(QObject *receiver, QEvent *event);
|
||||
bool autoAwayActive = false;
|
||||
Status beforeDisconnect = Status::Offline;
|
||||
QTimer* idleTimer;
|
||||
QTimer* timer, *offlineMsgTimer;
|
||||
QTranslator* translator;
|
||||
QRegExp nameMention, sanitizedNameMention;
|
||||
bool eventFlag;
|
||||
bool eventIcon;
|
||||
};
|
||||
|
||||
void toxActivateEventHandler(const QByteArray& data);
|
||||
|
|
141
translations/de.ts
vendored
|
@ -743,10 +743,6 @@ Alias:</translation>
|
|||
<source>Autoaccept files</source>
|
||||
<translation>Dateien automatisch annehmen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Save files in</source>
|
||||
<translation type="vanished">Speichern unter</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/generalsettings.ui" line="215"/>
|
||||
<source>PushButton</source>
|
||||
|
@ -1260,24 +1256,119 @@ Soll die alte Historiedatei gelöscht werden?</translation>
|
|||
<translation>Tippen anzeigen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="60"/>
|
||||
<source>Keep History (unstable)</source>
|
||||
<translation>Historie behalten (instabil)</translation>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="61"/>
|
||||
<source>Keep chat history (mostly stable)</source>
|
||||
<translation>Chatverlauf speichern (größtenteils stabil)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="70"/>
|
||||
<source>Encryption</source>
|
||||
<translation>Verschlüsselung</translation>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="71"/>
|
||||
<source>Local file encryption</source>
|
||||
<translation>Lokale Dateien verschlüsseln</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="79"/>
|
||||
<source>Encrypt Tox datafile</source>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="77"/>
|
||||
<source>All Tox communications over the internet are encrypted, and this cannot be disabled. However, you may optionally password protect your local Tox files.</source>
|
||||
<translation>Die gesamte Tox-Kommunikation über das Internet ist verschlüsselt und kann auch nicht deaktiviert werden. Es ist optional auch möglich, die lokal gespeicherten Tox-Daten mit einem Passwort zu schützen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="92"/>
|
||||
<source>Encrypt Tox data file</source>
|
||||
<translation>Tox Datendatei verschlüsseln</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="89"/>
|
||||
<source>Encrypt History</source>
|
||||
<translation>Historie verschlüsseln</translation>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="113"/>
|
||||
<source>Encrypt chat history</source>
|
||||
<translation>Chatverlauf verschlüsseln</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="99"/>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="123"/>
|
||||
<source>Change password</source>
|
||||
<translation>Passwort ändern</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="77"/>
|
||||
<source>Please set your new chat history password.</source>
|
||||
<translation>Bitte Passwort zum Verschlüsseln des Chatverlaufs setzen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="79"/>
|
||||
<source>It appears you have an unused encrypted chat history; if the password matches, it will be added to your current history.</source>
|
||||
<translation>Es scheint als gäbe es einen unverschlüsselten Chatverlauf. Wenn die Passwörter übereinstimmen wird dieser zum aktuellen Chatverlauf hinzugefügt.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="82"/>
|
||||
<source>Use data file password</source>
|
||||
<translation>Nutze das Tox-Datendatei Passwort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="109"/>
|
||||
<source>Successfully decrypted old chat history</source>
|
||||
<translation>Alter Chatverlauf erfolgreich entschlüsselt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="109"/>
|
||||
<source>You have succesfully decrypted the old chat history, and it has been added to your current history and re-encrypted.</source>
|
||||
<translation>Der alte Chatverlauf wurde erfolgreich entschlüsselt, zum aktuellen Chatverlauf hinzugefügt und wieder verschlüsselt.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="116"/>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="145"/>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="162"/>
|
||||
<source>Old encrypted chat history</source>
|
||||
<translation>Alter verschlüsselter Chatverlauf</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="116"/>
|
||||
<source>There is currently an unused encrypted chat history, but the password you just entered doesn't match.
|
||||
Would you like to try again?
|
||||
Canceling will delete the old history and set the password to what you just entered.</source>
|
||||
<translation>Es gibt aktuell einen ungenutzten verschlüsselten Chatverlauf, aber das eingegebene Passwort stimmt nicht überein.
|
||||
Möchten Sie ein anderes probieren?
|
||||
Wenn Sie abbrechen wird der alte Chatverlauf gelöscht und das soeben eingegebene Passwort verwendet.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="116"/>
|
||||
<source>This happens when enabling encryption after previously \"Disabling History\"</source>
|
||||
<translation>Dies passiert, wenn die Verschlüsselung nach einem vorherigen Deaktivieren des Chatverlaufs wieder aktiviert wird.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="146"/>
|
||||
<source>Would you like to decrypt your chat history?
|
||||
Otherwise it will be deleted.</source>
|
||||
<translation>Möchten Sie den Chatverlauf entschlüsseln?
|
||||
Ansonsten wird dieser gelöscht.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="163"/>
|
||||
<source>Are you sure you want to lose your entire chat history?</source>
|
||||
<translation>Sind Sie sicher, dass der gesamte Chatverlauf gelöscht werden soll?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="195"/>
|
||||
<source>Please set your new data file password.</source>
|
||||
<translation>Bitte Passwort zum Verschlüsseln der Datendatei setzen.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="197"/>
|
||||
<source>Use chat history password</source>
|
||||
<translation>Nutze das Chatverlauf-Passwort</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="240"/>
|
||||
<source>Decrypt your data file</source>
|
||||
<translation>Datendatei entschlüsseln</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacyform.cpp" line="240"/>
|
||||
<source>Would you like to decrypt your data file?</source>
|
||||
<translation>Möchten Sie die Datendatei entschlüsseln?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/setpassworddialog.cpp" line="33"/>
|
||||
<location filename="../src/widget/form/setpassworddialog.cpp" line="61"/>
|
||||
<source>The passwords don't match.</source>
|
||||
<translation>Die Passwörter stimmen nicht überein.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/settings/privacysettings.ui" line="102"/>
|
||||
|
@ -1399,14 +1490,24 @@ Es wird beim Neustart von qTox installiert.</translation>
|
|||
<context>
|
||||
<name>SetPasswordDialog</name>
|
||||
<message>
|
||||
<location filename="../src/widget/form/setpassworddialog.ui" line="23"/>
|
||||
<source>Type Password</source>
|
||||
<location filename="../src/widget/form/setpassworddialog.ui" line="14"/>
|
||||
<source>Set your password</source>
|
||||
<translation>Passwort setzen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/setpassworddialog.ui" line="41"/>
|
||||
<source>Type password</source>
|
||||
<translation>Passwort eingeben</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/setpassworddialog.ui" line="37"/>
|
||||
<source>Repeat Password</source>
|
||||
<translation>Passworteingabe wiederholen</translation>
|
||||
<location filename="../src/widget/form/setpassworddialog.ui" line="31"/>
|
||||
<source>Repeat password</source>
|
||||
<translation>Passwort wiederholen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../src/widget/form/setpassworddialog.ui" line="65"/>
|
||||
<source>Password strength</source>
|
||||
<translation>Passwortstärke</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
|
|