- initial import
13
README.md
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
This is my experiment to get:
|
||||||
|
- simple app to make periodical breaks on my Kubuntu 20.04
|
||||||
|
- learn Linux GUI apps packaging and distribution
|
||||||
|
- avoid too heavy apps for simple task
|
||||||
|
|
||||||
|
Please don't blame on me for this app.
|
||||||
|
My thanks for authors of similar apps ! You inspired me to make this one.
|
||||||
|
|
||||||
|
There is scripts/build_linux.sh script to produce AppImage file.
|
||||||
|
Release part has ready one. But this was built on Kubuntu 20.04 and will require recent versions of runtime libraries.
|
||||||
|
|
||||||
|
Please send me questions and requests on dmytro.bogovych (at) gmail.com.
|
||||||
|
|
||||||
21
app/aboutdlg.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include "aboutdlg.h"
|
||||||
|
#include "ui_aboutdlg.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
AboutDlg::AboutDlg(QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::AboutDlg)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
|
||||||
|
auto version_text = QString("Version %1.%2.%3")
|
||||||
|
.arg(QBREAK_VERSION_MAJOR)
|
||||||
|
.arg(QBREAK_VERSION_MINOR)
|
||||||
|
.arg(QBREAK_VERSION_SUFFIX);
|
||||||
|
ui->mAppVersionLabel->setText(version_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
AboutDlg::~AboutDlg()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
22
app/aboutdlg.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef ABOUTDLG_H
|
||||||
|
#define ABOUTDLG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class AboutDlg;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AboutDlg : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit AboutDlg(QWidget *parent = nullptr);
|
||||||
|
~AboutDlg();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::AboutDlg *ui;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ABOUTDLG_H
|
||||||
133
app/aboutdlg.ui
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>AboutDlg</class>
|
||||||
|
<widget class="QDialog" name="AboutDlg">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>300</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="mAppNameLabel">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>QBreak.</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="mAppVersionLabel">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>25</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Version</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="mAppInfoLabel">
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>16777215</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>App to make breaks in work. Thanks to the authors of similar apps which inspired me to make this one.</string>
|
||||||
|
</property>
|
||||||
|
<property name="textFormat">
|
||||||
|
<enum>Qt::PlainText</enum>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
<property name="wordWrap">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Ok</set>
|
||||||
|
</property>
|
||||||
|
<property name="centerButtons">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>AboutDlg</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>AboutDlg</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>
|
||||||
3
app/assets/images/README.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
URL for used image:
|
||||||
|
https://thenounproject.com/icon/coffee-break-537907/
|
||||||
|
|
||||||
BIN
app/assets/images/app_icon.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
4
app/assets/images/app_icon.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="1200pt" height="1200pt" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m228 72c-9.332 0-14.488 4.707-18.375 9.375-5.1367 6.5391-7.332 15.398-4.125 23.25 18.855 47.793 10.898 59.164 0.375 85.5s-22.758 65.922-0.375 129.75c3.3164 12.883 18.977 20.988 31.5 16.5s19.121-20.566 13.5-32.625c-19.188-54.711-10.637-70.312-0.375-96s23.09-63.176 0.375-120.75c-3.4375-9.0625-12.848-14.941-22.5-15zm324 0c-9.332 0-14.488 4.707-18.375 9.375-5.1367 6.5391-7.332 15.398-4.125 23.25 18.855 47.793 10.898 59.164 0.375 85.5s-22.758 65.922-0.375 129.75c3.3164 12.883 18.977 20.988 31.5 16.5s19.121-20.566 13.5-32.625c-19.188-54.711-10.637-70.312-0.375-96s23.09-63.176 0.375-120.75c-3.4375-9.0625-12.848-14.941-22.5-15zm324 0c-9.332 0-14.488 4.707-18.375 9.375-5.1367 6.5391-7.332 15.398-4.125 23.25 18.855 47.793 10.898 59.164 0.375 85.5s-22.758 65.922-0.375 129.75c3.3164 12.883 18.977 20.988 31.5 16.5s19.121-20.566 13.5-32.625c-19.188-54.711-10.637-70.312-0.375-96s23.09-63.176 0.375-120.75c-3.4375-9.0625-12.848-14.941-22.5-15zm-782.25 360c-11.797 1.1133-21.801 12.148-21.75 24 0 304.82 77.43 453.77 174.75 624h-126.75c-13.297 0-24 10.703-24 24s10.703 24 24 24h864c13.297 0 24-10.703 24-24s-10.703-24-24-24h-126.75c25.375-44.387 49.617-87.379 71.25-132h115.5c46.531 0 84-37.469 84-84v-264c0-46.531-37.465-84-84-84h-13.125c0.69922-19.352 1.125-39.258 1.125-60 0-12.566-11.434-24-24-24h-912c-0.75-0.035156-1.5-0.035156-2.25 0zm27 48h862.5c-4.168 292.51-80.758 424.66-181.5 600h-499.5c-100.74-175.34-177.33-307.49-181.5-600zm907.5 84h15.75c24.383 0 36 11.621 36 36v264c0 24.383-11.617 36-36 36h-93.75c39.703-92.473 68.336-195.91 78-336z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/assets/images/app_icon_dark.png
Normal file
|
After Width: | Height: | Size: 5.2 KiB |
BIN
app/assets/images/app_icon_light.png
Normal file
|
After Width: | Height: | Size: 4.8 KiB |
3
app/assets/images/coffee_cup/README.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
URL: https://ru.seaicons.com/89980/
|
||||||
|
License: https://creativecommons.org/licenses/by-sa/4.0/
|
||||||
|
|
||||||
BIN
app/assets/images/coffee_cup/alecive-flatwoken-apps-caffeine.ico
Normal file
|
After Width: | Height: | Size: 172 KiB |
BIN
app/assets/images/coffee_cup/icon_128x128.png
Normal file
|
After Width: | Height: | Size: 7.4 KiB |
BIN
app/assets/images/coffee_cup/icon_16x16.png
Normal file
|
After Width: | Height: | Size: 674 B |
BIN
app/assets/images/coffee_cup/icon_24x24.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/assets/images/coffee_cup/icon_32x32.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/assets/images/coffee_cup/icon_48x48.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/assets/images/coffee_cup/icon_512x512.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
app/assets/images/coffee_cup/icon_64x64.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
app/assets/images/coffee_cup/icon_72x72.png
Normal file
|
After Width: | Height: | Size: 4.2 KiB |
BIN
app/assets/images/coffee_cup/icon_96x96.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
app/assets/images/noun-coffee-break-537907.png
Normal file
|
After Width: | Height: | Size: 28 KiB |
4
app/assets/images/noun-coffee-break-537907.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="1200pt" height="1200pt" version="1.1" viewBox="0 0 1200 1200" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="m228 72c-9.332 0-14.488 4.707-18.375 9.375-5.1367 6.5391-7.332 15.398-4.125 23.25 18.855 47.793 10.898 59.164 0.375 85.5s-22.758 65.922-0.375 129.75c3.3164 12.883 18.977 20.988 31.5 16.5s19.121-20.566 13.5-32.625c-19.188-54.711-10.637-70.312-0.375-96s23.09-63.176 0.375-120.75c-3.4375-9.0625-12.848-14.941-22.5-15zm324 0c-9.332 0-14.488 4.707-18.375 9.375-5.1367 6.5391-7.332 15.398-4.125 23.25 18.855 47.793 10.898 59.164 0.375 85.5s-22.758 65.922-0.375 129.75c3.3164 12.883 18.977 20.988 31.5 16.5s19.121-20.566 13.5-32.625c-19.188-54.711-10.637-70.312-0.375-96s23.09-63.176 0.375-120.75c-3.4375-9.0625-12.848-14.941-22.5-15zm324 0c-9.332 0-14.488 4.707-18.375 9.375-5.1367 6.5391-7.332 15.398-4.125 23.25 18.855 47.793 10.898 59.164 0.375 85.5s-22.758 65.922-0.375 129.75c3.3164 12.883 18.977 20.988 31.5 16.5s19.121-20.566 13.5-32.625c-19.188-54.711-10.637-70.312-0.375-96s23.09-63.176 0.375-120.75c-3.4375-9.0625-12.848-14.941-22.5-15zm-782.25 360c-11.797 1.1133-21.801 12.148-21.75 24 0 304.82 77.43 453.77 174.75 624h-126.75c-13.297 0-24 10.703-24 24s10.703 24 24 24h864c13.297 0 24-10.703 24-24s-10.703-24-24-24h-126.75c25.375-44.387 49.617-87.379 71.25-132h115.5c46.531 0 84-37.469 84-84v-264c0-46.531-37.465-84-84-84h-13.125c0.69922-19.352 1.125-39.258 1.125-60 0-12.566-11.434-24-24-24h-912c-0.75-0.035156-1.5-0.035156-2.25 0zm27 48h862.5c-4.168 292.51-80.758 424.66-181.5 600h-499.5c-100.74-175.34-177.33-307.49-181.5-600zm907.5 84h15.75c24.383 0 36 11.621 36 36v264c0 24.383-11.617 36-36 36h-93.75c39.703-92.473 68.336-195.91 78-336z"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.7 KiB |
12
app/assets/misc/qbreak.desktop
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
GenericName=App to make the breaks
|
||||||
|
Name=QBreak
|
||||||
|
Version=0.0.5
|
||||||
|
Exec=/usr/bin/qbreak
|
||||||
|
Comment=App to make periodical breaks
|
||||||
|
Icon=qbreak
|
||||||
|
Type=Application
|
||||||
|
Terminal=false
|
||||||
|
StartupNotify=false
|
||||||
|
Encoding=UTF-8
|
||||||
|
Categories=Utility;
|
||||||
140
app/autostart.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#include "autostart.h"
|
||||||
|
#include <QDebug>
|
||||||
|
|
||||||
|
#if defined(TARGET_LINUX)
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <pwd.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QDir>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
#define QBREAK_DESKTOP_NAME "qbreak.desktop"
|
||||||
|
|
||||||
|
static std::string read_desktop_file()
|
||||||
|
{
|
||||||
|
QFile f(":/assets/misc/qbreak.desktop");
|
||||||
|
f.open(QFile::ReadOnly);
|
||||||
|
auto bytes = f.readAll();
|
||||||
|
return bytes.toStdString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string fixup_desktop_file(const std::string& desktop_unit, const std::string& path)
|
||||||
|
{
|
||||||
|
// load set with first file
|
||||||
|
std::istringstream inf(desktop_unit);
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
std::string line;
|
||||||
|
for (unsigned int i=0; std::getline(inf,line); i++)
|
||||||
|
{
|
||||||
|
if (line.find("Exec=") == 0)
|
||||||
|
{
|
||||||
|
line = "Exec=" + path;
|
||||||
|
}
|
||||||
|
lines.push_back(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
std::copy(lines.begin(), lines.end(), std::ostream_iterator<std::string>(oss, "\n"));
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
static fs::path home_dir()
|
||||||
|
{
|
||||||
|
struct passwd *pw = getpwuid(getuid());
|
||||||
|
return pw ? pw->pw_dir : std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
static fs::path autostart_dir()
|
||||||
|
{
|
||||||
|
return home_dir() / ".config" / "autostart";
|
||||||
|
}
|
||||||
|
|
||||||
|
static fs::path autostart_path()
|
||||||
|
{
|
||||||
|
return autostart_dir() / QBREAK_DESKTOP_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
void autostart::enable(const std::string& path_to_me)
|
||||||
|
{
|
||||||
|
// Put .desktop file to ~/.config/autostart
|
||||||
|
std::ofstream ofs(autostart_path());
|
||||||
|
if (ofs.is_open())
|
||||||
|
{
|
||||||
|
ofs << fixup_desktop_file(read_desktop_file(), path_to_me);
|
||||||
|
ofs.close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qDebug() << "Failed to write the desktop entry into autostart dir. Error: " << errno;
|
||||||
|
}
|
||||||
|
|
||||||
|
void autostart::disable()
|
||||||
|
{
|
||||||
|
// Remove .desktop file from ~/.config/autostart
|
||||||
|
::remove(autostart_path().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool autostart::is_enabled()
|
||||||
|
{
|
||||||
|
return fs::exists(fs::path(autostart_path()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- appmenu -----
|
||||||
|
static fs::path appmenu_install_dir()
|
||||||
|
{
|
||||||
|
// Global one
|
||||||
|
// return fs::path("/usr/share/applications");
|
||||||
|
|
||||||
|
// User only
|
||||||
|
return home_dir() / ".local" / "share" / "applications";
|
||||||
|
}
|
||||||
|
|
||||||
|
void appmenu::install(const std::string& path_to_me)
|
||||||
|
{
|
||||||
|
// Put .desktop file to ~/.config/autostart
|
||||||
|
std::ofstream ofs(appmenu_install_dir() / QBREAK_DESKTOP_NAME);
|
||||||
|
if (ofs.is_open())
|
||||||
|
{
|
||||||
|
ofs << fixup_desktop_file(read_desktop_file(), path_to_me);
|
||||||
|
ofs.close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
qDebug() << "Failed to write the desktop entry into app menu dir. Error: " << errno;
|
||||||
|
|
||||||
|
// Install latest icons
|
||||||
|
{
|
||||||
|
auto icons = {"16x16", "32x32", "64x64", "128x128", "512x512"};
|
||||||
|
|
||||||
|
// Here Qt part
|
||||||
|
auto target_dir = QFileInfo(QDir::homePath() + "/.local/share/icons/hicolor").absoluteFilePath();
|
||||||
|
if (QFileInfo::exists(target_dir))
|
||||||
|
{
|
||||||
|
// Copy icons from resources
|
||||||
|
for (auto& icon_suffix: icons)
|
||||||
|
{
|
||||||
|
QString icon_src = QString(":/assets/images/coffee_cup/icon_") + icon_suffix + ".png",
|
||||||
|
icon_dst = target_dir + "/" + icon_suffix + "/apps/qbreak.png";
|
||||||
|
QFile::copy(icon_src, icon_dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void appmenu::uninstall()
|
||||||
|
{
|
||||||
|
if (is_installed())
|
||||||
|
fs::remove(appmenu_install_dir() / QBREAK_DESKTOP_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool appmenu::is_installed()
|
||||||
|
{
|
||||||
|
return fs::exists(appmenu_install_dir() / QBREAK_DESKTOP_NAME);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
22
app/autostart.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#ifndef AUTOSTART_H
|
||||||
|
#define AUTOSTART_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class autostart
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void enable(const std::string& path_to_me);
|
||||||
|
static void disable();
|
||||||
|
static bool is_enabled();
|
||||||
|
};
|
||||||
|
|
||||||
|
class appmenu
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void install(const std::string& path_to_me);
|
||||||
|
static void uninstall();
|
||||||
|
static bool is_installed();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // AUTOSTART_H
|
||||||
9
app/config.h
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#ifndef CONFIG_H
|
||||||
|
#define CONFIG_H
|
||||||
|
|
||||||
|
// App version
|
||||||
|
#define QBREAK_VERSION_MAJOR 0
|
||||||
|
#define QBREAK_VERSION_MINOR 0
|
||||||
|
#define QBREAK_VERSION_SUFFIX 5
|
||||||
|
|
||||||
|
#endif // CONFIG_H
|
||||||
57
app/main.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
#include "autostart.h"
|
||||||
|
#include "runguard.h"
|
||||||
|
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QCommandLineParser>
|
||||||
|
#include <QTranslator>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
RunGuard guard("adjfaifaif");
|
||||||
|
if ( !guard.tryToRun() )
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
|
||||||
|
QCoreApplication::setOrganizationName("qbreak.com");
|
||||||
|
QCoreApplication::setOrganizationDomain("qbreak.com");
|
||||||
|
QCoreApplication::setApplicationName("QBreak");
|
||||||
|
|
||||||
|
QTranslator translator;
|
||||||
|
bool ok = translator.load(":/i18n/strings_" + QLocale::system().name());
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
app.installTranslator(&translator);
|
||||||
|
|
||||||
|
app.setQuitOnLastWindowClosed(false);
|
||||||
|
|
||||||
|
QCommandLineParser parser;
|
||||||
|
QCommandLineOption test_1(QStringList() << "t1" << "test_1");
|
||||||
|
parser.addOption(test_1);
|
||||||
|
|
||||||
|
// Run app with break intervals & duration 60 seconds.
|
||||||
|
// This should trigger the notification in 30 seconds before breaks.
|
||||||
|
QCommandLineOption test_2(QStringList() << "t2" << "test_2");
|
||||||
|
parser.addOption(test_2);
|
||||||
|
|
||||||
|
parser.process(app);
|
||||||
|
|
||||||
|
// Put itself into app menu
|
||||||
|
appmenu::install(QFileInfo(argv[0]).absoluteFilePath().toStdString());
|
||||||
|
|
||||||
|
// Main window is full screen window, so start with tray icon only
|
||||||
|
MainWindow w;
|
||||||
|
w.hide();
|
||||||
|
|
||||||
|
if (parser.isSet(test_1))
|
||||||
|
w.test_1();
|
||||||
|
else
|
||||||
|
if (parser.isSet(test_2))
|
||||||
|
w.test_2();
|
||||||
|
|
||||||
|
int retcode = app.exec();
|
||||||
|
|
||||||
|
return retcode;
|
||||||
|
}
|
||||||
379
app/mainwindow.cpp
Normal file
@ -0,0 +1,379 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
#include "settingsdialog.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "autostart.h"
|
||||||
|
#include "aboutdlg.h"
|
||||||
|
|
||||||
|
#include <QMenu>
|
||||||
|
#include <QAction>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
#include <QSvgGenerator>
|
||||||
|
#include <QPalette>
|
||||||
|
#include <QScreen>
|
||||||
|
#include <QWindow>
|
||||||
|
#include <QFileInfo>
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent)
|
||||||
|
, ui(new Ui::MainWindow)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
if (mSettingsDialog)
|
||||||
|
delete mSettingsDialog;
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isDarkTheme()
|
||||||
|
{
|
||||||
|
// Thanks to stackoverflow ! Another idea is to use https://successfulsoftware.net/2021/03/31/how-to-add-a-dark-theme-to-your-qt-application/
|
||||||
|
auto label = QLabel("am I in the dark?");
|
||||||
|
auto text_hsv_value = label.palette().color(QPalette::WindowText).value();
|
||||||
|
auto bg_hsv_value = label.palette().color(QPalette::Background).value();
|
||||||
|
bool dark_theme_found = text_hsv_value > bg_hsv_value;
|
||||||
|
|
||||||
|
return dark_theme_found;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon MainWindow::getAppIcon()
|
||||||
|
{
|
||||||
|
QIcon app_icon(QPixmap(QString(":/assets/images/coffee_cup/icon_128x128.png")));
|
||||||
|
|
||||||
|
return app_icon;
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon MainWindow::getTrayIcon()
|
||||||
|
{
|
||||||
|
// QString app_icon_name = isDarkTheme() ? "app_icon_dark.png" : "app_icon_light.png";
|
||||||
|
// QIcon app_icon(QPixmap(QString(":/assets/images/%1").arg(app_icon_name)));
|
||||||
|
// return app_icon;
|
||||||
|
|
||||||
|
return QIcon(QPixmap(":/assets/images/coffee_cup/icon_64x64.png"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::init()
|
||||||
|
{
|
||||||
|
// Icon
|
||||||
|
setWindowIcon(getAppIcon());
|
||||||
|
|
||||||
|
// ToDo: finish the multiple monitor support
|
||||||
|
/*QDesktopWidget *widget = QApplication::desktop();
|
||||||
|
qDebug() << "Number of screens:" << widget->screenCount();
|
||||||
|
|
||||||
|
connect(widget, &QDesktopWidget::screenCountChanged, this, [](int screenCount){});
|
||||||
|
if (widget->screenCount() > 1) {
|
||||||
|
} else if (widget->screenCount() == 1){
|
||||||
|
}*/
|
||||||
|
|
||||||
|
// Tray icon
|
||||||
|
createTrayIcon();
|
||||||
|
|
||||||
|
// Latest config
|
||||||
|
loadConfig();
|
||||||
|
|
||||||
|
// Settings dialog
|
||||||
|
mSettingsDialog = nullptr;
|
||||||
|
|
||||||
|
// Timer
|
||||||
|
mTimer = new QTimer(this);
|
||||||
|
mTimer->setTimerType(Qt::TimerType::CoarseTimer);
|
||||||
|
mTimer->setSingleShot(true);
|
||||||
|
connect(mTimer, SIGNAL(timeout()), this, SLOT(onLongBreakStart()));
|
||||||
|
|
||||||
|
mNotifyTimer = new QTimer(this);
|
||||||
|
mNotifyTimer->setTimerType(Qt::TimerType::CoarseTimer);
|
||||||
|
mNotifyTimer->setSingleShot(true);
|
||||||
|
connect(mNotifyTimer, SIGNAL(timeout()), this, SLOT(onLongBreakNotify()));
|
||||||
|
|
||||||
|
|
||||||
|
mUpdateUITimer = new QTimer(this);
|
||||||
|
mUpdateUITimer->setTimerType(Qt::TimerType::CoarseTimer);
|
||||||
|
mUpdateUITimer->setSingleShot(false);
|
||||||
|
mUpdateUITimer->setInterval(std::chrono::minutes(1));
|
||||||
|
connect(mUpdateUITimer, SIGNAL(timeout()), this, SLOT(onUpdateUI()));
|
||||||
|
mUpdateUITimer->start();
|
||||||
|
|
||||||
|
mProgressTimer = new QTimer(this);
|
||||||
|
mProgressTimer->setInterval(std::chrono::milliseconds(1000));
|
||||||
|
mProgressTimer->setSingleShot(false);
|
||||||
|
connect(mProgressTimer, SIGNAL(timeout()), this, SLOT(onProgress()));
|
||||||
|
|
||||||
|
connect(ui->mPostponeButton, SIGNAL(clicked()), this, SLOT(onLongBreakPostpone()));
|
||||||
|
|
||||||
|
// Use the latest config
|
||||||
|
applyConfig();
|
||||||
|
|
||||||
|
// Refresh UI
|
||||||
|
onUpdateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::loadConfig()
|
||||||
|
{
|
||||||
|
app_settings settings;
|
||||||
|
mAppConfig = settings.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::applyConfig()
|
||||||
|
{
|
||||||
|
if (mTimer)
|
||||||
|
{
|
||||||
|
if (mTimer->interval() != mAppConfig.longbreak_interval)
|
||||||
|
{
|
||||||
|
mTimer->stop();
|
||||||
|
mTimer->setInterval(std::chrono::seconds(mAppConfig.longbreak_interval));
|
||||||
|
mTimer->start();
|
||||||
|
|
||||||
|
mNotifyTimer->stop();
|
||||||
|
mNotifyTimer->setInterval(std::chrono::seconds(mAppConfig.longbreak_interval - 30));
|
||||||
|
mNotifyTimer->start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mAppConfig.window_on_top)
|
||||||
|
setWindowFlags(windowFlags() | Qt::WindowStaysOnTopHint);
|
||||||
|
else
|
||||||
|
setWindowFlags(windowFlags() & ~Qt::WindowStaysOnTopHint);
|
||||||
|
|
||||||
|
if (mAppConfig.autostart)
|
||||||
|
{
|
||||||
|
QString path_to_me = QFileInfo(QApplication::arguments().front()).canonicalPath();
|
||||||
|
autostart::enable(path_to_me.toStdString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
autostart::disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::schedule()
|
||||||
|
{
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::test_1()
|
||||||
|
{
|
||||||
|
// 10 seconds test break
|
||||||
|
mAppConfig.longbreak_length = 10;
|
||||||
|
mAppConfig.longbreak_postpone_interval = 10;
|
||||||
|
mAppConfig.longbreak_interval = 10;
|
||||||
|
|
||||||
|
mAppConfig.window_on_top = true;
|
||||||
|
mAppConfig.verbose = true;
|
||||||
|
|
||||||
|
onLongBreakStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::test_2()
|
||||||
|
{
|
||||||
|
// 60 seconds test break
|
||||||
|
mAppConfig.longbreak_length = 60;
|
||||||
|
mAppConfig.longbreak_postpone_interval = 60;
|
||||||
|
mAppConfig.longbreak_interval = 60;
|
||||||
|
|
||||||
|
mAppConfig.window_on_top = true;
|
||||||
|
mAppConfig.verbose = true;
|
||||||
|
|
||||||
|
applyConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::showMe()
|
||||||
|
{
|
||||||
|
// Show on primary screen
|
||||||
|
QScreen* screen = QGuiApplication::primaryScreen();
|
||||||
|
showFullScreen();
|
||||||
|
if (windowHandle())
|
||||||
|
{
|
||||||
|
windowHandle()->setScreen(screen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::hideMe()
|
||||||
|
{
|
||||||
|
this->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QString secondsToText(int seconds)
|
||||||
|
{
|
||||||
|
if (seconds < 60)
|
||||||
|
return QString("%1s").arg(seconds);
|
||||||
|
else
|
||||||
|
return QString("%1m %2s").arg(seconds / 60).arg(seconds % 60);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::createTrayIcon()
|
||||||
|
{
|
||||||
|
mTrayIcon = new QSystemTrayIcon(this);
|
||||||
|
QMenu* menu = new QMenu(this);
|
||||||
|
|
||||||
|
QAction* nextBreakAction = new QAction(tr("Start next break"), menu);
|
||||||
|
connect(nextBreakAction, SIGNAL(triggered()), this, SLOT(onNextBreak()));
|
||||||
|
|
||||||
|
QAction* settingsAction = new QAction(tr("Settings"), menu);
|
||||||
|
connect(settingsAction, SIGNAL(triggered()), this, SLOT(onSettings()));
|
||||||
|
|
||||||
|
QAction* aboutAction = new QAction(tr("About"), menu);
|
||||||
|
connect(aboutAction, SIGNAL(triggered()), this, SLOT(onAbout()));
|
||||||
|
|
||||||
|
QAction* exitAction = new QAction(tr("Exit"), menu);
|
||||||
|
connect(exitAction, SIGNAL(triggered()), this, SLOT(onExit()));
|
||||||
|
|
||||||
|
menu->addAction(nextBreakAction);
|
||||||
|
menu->addAction(settingsAction);
|
||||||
|
menu->addAction(aboutAction);
|
||||||
|
menu->addAction(exitAction);
|
||||||
|
|
||||||
|
mTrayIcon->setContextMenu(menu);
|
||||||
|
mTrayIcon->setIcon(getTrayIcon());
|
||||||
|
mTrayIcon->setToolTip(AppName);
|
||||||
|
mTrayIcon->show();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onUpdateUI()
|
||||||
|
{
|
||||||
|
if (mTrayIcon)
|
||||||
|
{
|
||||||
|
if (mProgressTimer->isActive())
|
||||||
|
{
|
||||||
|
// Break is in effect now
|
||||||
|
mTrayIcon->setToolTip(QString());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (mTimer->isActive())
|
||||||
|
{
|
||||||
|
auto remaining = std::chrono::duration_cast<std::chrono::minutes>(mTimer->remainingTimeAsDuration());
|
||||||
|
if (remaining.count() == 0)
|
||||||
|
mTrayIcon->setToolTip(tr("Less than a minute left until the next break."));
|
||||||
|
else
|
||||||
|
mTrayIcon->setToolTip(tr("There are %1 minutes left until the next break.").arg(remaining.count()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onLongBreakNotify()
|
||||||
|
{
|
||||||
|
mTrayIcon->showMessage(tr("New break"),
|
||||||
|
tr("New break will start in %1 secs").arg(Default_Notify_Length),
|
||||||
|
getAppIcon(),
|
||||||
|
30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onLongBreakStart()
|
||||||
|
{
|
||||||
|
qDebug() << "Long break starts for " << secondsToText(mAppConfig.longbreak_postpone_interval);
|
||||||
|
|
||||||
|
ui->mPostponeButton->setText(tr("Postpone for ") + secondsToText(mAppConfig.longbreak_postpone_interval));
|
||||||
|
showMe();
|
||||||
|
|
||||||
|
mBreakStartTime = std::chrono::steady_clock::now();
|
||||||
|
|
||||||
|
// Start progress bar
|
||||||
|
mProgressTimer->start();
|
||||||
|
|
||||||
|
// Refresh UI
|
||||||
|
onUpdateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onLongBreakEnd()
|
||||||
|
{
|
||||||
|
qDebug() << "Long break ends.";
|
||||||
|
|
||||||
|
// Prepare to next triggering
|
||||||
|
ui->mProgressBar->setValue(0);
|
||||||
|
|
||||||
|
// Hide the window
|
||||||
|
hideMe();
|
||||||
|
|
||||||
|
// Start new timer
|
||||||
|
mTimer->start(std::chrono::seconds(mAppConfig.longbreak_interval));
|
||||||
|
mNotifyTimer->start(std::chrono::seconds(mAppConfig.longbreak_interval - 30));
|
||||||
|
|
||||||
|
// Refresh UI
|
||||||
|
onUpdateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onLongBreakPostpone()
|
||||||
|
{
|
||||||
|
qDebug() << "Long break postponed.";
|
||||||
|
hideMe();
|
||||||
|
|
||||||
|
mProgressTimer->stop();
|
||||||
|
ui->mProgressBar->setValue(0);
|
||||||
|
|
||||||
|
// Start timer again
|
||||||
|
mTimer->start(std::chrono::seconds(mAppConfig.longbreak_postpone_interval));
|
||||||
|
mNotifyTimer->start(std::chrono::seconds(mAppConfig.longbreak_postpone_interval - 30));
|
||||||
|
|
||||||
|
// Refresh UI
|
||||||
|
onUpdateUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onProgress()
|
||||||
|
{
|
||||||
|
auto timestamp = std::chrono::steady_clock::now();
|
||||||
|
auto delta = std::chrono::duration_cast<std::chrono::seconds>(timestamp - mBreakStartTime).count();
|
||||||
|
|
||||||
|
int percents = (delta / float(mAppConfig.longbreak_length) * 100);
|
||||||
|
ui->mProgressBar->setValue(percents);
|
||||||
|
int remaining = mAppConfig.longbreak_length - delta;
|
||||||
|
if (remaining < 0)
|
||||||
|
remaining = 0;
|
||||||
|
|
||||||
|
ui->mRemainingLabel->setText(QString("Remaining: ") + secondsToText(remaining));
|
||||||
|
|
||||||
|
if (percents > 100)
|
||||||
|
{
|
||||||
|
mProgressTimer->stop();
|
||||||
|
onLongBreakEnd();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
showMe();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onNextBreak()
|
||||||
|
{
|
||||||
|
mTimer->stop();
|
||||||
|
mNotifyTimer->stop();
|
||||||
|
|
||||||
|
onLongBreakStart();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onSettings()
|
||||||
|
{
|
||||||
|
if (mSettingsDialog == nullptr)
|
||||||
|
{
|
||||||
|
// Settings dialog must be top level window to be positioned on the primary screen
|
||||||
|
mSettingsDialog = new SettingsDialog(nullptr);
|
||||||
|
connect(mSettingsDialog, &QDialog::accepted, [this]()
|
||||||
|
{
|
||||||
|
mAppConfig = app_settings::load();
|
||||||
|
applyConfig();
|
||||||
|
mSettingsDialog->hide();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(mSettingsDialog, &QDialog::rejected, [this]()
|
||||||
|
{
|
||||||
|
mSettingsDialog->hide();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// Move to primary screen
|
||||||
|
mSettingsDialog->show();
|
||||||
|
QScreen* screen = QGuiApplication::primaryScreen();
|
||||||
|
mSettingsDialog->windowHandle()->setScreen(screen);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onAbout()
|
||||||
|
{
|
||||||
|
AboutDlg dlg;
|
||||||
|
dlg.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::onExit()
|
||||||
|
{
|
||||||
|
this->close();
|
||||||
|
QApplication::exit();
|
||||||
|
}
|
||||||
67
app/mainwindow.h
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QSystemTrayIcon>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
#include "settings.h"
|
||||||
|
#include "settingsdialog.h"
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
namespace Ui { class MainWindow; }
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
// Start screen immediately with 15 seconds interval
|
||||||
|
void test_1();
|
||||||
|
|
||||||
|
// Run 60/60 seconds interval & duration
|
||||||
|
void test_2();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
QTimer* mTimer;
|
||||||
|
QTimer* mNotifyTimer;
|
||||||
|
QTimer* mShowNotifyTimer;
|
||||||
|
QTimer* mUpdateUITimer;
|
||||||
|
QTimer* mProgressTimer;
|
||||||
|
QSystemTrayIcon* mTrayIcon;
|
||||||
|
SettingsDialog* mSettingsDialog;
|
||||||
|
std::chrono::steady_clock::time_point mBreakStartTime;
|
||||||
|
app_settings::config mAppConfig;
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void loadConfig();
|
||||||
|
void applyConfig();
|
||||||
|
void schedule();
|
||||||
|
void createTrayIcon();
|
||||||
|
void showMe();
|
||||||
|
void hideMe();
|
||||||
|
|
||||||
|
void startNotification();
|
||||||
|
|
||||||
|
QIcon getAppIcon();
|
||||||
|
QIcon getTrayIcon();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void onUpdateUI();
|
||||||
|
void onLongBreakNotify();
|
||||||
|
void onLongBreakStart();
|
||||||
|
void onLongBreakPostpone();
|
||||||
|
void onLongBreakEnd();
|
||||||
|
void onProgress();
|
||||||
|
void onNextBreak();
|
||||||
|
void onSettings();
|
||||||
|
void onAbout();
|
||||||
|
void onExit();
|
||||||
|
};
|
||||||
|
#endif // MAINWINDOW_H
|
||||||
143
app/mainwindow.ui
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>MainWindow</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralwidget">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>75</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_4">
|
||||||
|
<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>
|
||||||
|
<spacer name="verticalSpacer_6">
|
||||||
|
<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 alignment="Qt::AlignHCenter|Qt::AlignVCenter">
|
||||||
|
<widget class="QProgressBar" name="mProgressBar">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>400</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="mRemainingLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">Remaining:</string>
|
||||||
|
</property>
|
||||||
|
<property name="alignment">
|
||||||
|
<set>Qt::AlignCenter</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_3">
|
||||||
|
<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>
|
||||||
|
<spacer name="verticalSpacer_5">
|
||||||
|
<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 alignment="Qt::AlignHCenter">
|
||||||
|
<widget class="QPushButton" name="mPostponeButton">
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>150</width>
|
||||||
|
<height>0</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>300</width>
|
||||||
|
<height>100</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">Postpone for 10 minutes</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>20</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
||||||
43
app/qbreak.pro
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
QT += core gui svg
|
||||||
|
|
||||||
|
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||||
|
|
||||||
|
CONFIG += c++17 lrelease embed_translations
|
||||||
|
|
||||||
|
# You can make your code fail to compile if it uses deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
aboutdlg.cpp \
|
||||||
|
autostart.cpp \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp \
|
||||||
|
settings.cpp \
|
||||||
|
settingsdialog.cpp \
|
||||||
|
runguard.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
aboutdlg.h \
|
||||||
|
autostart.h \
|
||||||
|
config.h \
|
||||||
|
mainwindow.h \
|
||||||
|
settings.h \
|
||||||
|
settingsdialog.h \
|
||||||
|
runguard.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
aboutdlg.ui \
|
||||||
|
mainwindow.ui \
|
||||||
|
settingsdialog.ui
|
||||||
|
|
||||||
|
RESOURCES = qbreak.qrc
|
||||||
|
|
||||||
|
TRANSLATIONS = strings.ts strings_en.ts strings_ru.ts
|
||||||
|
|
||||||
|
unix:!macx: DEFINES += TARGET_LINUX
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
16
app/qbreak.qrc
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/">
|
||||||
|
<file>assets/images/app_icon_dark.png</file>
|
||||||
|
<file>assets/images/app_icon_light.png</file>
|
||||||
|
<file>assets/misc/qbreak.desktop</file>
|
||||||
|
<file>assets/images/coffee_cup/icon_16x16.png</file>
|
||||||
|
<file>assets/images/coffee_cup/icon_24x24.png</file>
|
||||||
|
<file>assets/images/coffee_cup/icon_32x32.png</file>
|
||||||
|
<file>assets/images/coffee_cup/icon_48x48.png</file>
|
||||||
|
<file>assets/images/coffee_cup/icon_64x64.png</file>
|
||||||
|
<file>assets/images/coffee_cup/icon_72x72.png</file>
|
||||||
|
<file>assets/images/coffee_cup/icon_96x96.png</file>
|
||||||
|
<file>assets/images/coffee_cup/icon_128x128.png</file>
|
||||||
|
<file>assets/images/coffee_cup/icon_512x512.png</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
79
app/runguard.cpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#include "runguard.h"
|
||||||
|
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
|
||||||
|
QString generateKeyHash( const QString& key, const QString& salt )
|
||||||
|
{
|
||||||
|
QByteArray data;
|
||||||
|
|
||||||
|
data.append( key.toUtf8() );
|
||||||
|
data.append( salt.toUtf8() );
|
||||||
|
data = QCryptographicHash::hash( data, QCryptographicHash::Sha1 ).toHex();
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
RunGuard::RunGuard( const QString& key )
|
||||||
|
: key( key )
|
||||||
|
, memLockKey( generateKeyHash( key, "_memLockKey" ) )
|
||||||
|
, sharedmemKey( generateKeyHash( key, "_sharedmemKey" ) )
|
||||||
|
, sharedMem( sharedmemKey )
|
||||||
|
, memLock( memLockKey, 1 )
|
||||||
|
{
|
||||||
|
memLock.acquire();
|
||||||
|
{
|
||||||
|
QSharedMemory fix( sharedmemKey ); // Fix for *nix: http://habrahabr.ru/post/173281/
|
||||||
|
fix.attach();
|
||||||
|
}
|
||||||
|
memLock.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
RunGuard::~RunGuard()
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RunGuard::isAnotherRunning()
|
||||||
|
{
|
||||||
|
if ( sharedMem.isAttached() )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memLock.acquire();
|
||||||
|
const bool isRunning = sharedMem.attach();
|
||||||
|
if ( isRunning )
|
||||||
|
sharedMem.detach();
|
||||||
|
memLock.release();
|
||||||
|
|
||||||
|
return isRunning;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RunGuard::tryToRun()
|
||||||
|
{
|
||||||
|
if ( isAnotherRunning() ) // Extra check
|
||||||
|
return false;
|
||||||
|
|
||||||
|
memLock.acquire();
|
||||||
|
const bool result = sharedMem.create( sizeof( quint64 ) );
|
||||||
|
memLock.release();
|
||||||
|
if ( !result )
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunGuard::release()
|
||||||
|
{
|
||||||
|
memLock.acquire();
|
||||||
|
if ( sharedMem.isAttached() )
|
||||||
|
sharedMem.detach();
|
||||||
|
memLock.release();
|
||||||
|
}
|
||||||
32
app/runguard.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef RUNGUARD_H
|
||||||
|
#define RUNGUARD_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QSharedMemory>
|
||||||
|
#include <QSystemSemaphore>
|
||||||
|
|
||||||
|
|
||||||
|
class RunGuard
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
RunGuard( const QString& key );
|
||||||
|
~RunGuard();
|
||||||
|
|
||||||
|
bool isAnotherRunning();
|
||||||
|
bool tryToRun();
|
||||||
|
void release();
|
||||||
|
|
||||||
|
private:
|
||||||
|
const QString key;
|
||||||
|
const QString memLockKey;
|
||||||
|
const QString sharedmemKey;
|
||||||
|
|
||||||
|
QSharedMemory sharedMem;
|
||||||
|
QSystemSemaphore memLock;
|
||||||
|
|
||||||
|
Q_DISABLE_COPY( RunGuard )
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // RUNGUARD_H
|
||||||
37
app/settings.cpp
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <QSettings>
|
||||||
|
|
||||||
|
// Key names for settings
|
||||||
|
const QString Key_LongBreak_Interval = "LongBreak_Interval";
|
||||||
|
const QString Key_LongBreak_PostponeInterval = "LongBreak_Postpone_Interval";
|
||||||
|
const QString Key_LongBreak_Length = "LongBreak_Length";
|
||||||
|
const QString Key_Window_On_Top = "Window_On_Top";
|
||||||
|
const QString Key_Verbose = "Verbose";
|
||||||
|
const QString Key_Autostart = "Autostart";
|
||||||
|
|
||||||
|
void app_settings::save(const config &cfg)
|
||||||
|
{
|
||||||
|
QSettings s;
|
||||||
|
|
||||||
|
s.setValue(Key_LongBreak_Interval, cfg.longbreak_interval);
|
||||||
|
s.setValue(Key_LongBreak_Length, cfg.longbreak_length);
|
||||||
|
s.setValue(Key_LongBreak_PostponeInterval, cfg.longbreak_postpone_interval);
|
||||||
|
s.setValue(Key_Window_On_Top, cfg.window_on_top);
|
||||||
|
s.setValue(Key_Verbose, cfg.verbose);
|
||||||
|
s.setValue(Key_Autostart, cfg.autostart);
|
||||||
|
}
|
||||||
|
|
||||||
|
app_settings::config app_settings::load()
|
||||||
|
{
|
||||||
|
QSettings s;
|
||||||
|
config r;
|
||||||
|
|
||||||
|
r.longbreak_interval = s.value(Key_LongBreak_Interval, Default_LongBreak_Interval).toInt();
|
||||||
|
r.longbreak_length = s.value(Key_LongBreak_Length, Default_LongBreak_Length).toInt();
|
||||||
|
r.longbreak_postpone_interval = s.value(Key_LongBreak_PostponeInterval, Default_LongBreak_PostponeInterval).toInt();
|
||||||
|
r.window_on_top = s.value(Key_Window_On_Top, Default_Autostart).toBool();
|
||||||
|
r.verbose = s.value(Key_Verbose, Default_Verbose).toBool();
|
||||||
|
r.autostart = s.value(Key_Autostart, Default_Autostart).toBool();
|
||||||
|
return r;
|
||||||
|
}
|
||||||
44
app/settings.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#ifndef SETTINGS_H
|
||||||
|
#define SETTINGS_H
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
|
||||||
|
// Default values in seconds
|
||||||
|
const int Default_LongBreak_Interval = 50 * 60;
|
||||||
|
const int Default_LongBreak_PostponeInterval = 10 * 60;
|
||||||
|
const int Default_LongBreak_Length = 10 * 60;
|
||||||
|
const int Default_Notify_Length = 30;
|
||||||
|
|
||||||
|
// Is break window is running on the top ?
|
||||||
|
const bool Default_WindowOnTop = true;
|
||||||
|
|
||||||
|
// Is verbose debug output
|
||||||
|
const bool Default_Verbose = false;
|
||||||
|
|
||||||
|
// Default autostart
|
||||||
|
const bool Default_Autostart = true;
|
||||||
|
|
||||||
|
|
||||||
|
// Used app name
|
||||||
|
const QString AppName = "QBreak";
|
||||||
|
|
||||||
|
class app_settings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct config
|
||||||
|
{
|
||||||
|
// Seconds
|
||||||
|
int longbreak_interval = Default_LongBreak_Interval;
|
||||||
|
int longbreak_postpone_interval = Default_LongBreak_PostponeInterval;
|
||||||
|
int longbreak_length = Default_LongBreak_Length;
|
||||||
|
bool window_on_top = Default_WindowOnTop;
|
||||||
|
bool verbose = Default_Verbose;
|
||||||
|
bool autostart = Default_Autostart;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void save(const config& cfg);
|
||||||
|
static config load();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // SETTINGS_H
|
||||||
47
app/settingsdialog.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include "settingsdialog.h"
|
||||||
|
#include "ui_settingsdialog.h"
|
||||||
|
#include "settings.h"
|
||||||
|
|
||||||
|
#include <QToolTip>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QWindow>
|
||||||
|
|
||||||
|
const QString ConversionError = "Integer value expected.";
|
||||||
|
|
||||||
|
SettingsDialog::SettingsDialog(QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::SettingsDialog)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsDialog::~SettingsDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::init()
|
||||||
|
{
|
||||||
|
setWindowTitle("Settings");
|
||||||
|
|
||||||
|
auto c = app_settings::load();
|
||||||
|
ui->mAutostartCheckbox->setChecked(c.autostart);
|
||||||
|
ui->mWindowOnTopCheckbox->setChecked(c.window_on_top);
|
||||||
|
ui->mBreakIntervalEdit->setText(QString::number(c.longbreak_interval / 60));
|
||||||
|
ui->mBreakDurationEdit->setText(QString::number(c.longbreak_length / 60));
|
||||||
|
ui->mPostponeTimeEdit->setText(QString::number(c.longbreak_postpone_interval / 60));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SettingsDialog::accept()
|
||||||
|
{
|
||||||
|
auto c = app_settings::load();
|
||||||
|
c.autostart = ui->mAutostartCheckbox->isChecked();
|
||||||
|
c.window_on_top = ui->mWindowOnTopCheckbox->isChecked();
|
||||||
|
c.longbreak_interval = ui->mBreakIntervalEdit->text().toInt() * 60;
|
||||||
|
c.longbreak_length = ui->mBreakDurationEdit->text().toInt() * 60;
|
||||||
|
c.longbreak_postpone_interval = ui->mPostponeTimeEdit->text().toInt() * 60;
|
||||||
|
app_settings::save(c);
|
||||||
|
|
||||||
|
emit accepted();
|
||||||
|
}
|
||||||
27
app/settingsdialog.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#ifndef SETTINGSDIALOG_H
|
||||||
|
#define SETTINGSDIALOG_H
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class SettingsDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsDialog : public QDialog
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SettingsDialog(QWidget *parent = nullptr);
|
||||||
|
~SettingsDialog();
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::SettingsDialog *ui;
|
||||||
|
|
||||||
|
void init();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void accept();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // SETTINGSDIALOG_H
|
||||||
156
app/settingsdialog.ui
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>SettingsDialog</class>
|
||||||
|
<widget class="QDialog" name="SettingsDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>746</width>
|
||||||
|
<height>167</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="minimumSize">
|
||||||
|
<size>
|
||||||
|
<width>746</width>
|
||||||
|
<height>167</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="maximumSize">
|
||||||
|
<size>
|
||||||
|
<width>800</width>
|
||||||
|
<height>400</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Dialog</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizeGripEnabled">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<property name="modal">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QFormLayout" name="formLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>8</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>6</number>
|
||||||
|
</property>
|
||||||
|
<item row="0" column="0">
|
||||||
|
<widget class="QLabel" name="label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Autostart</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="1">
|
||||||
|
<widget class="QCheckBox" name="mAutostartCheckbox">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="label_2">
|
||||||
|
<property name="text">
|
||||||
|
<string>Window on top</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="1">
|
||||||
|
<widget class="QCheckBox" name="mWindowOnTopCheckbox">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QLabel" name="label_3">
|
||||||
|
<property name="text">
|
||||||
|
<string>Break interval (minutes)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="1">
|
||||||
|
<widget class="QLineEdit" name="mBreakIntervalEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="0">
|
||||||
|
<widget class="QLabel" name="label_4">
|
||||||
|
<property name="text">
|
||||||
|
<string>Break duration (minutes)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="3" column="1">
|
||||||
|
<widget class="QLineEdit" name="mBreakDurationEdit"/>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="0">
|
||||||
|
<widget class="QLabel" name="label_5">
|
||||||
|
<property name="text">
|
||||||
|
<string>Postpone time (minutes)</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="4" column="1">
|
||||||
|
<widget class="QLineEdit" name="mPostponeTimeEdit"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</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>SettingsDialog</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>SettingsDialog</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>
|
||||||
113
app/strings.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="en_US" sourcelanguage="en">
|
||||||
|
<context>
|
||||||
|
<name>AboutDlg</name>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="20"/>
|
||||||
|
<source>Dialog</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="32"/>
|
||||||
|
<source>QBreak.</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="51"/>
|
||||||
|
<source>Version</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="70"/>
|
||||||
|
<source>App to make breaks in work. Thanks to the authors of similar apps which inspired me to make this one.</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.ui" line="14"/>
|
||||||
|
<source>MainWindow</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="213"/>
|
||||||
|
<source>Start next break</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="216"/>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="219"/>
|
||||||
|
<source>About</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="222"/>
|
||||||
|
<source>Exit</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="250"/>
|
||||||
|
<source>Less than a minute left until the next break.</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="252"/>
|
||||||
|
<source>There are %1 minutes left until the next break.</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="259"/>
|
||||||
|
<source>New break</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="260"/>
|
||||||
|
<source>New break will start in %1 secs</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="269"/>
|
||||||
|
<source>Postpone for </source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingsDialog</name>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="26"/>
|
||||||
|
<source>Dialog</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="52"/>
|
||||||
|
<source>Autostart</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="66"/>
|
||||||
|
<source>Window on top</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="80"/>
|
||||||
|
<source>Break interval (minutes)</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="90"/>
|
||||||
|
<source>Break duration (minutes)</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="100"/>
|
||||||
|
<source>Postpone time (minutes)</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
113
app/strings_en.ts
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="en_US" sourcelanguage="en">
|
||||||
|
<context>
|
||||||
|
<name>AboutDlg</name>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="20"/>
|
||||||
|
<source>Dialog</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="32"/>
|
||||||
|
<source>QBreak.</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="51"/>
|
||||||
|
<source>Version</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="70"/>
|
||||||
|
<source>App to make breaks in work. Thanks to the authors of similar apps which inspired me to make this one.</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.ui" line="14"/>
|
||||||
|
<source>MainWindow</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="213"/>
|
||||||
|
<source>Start next break</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="216"/>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="219"/>
|
||||||
|
<source>About</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="222"/>
|
||||||
|
<source>Exit</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="250"/>
|
||||||
|
<source>Less than a minute left until the next break.</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="252"/>
|
||||||
|
<source>There are %1 minutes left until the next break.</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="259"/>
|
||||||
|
<source>New break</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="260"/>
|
||||||
|
<source>New break will start in %1 secs</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="269"/>
|
||||||
|
<source>Postpone for </source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingsDialog</name>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="26"/>
|
||||||
|
<source>Dialog</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="52"/>
|
||||||
|
<source>Autostart</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="66"/>
|
||||||
|
<source>Window on top</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="80"/>
|
||||||
|
<source>Break interval (minutes)</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="90"/>
|
||||||
|
<source>Break duration (minutes)</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="100"/>
|
||||||
|
<source>Postpone time (minutes)</source>
|
||||||
|
<translation></translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
125
app/strings_ru.ts
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!DOCTYPE TS>
|
||||||
|
<TS version="2.1" language="ru" sourcelanguage="en">
|
||||||
|
<context>
|
||||||
|
<name>AboutDlg</name>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="20"/>
|
||||||
|
<source>Dialog</source>
|
||||||
|
<translation>О программе</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="32"/>
|
||||||
|
<source>QBreak.</source>
|
||||||
|
<translation>QBreak.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="51"/>
|
||||||
|
<source>Version</source>
|
||||||
|
<translation>Версия</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="aboutdlg.ui" line="70"/>
|
||||||
|
<source>App to make breaks in work. Thanks to the authors of similar apps which inspired me to make this one.</source>
|
||||||
|
<translation>Приложение чтобы устраивать себе перерывы в работе. Спасибо авторам аналогичных приложений которые вдохновили меня сделать это приложение.</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>MainWindow</name>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.ui" line="14"/>
|
||||||
|
<source>MainWindow</source>
|
||||||
|
<translation>QBreak</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="213"/>
|
||||||
|
<source>Start next break</source>
|
||||||
|
<translation>Начать перерыв</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="216"/>
|
||||||
|
<source>Settings</source>
|
||||||
|
<translation>Настройки</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="219"/>
|
||||||
|
<source>About</source>
|
||||||
|
<translation>О программе</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="222"/>
|
||||||
|
<source>Exit</source>
|
||||||
|
<translation>Выход</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="250"/>
|
||||||
|
<source>Less than a minute left until the next break.</source>
|
||||||
|
<translation>До перерыва осталось меньше минуты.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="252"/>
|
||||||
|
<source>There are %1 minutes left until the next break.</source>
|
||||||
|
<translation>Осталось %1 минут до перерыва.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="259"/>
|
||||||
|
<source>New break</source>
|
||||||
|
<translation>Скоро перерыв</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="260"/>
|
||||||
|
<source>New break will start in %1 secs</source>
|
||||||
|
<translation>Перерыв начнется через %1 секунд.</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="mainwindow.cpp" line="269"/>
|
||||||
|
<source>Postpone for </source>
|
||||||
|
<translation>Отложить на</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
<context>
|
||||||
|
<name>SettingsDialog</name>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="26"/>
|
||||||
|
<source>Dialog</source>
|
||||||
|
<translation>Настройки</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="52"/>
|
||||||
|
<source>Autostart</source>
|
||||||
|
<translation>Автозапуск</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="66"/>
|
||||||
|
<source>Window on top</source>
|
||||||
|
<translation>Показывать поверх всех окон</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="80"/>
|
||||||
|
<source>Break interval (minutes)</source>
|
||||||
|
<translation>Промежуток между перерывами (в минутах)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="90"/>
|
||||||
|
<source>Break duration (minutes)</source>
|
||||||
|
<translation>Длина перерывов (в минутах)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<location filename="settingsdialog.ui" line="100"/>
|
||||||
|
<source>Postpone time (minutes)</source>
|
||||||
|
<translation>Насколько можно отложить перерыв (в минутах)</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Break interval (seconds)</source>
|
||||||
|
<translation type="vanished">Промежуток между перерывами в секундах</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Break duration (seconds)</source>
|
||||||
|
<translation type="vanished">Длина перерыва в секундах</translation>
|
||||||
|
</message>
|
||||||
|
<message>
|
||||||
|
<source>Postpone time (seconds)</source>
|
||||||
|
<translation type="vanished">На сколько можно отложить перерыв (в секундах)</translation>
|
||||||
|
</message>
|
||||||
|
</context>
|
||||||
|
</TS>
|
||||||
BIN
scripts/appimage_dir.tar.gz
Normal file
5
scripts/build_linux.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#export QTDIR=/home/$USER/qt/5.12.10/gcc_64
|
||||||
|
export QTDIR=/home/$USER/qt5/5.12.12/gcc_64
|
||||||
|
|
||||||
|
/usr/bin/python3 build_qbreak.py
|
||||||
|
|
||||||
93
scripts/build_qbreak.py
Executable file
@ -0,0 +1,93 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import typing
|
||||||
|
import platform
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import glob
|
||||||
|
from pathlib import Path
|
||||||
|
import multiprocessing
|
||||||
|
import build_utils
|
||||||
|
|
||||||
|
EXIT_OK = 0
|
||||||
|
EXIT_ERROR = 1
|
||||||
|
|
||||||
|
# Check if Qt is specified
|
||||||
|
if not 'QTDIR' in os.environ:
|
||||||
|
print('Qt location must be set in QTDIR environment variable.')
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# Prepare build directory
|
||||||
|
build_dir = Path('build')
|
||||||
|
if build_dir.exists():
|
||||||
|
shutil.rmtree(build_dir)
|
||||||
|
os.mkdir(build_dir)
|
||||||
|
|
||||||
|
app_source = Path('../app').resolve()
|
||||||
|
version_suffix = build_utils.get_version(app_source / 'config.h', 'QBREAK_VERSION_SUFFIX')
|
||||||
|
version_minor = build_utils.get_version(app_source / 'config.h', 'QBREAK_VERSION_MINOR')
|
||||||
|
version_major = build_utils.get_version(app_source / 'config.h', 'QBREAK_VERSION_MAJOR')
|
||||||
|
|
||||||
|
app_version = f'{version_major}.{version_minor}.{version_suffix}'
|
||||||
|
print (f'Found QBreak version: {app_version}')
|
||||||
|
|
||||||
|
# Go to build directory
|
||||||
|
os.chdir(build_dir)
|
||||||
|
|
||||||
|
if platform.system() == 'Linux':
|
||||||
|
print('Linux detected')
|
||||||
|
print('Configure...')
|
||||||
|
retcode = os.system('qmake ../../app')
|
||||||
|
if retcode != 0:
|
||||||
|
print(f'qmake call failed with code {retcode}')
|
||||||
|
exit(retcode)
|
||||||
|
|
||||||
|
print('Build...')
|
||||||
|
retcode = os.system('make -j4')
|
||||||
|
if retcode != 0:
|
||||||
|
print(f'make call failed with code {retcode}')
|
||||||
|
exit(retcode)
|
||||||
|
|
||||||
|
# Build appimage
|
||||||
|
print('Assembling app...')
|
||||||
|
os.chdir('..')
|
||||||
|
|
||||||
|
# Remove possible old image
|
||||||
|
if os.path.exists('appimage_dir'):
|
||||||
|
shutil.rmtree('appimage_dir')
|
||||||
|
|
||||||
|
# Expand image template
|
||||||
|
retcode = os.system('tar -xvzf appimage_dir.tar.gz')
|
||||||
|
if retcode != 0:
|
||||||
|
print(f'Failed to expand template directory, code {retcode}')
|
||||||
|
exit(retcode)
|
||||||
|
|
||||||
|
# Copy binary file
|
||||||
|
shutil.copy('build/qbreak', 'appimage_dir/usr/bin')
|
||||||
|
|
||||||
|
deploy_options = [
|
||||||
|
'-always-overwrite',
|
||||||
|
'-verbose=2',
|
||||||
|
'-appimage',
|
||||||
|
'-qmake=' + os.environ['QTDIR'] + '/bin/qmake',
|
||||||
|
'-unsupported-allow-new-glibc',
|
||||||
|
#'-no-translations',
|
||||||
|
'-extra-plugins=iconengines,platformthemes/libqgtk3.so'
|
||||||
|
]
|
||||||
|
|
||||||
|
desktop_path = 'appimage_dir/usr/share/applications/qbreak.desktop'
|
||||||
|
cmd_deploy = f'./linuxdeployqt {desktop_path} {" ".join(deploy_options)}'
|
||||||
|
retcode = os.system(cmd_deploy)
|
||||||
|
if retcode != 0:
|
||||||
|
print(f'linuxdeployqt failed with code {retcode}')
|
||||||
|
print(cmd_deploy)
|
||||||
|
exit(retcode)
|
||||||
|
|
||||||
|
releases_dir = Path('releases')
|
||||||
|
if not releases_dir.exists():
|
||||||
|
os.mkdir(releases_dir)
|
||||||
|
for f in os.listdir():
|
||||||
|
if f.endswith('x86_64.AppImage') and f.startswith('QBreak'):
|
||||||
|
shutil.move(f, releases_dir / f'qbreak-{app_version}-x86_64.AppImage')
|
||||||
|
|
||||||
|
exit(0)
|
||||||
170
scripts/build_utils.py
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import multiprocessing
|
||||||
|
import shutil
|
||||||
|
import platform
|
||||||
|
import re
|
||||||
|
import glob
|
||||||
|
import typing
|
||||||
|
import codecs
|
||||||
|
|
||||||
|
# from PyInquirer import prompt, print_json
|
||||||
|
|
||||||
|
|
||||||
|
AR_TEMP_DIR = "ar_temp_dir"
|
||||||
|
|
||||||
|
def show_menu(message, lst):
|
||||||
|
print()
|
||||||
|
# Print selection menu
|
||||||
|
for l in range(len(lst)):
|
||||||
|
print(f'{l}) {lst[l]}')
|
||||||
|
|
||||||
|
return lst[int(input(message))]
|
||||||
|
|
||||||
|
|
||||||
|
def ask_password(message):
|
||||||
|
return input(message)
|
||||||
|
|
||||||
|
|
||||||
|
# Replace substring in a file
|
||||||
|
def inplace_change(filename, old_string, new_string):
|
||||||
|
# Safely read the input filename using 'with'
|
||||||
|
with open(filename) as f:
|
||||||
|
s = f.read()
|
||||||
|
if old_string not in s:
|
||||||
|
print('"{old_string}" not found in {filename}.'.format(**locals()))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Safely write the changed content, if found in the file
|
||||||
|
with open(filename, 'w') as f:
|
||||||
|
print('Changing "{old_string}" to "{new_string}" in {filename}'.format(**locals()))
|
||||||
|
s = s.replace(old_string, new_string)
|
||||||
|
f.write(s)
|
||||||
|
|
||||||
|
|
||||||
|
# Architecture used for VS2019 architecture parameters
|
||||||
|
def make_build(path: str, profile: str, params: dict, merge_libs :bool, generator: str=None, architecture: str = None):
|
||||||
|
cmdline = "cmake "
|
||||||
|
if generator is not None:
|
||||||
|
cmdline += f'-G "{generator}"'
|
||||||
|
if architecture is not None:
|
||||||
|
cmdline += f' -A {architecture}'
|
||||||
|
|
||||||
|
for key in params:
|
||||||
|
cmdline += f" -D {key}={params[key]}"
|
||||||
|
|
||||||
|
cmdline += f" -D CMAKE_BUILD_TYPE={profile}"
|
||||||
|
cmdline += f" {path}"
|
||||||
|
print(f"Cmake configure call: {cmdline}")
|
||||||
|
|
||||||
|
retcode = os.system(cmdline)
|
||||||
|
|
||||||
|
if retcode != 0:
|
||||||
|
return retcode
|
||||||
|
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
cmdline = "cmake --build . --config %s -j%d" % (profile, multiprocessing.cpu_count())
|
||||||
|
else:
|
||||||
|
cmdline = f"cmake --build . -j{multiprocessing.cpu_count()}"
|
||||||
|
|
||||||
|
retcode = os.system(cmdline)
|
||||||
|
if retcode != 0:
|
||||||
|
return retcode
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def get_version(path, name):
|
||||||
|
with codecs.open(path, mode='r', encoding='utf-8') as f:
|
||||||
|
t = f.read()
|
||||||
|
|
||||||
|
pattern = r"#define " + name + r" (?P<number>[\d]+)"
|
||||||
|
m = re.search(pattern=pattern, string=t, flags=re.MULTILINE)
|
||||||
|
if m is not None:
|
||||||
|
return m.group("number")
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def make_chdir(path):
|
||||||
|
# Prepare build directory
|
||||||
|
if os.path.exists(path):
|
||||||
|
shutil.rmtree(path)
|
||||||
|
os.mkdir(path)
|
||||||
|
os.chdir(path)
|
||||||
|
|
||||||
|
|
||||||
|
def upload_to_builds(path):
|
||||||
|
# Build command line
|
||||||
|
if platform.system().lower() == "windows":
|
||||||
|
cmdline = f"pscp.exe {path} uploader@f.sevana.biz:/var/www/f.sevana.biz/public_html/"
|
||||||
|
else:
|
||||||
|
cmdline = f"scp -o \"StrictHostKeyChecking no\" {path} uploader@f.sevana.biz:/var/www/f.sevana.biz/public_html/"
|
||||||
|
|
||||||
|
# Upload
|
||||||
|
retcode = os.system(cmdline)
|
||||||
|
|
||||||
|
# Print results
|
||||||
|
if retcode == 0:
|
||||||
|
print (f"https://f.sevana.biz/{os.path.basename(path)}")
|
||||||
|
else:
|
||||||
|
print ("Failed to upload. Return code: %d" % retcode)
|
||||||
|
|
||||||
|
|
||||||
|
def merge_static_libs(main_lib, build_dir, use_android_ndk: bool):
|
||||||
|
# Find all static libraries
|
||||||
|
print ("Looking for additional static libraries to merge...")
|
||||||
|
files = glob.glob("**/*.a", recursive=True)
|
||||||
|
print (files)
|
||||||
|
|
||||||
|
if platform.system() in ["Linux", "Darwin"]:
|
||||||
|
print ("Merging with binutils...")
|
||||||
|
|
||||||
|
if use_android_ndk:
|
||||||
|
if platform.system() == "Darwin":
|
||||||
|
toolchain_prefix = "darwin-x86_64"
|
||||||
|
else:
|
||||||
|
toolchain_prefix = "linux-x86_64"
|
||||||
|
|
||||||
|
ar_command = f"{path_android_ndk}/toolchains/llvm/prebuilt/{toolchain_prefix}/bin/aarch64-linux-android-ar"
|
||||||
|
else:
|
||||||
|
ar_command = "ar"
|
||||||
|
|
||||||
|
# Extract all object files
|
||||||
|
os.system("mkdir %s" % AR_TEMP_DIR)
|
||||||
|
os.chdir(AR_TEMP_DIR)
|
||||||
|
for libname in files:
|
||||||
|
cmdline_extract = f"{ar_command} -x {os.path.join('..', libname)}"
|
||||||
|
retcode = os.system(cmdline_extract)
|
||||||
|
if retcode != 0:
|
||||||
|
print(f"Failed: {cmdline_extract}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Extract object files from main library
|
||||||
|
cmdline_extract = f"{ar_command} -x {os.path.join('..', main_lib)}"
|
||||||
|
retcode = os.system(cmdline_extract)
|
||||||
|
if retcode != 0:
|
||||||
|
print(f"Failed: {cmdline_extract}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
cmdline = f"{ar_command} -qc {os.path.basename(main_lib)} *.o"
|
||||||
|
retcode = os.system(cmdline)
|
||||||
|
if retcode != 0:
|
||||||
|
print(f"Failed: {cmdline}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Replace original library with a new one
|
||||||
|
os.chdir("..")
|
||||||
|
os.remove(main_lib)
|
||||||
|
shutil.move(os.path.join(AR_TEMP_DIR, os.path.basename(main_lib)), main_lib)
|
||||||
|
# os.system("strip %s" % main_lib)
|
||||||
|
shutil.rmtree(AR_TEMP_DIR)
|
||||||
|
|
||||||
|
if 'ANDROID_NDK_HOME' in os.environ:
|
||||||
|
path_android_ndk = os.environ['ANDROID_NDK_HOME']
|
||||||
|
else:
|
||||||
|
path_android_ndk = None
|
||||||
|
|
||||||
|
# "~/Library/Android/sdk/ndk/21.0.6113669"
|
||||||