First version on loading themes is ready.
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 197 KiB |
|
@ -18,6 +18,8 @@ to link the code of portions of this program with the OpenSSL library.
|
|||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
using "colors.palette";
|
||||
|
||||
using "basic_types.style";
|
||||
|
||||
semibold: "Open Sans Semibold";
|
||||
|
@ -30,10 +32,10 @@ emojiImgSize: 18px; // exceptional value for retina
|
|||
emojiSize: 18px;
|
||||
emojiPadding: 0px;
|
||||
|
||||
counterBG: #f23c34;
|
||||
counterMuteBG: #888888;
|
||||
counterColor: #ffffff;
|
||||
counterMacInvColor: #ffffff01;
|
||||
counterBg: #f23c34;
|
||||
counterMuteBg: #888888;
|
||||
counterFg: #ffffff;
|
||||
counterMacInvFg: #ffffff01;
|
||||
|
||||
lineWidth: 1px;
|
||||
|
||||
|
@ -46,14 +48,6 @@ wndMinWidth: 380px;
|
|||
adaptiveNormalWidth: 640px;
|
||||
adaptiveWideWidth: 1366px;
|
||||
|
||||
windowBg: #ffffff; // fallback for background: white
|
||||
windowActiveBg: #40ace3; // fallback for blue filled active areas
|
||||
windowTextFg: #000000; // fallback for text color: black
|
||||
windowSubTextFg: #8a8a8a; // fallback for subtext color: gray
|
||||
windowSubTextFgOver: #7c99b2; // fallback for subtext over color: gray over blue bg
|
||||
windowActiveTextFg: #1485c2; // fallback for active color: blue online
|
||||
windowShadowFg: #000000; // fallback for shadow color
|
||||
|
||||
semiboldButtonBlueText: #2b99d5;
|
||||
|
||||
wndMinHeight: 480px;
|
||||
|
@ -65,8 +59,6 @@ wndShadowShift: 1px;
|
|||
layerAlpha: 0.5;
|
||||
layerBg: black;
|
||||
|
||||
overBg: #edf2f5;
|
||||
|
||||
labelDefFlat: flatLabel {
|
||||
font: font(fsize);
|
||||
width: 0px;
|
||||
|
@ -75,7 +67,6 @@ labelDefFlat: flatLabel {
|
|||
textFg: windowTextFg;
|
||||
}
|
||||
|
||||
boxBg: white;
|
||||
boxVerticalMargin: 10px;
|
||||
boxWidth: 320px;
|
||||
boxWideWidth: 364px;
|
||||
|
@ -86,7 +77,6 @@ boxTextFont: font(boxFontSize);
|
|||
boxLittleSkip: 10px;
|
||||
boxMediumSkip: 20px;
|
||||
|
||||
boxTitleFg: #444444;
|
||||
boxTitleFont: font(boxFontSize bold);
|
||||
boxTitlePosition: point(26px, 28px);
|
||||
boxTitleHeight: 54px;
|
||||
|
@ -97,7 +87,7 @@ defaultBoxButton: RoundButton {
|
|||
textFgOver: #2f9fea;
|
||||
secondaryTextFg: #2f9fea;
|
||||
secondaryTextFgOver: #2f9fea;
|
||||
textBg: white;
|
||||
textBg: boxBg;
|
||||
textBgOver: #edf7ff;
|
||||
|
||||
width: -24px;
|
||||
|
@ -139,7 +129,7 @@ boxLabel: flatLabel(labelDefFlat) {
|
|||
defaultLeftOutlineButton: OutlineButton {
|
||||
outlineWidth: 3px;
|
||||
outlineFg: windowBg;
|
||||
outlineFgOver: windowActiveBg;
|
||||
outlineFgOver: windowActiveFill;
|
||||
|
||||
textBg: windowBg;
|
||||
textBgOver: #f2f7fa;
|
||||
|
@ -533,24 +523,6 @@ introLabel: flatLabel(labelDefFlat) {
|
|||
align: align(center);
|
||||
}
|
||||
|
||||
introPointsTop: -30px; // intro steps bottom points
|
||||
introPointWidth: 4px;
|
||||
introPointHeight: 4px;
|
||||
introPointHoverWidth: 10px;
|
||||
introPointHoverHeight: 10px;
|
||||
introPointLeft: 3px;
|
||||
introPointTop: 3px;
|
||||
introPointDelta: 10px;
|
||||
introPointColor: #000000;
|
||||
introPointAlpha: 0.5;
|
||||
introPointHoverColor: #86b4e3;
|
||||
introPointStepT: transition(sineInOut);
|
||||
introPointAlphaT: transition(linear);
|
||||
introPointShowStepT: transition(easeOutCirc);
|
||||
introPointHideStepT: transition(easeInCirc);
|
||||
introPointShowAlphaT: transition(easeInCirc);
|
||||
introPointHideAlphaT: transition(easeOutCirc);
|
||||
|
||||
introStepSize: size(400px, 200px);
|
||||
introSize: size(400px, 400px);
|
||||
introSlideShift: 500px; // intro hiding animation
|
||||
|
@ -581,17 +553,6 @@ btnIntroNext: flatButton(btnDefNext, btnDefBig) {
|
|||
boxShadow: icon {{ "box_shadow", windowShadowFg }};
|
||||
boxShadowShift: 2px;
|
||||
|
||||
introCountry: countryInput {
|
||||
width: 300px;
|
||||
height: 41px;
|
||||
top: 33px;
|
||||
bgColor: #f2f2f2;
|
||||
ptrSize: size(15px, 8px);
|
||||
textMrg: margins(16px, 5px, 16px, 15px);
|
||||
font: inpDefFont;
|
||||
align: align(left);
|
||||
}
|
||||
|
||||
introPhoneTop: 8px;
|
||||
inpIntroCountryCode: flatInput(inpDefGray) {
|
||||
width: 70px;
|
||||
|
@ -632,7 +593,7 @@ countryRowHeight: 36px;
|
|||
countryRowNameFont: semiboldFont;
|
||||
countryRowPadding: margins(22px, 9px, 8px, 0px);
|
||||
countryRowCodeFont: font(fsize);
|
||||
countryRowBgOver: overBg;
|
||||
countryRowBgOver: windowOverBg;
|
||||
countryRowCodeFg: #808080;
|
||||
countryRowCodeFgOver: #7c99b2;
|
||||
countriesSkip: 12px;
|
||||
|
@ -711,7 +672,7 @@ defaultActiveButton: RoundButton {
|
|||
textFgOver: #ffffff;
|
||||
secondaryTextFg: #cceeff;
|
||||
secondaryTextFgOver: #cceeff;
|
||||
textBg: windowActiveBg;
|
||||
textBg: windowActiveFill;
|
||||
textBgOver: #46b4eb;
|
||||
|
||||
secondarySkip: 7px;
|
||||
|
@ -807,8 +768,6 @@ msgLinkColor: #2a6dc2;
|
|||
msgPressedLinkColor: #004bad;
|
||||
msgSkip: 40px;
|
||||
msgPtr: 8px;
|
||||
msgBG: ":/gui/art/bg.jpg";
|
||||
msgBG0: ":/gui/art/bg0.png";
|
||||
|
||||
msgDateSpace: 12px;
|
||||
msgDateDelta: point(2px, 5px);
|
||||
|
@ -960,7 +919,7 @@ msgFileThumbLinkOutFgSelected: #31a298;
|
|||
msgFileNameTop: 16px;
|
||||
msgFileStatusTop: 37px;
|
||||
msgFileMinWidth: 294px;
|
||||
msgFileInBg: windowActiveBg;
|
||||
msgFileInBg: windowActiveFill;
|
||||
msgFileInBgOver: #4eade3;
|
||||
msgFileInBgSelected: #51a3d3;
|
||||
msgFileOutBg: #78c67f;
|
||||
|
@ -976,7 +935,7 @@ msgWaveformBar: 2px;
|
|||
msgWaveformSkip: 1px;
|
||||
msgWaveformMin: 2px;
|
||||
msgWaveformMax: 20px;
|
||||
msgWaveformInActive: windowActiveBg;
|
||||
msgWaveformInActive: windowActiveFill;
|
||||
msgWaveformInActiveSelected: #51a3d3;
|
||||
msgWaveformInInactive: #d4dee6;
|
||||
msgWaveformInInactiveSelected: #9cc1e1;
|
||||
|
@ -1100,7 +1059,7 @@ contactsStatusFont: font(fsize);
|
|||
contactsStatusFg: #999999;
|
||||
contactsStatusFgOver: #7c99b2;
|
||||
contactsStatusFgOnline: #3b8dcc;
|
||||
contactsBgOver: overBg;
|
||||
contactsBgOver: windowOverBg;
|
||||
contactsCheckPosition: point(8px, 16px);
|
||||
contactsAboutBg: #f7f7f7;
|
||||
contactsAboutShadow: #0000001F;
|
||||
|
|
|
@ -141,17 +141,6 @@ flatScroll {
|
|||
hiding: int;
|
||||
}
|
||||
|
||||
countryInput {
|
||||
width: pixels;
|
||||
height: pixels;
|
||||
top: pixels;
|
||||
bgColor: color;
|
||||
ptrSize: size;
|
||||
textMrg: margins;
|
||||
font: font;
|
||||
align: align;
|
||||
}
|
||||
|
||||
flatLabel {
|
||||
font: font;
|
||||
margin: margins;
|
||||
|
@ -161,23 +150,6 @@ flatLabel {
|
|||
maxHeight: pixels;
|
||||
}
|
||||
|
||||
switcher {
|
||||
border: pixels;
|
||||
borderColor: color;
|
||||
|
||||
bgColor: color;
|
||||
bgHovered: color;
|
||||
bgActive: color;
|
||||
|
||||
height: pixels;
|
||||
|
||||
font: font;
|
||||
textColor: color;
|
||||
activeColor: color;
|
||||
|
||||
duration: int;
|
||||
}
|
||||
|
||||
Tooltip {
|
||||
textBg: color;
|
||||
textFg: color;
|
||||
|
|
|
@ -18,42 +18,15 @@ to link the code of portions of this program with the OpenSSL library.
|
|||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Window {
|
||||
windowBg: #ffffff; // white: fallback for background
|
||||
windowTextFg: #000000; // black: fallback for text color
|
||||
windowSubTextFg: #8a8a8a; // gray: fallback for subtext color
|
||||
windowActiveFill: #40ace3; // bright blue: fallback for blue filled active areas
|
||||
windowOverBg: #edf2f5; // light blue: fallback for over background
|
||||
windowSubTextFgOver: #7c99b2; // gray over light blue: fallback for subtext over color
|
||||
windowActiveTextFg: #1485c2; // online blue: fallback for active color
|
||||
windowShadowFg: #000000; // black: fallback for shadow color
|
||||
|
||||
struct ChatBackgroundUpdate {
|
||||
enum class Type {
|
||||
New,
|
||||
Changed,
|
||||
Start,
|
||||
};
|
||||
|
||||
ChatBackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) {
|
||||
}
|
||||
Type type;
|
||||
bool tiled;
|
||||
};
|
||||
|
||||
class ChatBackground : public base::Observable<ChatBackgroundUpdate> {
|
||||
public:
|
||||
bool empty() const;
|
||||
void initIfEmpty();
|
||||
void init(int32 id, QPixmap &&image);
|
||||
void reset();
|
||||
|
||||
int32 id() const;
|
||||
const QPixmap &image() const;
|
||||
bool tile() const;
|
||||
void setTile(bool tile);
|
||||
|
||||
private:
|
||||
int32 _id = 0;
|
||||
QPixmap _image;
|
||||
bool _tile = false;
|
||||
|
||||
};
|
||||
|
||||
ChatBackground *chatBackground();
|
||||
|
||||
} // namespace Window
|
||||
boxBg: windowBg;
|
||||
boxTitleFg: #444444 | windowTextFg;
|
Before Width: | Height: | Size: 135 B After Width: | Height: | Size: 148 B |
Before Width: | Height: | Size: 177 B After Width: | Height: | Size: 197 B |
Before Width: | Height: | Size: 265 B After Width: | Height: | Size: 272 B |
Before Width: | Height: | Size: 352 B After Width: | Height: | Size: 365 B |
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// This is a sample Telegram Desktop theme file.
|
||||
// It was generated from the 'colors.palette' style file.
|
||||
//
|
||||
// To create a theme with a background image included you should
|
||||
// put two files in a .zip archive:
|
||||
//
|
||||
// First one is the color scheme like the one you're viewing
|
||||
// right now, this file should be named 'colors.tdesktop-theme'.
|
||||
//
|
||||
// Second one should be the background image and it can be named
|
||||
// 'background.jpg', 'background.png', 'tiled.jpg' or 'tiled.png'.
|
||||
// You should name it 'background' (if you'd like it not to be tiled),
|
||||
// or it can be named 'tiled' (if you'd like it to be tiled).
|
||||
//
|
||||
// After that you need to change the extension of your .zip archive
|
||||
// to 'tdesktop-theme', so you'll have:
|
||||
//
|
||||
// mytheme.tdesktop-theme
|
||||
// |-colors.tdesktop-theme
|
||||
// |-background.jpg (or tiled.jpg, background.png, tiled.png)
|
||||
//
|
||||
|
||||
windowBg: #ffffff;
|
||||
windowTextFg: #000000;
|
||||
windowSubTextFg: #8a8a8a;
|
||||
windowActiveFill: #40ace3;
|
||||
windowOverBg: #edf2f5;
|
||||
windowSubTextFgOver: #7c99b2;
|
||||
windowActiveTextFg: #1485c2;
|
||||
windowShadowFg: #000000;
|
||||
boxBg: #ffffff;
|
||||
boxTitleFg: #444444; // windowTextFg;
|
|
@ -5,7 +5,7 @@
|
|||
<file>art/fonts/OpenSans-Semibold.ttf</file>
|
||||
<file>art/newmsg.wav</file>
|
||||
<file>art/bg.jpg</file>
|
||||
<file>art/bg0.png</file>
|
||||
<file>art/bg_old.png</file>
|
||||
<file>art/icon256.png</file>
|
||||
<file>art/iconbig256.png</file>
|
||||
</qresource>
|
||||
|
|
|
@ -542,7 +542,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org\n\
|
|||
tcpp << "\t\tfor (const char *v = key.constData() + from, *e = v + len; v != e; ++v, ++value) {\n";
|
||||
tcpp << "\t\t\tif (*v != *value) return false;\n";
|
||||
tcpp << "\t\t}\n";
|
||||
tcpp << "\t\treturn true; \n";
|
||||
tcpp << "\t\treturn true;\n";
|
||||
tcpp << "\t}\n";
|
||||
|
||||
tcpp << "}\n\n";
|
||||
|
|
|
@ -30,10 +30,13 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "historywidget.h"
|
||||
#include "localstorage.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "window/window_theme.h"
|
||||
|
||||
ApiWrap::ApiWrap(QObject *parent) : QObject(parent)
|
||||
, _messageDataResolveDelayed(new SingleDelayedCall(this, "resolveMessageDatas")) {
|
||||
App::initBackground();
|
||||
if (!Local::readBackground()) {
|
||||
Window::Theme::Background()->start();
|
||||
}
|
||||
|
||||
connect(&_webPagesTimer, SIGNAL(timeout()), this, SLOT(resolveWebPages()));
|
||||
connect(&_draftsSaveTimer, SIGNAL(timeout()), this, SLOT(saveDraftsToCloud()));
|
||||
|
|
|
@ -42,7 +42,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "apiwrap.h"
|
||||
#include "numbers.h"
|
||||
#include "observer_peer.h"
|
||||
#include "window/chat_background.h"
|
||||
#include "window/window_theme.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "platform/platform_notifications_manager.h"
|
||||
|
||||
|
@ -134,7 +134,6 @@ namespace {
|
|||
style::color _historyScrollBgColor;
|
||||
style::color _historyScrollBarOverColor;
|
||||
style::color _historyScrollBgOverColor;
|
||||
style::color _introPointHoverColor;
|
||||
}
|
||||
|
||||
namespace App {
|
||||
|
@ -211,6 +210,7 @@ namespace {
|
|||
}
|
||||
MTP::setAuthedId(0);
|
||||
Local::reset();
|
||||
Window::Theme::Background()->reset();
|
||||
|
||||
cSetOtherOnline(0);
|
||||
histories().clear();
|
||||
|
@ -2456,6 +2456,21 @@ namespace {
|
|||
_launchState = state;
|
||||
}
|
||||
|
||||
void restart() {
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
bool updateReady = (Sandbox::updatingState() == Application::UpdatingReady);
|
||||
#else // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
bool updateReady = false;
|
||||
#endif // else for !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
if (updateReady) {
|
||||
cSetRestartingUpdate(true);
|
||||
} else {
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
}
|
||||
App::quit();
|
||||
}
|
||||
|
||||
QImage readImage(QByteArray data, QByteArray *format, bool opaque, bool *animated) {
|
||||
QByteArray tmpFormat;
|
||||
QImage result;
|
||||
|
@ -2761,22 +2776,19 @@ namespace {
|
|||
void initBackground(int32 id, const QImage &p, bool nowrite) {
|
||||
if (Local::readBackground()) return;
|
||||
|
||||
uint64 components[3] = { 0 }, componentsScroll[3] = { 0 }, componentsPoint[3] = { 0 };
|
||||
int size = 0;
|
||||
|
||||
QImage img(p);
|
||||
bool remove = false;
|
||||
bool remove = (id == Window::Theme::kThemeBackground);
|
||||
if (p.isNull()) {
|
||||
if (id == DefaultChatBackground) {
|
||||
img.load(st::msgBG);
|
||||
if (id == Window::Theme::kDefaultBackground) {
|
||||
img.load(qsl(":/gui/art/bg.jpg"));
|
||||
} else {
|
||||
img.load(st::msgBG0);
|
||||
img.load(qsl(":/gui/art/bg_old.png"));
|
||||
if (cRetina()) {
|
||||
img = img.scaledToWidth(img.width() * 2, Qt::SmoothTransformation);
|
||||
} else if (cScale() != dbisOne) {
|
||||
img = img.scaledToWidth(convertScale(img.width()), Qt::SmoothTransformation);
|
||||
}
|
||||
id = 0;
|
||||
id = Window::Theme::kOldBackground;
|
||||
}
|
||||
remove = true;
|
||||
}
|
||||
|
@ -2789,11 +2801,16 @@ namespace {
|
|||
Local::writeBackground(id, remove ? QImage() : img);
|
||||
}
|
||||
|
||||
int w = img.width(), h = img.height();
|
||||
size = w * h;
|
||||
const uchar *pix = img.constBits();
|
||||
if (pix) {
|
||||
for (int32 i = 0, l = size * 4; i < l; i += 4) {
|
||||
initColorsFromBackground(img);
|
||||
}
|
||||
|
||||
void initColorsFromBackground(const QImage &img) {
|
||||
uint64 components[3] = { 0 }, componentsScroll[3] = { 0 };
|
||||
auto w = img.width();
|
||||
auto h = img.height();
|
||||
auto size = w * h;
|
||||
if (auto pix = img.constBits()) {
|
||||
for (int i = 0, l = size * 4; i != l; i += 4) {
|
||||
components[2] += pix[i + 0];
|
||||
components[1] += pix[i + 1];
|
||||
components[0] += pix[i + 2];
|
||||
|
@ -2801,7 +2818,9 @@ namespace {
|
|||
}
|
||||
|
||||
if (size) {
|
||||
for (int32 i = 0; i < 3; ++i) components[i] /= size;
|
||||
for (int i = 0; i != 3; ++i) {
|
||||
components[i] /= size;
|
||||
}
|
||||
}
|
||||
int maxtomin[3] = { 0, 1, 2 };
|
||||
if (components[maxtomin[0]] < components[maxtomin[1]]) {
|
||||
|
@ -2816,10 +2835,7 @@ namespace {
|
|||
|
||||
uint64 max = qMax(1ULL, components[maxtomin[0]]), mid = qMax(1ULL, components[maxtomin[1]]), min = qMax(1ULL, components[maxtomin[2]]);
|
||||
|
||||
Window::chatBackground()->init(id, pixmapFromImageInPlace(std_::move(img)));
|
||||
|
||||
memcpy(componentsScroll, components, sizeof(components));
|
||||
memcpy(componentsPoint, components, sizeof(components));
|
||||
|
||||
if (max != min) {
|
||||
if (min > uint64(qRound(0.77 * max))) {
|
||||
|
@ -2832,15 +2848,6 @@ namespace {
|
|||
uint64 newmid = max - ((max - mid) * (max - newmin)) / (max - min);
|
||||
componentsScroll[maxtomin[1]] = newmid;
|
||||
componentsScroll[maxtomin[2]] = newmin;
|
||||
|
||||
uint64 pmax = 227; // 89% brightness
|
||||
uint64 pmin = qRound(0.75 * pmax); // 41% saturation
|
||||
uint64 pmid = pmax - ((max - mid) * (pmax - pmin)) / (max - min);
|
||||
componentsPoint[maxtomin[0]] = pmax;
|
||||
componentsPoint[maxtomin[1]] = pmid;
|
||||
componentsPoint[maxtomin[2]] = pmin;
|
||||
} else {
|
||||
componentsPoint[0] = componentsPoint[1] = componentsPoint[2] = 227; // 89% brightness
|
||||
}
|
||||
|
||||
float64 luminance = 0.299 * componentsScroll[0] + 0.587 * componentsScroll[1] + 0.114 * componentsScroll[2];
|
||||
|
@ -2886,9 +2893,6 @@ namespace {
|
|||
_historyScrollBarOverColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.barOverColor->c.alphaF() * 0xFF));
|
||||
_historyScrollBgOverColor = style::color(rScroll, gScroll, bScroll, qRound(st::historyScroll.bgOverColor->c.alphaF() * 0xFF));
|
||||
|
||||
uchar rPoint = uchar(componentsPoint[0]), gPoint = uchar(componentsPoint[1]), bPoint = uchar(componentsPoint[2]);
|
||||
_introPointHoverColor = style::color(rPoint, gPoint, bPoint);
|
||||
|
||||
if (App::main()) {
|
||||
App::main()->updateScrollColors();
|
||||
}
|
||||
|
@ -2919,10 +2923,6 @@ namespace {
|
|||
return _historyScrollBgOverColor;
|
||||
}
|
||||
|
||||
const style::color &introPointHoverColor() {
|
||||
return _introPointHoverColor;
|
||||
}
|
||||
|
||||
WallPapers gServerBackgrounds;
|
||||
|
||||
}
|
||||
|
|
|
@ -236,6 +236,7 @@ namespace App {
|
|||
void allDraftsSaved();
|
||||
LaunchState launchState();
|
||||
void setLaunchState(LaunchState state);
|
||||
void restart();
|
||||
|
||||
QImage readImage(QByteArray data, QByteArray *format = 0, bool opaque = true, bool *animated = 0);
|
||||
QImage readImage(const QString &file, QByteArray *format = 0, bool opaque = true, bool *animated = 0, QByteArray *content = 0);
|
||||
|
@ -292,7 +293,8 @@ namespace App {
|
|||
return roundRect(p, rect.x(), rect.y(), rect.width(), rect.height(), bg, radius);
|
||||
}
|
||||
|
||||
void initBackground(int32 id = DefaultChatBackground, const QImage &p = QImage(), bool nowrite = false);
|
||||
void initColorsFromBackground(const QImage &image);
|
||||
void initBackground(int32 id, const QImage &p = QImage(), bool nowrite = false);
|
||||
|
||||
const style::color &msgServiceBg();
|
||||
const style::color &msgServiceSelectBg();
|
||||
|
@ -300,7 +302,6 @@ namespace App {
|
|||
const style::color &historyScrollBgColor();
|
||||
const style::color &historyScrollBarOverColor();
|
||||
const style::color &historyScrollBgOverColor();
|
||||
const style::color &introPointHoverColor();
|
||||
|
||||
struct WallPaper {
|
||||
WallPaper(int32 id, ImagePtr thumb, ImagePtr full) : id(id), thumb(thumb), full(full) {
|
||||
|
|
|
@ -34,7 +34,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "autoupdater.h"
|
||||
#include "core/observer.h"
|
||||
#include "observer_peer.h"
|
||||
#include "window/chat_background.h"
|
||||
#include "window/window_theme.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "window/notifications_manager.h"
|
||||
#include "history/history_location_manager.h"
|
||||
|
@ -676,11 +676,7 @@ namespace Sandbox {
|
|||
|
||||
}
|
||||
|
||||
AppClass::AppClass() : QObject()
|
||||
, _lastActionTime(0)
|
||||
, _window(0)
|
||||
, _uploader(0)
|
||||
, _translator(0) {
|
||||
AppClass::AppClass() : QObject() {
|
||||
AppObject = this;
|
||||
|
||||
Fonts::start();
|
||||
|
@ -702,33 +698,7 @@ AppClass::AppClass() : QObject()
|
|||
cSetConfigScale(dbisOne);
|
||||
cSetRealScale(dbisOne);
|
||||
}
|
||||
|
||||
if (cLang() < languageTest) {
|
||||
cSetLang(Sandbox::LangSystem());
|
||||
}
|
||||
if (cLang() == languageTest) {
|
||||
if (QFileInfo(cLangFile()).exists()) {
|
||||
LangLoaderPlain loader(cLangFile());
|
||||
cSetLangErrors(loader.errors());
|
||||
if (!cLangErrors().isEmpty()) {
|
||||
LOG(("Lang load errors: %1").arg(cLangErrors()));
|
||||
} else if (!loader.warnings().isEmpty()) {
|
||||
LOG(("Lang load warnings: %1").arg(loader.warnings()));
|
||||
}
|
||||
} else {
|
||||
cSetLang(languageDefault);
|
||||
}
|
||||
} else if (cLang() > languageDefault && cLang() < languageCount) {
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[cLang()].c_str() + qsl(".strings"));
|
||||
if (!loader.errors().isEmpty()) {
|
||||
LOG(("Lang load errors: %1").arg(loader.errors()));
|
||||
} else if (!loader.warnings().isEmpty()) {
|
||||
LOG(("Lang load warnings: %1").arg(loader.warnings()));
|
||||
}
|
||||
}
|
||||
|
||||
application()->installTranslator(_translator = new Translator());
|
||||
|
||||
loadLanguage();
|
||||
style::startManager();
|
||||
anim::startManager();
|
||||
historyInit();
|
||||
|
@ -812,6 +782,33 @@ AppClass::AppClass() : QObject()
|
|||
}
|
||||
}
|
||||
|
||||
void AppClass::loadLanguage() {
|
||||
if (cLang() < languageTest) {
|
||||
cSetLang(Sandbox::LangSystem());
|
||||
}
|
||||
if (cLang() == languageTest) {
|
||||
if (QFileInfo(cLangFile()).exists()) {
|
||||
LangLoaderPlain loader(cLangFile());
|
||||
cSetLangErrors(loader.errors());
|
||||
if (!cLangErrors().isEmpty()) {
|
||||
LOG(("Lang load errors: %1").arg(cLangErrors()));
|
||||
} else if (!loader.warnings().isEmpty()) {
|
||||
LOG(("Lang load warnings: %1").arg(loader.warnings()));
|
||||
}
|
||||
} else {
|
||||
cSetLang(languageDefault);
|
||||
}
|
||||
} else if (cLang() > languageDefault && cLang() < languageCount) {
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[cLang()].c_str() + qsl(".strings"));
|
||||
if (!loader.errors().isEmpty()) {
|
||||
LOG(("Lang load errors: %1").arg(loader.errors()));
|
||||
} else if (!loader.warnings().isEmpty()) {
|
||||
LOG(("Lang load warnings: %1").arg(loader.warnings()));
|
||||
}
|
||||
}
|
||||
application()->installTranslator(_translator = new Translator());
|
||||
}
|
||||
|
||||
void AppClass::regPhotoUpdate(const PeerId &peer, const FullMsgId &msgId) {
|
||||
photoUpdates.insert(msgId, peer);
|
||||
}
|
||||
|
@ -998,9 +995,7 @@ void AppClass::onSwitchDebugMode() {
|
|||
if (cDebug()) {
|
||||
QFile(cWorkingDir() + qsl("tdata/withdebug")).remove();
|
||||
cSetDebug(false);
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
App::quit();
|
||||
App::restart();
|
||||
} else {
|
||||
cSetDebug(true);
|
||||
DEBUG_LOG(("Debug logs started."));
|
||||
|
@ -1017,9 +1012,7 @@ void AppClass::onSwitchWorkMode() {
|
|||
Global::SetDialogsModeEnabled(!Global::DialogsModeEnabled());
|
||||
Global::SetDialogsMode(Dialogs::Mode::All);
|
||||
Local::writeUserSettings();
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
App::quit();
|
||||
App::restart();
|
||||
}
|
||||
|
||||
void AppClass::onSwitchTestMode() {
|
||||
|
@ -1034,9 +1027,7 @@ void AppClass::onSwitchTestMode() {
|
|||
}
|
||||
cSetTestMode(true);
|
||||
}
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
App::quit();
|
||||
App::restart();
|
||||
}
|
||||
|
||||
FileUploader *AppClass::uploader() {
|
||||
|
@ -1120,7 +1111,7 @@ AppClass::~AppClass() {
|
|||
delete base::take(_uploader);
|
||||
delete base::take(_translator);
|
||||
|
||||
Window::chatBackground()->reset();
|
||||
Window::Theme::Unload();
|
||||
|
||||
Media::Player::finish();
|
||||
style::stopManager();
|
||||
|
|
|
@ -204,17 +204,18 @@ public slots:
|
|||
void call_handleObservables();
|
||||
|
||||
private:
|
||||
void loadLanguage();
|
||||
|
||||
QMap<FullMsgId, PeerId> photoUpdates;
|
||||
|
||||
QMap<int32, uint64> killDownloadSessionTimes;
|
||||
SingleTimer killDownloadSessionsTimer;
|
||||
|
||||
uint64 _lastActionTime;
|
||||
uint64 _lastActionTime = 0;
|
||||
|
||||
MainWindow *_window;
|
||||
FileUploader *_uploader;
|
||||
Translator *_translator;
|
||||
MainWindow *_window = nullptr;
|
||||
FileUploader *_uploader = nullptr;
|
||||
Translator *_translator = nullptr;
|
||||
|
||||
SingleTimer _mtpUnpauseTimer;
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "backgroundbox.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
#include "window/chat_background.h"
|
||||
#include "window/window_theme.h"
|
||||
#include "styles/style_overview.h"
|
||||
|
||||
BackgroundBox::BackgroundBox() : ItemListBox(st::backgroundScroll)
|
||||
|
@ -48,8 +48,8 @@ void BackgroundBox::onBackgroundChosen(int index) {
|
|||
const App::WallPaper &paper(App::cServerBackgrounds().at(index));
|
||||
if (App::main()) App::main()->setChatBackground(paper);
|
||||
|
||||
using Update = Window::ChatBackgroundUpdate;
|
||||
Window::chatBackground()->notify(Update(Update::Type::Start, !paper.id));
|
||||
using Update = Window::Theme::BackgroundUpdate;
|
||||
Window::Theme::Background()->notify(Update(Update::Type::Start, !paper.id));
|
||||
}
|
||||
onClose();
|
||||
}
|
||||
|
@ -73,7 +73,8 @@ BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent)
|
|||
void BackgroundBox::Inner::gotWallpapers(const MTPVector<MTPWallPaper> &result) {
|
||||
App::WallPapers wallpapers;
|
||||
|
||||
wallpapers.push_back(App::WallPaper(0, ImagePtr(st::msgBG0), ImagePtr(st::msgBG0)));
|
||||
auto oldBackground = ImagePtr(qsl(":/gui/art/bg_old.png"));
|
||||
wallpapers.push_back(App::WallPaper(Window::Theme::kOldBackground, oldBackground, oldBackground));
|
||||
const auto &v(result.c_vector().v);
|
||||
for (int i = 0, l = v.size(); i < l; ++i) {
|
||||
const auto &w(v.at(i));
|
||||
|
@ -161,7 +162,7 @@ void BackgroundBox::Inner::paintEvent(QPaintEvent *e) {
|
|||
const QPixmap &pix(paper.thumb->pix(st::backgroundSize.width(), st::backgroundSize.height()));
|
||||
p.drawPixmap(x, y, pix);
|
||||
|
||||
if (paper.id == Window::chatBackground()->id()) {
|
||||
if (paper.id == Window::Theme::Background()->id()) {
|
||||
int checkPosX = x + st::backgroundSize.width() - st::overviewPhotoChecked.width();
|
||||
int checkPosY = y + st::backgroundSize.height() - st::overviewPhotoChecked.height();
|
||||
st::overviewPhotoChecked.paint(p, QPoint(checkPosX, checkPosY), width());
|
||||
|
|
|
@ -106,7 +106,7 @@ contactsMultiSelect: MultiSelect {
|
|||
font: normalFont;
|
||||
textBg: contactsBgOver;
|
||||
textFg: windowTextFg;
|
||||
textActiveBg: windowActiveBg;
|
||||
textActiveBg: windowActiveFill;
|
||||
textActiveFg: white;
|
||||
deleteFg: white;
|
||||
deleteLeft: 10px;
|
||||
|
@ -153,10 +153,10 @@ contactsPhotoCheckbox: RoundImageCheckbox {
|
|||
imageRadius: 21px;
|
||||
imageSmallRadius: 18px;
|
||||
selectWidth: 2px;
|
||||
selectFg: windowActiveBg;
|
||||
selectFg: windowActiveFill;
|
||||
selectDuration: 150;
|
||||
checkBorder: windowBg;
|
||||
checkBg: windowActiveBg;
|
||||
checkBg: windowActiveFill;
|
||||
checkRadius: 10px;
|
||||
checkSmallRadius: 3px;
|
||||
checkIcon: icon {{ "default_checkbox_check", windowBg, point(3px, 6px) }};
|
||||
|
|
|
@ -200,9 +200,7 @@ void ConnectionBox::onSave() {
|
|||
Local::writeSettings();
|
||||
Global::RefConnectionTypeChanged().notify();
|
||||
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
App::quit();
|
||||
App::restart();
|
||||
} else {
|
||||
Global::SetTryIPv6(_tryIPv6->checked());
|
||||
Local::writeSettings();
|
||||
|
|
|
@ -356,7 +356,7 @@ void ContactsBox::onFilterUpdate(const QString &filter) {
|
|||
void ContactsBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) {
|
||||
using AddItemWay = Ui::MultiSelect::AddItemWay;
|
||||
auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default;
|
||||
_select->entity()->addItem(peer->id, peer->shortName(), st::windowActiveBg, PaintUserpicCallback(peer), addItemWay);
|
||||
_select->entity()->addItem(peer->id, peer->shortName(), st::windowActiveFill, PaintUserpicCallback(peer), addItemWay);
|
||||
}
|
||||
|
||||
void ContactsBox::onPeerSelectedChanged(PeerData *peer, bool checked) {
|
||||
|
|
|
@ -45,7 +45,7 @@ _close(this, lang(lng_box_ok), st::defaultBoxButton) {
|
|||
for (int32 i = 0; i < languageCount; ++i) {
|
||||
LangLoaderResult result;
|
||||
if (i) {
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), LangLoaderRequest(lng_language_name));
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), langLoaderRequest(lng_language_name));
|
||||
result = loader.found();
|
||||
} else {
|
||||
result.insert(lng_language_name, langOriginal(lng_language_name));
|
||||
|
@ -74,7 +74,7 @@ void LanguageBox::showAll() {
|
|||
void LanguageBox::mousePressEvent(QMouseEvent *e) {
|
||||
if ((e->modifiers() & Qt::CTRL) && (e->modifiers() & Qt::ALT) && (e->modifiers() & Qt::SHIFT)) {
|
||||
for (int32 i = 1; i < languageCount; ++i) {
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), LangLoaderRequest(lngkeys_cnt));
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[i].c_str() + qsl(".strings"), langLoaderRequest(lngkeys_cnt));
|
||||
if (!loader.errors().isEmpty()) {
|
||||
Ui::showLayer(new InformBox(qsl("Lang \"") + LanguageCodes[i].c_str() + qsl("\" error :(\n\nError: ") + loader.errors()));
|
||||
return;
|
||||
|
@ -104,10 +104,10 @@ void LanguageBox::onChange() {
|
|||
if (_langs[i]->checked() && langId != cLang()) {
|
||||
LangLoaderResult result;
|
||||
if (langId > 0) {
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), LangLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), langLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
|
||||
result = loader.found();
|
||||
} else if (langId == languageTest) {
|
||||
LangLoaderPlain loader(cLangFile(), LangLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
|
||||
LangLoaderPlain loader(cLangFile(), langLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
|
||||
result = loader.found();
|
||||
}
|
||||
QString text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)),
|
||||
|
@ -134,9 +134,7 @@ void LanguageBox::onSave() {
|
|||
if (_langs[i]->checked()) {
|
||||
cSetLang(_langs[i]->val());
|
||||
Local::writeSettings();
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
App::quit();
|
||||
App::restart();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -236,7 +236,7 @@ void ShareBox::onFilterUpdate(const QString &query) {
|
|||
void ShareBox::addPeerToMultiSelect(PeerData *peer, bool skipAnimation) {
|
||||
using AddItemWay = Ui::MultiSelect::AddItemWay;
|
||||
auto addItemWay = skipAnimation ? AddItemWay::SkipAnimation : AddItemWay::Default;
|
||||
_select->addItem(peer->id, peer->shortName(), st::windowActiveBg, PaintUserpicCallback(peer), addItemWay);
|
||||
_select->addItem(peer->id, peer->shortName(), st::windowActiveFill, PaintUserpicCallback(peer), addItemWay);
|
||||
}
|
||||
|
||||
void ShareBox::onPeerSelectedChanged(PeerData *peer, bool checked) {
|
||||
|
|
|
@ -62,6 +62,8 @@ public:
|
|||
Plus,
|
||||
Minus,
|
||||
Equals,
|
||||
And,
|
||||
Or,
|
||||
Name, // [0-9a-zA-Z_]+ with at least one letter.
|
||||
};
|
||||
Type type;
|
||||
|
@ -144,6 +146,8 @@ private:
|
|||
{ '+', Type::Plus },
|
||||
{ '-', Type::Minus },
|
||||
{ '=', Type::Equals },
|
||||
{ '&', Type::And },
|
||||
{ '|', Type::Or },
|
||||
};
|
||||
|
||||
};
|
||||
|
|
|
@ -41,6 +41,54 @@ namespace {
|
|||
constexpr int kErrorBadIconSize = 861;
|
||||
constexpr int kErrorBadIconFormat = 862;
|
||||
|
||||
// crc32 hash, taken somewhere from the internet
|
||||
|
||||
class Crc32Table {
|
||||
public:
|
||||
Crc32Table() {
|
||||
quint32 poly = 0x04c11db7;
|
||||
for (auto i = 0; i != 256; ++i) {
|
||||
_data[i] = reflect(i, 8) << 24;
|
||||
for (auto j = 0; j != 8; ++j) {
|
||||
_data[i] = (_data[i] << 1) ^ (_data[i] & (1 << 31) ? poly : 0);
|
||||
}
|
||||
_data[i] = reflect(_data[i], 32);
|
||||
}
|
||||
}
|
||||
|
||||
inline quint32 operator[](int index) const {
|
||||
return _data[index];
|
||||
}
|
||||
|
||||
private:
|
||||
quint32 reflect(quint32 val, char ch) {
|
||||
quint32 result = 0;
|
||||
for (int i = 1; i < (ch + 1); ++i) {
|
||||
if (val & 1) {
|
||||
result |= 1 << (ch - i);
|
||||
}
|
||||
val >>= 1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
quint32 _data[256];
|
||||
|
||||
};
|
||||
|
||||
qint32 hashCrc32(const void *data, int len) {
|
||||
static Crc32Table table;
|
||||
|
||||
const uchar *buffer = static_cast<const uchar *>(data);
|
||||
|
||||
quint32 crc = 0xffffffff;
|
||||
for (int i = 0; i != len; ++i) {
|
||||
crc = (crc >> 8) ^ table[(crc & 0xFF) ^ buffer[i]];
|
||||
}
|
||||
|
||||
return static_cast<qint32>(crc ^ 0xffffffff);
|
||||
}
|
||||
|
||||
char hexChar(uchar ch) {
|
||||
if (ch < 10) {
|
||||
return '0' + ch;
|
||||
|
@ -118,13 +166,38 @@ QString pxValueName(int value) {
|
|||
return result + QString::number(value);
|
||||
}
|
||||
|
||||
QString moduleBaseName(const structure::Module &module) {
|
||||
auto moduleInfo = QFileInfo(module.filepath());
|
||||
auto moduleIsPalette = (moduleInfo.suffix() == "palette");
|
||||
return moduleIsPalette ? "palette" : "style_" + moduleInfo.baseName();
|
||||
}
|
||||
|
||||
QChar paletteColorPart(uchar part) {
|
||||
part = (part & 0x0F);
|
||||
if (part >= 10) {
|
||||
return 'a' + (part - 10);
|
||||
}
|
||||
return '0' + part;
|
||||
}
|
||||
|
||||
QString paletteColorComponent(uchar value) {
|
||||
return QString() + paletteColorPart(value >> 4) + paletteColorPart(value);
|
||||
}
|
||||
|
||||
QString paletteColorValue(const structure::data::color &value) {
|
||||
auto result = paletteColorComponent(value.red) + paletteColorComponent(value.green) + paletteColorComponent(value.blue);
|
||||
if (value.alpha != 255) result += paletteColorComponent(value.alpha);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Generator::Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project)
|
||||
Generator::Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project, bool isPalette)
|
||||
: module_(module)
|
||||
, basePath_(destBasePath)
|
||||
, baseName_(QFileInfo(basePath_).baseName())
|
||||
, project_(project) {
|
||||
, project_(project)
|
||||
, isPalette_(isPalette) {
|
||||
}
|
||||
|
||||
bool Generator::writeHeader() {
|
||||
|
@ -164,8 +237,13 @@ public:\n\
|
|||
}\n\
|
||||
};\n\
|
||||
Module_" << baseName_ << " registrator;\n";
|
||||
if (!writeVariableDefinitions()) {
|
||||
return false;
|
||||
if (isPalette_) {
|
||||
source_->newline();
|
||||
source_->stream() << "style::palette _palette;\n";
|
||||
} else {
|
||||
if (!writeVariableDefinitions()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
source_->newline().popNamespace();
|
||||
|
||||
|
@ -174,8 +252,11 @@ Module_" << baseName_ << " registrator;\n";
|
|||
return false;
|
||||
}
|
||||
|
||||
source_->popNamespace().newline();
|
||||
source_->newline().pushNamespace("style").pushNamespace("internal").newline();
|
||||
source_->popNamespace().newline().pushNamespace("style");
|
||||
if (isPalette_) {
|
||||
writeSetPaletteColor();
|
||||
}
|
||||
source_->pushNamespace("internal").newline();
|
||||
if (!writeVariableInit()) {
|
||||
return false;
|
||||
}
|
||||
|
@ -329,12 +410,84 @@ bool Generator::writeHeaderStyleNamespace() {
|
|||
if (!writeStructsDefinitions()) {
|
||||
return false;
|
||||
}
|
||||
} else if (isPalette_) {
|
||||
if (!wroteForwardDeclarations) {
|
||||
header_->newline();
|
||||
}
|
||||
if (!writePaletteDefinition()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
header_->popNamespace().newline();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Generator::writePaletteDefinition() {
|
||||
header_->stream() << "\
|
||||
class palette {\n\
|
||||
public:\n\
|
||||
QByteArray save() const;\n\
|
||||
bool load(const QByteArray &cache);\n\
|
||||
bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);\n\
|
||||
bool setColor(QLatin1String name, QLatin1String from);\n\
|
||||
\n\
|
||||
// Created not inited, should be finalized before usage.\n\
|
||||
void finalize();\n\
|
||||
\n";
|
||||
|
||||
QByteArray checksumString;
|
||||
int indexInPalette = 0;
|
||||
if (!module_.enumVariables([this, &indexInPalette, &checksumString](const Variable &value) -> bool {
|
||||
auto name = value.name.back();
|
||||
if (value.value.type().tag != structure::TypeTag::Color) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto type = typeToString(value.value.type());
|
||||
auto index = (indexInPalette++);
|
||||
header_->stream() << "\tinline const " << type << " &" << name << "() const { return _colors[" << index << "]; };\n";
|
||||
checksumString.append(':' + name);
|
||||
return true;
|
||||
})) return false;
|
||||
|
||||
auto checksum = hashCrc32(checksumString.constData(), checksumString.size());
|
||||
auto count = indexInPalette;
|
||||
auto type = typeToString({ structure::TypeTag::Color });
|
||||
header_->stream() << "\
|
||||
\n\
|
||||
static constexpr int32 kChecksum = " << checksum << ";\n\
|
||||
\n\
|
||||
private:\n\
|
||||
struct TempColorData { uchar r, g, b, a; };\n\
|
||||
void compute(int index, int fallbackIndex, TempColorData data);\n\
|
||||
\n\
|
||||
enum class Status {\n\
|
||||
Initial,\n\
|
||||
Loaded,\n\
|
||||
Fallback,\n\
|
||||
};\n\
|
||||
\n\
|
||||
" << type << " _colors[" << count << "] = { Qt::Uninitialized };\n\
|
||||
Status _status[" << count << "] = { Status::Initial };\n\
|
||||
\n\
|
||||
};\n\
|
||||
\n\
|
||||
namespace main_palette {\n\
|
||||
\n\
|
||||
QByteArray save();\n\
|
||||
bool load(const QByteArray &cache);\n\
|
||||
bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);\n\
|
||||
bool setColor(QLatin1String name, QLatin1String from);\n\
|
||||
\n\
|
||||
} // namespace main_palette\n\
|
||||
\n";
|
||||
|
||||
header_->newline();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Generator::writeStructsForwardDeclarations() {
|
||||
bool hasNoExternalStructs = module_.enumVariables([this](const Variable &value) -> bool {
|
||||
if (value.value.type().tag == structure::TypeTag::Struct) {
|
||||
|
@ -411,7 +564,7 @@ bool Generator::writeIncludesInSource() {
|
|||
}
|
||||
|
||||
bool result = module_.enumIncludes([this](const Module &module) -> bool {
|
||||
source_->include("style_" + QFileInfo(module.filepath()).baseName() + ".h");
|
||||
source_->include(moduleBaseName(module) + ".h");
|
||||
return true;
|
||||
});
|
||||
source_->newline();
|
||||
|
@ -441,19 +594,189 @@ bool Generator::writeRefsDefinition() {
|
|||
return true;
|
||||
}
|
||||
|
||||
source_->newline();
|
||||
bool result = module_.enumVariables([this](const Variable &variable) -> bool {
|
||||
auto name = variable.name.back();
|
||||
auto type = typeToString(variable.value.type());
|
||||
if (type.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
source_->stream() << "const " << type << " &" << name << "(_" << name << ");\n";
|
||||
source_->stream() << "const " << type << " &" << name << "(";
|
||||
if (isPalette_) {
|
||||
source_->stream() << "_palette." << name << "()";
|
||||
} else {
|
||||
source_->stream() << "_" << name;
|
||||
}
|
||||
source_->stream() << ");\n";
|
||||
return true;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Generator::writeSetPaletteColor() {
|
||||
source_->newline();
|
||||
source_->stream() << "void palette::finalize() {\n";
|
||||
|
||||
int indexInPalette = 0;
|
||||
bool result = module_.enumVariables([this, &indexInPalette](const Variable &variable) -> bool {
|
||||
auto name = variable.name.back();
|
||||
auto index = indexInPalette++;
|
||||
paletteIndices_[name] = index;
|
||||
if (variable.value.type().tag != structure::TypeTag::Color) {
|
||||
return false;
|
||||
}
|
||||
auto color = variable.value.Color();
|
||||
auto fallbackIndex = paletteIndices_.value(variable.value.Color().fallback, -1);
|
||||
source_->stream() << "\tcompute(" << index << ", " << fallbackIndex << ", {" << color.red << ", " << color.green << ", " << color.blue << ", " << color.alpha << "});\n";
|
||||
return true;
|
||||
});
|
||||
auto count = indexInPalette;
|
||||
|
||||
source_->stream() << "\
|
||||
}\n\
|
||||
\n\
|
||||
void palette::compute(int index, int fallbackIndex, TempColorData data) {\n\
|
||||
if (_status[index] == Status::Initial) {\n\
|
||||
if (fallbackIndex >= 0 && _status[fallbackIndex] != Status::Initial) {\n\
|
||||
_status[index] = Status::Fallback;\n\
|
||||
_colors[index] = _colors[fallbackIndex];\n\
|
||||
} else {\n\
|
||||
_colors[index] = { data.r, data.g, data.b, data.a };\n\
|
||||
}\n\
|
||||
}\n\
|
||||
}\n";
|
||||
|
||||
source_->newline().pushNamespace().newline();
|
||||
source_->stream() << "\
|
||||
int getPaletteIndex(QLatin1String name) {\n\
|
||||
auto size = name.size();\n\
|
||||
auto data = name.data();\n";
|
||||
|
||||
int already = 0;
|
||||
QString prefix;
|
||||
QString tabs;
|
||||
for (auto i = paletteIndices_.end(), b = paletteIndices_.begin(); i != b;) {
|
||||
--i;
|
||||
auto name = i.key();
|
||||
auto index = i.value();
|
||||
auto prev = i;
|
||||
auto next = (i == b) ? QString() : (--prev).key();
|
||||
while ((prefix.size() > name.size()) || (!prefix.isEmpty() && prefix.mid(0, already - 1) != name.mid(0, already - 1))) {
|
||||
source_->stream() << "\n" << tabs << "};";
|
||||
prefix.chop(1);
|
||||
tabs.chop(1);
|
||||
--already;
|
||||
}
|
||||
if (!prefix.isEmpty() && prefix[already - 1] != name[already - 1]) {
|
||||
source_->stream() << "\n" << tabs << "case '" << name[already - 1] << "':";
|
||||
prefix[already - 1] = name[already - 1];
|
||||
}
|
||||
while (name.size() > already) {
|
||||
if (name.mid(0, already) != next.mid(0, already)) {
|
||||
break;
|
||||
} else if (next.size() <= already) {
|
||||
source_->stream() << "\n" << tabs << "\tif (size == " << name.size() << ")";
|
||||
break;
|
||||
}
|
||||
source_->stream() << "\n" << tabs << "\tif (size > " << already << ") switch (data[" << already << "]) {\n";
|
||||
prefix.append(name[already]);
|
||||
tabs.append('\t');
|
||||
++already;
|
||||
source_->stream() << tabs << "case '" << name[already - 1] << "':";
|
||||
}
|
||||
if (name.size() == already || name.mid(0, already) != next.mid(0, already)) {
|
||||
source_->stream() << " return (size == " << name.size();
|
||||
if (name.size() != already) {
|
||||
source_->stream() << " && ";
|
||||
}
|
||||
} else {
|
||||
source_->stream() << " return (";
|
||||
}
|
||||
if (already != name.size()) {
|
||||
source_->stream() << "!memcmp(data + " << already << ", \"" << name.mid(already) << "\", " << (name.size() - already) << ")";
|
||||
}
|
||||
source_->stream() << ") ? " << index << " : -1;";
|
||||
}
|
||||
while (!prefix.isEmpty()) {
|
||||
source_->stream() << "\n" << tabs << "};";
|
||||
prefix.chop(1);
|
||||
tabs.chop(1);
|
||||
--already;
|
||||
}
|
||||
|
||||
source_->stream() << "\
|
||||
\n\
|
||||
return -1;\n\
|
||||
}\n";
|
||||
|
||||
source_->newline().popNamespace().newline();
|
||||
source_->stream() << "\
|
||||
QByteArray palette::save() const {\n\
|
||||
auto result = QByteArray(" << (count * 4) << ", Qt::Uninitialized);\n\
|
||||
for (auto i = 0, index = 0; i != " << count << "; ++i) {\n\
|
||||
result[index++] = static_cast<uchar>(_colors[i]->c.red());\n\
|
||||
result[index++] = static_cast<uchar>(_colors[i]->c.green());\n\
|
||||
result[index++] = static_cast<uchar>(_colors[i]->c.blue());\n\
|
||||
result[index++] = static_cast<uchar>(_colors[i]->c.alpha());\n\
|
||||
}\n\
|
||||
return result;\n\
|
||||
}\n\
|
||||
\n\
|
||||
bool palette::load(const QByteArray &cache) {\n\
|
||||
if (cache.size() != " << (count * 4) << ") return false;\n\
|
||||
\n\
|
||||
auto p = reinterpret_cast<const uchar*>(cache.constData());\n\
|
||||
for (auto i = 0; i != " << count << "; ++i) {\n\
|
||||
_colors[i] = { p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3] };\n\
|
||||
_status[i] = Status::Loaded;\n\
|
||||
}\n\
|
||||
return true;\n\
|
||||
}\n\
|
||||
\n\
|
||||
bool palette::setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a) {\n\
|
||||
auto index = getPaletteIndex(name);\n\
|
||||
if (index >= 0) {\n\
|
||||
_colors[index] = { r, g, b, a };\n\
|
||||
_status[index] = Status::Loaded;\n\
|
||||
return true;\n\
|
||||
}\n\
|
||||
return false;\n\
|
||||
}\n\
|
||||
\n\
|
||||
bool palette::setColor(QLatin1String name, QLatin1String from) {\n\
|
||||
auto nameIndex = getPaletteIndex(name);\n\
|
||||
auto fromIndex = getPaletteIndex(from);\n\
|
||||
if (nameIndex >= 0 && fromIndex >= 0 && _status[fromIndex] == Status::Loaded) {\n\
|
||||
_colors[nameIndex] = _colors[fromIndex];\n\
|
||||
_status[nameIndex] = Status::Loaded;\n\
|
||||
return true;\n\
|
||||
}\n\
|
||||
return false;\n\
|
||||
}\n\
|
||||
\n\
|
||||
namespace main_palette {\n\
|
||||
\n\
|
||||
QByteArray save() {\n\
|
||||
return _palette.save();\n\
|
||||
}\n\
|
||||
\n\
|
||||
bool load(const QByteArray &cache) {\n\
|
||||
return _palette.load(cache);\n\
|
||||
}\n\
|
||||
\n\
|
||||
bool setColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a) {\n\
|
||||
return _palette.setColor(name, r, g, b, a);\n\
|
||||
}\n\
|
||||
\n\
|
||||
bool setColor(QLatin1String name, QLatin1String from) {\n\
|
||||
return _palette.setColor(name, from);\n\
|
||||
}\n\
|
||||
\n\
|
||||
} // namespace main_palette\n\
|
||||
\n";
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Generator::writeVariableInit() {
|
||||
if (!module_.hasVariables()) {
|
||||
return true;
|
||||
|
@ -486,7 +809,7 @@ void init_" << baseName_ << "() {\n\
|
|||
bool writtenAtLeastOne = false;
|
||||
bool result = module_.enumIncludes([this,&writtenAtLeastOne](const Module &module) -> bool {
|
||||
if (module.hasVariables()) {
|
||||
source_->stream() << "\tinit_style_" + QFileInfo(module.filepath()).baseName() + "();\n";
|
||||
source_->stream() << "\tinit_" + moduleBaseName(module) + "();\n";
|
||||
writtenAtLeastOne = true;
|
||||
}
|
||||
return true;
|
||||
|
@ -509,7 +832,9 @@ void init_" << baseName_ << "() {\n\
|
|||
source_->newline();
|
||||
}
|
||||
|
||||
bool result = module_.enumVariables([this](const Variable &variable) -> bool {
|
||||
if (isPalette_) {
|
||||
source_->stream() << "\t_palette.finalize();\n";
|
||||
} else if (!module_.enumVariables([this](const Variable &variable) -> bool {
|
||||
auto name = variable.name.back();
|
||||
auto value = valueAssignmentCode(variable.value);
|
||||
if (value.isEmpty()) {
|
||||
|
@ -517,11 +842,12 @@ void init_" << baseName_ << "() {\n\
|
|||
}
|
||||
source_->stream() << "\t_" << name << " = " << value << ";\n";
|
||||
return true;
|
||||
});
|
||||
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
source_->stream() << "\
|
||||
}\n\n";
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Generator::writePxValuesInit() {
|
||||
|
@ -629,7 +955,6 @@ QByteArray iconMaskValuePng(const QString &filepath) {
|
|||
{
|
||||
QBuffer buffer(&result);
|
||||
composed.save(&buffer, "PNG");
|
||||
// composed.save(filePath + "@final.png", "PNG");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -735,5 +1060,87 @@ bool Generator::collectUniqueValues() {
|
|||
return module_.enumVariables(collector);
|
||||
}
|
||||
|
||||
bool Generator::writeSampleTheme(const QString &filepath) {
|
||||
QByteArray content;
|
||||
QTextStream stream(&content);
|
||||
|
||||
stream << "\
|
||||
//\n\
|
||||
// This is a sample Telegram Desktop theme file.\n\
|
||||
// It was generated from the 'colors.palette' style file.\n\
|
||||
//\n\
|
||||
// To create a theme with a background image included you should\n\
|
||||
// put two files in a .zip archive:\n\
|
||||
//\n\
|
||||
// First one is the color scheme like the one you're viewing\n\
|
||||
// right now, this file should be named 'colors.tdesktop-theme'.\n\
|
||||
//\n\
|
||||
// Second one should be the background image and it can be named\n\
|
||||
// 'background.jpg', 'background.png', 'tiled.jpg' or 'tiled.png'.\n\
|
||||
// You should name it 'background' (if you'd like it not to be tiled),\n\
|
||||
// or it can be named 'tiled' (if you'd like it to be tiled).\n\
|
||||
//\n\
|
||||
// After that you need to change the extension of your .zip archive\n\
|
||||
// to 'tdesktop-theme', so you'll have:\n\
|
||||
//\n\
|
||||
// mytheme.tdesktop-theme\n\
|
||||
// |-colors.tdesktop-theme\n\
|
||||
// |-background.jpg (or tiled.jpg, background.png, tiled.png)\n\
|
||||
//\n\n";
|
||||
|
||||
QList<structure::FullName> names;
|
||||
module_.enumVariables([this, &names](const Variable &variable) -> bool {
|
||||
names.push_back(variable.name);
|
||||
return true;
|
||||
});
|
||||
bool result = module_.enumVariables([this, &names, &stream](const Variable &variable) -> bool {
|
||||
auto name = variable.name.back();
|
||||
if (variable.value.type().tag != structure::TypeTag::Color) {
|
||||
return false;
|
||||
}
|
||||
auto color = variable.value.Color();
|
||||
auto colorString = paletteColorValue(color);
|
||||
auto fallbackIndex = paletteIndices_.value(variable.value.Color().fallback, -1);
|
||||
if (fallbackIndex >= 0) {
|
||||
auto fallbackVariable = module_.findVariableInModule(names[fallbackIndex], module_);
|
||||
if (!fallbackVariable || fallbackVariable->value.type().tag != structure::TypeTag::Color) {
|
||||
return false;
|
||||
}
|
||||
auto fallbackName = fallbackVariable->name.back();
|
||||
auto fallbackColor = fallbackVariable->value.Color();
|
||||
if (colorString == paletteColorValue(fallbackColor)) {
|
||||
stream << name << ": " << fallbackName << ";\n";
|
||||
} else {
|
||||
stream << name << ": #" << colorString << "; // " << fallbackName << ";\n";
|
||||
}
|
||||
} else {
|
||||
stream << name << ": #" << colorString << ";\n";
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (!result) {
|
||||
return result;
|
||||
}
|
||||
|
||||
stream.flush();
|
||||
|
||||
QFile file(filepath);
|
||||
if (file.open(QIODevice::ReadOnly)) {
|
||||
if (file.readAll() == content) {
|
||||
file.close();
|
||||
return true;
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
if (!file.open(QIODevice::WriteOnly)) {
|
||||
return false;
|
||||
}
|
||||
if (file.write(content) != content.size()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace style
|
||||
} // namespace codegen
|
||||
|
|
|
@ -34,12 +34,13 @@ class Module;
|
|||
|
||||
class Generator {
|
||||
public:
|
||||
Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project);
|
||||
Generator(const structure::Module &module, const QString &destBasePath, const common::ProjectInfo &project, bool isPalette);
|
||||
Generator(const Generator &other) = delete;
|
||||
Generator &operator=(const Generator &other) = delete;
|
||||
|
||||
bool writeHeader();
|
||||
bool writeSource();
|
||||
bool writeSampleTheme(const QString &filepath);
|
||||
|
||||
private:
|
||||
QString typeToString(structure::Type type) const;
|
||||
|
@ -49,11 +50,13 @@ private:
|
|||
bool writeHeaderStyleNamespace();
|
||||
bool writeStructsForwardDeclarations();
|
||||
bool writeStructsDefinitions();
|
||||
bool writePaletteDefinition();
|
||||
bool writeRefsDeclarations();
|
||||
|
||||
bool writeIncludesInSource();
|
||||
bool writeVariableDefinitions();
|
||||
bool writeRefsDefinition();
|
||||
bool writeSetPaletteColor();
|
||||
bool writeVariableInit();
|
||||
bool writePxValuesInit();
|
||||
bool writeFontFamiliesInit();
|
||||
|
@ -66,10 +69,12 @@ private:
|
|||
QString basePath_, baseName_;
|
||||
const common::ProjectInfo &project_;
|
||||
std::unique_ptr<common::CppFile> source_, header_;
|
||||
bool isPalette_ = false;
|
||||
|
||||
QMap<int, bool> pxValues_;
|
||||
QMap<std::string, int> fontFamilies_;
|
||||
QMap<QString, int> iconMasks_; // icon file -> index
|
||||
QMap<QString, int> paletteIndices_;
|
||||
|
||||
std::vector<int> scales = { 4, 5, 6, 8 }; // scale / 4 gives our 1.00, 1.25, 1.50, 2.00
|
||||
std::vector<const char *>scaleNames = { "dbisOne", "dbisOneAndQuarter", "dbisOneAndHalf", "dbisTwo" };
|
||||
|
|
|
@ -41,16 +41,12 @@ using common::logError;
|
|||
|
||||
Options parseOptions() {
|
||||
Options result;
|
||||
auto args(QCoreApplication::instance()->arguments());
|
||||
auto args = QCoreApplication::instance()->arguments();
|
||||
for (int i = 1, count = args.size(); i < count; ++i) { // skip first
|
||||
const auto &arg(args.at(i));
|
||||
|
||||
// Rebuild all dependencies
|
||||
if (arg == "--rebuild") {
|
||||
result.rebuildDependencies = true;
|
||||
auto &arg = args.at(i);
|
||||
|
||||
// Include paths
|
||||
} else if (arg == "-I") {
|
||||
if (arg == "-I") {
|
||||
if (++i == count) {
|
||||
logError(kErrorIncludePathExpected, "Command Line") << "include path expected after -I";
|
||||
return Options();
|
||||
|
@ -96,6 +92,7 @@ Options parseOptions() {
|
|||
logError(kErrorInputPathExpected, "Command Line") << "input path expected";
|
||||
return Options();
|
||||
}
|
||||
result.isPalette = (QFileInfo(result.inputPath).suffix() == "palette");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ struct Options {
|
|||
QStringList includePaths = { "." };
|
||||
QString outputPath = ".";
|
||||
QString inputPath;
|
||||
bool rebuildDependencies = false;
|
||||
bool isPalette = false;
|
||||
};
|
||||
|
||||
// Parsing failed if inputPath is empty in the result.
|
||||
|
|
|
@ -77,6 +77,10 @@ bool isValidColor(const QString &str) {
|
|||
return true;
|
||||
}
|
||||
|
||||
uchar toGray(uchar r, uchar g, uchar b) {
|
||||
return qMax(qMin(int(0.21 * r + 0.72 * g + 0.07 * b), 255), 0);
|
||||
}
|
||||
|
||||
uchar readHexUchar(QChar ch) {
|
||||
auto code = ch.unicode();
|
||||
return (code >= '0' && code <= '9') ? ((code - '0') & 0xFF) : ((code + 10 - 'a') & 0xFF);
|
||||
|
@ -86,7 +90,7 @@ uchar readHexUchar(QChar char1, QChar char2) {
|
|||
return ((readHexUchar(char1) & 0x0F) << 4) | (readHexUchar(char2) & 0x0F);
|
||||
}
|
||||
|
||||
structure::data::color convertWebColor(const QString &str) {
|
||||
structure::data::color convertWebColor(const QString &str, const QString &fallback = QString()) {
|
||||
uchar r = 0, g = 0, b = 0, a = 255;
|
||||
if (isValidColor(str)) {
|
||||
r = readHexUchar(str.at(0), str.at(1));
|
||||
|
@ -96,7 +100,7 @@ structure::data::color convertWebColor(const QString &str) {
|
|||
a = readHexUchar(str.at(6), str.at(7));
|
||||
}
|
||||
}
|
||||
return { r, g, b, a };
|
||||
return { r, g, b, a, fallback };
|
||||
}
|
||||
|
||||
structure::data::color convertIntColor(int r, int g, int b, int a) {
|
||||
|
@ -216,6 +220,11 @@ ParsedFile::ModulePtr ParsedFile::readIncluded() {
|
|||
}
|
||||
|
||||
structure::Struct ParsedFile::readStruct(const QString &name) {
|
||||
if (options_.isPalette) {
|
||||
logErrorUnexpectedToken() << "unique color variable for the palette";
|
||||
return {};
|
||||
}
|
||||
|
||||
structure::Struct result = { composeFullName(name) };
|
||||
do {
|
||||
if (auto fieldName = file_.getToken(BasicType::Name)) {
|
||||
|
@ -236,6 +245,10 @@ structure::Variable ParsedFile::readVariable(const QString &name) {
|
|||
structure::Variable result = { composeFullName(name) };
|
||||
if (auto value = readValue()) {
|
||||
result.value = value;
|
||||
if (options_.isPalette && value.type().tag != structure::TypeTag::Color) {
|
||||
logErrorUnexpectedToken() << "unique color variable for the palette";
|
||||
return {};
|
||||
}
|
||||
if (value.type().tag != structure::TypeTag::Struct || !value.copyOf().empty()) {
|
||||
assertNextToken(BasicType::Semicolon);
|
||||
}
|
||||
|
@ -491,14 +504,35 @@ structure::Value ParsedFile::readStringValue() {
|
|||
|
||||
structure::Value ParsedFile::readColorValue() {
|
||||
if (auto numberSign = file_.getToken(BasicType::Number)) {
|
||||
auto color = file_.getAnyToken();
|
||||
if (color.type == BasicType::Int || color.type == BasicType::Name) {
|
||||
auto chars = tokenValue(color).toLower();
|
||||
if (isValidColor(chars)) {
|
||||
return { convertWebColor(chars) };
|
||||
if (options_.isPalette || true) { // enable for now
|
||||
auto color = file_.getAnyToken();
|
||||
if (color.type == BasicType::Int || color.type == BasicType::Name) {
|
||||
auto chars = tokenValue(color).toLower();
|
||||
if (isValidColor(chars)) {
|
||||
if (auto fallbackSeparator = file_.getToken(BasicType::Or)) {
|
||||
if (options_.isPalette) {
|
||||
if (auto fallbackName = file_.getToken(BasicType::Name)) {
|
||||
structure::FullName name = { tokenValue(fallbackName) };
|
||||
if (auto variable = module_->findVariableInModule(name, *module_)) {
|
||||
return { convertWebColor(chars, tokenValue(fallbackName)) };
|
||||
} else {
|
||||
logError(kErrorIdentifierNotFound) << "fallback color name";
|
||||
}
|
||||
} else {
|
||||
logErrorUnexpectedToken() << "fallback color name";
|
||||
}
|
||||
} else {
|
||||
logErrorUnexpectedToken() << "';', color fallbacks are only allowed in palette module";
|
||||
}
|
||||
} else {
|
||||
return { convertWebColor(chars) };
|
||||
}
|
||||
}
|
||||
} else {
|
||||
logErrorUnexpectedToken() << "color value in #ccc, #ccca, #cccccc or #ccccccaa format";
|
||||
}
|
||||
} else {
|
||||
logErrorUnexpectedToken() << "color value in #ccc, #ccca, #cccccc or #ccccccaa format";
|
||||
logErrorUnexpectedToken() << "color value alias, unique color values are only allowed in palette module";
|
||||
}
|
||||
}
|
||||
return {};
|
||||
|
@ -815,6 +849,7 @@ Options ParsedFile::includedOptions(const QString &filepath) {
|
|||
auto result = options_;
|
||||
result.inputPath = filepath;
|
||||
result.includePaths[0] = QFileInfo(filePath_).dir().absolutePath();
|
||||
result.isPalette = (QFileInfo(filepath).suffix() == "palette");
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -49,14 +49,7 @@ int Processor::launch() {
|
|||
}
|
||||
|
||||
auto module = parser_->getResult();
|
||||
if (options_.rebuildDependencies) {
|
||||
bool result = module->enumIncludes([this](const structure::Module &included) -> bool {
|
||||
return write(included);
|
||||
});
|
||||
if (!result) {
|
||||
return -1;
|
||||
}
|
||||
} else if (!write(*module)) {
|
||||
if (!write(*module)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
@ -72,7 +65,7 @@ bool Processor::write(const structure::Module &module) const {
|
|||
}
|
||||
|
||||
QFileInfo srcFile(module.filepath());
|
||||
QString dstFilePath = dir.absolutePath() + '/' + destFileBaseName(module);
|
||||
QString dstFilePath = dir.absolutePath() + '/' + (options_.isPalette ? "palette" : destFileBaseName(module));
|
||||
|
||||
common::ProjectInfo project = {
|
||||
"codegen_style",
|
||||
|
@ -81,13 +74,17 @@ bool Processor::write(const structure::Module &module) const {
|
|||
forceReGenerate
|
||||
};
|
||||
|
||||
Generator generator(module, dstFilePath, project);
|
||||
Generator generator(module, dstFilePath, project, options_.isPalette);
|
||||
if (!generator.writeHeader()) {
|
||||
return false;
|
||||
}
|
||||
if (!generator.writeSource()) {
|
||||
return false;
|
||||
}
|
||||
auto themePath = srcFile.absoluteDir().absolutePath() + "/sample.tdesktop-theme";
|
||||
if (options_.isPalette && !generator.writeSampleTheme(themePath)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ struct size {
|
|||
|
||||
struct color {
|
||||
uchar red, green, blue, alpha;
|
||||
QString fallback;
|
||||
};
|
||||
|
||||
struct margins {
|
||||
|
|
|
@ -341,8 +341,6 @@ static const char *DefaultCountry = "US";
|
|||
static const char *DefaultLanguage = "en";
|
||||
|
||||
enum {
|
||||
DefaultChatBackground = 21,
|
||||
|
||||
DialogsFirstLoad = 20, // first dialogs part size requested
|
||||
DialogsPerPage = 500, // next dialogs part size
|
||||
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "core/parse_helper.h"
|
||||
|
||||
namespace base {
|
||||
namespace parse {
|
||||
|
||||
// inspired by https://github.com/sindresorhus/strip-json-comments
|
||||
QByteArray stripComments(const QByteArray &content) {
|
||||
enum class InsideComment {
|
||||
None,
|
||||
SingleLine,
|
||||
MultiLine,
|
||||
};
|
||||
auto insideComment = InsideComment::None;
|
||||
auto insideString = false;
|
||||
|
||||
QByteArray result;
|
||||
auto begin = content.cbegin(), end = content.cend(), offset = begin;
|
||||
auto feedContent = [&result, &offset, end](const char *ch) {
|
||||
if (ch > offset) {
|
||||
if (result.isEmpty()) result.reserve(end - offset - 2);
|
||||
result.append(offset, ch - offset);
|
||||
offset = ch;
|
||||
}
|
||||
};
|
||||
auto feedComment = [&result, &offset, end](const char *ch) {
|
||||
if (ch > offset) {
|
||||
if (result.isEmpty()) result.reserve(end - offset - 2);
|
||||
result.append(' ');
|
||||
offset = ch;
|
||||
}
|
||||
};
|
||||
for (auto ch = offset; ch != end;) {
|
||||
auto currentChar = *ch;
|
||||
auto nextChar = (ch + 1 == end) ? 0 : *(ch + 1);
|
||||
|
||||
if (insideComment == InsideComment::None && currentChar == '"') {
|
||||
auto escaped = ((ch > begin) && *(ch - 1) == '\\') && ((ch - 1 < begin) || *(ch - 2) != '\\');
|
||||
if (!escaped) {
|
||||
insideString = !insideString;
|
||||
}
|
||||
}
|
||||
if (insideString) {
|
||||
++ch;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '/') {
|
||||
feedContent(ch);
|
||||
insideComment = InsideComment::SingleLine;
|
||||
ch += 2;
|
||||
} else if (insideComment == InsideComment::SingleLine && currentChar == '\r' && nextChar == '\n') {
|
||||
feedComment(ch);
|
||||
ch += 2;
|
||||
insideComment = InsideComment::None;
|
||||
} else if (insideComment == InsideComment::SingleLine && currentChar == '\n') {
|
||||
feedComment(ch);
|
||||
++ch;
|
||||
insideComment = InsideComment::None;
|
||||
} else if (insideComment == InsideComment::None && currentChar == '/' && nextChar == '*') {
|
||||
feedContent(ch);
|
||||
ch += 2;
|
||||
insideComment = InsideComment::MultiLine;
|
||||
} else if (insideComment == InsideComment::MultiLine && currentChar == '*' && nextChar == '/') {
|
||||
ch += 2;
|
||||
feedComment(ch);
|
||||
insideComment = InsideComment::None;
|
||||
} else if (insideComment == InsideComment::MultiLine && currentChar == '\r' && nextChar == '\n') {
|
||||
feedComment(ch);
|
||||
ch += 2;
|
||||
feedContent(ch);
|
||||
} else if (insideComment == InsideComment::MultiLine && currentChar == '\n') {
|
||||
feedComment(ch);
|
||||
++ch;
|
||||
feedContent(ch);
|
||||
} else {
|
||||
++ch;
|
||||
}
|
||||
}
|
||||
|
||||
if (insideComment == InsideComment::MultiLine) {
|
||||
// unexpected end of content
|
||||
}
|
||||
if (insideComment == InsideComment::None && end > offset) {
|
||||
if (result.isEmpty()) {
|
||||
return content;
|
||||
} else {
|
||||
result.append(offset, end - offset);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace parse
|
||||
} // namespace base
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace base {
|
||||
namespace parse {
|
||||
|
||||
// Strip all C-style comments.
|
||||
QByteArray stripComments(const QByteArray &content);
|
||||
|
||||
inline bool skipWhitespaces(const char *&from, const char *end) {
|
||||
t_assert(from <= end);
|
||||
while (from != end && (
|
||||
(*from == ' ') ||
|
||||
(*from == '\n') ||
|
||||
(*from == '\t') ||
|
||||
(*from == '\r'))) {
|
||||
++from;
|
||||
}
|
||||
return (from != end);
|
||||
}
|
||||
|
||||
inline QByteArray readName(const char *&from, const char *end) {
|
||||
t_assert(from <= end);
|
||||
auto start = from;
|
||||
while (from != end && (
|
||||
(*from >= 'a' && *from <= 'z') ||
|
||||
(*from >= 'A' && *from <= 'Z') ||
|
||||
(*from >= '0' && *from <= '9') ||
|
||||
(*from == '_'))) {
|
||||
++from;
|
||||
}
|
||||
return QByteArray::fromRawData(start, from - start);
|
||||
}
|
||||
|
||||
} // namespace parse
|
||||
} // namespace base
|
|
@ -78,6 +78,10 @@ inline QString str_const_toString(const str_const &str) {
|
|||
return QString::fromUtf8(str.c_str(), str.size());
|
||||
}
|
||||
|
||||
inline QByteArray str_const_toByteArray(const str_const &str) {
|
||||
return QByteArray::fromRawData(str.c_str(), str.size());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void accumulate_max(T &a, const T &b) { if (a < b) a = b; }
|
||||
|
||||
|
|
|
@ -0,0 +1,390 @@
|
|||
/*
|
||||
WARNING! All changes made in this file will be lost!
|
||||
Created from 'colors.palette' by 'codegen_style'
|
||||
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "zip.h"
|
||||
#include "unzip.h"
|
||||
|
||||
namespace zlib {
|
||||
namespace internal {
|
||||
|
||||
class InMemoryFile {
|
||||
public:
|
||||
InMemoryFile(const QByteArray &data = QByteArray()) : _data(data) {
|
||||
}
|
||||
|
||||
zlib_filefunc_def funcs() {
|
||||
zlib_filefunc_def result;
|
||||
result.opaque = this;
|
||||
result.zopen_file = &InMemoryFile::Open;
|
||||
result.zerror_file = &InMemoryFile::Error;
|
||||
result.zread_file = &InMemoryFile::Read;
|
||||
result.zwrite_file = &InMemoryFile::Write;
|
||||
result.zclose_file = &InMemoryFile::Close;
|
||||
result.zseek_file = &InMemoryFile::Seek;
|
||||
result.ztell_file = &InMemoryFile::Tell;
|
||||
return result;
|
||||
}
|
||||
|
||||
int error() const {
|
||||
return _error;
|
||||
}
|
||||
|
||||
QByteArray result() const {
|
||||
return _data;
|
||||
}
|
||||
|
||||
private:
|
||||
voidpf open(const char *filename, int mode) {
|
||||
if (mode & ZLIB_FILEFUNC_MODE_WRITE) {
|
||||
if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
|
||||
_data.clear();
|
||||
}
|
||||
_position = _data.size();
|
||||
_data.reserve(2 * 1024 * 1024);
|
||||
} else if (mode & ZLIB_FILEFUNC_MODE_READ) {
|
||||
_position = 0;
|
||||
}
|
||||
_error = 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
uLong read(voidpf stream, void* buf, uLong size) {
|
||||
uLong toRead = 0;
|
||||
if (!_error) {
|
||||
if (_data.size() > int(_position)) {
|
||||
toRead = qMin(size, uLong(_data.size() - _position));
|
||||
memcpy(buf, _data.constData() + _position, toRead);
|
||||
_position += toRead;
|
||||
}
|
||||
if (toRead < size) {
|
||||
_error = -1;
|
||||
}
|
||||
}
|
||||
return toRead;
|
||||
}
|
||||
|
||||
uLong write(voidpf stream, const void* buf, uLong size) {
|
||||
if (_data.size() < int(_position + size)) {
|
||||
_data.resize(_position + size);
|
||||
}
|
||||
memcpy(_data.data() + _position, buf, size);
|
||||
_position += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
int close(voidpf stream) {
|
||||
auto result = _error;
|
||||
_position = 0;
|
||||
_error = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int error(voidpf stream) const {
|
||||
return _error;
|
||||
}
|
||||
|
||||
long tell(voidpf stream) const {
|
||||
return _position;
|
||||
}
|
||||
|
||||
long seek(voidpf stream, uLong offset, int origin) {
|
||||
if (!_error) {
|
||||
switch (origin) {
|
||||
case ZLIB_FILEFUNC_SEEK_SET: _position = offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_CUR: _position += offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_END: _position = _data.size() + offset; break;
|
||||
}
|
||||
if (int(_position) > _data.size()) {
|
||||
_error = -1;
|
||||
}
|
||||
}
|
||||
return _error;
|
||||
}
|
||||
|
||||
static voidpf Open(voidpf opaque, const char* filename, int mode) {
|
||||
return static_cast<InMemoryFile*>(opaque)->open(filename, mode);
|
||||
}
|
||||
|
||||
static uLong Read(voidpf opaque, voidpf stream, void* buf, uLong size) {
|
||||
return static_cast<InMemoryFile*>(opaque)->read(stream, buf, size);
|
||||
}
|
||||
|
||||
static uLong Write(voidpf opaque, voidpf stream, const void* buf, uLong size) {
|
||||
return static_cast<InMemoryFile*>(opaque)->write(stream, buf, size);
|
||||
}
|
||||
|
||||
static int Close(voidpf opaque, voidpf stream) {
|
||||
return static_cast<InMemoryFile*>(opaque)->close(stream);
|
||||
}
|
||||
|
||||
static int Error(voidpf opaque, voidpf stream) {
|
||||
return static_cast<InMemoryFile*>(opaque)->error(stream);
|
||||
}
|
||||
|
||||
static long Tell(voidpf opaque, voidpf stream) {
|
||||
return static_cast<InMemoryFile*>(opaque)->tell(stream);
|
||||
}
|
||||
|
||||
static long Seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
|
||||
return static_cast<InMemoryFile*>(opaque)->seek(stream, offset, origin);
|
||||
}
|
||||
|
||||
uLong _position = 0;
|
||||
int _error = 0;
|
||||
QByteArray _data;
|
||||
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
constexpr int kCaseSensitive = 1;
|
||||
constexpr int kCaseInsensitive = 2;
|
||||
|
||||
class FileToRead {
|
||||
public:
|
||||
FileToRead(const QByteArray &content) : _data(content) {
|
||||
auto funcs = _data.funcs();
|
||||
if (!(_handle = unzOpen2(nullptr, &funcs))) {
|
||||
_error = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int getGlobalInfo(unz_global_info *pglobal_info) {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzGetGlobalInfo(_handle, pglobal_info) : -1;
|
||||
}
|
||||
return _error;
|
||||
}
|
||||
|
||||
int locateFile(const char *szFileName, int iCaseSensitivity) {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzLocateFile(_handle, szFileName, iCaseSensitivity) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int getCurrentFileInfo(
|
||||
unz_file_info *pfile_info,
|
||||
char *szFileName,
|
||||
uLong fileNameBufferSize,
|
||||
void *extraField,
|
||||
uLong extraFieldBufferSize,
|
||||
char *szComment,
|
||||
uLong commentBufferSize
|
||||
) {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzGetCurrentFileInfo(
|
||||
_handle,
|
||||
pfile_info,
|
||||
szFileName,
|
||||
fileNameBufferSize,
|
||||
extraField,
|
||||
extraFieldBufferSize,
|
||||
szComment,
|
||||
commentBufferSize
|
||||
) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int openCurrentFile() {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzOpenCurrentFile(_handle) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int readCurrentFile(voidp buf, unsigned len) {
|
||||
if (error() == UNZ_OK) {
|
||||
auto result = _handle ? unzReadCurrentFile(_handle, buf, len) : -1;
|
||||
if (result >= 0) {
|
||||
return result;
|
||||
} else {
|
||||
_error = result;
|
||||
}
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int closeCurrentFile() {
|
||||
if (error() == UNZ_OK) {
|
||||
_error = _handle ? unzCloseCurrentFile(_handle) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
QByteArray readCurrentFileContent(int fileSizeLimit) {
|
||||
unz_file_info fileInfo = { 0 };
|
||||
if (getCurrentFileInfo(&fileInfo, nullptr, 0, nullptr, 0, nullptr, 0) != UNZ_OK) {
|
||||
LOG(("Error: could not get current file info in a zip file."));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
auto size = fileInfo.uncompressed_size;
|
||||
if (size > fileSizeLimit) {
|
||||
if (_error == UNZ_OK) _error = -1;
|
||||
LOG(("Error: current file is too large (should be less than %1, got %2) in a zip file.").arg(fileSizeLimit).arg(size));
|
||||
return QByteArray();
|
||||
}
|
||||
if (openCurrentFile() != UNZ_OK) {
|
||||
LOG(("Error: could not open current file in a zip file."));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
QByteArray result;
|
||||
result.resize(size);
|
||||
|
||||
auto couldRead = readCurrentFile(result.data(), size);
|
||||
if (couldRead != size) {
|
||||
LOG(("Error: could not read current file in a zip file, got %1.").arg(couldRead));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
if (closeCurrentFile() != UNZ_OK) {
|
||||
LOG(("Error: could not close current file in a zip file."));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QByteArray readFileContent(const char *szFileName, int iCaseSensitivity, int fileSizeLimit) {
|
||||
if (locateFile(szFileName, iCaseSensitivity) != UNZ_OK) {
|
||||
LOG(("Error: could not locate '%1' in a zip file.").arg(szFileName));
|
||||
return false;
|
||||
}
|
||||
return readCurrentFileContent(fileSizeLimit);
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (_handle && unzClose(_handle) != UNZ_OK && _error == UNZ_OK) {
|
||||
_error = -1;
|
||||
}
|
||||
_handle = nullptr;
|
||||
}
|
||||
|
||||
int error() const {
|
||||
if (auto dataError = _data.error()) {
|
||||
return dataError;
|
||||
}
|
||||
return _error;
|
||||
}
|
||||
|
||||
void clearError() {
|
||||
_error = UNZ_OK;
|
||||
}
|
||||
|
||||
~FileToRead() {
|
||||
close();
|
||||
}
|
||||
|
||||
private:
|
||||
internal::InMemoryFile _data;
|
||||
unzFile _handle = nullptr;
|
||||
int _error = 0;
|
||||
|
||||
};
|
||||
|
||||
class FileToWrite {
|
||||
public:
|
||||
FileToWrite() {
|
||||
auto funcs = _data.funcs();
|
||||
if (!(_handle = zipOpen2(nullptr, APPEND_STATUS_CREATE, nullptr, &funcs))) {
|
||||
_error = -1;
|
||||
}
|
||||
}
|
||||
|
||||
int openNewFile(
|
||||
const char *filename,
|
||||
const zip_fileinfo *zipfi,
|
||||
const void *extrafield_local,
|
||||
uInt size_extrafield_local,
|
||||
const void* extrafield_global,
|
||||
uInt size_extrafield_global,
|
||||
const char* comment,
|
||||
int method,
|
||||
int level
|
||||
) {
|
||||
if (error() == ZIP_OK) {
|
||||
_error = _handle ? zipOpenNewFileInZip(
|
||||
_handle,
|
||||
filename,
|
||||
zipfi,
|
||||
extrafield_local,
|
||||
size_extrafield_local,
|
||||
extrafield_global,
|
||||
size_extrafield_global,
|
||||
comment,
|
||||
method,
|
||||
level
|
||||
) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int writeInFile(const void* buf, unsigned len) {
|
||||
if (error() == ZIP_OK) {
|
||||
_error = _handle ? zipWriteInFileInZip(_handle, buf, len) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
int closeFile() {
|
||||
if (error() == ZIP_OK) {
|
||||
_error = _handle ? zipCloseFileInZip(_handle) : -1;
|
||||
}
|
||||
return error();
|
||||
}
|
||||
|
||||
void close() {
|
||||
if (_handle && zipClose(_handle, nullptr) != ZIP_OK && _error == ZIP_OK) {
|
||||
_error = -1;
|
||||
}
|
||||
_handle = nullptr;
|
||||
}
|
||||
|
||||
int error() const {
|
||||
if (auto dataError = _data.error()) {
|
||||
return dataError;
|
||||
}
|
||||
return _error;
|
||||
}
|
||||
|
||||
QByteArray result() const {
|
||||
return _data.result();
|
||||
}
|
||||
|
||||
~FileToWrite() {
|
||||
close();
|
||||
}
|
||||
|
||||
private:
|
||||
internal::InMemoryFile _data;
|
||||
zipFile _handle = nullptr;
|
||||
int _error = 0;
|
||||
|
||||
};
|
||||
|
||||
} // namespace zlib
|
|
@ -25,7 +25,7 @@ using "ui/widgets/widgets.style";
|
|||
|
||||
dialogsUnreadFg: #ffffff;
|
||||
dialogsUnreadFgActive: #5b94bf;
|
||||
dialogsUnreadBg: windowActiveBg;
|
||||
dialogsUnreadBg: windowActiveFill;
|
||||
dialogsUnreadBgMuted: #bbbbbb;
|
||||
dialogsUnreadBgActive: #ffffff;
|
||||
dialogsUnreadBgMutedActive: #d3e2ee;
|
||||
|
|
|
@ -183,7 +183,7 @@ void paintUnreadBadge(Painter &p, const QRect &rect, const UnreadBadgeStyle &st)
|
|||
t_assert(st.sizeId < UnreadBadgeSizesCount);
|
||||
badgeData = &unreadBadgeStyle->sizes[st.sizeId];
|
||||
}
|
||||
style::color bg = unreadBadgeStyle->bg[index];
|
||||
auto &bg = unreadBadgeStyle->bg[index];
|
||||
if (badgeData->left[index].isNull()) {
|
||||
int imgsize = size * cIntRetinaFactor(), imgsizehalf = sizehalf * cIntRetinaFactor();
|
||||
createCircleMask(badgeData, size);
|
||||
|
|
|
@ -667,6 +667,8 @@ struct Data {
|
|||
|
||||
base::Observable<HistoryItem*> ItemRemoved;
|
||||
|
||||
bool ApplyingTheme = false;
|
||||
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
@ -782,4 +784,6 @@ DefineRefVar(Global, base::Observable<void>, LocalPasscodeChanged);
|
|||
|
||||
DefineRefVar(Global, base::Observable<HistoryItem*>, ItemRemoved);
|
||||
|
||||
DefineVar(Global, bool, ApplyingTheme);
|
||||
|
||||
} // namespace Global
|
||||
|
|
|
@ -356,6 +356,8 @@ DeclareRefVar(base::Observable<void>, LocalPasscodeChanged);
|
|||
|
||||
DeclareRefVar(base::Observable<HistoryItem*>, ItemRemoved);
|
||||
|
||||
DeclareVar(bool, ApplyingTheme);
|
||||
|
||||
} // namespace Global
|
||||
|
||||
namespace Adaptive {
|
||||
|
|
|
@ -1172,7 +1172,7 @@ void HistoryDocument::draw(Painter &p, const QRect &r, TextSelection selection,
|
|||
|
||||
if (radial) {
|
||||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||
style::color bg(outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg));
|
||||
auto &bg = outbg ? (selected ? st::msgOutBgSelected : st::msgOutBg) : (selected ? st::msgInBgSelected : st::msgInBg);
|
||||
_animation->radial.draw(p, rinner, st::msgFileRadialLine, bg);
|
||||
}
|
||||
|
||||
|
|
|
@ -1291,8 +1291,8 @@ void HistoryMessage::draw(Painter &p, const QRect &r, TextSelection selection, u
|
|||
auto top = marginTop();
|
||||
QRect r(left, top, width, height - top - marginBottom());
|
||||
|
||||
style::color bg(selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg));
|
||||
style::color sh(selected ? (outbg ? st::msgOutShadowSelected : st::msgInShadowSelected) : (outbg ? st::msgOutShadow : st::msgInShadow));
|
||||
auto &bg = selected ? (outbg ? st::msgOutBgSelected : st::msgInBgSelected) : (outbg ? st::msgOutBg : st::msgInBg);
|
||||
auto &sh = selected ? (outbg ? st::msgOutShadowSelected : st::msgInShadowSelected) : (outbg ? st::msgOutShadow : st::msgInShadow);
|
||||
RoundCorners cors(selected ? (outbg ? MessageOutSelectedCorners : MessageInSelectedCorners) : (outbg ? MessageOutCorners : MessageInCorners));
|
||||
App::roundRect(p, r, bg, cors, &sh);
|
||||
|
||||
|
|
|
@ -51,7 +51,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "localstorage.h"
|
||||
#include "apiwrap.h"
|
||||
#include "window/top_bar_widget.h"
|
||||
#include "window/chat_background.h"
|
||||
#include "window/window_theme.h"
|
||||
#include "observer_peer.h"
|
||||
#include "core/qthelp_regex.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
|
@ -8746,8 +8746,8 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
|
|||
int fromy = App::main()->backgroundFromY(), x = 0, y = 0;
|
||||
QPixmap cached = App::main()->cachedBackground(fill, x, y);
|
||||
if (cached.isNull()) {
|
||||
auto &pix = Window::chatBackground()->image();
|
||||
if (Window::chatBackground()->tile()) {
|
||||
auto &pix = Window::Theme::Background()->image();
|
||||
if (Window::Theme::Background()->tile()) {
|
||||
int left = r.left(), top = r.top(), right = r.left() + r.width(), bottom = r.top() + r.height();
|
||||
float64 w = pix.width() / cRetinaFactor(), h = pix.height() / cRetinaFactor();
|
||||
int sx = qFloor(left / w), sy = qFloor((top - fromy) / h), cx = qCeil(right / w), cy = qCeil((bottom - fromy) / h);
|
||||
|
|
|
@ -21,6 +21,28 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
using "basic.style";
|
||||
using "ui/widgets/widgets.style";
|
||||
|
||||
countryInput {
|
||||
width: pixels;
|
||||
height: pixels;
|
||||
top: pixels;
|
||||
bgColor: color;
|
||||
ptrSize: size;
|
||||
textMrg: margins;
|
||||
font: font;
|
||||
align: align;
|
||||
}
|
||||
|
||||
introCountry: countryInput {
|
||||
width: 300px;
|
||||
height: 41px;
|
||||
top: 33px;
|
||||
bgColor: #f2f2f2;
|
||||
ptrSize: size(15px, 8px);
|
||||
textMrg: margins(16px, 5px, 16px, 15px);
|
||||
font: inpDefFont;
|
||||
align: align(left);
|
||||
}
|
||||
|
||||
introErrLabel: flatLabel(labelDefFlat) {
|
||||
font: introErrFont;
|
||||
align: align(center);
|
||||
|
|
|
@ -25,6 +25,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "application.h"
|
||||
#include "intro/introsignup.h"
|
||||
#include "intro/intropwdcheck.h"
|
||||
#include "styles/style_intro.h"
|
||||
|
||||
CodeInput::CodeInput(QWidget *parent, const style::flatInput &st, const QString &ph) : FlatInput(parent, st, ph) {
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "stdafx.h"
|
||||
#include "intro/intropwdcheck.h"
|
||||
|
||||
#include "styles/style_intro.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "boxes/confirmbox.h"
|
||||
#include "lang.h"
|
||||
|
|
|
@ -21,6 +21,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "stdafx.h"
|
||||
#include "intro/introsignup.h"
|
||||
|
||||
#include "styles/style_intro.h"
|
||||
#include "ui/filedialog.h"
|
||||
#include "boxes/photocropbox.h"
|
||||
#include "lang.h"
|
||||
|
|
|
@ -34,7 +34,7 @@ IntroStart::IntroStart(IntroWidget *parent) : IntroStep(parent)
|
|||
if (cLang() == languageDefault) {
|
||||
int32 l = Sandbox::LangSystem();
|
||||
if (l != languageDefault) {
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[l].c_str() + qsl(".strings"), LangLoaderRequest(lng_switch_to_this));
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[l].c_str() + qsl(".strings"), langLoaderRequest(lng_switch_to_this));
|
||||
QString text = loader.found().value(lng_switch_to_this);
|
||||
if (!text.isEmpty()) {
|
||||
_changeLang.setText(text);
|
||||
|
|
|
@ -73,9 +73,7 @@ void IntroWidget::langChangeTo(int32 langId) {
|
|||
void IntroWidget::onChangeLang() {
|
||||
cSetLang(_langChangeTo);
|
||||
Local::writeSettings();
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(false);
|
||||
App::quit();
|
||||
App::restart();
|
||||
}
|
||||
|
||||
void IntroWidget::onParentResize(const QSize &newSize) {
|
||||
|
|
|
@ -68,7 +68,7 @@ QString langNewVersionText() {
|
|||
QString langNewVersionTextForLang(int langId) {
|
||||
LangLoaderResult result;
|
||||
if (langId) {
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), LangLoaderRequest(lng_language_name, NEW_VER_KEY));
|
||||
LangLoaderPlain loader(qsl(":/langs/lang_") + LanguageCodes[langId].c_str() + qsl(".strings"), langLoaderRequest(lng_language_name, NEW_VER_KEY));
|
||||
result = loader.found();
|
||||
} else {
|
||||
result.insert(lng_language_name, langOriginal(lng_language_name));
|
||||
|
|
|
@ -178,11 +178,11 @@ private:
|
|||
|
||||
LangLoader(const LangLoader &);
|
||||
LangLoader &operator=(const LangLoader &);
|
||||
|
||||
};
|
||||
|
||||
class Translator : public QTranslator {
|
||||
public:
|
||||
|
||||
QString translate(const char *context, const char *sourceText, const char *disambiguation = 0, int n = -1) const override;
|
||||
|
||||
};
|
||||
|
|
|
@ -21,52 +21,11 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "stdafx.h"
|
||||
#include "langloaderplain.h"
|
||||
|
||||
namespace {
|
||||
|
||||
bool skipWhitespaces(const char *&from, const char *end) {
|
||||
while (from < end && (*from == ' ' || *from == '\n' || *from == '\t' || *from == '\r')) {
|
||||
++from;
|
||||
}
|
||||
return (from < end);
|
||||
}
|
||||
|
||||
bool skipComment(const char *&from, const char *end) {
|
||||
if (from >= end) return false;
|
||||
if (*from == '/') {
|
||||
if (from + 1 >= end) return true;
|
||||
if (*(from + 1) == '*') {
|
||||
from += 2;
|
||||
while (from + 1 < end && (*from != '*' || *(from + 1) != '/')) {
|
||||
++from;
|
||||
}
|
||||
from += 2;
|
||||
return (from < end);
|
||||
} else if (*(from + 1) == '/') {
|
||||
from += 2;
|
||||
while (from < end && *from != '\n' && *from != '\r') {
|
||||
++from;
|
||||
}
|
||||
return (from < end);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool skipJunk(const char *&from, const char *end) {
|
||||
const char *start;
|
||||
do {
|
||||
start = from;
|
||||
if (!skipWhitespaces(from, end)) return false;
|
||||
if (!skipComment(from, end)) throw Exception("Unexpected end of comment!");
|
||||
} while (start != from);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#include "core/parse_helper.h"
|
||||
|
||||
bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
||||
if (!skipJunk(from, end)) return false;
|
||||
using base::parse::skipWhitespaces;
|
||||
if (!skipWhitespaces(from, end)) return false;
|
||||
|
||||
if (*from != '"') throw Exception(QString("Expected quote before key name!"));
|
||||
++from;
|
||||
|
@ -77,13 +36,13 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
|
||||
QByteArray varName = QByteArray(nameStart, from - nameStart);
|
||||
|
||||
if (*from != '"') throw Exception(QString("Expected quote after key name '%1'!").arg(QLatin1String(varName)));
|
||||
if (from == end || *from != '"') throw Exception(QString("Expected quote after key name '%1'!").arg(QLatin1String(varName)));
|
||||
++from;
|
||||
|
||||
if (!skipJunk(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (!skipWhitespaces(from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*from != '=') throw Exception(QString("'=' expected in key '%1'!").arg(QLatin1String(varName)));
|
||||
|
||||
if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*from != '"') throw Exception(QString("Expected string after '=' in key '%1'!").arg(QLatin1String(varName)));
|
||||
|
||||
LangKey varKey = keyIndex(varName);
|
||||
|
@ -231,10 +190,10 @@ bool LangLoaderPlain::readKeyValue(const char *&from, const char *end) {
|
|||
if (from >= end) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (readingValue && from > start) varValue.append(start, from - start);
|
||||
|
||||
if (!skipJunk(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (!skipWhitespaces(++from, end)) throw Exception(QString("Unexpected end of file in key '%1'!").arg(QLatin1String(varName)));
|
||||
if (*from != ';') throw Exception(QString("';' expected after \"value\" in key '%1'!").arg(QLatin1String(varName)));
|
||||
|
||||
skipJunk(++from, end);
|
||||
skipWhitespaces(++from, end);
|
||||
|
||||
if (readingValue) {
|
||||
if (feedingValue) {
|
||||
|
@ -299,9 +258,11 @@ LangLoaderPlain::LangLoaderPlain(const QString &file, const LangLoaderRequest &r
|
|||
}
|
||||
}
|
||||
|
||||
const char *text = data.constData() + skip, *end = text + data.size() - skip;
|
||||
data = base::parse::stripComments(data);
|
||||
|
||||
auto text = data.constData() + skip, end = text + data.size() - skip;
|
||||
try {
|
||||
while (true) {
|
||||
while (text != end) {
|
||||
if (!readKeyValue(text, end)) {
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -22,24 +22,32 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "lang.h"
|
||||
|
||||
class LangLoaderRequest : public QMap <LangKey, bool> {
|
||||
public:
|
||||
LangLoaderRequest() {
|
||||
}
|
||||
LangLoaderRequest(LangKey key1) {
|
||||
insert(key1, true);
|
||||
}
|
||||
LangLoaderRequest(LangKey key1, LangKey key2) {
|
||||
insert(key1, true);
|
||||
insert(key2, true);
|
||||
}
|
||||
LangLoaderRequest(LangKey key1, LangKey key2, LangKey key3) {
|
||||
insert(key1, true);
|
||||
insert(key2, true);
|
||||
insert(key3, true);
|
||||
using LangLoaderRequest = OrderedSet<LangKey>;
|
||||
|
||||
template <typename ...Args>
|
||||
struct LangLoaderRequestHelper;
|
||||
|
||||
template <>
|
||||
struct LangLoaderRequestHelper<> {
|
||||
static inline void fill(LangLoaderRequest &result) {
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Arg, typename ...Args>
|
||||
struct LangLoaderRequestHelper<Arg, Args...> {
|
||||
static inline void fill(LangLoaderRequest &result, Arg arg, Args ...args) {
|
||||
result.insert(arg);
|
||||
LangLoaderRequestHelper<Args...>::fill(result, args...);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ...Args>
|
||||
inline LangLoaderRequest langLoaderRequest(Args ...args) {
|
||||
LangLoaderRequest result;
|
||||
LangLoaderRequestHelper<Args...>::fill(result, args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
using LangLoaderResult = QMap<LangKey, LangString>;
|
||||
class LangLoaderPlain : public LangLoader {
|
||||
public:
|
||||
|
|
|
@ -26,7 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "serialize/serialize_document.h"
|
||||
#include "serialize/serialize_common.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "window/chat_background.h"
|
||||
#include "window/window_theme.h"
|
||||
#include "observer_peer.h"
|
||||
#include "mainwidget.h"
|
||||
#include "mainwindow.h"
|
||||
|
@ -38,10 +38,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
namespace Local {
|
||||
namespace {
|
||||
|
||||
constexpr int kThemeFileSizeLimit = 5 * 1024 * 1024;
|
||||
|
||||
using FileKey = quint64;
|
||||
|
||||
static const char tdfMagic[] = { 'T', 'D', 'F', '$' };
|
||||
static const int32 tdfMagicLen = sizeof(tdfMagic);
|
||||
constexpr char tdfMagic[] = { 'T', 'D', 'F', '$' };
|
||||
constexpr int tdfMagicLen = sizeof(tdfMagic);
|
||||
|
||||
QString toFilePart(FileKey val) {
|
||||
QString result;
|
||||
|
@ -68,30 +70,32 @@ bool _userWorking() {
|
|||
return _manager && !_basePath.isEmpty() && !_userBasePath.isEmpty();
|
||||
}
|
||||
|
||||
enum FileOptions {
|
||||
UserPath = 0x01,
|
||||
SafePath = 0x02,
|
||||
enum class FileOption {
|
||||
User = 0x01,
|
||||
Safe = 0x02,
|
||||
};
|
||||
Q_DECLARE_FLAGS(FileOptions, FileOption);
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(FileOptions);
|
||||
|
||||
bool keyAlreadyUsed(QString &name, int options = UserPath | SafePath) {
|
||||
bool keyAlreadyUsed(QString &name, FileOptions options = FileOption::User | FileOption::Safe) {
|
||||
name += '0';
|
||||
if (QFileInfo(name).exists()) return true;
|
||||
if (options & SafePath) {
|
||||
if (options & (FileOption::Safe)) {
|
||||
name[name.size() - 1] = '1';
|
||||
return QFileInfo(name).exists();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FileKey genKey(int options = UserPath | SafePath) {
|
||||
if (options & UserPath) {
|
||||
FileKey genKey(FileOptions options = FileOption::User | FileOption::Safe) {
|
||||
if (options & FileOption::User) {
|
||||
if (!_userWorking()) return 0;
|
||||
} else {
|
||||
if (!_working()) return 0;
|
||||
}
|
||||
|
||||
FileKey result;
|
||||
QString base = (options & UserPath) ? _userBasePath : _basePath, path;
|
||||
QString base = (options & FileOption::User) ? _userBasePath : _basePath, path;
|
||||
path.reserve(base.size() + 0x11);
|
||||
path += base;
|
||||
do {
|
||||
|
@ -103,18 +107,18 @@ FileKey genKey(int options = UserPath | SafePath) {
|
|||
return result;
|
||||
}
|
||||
|
||||
void clearKey(const FileKey &key, int options = UserPath | SafePath) {
|
||||
if (options & UserPath) {
|
||||
void clearKey(const FileKey &key, FileOptions options = FileOption::User | FileOption::Safe) {
|
||||
if (options & FileOption::User) {
|
||||
if (!_userWorking()) return;
|
||||
} else {
|
||||
if (!_working()) return;
|
||||
}
|
||||
|
||||
QString base = (options & UserPath) ? _userBasePath : _basePath, name;
|
||||
QString base = (options & FileOption::User) ? _userBasePath : _basePath, name;
|
||||
name.reserve(base.size() + 0x11);
|
||||
name.append(base).append(toFilePart(key)).append('0');
|
||||
QFile::remove(name);
|
||||
if (options & SafePath) {
|
||||
if (options & FileOption::Safe) {
|
||||
name[name.size() - 1] = '1';
|
||||
QFile::remove(name);
|
||||
}
|
||||
|
@ -193,14 +197,14 @@ struct EncryptedDescriptor {
|
|||
};
|
||||
|
||||
struct FileWriteDescriptor {
|
||||
FileWriteDescriptor(const FileKey &key, int options = UserPath | SafePath) : dataSize(0) {
|
||||
FileWriteDescriptor(const FileKey &key, FileOptions options = FileOption::User | FileOption::Safe) {
|
||||
init(toFilePart(key), options);
|
||||
}
|
||||
FileWriteDescriptor(const QString &name, int options = UserPath | SafePath) : dataSize(0) {
|
||||
FileWriteDescriptor(const QString &name, FileOptions options = FileOption::User | FileOption::Safe) {
|
||||
init(name, options);
|
||||
}
|
||||
void init(const QString &name, int options) {
|
||||
if (options & UserPath) {
|
||||
void init(const QString &name, FileOptions options) {
|
||||
if (options & FileOption::User) {
|
||||
if (!_userWorking()) return;
|
||||
} else {
|
||||
if (!_working()) return;
|
||||
|
@ -208,9 +212,9 @@ struct FileWriteDescriptor {
|
|||
|
||||
// detect order of read attempts and file version
|
||||
QString toTry[2];
|
||||
toTry[0] = ((options & UserPath) ? _userBasePath : _basePath) + name + '0';
|
||||
if (options & SafePath) {
|
||||
toTry[1] = ((options & UserPath) ? _userBasePath : _basePath) + name + '1';
|
||||
toTry[0] = ((options & FileOption::User) ? _userBasePath : _basePath) + name + '0';
|
||||
if (options & FileOption::Safe) {
|
||||
toTry[1] = ((options & FileOption::User) ? _userBasePath : _basePath) + name + '1';
|
||||
QFileInfo toTry0(toTry[0]);
|
||||
QFileInfo toTry1(toTry[1]);
|
||||
if (toTry0.exists()) {
|
||||
|
@ -295,15 +299,15 @@ struct FileWriteDescriptor {
|
|||
QString toDelete;
|
||||
|
||||
HashMd5 md5;
|
||||
int32 dataSize;
|
||||
int32 dataSize = 0;
|
||||
|
||||
~FileWriteDescriptor() {
|
||||
finish();
|
||||
}
|
||||
};
|
||||
|
||||
bool readFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath) {
|
||||
if (options & UserPath) {
|
||||
bool readFile(FileReadDescriptor &result, const QString &name, FileOptions options = FileOption::User | FileOption::Safe) {
|
||||
if (options & FileOption::User) {
|
||||
if (!_userWorking()) return false;
|
||||
} else {
|
||||
if (!_working()) return false;
|
||||
|
@ -311,11 +315,11 @@ bool readFile(FileReadDescriptor &result, const QString &name, int options = Use
|
|||
|
||||
// detect order of read attempts
|
||||
QString toTry[2];
|
||||
toTry[0] = ((options & UserPath) ? _userBasePath : _basePath) + name + '0';
|
||||
if (options & SafePath) {
|
||||
toTry[0] = ((options & FileOption::User) ? _userBasePath : _basePath) + name + '0';
|
||||
if (options & FileOption::Safe) {
|
||||
QFileInfo toTry0(toTry[0]);
|
||||
if (toTry0.exists()) {
|
||||
toTry[1] = ((options & UserPath) ? _userBasePath : _basePath) + name + '1';
|
||||
toTry[1] = ((options & FileOption::User) ? _userBasePath : _basePath) + name + '1';
|
||||
QFileInfo toTry1(toTry[1]);
|
||||
if (toTry1.exists()) {
|
||||
QDateTime mod0 = toTry0.lastModified(), mod1 = toTry1.lastModified();
|
||||
|
@ -435,7 +439,7 @@ bool decryptLocal(EncryptedDescriptor &result, const QByteArray &encrypted, cons
|
|||
return true;
|
||||
}
|
||||
|
||||
bool readEncryptedFile(FileReadDescriptor &result, const QString &name, int options = UserPath | SafePath, const MTP::AuthKey &key = _localKey) {
|
||||
bool readEncryptedFile(FileReadDescriptor &result, const QString &name, FileOptions options = FileOption::User | FileOption::Safe, const MTP::AuthKey &key = _localKey) {
|
||||
if (!readFile(result, name, options)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -465,7 +469,7 @@ bool readEncryptedFile(FileReadDescriptor &result, const QString &name, int opti
|
|||
return true;
|
||||
}
|
||||
|
||||
bool readEncryptedFile(FileReadDescriptor &result, const FileKey &fkey, int options = UserPath | SafePath, const MTP::AuthKey &key = _localKey) {
|
||||
bool readEncryptedFile(FileReadDescriptor &result, const FileKey &fkey, FileOptions options = FileOption::User | FileOption::Safe, const MTP::AuthKey &key = _localKey) {
|
||||
return readEncryptedFile(result, toFilePart(fkey), options, key);
|
||||
}
|
||||
|
||||
|
@ -552,6 +556,7 @@ enum {
|
|||
dbiNativeNotifications = 0x44,
|
||||
dbiNotificationsCount = 0x45,
|
||||
dbiNotificationsCorner = 0x46,
|
||||
dbiTheme = 0x47,
|
||||
|
||||
dbiEncryptedWithSalt = 333,
|
||||
dbiEncrypted = 444,
|
||||
|
@ -591,6 +596,9 @@ FileKey _savedGifsKey = 0;
|
|||
|
||||
FileKey _backgroundKey = 0;
|
||||
bool _backgroundWasRead = false;
|
||||
bool _backgroundCanWrite = true;
|
||||
|
||||
FileKey _themeKey = 0, _applyingThemeKey = 0;
|
||||
|
||||
bool _readingUserSettings = false;
|
||||
FileKey _userSettingsKey = 0;
|
||||
|
@ -1057,6 +1065,15 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) {
|
|||
};
|
||||
} break;
|
||||
|
||||
case dbiTheme: {
|
||||
quint64 themeKey = 0, applyingThemeKey = 0;
|
||||
stream >> themeKey >> applyingThemeKey;
|
||||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
_themeKey = themeKey;
|
||||
_applyingThemeKey = applyingThemeKey;
|
||||
} break;
|
||||
|
||||
case dbiTryIPv6: {
|
||||
qint32 v;
|
||||
stream >> v;
|
||||
|
@ -1185,7 +1202,7 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version) {
|
|||
if (!_checkStreamStatus(stream)) return false;
|
||||
|
||||
bool tile = (version < 8005 && !_backgroundKey) ? false : (v == 1);
|
||||
Window::chatBackground()->setTile(tile);
|
||||
Window::Theme::Background()->setTile(tile);
|
||||
} break;
|
||||
|
||||
case dbiAdaptiveForWide: {
|
||||
|
@ -1610,7 +1627,7 @@ void _writeUserSettings() {
|
|||
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << quint32(dbiSendKey) << qint32(cCtrlEnter() ? dbiskCtrlEnter : dbiskEnter);
|
||||
data.stream << quint32(dbiTileBackground) << qint32(Window::chatBackground()->tile() ? 1 : 0);
|
||||
data.stream << quint32(dbiTileBackground) << qint32(Window::Theme::Background()->tile() ? 1 : 0);
|
||||
data.stream << quint32(dbiAdaptiveForWide) << qint32(Global::AdaptiveForWide() ? 1 : 0);
|
||||
data.stream << quint32(dbiAutoLock) << qint32(Global::AutoLock());
|
||||
data.stream << quint32(dbiReplaceEmojis) << qint32(cReplaceEmojis() ? 1 : 0);
|
||||
|
@ -1691,7 +1708,7 @@ void _readUserSettings() {
|
|||
}
|
||||
|
||||
void _writeMtpData() {
|
||||
FileWriteDescriptor mtp(toFilePart(_dataNameKey), SafePath);
|
||||
FileWriteDescriptor mtp(toFilePart(_dataNameKey), FileOption::Safe);
|
||||
if (!_localKey.created()) {
|
||||
LOG(("App Error: localkey not created in _writeMtpData()"));
|
||||
return;
|
||||
|
@ -1714,7 +1731,7 @@ void _writeMtpData() {
|
|||
|
||||
void _readMtpData() {
|
||||
FileReadDescriptor mtp;
|
||||
if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), SafePath)) {
|
||||
if (!readEncryptedFile(mtp, toFilePart(_dataNameKey), FileOption::Safe)) {
|
||||
if (_localKey.created()) {
|
||||
_readOldMtpData();
|
||||
_writeMtpData();
|
||||
|
@ -2074,11 +2091,12 @@ void finish() {
|
|||
_manager->finish();
|
||||
_manager->deleteLater();
|
||||
_manager = 0;
|
||||
delete _localLoader;
|
||||
_localLoader = 0;
|
||||
delete base::take(_localLoader);
|
||||
}
|
||||
}
|
||||
|
||||
void readTheme();
|
||||
|
||||
void start() {
|
||||
t_assert(_manager == 0);
|
||||
|
||||
|
@ -2089,7 +2107,7 @@ void start() {
|
|||
if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
|
||||
|
||||
FileReadDescriptor settingsData;
|
||||
if (!readFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), SafePath)) {
|
||||
if (!readFile(settingsData, cTestMode() ? qsl("settings_test") : qsl("settings"), FileOption::Safe)) {
|
||||
_readOldSettings();
|
||||
_readOldUserSettings(false); // needed further in _readUserSettings
|
||||
_readOldMtpData(false); // needed further in _readMtpData
|
||||
|
@ -2156,6 +2174,8 @@ void start() {
|
|||
|
||||
_oldSettingsVersion = settingsData.version;
|
||||
_settingsSalt = salt;
|
||||
|
||||
readTheme();
|
||||
}
|
||||
|
||||
void writeSettings() {
|
||||
|
@ -2166,7 +2186,7 @@ void writeSettings() {
|
|||
|
||||
if (!QDir().exists(_basePath)) QDir().mkpath(_basePath);
|
||||
|
||||
FileWriteDescriptor settings(cTestMode() ? qsl("settings_test") : qsl("settings"), SafePath);
|
||||
FileWriteDescriptor settings(cTestMode() ? qsl("settings_test") : qsl("settings"), FileOption::Safe);
|
||||
if (_settingsSalt.isEmpty() || !_settingsKey.created()) {
|
||||
_settingsSalt.resize(LocalEncryptSaltSize);
|
||||
memset_rand(_settingsSalt.data(), _settingsSalt.size());
|
||||
|
@ -2212,7 +2232,9 @@ void writeSettings() {
|
|||
auto &proxy = Global::ConnectionProxy();
|
||||
size += Serialize::stringSize(proxy.host) + sizeof(qint32) + Serialize::stringSize(proxy.user) + Serialize::stringSize(proxy.password);
|
||||
}
|
||||
|
||||
if (_themeKey || _applyingThemeKey) {
|
||||
size += sizeof(quint32) + 2 * sizeof(quint64);
|
||||
}
|
||||
size += sizeof(quint32) + sizeof(qint32) * 7;
|
||||
|
||||
EncryptedDescriptor data(size);
|
||||
|
@ -2242,6 +2264,9 @@ void writeSettings() {
|
|||
data.stream << proxy.host << qint32(proxy.port) << proxy.user << proxy.password;
|
||||
}
|
||||
data.stream << quint32(dbiTryIPv6) << qint32(Global::TryIPv6());
|
||||
if (_themeKey || _applyingThemeKey) {
|
||||
data.stream << quint32(dbiTheme) << quint64(_themeKey) << quint64(_applyingThemeKey);
|
||||
}
|
||||
|
||||
TWindowPos pos(cWindowPos());
|
||||
data.stream << quint32(dbiWindowPosition) << qint32(pos.x) << qint32(pos.y) << qint32(pos.w) << qint32(pos.h) << qint32(pos.moncrc) << qint32(pos.maximized);
|
||||
|
@ -2604,7 +2629,7 @@ void writeImage(const StorageKey &location, const StorageImageSaved &image, bool
|
|||
qint32 size = _storageImageSize(image.data.size());
|
||||
StorageMap::const_iterator i = _imagesMap.constFind(location);
|
||||
if (i == _imagesMap.cend()) {
|
||||
i = _imagesMap.insert(location, FileDesc(genKey(UserPath), size));
|
||||
i = _imagesMap.insert(location, FileDesc(genKey(FileOption::User), size));
|
||||
_storageImagesSize += size;
|
||||
_mapChanged = true;
|
||||
_writeMap();
|
||||
|
@ -2613,7 +2638,7 @@ void writeImage(const StorageKey &location, const StorageImageSaved &image, bool
|
|||
}
|
||||
EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + image.data.size());
|
||||
data.stream << quint64(location.first) << quint64(location.second) << quint32(image.type) << image.data;
|
||||
FileWriteDescriptor file(i.value().first, UserPath);
|
||||
FileWriteDescriptor file(i.value().first, FileOption::User);
|
||||
file.writeEncrypted(data);
|
||||
if (i.value().second != size) {
|
||||
_storageImagesSize += size;
|
||||
|
@ -2630,7 +2655,7 @@ public:
|
|||
}
|
||||
void process() {
|
||||
FileReadDescriptor image;
|
||||
if (!readEncryptedFile(image, _key, UserPath)) {
|
||||
if (!readEncryptedFile(image, _key, FileOption::User)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2701,7 +2726,7 @@ public:
|
|||
void clearInMap() {
|
||||
StorageMap::iterator j = _imagesMap.find(_location);
|
||||
if (j != _imagesMap.cend() && j->first == _key) {
|
||||
clearKey(_key, UserPath);
|
||||
clearKey(_key, FileOption::User);
|
||||
_storageImagesSize -= j->second;
|
||||
_imagesMap.erase(j);
|
||||
}
|
||||
|
@ -2730,7 +2755,7 @@ void writeStickerImage(const StorageKey &location, const QByteArray &sticker, bo
|
|||
qint32 size = _storageStickerSize(sticker.size());
|
||||
StorageMap::const_iterator i = _stickerImagesMap.constFind(location);
|
||||
if (i == _stickerImagesMap.cend()) {
|
||||
i = _stickerImagesMap.insert(location, FileDesc(genKey(UserPath), size));
|
||||
i = _stickerImagesMap.insert(location, FileDesc(genKey(FileOption::User), size));
|
||||
_storageStickersSize += size;
|
||||
_mapChanged = true;
|
||||
_writeMap();
|
||||
|
@ -2739,7 +2764,7 @@ void writeStickerImage(const StorageKey &location, const QByteArray &sticker, bo
|
|||
}
|
||||
EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + sticker.size());
|
||||
data.stream << quint64(location.first) << quint64(location.second) << sticker;
|
||||
FileWriteDescriptor file(i.value().first, UserPath);
|
||||
FileWriteDescriptor file(i.value().first, FileOption::User);
|
||||
file.writeEncrypted(data);
|
||||
if (i.value().second != size) {
|
||||
_storageStickersSize += size;
|
||||
|
@ -2760,7 +2785,7 @@ public:
|
|||
void clearInMap() {
|
||||
auto j = _stickerImagesMap.find(_location);
|
||||
if (j != _stickerImagesMap.cend() && j->first == _key) {
|
||||
clearKey(j.value().first, UserPath);
|
||||
clearKey(j.value().first, FileOption::User);
|
||||
_storageStickersSize -= j.value().second;
|
||||
_stickerImagesMap.erase(j);
|
||||
}
|
||||
|
@ -2804,7 +2829,7 @@ void writeAudio(const StorageKey &location, const QByteArray &audio, bool overwr
|
|||
qint32 size = _storageAudioSize(audio.size());
|
||||
StorageMap::const_iterator i = _audiosMap.constFind(location);
|
||||
if (i == _audiosMap.cend()) {
|
||||
i = _audiosMap.insert(location, FileDesc(genKey(UserPath), size));
|
||||
i = _audiosMap.insert(location, FileDesc(genKey(FileOption::User), size));
|
||||
_storageAudiosSize += size;
|
||||
_mapChanged = true;
|
||||
_writeMap();
|
||||
|
@ -2813,7 +2838,7 @@ void writeAudio(const StorageKey &location, const QByteArray &audio, bool overwr
|
|||
}
|
||||
EncryptedDescriptor data(sizeof(quint64) * 2 + sizeof(quint32) + sizeof(quint32) + audio.size());
|
||||
data.stream << quint64(location.first) << quint64(location.second) << audio;
|
||||
FileWriteDescriptor file(i.value().first, UserPath);
|
||||
FileWriteDescriptor file(i.value().first, FileOption::User);
|
||||
file.writeEncrypted(data);
|
||||
if (i.value().second != size) {
|
||||
_storageAudiosSize += size;
|
||||
|
@ -2834,7 +2859,7 @@ public:
|
|||
void clearInMap() {
|
||||
auto j = _audiosMap.find(_location);
|
||||
if (j != _audiosMap.cend() && j->first == _key) {
|
||||
clearKey(j.value().first, UserPath);
|
||||
clearKey(j.value().first, FileOption::User);
|
||||
_storageAudiosSize -= j.value().second;
|
||||
_audiosMap.erase(j);
|
||||
}
|
||||
|
@ -2882,7 +2907,7 @@ void writeWebFile(const QString &url, const QByteArray &content, bool overwrite)
|
|||
qint32 size = _storageWebFileSize(url, content.size());
|
||||
WebFilesMap::const_iterator i = _webFilesMap.constFind(url);
|
||||
if (i == _webFilesMap.cend()) {
|
||||
i = _webFilesMap.insert(url, FileDesc(genKey(UserPath), size));
|
||||
i = _webFilesMap.insert(url, FileDesc(genKey(FileOption::User), size));
|
||||
_storageWebFilesSize += size;
|
||||
_writeLocations();
|
||||
} else if (!overwrite) {
|
||||
|
@ -2890,7 +2915,7 @@ void writeWebFile(const QString &url, const QByteArray &content, bool overwrite)
|
|||
}
|
||||
EncryptedDescriptor data(Serialize::stringSize(url) + sizeof(quint32) + sizeof(quint32) + content.size());
|
||||
data.stream << url << content;
|
||||
FileWriteDescriptor file(i.value().first, UserPath);
|
||||
FileWriteDescriptor file(i.value().first, FileOption::User);
|
||||
file.writeEncrypted(data);
|
||||
if (i.value().second != size) {
|
||||
_storageWebFilesSize += size;
|
||||
|
@ -2909,7 +2934,7 @@ public:
|
|||
}
|
||||
void process() {
|
||||
FileReadDescriptor image;
|
||||
if (!readEncryptedFile(image, _key, UserPath)) {
|
||||
if (!readEncryptedFile(image, _key, FileOption::User)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2925,7 +2950,7 @@ public:
|
|||
} else {
|
||||
WebFilesMap::iterator j = _webFilesMap.find(_url);
|
||||
if (j != _webFilesMap.cend() && j->first == _key) {
|
||||
clearKey(j.value().first, UserPath);
|
||||
clearKey(j.value().first, FileOption::User);
|
||||
_storageWebFilesSize -= j.value().second;
|
||||
_webFilesMap.erase(j);
|
||||
}
|
||||
|
@ -3592,11 +3617,11 @@ void readSavedGifs() {
|
|||
}
|
||||
|
||||
void writeBackground(int32 id, const QImage &img) {
|
||||
if (!_working()) return;
|
||||
if (!_working() || !_backgroundCanWrite) return;
|
||||
|
||||
QByteArray png;
|
||||
QByteArray bmp;
|
||||
if (!img.isNull()) {
|
||||
QBuffer buf(&png);
|
||||
QBuffer buf(&bmp);
|
||||
if (!img.save(&buf, "BMP")) return;
|
||||
}
|
||||
if (!_backgroundKey) {
|
||||
|
@ -3604,17 +3629,19 @@ void writeBackground(int32 id, const QImage &img) {
|
|||
_mapChanged = true;
|
||||
_writeMap(WriteMapFast);
|
||||
}
|
||||
quint32 size = sizeof(qint32) + sizeof(quint32) + (png.isEmpty() ? 0 : (sizeof(quint32) + png.size()));
|
||||
quint32 size = sizeof(qint32) + sizeof(quint32) + (bmp.isEmpty() ? 0 : (sizeof(quint32) + bmp.size()));
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << qint32(id);
|
||||
if (!png.isEmpty()) data.stream << png;
|
||||
if (!bmp.isEmpty()) data.stream << bmp;
|
||||
|
||||
FileWriteDescriptor file(_backgroundKey);
|
||||
file.writeEncrypted(data);
|
||||
}
|
||||
|
||||
bool readBackground() {
|
||||
if (_backgroundWasRead) return false;
|
||||
if (_backgroundWasRead || Global::ApplyingTheme()) {
|
||||
return false;
|
||||
}
|
||||
_backgroundWasRead = true;
|
||||
|
||||
FileReadDescriptor bg;
|
||||
|
@ -3628,30 +3655,116 @@ bool readBackground() {
|
|||
QByteArray pngData;
|
||||
qint32 id;
|
||||
bg.stream >> id;
|
||||
if (!id || id == DefaultChatBackground) {
|
||||
if (id == Window::Theme::kOldBackground || id == Window::Theme::kDefaultBackground) {
|
||||
_backgroundCanWrite = false;
|
||||
if (bg.version < 8005) {
|
||||
App::initBackground(DefaultChatBackground, QImage(), true);
|
||||
if (!id) Window::chatBackground()->setTile(!DefaultChatBackground);
|
||||
Window::Theme::Background()->setImage(Window::Theme::kDefaultBackground);
|
||||
if (id == Window::Theme::kOldBackground) {
|
||||
Window::Theme::Background()->setTile(false);
|
||||
}
|
||||
} else {
|
||||
App::initBackground(id, QImage(), true);
|
||||
Window::Theme::Background()->setImage(id);
|
||||
}
|
||||
_backgroundCanWrite = true;
|
||||
return true;
|
||||
}
|
||||
bg.stream >> pngData;
|
||||
|
||||
QImage img;
|
||||
QImage image;
|
||||
QBuffer buf(&pngData);
|
||||
QImageReader reader(&buf);
|
||||
#ifndef OS_MAC_OLD
|
||||
reader.setAutoTransform(true);
|
||||
#endif // OS_MAC_OLD
|
||||
if (reader.read(&img)) {
|
||||
App::initBackground(id, img, true);
|
||||
if (reader.read(&image)) {
|
||||
_backgroundCanWrite = false;
|
||||
Window::Theme::Background()->setImage(id, std_::move(image));
|
||||
_backgroundCanWrite = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool readThemeUsingKey(FileKey key) {
|
||||
FileReadDescriptor theme;
|
||||
if (!readEncryptedFile(theme, key, FileOption::Safe, _settingsKey)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QByteArray themeContent;
|
||||
QString pathRelative, pathAbsolute;
|
||||
Window::Theme::Cached cache;
|
||||
theme.stream >> themeContent;
|
||||
theme.stream >> pathRelative >> pathAbsolute;
|
||||
if (theme.stream.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
QFile file(pathRelative);
|
||||
if (pathRelative.isEmpty() || !file.exists()) {
|
||||
file.setFileName(pathAbsolute);
|
||||
}
|
||||
|
||||
auto changed = false;
|
||||
if (!file.fileName().isEmpty() && file.exists() && file.open(QIODevice::ReadOnly)) {
|
||||
if (file.size() > kThemeFileSizeLimit) {
|
||||
LOG(("Error: theme file too large: %1 (should be less than 5 MB, got %2)").arg(file.fileName()).arg(file.size()));
|
||||
return false;
|
||||
}
|
||||
auto fileContent = file.readAll();
|
||||
file.close();
|
||||
if (themeContent != fileContent) {
|
||||
themeContent = fileContent;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (!changed) {
|
||||
quint32 backgroundIsTiled = 0;
|
||||
theme.stream >> cache.paletteChecksum >> cache.contentChecksum >> cache.colors >> cache.background >> backgroundIsTiled;
|
||||
cache.tiled = (backgroundIsTiled == 1);
|
||||
if (theme.stream.status() != QDataStream::Ok) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return Window::Theme::Load(pathRelative, pathAbsolute, themeContent, cache);
|
||||
}
|
||||
|
||||
void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, const Window::Theme::Cached &cache) {
|
||||
auto key = _applyingThemeKey ? _applyingThemeKey : _themeKey;
|
||||
if (!key) {
|
||||
key = _themeKey = genKey();
|
||||
writeSettings();
|
||||
}
|
||||
|
||||
auto backgroundTiled = static_cast<quint32>(cache.tiled ? 1 : 0);
|
||||
quint32 size = Serialize::bytearraySize(content);
|
||||
size += Serialize::stringSize(pathRelative) + Serialize::stringSize(pathAbsolute);
|
||||
size += sizeof(int32) * 2 + Serialize::bytearraySize(cache.colors) + Serialize::bytearraySize(cache.background) + sizeof(quint32);
|
||||
EncryptedDescriptor data(size);
|
||||
data.stream << content;
|
||||
data.stream << pathRelative << pathAbsolute;
|
||||
data.stream << cache.paletteChecksum << cache.contentChecksum << cache.colors << cache.background << backgroundTiled;
|
||||
|
||||
FileWriteDescriptor file(key, FileOption::Safe);
|
||||
file.writeEncrypted(data, _settingsKey);
|
||||
}
|
||||
|
||||
void readTheme() {
|
||||
if (_applyingThemeKey) {
|
||||
if (readThemeUsingKey(_applyingThemeKey)) {
|
||||
Global::SetApplyingTheme(true);
|
||||
} else {
|
||||
clearKey(_applyingThemeKey);
|
||||
_applyingThemeKey = 0;
|
||||
writeSettings();
|
||||
readTheme();
|
||||
}
|
||||
} else if (_themeKey && !readThemeUsingKey(_themeKey)) {
|
||||
clearKey(_themeKey);
|
||||
_themeKey = 0;
|
||||
writeSettings();
|
||||
}
|
||||
}
|
||||
|
||||
uint32 _peerSize(PeerData *peer) {
|
||||
uint32 result = sizeof(quint64) + sizeof(quint64) + Serialize::storageImageLocationSize();
|
||||
if (peer->isUser()) {
|
||||
|
@ -4330,16 +4443,16 @@ void ClearManager::onStart() {
|
|||
break;
|
||||
case ClearManagerStorage:
|
||||
for (StorageMap::const_iterator i = images.cbegin(), e = images.cend(); i != e; ++i) {
|
||||
clearKey(i.value().first, UserPath);
|
||||
clearKey(i.value().first, FileOption::User);
|
||||
}
|
||||
for (StorageMap::const_iterator i = stickers.cbegin(), e = stickers.cend(); i != e; ++i) {
|
||||
clearKey(i.value().first, UserPath);
|
||||
clearKey(i.value().first, FileOption::User);
|
||||
}
|
||||
for (StorageMap::const_iterator i = audios.cbegin(), e = audios.cend(); i != e; ++i) {
|
||||
clearKey(i.value().first, UserPath);
|
||||
clearKey(i.value().first, FileOption::User);
|
||||
}
|
||||
for (WebFilesMap::const_iterator i = webFiles.cbegin(), e = webFiles.cend(); i != e; ++i) {
|
||||
clearKey(i.value().first, UserPath);
|
||||
clearKey(i.value().first, FileOption::User);
|
||||
}
|
||||
result = true;
|
||||
break;
|
||||
|
|
|
@ -22,6 +22,12 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
|
||||
#include "core/basic_types.h"
|
||||
|
||||
namespace Window {
|
||||
namespace Theme {
|
||||
struct Cached;
|
||||
} // namespace Theme
|
||||
} // namespace Window
|
||||
|
||||
namespace Local {
|
||||
|
||||
void start();
|
||||
|
@ -145,6 +151,8 @@ int32 countSavedGifsHash();
|
|||
void writeBackground(int32 id, const QImage &img);
|
||||
bool readBackground();
|
||||
|
||||
void writeTheme(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, const Window::Theme::Cached &cache);
|
||||
|
||||
void writeRecentHashtagsAndBots();
|
||||
void readRecentHashtagsAndBots();
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "media/player/media_player_instance.h"
|
||||
#include "core/qthelp_regex.h"
|
||||
#include "core/qthelp_url.h"
|
||||
#include "window/chat_background.h"
|
||||
#include "window/window_theme.h"
|
||||
#include "window/player_wrap_widget.h"
|
||||
|
||||
StackItemSection::StackItemSection(std_::unique_ptr<Window::SectionMemento> &&memento) : StackItem(nullptr)
|
||||
|
@ -115,8 +115,8 @@ MainWidget::MainWidget(MainWindow *window) : TWidget(window)
|
|||
_webPageOrGameUpdater.setSingleShot(true);
|
||||
connect(&_webPageOrGameUpdater, SIGNAL(timeout()), this, SLOT(webPagesOrGamesUpdate()));
|
||||
|
||||
subscribe(Window::chatBackground(), [this](const Window::ChatBackgroundUpdate &update) {
|
||||
using Update = Window::ChatBackgroundUpdate;
|
||||
using Update = Window::Theme::BackgroundUpdate;
|
||||
subscribe(Window::Theme::Background(), [this](const Update &update) {
|
||||
if (update.type == Update::Type::New || update.type == Update::Type::Changed) {
|
||||
clearCachedBackground();
|
||||
}
|
||||
|
@ -1068,8 +1068,8 @@ bool MainWidget::sendMessageFail(const RPCError &error) {
|
|||
}
|
||||
|
||||
void MainWidget::onCacheBackground() {
|
||||
auto &bg = Window::chatBackground()->image();
|
||||
if (Window::chatBackground()->tile()) {
|
||||
auto &bg = Window::Theme::Background()->image();
|
||||
if (Window::Theme::Background()->tile()) {
|
||||
QImage result(_willCacheFor.width() * cIntRetinaFactor(), _willCacheFor.height() * cIntRetinaFactor(), QImage::Format_RGB32);
|
||||
result.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
|
@ -1879,7 +1879,7 @@ QPixmap MainWidget::cachedBackground(const QRect &forRect, int &x, int &y) {
|
|||
}
|
||||
|
||||
void MainWidget::backgroundParams(const QRect &forRect, QRect &to, QRect &from) const {
|
||||
auto bg = Window::chatBackground()->image().size();
|
||||
auto bg = Window::Theme::Background()->image().size();
|
||||
if (uint64(bg.width()) * forRect.height() > uint64(bg.height()) * forRect.width()) {
|
||||
float64 pxsize = forRect.height() / float64(bg.height());
|
||||
int takewidth = qCeil(forRect.width() / pxsize);
|
||||
|
@ -1929,11 +1929,11 @@ void MainWidget::checkChatBackground() {
|
|||
if (_background) {
|
||||
if (_background->full->loaded()) {
|
||||
if (_background->full->isNull()) {
|
||||
App::initBackground();
|
||||
} else if (_background->id == 0 || _background->id == DefaultChatBackground) {
|
||||
App::initBackground(_background->id);
|
||||
Window::Theme::Background()->setImage(Window::Theme::kDefaultBackground);
|
||||
} else if (_background->id == Window::Theme::kOldBackground || _background->id == Window::Theme::kDefaultBackground) {
|
||||
Window::Theme::Background()->setImage(_background->id);
|
||||
} else {
|
||||
App::initBackground(_background->id, _background->full->pix().toImage());
|
||||
Window::Theme::Background()->setImage(_background->id, _background->full->pix().toImage());
|
||||
}
|
||||
_background = nullptr;
|
||||
QTimer::singleShot(0, this, SLOT(update()));
|
||||
|
|
|
@ -24,7 +24,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "dialogs/dialogs_layout.h"
|
||||
#include "styles/style_dialogs.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "zip.h"
|
||||
#include "core/zlib_help.h"
|
||||
#include "lang.h"
|
||||
#include "shortcuts.h"
|
||||
#include "application.h"
|
||||
|
@ -1396,7 +1396,7 @@ QImage MainWindow::iconLarge() const {
|
|||
return iconbig256;
|
||||
}
|
||||
|
||||
void MainWindow::placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) {
|
||||
void MainWindow::placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) {
|
||||
QPainter p(&img);
|
||||
|
||||
QString cnt = (count < 100) ? QString("%1").arg(count) : QString("..%1").arg(count % 10, 1, 10, QChar('0'));
|
||||
|
@ -1434,7 +1434,7 @@ void MainWindow::placeSmallCounter(QImage &img, int size, int count, style::colo
|
|||
|
||||
}
|
||||
|
||||
QImage MainWindow::iconWithCounter(int size, int count, style::color bg, bool smallIcon) {
|
||||
QImage MainWindow::iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) {
|
||||
bool layer = false;
|
||||
if (size < 0) {
|
||||
size = -size;
|
||||
|
@ -1449,7 +1449,7 @@ QImage MainWindow::iconWithCounter(int size, int count, style::color bg, bool sm
|
|||
result.fill(st::transparent->c);
|
||||
{
|
||||
QPainter p(&result);
|
||||
p.setBrush(bg->b);
|
||||
p.setBrush(bg);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setRenderHint(QPainter::Antialiasing);
|
||||
int32 fontSize;
|
||||
|
@ -1478,9 +1478,9 @@ QImage MainWindow::iconWithCounter(int size, int count, style::color bg, bool sm
|
|||
r = (cntSize < 2) ? 16 : ((cntSize < 3) ? 14 : 8);
|
||||
}
|
||||
p.drawRoundedRect(QRect(size - w - d * 2, size - f->height, w + d * 2, f->height), r, r);
|
||||
p.setFont(f->f);
|
||||
p.setFont(f);
|
||||
|
||||
p.setPen(st::counterColor->p);
|
||||
p.setPen(st::counterFg);
|
||||
|
||||
p.drawText(size - w - d, size - f->height + f->ascent, cnt);
|
||||
}
|
||||
|
@ -1493,7 +1493,7 @@ QImage MainWindow::iconWithCounter(int size, int count, style::color bg, bool sm
|
|||
if (!count) return img;
|
||||
|
||||
if (smallIcon) {
|
||||
placeSmallCounter(img, size, count, bg, QPoint(), st::counterColor);
|
||||
placeSmallCounter(img, size, count, bg, QPoint(), st::counterFg);
|
||||
} else {
|
||||
QPainter p(&img);
|
||||
p.drawPixmap(size / 2, size / 2, App::pixmapFromImageInPlace(iconWithCounter(-size / 2, count, bg, false)));
|
||||
|
@ -1997,91 +1997,6 @@ void LastCrashedWindow::onSendReport() {
|
|||
updateControls();
|
||||
}
|
||||
|
||||
namespace {
|
||||
struct zByteArray {
|
||||
zByteArray() : pos(0), err(0) {
|
||||
}
|
||||
uLong pos;
|
||||
int err;
|
||||
QByteArray data;
|
||||
};
|
||||
|
||||
voidpf zByteArrayOpenFile(voidpf opaque, const char* filename, int mode) {
|
||||
zByteArray *ba = (zByteArray*)opaque;
|
||||
if (mode & ZLIB_FILEFUNC_MODE_WRITE) {
|
||||
if (mode & ZLIB_FILEFUNC_MODE_CREATE) {
|
||||
ba->data.clear();
|
||||
}
|
||||
ba->pos = ba->data.size();
|
||||
ba->data.reserve(2 * 1024 * 1024);
|
||||
} else if (mode & ZLIB_FILEFUNC_MODE_READ) {
|
||||
ba->pos = 0;
|
||||
}
|
||||
ba->err = 0;
|
||||
return opaque;
|
||||
}
|
||||
|
||||
uLong zByteArrayReadFile(voidpf opaque, voidpf stream, void* buf, uLong size) {
|
||||
zByteArray *ba = (zByteArray*)opaque;
|
||||
uLong toRead = 0;
|
||||
if (!ba->err) {
|
||||
if (ba->data.size() > int(ba->pos)) {
|
||||
toRead = qMin(size, uLong(ba->data.size() - ba->pos));
|
||||
memcpy(buf, ba->data.constData() + ba->pos, toRead);
|
||||
ba->pos += toRead;
|
||||
}
|
||||
if (toRead < size) {
|
||||
ba->err = -1;
|
||||
}
|
||||
}
|
||||
return toRead;
|
||||
}
|
||||
|
||||
uLong zByteArrayWriteFile(voidpf opaque, voidpf stream, const void* buf, uLong size) {
|
||||
zByteArray *ba = (zByteArray*)opaque;
|
||||
if (ba->data.size() < int(ba->pos + size)) {
|
||||
ba->data.resize(ba->pos + size);
|
||||
}
|
||||
memcpy(ba->data.data() + ba->pos, buf, size);
|
||||
ba->pos += size;
|
||||
return size;
|
||||
}
|
||||
|
||||
int zByteArrayCloseFile(voidpf opaque, voidpf stream) {
|
||||
zByteArray *ba = (zByteArray*)opaque;
|
||||
int result = ba->err;
|
||||
ba->pos = 0;
|
||||
ba->err = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
int zByteArrayErrorFile(voidpf opaque, voidpf stream) {
|
||||
zByteArray *ba = (zByteArray*)opaque;
|
||||
return ba->err;
|
||||
}
|
||||
|
||||
long zByteArrayTellFile(voidpf opaque, voidpf stream) {
|
||||
zByteArray *ba = (zByteArray*)opaque;
|
||||
return ba->pos;
|
||||
}
|
||||
|
||||
long zByteArraySeekFile(voidpf opaque, voidpf stream, uLong offset, int origin) {
|
||||
zByteArray *ba = (zByteArray*)opaque;
|
||||
if (!ba->err) {
|
||||
switch (origin) {
|
||||
case ZLIB_FILEFUNC_SEEK_SET: ba->pos = offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_CUR: ba->pos += offset; break;
|
||||
case ZLIB_FILEFUNC_SEEK_END: ba->pos = ba->data.size() + offset; break;
|
||||
}
|
||||
if (int(ba->pos) > ba->data.size()) {
|
||||
ba->err = -1;
|
||||
}
|
||||
}
|
||||
return ba->err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
QString LastCrashedWindow::minidumpFileName() {
|
||||
QFileInfo dmpFile(_minidumpFull);
|
||||
if (dmpFile.exists() && dmpFile.size() > 0 && dmpFile.size() < 20 * 1024 * 1024 &&
|
||||
|
@ -2138,45 +2053,24 @@ void LastCrashedWindow::onCheckingFinished() {
|
|||
file.close();
|
||||
|
||||
QString zipName = QString(dmpName).replace(qstr(".dmp"), qstr(".zip"));
|
||||
zByteArray minidumpZip;
|
||||
|
||||
bool failed = false;
|
||||
zlib_filefunc_def zfuncs;
|
||||
zfuncs.opaque = &minidumpZip;
|
||||
zfuncs.zopen_file = zByteArrayOpenFile;
|
||||
zfuncs.zerror_file = zByteArrayErrorFile;
|
||||
zfuncs.zread_file = zByteArrayReadFile;
|
||||
zfuncs.zwrite_file = zByteArrayWriteFile;
|
||||
zfuncs.zclose_file = zByteArrayCloseFile;
|
||||
zfuncs.zseek_file = zByteArraySeekFile;
|
||||
zfuncs.ztell_file = zByteArrayTellFile;
|
||||
zlib::FileToWrite minidumpZip;
|
||||
|
||||
if (zipFile zf = zipOpen2(0, APPEND_STATUS_CREATE, 0, &zfuncs)) {
|
||||
zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 };
|
||||
QByteArray dmpNameUtf = dmpName.toUtf8();
|
||||
if (zipOpenNewFileInZip(zf, dmpNameUtf.constData(), &zfi, NULL, 0, NULL, 0, NULL, Z_DEFLATED, Z_DEFAULT_COMPRESSION) != ZIP_OK) {
|
||||
failed = true;
|
||||
} else if (zipWriteInFileInZip(zf, minidump.constData(), minidump.size()) != 0) {
|
||||
failed = true;
|
||||
} else if (zipCloseFileInZip(zf) != 0) {
|
||||
failed = true;
|
||||
}
|
||||
if (zipClose(zf, NULL) != 0) {
|
||||
failed = true;
|
||||
}
|
||||
if (failed) {
|
||||
minidumpZip.err = -1;
|
||||
}
|
||||
}
|
||||
zip_fileinfo zfi = { { 0, 0, 0, 0, 0, 0 }, 0, 0, 0 };
|
||||
QByteArray dmpNameUtf = dmpName.toUtf8();
|
||||
minidumpZip.openNewFile(dmpNameUtf.constData(), &zfi, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION);
|
||||
minidumpZip.writeInFile(minidump.constData(), minidump.size());
|
||||
minidumpZip.closeFile();
|
||||
minidumpZip.close();
|
||||
|
||||
if (!minidumpZip.err) {
|
||||
if (minidumpZip.error() == ZIP_OK) {
|
||||
QHttpPart dumpPart;
|
||||
dumpPart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/octet-stream"));
|
||||
dumpPart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(qsl("form-data; name=\"dump\"; filename=\"%1\"").arg(zipName)));
|
||||
dumpPart.setBody(minidumpZip.data);
|
||||
dumpPart.setBody(minidumpZip.result());
|
||||
multipart->append(dumpPart);
|
||||
|
||||
_minidump.setText(qsl("+ %1 (%2 KB)").arg(zipName).arg(minidumpZip.data.size() / 1024));
|
||||
_minidump.setText(qsl("+ %1 (%2 KB)").arg(zipName).arg(minidumpZip.result().size() / 1024));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -158,7 +158,7 @@ public:
|
|||
|
||||
void updateUnreadCounter();
|
||||
|
||||
QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon);
|
||||
QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon);
|
||||
|
||||
bool contentOverlapped(const QRect &globalRect);
|
||||
bool contentOverlapped(QWidget *w, QPaintEvent *e) {
|
||||
|
@ -232,7 +232,7 @@ private:
|
|||
|
||||
QPixmap grabInner();
|
||||
|
||||
void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color);
|
||||
void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color);
|
||||
QImage icon16, icon32, icon64, iconbig16, iconbig32, iconbig64;
|
||||
|
||||
QWidget *centralwidget;
|
||||
|
|
|
@ -468,7 +468,7 @@ void Voice::paint(Painter &p, const QRect &clip, TextSelection selection, const
|
|||
|
||||
if (radial) {
|
||||
QRect rinner(inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine)));
|
||||
style::color bg(selected ? st::msgInBgSelected : st::msgInBg);
|
||||
auto &bg = selected ? st::msgInBgSelected : st::msgInBg;
|
||||
_radial->draw(p, rinner, st::msgFileRadialLine, bg);
|
||||
}
|
||||
|
||||
|
@ -679,7 +679,7 @@ void Document::paint(Painter &p, const QRect &clip, TextSelection selection, con
|
|||
|
||||
if (radial) {
|
||||
auto rinner = inner.marginsRemoved(QMargins(st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine, st::msgFileRadialLine));
|
||||
style::color bg(selected ? st::msgInBgSelected : st::msgInBg);
|
||||
auto &bg = selected ? st::msgInBgSelected : st::msgInBg;
|
||||
_radial->draw(p, rinner, st::msgFileRadialLine, bg);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "ui/widgets/tooltip.h"
|
||||
#include "ui/buttons/icon_button.h"
|
||||
#include "window/top_bar_widget.h"
|
||||
#include "window/chat_background.h"
|
||||
#include "window/window_theme.h"
|
||||
#include "lang.h"
|
||||
#include "mainwindow.h"
|
||||
#include "mainwidget.h"
|
||||
|
|
|
@ -103,7 +103,7 @@ QImage _trayIconImageGen() {
|
|||
} else if (_trayIconSize >= 32) {
|
||||
layerSize = -20;
|
||||
}
|
||||
QImage layer = App::wnd()->iconWithCounter(layerSize, counter, (muted ? st::counterMuteBG : st::counterBG), false);
|
||||
QImage layer = App::wnd()->iconWithCounter(layerSize, counter, (muted ? st::counterMuteBg : st::counterBg), false);
|
||||
p.drawImage(_trayIconImage.width() - layer.width() - 1, _trayIconImage.height() - layer.height() - 1, layer);
|
||||
}
|
||||
}
|
||||
|
@ -355,7 +355,7 @@ void MainWindow::psUpdateCounter() {
|
|||
int32 counter = App::histories().unreadBadge();
|
||||
bool muted = App::histories().unreadOnlyMuted();
|
||||
|
||||
style::color bg = muted ? st::counterMuteBG : st::counterBG;
|
||||
auto &bg = (muted ? st::counterMuteBg : st::counterBg);
|
||||
icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(16, counter, bg, true)));
|
||||
icon.addPixmap(App::pixmapFromImageInPlace(iconWithCounter(32, counter, bg, true)));
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ public:
|
|||
|
||||
bool psHasNativeNotifications();
|
||||
|
||||
virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0;
|
||||
virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0;
|
||||
|
||||
static void LibsLoaded();
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ public:
|
|||
return !(QSysInfo::macVersion() < QSysInfo::MV_10_8);
|
||||
}
|
||||
|
||||
virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0;
|
||||
virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0;
|
||||
|
||||
void closeWithoutDestroy() override;
|
||||
|
||||
|
@ -114,7 +114,7 @@ protected:
|
|||
|
||||
void psTrayMenuUpdated();
|
||||
void psSetupTrayIcon();
|
||||
virtual void placeSmallCounter(QImage &img, int size, int count, style::color bg, const QPoint &shift, style::color color) = 0;
|
||||
virtual void placeSmallCounter(QImage &img, int size, int count, const style::color &bg, const QPoint &shift, const style::color &color) = 0;
|
||||
|
||||
QTimer psUpdatedPositionTimer;
|
||||
|
||||
|
|
|
@ -128,7 +128,7 @@ void MainWindow::psUpdateWorkmode() {
|
|||
setWindowIcon(wndIcon);
|
||||
}
|
||||
|
||||
void _placeCounter(QImage &img, int size, int count, style::color bg, style::color color) {
|
||||
void _placeCounter(QImage &img, int size, int count, const style::color &bg, const style::color &color) {
|
||||
if (!count) return;
|
||||
|
||||
QPainter p(&img);
|
||||
|
@ -176,13 +176,13 @@ void MainWindow::psUpdateCounter() {
|
|||
bool muted = App::histories().unreadOnlyMuted();
|
||||
bool dm = objc_darkMode();
|
||||
|
||||
style::color bg = muted ? st::counterMuteBG : st::counterBG;
|
||||
auto &bg = (muted ? st::counterMuteBg : st::counterBg);
|
||||
QIcon icon;
|
||||
QImage img(psTrayIcon(dm)), imgsel(psTrayIcon(true));
|
||||
img.detach();
|
||||
imgsel.detach();
|
||||
int32 size = cRetina() ? 44 : 22;
|
||||
_placeCounter(img, size, counter, bg, (dm && muted) ? st::counterMacInvColor : st::counterColor);
|
||||
_placeCounter(img, size, counter, bg, (dm && muted) ? st::counterMacInvFg : st::counterFg);
|
||||
_placeCounter(imgsel, size, counter, st::white, st::counterMacInvColor);
|
||||
icon.addPixmap(App::pixmapFromImageInPlace(std_::move(img)));
|
||||
icon.addPixmap(App::pixmapFromImageInPlace(std_::move(imgsel)), QIcon::Selected);
|
||||
|
|
|
@ -718,7 +718,7 @@ void MainWindow::psUpdateCounter() {
|
|||
auto iconSizeSmall = QSize(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON));
|
||||
auto iconSizeBig = QSize(GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
|
||||
|
||||
style::color bg = muted ? st::counterMuteBG : st::counterBG;
|
||||
auto bg = (muted ? st::counterMuteBg : st::counterBg);
|
||||
auto iconSmallPixmap16 = App::pixmapFromImageInPlace(iconWithCounter(16, counter, bg, true));
|
||||
auto iconSmallPixmap32 = App::pixmapFromImageInPlace(iconWithCounter(32, counter, bg, true));
|
||||
QIcon iconSmall, iconBig;
|
||||
|
|
|
@ -67,7 +67,7 @@ public:
|
|||
|
||||
bool psHasNativeNotifications();
|
||||
|
||||
virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0;
|
||||
virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0;
|
||||
|
||||
static UINT TaskbarCreatedMsgId() {
|
||||
return _taskbarCreatedMsgId;
|
||||
|
|
|
@ -65,14 +65,14 @@ profileSecondaryButton: RoundButton(profilePrimaryButton) {
|
|||
textBg: #ffffff;
|
||||
textBgOver: #f2f7fa;
|
||||
}
|
||||
profileAddMemberIcon: icon {{ "profile_add_member", windowActiveBg, point(20px, 10px) }};
|
||||
profileAddMemberIcon: icon {{ "profile_add_member", windowActiveFill, point(20px, 10px) }};
|
||||
profileAddMemberButton: RoundButton(profileSecondaryButton) {
|
||||
width: 62px;
|
||||
icon: profileAddMemberIcon;
|
||||
}
|
||||
|
||||
profileDropAreaBg: profileBg;
|
||||
profileDropAreaFg: windowActiveBg;
|
||||
profileDropAreaFg: windowActiveFill;
|
||||
profileDropAreaPadding: margins(25px, 3px, 25px, 20px);
|
||||
profileDropAreaTitleFont: font(24px);
|
||||
profileDropAreaTitleTop: 30px;
|
||||
|
|
|
@ -77,7 +77,7 @@ public:
|
|||
bool psHasNativeNotifications();
|
||||
void psCleanNotifyPhotosIn(int32 dt);
|
||||
|
||||
virtual QImage iconWithCounter(int size, int count, style::color bg, bool smallIcon) = 0;
|
||||
virtual QImage iconWithCounter(int size, int count, const style::color &bg, bool smallIcon) = 0;
|
||||
|
||||
~PsMainWindow();
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "ui/buttons/checkbox.h"
|
||||
#include "localstorage.h"
|
||||
#include "mainwindow.h"
|
||||
#include "window/chat_background.h"
|
||||
#include "window/window_theme.h"
|
||||
|
||||
namespace Settings {
|
||||
|
||||
|
@ -37,8 +37,6 @@ BackgroundRow::BackgroundRow(QWidget *parent) : TWidget(parent)
|
|||
, _chooseFromGallery(this, lang(lng_settings_bg_from_gallery), st::defaultBoxLinkButton)
|
||||
, _chooseFromFile(this, lang(lng_settings_bg_from_file), st::defaultBoxLinkButton)
|
||||
, _radial(animation(this, &BackgroundRow::step_radial)) {
|
||||
Window::chatBackground()->initIfEmpty();
|
||||
|
||||
updateImage();
|
||||
|
||||
connect(_chooseFromGallery, SIGNAL(clicked()), this, SIGNAL(chooseFromGallery()));
|
||||
|
@ -145,7 +143,7 @@ void BackgroundRow::updateImage() {
|
|||
back.setDevicePixelRatio(cRetinaFactor());
|
||||
{
|
||||
QPainter p(&back);
|
||||
auto &pix = Window::chatBackground()->image();
|
||||
auto &pix = Window::Theme::Background()->image();
|
||||
int sx = (pix.width() > pix.height()) ? ((pix.width() - pix.height()) / 2) : 0;
|
||||
int sy = (pix.height() > pix.width()) ? ((pix.height() - pix.width()) / 2) : 0;
|
||||
int s = (pix.width() > pix.height()) ? pix.height() : pix.width();
|
||||
|
@ -169,8 +167,8 @@ BackgroundWidget::BackgroundWidget(QWidget *parent, UserData *self) : BlockWidge
|
|||
subscribe(FileDialog::QueryDone(), [this](const FileDialog::QueryUpdate &update) {
|
||||
notifyFileQueryUpdated(update);
|
||||
});
|
||||
subscribe(Window::chatBackground(), [this](const Window::ChatBackgroundUpdate &update) {
|
||||
using Update = Window::ChatBackgroundUpdate;
|
||||
using Update = Window::Theme::BackgroundUpdate;
|
||||
subscribe(Window::Theme::Background(), [this](const Update &update) {
|
||||
if (update.type == Update::Type::New) {
|
||||
_background->updateImage();
|
||||
} else if (update.type == Update::Type::Start) {
|
||||
|
@ -194,7 +192,7 @@ void BackgroundWidget::createControls() {
|
|||
connect(_background, SIGNAL(chooseFromGallery()), this, SLOT(onChooseFromGallery()));
|
||||
connect(_background, SIGNAL(chooseFromFile()), this, SLOT(onChooseFromFile()));
|
||||
|
||||
addChildRow(_tile, margin, lang(lng_settings_bg_tile), SLOT(onTile()), Window::chatBackground()->tile());
|
||||
addChildRow(_tile, margin, lang(lng_settings_bg_tile), SLOT(onTile()), Window::Theme::Background()->tile());
|
||||
addChildRow(_adaptive, margin, slidedPadding, lang(lng_settings_adaptive_wide), SLOT(onAdaptive()), Global::AdaptiveForWide());
|
||||
if (Global::AdaptiveLayout() != Adaptive::WideLayout) {
|
||||
_adaptive->hideFast();
|
||||
|
@ -211,10 +209,12 @@ void BackgroundWidget::needBackgroundUpdate(bool tile) {
|
|||
}
|
||||
|
||||
void BackgroundWidget::onChooseFromFile() {
|
||||
QStringList imgExtensions(cImgExtensions());
|
||||
QString filter(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(");;") + filedialogAllFilesFilter());
|
||||
auto imgExtensions = cImgExtensions();
|
||||
auto filters = QStringList(qsl("Image files (*") + imgExtensions.join(qsl(" *")) + qsl(")"));
|
||||
filters.push_back(qsl("Theme files (*.tdesktop-theme)"));
|
||||
filters.push_back(filedialogAllFilesFilter());
|
||||
|
||||
_chooseFromFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filter);
|
||||
_chooseFromFileQueryId = FileDialog::queryReadFile(lang(lng_choose_images), filters.join(qsl(";;")));
|
||||
}
|
||||
|
||||
void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update) {
|
||||
|
@ -227,11 +227,28 @@ void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &upd
|
|||
return;
|
||||
}
|
||||
|
||||
auto filePath = update.filePaths.front();
|
||||
if (filePath.endsWith(qstr(".tdesktop-theme"), Qt::CaseInsensitive)) {
|
||||
QByteArray themeContent;
|
||||
Window::Theme::Instance theme;
|
||||
if (Window::Theme::LoadFromFile(filePath, &theme, &themeContent)) {
|
||||
Local::writeTheme(QDir().relativeFilePath(filePath), QFileInfo(filePath).absoluteFilePath(), themeContent, theme.cached);
|
||||
if (Window::Theme::Background()->tile() != theme.cached.tiled) {
|
||||
Window::Theme::Background()->setTile(theme.cached.tiled);
|
||||
}
|
||||
if (!theme.cached.background.isEmpty()) {
|
||||
Local::writeBackground(Window::Theme::kThemeBackground, QImage());
|
||||
}
|
||||
App::restart();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
QImage img;
|
||||
if (!update.remoteContent.isEmpty()) {
|
||||
img = App::readImage(update.remoteContent);
|
||||
} else {
|
||||
img = App::readImage(update.filePaths.front());
|
||||
img = App::readImage(filePath);
|
||||
}
|
||||
|
||||
if (img.isNull() || img.width() <= 0 || img.height() <= 0) return;
|
||||
|
@ -242,13 +259,13 @@ void BackgroundWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &upd
|
|||
img = img.copy(0, (img.height() - 4096 * img.width()) / 2, img.width(), 4096 * img.width());
|
||||
}
|
||||
|
||||
App::initBackground(-1, img);
|
||||
Window::Theme::Background()->setImage(Window::Theme::kCustomBackground, std_::move(img));
|
||||
_tile->setChecked(false);
|
||||
_background->updateImage();
|
||||
}
|
||||
|
||||
void BackgroundWidget::onTile() {
|
||||
Window::chatBackground()->setTile(_tile->checked());
|
||||
Window::Theme::Background()->setTile(_tile->checked());
|
||||
}
|
||||
|
||||
void BackgroundWidget::onAdaptive() {
|
||||
|
|
|
@ -231,7 +231,7 @@ void GeneralWidget::notifyFileQueryUpdated(const FileDialog::QueryUpdate &update
|
|||
}
|
||||
|
||||
_testLanguage = QFileInfo(update.filePaths.front()).absoluteFilePath();
|
||||
LangLoaderPlain loader(_testLanguage, LangLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
|
||||
LangLoaderPlain loader(_testLanguage, langLoaderRequest(lng_sure_save_language, lng_cancel, lng_box_ok));
|
||||
if (loader.errors().isEmpty()) {
|
||||
LangLoaderResult result = loader.found();
|
||||
QString text = result.value(lng_sure_save_language, langOriginal(lng_sure_save_language)),
|
||||
|
@ -263,17 +263,8 @@ void GeneralWidget::onSaveTestLanguage() {
|
|||
void GeneralWidget::onRestart() {
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
checkReadyUpdate();
|
||||
if (_updateRow->entity()->isUpdateReady()) {
|
||||
cSetRestartingUpdate(true);
|
||||
} else {
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
}
|
||||
#else // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
#endif // else for !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
App::quit();
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
App::restart();
|
||||
}
|
||||
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
|
|
|
@ -117,18 +117,7 @@ void ScaleWidget::scaleChanged() {
|
|||
}
|
||||
|
||||
void ScaleWidget::onRestartNow() {
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
bool updateReady = (Sandbox::updatingState() == Application::UpdatingReady);
|
||||
#else // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
bool updateReady = false;
|
||||
#endif // else for !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
if (updateReady) {
|
||||
cSetRestartingUpdate(true);
|
||||
} else {
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(true);
|
||||
}
|
||||
App::quit();
|
||||
App::restart();
|
||||
}
|
||||
|
||||
} // namespace Settings
|
||||
|
|
|
@ -26,6 +26,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "mainwidget.h"
|
||||
#include "media/player/media_player_instance.h"
|
||||
#include "pspecific.h"
|
||||
#include "core/parse_helper.h"
|
||||
|
||||
namespace ShortcutCommands {
|
||||
|
||||
|
@ -156,76 +157,6 @@ inline bool qMapLessThanKey(const ShortcutCommands::Handler &a, const ShortcutCo
|
|||
|
||||
namespace Shortcuts {
|
||||
|
||||
// inspired by https://github.com/sindresorhus/strip-json-comments
|
||||
QByteArray _stripJsonComments(const QByteArray &json) {
|
||||
enum InsideComment {
|
||||
InsideCommentNone,
|
||||
InsideCommentSingleLine,
|
||||
InsideCommentMultiLine,
|
||||
};
|
||||
InsideComment insideComment = InsideCommentNone;
|
||||
bool insideString = false;
|
||||
|
||||
QByteArray result;
|
||||
|
||||
const char *b = json.cbegin(), *e = json.cend(), *offset = b;
|
||||
for (const char *ch = offset; ch != e; ++ch) {
|
||||
char currentChar = *ch;
|
||||
char nextChar = (ch + 1 == e) ? 0 : *(ch + 1);
|
||||
|
||||
if (insideComment == InsideCommentNone && currentChar == '"') {
|
||||
bool escaped = ((ch > b) && *(ch - 1) == '\\') && ((ch - 1 < b) || *(ch - 2) != '\\');
|
||||
if (!escaped) {
|
||||
insideString = !insideString;
|
||||
}
|
||||
}
|
||||
|
||||
if (insideString) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '/') {
|
||||
if (ch > offset) {
|
||||
if (result.isEmpty()) result.reserve(json.size() - 2);
|
||||
result.append(offset, ch - offset);
|
||||
offset = ch;
|
||||
}
|
||||
insideComment = InsideCommentSingleLine;
|
||||
++ch;
|
||||
} else if (insideComment == InsideCommentSingleLine && currentChar == '\r' && nextChar == '\n') {
|
||||
if (ch > offset) {
|
||||
offset = ch;
|
||||
}
|
||||
++ch;
|
||||
insideComment = InsideCommentNone;
|
||||
} else if (insideComment == InsideCommentSingleLine && currentChar == '\n') {
|
||||
if (ch > offset) {
|
||||
offset = ch;
|
||||
}
|
||||
insideComment = InsideCommentNone;
|
||||
} else if (insideComment == InsideCommentNone && currentChar == '/' && nextChar == '*') {
|
||||
if (ch > offset) {
|
||||
if (result.isEmpty()) result.reserve(json.size() - 2);
|
||||
result.append(offset, ch - offset);
|
||||
offset = ch;
|
||||
}
|
||||
insideComment = InsideCommentMultiLine;
|
||||
++ch;
|
||||
} else if (insideComment == InsideCommentMultiLine && currentChar == '*' && nextChar == '/') {
|
||||
if (ch > offset) {
|
||||
offset = ch;
|
||||
}
|
||||
++ch;
|
||||
insideComment = InsideCommentNone;
|
||||
}
|
||||
}
|
||||
|
||||
if (insideComment == InsideCommentNone && e > offset && !result.isEmpty()) {
|
||||
result.append(offset, e - offset);
|
||||
}
|
||||
return result.isEmpty() ? json : result;
|
||||
}
|
||||
|
||||
struct DataStruct;
|
||||
DataStruct *DataPtr = nullptr;
|
||||
|
||||
|
@ -406,7 +337,7 @@ void start() {
|
|||
QFile defaultFile(cWorkingDir() + qsl("tdata/shortcuts-default.json"));
|
||||
if (defaultFile.open(QIODevice::ReadOnly)) {
|
||||
QJsonParseError error = { 0, QJsonParseError::NoError };
|
||||
QJsonDocument doc = QJsonDocument::fromJson(_stripJsonComments(defaultFile.readAll()), &error);
|
||||
QJsonDocument doc = QJsonDocument::fromJson(base::parse::stripComments(defaultFile.readAll()), &error);
|
||||
defaultFile.close();
|
||||
|
||||
if (error.error == QJsonParseError::NoError && doc.isArray()) {
|
||||
|
@ -457,7 +388,7 @@ void start() {
|
|||
if (customFile.exists()) {
|
||||
if (customFile.open(QIODevice::ReadOnly)) {
|
||||
QJsonParseError error = { 0, QJsonParseError::NoError };
|
||||
QJsonDocument doc = QJsonDocument::fromJson(_stripJsonComments(customFile.readAll()), &error);
|
||||
QJsonDocument doc = QJsonDocument::fromJson(base::parse::stripComments(customFile.readAll()), &error);
|
||||
customFile.close();
|
||||
|
||||
if (error.error != QJsonParseError::NoError) {
|
||||
|
|
|
@ -22,18 +22,18 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
|
||||
namespace Shortcuts {
|
||||
|
||||
void start();
|
||||
const QStringList &errors();
|
||||
void start();
|
||||
const QStringList &errors();
|
||||
|
||||
bool launch(int shortcutId);
|
||||
bool launch(const QString &command);
|
||||
bool launch(int shortcutId);
|
||||
bool launch(const QString &command);
|
||||
|
||||
// Media shortcuts are not enabled by default, because other
|
||||
// applications also use them. They are enabled only when
|
||||
// the in-app player is active and disabled back after.
|
||||
void enableMediaShortcuts();
|
||||
void disableMediaShortcuts();
|
||||
// Media shortcuts are not enabled by default, because other
|
||||
// applications also use them. They are enabled only when
|
||||
// the in-app player is active and disabled back after.
|
||||
void enableMediaShortcuts();
|
||||
void disableMediaShortcuts();
|
||||
|
||||
void finish();
|
||||
void finish();
|
||||
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "mtproto/facade.h"
|
||||
|
||||
#include "ui/style/style_core.h"
|
||||
#include "styles/palette.h"
|
||||
#include "styles/style_basic_types.h"
|
||||
#include "styles/style_basic.h"
|
||||
|
||||
|
|
|
@ -135,15 +135,8 @@ UpdateBtn::UpdateBtn(QWidget *parent) : SysBtn(parent, st::sysUpd, lang(lng_menu
|
|||
setClickedCallback([]() {
|
||||
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
|
||||
checkReadyUpdate();
|
||||
if (Sandbox::updatingState() == Application::UpdatingReady) {
|
||||
cSetRestartingUpdate(true);
|
||||
} else
|
||||
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
|
||||
{
|
||||
cSetRestarting(true);
|
||||
cSetRestartingToSettings(false);
|
||||
}
|
||||
App::quit();
|
||||
App::restart();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -325,7 +325,7 @@ void TitleWidget::updateCounter() {
|
|||
int32 counter = App::histories().unreadBadge();
|
||||
bool muted = App::histories().unreadOnlyMuted();
|
||||
|
||||
style::color bg = muted ? st::counterMuteBG : st::counterBG;
|
||||
auto &bg = (muted ? st::counterMuteBg : st::counterBg);
|
||||
|
||||
if (counter > 0) {
|
||||
int32 size = cRetina() ? -32 : -16;
|
||||
|
|
|
@ -97,7 +97,6 @@ void startManager() {
|
|||
stopManager();
|
||||
|
||||
_manager = new AnimationManager();
|
||||
|
||||
}
|
||||
|
||||
void stopManager() {
|
||||
|
|
|
@ -25,11 +25,10 @@ Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
|||
#include "ui/flatbutton.h"
|
||||
#include "ui/effects/rect_shadow.h"
|
||||
#include "boxes/abstractbox.h"
|
||||
#include "styles/style_intro.h"
|
||||
|
||||
QString findValidCode(QString fullCode);
|
||||
|
||||
class CountrySelect;
|
||||
|
||||
namespace Ui {
|
||||
class MultiSelect;
|
||||
} // namespace Ui
|
||||
|
@ -63,8 +62,6 @@ private:
|
|||
bool _active;
|
||||
QString _text;
|
||||
|
||||
CountrySelect *_select;
|
||||
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
|
|
@ -82,8 +82,8 @@ void FlatButton::step_appearance(float64 ms, bool timer) {
|
|||
}
|
||||
|
||||
void FlatButton::onStateChange(int oldState, ButtonStateChangeSource source) {
|
||||
style::color bgColorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downBgColor : _st.overBgColor) : _st.bgColor;
|
||||
style::color colorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downColor : _st.overColor) : _st.color;
|
||||
auto &bgColorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downBgColor : _st.overBgColor) : _st.bgColor;
|
||||
auto &colorTo = (_state & StateOver) ? ((_state & StateDown) ? _st.downColor : _st.overColor) : _st.color;
|
||||
|
||||
a_bg.start(bgColorTo->c);
|
||||
a_text.start(colorTo->c);
|
||||
|
|
|
@ -55,6 +55,9 @@ public:
|
|||
void registerModule(ModuleBase *module);
|
||||
void unregisterModule(ModuleBase *module);
|
||||
|
||||
// This method is implemented in palette.cpp (codegen).
|
||||
bool setPaletteColor(QLatin1String name, uchar r, uchar g, uchar b, uchar a);
|
||||
|
||||
} // namespace internal
|
||||
|
||||
void startManager();
|
||||
|
|
|
@ -25,7 +25,7 @@ namespace Ui {
|
|||
|
||||
void ToggleableShadow::setMode(Mode mode) {
|
||||
if (mode == Mode::ShownFast || mode == Mode::HiddenFast) {
|
||||
if (!_a_opacity.animating()) {
|
||||
if (_a_opacity.animating()) {
|
||||
_a_opacity.finish();
|
||||
update();
|
||||
}
|
||||
|
|
|
@ -192,7 +192,7 @@ defaultMenu: Menu {
|
|||
skip: 5px;
|
||||
|
||||
itemBg: white;
|
||||
itemBgOver: overBg;
|
||||
itemBgOver: windowOverBg;
|
||||
itemFg: black;
|
||||
itemFgOver: black;
|
||||
itemFgDisabled: #cccccc;
|
||||
|
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "window/chat_background.h"
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "localstorage.h"
|
||||
|
||||
namespace Window {
|
||||
namespace {
|
||||
|
||||
NeverFreedPointer<ChatBackground> instance;
|
||||
|
||||
} // namespace
|
||||
|
||||
bool ChatBackground::empty() const {
|
||||
return _image.isNull();
|
||||
}
|
||||
|
||||
void ChatBackground::initIfEmpty() {
|
||||
if (empty()) {
|
||||
App::initBackground();
|
||||
}
|
||||
}
|
||||
|
||||
void ChatBackground::init(int32 id, QPixmap &&image) {
|
||||
_id = id;
|
||||
_image = std_::move(image);
|
||||
|
||||
notify(ChatBackgroundUpdate(ChatBackgroundUpdate::Type::New, _tile));
|
||||
}
|
||||
|
||||
void ChatBackground::reset() {
|
||||
_id = 0;
|
||||
_image = QPixmap();
|
||||
_tile = false;
|
||||
|
||||
notify(ChatBackgroundUpdate(ChatBackgroundUpdate::Type::New, _tile));
|
||||
}
|
||||
|
||||
int32 ChatBackground::id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
const QPixmap &ChatBackground::image() const {
|
||||
return _image;
|
||||
}
|
||||
|
||||
bool ChatBackground::tile() const {
|
||||
return _tile;
|
||||
}
|
||||
|
||||
void ChatBackground::setTile(bool tile) {
|
||||
if (_tile != tile) {
|
||||
_tile = tile;
|
||||
Local::writeUserSettings();
|
||||
notify(ChatBackgroundUpdate(ChatBackgroundUpdate::Type::Changed, _tile));
|
||||
}
|
||||
}
|
||||
|
||||
ChatBackground *chatBackground() {
|
||||
instance.createIfNull();
|
||||
return instance.data();
|
||||
}
|
||||
|
||||
} // namespace Window
|
|
@ -72,7 +72,7 @@ notifySendReply: IconButton(defaultIconButton) {
|
|||
width: 36px;
|
||||
height: 36px;
|
||||
|
||||
icon: icon {{ "notification_send", windowActiveBg, point(3px, 9px) }};
|
||||
icon: icon {{ "notification_send", windowActiveFill, point(3px, 9px) }};
|
||||
iconPosition: point(0px, 0px);
|
||||
downIconPosition: point(0px, 1px);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#include "stdafx.h"
|
||||
#include "window/window_theme.h"
|
||||
|
||||
#include "mainwidget.h"
|
||||
#include "localstorage.h"
|
||||
#include "core/parse_helper.h"
|
||||
#include "core/zlib_help.h"
|
||||
|
||||
namespace Window {
|
||||
namespace Theme {
|
||||
namespace {
|
||||
|
||||
constexpr int kThemeFileSizeLimit = 5 * 1024 * 1024;
|
||||
constexpr int kThemeBackgroundSizeLimit = 4 * 1024 * 1024;
|
||||
constexpr int kThemeSchemeSizeLimit = 1024 * 1024;
|
||||
|
||||
struct Data {
|
||||
ChatBackground background;
|
||||
};
|
||||
NeverFreedPointer<Data> instance;
|
||||
|
||||
QByteArray readThemeContent(const QString &path) {
|
||||
QFile file(path);
|
||||
if (!file.exists()) {
|
||||
LOG(("Error: theme file not found: %1").arg(path));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
if (file.size() > kThemeFileSizeLimit) {
|
||||
LOG(("Error: theme file too large: %1 (should be less than 5 MB, got %2)").arg(path).arg(file.size()));
|
||||
return QByteArray();
|
||||
}
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
LOG(("Warning: could not open theme file: %1").arg(path));
|
||||
return QByteArray();
|
||||
}
|
||||
|
||||
return file.readAll();
|
||||
}
|
||||
|
||||
inline uchar readHexUchar(char code, bool &error) {
|
||||
if (code >= '0' && code <= '9') {
|
||||
return ((code - '0') & 0xFF);
|
||||
} else if (code >= 'a' && code <= 'f') {
|
||||
return ((code + 10 - 'a') & 0xFF);
|
||||
} else if (code >= 'A' && code <= 'F') {
|
||||
return ((code + 10 - 'A') & 0xFF);
|
||||
}
|
||||
error = true;
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
inline uchar readHexUchar(char char1, char char2, bool &error) {
|
||||
return ((readHexUchar(char1, error) & 0x0F) << 4) | (readHexUchar(char2, error) & 0x0F);
|
||||
}
|
||||
|
||||
bool readNameAndValue(const char *&from, const char *end, QByteArray *outName, QByteArray *outValue) {
|
||||
using base::parse::skipWhitespaces;
|
||||
using base::parse::readName;
|
||||
|
||||
if (!skipWhitespaces(from, end)) return true;
|
||||
|
||||
*outName = readName(from, end);
|
||||
if (outName->isEmpty()) {
|
||||
LOG(("Error: Could not read name in the color scheme."));
|
||||
return false;
|
||||
}
|
||||
if (!skipWhitespaces(from, end)) {
|
||||
LOG(("Error: Unexpected end of the color scheme."));
|
||||
return false;
|
||||
}
|
||||
if (*from != ':') {
|
||||
LOG(("Error: Expected ':' between each name and value in the color scheme."));
|
||||
return false;
|
||||
}
|
||||
if (!skipWhitespaces(++from, end)) {
|
||||
LOG(("Error: Unexpected end of the color scheme."));
|
||||
return false;
|
||||
}
|
||||
auto valueStart = from;
|
||||
if (*from == '#') ++from;
|
||||
|
||||
if (readName(from, end).isEmpty()) {
|
||||
LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme."));
|
||||
return false;
|
||||
}
|
||||
*outValue = QByteArray::fromRawData(valueStart, from - valueStart);
|
||||
|
||||
if (!skipWhitespaces(from, end)) {
|
||||
LOG(("Error: Unexpected end of the color scheme."));
|
||||
return false;
|
||||
}
|
||||
if (*from != ';') {
|
||||
LOG(("Error: Expected ';' after each value in the color scheme."));
|
||||
return false;
|
||||
}
|
||||
++from;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadColorScheme(const QByteArray &content, Instance *out = nullptr) {
|
||||
if (content.size() > kThemeSchemeSizeLimit) {
|
||||
LOG(("Error: color scheme file too large (should be less than 1 MB, got %2)").arg(content.size()));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto data = base::parse::stripComments(content);
|
||||
auto from = data.constData(), end = from + data.size();
|
||||
while (from != end) {
|
||||
QByteArray name, value;
|
||||
if (!readNameAndValue(from, end, &name, &value)) {
|
||||
return false;
|
||||
}
|
||||
if (name.isEmpty()) { // End of content reached.
|
||||
return true;
|
||||
}
|
||||
|
||||
auto size = value.size();
|
||||
auto error = false;
|
||||
if (value[0] == '#' && (size == 7 || size == 8)) {
|
||||
auto r = readHexUchar(value[1], value[2], error);
|
||||
auto g = readHexUchar(value[3], value[4], error);
|
||||
auto b = readHexUchar(value[5], value[6], error);
|
||||
auto a = (size == 8) ? readHexUchar(value[7], value[8], error) : uchar(255);
|
||||
if (!error) {
|
||||
if (out) {
|
||||
error = !out->palette.setColor(QLatin1String(name), r, g, b, a);
|
||||
} else {
|
||||
error = !style::main_palette::setColor(QLatin1String(name), r, g, b, a);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (out) {
|
||||
error = !out->palette.setColor(QLatin1String(name), QLatin1String(value));
|
||||
} else {
|
||||
error = !style::main_palette::setColor(QLatin1String(name), QLatin1String(value));
|
||||
}
|
||||
}
|
||||
if (error) {
|
||||
LOG(("Error: Expected a color value in #rrggbb or #rrggbbaa format in the color scheme."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void applyBackground(QImage &&background, bool tiled, Instance *out) {
|
||||
if (out) {
|
||||
out->background = std_::move(background);
|
||||
out->tiled = tiled;
|
||||
} else {
|
||||
Background()->setThemeData(std_::move(background), tiled);
|
||||
}
|
||||
}
|
||||
|
||||
bool loadThemeFromCache(const QByteArray &content, Cached &cache) {
|
||||
if (cache.paletteChecksum != style::palette::kChecksum) {
|
||||
return false;
|
||||
}
|
||||
if (cache.contentChecksum != hashCrc32(content.constData(), content.size())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
QImage background;
|
||||
if (!cache.background.isEmpty()) {
|
||||
QBuffer buffer(&cache.background);
|
||||
QImageReader reader(&buffer);
|
||||
#ifndef OS_MAC_OLD
|
||||
reader.setAutoTransform(true);
|
||||
#endif // OS_MAC_OLD
|
||||
if (!reader.read(&background) || background.isNull()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!style::main_palette::load(cache.colors)) {
|
||||
return false;
|
||||
}
|
||||
if (!background.isNull()) {
|
||||
applyBackground(std_::move(background), cache.tiled, nullptr);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum class LoadResult {
|
||||
Loaded,
|
||||
Failed,
|
||||
NotFound,
|
||||
};
|
||||
|
||||
LoadResult loadBackgroundFromFile(zlib::FileToRead &file, const char *filename, QByteArray *outBackground) {
|
||||
*outBackground = file.readFileContent(filename, zlib::kCaseInsensitive, kThemeBackgroundSizeLimit);
|
||||
if (file.error() == UNZ_OK) {
|
||||
return LoadResult::Loaded;
|
||||
} else if (file.error() == UNZ_END_OF_LIST_OF_FILE) {
|
||||
file.clearError();
|
||||
return LoadResult::NotFound;
|
||||
}
|
||||
LOG(("Error: could not read '%1' in the theme file.").arg(filename));
|
||||
return LoadResult::Failed;
|
||||
}
|
||||
|
||||
bool loadBackground(zlib::FileToRead &file, QByteArray *outBackground, bool *outTiled) {
|
||||
auto result = loadBackgroundFromFile(file, "background.jpg", outBackground);
|
||||
if (result != LoadResult::NotFound) return (result == LoadResult::Loaded);
|
||||
|
||||
result = loadBackgroundFromFile(file, "background.png", outBackground);
|
||||
if (result != LoadResult::NotFound) return (result == LoadResult::Loaded);
|
||||
|
||||
*outTiled = true;
|
||||
result = loadBackgroundFromFile(file, "tiled.jpg", outBackground);
|
||||
if (result != LoadResult::NotFound) return (result == LoadResult::Loaded);
|
||||
|
||||
result = loadBackgroundFromFile(file, "tiled.png", outBackground);
|
||||
if (result != LoadResult::NotFound) return (result == LoadResult::Loaded);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadTheme(const QByteArray &content, Cached &cache, Instance *out = nullptr) {
|
||||
cache = Cached();
|
||||
zlib::FileToRead file(content);
|
||||
|
||||
unz_global_info globalInfo = { 0 };
|
||||
file.getGlobalInfo(&globalInfo);
|
||||
if (file.error() == UNZ_OK) {
|
||||
auto schemeContent = file.readFileContent("colors.tdesktop-theme", zlib::kCaseInsensitive, kThemeSchemeSizeLimit);
|
||||
if (file.error() != UNZ_OK) {
|
||||
LOG(("Error: could not read 'colors.tdesktop-theme' in the theme file."));
|
||||
return false;
|
||||
}
|
||||
if (!loadColorScheme(schemeContent, out)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto backgroundTiled = false;
|
||||
auto backgroundContent = QByteArray();
|
||||
if (!loadBackground(file, &backgroundContent, &backgroundTiled)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!backgroundContent.isEmpty()) {
|
||||
auto background = App::readImage(backgroundContent);
|
||||
if (background.isNull()) {
|
||||
LOG(("Error: could not read background image in the theme file."));
|
||||
return false;
|
||||
}
|
||||
QBuffer buffer(&cache.background);
|
||||
if (!background.save(&buffer, "BMP")) {
|
||||
LOG(("Error: could not write background image as a BMP to cache."));
|
||||
return false;
|
||||
}
|
||||
cache.tiled = backgroundTiled;
|
||||
|
||||
applyBackground(std_::move(background), cache.tiled, out);
|
||||
}
|
||||
} else {
|
||||
// Looks like it is not a .zip theme.
|
||||
if (!loadColorScheme(content, out)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (out) {
|
||||
cache.colors = out->palette.save();
|
||||
} else {
|
||||
cache.colors = style::main_palette::save();
|
||||
}
|
||||
cache.paletteChecksum = style::palette::kChecksum;
|
||||
cache.contentChecksum = hashCrc32(content.constData(), content.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QImage prepareBackgroundImage(QImage &&image) {
|
||||
if (image.format() != QImage::Format_ARGB32 && image.format() != QImage::Format_ARGB32_Premultiplied && image.format() != QImage::Format_RGB32) {
|
||||
image = std_::move(image).convertToFormat(QImage::Format_RGB32);
|
||||
}
|
||||
image.setDevicePixelRatio(cRetinaFactor());
|
||||
return std_::move(image);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ChatBackground::setThemeData(QImage &&themeImage, bool themeTile) {
|
||||
_themeImage = prepareBackgroundImage(std_::move(themeImage));
|
||||
_themeTile = themeTile;
|
||||
}
|
||||
|
||||
void ChatBackground::start() {
|
||||
if (_id == internal::kUninitializedBackground) {
|
||||
setImage(kThemeBackground);
|
||||
}
|
||||
}
|
||||
|
||||
void ChatBackground::setImage(int32 id, QImage &&image) {
|
||||
if (id == kThemeBackground && _themeImage.isNull()) {
|
||||
id = kDefaultBackground;
|
||||
}
|
||||
_id = id;
|
||||
if (_id == kThemeBackground) {
|
||||
_tile = _themeTile;
|
||||
setPreparedImage(QImage(_themeImage));
|
||||
} else {
|
||||
if (_id == kDefaultBackground) {
|
||||
image.load(qsl(":/gui/art/bg.jpg"));
|
||||
} else if (_id == kOldBackground || image.isNull()) {
|
||||
_id = kOldBackground;
|
||||
image.load(qsl(":/gui/art/bg_old.png"));
|
||||
if (cRetina()) {
|
||||
image = image.scaledToWidth(image.width() * 2, Qt::SmoothTransformation);
|
||||
} else if (cScale() != dbisOne) {
|
||||
image = image.scaledToWidth(convertScale(image.width()), Qt::SmoothTransformation);
|
||||
}
|
||||
}
|
||||
Local::writeBackground(_id, (_id == kDefaultBackground || _id == kOldBackground) ? QImage() : image);
|
||||
setPreparedImage(prepareBackgroundImage(std_::move(image)));
|
||||
}
|
||||
t_assert(!_image.isNull());
|
||||
notify(BackgroundUpdate(BackgroundUpdate::Type::New, _tile));
|
||||
}
|
||||
|
||||
void ChatBackground::setPreparedImage(QImage &&image) {
|
||||
App::initColorsFromBackground(image);
|
||||
_image = App::pixmapFromImageInPlace(std_::move(image));
|
||||
}
|
||||
|
||||
int32 ChatBackground::id() const {
|
||||
return _id;
|
||||
}
|
||||
|
||||
const QPixmap &ChatBackground::image() const {
|
||||
return _image;
|
||||
}
|
||||
|
||||
bool ChatBackground::tile() const {
|
||||
return _tile;
|
||||
}
|
||||
|
||||
void ChatBackground::setTile(bool tile) {
|
||||
if (_image.isNull()) {
|
||||
// We should start first, otherwise the default call
|
||||
// to start() will reset this value to _themeTile.
|
||||
start();
|
||||
}
|
||||
if (_tile != tile) {
|
||||
_tile = tile;
|
||||
Local::writeUserSettings();
|
||||
notify(BackgroundUpdate(BackgroundUpdate::Type::Changed, _tile));
|
||||
}
|
||||
}
|
||||
|
||||
ChatBackground *Background() {
|
||||
instance.createIfNull();
|
||||
return &instance->background;
|
||||
}
|
||||
|
||||
bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache) {
|
||||
if (content.size() < 4) {
|
||||
LOG(("Error: Could not load theme from '%1' (%2)").arg(pathRelative).arg(pathAbsolute));
|
||||
return false;
|
||||
}
|
||||
|
||||
instance.createIfNull();
|
||||
if (loadThemeFromCache(content, cache)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!loadTheme(content, cache)) {
|
||||
return false;
|
||||
}
|
||||
Local::writeTheme(pathRelative, pathAbsolute, content, cache);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Unload() {
|
||||
instance.clear();
|
||||
}
|
||||
|
||||
bool LoadFromFile(const QString &path, Instance *out, QByteArray *outContent) {
|
||||
*outContent = readThemeContent(path);
|
||||
if (outContent->size() < 4) {
|
||||
LOG(("Error: Could not load theme from %1").arg(path));
|
||||
return false;
|
||||
}
|
||||
|
||||
return loadTheme(*outContent, out->cached, out);
|
||||
}
|
||||
|
||||
} // namespace Theme
|
||||
} // namespace Window
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop version of Telegram messaging app, see https://telegram.org
|
||||
|
||||
Telegram Desktop is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
It is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
In addition, as a special exception, the copyright holders give permission
|
||||
to link the code of portions of this program with the OpenSSL library.
|
||||
|
||||
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
|
||||
Copyright (c) 2014-2016 John Preston, https://desktop.telegram.org
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace Window {
|
||||
namespace Theme {
|
||||
namespace internal {
|
||||
|
||||
constexpr int32 kUninitializedBackground = -3;
|
||||
|
||||
} // namespace internal
|
||||
|
||||
constexpr int32 kThemeBackground = -2;
|
||||
constexpr int32 kCustomBackground = -1;
|
||||
constexpr int32 kOldBackground = 0;
|
||||
constexpr int32 kDefaultBackground = 21;
|
||||
|
||||
struct Cached {
|
||||
QByteArray colors;
|
||||
QByteArray background;
|
||||
bool tiled = false;
|
||||
int32 paletteChecksum = 0;
|
||||
int32 contentChecksum = 0;
|
||||
};
|
||||
bool Load(const QString &pathRelative, const QString &pathAbsolute, const QByteArray &content, Cached &cache);
|
||||
void Unload();
|
||||
|
||||
struct Instance {
|
||||
style::palette palette;
|
||||
QImage background;
|
||||
Cached cached;
|
||||
bool tiled = false;
|
||||
};
|
||||
bool LoadFromFile(const QString &file, Instance *out, QByteArray *outContent);
|
||||
|
||||
struct BackgroundUpdate {
|
||||
enum class Type {
|
||||
New,
|
||||
Changed,
|
||||
Start,
|
||||
};
|
||||
|
||||
BackgroundUpdate(Type type, bool tiled) : type(type), tiled(tiled) {
|
||||
}
|
||||
Type type;
|
||||
bool tiled;
|
||||
};
|
||||
|
||||
class ChatBackground : public base::Observable<BackgroundUpdate> {
|
||||
public:
|
||||
// This method is allowed to (and should) be called before start().
|
||||
void setThemeData(QImage &&themeImage, bool themeTile);
|
||||
|
||||
// This method is setting the default (themed) image if none was set yet.
|
||||
void start();
|
||||
void setImage(int32 id, QImage &&image = QImage());
|
||||
void setTile(bool tile);
|
||||
void reset() {
|
||||
setImage(kThemeBackground);
|
||||
}
|
||||
|
||||
int32 id() const;
|
||||
const QPixmap &image() const;
|
||||
bool tile() const;
|
||||
|
||||
private:
|
||||
void setPreparedImage(QImage &&image);
|
||||
|
||||
int32 _id = internal::kUninitializedBackground;
|
||||
QPixmap _image;
|
||||
bool _tile = false;
|
||||
|
||||
QImage _themeImage;
|
||||
bool _themeTile = false;
|
||||
|
||||
};
|
||||
|
||||
ChatBackground *Background();
|
||||
|
||||
} // namespace Theme
|
||||
} // namespace Window
|
|
@ -0,0 +1,437 @@
|
|||
/* unzip.h -- IO for uncompress .zip files using zlib
|
||||
Version 1.1, February 14h, 2010
|
||||
part of the MiniZip project - ( http://www.winimage.com/zLibDll/minizip.html )
|
||||
|
||||
Copyright (C) 1998-2010 Gilles Vollant (minizip) ( http://www.winimage.com/zLibDll/minizip.html )
|
||||
|
||||
Modifications of Unzip for Zip64
|
||||
Copyright (C) 2007-2008 Even Rouault
|
||||
|
||||
Modifications for Zip64 support on both zip and unzip
|
||||
Copyright (C) 2009-2010 Mathias Svensson ( http://result42.com )
|
||||
|
||||
For more info read MiniZip_info.txt
|
||||
|
||||
---------------------------------------------------------------------------------
|
||||
|
||||
Condition of use and distribution are the same than zlib :
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
---------------------------------------------------------------------------------
|
||||
|
||||
Changes
|
||||
|
||||
See header of unzip64.c
|
||||
|
||||
*/
|
||||
|
||||
#ifndef _unz64_H
|
||||
#define _unz64_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifndef _ZLIB_H
|
||||
#include "zlib.h"
|
||||
#endif
|
||||
|
||||
#ifndef _ZLIBIOAPI_H
|
||||
#include "ioapi.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_BZIP2
|
||||
#include "bzlib.h"
|
||||
#endif
|
||||
|
||||
#define Z_BZIP2ED 12
|
||||
|
||||
#if defined(STRICTUNZIP) || defined(STRICTZIPUNZIP)
|
||||
/* like the STRICT of WIN32, we define a pointer that cannot be converted
|
||||
from (void*) without cast */
|
||||
typedef struct TagunzFile__ { int unused; } unzFile__;
|
||||
typedef unzFile__ *unzFile;
|
||||
#else
|
||||
typedef voidp unzFile;
|
||||
#endif
|
||||
|
||||
|
||||
#define UNZ_OK (0)
|
||||
#define UNZ_END_OF_LIST_OF_FILE (-100)
|
||||
#define UNZ_ERRNO (Z_ERRNO)
|
||||
#define UNZ_EOF (0)
|
||||
#define UNZ_PARAMERROR (-102)
|
||||
#define UNZ_BADZIPFILE (-103)
|
||||
#define UNZ_INTERNALERROR (-104)
|
||||
#define UNZ_CRCERROR (-105)
|
||||
|
||||
/* tm_unz contain date/time info */
|
||||
typedef struct tm_unz_s
|
||||
{
|
||||
uInt tm_sec; /* seconds after the minute - [0,59] */
|
||||
uInt tm_min; /* minutes after the hour - [0,59] */
|
||||
uInt tm_hour; /* hours since midnight - [0,23] */
|
||||
uInt tm_mday; /* day of the month - [1,31] */
|
||||
uInt tm_mon; /* months since January - [0,11] */
|
||||
uInt tm_year; /* years - [1980..2044] */
|
||||
} tm_unz;
|
||||
|
||||
/* unz_global_info structure contain global data about the ZIPfile
|
||||
These data comes from the end of central dir */
|
||||
typedef struct unz_global_info64_s
|
||||
{
|
||||
ZPOS64_T number_entry; /* total number of entries in
|
||||
the central dir on this disk */
|
||||
uLong size_comment; /* size of the global comment of the zipfile */
|
||||
} unz_global_info64;
|
||||
|
||||
typedef struct unz_global_info_s
|
||||
{
|
||||
uLong number_entry; /* total number of entries in
|
||||
the central dir on this disk */
|
||||
uLong size_comment; /* size of the global comment of the zipfile */
|
||||
} unz_global_info;
|
||||
|
||||
/* unz_file_info contain information about a file in the zipfile */
|
||||
typedef struct unz_file_info64_s
|
||||
{
|
||||
uLong version; /* version made by 2 bytes */
|
||||
uLong version_needed; /* version needed to extract 2 bytes */
|
||||
uLong flag; /* general purpose bit flag 2 bytes */
|
||||
uLong compression_method; /* compression method 2 bytes */
|
||||
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
|
||||
uLong crc; /* crc-32 4 bytes */
|
||||
ZPOS64_T compressed_size; /* compressed size 8 bytes */
|
||||
ZPOS64_T uncompressed_size; /* uncompressed size 8 bytes */
|
||||
uLong size_filename; /* filename length 2 bytes */
|
||||
uLong size_file_extra; /* extra field length 2 bytes */
|
||||
uLong size_file_comment; /* file comment length 2 bytes */
|
||||
|
||||
uLong disk_num_start; /* disk number start 2 bytes */
|
||||
uLong internal_fa; /* internal file attributes 2 bytes */
|
||||
uLong external_fa; /* external file attributes 4 bytes */
|
||||
|
||||
tm_unz tmu_date;
|
||||
} unz_file_info64;
|
||||
|
||||
typedef struct unz_file_info_s
|
||||
{
|
||||
uLong version; /* version made by 2 bytes */
|
||||
uLong version_needed; /* version needed to extract 2 bytes */
|
||||
uLong flag; /* general purpose bit flag 2 bytes */
|
||||
uLong compression_method; /* compression method 2 bytes */
|
||||
uLong dosDate; /* last mod file date in Dos fmt 4 bytes */
|
||||
uLong crc; /* crc-32 4 bytes */
|
||||
uLong compressed_size; /* compressed size 4 bytes */
|
||||
uLong uncompressed_size; /* uncompressed size 4 bytes */
|
||||
uLong size_filename; /* filename length 2 bytes */
|
||||
uLong size_file_extra; /* extra field length 2 bytes */
|
||||
uLong size_file_comment; /* file comment length 2 bytes */
|
||||
|
||||
uLong disk_num_start; /* disk number start 2 bytes */
|
||||
uLong internal_fa; /* internal file attributes 2 bytes */
|
||||
uLong external_fa; /* external file attributes 4 bytes */
|
||||
|
||||
tm_unz tmu_date;
|
||||
} unz_file_info;
|
||||
|
||||
extern int ZEXPORT unzStringFileNameCompare OF ((const char* fileName1,
|
||||
const char* fileName2,
|
||||
int iCaseSensitivity));
|
||||
/*
|
||||
Compare two filename (fileName1,fileName2).
|
||||
If iCaseSenisivity = 1, comparision is case sensitivity (like strcmp)
|
||||
If iCaseSenisivity = 2, comparision is not case sensitivity (like strcmpi
|
||||
or strcasecmp)
|
||||
If iCaseSenisivity = 0, case sensitivity is defaut of your operating system
|
||||
(like 1 on Unix, 2 on Windows)
|
||||
*/
|
||||
|
||||
|
||||
extern unzFile ZEXPORT unzOpen OF((const char *path));
|
||||
extern unzFile ZEXPORT unzOpen64 OF((const void *path));
|
||||
/*
|
||||
Open a Zip file. path contain the full pathname (by example,
|
||||
on a Windows XP computer "c:\\zlib\\zlib113.zip" or on an Unix computer
|
||||
"zlib/zlib113.zip".
|
||||
If the zipfile cannot be opened (file don't exist or in not valid), the
|
||||
return value is NULL.
|
||||
Else, the return value is a unzFile Handle, usable with other function
|
||||
of this unzip package.
|
||||
the "64" function take a const void* pointer, because the path is just the
|
||||
value passed to the open64_file_func callback.
|
||||
Under Windows, if UNICODE is defined, using fill_fopen64_filefunc, the path
|
||||
is a pointer to a wide unicode string (LPCTSTR is LPCWSTR), so const char*
|
||||
does not describe the reality
|
||||
*/
|
||||
|
||||
|
||||
extern unzFile ZEXPORT unzOpen2 OF((const char *path,
|
||||
zlib_filefunc_def* pzlib_filefunc_def));
|
||||
/*
|
||||
Open a Zip file, like unzOpen, but provide a set of file low level API
|
||||
for read/write the zip file (see ioapi.h)
|
||||
*/
|
||||
|
||||
extern unzFile ZEXPORT unzOpen2_64 OF((const void *path,
|
||||
zlib_filefunc64_def* pzlib_filefunc_def));
|
||||
/*
|
||||
Open a Zip file, like unz64Open, but provide a set of file low level API
|
||||
for read/write the zip file (see ioapi.h)
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzClose OF((unzFile file));
|
||||
/*
|
||||
Close a ZipFile opened with unzOpen.
|
||||
If there is files inside the .Zip opened with unzOpenCurrentFile (see later),
|
||||
these files MUST be closed with unzCloseCurrentFile before call unzClose.
|
||||
return UNZ_OK if there is no problem. */
|
||||
|
||||
extern int ZEXPORT unzGetGlobalInfo OF((unzFile file,
|
||||
unz_global_info *pglobal_info));
|
||||
|
||||
extern int ZEXPORT unzGetGlobalInfo64 OF((unzFile file,
|
||||
unz_global_info64 *pglobal_info));
|
||||
/*
|
||||
Write info about the ZipFile in the *pglobal_info structure.
|
||||
No preparation of the structure is needed
|
||||
return UNZ_OK if there is no problem. */
|
||||
|
||||
|
||||
extern int ZEXPORT unzGetGlobalComment OF((unzFile file,
|
||||
char *szComment,
|
||||
uLong uSizeBuf));
|
||||
/*
|
||||
Get the global comment string of the ZipFile, in the szComment buffer.
|
||||
uSizeBuf is the size of the szComment buffer.
|
||||
return the number of byte copied or an error code <0
|
||||
*/
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
/* Unzip package allow you browse the directory of the zipfile */
|
||||
|
||||
extern int ZEXPORT unzGoToFirstFile OF((unzFile file));
|
||||
/*
|
||||
Set the current file of the zipfile to the first file.
|
||||
return UNZ_OK if there is no problem
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzGoToNextFile OF((unzFile file));
|
||||
/*
|
||||
Set the current file of the zipfile to the next file.
|
||||
return UNZ_OK if there is no problem
|
||||
return UNZ_END_OF_LIST_OF_FILE if the actual file was the latest.
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzLocateFile OF((unzFile file,
|
||||
const char *szFileName,
|
||||
int iCaseSensitivity));
|
||||
/*
|
||||
Try locate the file szFileName in the zipfile.
|
||||
For the iCaseSensitivity signification, see unzStringFileNameCompare
|
||||
|
||||
return value :
|
||||
UNZ_OK if the file is found. It becomes the current file.
|
||||
UNZ_END_OF_LIST_OF_FILE if the file is not found
|
||||
*/
|
||||
|
||||
|
||||
/* ****************************************** */
|
||||
/* Ryan supplied functions */
|
||||
/* unz_file_info contain information about a file in the zipfile */
|
||||
typedef struct unz_file_pos_s
|
||||
{
|
||||
uLong pos_in_zip_directory; /* offset in zip file directory */
|
||||
uLong num_of_file; /* # of file */
|
||||
} unz_file_pos;
|
||||
|
||||
extern int ZEXPORT unzGetFilePos(
|
||||
unzFile file,
|
||||
unz_file_pos* file_pos);
|
||||
|
||||
extern int ZEXPORT unzGoToFilePos(
|
||||
unzFile file,
|
||||
unz_file_pos* file_pos);
|
||||
|
||||
typedef struct unz64_file_pos_s
|
||||
{
|
||||
ZPOS64_T pos_in_zip_directory; /* offset in zip file directory */
|
||||
ZPOS64_T num_of_file; /* # of file */
|
||||
} unz64_file_pos;
|
||||
|
||||
extern int ZEXPORT unzGetFilePos64(
|
||||
unzFile file,
|
||||
unz64_file_pos* file_pos);
|
||||
|
||||
extern int ZEXPORT unzGoToFilePos64(
|
||||
unzFile file,
|
||||
const unz64_file_pos* file_pos);
|
||||
|
||||
/* ****************************************** */
|
||||
|
||||
extern int ZEXPORT unzGetCurrentFileInfo64 OF((unzFile file,
|
||||
unz_file_info64 *pfile_info,
|
||||
char *szFileName,
|
||||
uLong fileNameBufferSize,
|
||||
void *extraField,
|
||||
uLong extraFieldBufferSize,
|
||||
char *szComment,
|
||||
uLong commentBufferSize));
|
||||
|
||||
extern int ZEXPORT unzGetCurrentFileInfo OF((unzFile file,
|
||||
unz_file_info *pfile_info,
|
||||
char *szFileName,
|
||||
uLong fileNameBufferSize,
|
||||
void *extraField,
|
||||
uLong extraFieldBufferSize,
|
||||
char *szComment,
|
||||
uLong commentBufferSize));
|
||||
/*
|
||||
Get Info about the current file
|
||||
if pfile_info!=NULL, the *pfile_info structure will contain somes info about
|
||||
the current file
|
||||
if szFileName!=NULL, the filemane string will be copied in szFileName
|
||||
(fileNameBufferSize is the size of the buffer)
|
||||
if extraField!=NULL, the extra field information will be copied in extraField
|
||||
(extraFieldBufferSize is the size of the buffer).
|
||||
This is the Central-header version of the extra field
|
||||
if szComment!=NULL, the comment string of the file will be copied in szComment
|
||||
(commentBufferSize is the size of the buffer)
|
||||
*/
|
||||
|
||||
|
||||
/** Addition for GDAL : START */
|
||||
|
||||
extern ZPOS64_T ZEXPORT unzGetCurrentFileZStreamPos64 OF((unzFile file));
|
||||
|
||||
/** Addition for GDAL : END */
|
||||
|
||||
|
||||
/***************************************************************************/
|
||||
/* for reading the content of the current zipfile, you can open it, read data
|
||||
from it, and close it (you can close it before reading all the file)
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFile OF((unzFile file));
|
||||
/*
|
||||
Open for reading data the current file in the zipfile.
|
||||
If there is no error, the return value is UNZ_OK.
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFilePassword OF((unzFile file,
|
||||
const char* password));
|
||||
/*
|
||||
Open for reading data the current file in the zipfile.
|
||||
password is a crypting password
|
||||
If there is no error, the return value is UNZ_OK.
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFile2 OF((unzFile file,
|
||||
int* method,
|
||||
int* level,
|
||||
int raw));
|
||||
/*
|
||||
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
|
||||
if raw==1
|
||||
*method will receive method of compression, *level will receive level of
|
||||
compression
|
||||
note : you can set level parameter as NULL (if you did not want known level,
|
||||
but you CANNOT set method parameter as NULL
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzOpenCurrentFile3 OF((unzFile file,
|
||||
int* method,
|
||||
int* level,
|
||||
int raw,
|
||||
const char* password));
|
||||
/*
|
||||
Same than unzOpenCurrentFile, but open for read raw the file (not uncompress)
|
||||
if raw==1
|
||||
*method will receive method of compression, *level will receive level of
|
||||
compression
|
||||
note : you can set level parameter as NULL (if you did not want known level,
|
||||
but you CANNOT set method parameter as NULL
|
||||
*/
|
||||
|
||||
|
||||
extern int ZEXPORT unzCloseCurrentFile OF((unzFile file));
|
||||
/*
|
||||
Close the file in zip opened with unzOpenCurrentFile
|
||||
Return UNZ_CRCERROR if all the file was read but the CRC is not good
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzReadCurrentFile OF((unzFile file,
|
||||
voidp buf,
|
||||
unsigned len));
|
||||
/*
|
||||
Read bytes from the current file (opened by unzOpenCurrentFile)
|
||||
buf contain buffer where data must be copied
|
||||
len the size of buf.
|
||||
|
||||
return the number of byte copied if somes bytes are copied
|
||||
return 0 if the end of file was reached
|
||||
return <0 with error code if there is an error
|
||||
(UNZ_ERRNO for IO error, or zLib error for uncompress error)
|
||||
*/
|
||||
|
||||
extern z_off_t ZEXPORT unztell OF((unzFile file));
|
||||
|
||||
extern ZPOS64_T ZEXPORT unztell64 OF((unzFile file));
|
||||
/*
|
||||
Give the current position in uncompressed data
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzeof OF((unzFile file));
|
||||
/*
|
||||
return 1 if the end of file was reached, 0 elsewhere
|
||||
*/
|
||||
|
||||
extern int ZEXPORT unzGetLocalExtrafield OF((unzFile file,
|
||||
voidp buf,
|
||||
unsigned len));
|
||||
/*
|
||||
Read extra field from the current file (opened by unzOpenCurrentFile)
|
||||
This is the local-header version of the extra field (sometimes, there is
|
||||
more info in the local-header version than in the central-header)
|
||||
|
||||
if buf==NULL, it return the size of the local extra field
|
||||
|
||||
if buf!=NULL, len is the size of the buffer, the extra header is copied in
|
||||
buf.
|
||||
the return value is the number of bytes copied in buf, or (if <0)
|
||||
the error code
|
||||
*/
|
||||
|
||||
/***************************************************************************/
|
||||
|
||||
/* Get the current file offset */
|
||||
extern ZPOS64_T ZEXPORT unzGetOffset64 (unzFile file);
|
||||
extern uLong ZEXPORT unzGetOffset (unzFile file);
|
||||
|
||||
/* Set the current file offset */
|
||||
extern int ZEXPORT unzSetOffset64 (unzFile file, ZPOS64_T pos);
|
||||
extern int ZEXPORT unzSetOffset (unzFile file, uLong pos);
|
||||
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _unz64_H */
|
|
@ -34,6 +34,7 @@
|
|||
'minizip_loc': '<(third_party_loc)/minizip',
|
||||
'sp_media_key_tap_loc': '<(third_party_loc)/SPMediaKeyTap',
|
||||
'style_files': [
|
||||
'<(res_loc)/colors.palette',
|
||||
'<(res_loc)/basic.style',
|
||||
'<(res_loc)/basic_types.style',
|
||||
'<(src_loc)/boxes/boxes.style',
|
||||
|
@ -213,6 +214,9 @@
|
|||
'<(src_loc)/core/observer.cpp',
|
||||
'<(src_loc)/core/observer.h',
|
||||
'<(src_loc)/core/ordered_set.h',
|
||||
'<(src_loc)/core/parse_helper.cpp',
|
||||
'<(src_loc)/core/parse_helper.h',
|
||||
'<(src_loc)/core/qthelp_regex.h',
|
||||
'<(src_loc)/core/qthelp_url.cpp',
|
||||
'<(src_loc)/core/qthelp_url.h',
|
||||
'<(src_loc)/core/runtime_composer.cpp',
|
||||
|
@ -226,6 +230,7 @@
|
|||
'<(src_loc)/core/vector_of_moveable.h',
|
||||
'<(src_loc)/core/version.h',
|
||||
'<(src_loc)/core/virtual_method.h',
|
||||
'<(src_loc)/core/zlib_help.h',
|
||||
'<(src_loc)/data/data_abstract_structure.cpp',
|
||||
'<(src_loc)/data/data_abstract_structure.h',
|
||||
'<(src_loc)/data/data_drafts.cpp',
|
||||
|
@ -536,8 +541,6 @@
|
|||
'<(src_loc)/ui/scrollarea.h',
|
||||
'<(src_loc)/ui/twidget.cpp',
|
||||
'<(src_loc)/ui/twidget.h',
|
||||
'<(src_loc)/window/chat_background.cpp',
|
||||
'<(src_loc)/window/chat_background.h',
|
||||
'<(src_loc)/window/main_window.cpp',
|
||||
'<(src_loc)/window/main_window.h',
|
||||
'<(src_loc)/window/notifications_manager.cpp',
|
||||
|
@ -554,6 +557,8 @@
|
|||
'<(src_loc)/window/slide_animation.h',
|
||||
'<(src_loc)/window/top_bar_widget.cpp',
|
||||
'<(src_loc)/window/top_bar_widget.h',
|
||||
'<(src_loc)/window/window_theme.cpp',
|
||||
'<(src_loc)/window/window_theme.h',
|
||||
|
||||
'<(sp_media_key_tap_loc)/SPMediaKeyTap.m',
|
||||
'<(sp_media_key_tap_loc)/SPMediaKeyTap.h',
|
||||
|
|
|
@ -50,6 +50,33 @@
|
|||
'<@(qrc_files)',
|
||||
],
|
||||
'message': 'Updating dependent qrc files..',
|
||||
}, {
|
||||
'action_name': 'codegen_palette',
|
||||
'inputs': [
|
||||
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/update_dependent_styles.timestamp',
|
||||
'<(res_loc)/colors.palette',
|
||||
],
|
||||
'outputs': [
|
||||
'<(SHARED_INTERMEDIATE_DIR)/styles/palette.h',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/styles/palette.cpp',
|
||||
],
|
||||
'action': [
|
||||
'<(PRODUCT_DIR)/codegen_style<(exe_ext)',
|
||||
'-I<(res_loc)', '-I<(src_loc)',
|
||||
'-o<(SHARED_INTERMEDIATE_DIR)/styles',
|
||||
'-w<(PRODUCT_DIR)/../..',
|
||||
|
||||
# GYP/Ninja bug workaround: if we specify just <(RULE_INPUT_PATH)
|
||||
# the <(RULE_INPUT_ROOT) variables won't be available in Ninja,
|
||||
# and the 'message' will be just 'codegen_style-ing .style..'
|
||||
# Looks like the using the <(RULE_INPUT_ROOT) here "exports" it
|
||||
# for using in the 'message' field.
|
||||
|
||||
'<(res_loc)/colors.palette',
|
||||
],
|
||||
'message': 'codegen_palette-ing colors..',
|
||||
'process_outputs_as_sources': 1,
|
||||
}, {
|
||||
'action_name': 'codegen_lang',
|
||||
'inputs': [
|
||||
|
|