Use lib_ui from submodule.

This commit is contained in:
John Preston 2019-09-17 19:13:12 +03:00
parent 1b89348d89
commit 246c45ce0e
255 changed files with 122 additions and 61372 deletions

6
.gitmodules vendored
View File

@ -31,3 +31,9 @@
[submodule "Telegram/ThirdParty/gyp_helpers"]
path = Telegram/ThirdParty/gyp_helpers
url = https://github.com/desktop-app/gyp_helpers.git
[submodule "Telegram/ThirdParty/codegen"]
path = Telegram/ThirdParty/codegen
url = https://github.com/desktop-app/codegen.git
[submodule "Telegram/ThirdParty/lib_ui"]
path = Telegram/ThirdParty/lib_ui
url = https://github.com/desktop-app/lib_ui.git

View File

@ -1,303 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "colors.palette";
TextPalette {
linkFg: color;
monoFg: color;
selectBg: color;
selectFg: color;
selectLinkFg: color;
selectMonoFg: color;
selectOverlay: color;
}
TextStyle {
font: font;
linkFont: font;
linkFontOver: font;
lineHeight: pixels;
}
semibold: "Open Sans Semibold";
fsize: 13px;
normalFont: font(fsize);
semiboldFont: font(fsize semibold);
boxFontSize: 14px;
boxTextFont: font(boxFontSize);
emojiImgSize: 18px; // exceptional value for retina
emojiSize: 18px;
emojiPadding: 1px;
lineWidth: 1px;
defaultTextPalette: TextPalette {
linkFg: windowActiveTextFg;
monoFg: msgInMonoFg;
selectBg: msgInBgSelected;
selectFg: transparent; // use painter current pen instead
selectLinkFg: historyLinkInFgSelected;
selectMonoFg: msgInMonoFgSelected;
selectOverlay: msgSelectOverlay;
}
defaultTextStyle: TextStyle {
font: normalFont;
linkFont: normalFont;
linkFontOver: font(fsize underline);
lineHeight: 0px;
}
semiboldTextStyle: TextStyle(defaultTextStyle) {
font: semiboldFont;
linkFont: semiboldFont;
linkFontOver: font(fsize semibold underline);
}
shadowToggleDuration: 200;
slideDuration: 240;
slideShift: 100px;
slideShadow: icon {{ "slide_shadow", slideFadeOutShadowFg }};
slideWrapDuration: 150;
fadeWrapDuration: 200;
linkCropLimit: 360px;
linkFont: normalFont;
linkOverFont: font(fsize underline);
roundRadiusLarge: 6px;
roundRadiusSmall: 3px;
dateRadius: roundRadiusLarge;
buttonRadius: roundRadiusSmall;
setLittleSkip: 9px;
noContactsHeight: 100px;
noContactsFont: font(fsize);
noContactsColor: windowSubTextFg;
activeFadeInDuration: 500;
activeFadeOutDuration: 3000;
msgMaxWidth: 430px;
msgFont: font(fsize);
msgNameFont: semiboldFont;
msgNameStyle: semiboldTextStyle;
msgServiceFont: semiboldFont;
msgServiceNameFont: semiboldFont;
msgServicePhotoWidth: 100px;
msgDateFont: font(13px);
msgMinWidth: 160px;
msgPhotoSize: 33px;
msgPhotoSkip: 40px;
msgPadding: margins(13px, 7px, 13px, 8px);
msgMargin: margins(16px, 6px, 56px, 2px);
msgMarginTopAttached: 1px;
msgLnkPadding: 2px; // for media open / save links
msgShadow: 2px;
msgReplyPadding: margins(6px, 6px, 11px, 6px);
msgReplyBarPos: point(1px, 0px);
msgReplyBarSize: size(2px, 36px);
msgReplyBarSkip: 10px;
msgServicePadding: margins(12px, 3px, 12px, 4px);
msgServiceMargin: margins(10px, 10px, 10px, 2px);
msgDateSpace: 12px;
msgDateDelta: point(2px, 5px);
msgDateImgDelta: 4px;
msgDateImgPadding: point(8px, 2px);
msgDateImgCheckSpace: 4px;
messageTextStyle: defaultTextStyle;
msgDateTextStyle: defaultTextStyle;
serviceTextPalette: TextPalette(defaultTextPalette) {
linkFg: msgServiceFg;
monoFg: msgServiceFg;
selectBg: msgServiceBgSelected;
selectFg: msgServiceFg;
selectLinkFg: msgServiceFg;
selectMonoFg: msgServiceFg;
selectOverlay: msgServiceBgSelected;
}
serviceTextStyle: TextStyle(defaultTextStyle) {
font: msgServiceFont;
linkFont: msgServiceFont;
linkFontOver: font(fsize semibold underline);
}
inTextPalette: TextPalette(defaultTextPalette) {
linkFg: historyLinkInFg;
monoFg: msgInMonoFg;
selectBg: msgInBgSelected;
selectFg: historyTextInFgSelected;
selectLinkFg: historyLinkInFgSelected;
selectMonoFg: msgInMonoFgSelected;
selectOverlay: msgSelectOverlay;
}
inTextPaletteSelected: TextPalette(inTextPalette) {
linkFg: historyLinkInFgSelected;
monoFg: msgInMonoFgSelected;
}
outTextPalette: TextPalette(defaultTextPalette) {
linkFg: historyLinkOutFg;
monoFg: msgOutMonoFg;
selectBg: msgOutBgSelected;
selectFg: historyTextOutFgSelected;
selectLinkFg: historyLinkOutFgSelected;
selectMonoFg: msgOutMonoFgSelected;
selectOverlay: msgSelectOverlay;
}
outTextPaletteSelected: TextPalette(outTextPalette) {
linkFg: historyLinkOutFgSelected;
monoFg: msgOutMonoFgSelected;
}
fwdTextStyle: TextStyle(semiboldTextStyle) {
linkFontOver: semiboldFont;
}
inFwdTextPalette: TextPalette(defaultTextPalette) {
linkFg: msgInServiceFg;
}
outFwdTextPalette: TextPalette(defaultTextPalette) {
linkFg: msgOutServiceFg;
}
inFwdTextPaletteSelected: TextPalette(defaultTextPalette) {
linkFg: msgInServiceFgSelected;
}
outFwdTextPaletteSelected: TextPalette(defaultTextPalette) {
linkFg: msgOutServiceFgSelected;
}
inReplyTextPalette: TextPalette(inTextPalette) {
linkFg: msgInDateFg;
}
inReplyTextPaletteSelected: TextPalette(inTextPaletteSelected) {
linkFg: msgInDateFgSelected;
}
outReplyTextPalette: TextPalette(outTextPalette) {
linkFg: msgOutDateFg;
}
outReplyTextPaletteSelected: TextPalette(outTextPaletteSelected) {
linkFg: msgOutDateFgSelected;
}
imgReplyTextPalette: TextPalette(defaultTextPalette) {
linkFg: msgImgReplyBarColor;
}
inSemiboldPalette: TextPalette(inTextPalette) {
linkFg: msgInServiceFg;
selectFg: msgInServiceFgSelected;
selectLinkFg: msgInServiceFgSelected;
}
outSemiboldPalette: TextPalette(outTextPalette) {
linkFg: msgOutServiceFg;
selectFg: msgOutServiceFgSelected;
selectLinkFg: msgOutServiceFgSelected;
}
historyComposeAreaPalette: TextPalette(defaultTextPalette) {
linkFg: historyComposeAreaFgService;
}
mediaCaptionSkip: 5px;
mediaInBubbleSkip: 5px;
mediaThumbSize: 48px;
mediaNameTop: 3px;
mediaDetailsShift: 3px;
mediaUnreadSize: 7px;
mediaUnreadSkip: 5px;
mediaUnreadTop: 6px;
mediaInPalette: TextPalette(defaultTextPalette) {
linkFg: mediaInFg;
}
mediaInPaletteSelected: TextPalette(defaultTextPalette) {
linkFg: mediaInFgSelected;
}
textRectMargins: margins(-2px, -1px, -2px, -1px);
searchedBarHeight: 32px;
searchedBarFont: normalFont;
searchedBarPosition: point(17px, 7px);
smallCloseIcon: icon {{ "simple_close", smallCloseIconFg }};
smallCloseIconOver: icon {{ "simple_close", smallCloseIconFgOver }};
dialogsForwardCancelIcon: icon {{ "simple_close", dialogsForwardFg }};
emojiTextFont: font(15px);
emojiReplaceWidth: 52px;
emojiReplaceHeight: 56px;
emojiReplaceInnerHeight: 42px;
emojiReplacePadding: 14px;
dragFont: font(28px semibold);
dragSubfont: font(20px semibold);
dragColor: windowSubTextFg;
dragDropColor: windowActiveTextFg;
dragMargin: margins(0px, 10px, 0px, 10px);
dragPadding: margins(20px, 10px, 20px, 10px);
dragHeight: 72px;
radialSize: size(50px, 50px);
radialLine: 3px;
radialDuration: 350;
radialPeriod: 3000;
youtubeIcon: icon {
{ "media_youtube_play_bg", youtubePlayIconBg },
{ "media_youtube_play", youtubePlayIconFg, point(24px, 12px) },
};
videoIcon: icon {
{ "media_video_play_bg", videoPlayIconBg },
{ "media_video_play", videoPlayIconFg, point(12px, 12px) },
};
locationSize: size(320px, 240px);
mediaPlayerSuppressDuration: 150;
botDescSkip: 8px;
inlineResultsLeft: 11px;
inlineResultsSkip: 3px;
inlineMediaHeight: 96px;
inlineThumbSize: 64px;
inlineThumbSkip: 10px;
inlineTitleFg: windowFg;
inlineDescriptionFg: windowSubTextFg;
inlineRowMargin: 6px;
inlineRowBorder: 1px;
inlineRowBorderFg: shadowFg;
inlineRowFileNameTop: 2px;
inlineRowFileDescriptionTop: 23px;
inlineResultsMinWidth: 48px;
inlineDurationMargin: 3px;
toastTextStyle: defaultTextStyle;
toastMaxWidth: 480px;
toastMinMargin: 13px;
toastPadding: margins(19px, 13px, 19px, 12px);
toastFadeInDuration: 200;
toastFadeOutDuration: 1000;
historyReplyCancelIcon: icon {{ "box_button_close", historyReplyCancelFg }};
historyReplyCancelIconOver: icon {{ "box_button_close", historyReplyCancelFgOver }};
boxTitleCloseIcon: icon {{ "box_button_close", boxTitleCloseFg }};
boxTitleCloseIconOver: icon {{ "box_button_close", boxTitleCloseFgOver }};
notifyFadeRight: icon {{ "fade_horizontal", notificationBg }};
stickerIconLeft: icon {{ "fade_horizontal-flip_horizontal", emojiPanCategories }};
stickerIconRight: icon {{ "fade_horizontal", emojiPanCategories }};
emojiSuggestionsFadeLeft: icon {{ "fade_horizontal-flip_horizontal", boxBg }};
emojiSuggestionsFadeRight: icon {{ "fade_horizontal", boxBg }};
transparentPlaceholderSize: 4px;

View File

@ -1,559 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
// basic
windowBg: #ffffff; // white: fallback for background
windowFg: #000000; // black: fallback for text
windowBgOver: #f1f1f1; // light gray: fallback for background with mouse over
windowBgRipple: #e5e5e5; // darker gray: fallback for ripple effect
windowFgOver: windowFg; // black: fallback for text with mouse over
windowSubTextFg: #999999; // gray: fallback for additional text
windowSubTextFgOver: #919191; // darker gray: fallback for additional text with mouse over
windowBoldFg: #222222; // dark gray: fallback for bold text
windowBoldFgOver: #222222; // dark gray: fallback for bold text with mouse over
windowBgActive: #40a7e3; // bright blue: fallback for blue filled active areas
windowFgActive: #ffffff; // white: fallback for text on active areas
windowActiveTextFg: #168acd; // online blue: fallback for active text like online status
windowShadowFg: #000000; // black: fallback for shadow
windowShadowFgFallback: #f1f1f1; // gray: fallback for shadow without opacity
shadowFg: #00000018; // most shadows (including opacity)
slideFadeOutBg: #0000003c; // slide animation (chat to profile) fade out filling
slideFadeOutShadowFg: windowShadowFg; // slide animation (chat to profile) fade out right section shadow
imageBg: #000000; // image background fallback (when photo size is less than minimum allowed)
imageBgTransparent: #ffffff; // image background when displaying an image with opacity where no opacity is needed
// widgets
activeButtonBg: windowBgActive; // default active button background
activeButtonBgOver: #39a5db; // default active button background with mouse over
activeButtonBgRipple: #2095d0; // default active button ripple effect
activeButtonFg: windowFgActive; // default active button text
activeButtonFgOver: activeButtonFg; // default active button text with mouse over
activeButtonSecondaryFg: #cceeff; // default active button additional text (selected messages counter in forward / delete buttons)
activeButtonSecondaryFgOver: activeButtonSecondaryFg; // default active button additional text with mouse over
activeLineFg: #37a1de; // default active line (like code input field bottom border when you log in and field is focused)
activeLineFgError: #e48383; // default active line for error state (like code input field bottom border when you log in and you've entered incorrect code)
lightButtonBg: windowBg; // default light button background (like buttons in boxes)
lightButtonBgOver: #e3f1fa; // default light button background with mouse over
lightButtonBgRipple: #c9e4f6; // default light button ripple effect
lightButtonFg: windowActiveTextFg; // default light button text
lightButtonFgOver: lightButtonFg; // default light button text with mouse over
attentionButtonFg: #d14e4e; // default attention button text (like confirm button on log out)
attentionButtonFgOver: #d14e4e; // default attention button text with mouse over
attentionButtonBgOver: #fcdfde; // default attention button background with mouse over
attentionButtonBgRipple: #f4c3c2; // default attention button ripple effect
menuBg: windowBg; // default popup menu background
menuBgOver: windowBgOver; // default popup menu item background with mouse over
menuBgRipple: windowBgRipple; // default popup menu item ripple effect
menuIconFg: #a8a8a8; // default popup menu item icon (like main menu)
menuIconFgOver: #999999; // default popup menu item icon with mouse over
menuSubmenuArrowFg: #373737; // default popup menu submenu arrow icon (like in message field context menu in case of RTL system language)
menuFgDisabled: #cccccc; // default popup menu item disabled text (like unavailable items in message field context menu)
menuSeparatorFg: #f1f1f1; // default popup menu separator (like in message field context menu)
scrollBarBg: #00000053; // default scroll bar current rectangle, the bar itself (like in chats list)
scrollBarBgOver: #0000007a; // default scroll bar current rectangle with mouse over it
scrollBg: #0000001a; // default scroll bar background
scrollBgOver: #0000002c; // default scroll bar background with mouse over the scroll bar
smallCloseIconFg: #c7c7c7; // small X icon (like in Show all sessions box to the right for sessions termination)
smallCloseIconFgOver: #a3a3a3; // small X icon with mouse over
radialFg: windowFgActive; // default radial loader line (like in Media Viewer when loading a photo)
radialBg: #00000056; // default radial loader background (like in Media Viewer when loading a photo)
placeholderFg: windowSubTextFg; // default input field placeholder when field is not focused (like in phone input field when you log in)
placeholderFgActive: #aaaaaa; // default input field placeholder when field is focused
inputBorderFg: #e0e0e0; // default input field bottom border (like in code input field when you log in and field is not focused)
filterInputBorderFg: #54c3f3; // default rounded input field border (like in chats list search field when field is focused)
filterInputActiveBg: windowBg; // default rounded input field active background (like in chats list search field when field is focused)
filterInputInactiveBg: windowBgOver; // default rounded input field inactive background (like in chats list search field when field is inactive)
checkboxFg: #b3b3b3; // default unchecked checkbox rounded rectangle
botKbBg: menuBgOver; // bot keyboard button background
botKbDownBg: menuBgRipple; // bot keyboard button ripple effect
botKbColor: windowBoldFgOver; // bot keyboard button text
sliderBgInactive: #e1eaef; // default slider not active bar (like in Settings when you choose interface scale or custom notifications count)
sliderBgActive: windowBgActive; // default slider active bar (like in Settings when you choose interface scale or custom notifications count)
tooltipBg: #eef2f5; // tooltip background (like when you put mouse over the message timestamp and wait)
tooltipFg: #5d6c80; // tooltip text
tooltipBorderFg: #c9d1db; // tooltip border
// custom title bar for Windows and macOS
titleShadow: #00000003; // one pixel line shadow at the bottom of custom window title
titleBg: windowBgOver; // custom window title background when window is inactive
titleBgActive: titleBg; // custom window title background when window is active
titleButtonBg: titleBg; // custom window title minimize/maximize/restore button background when window is inactive (Windows only)
titleButtonFg: #ababab; // custom window title minimize/maximize/restore button icon when window is inactive (Windows only)
titleButtonBgOver: #e5e5e5; // custom window title minimize/maximize/restore button background with mouse over when window is inactive (Windows only)
titleButtonFgOver: #9a9a9a; // custom window title minimize/maximize/restore button icon with mouse over when window is inactive (Windows only)
titleButtonBgActive: titleButtonBg; // custom window title minimize/maximize/restore button background when window is active (Windows only)
titleButtonFgActive: titleButtonFg; // custom window title minimize/maximize/restore button icon when window is active (Windows only)
titleButtonBgActiveOver: titleButtonBgOver; // custom window title minimize/maximize/restore button background with mouse over when window is active (Windows only)
titleButtonFgActiveOver: titleButtonFgOver; // custom window title minimize/maximize/restore button icon with mouse over when window is active (Windows only)
titleButtonCloseBg: titleButtonBg; // custom window title close button background when window is inactive (Windows only)
titleButtonCloseFg: titleButtonFg; // custom window title close button icon when window is inactive (Windows only)
titleButtonCloseBgOver: #e81123; // custom window title close button background with mouse over when window is inactive (Windows only)
titleButtonCloseFgOver: windowFgActive; // custom window title close button icon with mouse over when window is inactive (Windows only)
titleButtonCloseBgActive: titleButtonCloseBg; // custom window title close button background when window is active (Windows only)
titleButtonCloseFgActive: titleButtonCloseFg; // custom window title close button icon when window is active (Windows only)
titleButtonCloseBgActiveOver: titleButtonCloseBgOver; // custom window title close button background with mouse over when window is active (Windows only)
titleButtonCloseFgActiveOver: titleButtonCloseFgOver; // custom window title close button icon with mouse over when window is active (Windows only)
titleFg: #acacac; // custom window title text when window is inactive (macOS only)
titleFgActive: #3e3c3e; // custom window title text when window is active (macOS only)
// tray icon
trayCounterBg: #f23c34; // tray icon counter background
trayCounterBgMute: #888888; // tray icon counter background if all unread messages are muted
trayCounterFg: #ffffff; // tray icon counter text
trayCounterBgMacInvert: #ffffff; // tray icon counter background when tray icon is pressed or when dark theme of macOS is used (macOS only)
trayCounterFgMacInvert: #ffffff01; // tray icon counter text when tray icon is pressed or when dark theme of macOS is used (macOS only)
// layers
layerBg: #0000007f; // box and main menu background layer fade
cancelIconFg: menuIconFg; // default for settings close icon and box search cancel icon
cancelIconFgOver: menuIconFgOver; // default for settings close icon and box search cancel icon with mouse over
// boxes
boxBg: windowBg; // box background
boxTextFg: windowFg; // box text
boxTextFgGood: #4ab44a; // accepted box text (like when choosing username that is not occupied)
boxTextFgError: #d84d4d; // rejecting box text (like when choosing username that is occupied)
boxTitleFg: #404040; // box title text
boxSearchBg: boxBg; // box search field background (like in contacts box)
boxTitleAdditionalFg: #808080; // box title additional text (like in create group box when you see chosen members count)
boxTitleCloseFg: cancelIconFg; // settings close icon and box search cancel icon (like in contacts box)
boxTitleCloseFgOver: cancelIconFgOver; // settings close icon and box search cancel icon (like in contacts box) with mouse over
//boxSearchCancelIconFg: cancelIconFg; // search cancel X button icon (like in contacts box) (not implemented yet)
//boxSearchCancelIconFgOver: cancelIconFgOver; // search cancel X button icon with mouse over (not implemented yet)
membersAboutLimitFg: windowSubTextFgOver; // text in channel members box about the limit (max 200 last members are shown)
contactsBg: windowBg; // contacts (and some other) box row background
contactsBgOver: windowBgOver; // contacts (and some other) box row background with mouse over
contactsNameFg: boxTextFg; // contacts (and some other) box row name text
contactsStatusFg: windowSubTextFg; // contacts (and some other) box row additional text (like last seen stamp)
contactsStatusFgOver: windowSubTextFgOver; // contacts (and some other) box row additional text (like last seen stamp) with mouse over
contactsStatusFgOnline: windowActiveTextFg; // contacts (and some other) box row active additional text (like online status)
photoCropFadeBg: layerBg; // avatar crop box fade background (when choosing a new photo in Settings or for a group)
photoCropPointFg: #ffffff7f; // avatar crop box corner rectangles (when choosing a new photo in Settings or for a group)
callArrowFg: #2ab32a | boxTextFgGood; // received phone call arrow (in calls list box)
callArrowMissedFg: #dd5b4a | boxTextFgError; // missed phone call arrow (in calls list box)
// intro
introBg: windowBg; // login background
introTitleFg: windowBoldFg; // login title text
introDescriptionFg: windowSubTextFg; // login description text
introErrorFg: windowSubTextFg; // login error text (like when providing a wrong log in code)
introCoverTopBg: #0f89d0; // intro gradient top (from)
introCoverBottomBg: #39b0f0; // intro gradient bottom (to)
introCoverIconsFg: #5ec6ff; // intro cloud graphics
introCoverPlaneTrace: #5ec6ff69; // intro plane traces
introCoverPlaneInner: #c6d8e8; // intro plane part
introCoverPlaneOuter: #a1bed4; // intro plane part
introCoverPlaneTop: #ffffff; // intro plane part
// dialogs
dialogsMenuIconFg: menuIconFg; // main menu and lock telegram icon
dialogsMenuIconFgOver: menuIconFgOver; // main menu and lock telegram icon with mouse over
dialogsBg: windowBg; // chat list background
dialogsNameFg: windowBoldFg; // chat list name text
dialogsChatIconFg: dialogsNameFg; // chat list group or channel icon
dialogsDateFg: windowSubTextFg; // chat list date text
dialogsTextFg: windowSubTextFg; // chat list message text
dialogsTextFgService: windowActiveTextFg; // chat list group sender name text (or media message type text)
dialogsDraftFg: #dd4b39; // chat list draft label
dialogsVerifiedIconBg: windowBgActive; // chat list verified icon background
dialogsVerifiedIconFg: windowFgActive; // chat list verified icon check
dialogsSendingIconFg: #c1c1c1; // chat list sending message icon (clock)
dialogsSentIconFg: #5dc452; // chat list sent message tick / double tick icon
dialogsUnreadBg: windowBgActive; // chat list unread badge background for not muted chat
dialogsUnreadBgMuted: #bbbbbb; // chat list unread badge background for muted chat
dialogsUnreadFg: windowFgActive; // chat list unread badge text
dialogsArchiveFg: #525252 | dialogsNameFg; // chat list archive name text
dialogsOnlineBadgeFg: #4dc920 | dialogsUnreadBg; // chat list online status
dialogsScamFg: dialogsDraftFg; // chat list scam label
dialogsBgOver: windowBgOver; // chat list background with mouse over
dialogsNameFgOver: windowBoldFgOver; // chat list name text with mouse over
dialogsChatIconFgOver: dialogsNameFgOver; // chat list group or channel icon with mouse over
dialogsDateFgOver: windowSubTextFgOver; // chat list date text with mouse over
dialogsTextFgOver: windowSubTextFgOver; // chat list message text with mouse over
dialogsTextFgServiceOver: dialogsTextFgService; // chat list group sender name text with mouse over
dialogsDraftFgOver: dialogsDraftFg; // chat list draft label with mouse over
dialogsVerifiedIconBgOver: dialogsVerifiedIconBg; // chat list verified icon background with mouse over
dialogsVerifiedIconFgOver: dialogsVerifiedIconFg; // chat list verified icon check with mouse over
dialogsSendingIconFgOver: dialogsSendingIconFg; // chat list sending message icon (clock) with mouse over
dialogsSentIconFgOver: dialogsSentIconFg; // chat list sent message tick / double tick icon with mouse over
dialogsUnreadBgOver: dialogsUnreadBg; // chat list unread badge background for not muted chat with mouse over
dialogsUnreadBgMutedOver: dialogsUnreadBgMuted; // chat list unread badge background for muted chat with mouse over
dialogsUnreadFgOver: dialogsUnreadFg; // chat list unread badge text with mouse over
dialogsArchiveFgOver: #525252 | dialogsNameFgOver; // chat list archive name text with mouse over
dialogsScamFgOver: dialogsDraftFgOver; // chat list scam label with mouse over
dialogsBgActive: #419fd9; // chat list background for current (active) chat
dialogsNameFgActive: windowFgActive; // chat list name text for current (active) chat
dialogsChatIconFgActive: dialogsNameFgActive; // chat list group or channel icon for current (active) chat
dialogsDateFgActive: windowFgActive; // chat list date text for current (active) chat
dialogsTextFgActive: windowFgActive; // chat list message text for current (active) chat
dialogsTextFgServiceActive: dialogsTextFgActive; // chat list group sender name text for current (active) chat
dialogsDraftFgActive: #c6e1f7; // chat list draft label for current (active) chat
dialogsVerifiedIconBgActive: dialogsTextFgActive; // chat list verified icon background for current (active) chat
dialogsVerifiedIconFgActive: dialogsBgActive; // chat list verified icon check for current (active) chat
dialogsSendingIconFgActive: #ffffff99; // chat list sending message icon (clock) for current (active) chat
dialogsSentIconFgActive: dialogsTextFgActive; // chat list sent message tick / double tick icon for current (active) chat
dialogsUnreadBgActive: dialogsTextFgActive; // chat list unread badge background for not muted chat for current (active) chat
dialogsUnreadBgMutedActive: dialogsDraftFgActive; // chat list unread badge background for muted chat for current (active) chat
dialogsUnreadFgActive: dialogsBgActive; // chat list unread badge text for current (active) chat
dialogsOnlineBadgeFgActive: #ffffff; // chat list online status for current (active) chat
dialogsScamFgActive: dialogsDraftFgActive; // chat list scam label for current (active) chat
dialogsRippleBg: windowBgRipple; // chat list background ripple effect
dialogsRippleBgActive: activeButtonBgRipple; // chat list background ripple effect for current (active) chat
dialogsForwardBg: dialogsBgActive; // forwarding panel background (when forwarding messages in the smallest window size)
dialogsForwardFg: dialogsNameFgActive; // forwarding panel text (when forwarding messages in the smallest window size)
searchedBarBg: windowBgOver; // search results bar background (in chats list, contacts box..)
searchedBarFg: windowSubTextFgOver; // search results bar text (in chats list, contacts box..)
// history
topBarBg: windowBg; // top bar background (in chat view, media overview..)
emojiPanBg: windowBg; // emoji panel background
emojiPanCategories: #f7f7f7 | windowBg; // emoji panel categories background
emojiPanHeaderFg: windowSubTextFg; // emoji panel section header text
emojiPanHeaderBg: #fffffff2 | emojiPanBg; // emoji panel section header background
emojiIconFg: checkboxFg; // emoji category icon
emojiIconFgActive: windowBgActive; // active emoji category icon
stickerPanDeleteBg: #000000ff; // delete X button background for custom sent stickers in stickers panel (legacy)
stickerPanDeleteFg: windowFgActive; // delete X button icon for custom sent stickers in stickers panel (legacy)
stickerPreviewBg: #ffffffb0; // sticker and GIF preview background (when you press and hold on a sticker)
historyTextInFg: windowFg; // inbox message text
historyTextInFgSelected: historyTextInFg; // inbox message selected text or text in a selected message
historyTextOutFg: windowFg; // outbox message text
historyTextOutFgSelected: historyTextOutFg; // outbox message selected text or text in a selected message
historyLinkInFg: windowActiveTextFg; // inbox message link
historyLinkInFgSelected: historyLinkInFg; // inbox message link in a selected text or message
historyLinkOutFg: windowActiveTextFg; // outbox message link
historyLinkOutFgSelected: historyLinkOutFg; // outbox message link in a selected text or message
historyFileNameInFg: historyTextInFg; // inbox media filename text
historyFileNameInFgSelected: historyFileNameInFg; // inbox media filename text in a selected message
historyFileNameOutFg: historyTextOutFg; // outbox media filename text
historyFileNameOutFgSelected: historyFileNameOutFg; // outbox media filename text in a selected message
historyOutIconFg: dialogsSentIconFg; // outbox message tick / double tick icon
historyOutIconFgSelected: #4da79f; // outbox message tick / double tick icon in a selected message
historyIconFgInverted: windowFgActive; // media message tick / double tick icon (like in sent photo)
historySendingOutIconFg: #98d292; // outbox sending message icon (clock)
historySendingInIconFg: #a0adb5; // inbox sending message icon (clock) (like in sent messages to yourself or in sent messages to a channel)
historySendingInvertedIconFg: #ffffffc8; // media sending message icon (clock) (like in sent photo)
historyCallArrowInFg: callArrowFg; // received phone call arrow
historyCallArrowInFgSelected: callArrowFg; // received phone call arrow in a selected message
historyCallArrowMissedInFg: callArrowMissedFg; // missed phone call arrow
historyCallArrowMissedInFgSelected: callArrowMissedFg; // missed phone call arrow in a selected message
historyCallArrowOutFg: historyCallArrowInFg; // outgoing phone call arrow
historyCallArrowOutFgSelected: historyCallArrowInFgSelected; // outgoing phone call arrow
historyUnreadBarBg: #fcfbfa; // new unread messages bar background
historyUnreadBarBorder: shadowFg; // new unread messages bar shadow
historyUnreadBarFg: #538bb4; // new unread messages bar text
historyForwardChooseBg: #0000004c; // forwarding messages in a large window size "choose recipient" background
historyForwardChooseFg: windowFgActive; // forwarding messages in a large window size "choose recipient" text
historyPeer1NameFg: #c03d33; // red group member name
historyPeer1NameFgSelected: historyPeer1NameFg; // red group member name in a selected message
historyPeer1UserpicBg: #e17076; // red userpic background
historyPeer2NameFg: #4fad2d; // green group member name
historyPeer2NameFgSelected: historyPeer2NameFg; // green group member name in a selected message
historyPeer2UserpicBg: #7bc862; // green userpic background
historyPeer3NameFg: #d09306; // yellow group member name
historyPeer3NameFgSelected: historyPeer3NameFg; // yellow group member name in a selected message
historyPeer3UserpicBg: #e5ca77; // yellow userpic background
historyPeer4NameFg: windowActiveTextFg; // blue group member name
historyPeer4NameFgSelected: historyPeer4NameFg; // blue group member name in a selected message
historyPeer4UserpicBg: #65aadd; // blue userpic background
historyPeer5NameFg: #8544d6; // purple group member name
historyPeer5NameFgSelected: historyPeer5NameFg; // purple group member name in a selected message
historyPeer5UserpicBg: #a695e7; // purple userpic background
historyPeer6NameFg: #cd4073; // pink group member name
historyPeer6NameFgSelected: historyPeer6NameFg; // pink group member name in a selected message
historyPeer6UserpicBg: #ee7aae; // pink userpic background
historyPeer7NameFg: #2996ad; // sea group member name
historyPeer7NameFgSelected: historyPeer7NameFg; // sea group member name in a selected message
historyPeer7UserpicBg: #6ec9cb; // sea userpic background
historyPeer8NameFg: #ce671b; // orange group member name
historyPeer8NameFgSelected: historyPeer8NameFg; // orange group member name in a selected message
historyPeer8UserpicBg: #faa774; // orange userpic background
historyPeerUserpicFg: windowFgActive; // default userpic initials
historyPeerSavedMessagesBg: historyPeer4UserpicBg; // saved messages userpic background
historyPeerArchiveUserpicBg: dialogsUnreadBgMuted; // archive folder userpic background
// Some values are marked as (adjusted), it means they're adjusted by
// hue and saturation of the average background color if user chooses
// some other (not bundled to this color theme) background. If the
// bundled background is used those colors are not adjusted in any way.
historyScrollBarBg: #517c417a; // scroll bar current rectangle, the bar itself in the chat view (adjusted)
historyScrollBarBgOver: #517c41bc; // scroll bar current rectangle with mouse over it in the chat view (adjusted)
historyScrollBg: #517c414c; // scroll bar background (adjusted)
historyScrollBgOver: #517c416b; // scroll bar background with mouse over the scroll bar (adjusted)
msgInBg: windowBg; // inbox message background
msgInBgSelected: #c2dcf2; // inbox selected message background (and background of selected text in those messages)
msgOutBg: #effdde; // outbox message background
msgOutBgSelected: #b7dbdb; // outbox selected message background (and background of selected text in those messages)
msgSelectOverlay: #358cd44c; // overlay which is filling the media parts of selected messages (like in selected photo message)
msgStickerOverlay: #358cd47f; // overlay which is filling the selected sticker message
msgInServiceFg: windowActiveTextFg; // inbox message information text (like information about a forwarded message original sender)
msgInServiceFgSelected: windowActiveTextFg; // inbox selected message information text (like information about a forwarded message original sender)
msgOutServiceFg: #3a8e26; // outbox message information text (like information about a forwarded message original sender)
msgOutServiceFgSelected: #367570; // outbox message information text (like information about a forwarded message original sender)
msgInShadow: #748ea229; // inbox message shadow (below the bubble)
msgInShadowSelected: #548dbb29; // inbox selected message shadow (below the bubble)
msgOutShadow: #3ac34740; // outbox message shadow (below the bubble)
msgOutShadowSelected: #37a78e40; // outbox selected message shadow (below the bubble)
msgInDateFg: #a0acb6; // inbox message time text
msgInDateFgSelected: #6a9cc5; // inbox selected message time text
msgOutDateFg: #6cc264; // outbox message time text
msgOutDateFgSelected: #50a79c; // outbox selected message time text
msgServiceFg: windowFgActive; // service message text (like date dividers or service message about the group title being changed)
msgServiceBg: #517c417f; // service message background (like in a service message about group title being changed) (adjusted)
msgServiceBgSelected: #96b38ba2; // service message selected text background (like in a service message about group title being changed) (adjusted)
msgInReplyBarColor: activeLineFg; // inbox message reply outline
msgInReplyBarSelColor: activeLineFg; // inbox selected message reply outline
msgOutReplyBarColor: historyOutIconFg; // outbox message reply outline
msgOutReplyBarSelColor: historyOutIconFgSelected; // outbox selected message reply outline
msgImgReplyBarColor: msgServiceFg; // sticker message reply outline
msgInMonoFg: #4e7391; // inbox message monospace text (like a message sent with `test` text)
msgOutMonoFg: #469165; // outbox message monospace text
msgInMonoFgSelected: msgInMonoFg; // inbox message monospace text in a selected text or message
msgOutMonoFgSelected: msgOutMonoFg; // outbox message monospace text in a selected text or message
msgDateImgFg: msgServiceFg; // media message time text (like time text in a sent photo)
msgDateImgBg: #00000054; // media message time bubble background (like time bubble in a sent photo) or file with thumbnail download icon circle background
msgDateImgBgOver: #00000074; // media message download icon circle background with mouse over (like file with thumbnail download icon)
msgDateImgBgSelected: #1c4a7187; // selected media message time bubble background
msgFileThumbLinkInFg: lightButtonFg; // inbox media file message with thumbnail download / open with button text
msgFileThumbLinkInFgSelected: lightButtonFgOver; // inbox selected media file message with thumbnail download / open with button text
msgFileThumbLinkOutFg: #5eba5b; // outbox media file message with thumbnail download / open with button text
msgFileThumbLinkOutFgSelected: #31a298; // outbox selected media file message with thumbnail download / open with button text
msgFileInBg: windowBgActive; // inbox audio file download circle background
msgFileInBgOver: #4eade3; // inbox audio file download circle background with mouse over
msgFileInBgSelected: #51a3d3; // inbox selected audio file download circle background
msgFileOutBg: #78c67f; // outbox audio file download circle background
msgFileOutBgOver: #6bc272; // outbox audio file download circle background with mouse over
msgFileOutBgSelected: #5fb389; // outbox selected audio file download circle background
msgFile1Bg: #72b1df; // blue shared links / files without image square thumbnail
msgFile1BgDark: #5c9ece; // blue shared files without image download circle background
msgFile1BgOver: #5294c4; // blue shared files without image download circle background with mouse over
msgFile1BgSelected: #5099d0; // blue shared files without image download circle background if file is selected
msgFile2Bg: #61b96e; // green shared links / shared files without image square thumbnail
msgFile2BgDark: #4da859; // green shared files without image download circle background
msgFile2BgOver: #44a050; // green shared files without image download circle background with mouse over
msgFile2BgSelected: #46a07e; // green shared files without image download circle background if file is selected
msgFile3Bg: #e47272; // red shared links / shared files without image square thumbnail
msgFile3BgDark: #cd5b5e; // red shared files without image download circle background
msgFile3BgOver: #c35154; // red shared files without image download circle background with mouse over
msgFile3BgSelected: #9f6a82; // red shared files without image download circle background if file is selected
msgFile4Bg: #efc274; // yellow shared links / shared files without image square thumbnail
msgFile4BgDark: #e6a561; // yellow shared files without image download circle background
msgFile4BgOver: #dc9c5a; // yellow shared files without image download circle background with mouse over
msgFile4BgSelected: #b19d84; // yellow shared files without image download circle background if file is selected
historyFileInIconFg: msgInBg; // inbox file without thumbnail (like audio file) download arrow icon
historyFileInIconFgSelected: msgInBgSelected; // inbox selected file without thumbnail (like audio file) download arrow icon
historyFileInRadialFg: historyFileInIconFg; // inbox file without thumbnail (like audio file) radial download animation line
historyFileInRadialFgSelected: historyFileInIconFgSelected; // inbox selected file without thumbnail (like audio file) radial download animation line
historyFileOutIconFg: msgOutBg; // outbox file without thumbnail (like audio file) download arrow icon
historyFileOutIconFgSelected: msgOutBgSelected; // outbox selected file without thumbnail (like audio file) download arrow icon
historyFileOutRadialFg: historyFileOutIconFg; // outbox file without thumbnail (like audio file) radial download animation line
historyFileOutRadialFgSelected: historyFileOutIconFgSelected; // outbox selected file without thumbnail (like audio file) radial download animation line
historyFileThumbIconFg: msgInBg; // file with thumbnail (or photo / video) download arrow icon
historyFileThumbIconFgSelected: msgInBgSelected; // selected file with thumbnail (or photo / video) download arrow icon
historyFileThumbRadialFg: historyFileThumbIconFg; // file with thumbnail (or photo / video) radial download animation line
historyFileThumbRadialFgSelected: historyFileThumbIconFgSelected; // selected file with thumbnail (or photo / video) radial download animation line
historyVideoMessageProgressFg: historyFileThumbIconFg; // radial playback progress in round video messages
msgWaveformInActive: windowBgActive; // inbox voice message active waveform lines (like played part of currently playing voice message)
msgWaveformInActiveSelected: #51a3d3; // inbox selected voice message active waveform lines (like played part of currently playing voice message)
msgWaveformInInactive: #d4dee6; // inbox voice message inactive waveform lines (like upcoming part of currently playing voice message)
msgWaveformInInactiveSelected: #9cc1e1; // inbox selected voice message inactive waveform lines (like upcoming part of currently playing voice message)
msgWaveformOutActive: #78c67f; // outbox voice message active waveform lines (like played part of currently playing voice message)
msgWaveformOutActiveSelected: #6badad; // outbox selected voice message active waveform lines (like played part of currently playing voice message)
msgWaveformOutInactive: #b3e2b4; // outbox voice message inactive waveform lines (like upcoming part of currently playing voice message)
msgWaveformOutInactiveSelected: #91c3c3; // outbox selected voice message inactive waveform lines (like upcoming part of currently playing voice message)
msgBotKbOverBgAdd: #ffffff20; // this is painted over a bot inline keyboard button (which has msgServiceBg background) when mouse is over that button
msgBotKbIconFg: msgServiceFg; // bot inline keyboard button icon in the top-right corner (like in @vote bot when a poll is ready to be shared)
msgBotKbRippleBg: #00000020; // bot inline keyboard button ripple effect
mediaInFg: msgInDateFg; // inbox media message status text (like in file that is being downloaded)
mediaInFgSelected: msgInDateFgSelected; // inbox selected media message status text (like in file that is being downloaded)
mediaOutFg: msgOutDateFg; // outbox media message status text (like in file that is being downloaded)
mediaOutFgSelected: msgOutDateFgSelected; // outbox selected media message status text (like in file that is being downloaded)
youtubePlayIconBg: #e83131c8; // youtube play icon background (when a link to a youtube video with a webpage preview is sent)
youtubePlayIconFg: windowFgActive; // youtube play icon arrow (when a link to a youtube video with a webpage preview is sent)
videoPlayIconBg: #0000007f; // other video play icon background (like when a link to a vimeo video with a webpage preview is sent)
videoPlayIconFg: #ffffff; // other video play icon arrow (like when a link to a vimeo video with a webpage preview is sent)
toastBg: #000000b2; // toast notification background (like when you click on your t.me link when editing your username)
toastFg: windowFgActive; // toast notification text (like when you click on your t.me link when editing your username)
reportSpamBg: emojiPanHeaderBg; // report spam panel background (like a non contact user writes your for the first time)
reportSpamFg: windowFg; // report spam panel text (when you send a report from that panel)
historyToDownBg: windowBg; // arrow button background (to scroll to the end of the viewed chat)
historyToDownBgOver: windowBgOver; // arrow button background with mouse over
historyToDownBgRipple: windowBgRipple; // arrow button ripple effect
historyToDownFg: menuIconFg; // arrow button icon
historyToDownFgOver: menuIconFgOver; // arrow button icon with mouse over
historyToDownShadow: #00000040; // arrow button shadow
historyComposeAreaBg: msgInBg; // history compose area background (message write area / reply information / forwarding information)
historyComposeAreaFg: historyTextInFg; // history compose area text
historyComposeAreaFgService: msgInDateFg; // history compose area text when replying to a media message
historyComposeIconFg: menuIconFg; // history compose area icon (like emoji, attach, bot command..)
historyComposeIconFgOver: menuIconFgOver; // history compose area icon with mouse over
historySendIconFg: windowBgActive; // send message icon
historySendIconFgOver: windowBgActive; // send message icon with mouse over
historyPinnedBg: historyComposeAreaBg; // pinned message area background
historyReplyBg: historyComposeAreaBg; // reply / forward / edit message area background
historyReplyIconFg: windowBgActive; // reply / forward / edit message left icon
historyReplyCancelFg: cancelIconFg; // reply / forward / edit message cancel button
historyReplyCancelFgOver: cancelIconFgOver; // reply / forward / edit message cancel button with mouse over
historyComposeButtonBg: historyComposeAreaBg; // unblock / join channel / mute channel button background
historyComposeButtonBgOver: windowBgOver; // unblock / join channel / mute channel button background with mouse over
historyComposeButtonBgRipple: windowBgRipple; // unblock / join channel / mute channel button ripple effect
mapPointDrop: #fd4444; // geo location marker background
mapPointDot: #ffffff; // geo location marker point
// overview
overviewCheckBg: #00000040; // shared media / files / links checkbox background for not selected rows when some rows are selected
overviewCheckBgActive: windowBgActive; // shared media / files / links checkbox background for selected rows
overviewCheckBorder: windowBg; // shared media round checkbox border
overviewCheckFg: windowBg; // shared files / links checkbox icon for not selected rows when some rows are selected
overviewCheckFgActive: windowBg; // shared files / links checkbox icon for selected rows
overviewPhotoSelectOverlay: #40ace333; // shared photos / videos / links fill for selected rows
// profile
profileStatusFgOver: #7c99b2; // group members list in group profile user last seen text with mouse over
profileVerifiedCheckBg: windowBgActive; // profile verified check icon background
profileVerifiedCheckFg: windowFgActive; // profile verified check icon tick
profileAdminStartFg: windowBgActive; // group members list creator star icon
profileAdminStarFgOver: profileAdminStartFg; // group members list creator star icon with mouse over
profileOtherAdminStarFg: windowSubTextFg; // group members list admin star icon
profileOtherAdminStarFgOver: profileStatusFgOver; // group members list admin star icon with mouse over
// settings
notificationsBoxMonitorFg: windowFg; // custom notifications settings box monitor color
notificationsBoxScreenBg: dialogsBgActive; // #6389a8; // custom notifications settings box monitor screen background
notificationSampleUserpicFg: windowBgActive; // custom notifications settings box small sample userpic placeholder
notificationSampleCloseFg: #d7d7d7 | windowSubTextFg; // custom notifications settings box small sample close button placeholder
notificationSampleTextFg: #d7d7d7 | windowSubTextFg; // custom notifications settings box small sample text placeholder
notificationSampleNameFg: #939393 | windowSubTextFg; // custom notifications settings box small sample name placeholder
changePhoneSimcardFrom: notificationSampleTextFg; // change phone number box left simcard icon
changePhoneSimcardTo: notificationSampleNameFg; // change phone number box right simcard and plane icons
mainMenuBg: windowBg; // main menu background
mainMenuCoverBg: dialogsBgActive; // main menu top cover background
mainMenuCoverFg: windowFgActive; // main menu top cover text
mainMenuCloudFg: activeButtonFg;
mainMenuCloudBg: #2785bf | activeButtonBgRipple;
mediaPlayerBg: windowBg; // audio file player background
mediaPlayerActiveFg: windowBgActive; // audio file player playback progress already played part
mediaPlayerInactiveFg: sliderBgInactive; // audio file player playback progress upcoming (not played yet) part with mouse over
mediaPlayerDisabledFg: #9dd1ef; // audio file player loading progress (when you're playing an audio file and switch to the previous one which is not loaded yet)
// mediaview
mediaviewFileBg: windowBg; // file rectangle background (when you view a png file in Media Viewer and go to a previous, not loaded yet, file)
mediaviewFileNameFg: windowFg; // file name in file rectangle
mediaviewFileSizeFg: windowSubTextFg; // file size text in file rectangle
mediaviewFileRedCornerFg: #d55959; // red file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .pdf)
mediaviewFileYellowCornerFg: #e8a659; // yellow file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .zip)
mediaviewFileGreenCornerFg: #49a957; // green file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .exe)
mediaviewFileBlueCornerFg: #599dcf; // blue file thumbnail placeholder corner in file rectangle (for a file without thumbnail, like .dmg)
mediaviewFileExtFg: activeButtonFg; // file extension text in file thumbnail placeholder in file rectangle
mediaviewMenuBg: #383838; // context menu in Media Viewer background
mediaviewMenuBgOver: #505050; // context menu item background with mouse over
mediaviewMenuBgRipple: #676767; // context menu item ripple effect
mediaviewMenuFg: windowFgActive; // context menu item text
mediaviewBg: #222222eb; // Media Viewer background
mediaviewVideoBg: imageBg; // Media Viewer background when viewing a video in full screen
mediaviewControlBg: #0000003c; // controls background (like next photo / previous photo)
mediaviewControlFg: windowFgActive; // controls icon (like next photo / previous photo)
mediaviewCaptionBg: #11111180; // caption text background (when viewing photo with caption)
mediaviewCaptionFg: mediaviewControlFg; // caption text
mediaviewTextLinkFg: #91d9ff; // caption text link
mediaviewSaveMsgBg: toastBg; // save to file toast message background in Media Viewer
mediaviewSaveMsgFg: toastFg; // save to file toast message text
mediaviewPlaybackActive: #c7c7c7; // video playback progress already played part
mediaviewPlaybackInactive: #252525; // video playback progress upcoming (not played yet) part
mediaviewPlaybackActiveOver: #ffffff; // video playback progress already played part with mouse over
mediaviewPlaybackInactiveOver: #474747; // video playback progress upcoming (not played yet) part with mouse over
mediaviewPlaybackProgressFg: #ffffffc7; // video playback progress text
mediaviewPlaybackIconFg: mediaviewPlaybackActive; // video playback controls icon
mediaviewPlaybackIconFgOver: mediaviewPlaybackActiveOver; // video playback controls icon with mouse over
mediaviewTransparentBg: #ffffff; // transparent filling part (when viewing a transparent .png file in Media Viewer)
mediaviewTransparentFg: #cccccc; // another transparent filling part
// notification
notificationBg: windowBg; // custom notification window background
// calls
callBg: #26282cf2; // phone call popup background
callNameFg: #ffffff; // phone call popup name text
callFingerprintBg: #00000066; // phone call popup emoji fingerprint background
callStatusFg: #aaabac; // phone call popup status text
callIconFg: #ffffff; // phone call popup answer, hangup and mute mic icon
callAnswerBg: #64c15b; // phone call popup answer button background
callAnswerRipple: #52b149; // phone call popup answer button ripple effect
callAnswerBgOuter: #50eb4126; // phone call popup answer button outer ripple effect
callHangupBg: #d75a5a; // phone call popup hangup button background
callHangupRipple: #c04646; // phone call popup hangup button ripple effect
callCancelBg: #ffffff; // phone call popup line busy cancel button background
callCancelFg: #777777; // phone call popup line busy cancel button icon
callCancelRipple: #f1f1f1; // phone call popup line busy cancel button ripple effect
callMuteRipple: #ffffff12; // phone call popup mute mic ripple effect
callBarBg: dialogsBgActive; // active phone call bar background
callBarMuteRipple: dialogsRippleBgActive; // active phone call bar mute and hangup button ripple effect
callBarBgMuted: #8f8f8f | dialogsUnreadBgMuted; // phone call bar with muted mic background
callBarUnmuteRipple: #7f7f7f | shadowFg; // phone call bar with muted mic mute and hangup button ripple effect
callBarFg: dialogsNameFgActive; // phone call bar text and icons
importantTooltipBg: toastBg;
importantTooltipFg: toastFg;
importantTooltipFgLink: mediaviewTextLinkFg;
outdatedFg: #ffffff;
outdateSoonBg: #e08543;
outdatedBg: #e05745;

File diff suppressed because it is too large Load Diff

View File

@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h"
#include "ui/text/text_utilities.h"
#include "platform/platform_file_utilities.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "core/click_handler_types.h"
#include "core/update_checker.h"
#include "styles/style_boxes.h"

View File

@ -634,4 +634,14 @@ bool isLayerShown() {
return false;
}
int DividerLabel::naturalWidth() const {
return -1;
}
void DividerLabel::resizeEvent(QResizeEvent *e) {
_background->lower();
_background->setGeometry(rect());
return PaddingWrap::resizeEvent(e);
}
} // namespace Ui

View File

@ -10,6 +10,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/layer_widget.h"
#include "base/unique_qptr.h"
#include "base/flags.h"
#include "ui/wrap/padding_wrap.h"
#include "ui/widgets/labels.h"
#include "ui/effects/animation_value.h"
#include "ui/text/text_entity.h"
#include "ui/rp_widget.h"
@ -455,4 +457,19 @@ void hideLayer(anim::type animated = anim::type::normal);
void hideSettingsAndLayer(anim::type animated = anim::type::normal);
bool isLayerShown();
class DividerLabel : public PaddingWrap<FlatLabel> {
public:
using PaddingWrap::PaddingWrap;
int naturalWidth() const override;
protected:
void resizeEvent(QResizeEvent *e) override;
private:
object_ptr<BoxContentDivider> _background
= object_ptr<BoxContentDivider>(this);
};
} // namespace Ui

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "ui/widgets/widgets.style";
using "intro/intro.style";

View File

@ -15,7 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/text/text_utilities.h"
#include "core/click_handler_types.h" // UrlClickHandler
#include "base/qthelp_url.h" // qthelp::url_encode
#include "platform/platform_info.h" // Platform::SystemVersionPretty
#include "base/platform/base_platform_info.h"
#include "mainwidget.h"
#include "numbers.h"
#include "app.h"

View File

@ -11,7 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/shadow.h"
#include "ui/widgets/input_fields.h"
#include "ui/ui_utility.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "app.h"
#include "styles/style_boxes.h"
#include "styles/style_mediaview.h"

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "ui/widgets/widgets.style";
using "window/window.style";

View File

@ -16,7 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/openssl_help.h"
#include "mtproto/connection.h"
#include "media/audio/media_audio_track.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "calls/calls_panel.h"
#include "data/data_user.h"
#include "data/data_session.h"

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "boxes/boxes.style";
using "ui/widgets/widgets.style";

View File

@ -7,11 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "chat_helpers/emoji_keywords.h"
#include "chat_helpers/emoji_suggestions_helper.h"
#include "emoji_suggestions_helper.h"
#include "lang/lang_instance.h"
#include "lang/lang_cloud_manager.h"
#include "core/application.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "ui/emoji_config.h"
#include "main/main_account.h"
#include "main/main_session.h"

View File

@ -1,31 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "emoji_suggestions.h"
#include "emoji_suggestions_data.h"
namespace Ui {
namespace Emoji {
inline utf16string QStringToUTF16(const QString &string) {
return utf16string(
reinterpret_cast<const utf16char*>(string.constData()),
string.size());
}
inline QString QStringFromUTF16(utf16string string) {
return QString::fromRawData(
reinterpret_cast<const QChar*>(string.data()),
string.size());
}
constexpr auto kSuggestionMaxLength = internal::kReplacementMaxLength;
} // namespace Emoji
} // namespace Ui

View File

@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/emoji_suggestions_widget.h"
#include "chat_helpers/emoji_keywords.h"
#include "chat_helpers/emoji_suggestions_helper.h"
#include "emoji_suggestions_helper.h"
#include "ui/effects/ripple_animation.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/inner_dropdown.h"

View File

@ -1,300 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/common/basic_tokenized_file.h"
#include "codegen/common/logging.h"
#include "codegen/common/clean_file_reader.h"
#include "codegen/common/checked_utf8_string.h"
using Token = codegen::common::BasicTokenizedFile::Token;
using Type = Token::Type;
namespace codegen {
namespace common {
namespace {
constexpr int kErrorUnterminatedStringLiteral = 201;
constexpr int kErrorIncorrectUtf8String = 202;
constexpr int kErrorIncorrectToken = 203;
constexpr int kErrorUnexpectedToken = 204;
bool isDigitChar(char ch) {
return (ch >= '0') && (ch <= '9');
}
bool isNameChar(char ch) {
return isDigitChar(ch) || ((ch >= 'a') && (ch <= 'z')) || ((ch >= 'A') && (ch <= 'Z')) || (ch == '_');
}
bool isWhitespaceChar(char ch) {
return (ch == '\n' || ch == '\r' || ch == ' ' || ch == '\t');
}
Token invalidToken() {
return { Type::Invalid, QString(), ConstUtf8String(nullptr, 0), false };
}
} // namespace
BasicTokenizedFile::BasicTokenizedFile(const QString &filepath) : reader_(filepath) {
}
BasicTokenizedFile::BasicTokenizedFile(const QByteArray &content, const QString &filepath) : reader_(content, filepath) {
}
bool BasicTokenizedFile::putBack() {
if (currentToken_ > 0) {
--currentToken_;
return true;
}
return false;
}
Token BasicTokenizedFile::getAnyToken() {
if (currentToken_ >= tokens_.size()) {
if (readToken() == Type::Invalid) {
return invalidToken();
}
}
return tokens_.at(currentToken_++);
}
Token BasicTokenizedFile::getToken(Type typeCondition) {
if (auto token = getAnyToken()) {
if (token.type == typeCondition) {
return token;
}
putBack();
}
return invalidToken();
}
Type BasicTokenizedFile::readToken() {
auto result = readOneToken(StartWithWhitespace::Allow);
// Try to read double token.
if (result == Type::Int) {
if (readOneToken(StartWithWhitespace::Deny) == Type::Dot) {
// We got int and dot, so it is double already.
result = uniteLastTokens(Type::Double);
// Try to read one more int (after dot).
if (readOneToken(StartWithWhitespace::Deny) == Type::Int) {
result = uniteLastTokens(Type::Double);
}
}
} else if (result == Type::Dot) {
if (readOneToken(StartWithWhitespace::Deny) == Type::Int) {
//We got dot and int, so it is double.
result = uniteLastTokens(Type::Double);
}
}
return result;
}
Type BasicTokenizedFile::readOneToken(StartWithWhitespace condition) {
skipWhitespaces();
if (tokenStartWhitespace_ && condition == StartWithWhitespace::Deny) {
return Type::Invalid;
}
if (reader_.atEnd()) {
return Type::Invalid;
}
auto ch = reader_.currentChar();
if (ch == '"') {
return readString();
} else if (isNameChar(ch)) {
return readNameOrNumber();
}
return readSingleLetter();
}
Type BasicTokenizedFile::saveToken(Type type, const QString &value) {
ConstUtf8String original = { tokenStart_, reader_.currentPtr() };
tokens_.push_back({ type, value, original, tokenStartWhitespace_ });
return type;
}
Type BasicTokenizedFile::uniteLastTokens(Type type) {
auto size = tokens_.size();
if (size < 2) {
return Type::Invalid;
}
auto &token(tokens_[size - 2]);
auto originalFrom = token.original.data();
auto originalTill = tokens_.back().original.end();
token.type = type;
token.original = { originalFrom, originalTill };
token.value += tokens_.back().value;
tokens_.pop_back();
return type;
}
QString BasicTokenizedFile::getCurrentLineComment() {
if (lineNumber_ > singleLineComments_.size()) {
reader_.logError(kErrorInternal, lineNumber_) << "internal tokenizer error (line number larger than comments list size).";
failed_ = true;
return QString();
}
auto commentBytes = singleLineComments_[lineNumber_ - 1].mid(2); // Skip "//"
CheckedUtf8String comment(commentBytes);
if (!comment.isValid()) {
reader_.logError(kErrorIncorrectUtf8String, lineNumber_) << "incorrect UTF-8 string in the comment.";
failed_ = true;
return QString();
}
return comment.toString().trimmed();
}
Type BasicTokenizedFile::readNameOrNumber() {
while (!reader_.atEnd()) {
if (!isDigitChar(reader_.currentChar())) {
break;
}
reader_.skipChar();
}
bool onlyDigits = true;
while (!reader_.atEnd()) {
if (!isNameChar(reader_.currentChar())) {
break;
}
onlyDigits = false;
reader_.skipChar();
}
return saveToken(onlyDigits ? Type::Int : Type::Name);
}
Type BasicTokenizedFile::readString() {
reader_.skipChar();
auto offset = reader_.currentPtr();
QByteArray value;
while (!reader_.atEnd()) {
auto ch = reader_.currentChar();
if (ch == '"') {
if (reader_.currentPtr() > offset) {
value.append(offset, reader_.currentPtr() - offset);
}
break;
}
if (ch == '\n') {
reader_.logError(kErrorUnterminatedStringLiteral, lineNumber_) << "unterminated string literal.";
failed_ = true;
return Type::Invalid;
}
if (ch == '\\') {
if (reader_.currentPtr() > offset) {
value.append(offset, reader_.currentPtr() - offset);
}
reader_.skipChar();
ch = reader_.currentChar();
if (reader_.atEnd() || ch == '\n') {
reader_.logError(kErrorUnterminatedStringLiteral, lineNumber_) << "unterminated string literal.";
failed_ = true;
return Type::Invalid;
}
offset = reader_.currentPtr() + 1;
if (ch == 'n') {
value.append('\n');
} else if (ch == 't') {
value.append('\t');
} else if (ch == '"') {
value.append('"');
} else if (ch == '\\') {
value.append('\\');
}
}
reader_.skipChar();
}
if (reader_.atEnd()) {
reader_.logError(kErrorUnterminatedStringLiteral, lineNumber_) << "unterminated string literal.";
failed_ = true;
return Type::Invalid;
}
CheckedUtf8String checked(value);
if (!checked.isValid()) {
reader_.logError(kErrorIncorrectUtf8String, lineNumber_) << "incorrect UTF-8 string literal.";
failed_ = true;
return Type::Invalid;
}
reader_.skipChar();
return saveToken(Type::String, checked.toString());
}
Type BasicTokenizedFile::readSingleLetter() {
auto type = singleLetterTokens_.value(reader_.currentChar(), Type::Invalid);
if (type == Type::Invalid) {
reader_.logError(kErrorIncorrectToken, lineNumber_) << "incorrect token '" << reader_.currentChar() << "'";
return Type::Invalid;
}
reader_.skipChar();
return saveToken(type);
}
void BasicTokenizedFile::skipWhitespaces() {
if (reader_.atEnd()) return;
auto ch = reader_.currentChar();
tokenStartWhitespace_ = isWhitespaceChar(ch);
if (tokenStartWhitespace_) {
do {
if (ch == '\n') {
++lineNumber_;
}
reader_.skipChar();
ch = reader_.currentChar();
} while (!reader_.atEnd() && isWhitespaceChar(ch));
}
tokenStart_ = reader_.currentPtr();
}
LogStream operator<<(LogStream &&stream, BasicTokenizedFile::Token::Type type) {
const char *value = "'invalid'";
switch (type) {
case Type::Invalid: break;
case Type::Int: value = "'int'"; break;
case Type::Double: value = "'double'"; break;
case Type::String: value = "'string'"; break;
case Type::LeftParenthesis: value = "'('"; break;
case Type::RightParenthesis: value = "')'"; break;
case Type::LeftBrace: value = "'{'"; break;
case Type::RightBrace: value = "'}'"; break;
case Type::LeftBracket: value = "'['"; break;
case Type::RightBracket: value = "']'"; break;
case Type::Colon: value = "':'"; break;
case Type::Semicolon: value = "';'"; break;
case Type::Comma: value = "','"; break;
case Type::Dot: value = "'.'"; break;
case Type::Number: value = "'#'"; break;
case Type::Plus: value = "'+'"; break;
case Type::Minus: value = "'-'"; break;
case Type::Equals: value = "'='"; break;
case Type::Name: value = "'identifier'"; break;
}
return std::forward<LogStream>(stream) << value;
}
LogStream BasicTokenizedFile::logError(int code) const {
return reader_.logError(code, lineNumber_);
}
LogStream BasicTokenizedFile::logErrorUnexpectedToken() const {
if (currentToken_ < tokens_.size()) {
auto token = tokens_.at(currentToken_).original.toStdString();
return logError(kErrorUnexpectedToken) << "unexpected token '" << token << "', expected ";
}
return logError(kErrorUnexpectedToken) << "unexpected token, expected ";
}
BasicTokenizedFile::~BasicTokenizedFile() = default;
} // namespace common
} // namespace codegen

View File

@ -1,156 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <QtCore/QMap>
#include <QtCore/QString>
#include <QtCore/QList>
#include "codegen/common/const_utf8_string.h"
#include "codegen/common/clean_file_reader.h"
namespace codegen {
namespace common {
class LogStream;
// Interface for reading a cleaned from comments file by basic tokens.
class BasicTokenizedFile {
public:
explicit BasicTokenizedFile(const QString &filepath);
explicit BasicTokenizedFile(const QByteArray &content, const QString &filepath = QString());
BasicTokenizedFile(const BasicTokenizedFile &other) = delete;
BasicTokenizedFile &operator=(const BasicTokenizedFile &other) = delete;
struct Token {
// String - utf8 string converted to QString.
enum class Type {
Invalid = 0,
Int,
Double,
String,
LeftParenthesis,
RightParenthesis,
LeftBrace,
RightBrace,
LeftBracket,
RightBracket,
Colon,
Semicolon,
Comma,
Dot,
Number,
Plus,
Minus,
Equals,
And,
Or,
Name, // [0-9a-zA-Z_]+ with at least one letter.
};
Type type;
QString value;
ConstUtf8String original;
bool hasLeftWhitespace;
explicit operator bool() const {
return (type != Type::Invalid);
}
};
bool read() {
if (reader_.read()) {
singleLineComments_ = reader_.singleLineComments();
return true;
}
return false;
}
bool atEnd() const {
return reader_.atEnd();
}
Token getAnyToken();
Token getToken(Token::Type typeCondition);
bool putBack();
bool failed() const {
return failed_;
}
QString getCurrentLineComment();
// Log error to std::cerr with 'code' at the current position in file.
LogStream logError(int code) const;
LogStream logErrorUnexpectedToken() const;
~BasicTokenizedFile();
private:
using Type = Token::Type;
void skipWhitespaces();
// Reads a token, including complex tokens, like double numbers.
Type readToken();
// Read exactly one token, applying condition on the whitespaces.
enum class StartWithWhitespace {
Allow,
Deny,
};
Type readOneToken(StartWithWhitespace condition);
// helpers
Type readNameOrNumber();
Type readString();
Type readSingleLetter();
Type saveToken(Type type, const QString &value = QString());
Type uniteLastTokens(Type type);
CleanFileReader reader_;
QList<Token> tokens_;
int currentToken_ = 0;
int lineNumber_ = 1;
bool failed_ = false;
QVector<QByteArray> singleLineComments_;
// Where the last (currently read) token has started.
const char *tokenStart_ = nullptr;
// Did the last (currently read) token start with a whitespace.
bool tokenStartWhitespace_ = false;
const QMap<char, Type> singleLetterTokens_ = {
{ '(', Type::LeftParenthesis },
{ ')', Type::RightParenthesis },
{ '{', Type::LeftBrace },
{ '}', Type::RightBrace },
{ '[', Type::LeftBracket },
{ ']', Type::RightBracket },
{ ':', Type::Colon },
{ ';', Type::Semicolon },
{ ',', Type::Comma },
{ '.', Type::Dot },
{ '#', Type::Number },
{ '+', Type::Plus },
{ '-', Type::Minus },
{ '=', Type::Equals },
{ '&', Type::And },
{ '|', Type::Or },
};
};
LogStream operator<<(LogStream &&stream, BasicTokenizedFile::Token::Type type);
template <>
LogStream operator<< <BasicTokenizedFile::Token::Type>(LogStream &&stream, BasicTokenizedFile::Token::Type &&value) = delete;
template <>
LogStream operator<< <const BasicTokenizedFile::Token::Type&>(LogStream &&stream, const BasicTokenizedFile::Token::Type &value) = delete;
} // namespace common
} // namespace codegen

View File

@ -1,41 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/common/checked_utf8_string.h"
#include <iostream>
#include <QtCore/QTextCodec>
#include "codegen/common/const_utf8_string.h"
namespace codegen {
namespace common {
CheckedUtf8String::CheckedUtf8String(const char *string, int size) {
if (size < 0) {
size = strlen(string);
}
if (!size) { // Valid empty string
return;
}
QTextCodec::ConverterState state;
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
string_ = codec->toUnicode(string, size, &state);
if (state.invalidChars > 0) {
valid_ = false;
}
}
CheckedUtf8String::CheckedUtf8String(const QByteArray &string) : CheckedUtf8String(string.constData(), string.size()) {
}
CheckedUtf8String::CheckedUtf8String(const ConstUtf8String &string) : CheckedUtf8String(string.data(), string.size()) {
}
} // namespace common
} // namespace codegen

View File

@ -1,44 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
class QByteArray;
namespace codegen {
namespace common {
class ConstUtf8String;
// Parses a char sequence to a QString using UTF-8 codec.
// You can check for invalid UTF-8 sequence by isValid() method.
class CheckedUtf8String {
public:
CheckedUtf8String(const CheckedUtf8String &other) = default;
CheckedUtf8String &operator=(const CheckedUtf8String &other) = default;
explicit CheckedUtf8String(const char *string, int size = -1);
explicit CheckedUtf8String(const QByteArray &string);
explicit CheckedUtf8String(const ConstUtf8String &string);
bool isValid() const {
return valid_;
}
const QString &toString() const {
return string_;
}
private:
QString string_;
bool valid_ = true;
};
} // namespace common
} // namespace codegen

View File

@ -1,173 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/common/clean_file.h"
#include <iostream>
#include <QtCore/QDir>
#include "codegen/common/logging.h"
namespace codegen {
namespace common {
namespace {
bool readFile(const QString &filepath, QByteArray *outResult) {
QFile f(filepath);
if (!f.exists()) {
logError(kErrorFileNotFound, filepath) << ": error: file does not exist.";
return false;
}
auto limit = CleanFile::MaxSize;
if (f.size() > limit) {
logError(kErrorFileTooLarge, filepath) << "' is too large, size=" << f.size() << " > maxsize=" << limit;
return false;
}
if (!f.open(QIODevice::ReadOnly)) {
logError(kErrorFileNotOpened, filepath) << "' for read.";
return false;
}
*outResult = f.readAll();
return true;
}
} // namespace
CleanFile::CleanFile(const QString &filepath)
: filepath_(filepath)
, read_(true) {
}
CleanFile::CleanFile(const QByteArray &content, const QString &filepath)
: filepath_(filepath)
, content_(content)
, read_(false) {
}
bool CleanFile::read() {
if (read_) {
if (!readFile(filepath_, &content_)) {
return false;
}
}
filepath_ = QFileInfo(filepath_).absoluteFilePath();
enum class InsideComment {
None,
SingleLine,
MultiLine,
};
auto insideComment = InsideComment::None;
bool insideString = false;
const char *begin = content_.cbegin(), *end = content_.cend(), *offset = begin;
auto feedContent = [this, &offset, end](const char *ch) {
if (ch > offset) {
if (result_.isEmpty()) result_.reserve(end - offset - 2);
result_.append(offset, ch - offset);
offset = ch;
}
};
auto lineNumber = 0;
auto feedComment = [this, &offset, end, &lineNumber](const char *ch, bool save = false) {
if (ch > offset) {
if (save) {
singleLineComments_.resize(lineNumber + 1);
singleLineComments_[lineNumber] = QByteArray(offset, ch - offset);
}
if (result_.isEmpty()) {
result_.reserve(end - offset - 2);
}
result_.append(' ');
offset = ch;
}
};
for (const char *ch = offset; ch != end;) {
char currentChar = *ch;
char nextChar = (ch + 1 == end) ? 0 : *(ch + 1);
if (insideComment == InsideComment::None && currentChar == '"') {
bool escaped = ((ch > begin) && *(ch - 1) == '\\') && ((ch - 1 < begin) || *(ch - 2) != '\\');
if (!escaped) {
insideString = !insideString;
}
}
if (insideString) {
if (currentChar == '\n') {
++lineNumber;
}
++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, true);
ch += 2;
++lineNumber;
insideComment = InsideComment::None;
} else if (insideComment == InsideComment::SingleLine && currentChar == '\n') {
feedComment(ch, true);
++ch;
++lineNumber;
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;
++lineNumber;
feedContent(ch);
} else if (insideComment == InsideComment::MultiLine && currentChar == '\n') {
feedComment(ch);
++ch;
++lineNumber;
feedContent(ch);
} else {
if (currentChar == '\n') {
++lineNumber;
}
++ch;
}
}
singleLineComments_.resize(lineNumber + 1);
if (insideComment == InsideComment::MultiLine) {
common::logError(kErrorUnexpectedEndOfFile, filepath_);
return false;
}
if (insideComment == InsideComment::None && end > offset) {
if (result_.isEmpty()) {
result_ = content_;
} else {
result_.append(offset, end - offset);
}
}
return true;
}
QVector<QByteArray> CleanFile::singleLineComments() const {
return singleLineComments_;
}
LogStream CleanFile::logError(int code, int line) const {
return common::logError(code, filepath_, line);
}
} // namespace common
} // namespace codegen

View File

@ -1,52 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
#include <QtCore/QByteArray>
#include <QtCore/QVector>
#include "codegen/common/logging.h"
namespace codegen {
namespace common {
// Reads a file removing all C-style comments.
class CleanFile {
public:
explicit CleanFile(const QString &filepath);
explicit CleanFile(const QByteArray &content, const QString &filepath = QString());
CleanFile(const CleanFile &other) = delete;
CleanFile &operator=(const CleanFile &other) = delete;
bool read();
QVector<QByteArray> singleLineComments() const;
const char *data() const {
return result_.constData();
}
const char *end() const {
return result_.constEnd();
}
static constexpr int MaxSize = 10 * 1024 * 1024;
// Log error to std::cerr with 'code' at line number 'line' in data().
LogStream logError(int code, int line) const;
private:
QString filepath_;
QByteArray content_, result_;
bool read_;
QVector<QByteArray> singleLineComments_;
};
} // namespace common
} // namespace codegen

View File

@ -1,71 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
#include "codegen/common/clean_file.h"
namespace codegen {
namespace common {
// Wrapper allows you to read forward the CleanFile without overflow checks.
class CleanFileReader {
public:
explicit CleanFileReader(const QString &filepath) : file_(filepath) {
}
explicit CleanFileReader(const QByteArray &content, const QString &filepath = QString()) : file_(content, filepath) {
}
bool read() {
if (!file_.read()) {
return false;
}
pos_ = file_.data();
end_ = file_.end();
return true;
}
bool atEnd() const {
return (pos_ == end_);
}
char currentChar() const {
return atEnd() ? 0 : *pos_;
}
bool skipChar() {
if (atEnd()) {
return false;
}
++pos_;
return true;
}
const char *currentPtr() const {
return pos_;
}
int charsLeft() const {
return (end_ - pos_);
}
QVector<QByteArray> singleLineComments() const {
return file_.singleLineComments();
}
// Log error to std::cerr with 'code' at line number 'line' in data().
LogStream logError(int code, int line) const {
return std::forward<LogStream>(file_.logError(code, line));
}
private:
CleanFile file_;
const char *pos_ = nullptr;
const char *end_ = nullptr;
};
} // namespace common
} // namespace codegen

View File

@ -1,62 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <string>
#include <QtCore/QString>
#include <QtCore/QByteArray>
namespace codegen {
namespace common {
// This is a simple wrapper around (const char*, size).
// Not null-terminated! It does not hold any ownership.
class ConstUtf8String {
public:
explicit ConstUtf8String(const char *string, int size = -1) : string_(string) {
if (size < 0) {
size = strlen(string);
}
size_ = size;
}
ConstUtf8String(const char *string, const char *end) : ConstUtf8String(string, end - string) {
}
QByteArray toByteArray() const {
return QByteArray(string_, size_);
}
std::string toStdString() const {
return std::string(string_, size_);
}
QString toStringUnchecked() const {
return QString::fromUtf8(string_, size_);
}
bool empty() const {
return size_ == 0;
}
const char *data() const {
return string_;
}
int size() const {
return size_;
}
const char *end() const {
return data() + size();
}
ConstUtf8String mid(int pos, int size = -1) {
return ConstUtf8String(string_ + pos, std::max(std::min(size, size_ - pos), 0));
}
private:
const char *string_;
int size_;
};
} // namespace common
} // namespace codegen

View File

@ -1,111 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/common/cpp_file.h"
#include <QtCore/QFileInfo>
#include <QtCore/QDir>
namespace codegen {
namespace common {
namespace {
void writeLicense(QTextStream &stream, const ProjectInfo &project) {
stream << "\
/*\n\
WARNING! All changes made in this file will be lost!\n\
Created from '" << project.source << "' by '" << project.name << "'\n\
\n\
This file is part of Telegram Desktop,\n\
the official desktop application for the Telegram messaging service.\n\
\n\
For license and copyright information please follow this link:\n\
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL\n\
*/\n";
}
} // namespace
CppFile::CppFile(const QString &path, const ProjectInfo &project)
: stream_(&content_)
, forceReGenerate_(project.forceReGenerate) {
bool cpp = path.toLower().endsWith(".cpp");
QFileInfo info(path);
info.dir().mkpath(".");
filepath_ = info.absoluteFilePath();
writeLicense(stream_, project);
if (cpp) {
include(info.baseName() + ".h").newline();
} else {
stream() << "#pragma once";
newline().newline();
}
}
CppFile &CppFile::include(const QString &header) {
stream() << "#include \"" << header << "\"";
return newline();
}
CppFile &CppFile::includeFromLibrary(const QString &header) {
stream() << "#include <" << header << ">";
return newline();
}
CppFile &CppFile::pushNamespace(const QString &name) {
namespaces_.push_back(name);
stream() << "namespace";
if (!name.isEmpty()) {
stream() << ' ' << name;
}
stream() << " {";
return newline();
}
CppFile &CppFile::popNamespace() {
if (namespaces_.isEmpty()) {
return *this;
}
auto name = namespaces_.back();
namespaces_.pop_back();
stream() << "} // namespace";
if (!name.isEmpty()) {
stream() << ' ' << name;
}
return newline();
}
bool CppFile::finalize() {
while (!namespaces_.isEmpty()) {
popNamespace();
}
stream_.flush();
QFile file(filepath_);
if (!forceReGenerate_ && 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 common
} // namespace codegen

View File

@ -1,57 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
#include <QtCore/QVector>
#include <QtCore/QTextStream>
namespace codegen {
namespace common {
struct ProjectInfo {
QString name;
QString source;
bool forceReGenerate;
};
// Creates a file with license header and codegen warning.
class CppFile {
public:
// If "basepath" is empty the folder containing "path" will be chosen.
// File ending with .cpp will be treated as source, otherwise like header.
CppFile(const QString &path, const ProjectInfo &project);
QTextStream &stream() {
return stream_;
}
CppFile &newline() {
stream() << "\n";
return *this;
}
CppFile &include(const QString &header);
CppFile &includeFromLibrary(const QString &header);
// Empty name adds anonymous namespace.
CppFile &pushNamespace(const QString &name = QString());
CppFile &popNamespace();
bool finalize();
private:
QString filepath_;
QByteArray content_;
QTextStream stream_;
QVector<QString> namespaces_;
bool forceReGenerate_;
};
} // namespace common
} // namespace codegen

View File

@ -1,40 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/common/logging.h"
#include <iostream>
#include <QtCore/QDir>
namespace codegen {
namespace common {
namespace {
QString WorkingPath = ".";
std::string relativeLocalPath(const QString &filepath) {
auto name = QFile::encodeName(QDir(WorkingPath).relativeFilePath(filepath));
return name.constData();
}
} // namespace
LogStream logError(int code, const QString &filepath, int line) {
std::cerr << relativeLocalPath(filepath);
if (line > 0) {
std::cerr << '(' << line << ')';
}
std::cerr << ": error " << code << ": ";
return LogStream(std::cerr);
}
void logSetWorkingPath(const QString &workingpath) {
WorkingPath = workingpath;
}
} // namespace common
} // namespace codegen

View File

@ -1,67 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
#include <iostream>
namespace codegen {
namespace common {
// Common error codes.
constexpr int kErrorFileNotFound = 101;
constexpr int kErrorFileTooLarge = 102;
constexpr int kErrorFileNotOpened = 103;
constexpr int kErrorUnexpectedEndOfFile = 104;
// Wrapper around std::ostream that adds '\n' to the end of the logging line.
class LogStream {
public:
enum NullType {
Null,
};
explicit LogStream(NullType) : final_(false) {
}
explicit LogStream(std::ostream &stream) : stream_(&stream) {
}
LogStream(LogStream &&other) : stream_(other.stream_), final_(other.final_) {
other.final_ = false;
}
std::ostream *stream() const {
return stream_;
}
~LogStream() {
if (final_) {
*stream_ << '\n';
}
}
private:
std::ostream *stream_ = nullptr;
bool final_ = true;
};
template <typename T>
LogStream operator<<(LogStream &&stream, T &&value) {
if (auto ostream = stream.stream()) {
*ostream << std::forward<T>(value);
}
return std::forward<LogStream>(stream);
}
// Outputs file name, line number and error code to std::err. Usage:
// logError(kErrorFileTooLarge, filepath) << "file too large, size=" << size;
LogStream logError(int code, const QString &filepath, int line = 0);
void logSetWorkingPath(const QString &workingpath);
static constexpr int kErrorInternal = 666;
} // namespace common
} // namespace codegen

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "codegen/common/logging.h"
#include <vector>
#include <map>
#include <set>
#include <memory>
#include <functional>
#include <QtCore/QString>
#include <QtCore/QSet>
#include <QtCore/QMap>
namespace codegen {
namespace emoji {
using Id = QString;
struct Emoji {
Id id;
bool postfixed = false;
bool variated = false;
bool colored = false;
};
struct Data {
std::vector<Emoji> list;
std::map<Id, int, std::greater<Id>> map;
std::set<int> postfixRequired;
std::vector<std::vector<int>> categories;
std::map<QString, int, std::greater<QString>> replaces;
};
Data PrepareData();
constexpr auto kPostfix = 0xFE0FU;
common::LogStream logDataError();
} // namespace emoji
} // namespace codegen

File diff suppressed because it is too large Load Diff

View File

@ -1,81 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <QtCore/QString>
#include <QtCore/QSet>
#include "codegen/common/cpp_file.h"
#include "codegen/emoji/options.h"
#include "codegen/emoji/data.h"
#include "codegen/emoji/replaces.h"
namespace codegen {
namespace emoji {
using uint32 = unsigned int;
class Generator {
public:
Generator(const Options &options);
Generator(const Generator &other) = delete;
Generator &operator=(const Generator &other) = delete;
int generate();
private:
#ifdef SUPPORT_IMAGE_GENERATION
QImage generateImage(int imageIndex);
bool writeImages();
#endif // SUPPORT_IMAGE_GENERATION
bool writeSource();
bool writeHeader();
bool writeSuggestionsSource();
bool writeSuggestionsHeader();
template <typename Callback>
bool enumerateWholeList(Callback callback);
bool writeInitCode();
bool writeSections();
bool writeReplacements();
bool writeGetSections();
bool writeFindReplace();
bool writeFind();
bool writeFindFromDictionary(
const std::map<QString, int, std::greater<QString>> &dictionary,
bool skipPostfixes = false,
const std::set<int> &postfixRequired = {});
bool writeGetReplacements();
void startBinary();
bool writeStringBinary(common::CppFile *source, const QString &string);
void writeIntBinary(common::CppFile *source, int data);
void writeUintBinary(common::CppFile *source, uint32 data);
const common::ProjectInfo &project_;
int colorsCount_ = 0;
#ifdef SUPPORT_IMAGE_GENERATION
bool writeImages_ = false;
#endif // SUPPORT_IMAGE_GENERATION
QString outputPath_;
QString spritePath_;
std::unique_ptr<common::CppFile> source_;
Data data_;
QString suggestionsPath_;
std::unique_ptr<common::CppFile> suggestionsSource_;
Replaces replaces_;
int _binaryFullLength = 0;
int _binaryCount = 0;
};
} // namespace emoji
} // namespace codegen

View File

@ -1,27 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include <QtGui/QGuiApplication>
#include "codegen/emoji/options.h"
#include "codegen/emoji/generator.h"
int main(int argc, char *argv[]) {
#ifdef SUPPORT_IMAGE_GENERATION
#ifndef Q_OS_MAC
#error "Image generation is supported only on macOS"
#endif // Q_OS_MAC
QGuiApplication app(argc, argv);
#else // SUPPORT_IMAGE_GENERATION
QCoreApplication app(argc, argv);
#endif // SUPPORT_IMAGE_GENERATION
auto options = codegen::emoji::parseOptions();
codegen::emoji::Generator generator(options);
return generator.generate();
}

View File

@ -1,64 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/emoji/options.h"
#include <ostream>
#include <QtCore/QCoreApplication>
#include "codegen/common/logging.h"
namespace codegen {
namespace emoji {
namespace {
constexpr int kErrorOutputPathExpected = 902;
constexpr int kErrorReplacesPathExpected = 903;
constexpr int kErrorOneReplacesPathExpected = 904;
} // namespace
using common::logError;
Options parseOptions() {
Options result;
auto args = QCoreApplication::instance()->arguments();
for (int i = 1, count = args.size(); i < count; ++i) { // skip first
auto &arg = args.at(i);
// Output path
if (arg == "-o") {
if (++i == count) {
logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o";
return Options();
} else {
result.outputPath = args.at(i);
}
} else if (arg.startsWith("-o")) {
result.outputPath = arg.mid(2);
#ifdef SUPPORT_IMAGE_GENERATION
} else if (arg == "--images") {
result.writeImages = true;
#endif // SUPPORT_IMAGE_GENERATION
} else if (result.replacesPath.isEmpty()) {
result.replacesPath = arg;
} else {
logError(kErrorOneReplacesPathExpected, "Command Line") << "only one replaces path expected";
return Options();
}
}
if (result.outputPath.isEmpty()) {
logError(kErrorOutputPathExpected, "Command Line") << "output path expected";
return Options();
} else if (result.replacesPath.isEmpty()) {
logError(kErrorReplacesPathExpected, "Command Line") << "replaces path expected";
return Options();
}
return result;
}
} // namespace emoji
} // namespace codegen

View File

@ -1,32 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
#include <QtCore/QStringList>
#ifdef __APPLE__
#define SUPPORT_IMAGE_GENERATION
#endif // __APPLE__
namespace codegen {
namespace emoji {
struct Options {
QString outputPath = ".";
QString replacesPath;
#ifdef SUPPORT_IMAGE_GENERATION
bool writeImages = false;
#endif // SUPPORT_IMAGE_GENERATION
};
// Parsing failed if inputPath is empty in the result.
Options parseOptions();
} // namespace emoji
} // namespace codegen

View File

@ -1,391 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/emoji/replaces.h"
#include "codegen/emoji/data.h"
#include <QtCore/QFile>
#include <QtCore/QJsonDocument>
#include <QtCore/QJsonObject>
#include <QtCore/QRegularExpression>
namespace codegen {
namespace emoji {
namespace {
constexpr auto kErrorBadReplaces = 402;
common::LogStream logReplacesError(const QString &filename) {
return common::logError(kErrorBadReplaces, filename) << "Bad data: ";
}
auto RegExpCode = QRegularExpression("^:[\\+\\-a-z0-9_]+:$");
auto RegExpTone = QRegularExpression("_tone[0-9]");
auto RegExpHex = QRegularExpression("^[0-9a-f]+$");
class ReplacementWords {
public:
ReplacementWords(const QString &string);
QVector<QString> result() const;
private:
friend ReplacementWords operator+(const ReplacementWords &a, const ReplacementWords &b);
QMap<QString, int> wordsWithCounts_;
};
ReplacementWords::ReplacementWords(const QString &string) {
auto feedWord = [this](QString &word) {
if (!word.isEmpty()) {
++wordsWithCounts_[word];
word.clear();
}
};
// Split by all non-letters-or-numbers.
// Leave '-' and '+' inside a word only if they're followed by a number.
auto word = QString();
for (auto i = string.cbegin(), e = string.cend(); i != e; ++i) {
if (i->isLetterOrNumber()) {
word.append(*i);
continue;
} else if (*i == '-' || *i == '+') {
if (i + 1 != e && (i + 1)->isNumber()) {
word.append(*i);
continue;
}
}
feedWord(word);
}
feedWord(word);
}
QVector<QString> ReplacementWords::result() const {
auto result = QVector<QString>();
for (auto i = wordsWithCounts_.cbegin(), e = wordsWithCounts_.cend(); i != e; ++i) {
for (auto j = 0, count = i.value(); j != count; ++j) {
result.push_back(i.key());
}
}
return result;
}
ReplacementWords operator+(const ReplacementWords &a, const ReplacementWords &b) {
ReplacementWords result = a;
for (auto i = b.wordsWithCounts_.cbegin(), e = b.wordsWithCounts_.cend(); i != e; ++i) {
auto j = result.wordsWithCounts_.constFind(i.key());
if (j == result.wordsWithCounts_.cend() || j.value() < i.value()) {
result.wordsWithCounts_[i.key()] = i.value();
}
}
return result;
}
bool AddReplacement(Replaces &result, const Id &id, const QString &replacement, const QString &name) {
auto replace = Replace();
replace.id = id;
replace.replacement = replacement;
replace.words = (ReplacementWords(replacement)).result();// + ReplacementWords(name)).result();
if (replace.words.isEmpty()) {
logReplacesError(result.filename) << "Child '" << replacement.toStdString() << "' has no words.";
return false;
}
result.list.push_back(replace);
return true;
}
QString ComposeString(const std::initializer_list<QChar> &chars) {
auto result = QString();
result.reserve(chars.size());
for (auto ch : chars) {
result.append(ch);
}
return result;
}
const auto NotSupported = [] {
auto result = QSet<QString>();
auto insert = [&result](auto... args) {
result.insert(ComposeString({ args... }));
};
insert(0x0023, 0xFE0F); // :pound_symbol:
insert(0x002A, 0xFE0F); // :asterisk_symbol:
for (auto i = 0; i != 10; ++i) {
insert(0x0030 + i, 0xFE0F); // :digit_zero: ... :digit_nine:
}
for (auto i = 0; i != 5; ++i) {
insert(0xD83C, 0xDFFB + i); // :tone1: ... :tone5:
}
for (auto i = 0; i != 26; ++i) {
insert(0xD83C, 0xDDE6 + i); // :regional_indicator_a: ... :regional_indicator_z:
}
insert(0x2640, 0xFE0F); // :female_sign:
insert(0x2642, 0xFE0F); // :male_sign:
insert(0x2695, 0xFE0F); // :medical_symbol:
return result;
}();
const auto ConvertMap = ([] {
auto result = QMap<QString, QString>();
auto insert = [&result](const std::initializer_list<QChar> &from, const std::initializer_list<QChar> &to) {
result.insert(ComposeString(from), ComposeString(to));
};
auto insertWithAdd = [&result](const std::initializer_list<QChar> &from, const QString &added) {
auto code = ComposeString(from);
result.insert(code, code + added);
};
auto maleModifier = ComposeString({ 0x200D, 0x2642, 0xFE0F });
auto femaleModifier = ComposeString({ 0x200D, 0x2640, 0xFE0F });
insertWithAdd({ 0xD83E, 0xDD26 }, maleModifier);
insertWithAdd({ 0xD83E, 0xDD37 }, femaleModifier);
insertWithAdd({ 0xD83E, 0xDD38 }, maleModifier);
insertWithAdd({ 0xD83E, 0xDD39 }, maleModifier);
insertWithAdd({ 0xD83E, 0xDD3C }, maleModifier);
insertWithAdd({ 0xD83E, 0xDD3D }, maleModifier);
insertWithAdd({ 0xD83E, 0xDD3E }, femaleModifier);
// :kiss_woman_man:
insert({ 0xD83D, 0xDC69, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC8B, 0x200D, 0xD83D, 0xDC68 }, { 0xD83D, 0xDC8F });
// :family_man_woman_boy:
insert({ 0xD83D, 0xDC68, 0x200D, 0xD83D, 0xDC69, 0x200D, 0xD83D, 0xDC66 }, { 0xD83D, 0xDC6A });
// :couple_with_heart_woman_man:
insert({ 0xD83D, 0xDC69, 0x200D, 0x2764, 0xFE0F, 0x200D, 0xD83D, 0xDC68 }, { 0xD83D, 0xDC91 });
auto insertFlag = [insert](char ch1, char ch2, char ch3, char ch4) {
insert({ 0xD83C, 0xDDE6 + (ch1 - 'a'), 0xD83C, 0xDDe6 + (ch2 - 'a') }, { 0xD83C, 0xDDE6 + (ch3 - 'a'), 0xD83C, 0xDDe6 + (ch4 - 'a') });
};
insertFlag('a', 'c', 's', 'h');
insertFlag('b', 'v', 'n', 'o');
insertFlag('c', 'p', 'f', 'r');
insertFlag('d', 'g', 'i', 'o');
insertFlag('e', 'a', 'e', 's');
insertFlag('h', 'm', 'a', 'u');
insertFlag('m', 'f', 'f', 'r');
insertFlag('s', 'j', 'n', 'o');
insertFlag('t', 'a', 's', 'h');
insertFlag('u', 'm', 'u', 's');
return result;
})();
// Empty string result means we should skip this one.
QString ConvertEmojiId(const Id &id, const QString &replacement) {
if (RegExpTone.match(replacement).hasMatch()) {
return QString();
}
if (NotSupported.contains(id)) {
return QString();
}
return ConvertMap.value(id, id);
}
} // namespace
Replaces PrepareReplaces(const QString &filename) {
auto result = Replaces(filename);
auto content = ([filename] {
QFile f(filename);
return f.open(QIODevice::ReadOnly) ? f.readAll() : QByteArray();
})();
if (content.isEmpty()) {
logReplacesError(filename) << "Could not read data.";
return result;
}
auto error = QJsonParseError();
auto document = QJsonDocument::fromJson(content, &error);
if (error.error != QJsonParseError::NoError) {
logReplacesError(filename) << "Could not parse data (" << int(error.error) << "): " << error.errorString().toStdString();
return result;
}
if (!document.isObject()) {
logReplacesError(filename) << "Root object not found.";
return result;
}
auto list = document.object();
for (auto i = list.constBegin(), e = list.constEnd(); i != e; ++i) {
if (!(*i).isObject()) {
logReplacesError(filename) << "Child object not found.";
return Replaces(filename);
}
auto childKey = i.key();
auto child = (*i).toObject();
auto failed = false;
auto getString = [filename, childKey, &child, &failed](const QString &key) {
auto it = child.constFind(key);
if (it == child.constEnd() || !(*it).isString()) {
logReplacesError(filename) << "Child '" << childKey.toStdString() << "' field not found: " << key.toStdString();
failed = true;
return QString();
}
return (*it).toString();
};
auto idParts = getString("output").split('-');
auto name = getString("name");
auto replacement = getString("alpha_code");
auto aliases = getString("aliases").split('|');
const auto Exceptions = { ":shrug:" };
for (const auto &exception : Exceptions) {
const auto index = aliases.indexOf(exception);
if (index >= 0) {
aliases.removeAt(index);
}
}
if (aliases.size() == 1 && aliases[0].isEmpty()) {
aliases.clear();
}
if (failed) {
return Replaces(filename);
}
if (!RegExpCode.match(replacement).hasMatch()) {
logReplacesError(filename) << "Child '" << childKey.toStdString() << "' alpha_code invalid: " << replacement.toStdString();
return Replaces(filename);
}
for (auto &alias : aliases) {
if (!RegExpCode.match(alias).hasMatch()) {
logReplacesError(filename) << "Child '" << childKey.toStdString() << "' alias invalid: " << alias.toStdString();
return Replaces(filename);
}
}
auto id = Id();
for (auto &idPart : idParts) {
auto ok = true;
auto utf32 = idPart.toInt(&ok, 0x10);
if (!ok || !RegExpHex.match(idPart).hasMatch()) {
logReplacesError(filename) << "Child '" << childKey.toStdString() << "' output part invalid: " << idPart.toStdString();
return Replaces(filename);
}
if (utf32 >= 0 && utf32 < 0x10000) {
auto ch = QChar(ushort(utf32));
if (ch.isLowSurrogate() || ch.isHighSurrogate()) {
logReplacesError(filename) << "Child '" << childKey.toStdString() << "' output part invalid: " << idPart.toStdString();
return Replaces(filename);
}
id.append(ch);
} else if (utf32 >= 0x10000 && utf32 <= 0x10FFFF) {
auto hi = ((utf32 - 0x10000) / 0x400) + 0xD800;
auto lo = ((utf32 - 0x10000) % 0x400) + 0xDC00;
id.append(QChar(ushort(hi)));
id.append(QChar(ushort(lo)));
} else {
logReplacesError(filename) << "Child '" << childKey.toStdString() << "' output part invalid: " << idPart.toStdString();
return Replaces(filename);
}
}
id = ConvertEmojiId(id, replacement);
if (id.isEmpty()) {
continue;
}
if (!AddReplacement(result, id, replacement, name)) {
return Replaces(filename);
}
for (auto &alias : aliases) {
if (!AddReplacement(result, id, alias, name)) {
return Replaces(filename);
}
}
}
if (!AddReplacement(result, ComposeString({ 0xD83D, 0xDC4D }), ":like:", "thumbs up")) {
return Replaces(filename);
}
if (!AddReplacement(result, ComposeString({ 0xD83D, 0xDC4E }), ":dislike:", "thumbs down")) {
return Replaces(filename);
}
if (!AddReplacement(result, ComposeString({ 0xD83E, 0xDD14 }), ":hmm:", "thinking")) {
return Replaces(filename);
}
if (!AddReplacement(result, ComposeString({ 0xD83E, 0xDD73 }), ":party:", "celebration")) {
return Replaces(filename);
}
return result;
}
bool CheckAndConvertReplaces(Replaces &replaces, const Data &data) {
auto result = Replaces(replaces.filename);
auto sorted = QMap<Id, Replace>();
auto findId = [&data](const Id &id) {
return data.map.find(id) != data.map.cend();
};
auto findAndSort = [findId, &data, &sorted](Id id, const Replace &replace) {
if (!findId(id)) {
id.replace(QChar(0xFE0F), QString());
if (!findId(id)) {
return false;
}
}
auto it = data.map.find(id);
id = data.list[it->second].id;
if (data.list[it->second].postfixed) {
id += QChar(kPostfix);
}
auto inserted = sorted.insertMulti(id, replace);
inserted.value().id = id;
return true;
};
// Find all replaces in data.map, adjust id if necessary.
// Store all replaces in sorted map to find them fast afterwards.
auto maleModifier = ComposeString({ 0x200D, 0x2642, 0xFE0F });
auto femaleModifier = ComposeString({ 0x200D, 0x2640, 0xFE0F });
for (auto &replace : replaces.list) {
if (findAndSort(replace.id, replace)) {
continue;
}
if (replace.id.endsWith(maleModifier)) {
auto defaultId = replace.id.mid(0, replace.id.size() - maleModifier.size());
if (findAndSort(defaultId, replace)) {
continue;
}
} else if (replace.id.endsWith(femaleModifier)) {
auto defaultId = replace.id.mid(0, replace.id.size() - femaleModifier.size());
if (findAndSort(defaultId, replace)) {
continue;
}
} else if (findId(replace.id + maleModifier)) {
if (findId(replace.id + femaleModifier)) {
logReplacesError(replaces.filename) << "Replace '" << replace.replacement.toStdString() << "' ambiguous.";
return false;
} else {
findAndSort(replace.id + maleModifier, replace);
continue;
}
} else if (findAndSort(replace.id + femaleModifier, replace)) {
continue;
}
logReplacesError(replaces.filename) << "Replace '" << replace.replacement.toStdString() << "' not found.";
return false;
}
// Go through all categories and put all replaces in order of emoji in categories.
result.list.reserve(replaces.list.size());
for (auto &category : data.categories) {
for (auto index : category) {
auto id = data.list[index].id;
if (data.list[index].postfixed) {
id += QChar(kPostfix);
}
for (auto it = sorted.find(id); it != sorted.cend(); sorted.erase(it), it = sorted.find(id)) {
result.list.push_back(it.value());
}
}
}
if (result.list.size() != replaces.list.size()) {
logReplacesError(replaces.filename) << "Some were not found.";
return false;
}
if (!sorted.isEmpty()) {
logReplacesError(replaces.filename) << "Weird.";
return false;
}
replaces = std::move(result);
return true;
}
} // namespace emoji
} // namespace codegen

View File

@ -1,34 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "codegen/common/logging.h"
#include "codegen/emoji/data.h"
#include <QtCore/QVector>
namespace codegen {
namespace emoji {
struct Replace {
Id id;
QString replacement;
QVector<QString> words;
};
struct Replaces {
Replaces(const QString &filename) : filename(filename) {
}
QString filename;
QVector<Replace> list;
};
Replaces PrepareReplaces(const QString &filename);
bool CheckAndConvertReplaces(Replaces &replaces, const Data &data);
} // namespace emoji
} // namespace codegen

View File

@ -1,593 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/lang/generator.h"
#include <memory>
#include <functional>
#include <QtCore/QDir>
#include <QtCore/QSet>
#include <QtGui/QImage>
#include <QtGui/QPainter>
namespace codegen {
namespace lang {
namespace {
char hexChar(uchar ch) {
if (ch < 10) {
return '0' + ch;
} else if (ch < 16) {
return 'a' + (ch - 10);
}
return '0';
}
char hexSecondChar(char ch) {
return hexChar((*reinterpret_cast<uchar*>(&ch)) & 0x0F);
}
char hexFirstChar(char ch) {
return hexChar((*reinterpret_cast<uchar*>(&ch)) >> 4);
}
QString stringToEncodedString(const QString &str) {
QString result, lineBreak = "\\\n";
result.reserve(str.size() * 8);
bool writingHexEscapedCharacters = false, startOnNewLine = false;
int lastCutSize = 0;
auto utf = str.toUtf8();
for (auto ch : utf) {
if (result.size() - lastCutSize > 80) {
startOnNewLine = true;
result.append(lineBreak);
lastCutSize = result.size();
}
if (ch == '\n') {
writingHexEscapedCharacters = false;
result.append("\\n");
} else if (ch == '\t') {
writingHexEscapedCharacters = false;
result.append("\\t");
} else if (ch == '"' || ch == '\\') {
writingHexEscapedCharacters = false;
result.append('\\').append(ch);
} else if (ch < 32 || static_cast<uchar>(ch) > 127) {
writingHexEscapedCharacters = true;
result.append("\\x").append(hexFirstChar(ch)).append(hexSecondChar(ch));
} else {
if (writingHexEscapedCharacters) {
writingHexEscapedCharacters = false;
result.append("\"\"");
}
result.append(ch);
}
}
return '"' + (startOnNewLine ? lineBreak : QString()) + result + '"';
}
QString stringToEncodedString(const std::string &str) {
return stringToEncodedString(QString::fromStdString(str));
}
QString stringToBinaryArray(const std::string &str) {
QStringList rows, chars;
chars.reserve(13);
rows.reserve(1 + (str.size() / 13));
for (uchar ch : str) {
if (chars.size() > 12) {
rows.push_back(chars.join(", "));
chars.clear();
}
chars.push_back(QString("0x") + hexFirstChar(ch) + hexSecondChar(ch));
}
if (!chars.isEmpty()) {
rows.push_back(chars.join(", "));
}
return QString("{") + ((rows.size() > 1) ? '\n' : ' ') + rows.join(",\n") + " }";
}
} // namespace
Generator::Generator(const LangPack &langpack, const QString &destBasePath, const common::ProjectInfo &project)
: langpack_(langpack)
, basePath_(destBasePath)
, baseName_(QFileInfo(basePath_).baseName())
, project_(project) {
}
bool Generator::writeHeader() {
header_ = std::make_unique<common::CppFile>(basePath_ + ".h", project_);
header_->include("lang/lang_tag.h").include("lang/lang_values.h").newline();
writeHeaderForwardDeclarations();
writeHeaderTagTypes();
writeHeaderInterface();
writeHeaderReactiveInterface();
return header_->finalize();
}
void Generator::writeHeaderForwardDeclarations() {
header_->pushNamespace("Lang").stream() << "\
\n\
inline constexpr auto kTagsCount = ushort(" << langpack_.tags.size() << ");\n\
inline constexpr auto kKeysCount = ushort(" << langpack_.entries.size() << ");\n\
\n";
header_->popNamespace().newline();
}
void Generator::writeHeaderTagTypes() {
auto index = 0;
for (auto &tag : langpack_.tags) {
if (tag.tag == kPluralTags[0]) {
auto elements = QStringList();
header_->stream()
<< "enum lngtag_" << tag.tag << " : int { ";
for (auto i = 0; i != kPluralTags.size(); ++i) {
elements.push_back("lt_" + kPluralTags[i] + " = " + QString::number(index + i * 1000));
}
header_->stream() << elements.join(", ") << " };\n";
++index;
} else {
header_->stream() << "enum lngtag_" << tag.tag << " : int { lt_" << tag.tag << " = " << index++ << " };\n";
}
}
header_->newline();
}
void Generator::writeHeaderInterface() {
header_->pushNamespace("Lang").stream() << "\
\n\
ushort GetTagIndex(QLatin1String tag);\n\
ushort GetKeyIndex(QLatin1String key);\n\
bool IsTagReplaced(ushort key, ushort tag);\n\
QString GetOriginalValue(ushort key);\n\
\n";
writeHeaderTagValueLookup();
header_->popNamespace().newline();
}
void Generator::writeHeaderTagValueLookup() {
header_->pushNamespace("details").stream() << "\
\n\
template <typename Tag>\n\
struct TagData;\n\
\n\
template <typename Tag>\n\
inline constexpr ushort TagValue() {\n\
return TagData<Tag>::value;\n\
}\n\
\n";
for (auto &tag : langpack_.tags) {
header_->stream() << "template <> struct TagData<lngtag_" << tag.tag << "> : std::integral_constant<ushort, ushort(lt_" << tag.tag << ")> {};\n";
}
header_->newline().popNamespace();
}
void Generator::writeHeaderReactiveInterface() {
header_->pushNamespace("tr");
writeHeaderProducersInterface();
writeHeaderProducersInstances();
header_->popNamespace().newline();
}
void Generator::writeHeaderProducersInterface() {
header_->pushNamespace("details").stream() << "\
\n\
struct Identity {\n\
QString operator()(const QString &value) const {\n\
return value;\n\
}\n\
};\n\
\n";
header_->popNamespace().newline();
header_->stream() << "\
struct now_t {\n\
};\n\
\n\
inline constexpr now_t now{};\n\
\n\
inline auto to_count() {\n\
return rpl::map([](auto value) {\n\
return float64(value);\n\
});\n\
}\n\
\n\
template <typename P>\n\
using S = std::decay_t<decltype(std::declval<P>()(QString()))>;\n\
\n\
template <typename ...Tags>\n\
struct phrase;\n\
\n";
std::set<QString> producersDeclared;
for (auto &entry : langpack_.entries) {
const auto isPlural = !entry.keyBase.isEmpty();
const auto &key = entry.key;
auto tags = QStringList();
auto producerArgs = QStringList();
auto currentArgs = QStringList();
auto values = QStringList();
values.push_back("base");
values.push_back("std::move(p)");
for (auto &tagData : entry.tags) {
const auto &tag = tagData.tag;
const auto isPluralTag = isPlural && (tag == kPluralTags[0]);
tags.push_back("lngtag_" + tag);
const auto type1 = "lngtag_" + tag;
const auto arg1 = type1 + (isPluralTag ? " type" : "");
const auto producerType2 = (isPluralTag ? "rpl::producer<float64> " : "rpl::producer<S<P>> ");
const auto producerArg2 = producerType2 + tag + "__val";
const auto currentType2 = (isPluralTag ? "float64 " : "const S<P> &");
const auto currentArg2 = currentType2 + tag + "__val";
producerArgs.push_back(arg1 + ", " + producerArg2);
currentArgs.push_back(arg1 + ", " + currentArg2);
if (isPluralTag) {
values.push_back("type");
}
values.push_back(tag + "__val");
}
producerArgs.push_back("P p = P()");
currentArgs.push_back("P p = P()");
if (!producersDeclared.emplace(tags.join(',')).second) {
continue;
}
header_->stream() << "\
template <>\n\
struct phrase<" << tags.join(", ") << "> {\n\
template <typename P = details::Identity>\n\
rpl::producer<S<P>> operator()(" << producerArgs.join(", ") << ") const {\n\
return ::Lang::details::Producer<" << tags.join(", ") << ">::template Combine(" << values.join(", ") << ");\n\
}\n\
\n\
template <typename P = details::Identity>\n\
S<P> operator()(now_t, " << currentArgs.join(", ") << ") const {\n\
return ::Lang::details::Producer<" << tags.join(", ") << ">::template Current(" << values.join(", ") << ");\n\
}\n\
\n\
ushort base;\n\
};\n\
\n";
}
}
void Generator::writeHeaderProducersInstances() {
auto index = 0;
for (auto &entry : langpack_.entries) {
const auto isPlural = !entry.keyBase.isEmpty();
const auto &key = entry.key;
auto tags = QStringList();
for (auto &tagData : entry.tags) {
const auto &tag = tagData.tag;
tags.push_back("lngtag_" + tag);
}
if (!isPlural || key == ComputePluralKey(entry.keyBase, 0)) {
header_->stream() << "\
inline constexpr phrase<" << tags.join(", ") << "> " << (isPlural ? entry.keyBase : key) << "{ ushort(" << index << ") };\n";
}
++index;
}
header_->newline();
}
bool Generator::writeSource() {
source_ = std::make_unique<common::CppFile>(basePath_ + ".cpp", project_);
source_->include("lang/lang_keys.h").pushNamespace("Lang").pushNamespace();
source_->stream() << "\
QChar DefaultData[] = {";
auto count = 0;
auto fulllength = 0;
for (auto &entry : langpack_.entries) {
for (auto ch : entry.value) {
if (fulllength > 0) source_->stream() << ",";
if (!count++) {
source_->stream() << "\n";
} else {
if (count == 12) {
count = 0;
}
source_->stream() << " ";
}
source_->stream() << "0x" << QString::number(ch.unicode(), 16);
++fulllength;
}
}
source_->stream() << " };\n\
\n\
int Offsets[] = {";
count = 0;
auto offset = 0;
auto writeOffset = [this, &count, &offset] {
if (offset > 0) source_->stream() << ",";
if (!count++) {
source_->stream() << "\n";
} else {
if (count == 12) {
count = 0;
}
source_->stream() << " ";
}
source_->stream() << offset;
};
for (auto &entry : langpack_.entries) {
writeOffset();
offset += entry.value.size();
}
writeOffset();
source_->stream() << " };\n";
source_->popNamespace().stream() << "\
\n\
ushort GetTagIndex(QLatin1String tag) {\n\
auto size = tag.size();\n\
auto data = tag.data();\n";
auto tagsSet = std::set<QString, std::greater<>>();
for (auto &tag : langpack_.tags) {
tagsSet.insert(tag.tag);
}
writeSetSearch(tagsSet, [](const QString &tag) {
return "ushort(lt_" + tag + ")";
}, "kTagsCount");
source_->stream() << "\
}\n\
\n\
ushort GetKeyIndex(QLatin1String key) {\n\
auto size = key.size();\n\
auto data = key.data();\n";
auto index = 0;
auto indices = std::map<QString, QString>();
for (auto &entry : langpack_.entries) {
indices.emplace(getFullKey(entry), QString::number(index++));
}
const auto indexOfKey = [&](const QString &full) {
const auto i = indices.find(full);
if (i == indices.end()) {
return QString();
}
return i->second;
};
auto taggedKeys = std::map<QString, QString>();
auto keysSet = std::set<QString, std::greater<>>();
for (auto &entry : langpack_.entries) {
if (!entry.keyBase.isEmpty()) {
for (auto i = 0; i != kPluralPartCount; ++i) {
auto keyName = entry.keyBase + '#' + kPluralParts[i];
taggedKeys.emplace(keyName, ComputePluralKey(entry.keyBase, i));
keysSet.insert(keyName);
}
} else {
auto full = getFullKey(entry);
if (full != entry.key) {
taggedKeys.emplace(entry.key, full);
}
keysSet.insert(entry.key);
}
}
writeSetSearch(keysSet, [&](const QString &key) {
auto it = taggedKeys.find(key);
const auto name = (it != taggedKeys.end()) ? it->second : key;
return indexOfKey(name);
}, "kKeysCount");
header_->popNamespace().newline();
source_->stream() << "\
}\n\
\n\
bool IsTagReplaced(ushort key, ushort tag) {\n\
switch (key) {\n";
auto lastWrittenPluralEntry = QString();
for (auto &entry : langpack_.entries) {
if (entry.tags.empty()) {
continue;
}
if (!entry.keyBase.isEmpty()) {
if (entry.keyBase == lastWrittenPluralEntry) {
continue;
}
lastWrittenPluralEntry = entry.keyBase;
for (auto i = 0; i != kPluralPartCount; ++i) {
source_->stream() << "\
case " << indexOfKey(ComputePluralKey(entry.keyBase, i)) << ":" << ((i + 1 == kPluralPartCount) ? " {" : "") << "\n";
}
} else {
source_->stream() << "\
case " << indexOfKey(getFullKey(entry)) << ": {\n";
}
source_->stream() << "\
switch (tag) {\n";
for (auto &tag : entry.tags) {
source_->stream() << "\
case lt_" << tag.tag << ":\n";
}
source_->stream() << "\
return true;\n\
}\n\
} break;\n";
}
source_->stream() << "\
}\
\n\
return false;\n\
}\n\
\n\
QString GetOriginalValue(ushort key) {\n\
Expects(key < kKeysCount);\n\
\n\
const auto offset = Offsets[key];\n\
return QString::fromRawData(DefaultData + offset, Offsets[key + 1] - offset);\n\
}\n\
\n";
return source_->finalize();
}
template <typename ComputeResult>
void Generator::writeSetSearch(const std::set<QString, std::greater<>> &set, ComputeResult computeResult, const QString &invalidResult) {
auto tabs = [](int size) {
return QString(size, '\t');
};
enum class UsedCheckType {
Switch,
If,
UpcomingIf,
};
auto checkTypes = QVector<UsedCheckType>();
auto checkLengthHistory = QVector<int>();
auto chars = QString();
auto tabsUsed = 1;
// Returns true if at least one check was finished.
auto finishChecksTillKey = [this, &chars, &checkTypes, &checkLengthHistory, &tabsUsed, tabs](const QString &key) {
auto result = false;
while (!chars.isEmpty() && key.midRef(0, chars.size()) != chars) {
result = true;
auto wasType = checkTypes.back();
chars.resize(chars.size() - 1);
checkTypes.pop_back();
if (wasType == UsedCheckType::Switch || wasType == UsedCheckType::If) {
--tabsUsed;
if (wasType == UsedCheckType::Switch) {
source_->stream() << tabs(tabsUsed) << "break;\n";
}
if ((!chars.isEmpty() && key.midRef(0, chars.size()) != chars) || key == chars) {
source_->stream() << tabs(tabsUsed) << "}\n";
checkLengthHistory.pop_back();
}
}
}
return result;
};
// Check if we can use "if" for a check on "charIndex" in "it" (otherwise only "switch")
auto canUseIfForCheck = [](auto it, auto end, int charIndex) {
auto key = *it;
auto i = it;
auto keyStart = key.mid(0, charIndex);
for (++i; i != end; ++i) {
auto nextKey = *i;
if (nextKey.mid(0, charIndex) != keyStart) {
return true;
} else if (nextKey.size() > charIndex && nextKey[charIndex] != key[charIndex]) {
return false;
}
}
return true;
};
auto countMinimalLength = [](auto it, auto end, int charIndex) {
auto key = *it;
auto i = it;
auto keyStart = key.mid(0, charIndex);
auto result = key.size();
for (++i; i != end; ++i) {
auto nextKey = *i;
if (nextKey.mid(0, charIndex) != keyStart) {
break;
} else if (nextKey.size() > charIndex && result > nextKey.size()) {
result = nextKey.size();
}
}
return result;
};
for (auto i = set.begin(), e = set.end(); i != e; ++i) {
// If we use just "auto" here and "name" becomes mutable,
// the operator[] will return QCharRef instead of QChar,
// and "auto ch = name[index]" will behave like "auto &ch =",
// if you assign something to "ch" after that you'll change "name" (!)
const auto name = *i;
auto weContinueOldSwitch = finishChecksTillKey(name);
while (chars.size() != name.size()) {
auto checking = chars.size();
auto partialKey = name.mid(0, checking);
auto keyChar = name[checking];
auto usedIfForCheckCount = 0;
auto minimalLengthCheck = countMinimalLength(i, e, checking);
for (; checking + usedIfForCheckCount != name.size(); ++usedIfForCheckCount) {
if (!canUseIfForCheck(i, e, checking + usedIfForCheckCount)
|| countMinimalLength(i, e, checking + usedIfForCheckCount) != minimalLengthCheck) {
break;
}
}
auto usedIfForCheck = !weContinueOldSwitch && (usedIfForCheckCount > 0);
const auto checkedLength = checkLengthHistory.isEmpty()
? 0
: checkLengthHistory.back();
const auto requiredLength = qMax(
minimalLengthCheck,
checkedLength);
auto checkLengthCondition = QString();
if (weContinueOldSwitch) {
weContinueOldSwitch = false;
} else {
checkLengthCondition = (requiredLength > checkedLength) ? ("size >= " + QString::number(requiredLength)) : QString();
if (!usedIfForCheck) {
source_->stream() << tabs(tabsUsed) << (checkLengthCondition.isEmpty() ? QString() : ("if (" + checkLengthCondition + ") ")) << "switch (data[" << checking << "]) {\n";
checkLengthHistory.push_back(requiredLength);
}
}
if (usedIfForCheck) {
auto conditions = QStringList();
if (usedIfForCheckCount > 1) {
conditions.push_back("!memcmp(data + " + QString::number(checking) + ", \"" + name.mid(checking, usedIfForCheckCount) + "\", " + QString::number(usedIfForCheckCount) + ")");
} else {
conditions.push_back("data[" + QString::number(checking) + "] == '" + keyChar + "'");
}
if (!checkLengthCondition.isEmpty()) {
conditions.push_front(checkLengthCondition);
}
source_->stream() << tabs(tabsUsed) << "if (" << conditions.join(" && ") << ") {\n";
checkLengthHistory.push_back(requiredLength);
checkTypes.push_back(UsedCheckType::If);
for (auto i = 1; i != usedIfForCheckCount; ++i) {
checkTypes.push_back(UsedCheckType::UpcomingIf);
chars.push_back(keyChar);
keyChar = name[checking + i];
}
} else {
source_->stream() << tabs(tabsUsed) << "case '" << keyChar << "':\n";
checkTypes.push_back(UsedCheckType::Switch);
}
++tabsUsed;
chars.push_back(keyChar);
}
source_->stream() << tabs(tabsUsed) << "return (size == " << chars.size() << ") ? " << computeResult(name) << " : " << invalidResult << ";\n";
}
finishChecksTillKey(QString());
source_->stream() << "\
\n\
return " << invalidResult << ";\n";
}
QString Generator::getFullKey(const LangPack::Entry &entry) {
if (!entry.keyBase.isEmpty() || entry.tags.empty()) {
return entry.key;
}
return entry.key + "__tagged";
}
} // namespace lang
} // namespace codegen

View File

@ -1,53 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <map>
#include <set>
#include <functional>
#include <QtCore/QString>
#include <QtCore/QSet>
#include "codegen/common/cpp_file.h"
#include "codegen/lang/parsed_file.h"
namespace codegen {
namespace lang {
class Generator {
public:
Generator(const LangPack &langpack, const QString &destBasePath, const common::ProjectInfo &project);
Generator(const Generator &other) = delete;
Generator &operator=(const Generator &other) = delete;
bool writeHeader();
bool writeSource();
private:
void writeHeaderForwardDeclarations();
void writeHeaderTagTypes();
void writeHeaderInterface();
void writeHeaderTagValueLookup();
void writeHeaderReactiveInterface();
void writeHeaderProducersInterface();
void writeHeaderProducersInstances();
QString getFullKey(const LangPack::Entry &entry);
template <typename ComputeResult>
void writeSetSearch(const std::set<QString, std::greater<>> &set, ComputeResult computeResult, const QString &invalidResult);
const LangPack &langpack_;
QString basePath_, baseName_;
const common::ProjectInfo &project_;
std::unique_ptr<common::CppFile> source_, header_;
};
} // namespace lang
} // namespace codegen

View File

@ -1,23 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include <QtCore/QCoreApplication>
#include "codegen/lang/options.h"
#include "codegen/lang/processor.h"
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
auto options = codegen::lang::parseOptions();
if (options.inputPath.isEmpty()) {
return -1;
}
codegen::lang::Processor processor(options);
return processor.launch();
}

View File

@ -1,74 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/lang/options.h"
#include <ostream>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include "codegen/common/logging.h"
namespace codegen {
namespace lang {
namespace {
constexpr int kErrorOutputPathExpected = 902;
constexpr int kErrorInputPathExpected = 903;
constexpr int kErrorSingleInputPathExpected = 904;
constexpr int kErrorWorkingPathExpected = 905;
} // namespace
using common::logError;
Options parseOptions() {
Options result;
auto args = QCoreApplication::instance()->arguments();
for (int i = 1, count = args.size(); i < count; ++i) { // skip first
auto &arg = args.at(i);
// Output path
if (arg == "-o") {
if (++i == count) {
logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o";
return Options();
} else {
result.outputPath = args.at(i);
}
} else if (arg.startsWith("-o")) {
result.outputPath = arg.mid(2);
// Working path
} else if (arg == "-w") {
if (++i == count) {
logError(kErrorWorkingPathExpected, "Command Line") << "working path expected after -w";
return Options();
} else {
common::logSetWorkingPath(args.at(i));
}
} else if (arg.startsWith("-w")) {
common::logSetWorkingPath(arg.mid(2));
// Input path
} else {
if (result.inputPath.isEmpty()) {
result.inputPath = arg;
} else {
logError(kErrorSingleInputPathExpected, "Command Line") << "only one input path expected";
return Options();
}
}
}
if (result.inputPath.isEmpty()) {
logError(kErrorInputPathExpected, "Command Line") << "input path expected";
return Options();
}
return result;
}
} // namespace lang
} // namespace codegen

View File

@ -1,25 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
#include <QtCore/QStringList>
namespace codegen {
namespace lang {
struct Options {
QString outputPath = ".";
QString inputPath;
};
// Parsing failed if inputPath is empty in the result.
Options parseOptions();
} // namespace lang
} // namespace codegen

View File

@ -1,341 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/lang/parsed_file.h"
#include <iostream>
#include <set>
#include <QtCore/QMap>
#include <QtCore/QDir>
#include <QtCore/QRegularExpression>
#include "codegen/common/basic_tokenized_file.h"
#include "codegen/common/logging.h"
namespace codegen {
namespace lang {
namespace {
using BasicToken = codegen::common::BasicTokenizedFile::Token;
using BasicType = BasicToken::Type;
constexpr int kErrorBadString = 806;
bool ValidateAnsiString(const QString &value) {
for (auto ch : value) {
if (ch.unicode() > 127) {
return false;
}
}
return true;
}
bool ValidateKey(const QString &key) {
static const auto validator = QRegularExpression("^[a-z0-9_.-]+(#(one|other))?$", QRegularExpression::CaseInsensitiveOption);
if (!validator.match(key).hasMatch()) {
return false;
}
if (key.indexOf("__") >= 0) {
return false;
}
return true;
}
bool ValidateTag(const QString &tag) {
static const auto validator = QRegularExpression("^[a-z0-9_]+$", QRegularExpression::CaseInsensitiveOption);
if (!validator.match(tag).hasMatch()) {
return false;
}
if (tag.indexOf("__") >= 0) {
return false;
}
return true;
}
QString PrepareCommandString(int index) {
static const QChar TextCommand(0x0010);
static const QChar TextCommandLangTag(0x0020);
auto result = QString(4, TextCommand);
result[1] = TextCommandLangTag;
result[2] = QChar(0x0020 + ushort(index));
return result;
}
} // namespace
const std::array<QString, kPluralPartCount> kPluralParts = { {
"zero",
"one",
"two",
"few",
"many",
"other",
} };
const std::array<QString, kPluralTagsCount> kPluralTags = { {
"count",
"count_short",
"count_decimal",
} };
QString ComputePluralKey(const QString &base, int index) {
return base + "__plural" + QString::number(index);
}
ParsedFile::ParsedFile(const Options &options)
: filePath_(options.inputPath)
, file_(filePath_)
, options_(options) {
}
bool ParsedFile::read() {
if (!file_.read()) {
return false;
}
do {
if (auto keyToken = file_.getToken(BasicType::String)) {
if (ValidateKey(keyToken.value)) {
if (auto equals = file_.getToken(BasicType::Equals)) {
if (auto valueToken = file_.getToken(BasicType::String)) {
assertNextToken(BasicType::Semicolon);
addEntity(keyToken.value, valueToken.value);
continue;
} else {
logErrorUnexpectedToken() << "string value for '" << keyToken.value.toStdString() << "' key";
}
} else {
logErrorUnexpectedToken() << "'=' for '" << keyToken.value.toStdString() << "' key";
}
} else {
logErrorUnexpectedToken() << "string key name (/^[a-z0-9_.-]+(#(one|other))?$/i)";
}
}
if (file_.atEnd()) {
break;
}
logErrorUnexpectedToken() << "ansi string key name";
} while (!failed());
fillPluralTags();
return !failed();
}
void ParsedFile::fillPluralTags() {
auto count = result_.entries.size();
for (auto i = 0; i != count;) {
auto &baseEntry = result_.entries[i];
if (baseEntry.keyBase.isEmpty()) {
++i;
continue;
}
logAssert(i + kPluralPartCount < count);
// Accumulate all tags from all plural variants.
auto tags = std::vector<LangPack::Tag>();
for (auto j = i; j != i + kPluralPartCount; ++j) {
if (tags.empty()) {
tags = result_.entries[j].tags;
} else {
for (auto &tag : result_.entries[j].tags) {
if (std::find(tags.begin(), tags.end(), tag) == tags.end()) {
tags.push_back(tag);
}
}
}
}
logAssert(!tags.empty());
logAssert(tags.front().tag == kPluralTags[0]);
// Set this tags list to all plural variants.
for (auto j = i; j != i + kPluralPartCount; ++j) {
result_.entries[j].tags = tags;
}
i += kPluralPartCount;
}
}
BasicToken ParsedFile::assertNextToken(BasicToken::Type type) {
auto result = file_.getToken(type);
if (!result) {
logErrorUnexpectedToken() << type;
}
return result;
}
common::LogStream ParsedFile::logErrorBadString() {
return logError(kErrorBadString);
}
QString ParsedFile::extractTagsData(const QString &value, LangPack *to) {
auto tagStart = value.indexOf('{');
if (tagStart < 0) {
return value;
}
auto tagEnd = 0;
auto finalValue = QString();
finalValue.reserve(value.size() * 2);
while (tagStart >= 0) {
if (tagStart > tagEnd) {
finalValue.append(value.midRef(tagEnd, tagStart - tagEnd));
}
++tagStart;
tagEnd = value.indexOf('}', tagStart);
if (tagEnd < 0) {
logErrorBadString() << "unexpected end of value, end of tag expected.";
return value;
}
finalValue.append(extractTagData(value.mid(tagStart, tagEnd - tagStart), to));
++tagEnd;
tagStart = value.indexOf('{', tagEnd);
}
if (tagEnd < value.size()) {
finalValue.append(value.midRef(tagEnd));
}
return finalValue;
}
QString ParsedFile::extractTagData(const QString &tagText, LangPack *to) {
auto numericPart = tagText.indexOf(':');
auto tag = (numericPart > 0) ? tagText.mid(0, numericPart) : tagText;
if (!ValidateTag(tag)) {
logErrorBadString() << "bad tag characters: '" << tagText.toStdString() << "'";
return QString();
}
for (auto &previousTag : to->tags) {
if (previousTag.tag == tag) {
logErrorBadString() << "duplicate found for tag '" << tagText.toStdString() << "'";
return QString();
}
}
auto index = 0;
auto tagIndex = result_.tags.size();
for (auto &alreadyTag : result_.tags) {
if (alreadyTag.tag == tag) {
tagIndex = index;
break;
}
++index;
}
if (tagIndex == result_.tags.size()) {
result_.tags.push_back({ tag });
}
if (numericPart > 0) {
auto numericParts = tagText.mid(numericPart + 1).split('|');
if (numericParts.size() != 3) {
logErrorBadString() << "bad option count for plural key part in tag: '" << tagText.toStdString() << "'";
return QString();
}
auto index = 0;
for (auto &part : numericParts) {
auto numericPartEntry = LangPack::Entry();
numericPartEntry.key = tag + QString::number(index++);
if (part.indexOf('#') != part.lastIndexOf('#')) {
logErrorBadString() << "bad option for plural key part in tag: '" << tagText.toStdString() << "', too many '#'.";
return QString();
}
numericPartEntry.value = part.replace('#', PrepareCommandString(tagIndex));
to->entries.push_back(numericPartEntry);
}
}
to->tags.push_back({ tag });
return PrepareCommandString(tagIndex);
}
void ParsedFile::addEntity(QString key, const QString &value) {
auto pluralPartOffset = key.indexOf('#');
auto pluralIndex = -1;
if (pluralPartOffset >= 0) {
auto pluralPart = key.mid(pluralPartOffset + 1);
pluralIndex = std::find(kPluralParts.begin(), kPluralParts.end(), pluralPart) - kPluralParts.begin();
if (pluralIndex < 0 || pluralIndex >= kPluralParts.size()) {
logErrorBadString() << "bad plural part for key '" << key.toStdString() << "': '" << pluralPart.toStdString() << "'";
return;
}
key = key.mid(0, pluralPartOffset);
}
auto checkKey = [this](const QString &key) {
for (auto &entry : result_.entries) {
if (entry.key == key) {
if (entry.keyBase.isEmpty() || !entry.tags.empty()) {
// Empty tags in plural entry means it was not encountered yet.
logErrorBadString() << "duplicate found for key '" << key.toStdString() << "'";
return false;
}
}
}
return true;
};
if (!checkKey(key)) {
return;
}
auto tagsData = LangPack();
auto entry = LangPack::Entry();
entry.key = key;
entry.value = extractTagsData(value, &tagsData);
entry.tags = tagsData.tags;
if (pluralIndex >= 0) {
logAssert(tagsData.entries.empty());
entry.keyBase = entry.key;
entry.key = ComputePluralKey(entry.keyBase, pluralIndex);
if (!checkKey(entry.key)) {
return;
}
auto baseIndex = -1;
auto alreadyCount = result_.entries.size();
for (auto i = 0; i != alreadyCount; ++i) {
if (result_.entries[i].keyBase == entry.keyBase) {
// This is not the first appearance of this plural key.
baseIndex = i;
break;
}
}
if (baseIndex < 0) {
baseIndex = result_.entries.size();
for (auto i = 0; i != kPluralPartCount; ++i) {
auto addingEntry = LangPack::Entry();
addingEntry.keyBase = entry.keyBase;
addingEntry.key = ComputePluralKey(entry.keyBase, i);
result_.entries.push_back(addingEntry);
}
}
auto entryIndex = baseIndex + pluralIndex;
logAssert(entryIndex < result_.entries.size());
auto &realEntry = result_.entries[entryIndex];
logAssert(realEntry.key == entry.key);
realEntry.value = entry.value;
// Add all new tags to the existing ones.
realEntry.tags = std::vector<LangPack::Tag>(1, LangPack::Tag{ kPluralTags[0] });
for (auto &tag : entry.tags) {
if (std::find(realEntry.tags.begin(), realEntry.tags.end(), tag) == realEntry.tags.end()) {
realEntry.tags.push_back(tag);
}
}
} else {
result_.entries.push_back(entry);
for (auto &tag : entry.tags) {
const auto plural = std::find(std::begin(kPluralTags), std::end(kPluralTags), tag.tag);
if (plural != std::end(kPluralTags)) {
logErrorBadString() << "plural tag '" << tag.tag.toStdString() << "' used in non-plural key '" << key.toStdString() << "'";
}
}
for (auto &tagEntry : tagsData.entries) {
auto taggedEntry = LangPack::Entry();
taggedEntry.key = key + "__" + tagEntry.key;
taggedEntry.value = tagEntry.value;
result_.entries.push_back(taggedEntry);
}
}
}
} // namespace lang
} // namespace codegen

View File

@ -1,106 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <string>
#include <array>
#include <functional>
#include <QImage>
#include "codegen/common/basic_tokenized_file.h"
#include "codegen/lang/options.h"
namespace codegen {
namespace lang {
constexpr auto kPluralPartCount = 6;
extern const std::array<QString, kPluralPartCount> kPluralParts;
constexpr auto kPluralTagsCount = 3;
extern const std::array<QString, kPluralTagsCount> kPluralTags;
QString ComputePluralKey(const QString &base, int index);
struct LangPack {
struct Tag {
QString tag;
};
struct Entry {
QString key;
QString value;
QString keyBase; // Empty for not plural entries.
std::vector<Tag> tags;
};
std::vector<Entry> entries;
std::vector<Tag> tags;
};
inline bool operator==(const LangPack::Tag &a, const LangPack::Tag &b) {
return a.tag == b.tag;
}
inline bool operator!=(const LangPack::Tag &a, const LangPack::Tag &b) {
return !(a == b);
}
// Parses an input file to the internal struct.
class ParsedFile {
public:
explicit ParsedFile(const Options &options);
ParsedFile(const ParsedFile &other) = delete;
ParsedFile &operator=(const ParsedFile &other) = delete;
bool read();
LangPack getResult() {
return result_;
}
private:
bool failed() const {
return failed_ || file_.failed();
}
// Log error to std::cerr with 'code' at the current position in file.
common::LogStream logError(int code) {
failed_ = true;
return file_.logError(code);
}
common::LogStream logErrorUnexpectedToken() {
failed_ = true;
return file_.logErrorUnexpectedToken();
}
common::LogStream logErrorBadString();
common::LogStream logAssert(bool assertion) {
if (!assertion) {
return logError(common::kErrorInternal) << "internal - ";
}
return common::LogStream(common::LogStream::Null);
}
// Read next token and fire unexpected token error if it is not of "type".
using BasicToken = common::BasicTokenizedFile::Token;
BasicToken assertNextToken(BasicToken::Type type);
void addEntity(QString key, const QString &value);
QString extractTagsData(const QString &value, LangPack *to);
QString extractTagData(const QString &tag, LangPack *to);
void fillPluralTags();
QString filePath_;
common::BasicTokenizedFile file_;
Options options_;
bool failed_ = false;
LangPack result_;
};
} // namespace lang
} // namespace codegen

View File

@ -1,71 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/lang/processor.h"
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include "codegen/common/cpp_file.h"
#include "codegen/lang/parsed_file.h"
#include "codegen/lang/generator.h"
namespace codegen {
namespace lang {
namespace {
constexpr int kErrorCantWritePath = 821;
} // namespace
Processor::Processor(const Options &options)
: parser_(std::make_unique<ParsedFile>(options))
, options_(options) {
}
int Processor::launch() {
if (!parser_->read()) {
return -1;
}
if (!write(parser_->getResult())) {
return -1;
}
return 0;
}
bool Processor::write(const LangPack &langpack) const {
bool forceReGenerate = false;
QDir dir(options_.outputPath);
if (!dir.mkpath(".")) {
common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString();
return false;
}
QFileInfo srcFile(options_.inputPath);
QString dstFilePath = dir.absolutePath() + "/lang_auto";
common::ProjectInfo project = {
"codegen_style",
srcFile.fileName(),
forceReGenerate
};
Generator generator(langpack, dstFilePath, project);
if (!generator.writeHeader()) {
return false;
}
if (!generator.writeSource()) {
return false;
}
return true;
}
Processor::~Processor() = default;
} // namespace lang
} // namespace codegen

View File

@ -1,40 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <QtCore/QString>
#include "codegen/lang/options.h"
namespace codegen {
namespace lang {
class ParsedFile;
struct LangPack;
// Walks through a file, parses it and generates the output.
class Processor {
public:
explicit Processor(const Options &options);
Processor(const Processor &other) = delete;
Processor &operator=(const Processor &other) = delete;
// Returns 0 on success.
int launch();
~Processor();
private:
bool write(const LangPack &langpack) const;
std::unique_ptr<ParsedFile> parser_;
const Options &options_;
};
} // namespace lang
} // namespace codegen

View File

@ -1,97 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/numbers/generator.h"
#include <QtCore/QDir>
#include <QtCore/QSet>
#include <functional>
namespace codegen {
namespace numbers {
namespace {
} // namespace
Generator::Generator(const Rules &rules, const QString &destBasePath, const common::ProjectInfo &project)
: rules_(rules)
, basePath_(destBasePath)
, project_(project) {
}
bool Generator::writeHeader() {
header_ = std::make_unique<common::CppFile>(basePath_ + ".h", project_);
header_->stream() << "QVector<int> phoneNumberParse(const QString &number);\n";
return header_->finalize();
}
bool Generator::writeSource() {
source_ = std::make_unique<common::CppFile>(basePath_ + ".cpp", project_);
source_->stream() << "\
QVector<int> phoneNumberParse(const QString &number) {\n\
QVector<int> result;\n\
\n\
int32 len = number.size();\n\
if (len > 0) switch (number.at(0).unicode()) {\n";
QString already;
for (auto i = rules_.data.cend(), e = rules_.data.cbegin(); i != e;) {
--i;
QString k = i.key();
bool onlyLastChanged = true;
while (!already.isEmpty() && (already.size() > k.size() || !already.endsWith(k.at(already.size() - 1)))) {
if (!onlyLastChanged) {
source_->stream() << QString("\t").repeated(1 + already.size()) << "}\n";
source_->stream() << QString("\t").repeated(already.size()) << "break;\n";
}
already = already.mid(0, already.size() - 1);
onlyLastChanged = false;
}
if (already == k) {
source_->stream() << QString("\t").repeated(1 + already.size()) << "}\n";
} else {
bool onlyFirstCheck = true;
while (already.size() < k.size()) {
if (!onlyFirstCheck) source_->stream() << QString("\t").repeated(1 + already.size()) << "if (len > " << already.size() << ") switch (number.at(" << already.size() << ").unicode()) {\n";
source_->stream() << QString("\t").repeated(1 + already.size()) << "case '" << k.at(already.size()).toLatin1() << "':\n";
already.push_back(k.at(already.size()));
onlyFirstCheck = false;
}
}
if (i.value().isEmpty()) {
source_->stream() << QString("\t").repeated(1 + already.size()) << "return QVector<int>(1, " << k.size() << ");\n";
} else {
source_->stream() << QString("\t").repeated(1 + already.size()) << "result.reserve(" << (i.value().size() + 1) << ");\n";
source_->stream() << QString("\t").repeated(1 + already.size()) << "result.push_back(" << k.size() << ");\n";
for (int j = 0, l = i.value().size(); j < l; ++j) {
source_->stream() << QString("\t").repeated(1 + already.size()) << "result.push_back(" << i.value().at(j) << ");\n";
}
source_->stream() << QString("\t").repeated(1 + already.size()) << "return result;\n";
}
}
bool onlyLastChanged = true;
while (!already.isEmpty()) {
if (!onlyLastChanged) {
source_->stream() << QString("\t").repeated(1 + already.size()) << "}\n";
}
already = already.mid(0, already.size() - 1);
onlyLastChanged = false;
}
source_->stream() << "\
}\n\
\n\
return result;\n\
}\n";
return source_->finalize();
}
} // namespace numbers
} // namespace codegen

View File

@ -1,37 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <QtCore/QString>
#include <QtCore/QSet>
#include "codegen/common/cpp_file.h"
#include "codegen/numbers/parsed_file.h"
namespace codegen {
namespace numbers {
class Generator {
public:
Generator(const Rules &rules, const QString &destBasePath, const common::ProjectInfo &project);
Generator(const Generator &other) = delete;
Generator &operator=(const Generator &other) = delete;
bool writeHeader();
bool writeSource();
private:
const Rules &rules_;
QString basePath_;
const common::ProjectInfo &project_;
std::unique_ptr<common::CppFile> source_, header_;
};
} // namespace numbers
} // namespace codegen

View File

@ -1,23 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include <QtCore/QCoreApplication>
#include "codegen/numbers/options.h"
#include "codegen/numbers/processor.h"
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
auto options = codegen::numbers::parseOptions();
if (options.inputPath.isEmpty()) {
return -1;
}
codegen::numbers::Processor processor(options);
return processor.launch();
}

View File

@ -1,73 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/numbers/options.h"
#include <ostream>
#include <QtCore/QCoreApplication>
#include "codegen/common/logging.h"
namespace codegen {
namespace numbers {
namespace {
constexpr int kErrorOutputPathExpected = 902;
constexpr int kErrorInputPathExpected = 903;
constexpr int kErrorSingleInputPathExpected = 904;
constexpr int kErrorWorkingPathExpected = 905;
} // namespace
using common::logError;
Options parseOptions() {
Options result;
auto args = QCoreApplication::instance()->arguments();
for (auto i = 1, count = args.size(); i < count; ++i) { // skip first
auto &arg = args.at(i);
// Output path
if (arg == "-o") {
if (++i == count) {
logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o";
return Options();
} else {
result.outputPath = args.at(i);
}
} else if (arg.startsWith("-o")) {
result.outputPath = arg.mid(2);
// Working path
} else if (arg == "-w") {
if (++i == count) {
logError(kErrorWorkingPathExpected, "Command Line") << "working path expected after -w";
return Options();
} else {
common::logSetWorkingPath(args.at(i));
}
} else if (arg.startsWith("-w")) {
common::logSetWorkingPath(arg.mid(2));
// Input path
} else {
if (result.inputPath.isEmpty()) {
result.inputPath = arg;
} else {
logError(kErrorSingleInputPathExpected, "Command Line") << "only one input path expected";
return Options();
}
}
}
if (result.inputPath.isEmpty()) {
logError(kErrorInputPathExpected, "Command Line") << "input path expected";
return Options();
}
return result;
}
} // namespace numbers
} // namespace codegen

View File

@ -1,25 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
#include <QtCore/QStringList>
namespace codegen {
namespace numbers {
struct Options {
QString outputPath = ".";
QString inputPath;
};
// Parsing failed if inputPath is empty in the result.
Options parseOptions();
} // namespace numbers
} // namespace codegen

View File

@ -1,132 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/numbers/parsed_file.h"
#include <iostream>
#include <QtCore/QMap>
#include <QtCore/QDir>
#include <QtCore/QRegularExpression>
#include "codegen/common/basic_tokenized_file.h"
#include "codegen/common/logging.h"
#include "codegen/common/clean_file_reader.h"
#include "codegen/common/checked_utf8_string.h"
using BasicToken = codegen::common::BasicTokenizedFile::Token;
using BasicType = BasicToken::Type;
namespace codegen {
namespace numbers {
namespace {
QByteArray replaceStrings(const QString &filepath) {
common::CleanFileReader reader(filepath);
if (!reader.read()) {
return QByteArray();
}
common::CheckedUtf8String string(reader.currentPtr(), reader.charsLeft());
if (!string.isValid()) {
return QByteArray();
}
QStringList lines = string.toString().split('\n');
for (auto &line : lines) {
auto match = QRegularExpression("^(\\d+;[A-Z]+;)([^;]+)(;.*)?$").match(line);
if (match.hasMatch()) {
line = match.captured(1) + '"' + match.captured(2) + '"' + match.captured(3);
}
}
return lines.join('\n').toUtf8();
}
} // namespace
ParsedFile::ParsedFile(const Options &options)
: content_(replaceStrings(options.inputPath))
, file_(content_, options.inputPath)
, options_(options) {
}
bool ParsedFile::read() {
if (content_.isEmpty() || !file_.read()) {
return false;
}
auto filepath = QFileInfo(options_.inputPath).absoluteFilePath();
do {
if (auto code = file_.getToken(BasicType::Int)) {
if (!file_.getToken(BasicType::Semicolon)) {
logErrorUnexpectedToken() << "';'";
return false;
}
if (!file_.getToken(BasicType::Name)) {
logErrorUnexpectedToken() << "country code";
return false;
}
if (!file_.getToken(BasicType::Semicolon)) {
logErrorUnexpectedToken() << "';'";
return false;
}
if (!file_.getToken(BasicType::String)) {
logErrorUnexpectedToken() << "country name";
return false;
}
if (file_.getToken(BasicType::Semicolon)) {
if (auto firstPart = file_.getToken(BasicType::Int)) {
if (firstPart.original.toByteArray() != code.original.toByteArray()) {
file_.putBack();
result_.data.insert(code.original.toStringUnchecked(), Rule());
continue;
}
Rule rule;
while (auto part = file_.getToken(BasicType::Name)) {
rule.push_back(part.original.size());
}
result_.data.insert(code.original.toStringUnchecked(), rule);
if (rule.isEmpty()) {
logErrorUnexpectedToken() << "bad phone pattern";
return false;
}
if (!file_.getToken(BasicType::Semicolon)) {
logErrorUnexpectedToken() << "';'";
return false;
}
if (!file_.getToken(BasicType::Int)) {
logErrorUnexpectedToken() << "country phone len";
return false;
}
file_.getToken(BasicType::Semicolon);
continue;
} else {
logErrorUnexpectedToken() << "country phone pattern";
return false;
}
} else if (file_.getToken(BasicType::Int)) {
file_.putBack();
result_.data.insert(code.original.toStringUnchecked(), Rule());
continue;
} else {
logErrorUnexpectedToken() << "country phone pattern";
return false;
}
}
if (file_.atEnd()) {
break;
}
logErrorUnexpectedToken() << "numbers rule";
} while (!failed());
if (failed()) {
result_.data.clear();
}
return !failed();
}
} // namespace numbers
} // namespace codegen

View File

@ -1,61 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <string>
#include "codegen/common/basic_tokenized_file.h"
#include "codegen/numbers/options.h"
namespace codegen {
namespace numbers {
using Rule = QVector<int>;
struct Rules {
QMap<QString, Rule> data;
};
// Parses an input file to the internal struct.
class ParsedFile {
public:
explicit ParsedFile(const Options &options);
ParsedFile(const ParsedFile &other) = delete;
ParsedFile &operator=(const ParsedFile &other) = delete;
bool read();
Rules getResult() {
return result_;
}
private:
bool failed() const {
return failed_ || file_.failed();
}
// Log error to std::cerr with 'code' at the current position in file.
common::LogStream logError(int code) {
failed_ = true;
return file_.logError(code);
}
common::LogStream logErrorUnexpectedToken() {
failed_ = true;
return file_.logErrorUnexpectedToken();
}
QByteArray content_;
common::BasicTokenizedFile file_;
Options options_;
bool failed_ = false;
Rules result_;
};
} // namespace numbers
} // namespace codegen

View File

@ -1,72 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/numbers/processor.h"
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include "codegen/common/cpp_file.h"
#include "codegen/numbers/parsed_file.h"
#include "codegen/numbers/generator.h"
namespace codegen {
namespace numbers {
namespace {
constexpr int kErrorCantWritePath = 851;
} // namespace
Processor::Processor(const Options &options)
: parser_(std::make_unique<ParsedFile>(options))
, options_(options) {
}
int Processor::launch() {
if (!parser_->read()) {
return -1;
}
auto result = parser_->getResult();
if (!write(result)) {
return -1;
}
return 0;
}
bool Processor::write(const Rules &rules) const {
QDir dir(options_.outputPath);
if (!dir.mkpath(".")) {
common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString();
return false;
}
QFileInfo srcFile(options_.inputPath);
QString dstFilePath = dir.absolutePath() + "/numbers";
common::ProjectInfo project = {
"codegen_style",
srcFile.fileName(),
false, // forceReGenerate
};
Generator generator(rules, dstFilePath, project);
if (!generator.writeHeader()) {
return false;
}
if (!generator.writeSource()) {
return false;
}
return true;
}
Processor::~Processor() = default;
} // namespace numbers
} // namespace codegen

View File

@ -1,40 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <QtCore/QString>
#include "codegen/numbers/options.h"
namespace codegen {
namespace numbers {
class ParsedFile;
struct Rules;
// Walks through a file, parses it and generates number formatter.
class Processor {
public:
explicit Processor(const Options &options);
Processor(const Processor &other) = delete;
Processor &operator=(const Processor &other) = delete;
// Returns 0 on success.
int launch();
~Processor();
private:
bool write(const Rules &rules) const;
std::unique_ptr<ParsedFile> parser_;
const Options &options_;
};
} // namespace numbers
} // namespace codegen

File diff suppressed because it is too large Load Diff

View File

@ -1,71 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <map>
#include <functional>
#include <QtCore/QString>
#include <QtCore/QSet>
#include "codegen/common/cpp_file.h"
#include "codegen/style/structure_types.h"
namespace codegen {
namespace style {
namespace structure {
class Module;
} // namespace structure
class Generator {
public:
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();
private:
QString typeToString(structure::Type type) const;
QString typeToDefaultValue(structure::Type type) const;
QString valueAssignmentCode(structure::Value value) const;
bool writeHeaderRequiredIncludes();
bool writeHeaderStyleNamespace();
bool writeStructsForwardDeclarations();
bool writeStructsDefinitions();
bool writePaletteDefinition();
bool writeRefsDeclarations();
bool writeIncludesInSource();
bool writeVariableDefinitions();
bool writeRefsDefinition();
bool writeSetPaletteColor();
bool writeVariableInit();
bool writePxValuesInit();
bool writeFontFamiliesInit();
bool writeIconValues();
bool writeIconsInit();
bool collectUniqueValues();
const structure::Module &module_;
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
std::map<QString, int, std::greater<QString>> paletteIndices_;
};
} // namespace style
} // namespace codegen

View File

@ -1,23 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include <QtCore/QCoreApplication>
#include "codegen/style/options.h"
#include "codegen/style/processor.h"
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
auto options = codegen::style::parseOptions();
if (options.inputPath.isEmpty()) {
return -1;
}
codegen::style::Processor processor(options);
return processor.launch();
}

View File

@ -1,90 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/style/module.h"
namespace codegen {
namespace style {
namespace structure {
namespace {
QString fullNameKey(const FullName &name) {
return name.join('.');
}
} // namespace
Module::Module(const QString &fullpath) : fullpath_(fullpath) {
}
void Module::addIncluded(std::unique_ptr<Module> &&value) {
included_.push_back(std::move(value));
}
bool Module::addStruct(const Struct &value) {
if (findStruct(value.name)) {
return false;
}
structsByName_.insert(fullNameKey(value.name), structs_.size());
structs_.push_back(value);
return true;
}
const Struct *Module::findStruct(const FullName &name) const {
if (auto result = findStructInModule(name, *this)) {
return result;
}
for (const auto &module : included_) {
if (auto result = module->findStruct(name)) {
return result;
}
}
return nullptr;
}
bool Module::addVariable(const Variable &value) {
if (findVariable(value.name)) {
return false;
}
variablesByName_.insert(fullNameKey(value.name), variables_.size());
variables_.push_back(value);
return true;
}
const Variable *Module::findVariable(const FullName &name, bool *outFromThisModule) const {
if (auto result = findVariableInModule(name, *this)) {
if (outFromThisModule) *outFromThisModule = true;
return result;
}
for (const auto &module : included_) {
if (auto result = module->findVariable(name)) {
if (outFromThisModule) *outFromThisModule = false;
return result;
}
}
return nullptr;
}
const Struct *Module::findStructInModule(const FullName &name, const Module &module) {
auto index = module.structsByName_.value(fullNameKey(name), -1);
if (index < 0) {
return nullptr;
}
return &module.structs_.at(index);
}
const Variable *Module::findVariableInModule(const FullName &name, const Module &module) {
auto index = module.variablesByName_.value(fullNameKey(name), -1);
if (index < 0) {
return nullptr;
}
return &module.variables_.at(index);
}
} // namespace structure
} // namespace style
} // namespace codegen

View File

@ -1,99 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
#include <QtCore/QList>
#include <QtCore/QMap>
#include <vector>
#include "codegen/style/structure_types.h"
namespace codegen {
namespace style {
namespace structure {
class Module {
public:
explicit Module(const QString &fullpath);
QString filepath() const {
return fullpath_;
}
void addIncluded(std::unique_ptr<Module> &&value);
bool hasIncludes() const {
return !included_.empty();
}
template <typename F>
bool enumIncludes(F functor) const {
for (const auto &module : included_) {
if (!functor(*module)) {
return false;
}
}
return true;
}
// Returns false if there is a struct with such name already.
bool addStruct(const Struct &value);
// Returns nullptr if there is no such struct in result_ or any of included modules.
const Struct *findStruct(const FullName &name) const;
bool hasStructs() const {
return !structs_.isEmpty();
}
template <typename F>
bool enumStructs(F functor) const {
for (const auto &value : structs_) {
if (!functor(value)) {
return false;
}
}
return true;
}
// Returns false if there is a variable with such name already.
bool addVariable(const Variable &value);
// Returns nullptr if there is no such variable in result_ or any of included modules.
const Variable *findVariable(const FullName &name, bool *outFromThisModule = nullptr) const;
bool hasVariables() const {
return !variables_.isEmpty();
}
template <typename F>
bool enumVariables(F functor) const {
for (const auto &value : variables_) {
if (!functor(value)) {
return false;
}
}
return true;
}
explicit operator bool() const {
return !fullpath_.isEmpty();
}
static const Struct *findStructInModule(const FullName &name, const Module &module);
static const Variable *findVariableInModule(const FullName &name, const Module &module);
private:
QString fullpath_;
std::vector<std::unique_ptr<Module>> included_;
QList<Struct> structs_;
QList<Variable> variables_;
QMap<QString, int> structsByName_;
QMap<QString, int> variablesByName_;
};
} // namespace structure
} // namespace style
} // namespace codegen

View File

@ -1,87 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/style/options.h"
#include <ostream>
#include <QtCore/QCoreApplication>
#include <QtCore/QDir>
#include "codegen/common/logging.h"
namespace codegen {
namespace style {
namespace {
constexpr int kErrorIncludePathExpected = 901;
constexpr int kErrorOutputPathExpected = 902;
constexpr int kErrorInputPathExpected = 903;
constexpr int kErrorSingleInputPathExpected = 904;
constexpr int kErrorWorkingPathExpected = 905;
} // namespace
using common::logError;
Options parseOptions() {
Options result;
auto args = QCoreApplication::instance()->arguments();
for (int i = 1, count = args.size(); i < count; ++i) { // skip first
auto &arg = args.at(i);
// Include paths
if (arg == "-I") {
if (++i == count) {
logError(kErrorIncludePathExpected, "Command Line") << "include path expected after -I";
return Options();
} else {
result.includePaths.push_back(args.at(i));
}
} else if (arg.startsWith("-I")) {
result.includePaths.push_back(arg.mid(2));
// Output path
} else if (arg == "-o") {
if (++i == count) {
logError(kErrorOutputPathExpected, "Command Line") << "output path expected after -o";
return Options();
} else {
result.outputPath = args.at(i);
}
} else if (arg.startsWith("-o")) {
result.outputPath = arg.mid(2);
// Working path
} else if (arg == "-w") {
if (++i == count) {
logError(kErrorWorkingPathExpected, "Command Line") << "working path expected after -w";
return Options();
} else {
common::logSetWorkingPath(args.at(i));
}
} else if (arg.startsWith("-w")) {
common::logSetWorkingPath(arg.mid(2));
// Input path
} else {
if (result.inputPath.isEmpty()) {
result.inputPath = arg;
} else {
logError(kErrorSingleInputPathExpected, "Command Line") << "only one input path expected";
return Options();
}
}
}
if (result.inputPath.isEmpty()) {
logError(kErrorInputPathExpected, "Command Line") << "input path expected";
return Options();
}
result.isPalette = (QFileInfo(result.inputPath).suffix() == "palette");
return result;
}
} // namespace style
} // namespace codegen

View File

@ -1,27 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <QtCore/QString>
#include <QtCore/QStringList>
namespace codegen {
namespace style {
struct Options {
QStringList includePaths = { "." };
QString outputPath = ".";
QString inputPath;
bool isPalette = false;
};
// Parsing failed if inputPath is empty in the result.
Options parseOptions();
} // namespace style
} // namespace codegen

View File

@ -1,840 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/style/parsed_file.h"
#include <iostream>
#include <QtCore/QMap>
#include <QtCore/QDir>
#include <QtCore/QRegularExpression>
#include "codegen/common/basic_tokenized_file.h"
#include "codegen/common/logging.h"
using BasicToken = codegen::common::BasicTokenizedFile::Token;
using BasicType = BasicToken::Type;
namespace codegen {
namespace style {
using structure::logFullName;
namespace {
constexpr int kErrorInIncluded = 801;
constexpr int kErrorTypeMismatch = 802;
constexpr int kErrorUnknownField = 803;
constexpr int kErrorIdentifierNotFound = 804;
constexpr int kErrorAlreadyDefined = 805;
constexpr int kErrorBadString = 806;
constexpr int kErrorIconDuplicate = 807;
constexpr int kErrorBadIconModifier = 808;
constexpr int kErrorCyclicDependency = 809;
QString findInputFile(const Options &options) {
for (const auto &dir : options.includePaths) {
QString tryPath = QDir(dir).absolutePath() + '/' + options.inputPath;
if (QFileInfo(tryPath).exists()) {
return tryPath;
}
}
return options.inputPath;
}
QString tokenValue(const BasicToken &token) {
if (token.type == BasicType::String) {
return token.value;
}
return token.original.toStringUnchecked();
}
bool isValidColor(const QString &str) {
auto len = str.size();
if (len != 6 && len != 8) {
return false;
}
for (auto ch : str) {
auto code = ch.unicode();
if ((code < '0' || code > '9') && (code < 'a' || code > 'f')) {
return false;
}
}
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);
}
uchar readHexUchar(QChar char1, QChar char2) {
return ((readHexUchar(char1) & 0x0F) << 4) | (readHexUchar(char2) & 0x0F);
}
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));
g = readHexUchar(str.at(2), str.at(3));
b = readHexUchar(str.at(4), str.at(5));
if (str.size() == 8) {
a = readHexUchar(str.at(6), str.at(7));
}
}
return { r, g, b, a, fallback };
}
structure::data::color convertIntColor(int r, int g, int b, int a) {
return { uchar(r & 0xFF), uchar(g & 0xFF), uchar(b & 0xFF), uchar(a & 0xFF) };
}
std::string logType(const structure::Type &type) {
if (type.tag == structure::TypeTag::Struct) {
return "struct " + logFullName(type.name);
}
static auto builtInTypes = new QMap<structure::TypeTag, std::string> {
{ structure::TypeTag::Int , "int" },
{ structure::TypeTag::Double , "double" },
{ structure::TypeTag::Pixels , "pixels" },
{ structure::TypeTag::String , "string" },
{ structure::TypeTag::Color , "color" },
{ structure::TypeTag::Point , "point" },
{ structure::TypeTag::Size , "size" },
{ structure::TypeTag::Align , "align" },
{ structure::TypeTag::Margins , "margins" },
{ structure::TypeTag::Font , "font" },
};
return builtInTypes->value(type.tag, "invalid");
}
bool validateAnsiString(const QString &value) {
for (auto ch : value) {
if (ch.unicode() > 127) {
return false;
}
}
return true;
}
bool validateAlignString(const QString &value) {
return QRegularExpression("^[a-z_]+$").match(value).hasMatch();
}
} // namespace
Modifier GetModifier(const QString &name) {
static QMap<QString, Modifier> modifiers;
if (modifiers.empty()) {
modifiers.insert("invert", [](QImage &image) {
image.invertPixels();
});
modifiers.insert("flip_horizontal", [](QImage &image) {
image = image.mirrored(true, false);
});
modifiers.insert("flip_vertical", [](QImage &image) {
image = image.mirrored(false, true);
});
}
return modifiers.value(name);
}
ParsedFile::ParsedFile(
const Options &options,
std::vector<QString> includeStack)
: filePath_(findInputFile(options))
, file_(filePath_)
, options_(options)
, includeStack_(includeStack) {
}
bool ParsedFile::read() {
if (std::find(begin(includeStack_), end(includeStack_), filePath_)
!= end(includeStack_)) {
logError(kErrorCyclicDependency) << "include cycle detected.";
return false;
} else if (!file_.read()) {
return false;
}
auto absolutePath = QFileInfo(filePath_).absoluteFilePath();
module_ = std::make_unique<structure::Module>(absolutePath);
do {
if (auto startToken = file_.getToken(BasicType::Name)) {
if (tokenValue(startToken) == "using") {
if (auto includedResult = readIncluded()) {
module_->addIncluded(std::move(includedResult));
continue;
}
} else if (auto braceOpen = file_.getToken(BasicType::LeftBrace)) {
if (auto structResult = readStruct(tokenValue(startToken))) {
if (module_->addStruct(structResult)) {
continue;
}
logError(kErrorAlreadyDefined) << "struct '" << logFullName(structResult.name) << "' already defined";
break;
}
} else if (auto colonToken = file_.getToken(BasicType::Colon)) {
if (auto variableResult = readVariable(tokenValue(startToken))) {
if (module_->addVariable(variableResult)) {
continue;
}
logError(kErrorAlreadyDefined) << "variable '" << logFullName(variableResult.name) << "' already defined";
break;
}
}
}
if (file_.atEnd()) {
break;
}
logErrorUnexpectedToken() << "using keyword, or struct definition, or variable definition";
} while (!failed());
if (failed()) {
module_ = nullptr;
}
return !failed();
}
common::LogStream ParsedFile::logErrorTypeMismatch() {
return logError(kErrorTypeMismatch) << "type mismatch: ";
}
ParsedFile::ModulePtr ParsedFile::readIncluded() {
if (auto usingFile = assertNextToken(BasicType::String)) {
if (assertNextToken(BasicType::Semicolon)) {
auto includeStack = includeStack_;
includeStack.push_back(filePath_);
ParsedFile included(
includedOptions(tokenValue(usingFile)),
includeStack);
if (included.read()) {
return included.getResult();
} else {
logError(kErrorInIncluded) << "error while parsing '" << tokenValue(usingFile).toStdString() << "'";
}
}
}
return nullptr;
}
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)) {
if (auto field = readStructField(tokenValue(fieldName))) {
result.fields.push_back(field);
}
} else if (assertNextToken(BasicType::RightBrace)) {
if (result.fields.isEmpty()) {
logErrorUnexpectedToken() << "at least one field in struct";
}
break;
}
} while (!failed());
return result;
}
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);
result.description = file_.getCurrentLineComment();
}
}
return result;
}
structure::StructField ParsedFile::readStructField(const QString &name) {
structure::StructField result = { composeFullName(name) };
if (auto colonToken = assertNextToken(BasicType::Colon)) {
if (auto type = readType()) {
result.type = type;
assertNextToken(BasicType::Semicolon);
}
}
return result;
}
structure::Type ParsedFile::readType() {
structure::Type result;
if (auto nameToken = assertNextToken(BasicType::Name)) {
auto name = tokenValue(nameToken);
if (auto builtInType = typeNames_.value(name.toStdString())) {
result = builtInType;
} else {
auto fullName = composeFullName(name);
if (module_->findStruct(fullName)) {
result.tag = structure::TypeTag::Struct;
result.name = fullName;
} else {
logError(kErrorIdentifierNotFound) << "type name '" << logFullName(fullName) << "' not found";
}
}
}
return result;
}
structure::Value ParsedFile::readValue() {
if (auto colorValue = readColorValue()) {
return colorValue;
} else if (auto pointValue = readPointValue()) {
return pointValue;
} else if (auto sizeValue = readSizeValue()) {
return sizeValue;
} else if (auto alignValue = readAlignValue()) {
return alignValue;
} else if (auto marginsValue = readMarginsValue()) {
return marginsValue;
} else if (auto fontValue = readFontValue()) {
return fontValue;
} else if (auto iconValue = readIconValue()) {
return iconValue;
} else if (auto numericValue = readNumericValue()) {
return numericValue;
} else if (auto stringValue = readStringValue()) {
return stringValue;
} else if (auto structValue = readStructValue()) {
return structValue;
} else if (auto copyValue = readCopyValue()) {
return copyValue;
} else {
logErrorUnexpectedToken() << "variable value";
}
return {};
}
structure::Value ParsedFile::readStructValue() {
if (auto structName = file_.getToken(BasicType::Name)) {
if (auto result = defaultConstructedStruct(composeFullName(tokenValue(structName)))) {
if (file_.getToken(BasicType::LeftParenthesis)) {
if (!readStructParents(result)) {
return {};
}
}
if (assertNextToken(BasicType::LeftBrace)) {
readStructValueInner(result);
}
return result;
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::defaultConstructedStruct(const structure::FullName &structName) {
if (auto pattern = module_->findStruct(structName)) {
QList<structure::data::field> fields;
fields.reserve(pattern->fields.size());
for (const auto &fieldType : pattern->fields) {
fields.push_back({
{ // variable
fieldType.name,
{ fieldType.type, Qt::Uninitialized }, // value
},
structure::data::field::Status::Uninitialized, // status
});
}
return { structName, fields };
}
return {};
}
void ParsedFile::applyStructParent(structure::Value &result, const structure::FullName &parentName) {
bool fromTheSameModule = false;
if (auto parent = module_->findVariable(parentName, &fromTheSameModule)) {
if (parent->value.type() != result.type()) {
logErrorTypeMismatch() << "parent '" << logFullName(parentName) << "' has type '" << logType(parent->value.type()) << "' while child value has type " << logType(result.type());
return;
}
const auto *srcFields(parent->value.Fields());
auto *dstFields(result.Fields());
if (!srcFields || !dstFields) {
logAssert(false) << "struct data check failed";
return;
}
logAssert(srcFields->size() == dstFields->size()) << "struct size check failed";
for (int i = 0, s = srcFields->size(); i != s; ++i) {
const auto &srcField(srcFields->at(i));
auto &dstField((*dstFields)[i]);
using Status = structure::data::field::Status;
if (srcField.status == Status::Explicit ||
dstField.status == Status::Uninitialized) {
const auto &srcValue(srcField.variable.value);
auto &dstValue(dstField.variable.value);
logAssert(srcValue.type() == dstValue.type()) << "struct field type check failed";
// Optimization: don't let the style files to contain unnamed inherited
// icons from the other (included) style files, because they will
// duplicate the binary data across different style c++ source files.
//
// Example:
// a.style has "A: Struct { icon: icon { ..file.. } };" and
// b.style has "B: Struct(A) { .. };" with non-overriden icon field.
// Then both style_a.cpp and style_b.cpp will contain binary data of "file".
if (!fromTheSameModule
&& srcValue.type().tag == structure::TypeTag::Icon
&& !srcValue.Icon().parts.empty()
&& srcValue.copyOf().isEmpty()) {
logError(kErrorIconDuplicate) << "an unnamed icon field '" << logFullName(srcField.variable.name) << "' is inherited from parent '" << logFullName(parentName) << "'";
return;
}
dstValue = srcValue;
dstField.status = Status::Implicit;
}
}
} else {
logError(kErrorIdentifierNotFound) << "parent '" << logFullName(parentName) << "' not found";
}
}
bool ParsedFile::readStructValueInner(structure::Value &result) {
do {
if (auto fieldName = file_.getToken(BasicType::Name)) {
if (!assertNextToken(BasicType::Colon)) {
return false;
}
if (auto field = readVariable(tokenValue(fieldName))) {
if (!assignStructField(result, field)) {
return false;
}
}
} else if (assertNextToken(BasicType::RightBrace)) {
return true;
}
} while (!failed());
return false;
}
bool ParsedFile::assignStructField(structure::Value &result, const structure::Variable &field) {
auto *fields = result.Fields();
if (!fields) {
logAssert(false) << "struct data check failed";
return false;
}
for (auto &already : *fields) {
if (already.variable.name == field.name) {
if (already.variable.value.type() == field.value.type()) {
already.variable.value = field.value;
already.status = structure::data::field::Status::Explicit;
return true;
} else {
logErrorTypeMismatch() << "field '" << logFullName(already.variable.name) << "' has type '" << logType(already.variable.value.type()) << "' while value has type '" << logType(field.value.type()) << "'";
return false;
}
}
}
logError(kErrorUnknownField) << "field '" << logFullName(field.name) << "' was not found in struct of type '" << logType(result.type()) << "'";
return false;
}
bool ParsedFile::readStructParents(structure::Value &result) {
do {
if (auto parentName = assertNextToken(BasicType::Name)) {
applyStructParent(result, composeFullName(tokenValue(parentName)));
if (file_.getToken(BasicType::RightParenthesis)) {
return true;
} else {
assertNextToken(BasicType::Comma);
}
} else {
logErrorUnexpectedToken() << "struct variable parent";
}
} while (!failed());
return false;
}
structure::Value ParsedFile::readPositiveValue() {
auto numericToken = file_.getAnyToken();
if (numericToken.type == BasicType::Int) {
return { structure::TypeTag::Int, tokenValue(numericToken).toInt() };
} else if (numericToken.type == BasicType::Double) {
return { structure::TypeTag::Double, tokenValue(numericToken).toDouble() };
} else if (numericToken.type == BasicType::Name) {
auto value = tokenValue(numericToken);
auto match = QRegularExpression("^\\d+px$").match(value);
if (match.hasMatch()) {
return { structure::TypeTag::Pixels, value.mid(0, value.size() - 2).toInt() };
}
}
file_.putBack();
return {};
}
structure::Value ParsedFile::readNumericValue() {
if (auto value = readPositiveValue()) {
return value;
} else if (auto minusToken = file_.getToken(BasicType::Minus)) {
if (auto positiveValue = readNumericValue()) {
return { positiveValue.type().tag, -positiveValue.Int() };
}
logErrorUnexpectedToken() << "numeric value";
}
return {};
}
structure::Value ParsedFile::readStringValue() {
if (auto stringToken = file_.getToken(BasicType::String)) {
auto value = tokenValue(stringToken);
if (validateAnsiString(value)) {
return { structure::TypeTag::String, stringToken.value.toStdString() };
}
logError(kErrorBadString) << "unicode symbols are not supported";
}
return {};
}
structure::Value ParsedFile::readColorValue() {
if (auto numberSign = file_.getToken(BasicType::Number)) {
if (options_.isPalette) {
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 alias, unique color values are only allowed in palette module";
}
} else if (auto transparentName = file_.getToken(BasicType::Name)) {
if (tokenValue(transparentName) == "transparent") {
return { structure::data::color { 255, 255, 255, 0 } };
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::readPointValue() {
if (auto font = file_.getToken(BasicType::Name)) {
if (tokenValue(font) == "point") {
assertNextToken(BasicType::LeftParenthesis);
auto x = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto y = readNumericOrNumericCopyValue();
if (x.type().tag != structure::TypeTag::Pixels ||
y.type().tag != structure::TypeTag::Pixels) {
logErrorTypeMismatch() << "expected two px values for the point";
}
assertNextToken(BasicType::RightParenthesis);
return { structure::data::point { x.Int(), y.Int() } };
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::readSizeValue() {
if (auto font = file_.getToken(BasicType::Name)) {
if (tokenValue(font) == "size") {
assertNextToken(BasicType::LeftParenthesis);
auto w = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto h = readNumericOrNumericCopyValue();
if (w.type().tag != structure::TypeTag::Pixels ||
h.type().tag != structure::TypeTag::Pixels) {
logErrorTypeMismatch() << "expected two px values for the size";
}
assertNextToken(BasicType::RightParenthesis);
return { structure::data::size { w.Int(), h.Int() } };
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::readAlignValue() {
if (auto font = file_.getToken(BasicType::Name)) {
if (tokenValue(font) == "align") {
assertNextToken(BasicType::LeftParenthesis);
auto align = tokenValue(assertNextToken(BasicType::Name));
assertNextToken(BasicType::RightParenthesis);
if (validateAlignString(align)) {
return { structure::TypeTag::Align, align.toStdString() };
} else {
logError(kErrorBadString) << "bad align string";
}
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::readMarginsValue() {
if (auto font = file_.getToken(BasicType::Name)) {
if (tokenValue(font) == "margins") {
assertNextToken(BasicType::LeftParenthesis);
auto l = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto t = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto r = readNumericOrNumericCopyValue(); assertNextToken(BasicType::Comma);
auto b = readNumericOrNumericCopyValue();
if (l.type().tag != structure::TypeTag::Pixels ||
t.type().tag != structure::TypeTag::Pixels ||
r.type().tag != structure::TypeTag::Pixels ||
b.type().tag != structure::TypeTag::Pixels) {
logErrorTypeMismatch() << "expected four px values for the margins";
}
assertNextToken(BasicType::RightParenthesis);
return { structure::data::margins { l.Int(), t.Int(), r.Int(), b.Int() } };
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::readFontValue() {
if (auto font = file_.getToken(BasicType::Name)) {
if (tokenValue(font) == "font") {
assertNextToken(BasicType::LeftParenthesis);
int flags = 0;
structure::Value family, size;
do {
if (auto formatToken = file_.getToken(BasicType::Name)) {
if (tokenValue(formatToken) == "bold") {
flags |= structure::data::font::Bold;
} else if (tokenValue(formatToken) == "italic") {
flags |= structure::data::font::Italic;
} else if (tokenValue(formatToken) == "underline") {
flags |= structure::data::font::Underline;
} else {
file_.putBack();
}
}
if (auto familyValue = readStringOrStringCopyValue()) {
family = familyValue;
} else if (auto sizeValue = readNumericOrNumericCopyValue()) {
size = sizeValue;
} else if (file_.getToken(BasicType::RightParenthesis)) {
break;
} else {
logErrorUnexpectedToken() << "font family, font size or ')'";
}
} while (!failed());
if (size.type().tag != structure::TypeTag::Pixels) {
logErrorTypeMismatch() << "px value for the font size expected";
}
return { structure::data::font { family.String(), size.Int(), flags } };
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::readIconValue() {
if (auto font = file_.getToken(BasicType::Name)) {
if (tokenValue(font) == "icon") {
std::vector<structure::data::monoicon> parts;
if (file_.getToken(BasicType::LeftBrace)) { // complex icon
do {
if (file_.getToken(BasicType::RightBrace)) {
break;
} else if (file_.getToken(BasicType::LeftBrace)) {
if (auto part = readMonoIconFields()) {
assertNextToken(BasicType::RightBrace);
parts.push_back(part);
file_.getToken(BasicType::Comma);
continue;
}
return {};
} else {
logErrorUnexpectedToken() << "icon part or '}'";
return {};
}
} while (true);
} else if (file_.getToken(BasicType::LeftParenthesis)) { // short icon
if (auto theOnlyPart = readMonoIconFields()) {
assertNextToken(BasicType::RightParenthesis);
parts.push_back(theOnlyPart);
}
}
return { structure::data::icon { parts } };
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::readCopyValue() {
if (auto copyName = file_.getToken(BasicType::Name)) {
structure::FullName name = { tokenValue(copyName) };
if (auto variable = module_->findVariable(name)) {
return variable->value.makeCopy(variable->name);
}
file_.putBack();
}
return {};
}
structure::Value ParsedFile::readNumericOrNumericCopyValue() {
if (auto result = readNumericValue()) {
return result;
} else if (auto copy = readCopyValue()) {
auto type = copy.type().tag;
if (type == structure::TypeTag::Int
|| type == structure::TypeTag::Double
|| type == structure::TypeTag::Pixels) {
return copy;
} else {
file_.putBack();
}
}
return {};
}
structure::Value ParsedFile::readStringOrStringCopyValue() {
if (auto result = readStringValue()) {
return result;
} else if (auto copy = readCopyValue()) {
auto type = copy.type().tag;
if (type == structure::TypeTag::String) {
return copy;
} else {
file_.putBack();
}
}
return {};
}
structure::data::monoicon ParsedFile::readMonoIconFields() {
structure::data::monoicon result;
result.filename = readMonoIconFilename();
if (!result.filename.isEmpty() && file_.getToken(BasicType::Comma)) {
if (auto color = readValue()) {
if (color.type().tag == structure::TypeTag::Color) {
result.color = color;
if (file_.getToken(BasicType::Comma)) {
if (auto offset = readValue()) {
if (offset.type().tag == structure::TypeTag::Point) {
result.offset = offset;
} else {
logErrorUnexpectedToken() << "icon offset";
}
} else {
logErrorUnexpectedToken() << "icon offset";
}
} else {
result.offset = { structure::data::point { 0, 0 } };
}
} else {
logErrorUnexpectedToken() << "icon color";
}
} else {
logErrorUnexpectedToken() << "icon color";
}
}
return result;
}
QString ParsedFile::readMonoIconFilename() {
if (auto filename = readValue()) {
if (filename.type().tag == structure::TypeTag::String) {
auto fullpath = QString::fromStdString(filename.String());
auto pathAndModifiers = fullpath.split('-');
auto filepath = pathAndModifiers[0];
auto modifiers = pathAndModifiers.mid(1);
for (auto modifierName : modifiers) {
if (!GetModifier(modifierName)) {
logError(kErrorBadIconModifier) << "unknown modifier: " << modifierName.toStdString();
return QString();
}
}
for (auto &path : options_.includePaths) {
QFileInfo fileinfo(path + '/' + filepath + ".png");
if (fileinfo.exists()) {
return path + '/' + fullpath;
}
}
for (auto &path : options_.includePaths) {
QFileInfo fileinfo(path + "/icons/" + filepath + ".png");
if (fileinfo.exists()) {
return path + "/icons/" + fullpath;
}
}
logError(common::kErrorFileNotFound) << "could not open icon file '" << filename.String() << "'";
} else if (filename.type().tag == structure::TypeTag::Size) {
return QString("size://%1,%2").arg(filename.Size().width).arg(filename.Size().height);
}
}
logErrorUnexpectedToken() << "icon filename or rect size";
return QString();
}
BasicToken ParsedFile::assertNextToken(BasicToken::Type type) {
auto result = file_.getToken(type);
if (!result) {
logErrorUnexpectedToken() << type;
}
return result;
}
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;
}
// Compose context-dependent full name.
structure::FullName ParsedFile::composeFullName(const QString &name) {
return { name };
}
} // namespace style
} // namespace codegen

View File

@ -1,132 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <string>
#include <functional>
#include <QImage>
#include "codegen/common/basic_tokenized_file.h"
#include "codegen/style/options.h"
#include "codegen/style/module.h"
namespace codegen {
namespace style {
using Modifier = std::function<void(QImage &image)>;
Modifier GetModifier(const QString &name);
// Parses an input file to the internal struct.
class ParsedFile {
public:
explicit ParsedFile(
const Options &options,
std::vector<QString> includeStack = {});
ParsedFile(const ParsedFile &other) = delete;
ParsedFile &operator=(const ParsedFile &other) = delete;
bool read();
using ModulePtr = std::unique_ptr<structure::Module>;
ModulePtr getResult() {
return std::move(module_);
}
private:
bool failed() const {
return failed_ || file_.failed();
}
// Log error to std::cerr with 'code' at the current position in file.
common::LogStream logError(int code) {
failed_ = true;
return file_.logError(code);
}
common::LogStream logErrorUnexpectedToken() {
failed_ = true;
return file_.logErrorUnexpectedToken();
}
common::LogStream logErrorTypeMismatch();
common::LogStream logAssert(bool assertion) {
if (!assertion) {
return logError(common::kErrorInternal) << "internal - ";
}
return common::LogStream(common::LogStream::Null);
}
// Helper methods for context-dependent reading.
ModulePtr readIncluded();
structure::Struct readStruct(const QString &name);
structure::Variable readVariable(const QString &name);
structure::StructField readStructField(const QString &name);
structure::Type readType();
structure::Value readValue();
structure::Value readStructValue();
structure::Value defaultConstructedStruct(const structure::FullName &name);
void applyStructParent(structure::Value &result, const structure::FullName &parentName);
bool readStructValueInner(structure::Value &result);
bool assignStructField(structure::Value &result, const structure::Variable &field);
bool readStructParents(structure::Value &result);
// Simple methods for reading value types.
structure::Value readPositiveValue();
structure::Value readNumericValue();
structure::Value readStringValue();
structure::Value readColorValue();
structure::Value readPointValue();
structure::Value readSizeValue();
structure::Value readAlignValue();
structure::Value readMarginsValue();
structure::Value readFontValue();
structure::Value readIconValue();
structure::Value readCopyValue();
structure::Value readNumericOrNumericCopyValue();
structure::Value readStringOrStringCopyValue();
structure::data::monoicon readMonoIconFields();
QString readMonoIconFilename();
// Read next token and fire unexpected token error if it is not of "type".
using BasicToken = common::BasicTokenizedFile::Token;
BasicToken assertNextToken(BasicToken::Type type);
// Look through include directories in options_ and find absolute include path.
Options includedOptions(const QString &filepath);
// Compose context-dependent full name.
structure::FullName composeFullName(const QString &name);
QString filePath_;
common::BasicTokenizedFile file_;
Options options_;
bool failed_ = false;
ModulePtr module_;
std::vector<QString> includeStack_;
QMap<std::string, structure::Type> typeNames_ = {
{ "int" , { structure::TypeTag::Int } },
{ "double" , { structure::TypeTag::Double } },
{ "pixels" , { structure::TypeTag::Pixels } },
{ "string" , { structure::TypeTag::String } },
{ "color" , { structure::TypeTag::Color } },
{ "point" , { structure::TypeTag::Point } },
{ "size" , { structure::TypeTag::Size } },
{ "align" , { structure::TypeTag::Align } },
{ "margins" , { structure::TypeTag::Margins } },
{ "font" , { structure::TypeTag::Font } },
{ "icon" , { structure::TypeTag::Icon } },
};
};
} // namespace style
} // namespace codegen

View File

@ -1,76 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/style/processor.h"
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include "codegen/common/cpp_file.h"
#include "codegen/style/parsed_file.h"
#include "codegen/style/generator.h"
namespace codegen {
namespace style {
namespace {
constexpr int kErrorCantWritePath = 821;
QString destFileBaseName(const structure::Module &module) {
return "style_" + QFileInfo(module.filepath()).baseName();
}
} // namespace
Processor::Processor(const Options &options)
: parser_(std::make_unique<ParsedFile>(options))
, options_(options) {
}
int Processor::launch() {
if (!parser_->read()) {
return -1;
}
auto module = parser_->getResult();
if (!write(*module)) {
return -1;
}
return 0;
}
bool Processor::write(const structure::Module &module) const {
bool forceReGenerate = false;
QDir dir(options_.outputPath);
if (!dir.mkpath(".")) {
common::logError(kErrorCantWritePath, "Command Line") << "can not open path for writing: " << dir.absolutePath().toStdString();
return false;
}
QFileInfo srcFile(module.filepath());
QString dstFilePath = dir.absolutePath() + '/' + (options_.isPalette ? "palette" : destFileBaseName(module));
common::ProjectInfo project = {
"codegen_style",
srcFile.fileName(),
forceReGenerate
};
Generator generator(module, dstFilePath, project, options_.isPalette);
if (!generator.writeHeader()) {
return false;
}
if (!generator.writeSource()) {
return false;
}
return true;
}
Processor::~Processor() = default;
} // namespace style
} // namespace codegen

View File

@ -1,43 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <QtCore/QString>
#include "codegen/style/options.h"
namespace codegen {
namespace style {
namespace structure {
class Module;
} // namespace structure
class ParsedFile;
// Walks through a file, parses it and parses dependency files if necessary.
// Uses Generator class to produce the final output.
class Processor {
public:
explicit Processor(const Options &options);
Processor(const Processor &other) = delete;
Processor &operator=(const Processor &other) = delete;
// Returns 0 on success.
int launch();
~Processor();
private:
bool write(const structure::Module &module) const;
std::unique_ptr<ParsedFile> parser_;
const Options &options_;
};
} // namespace style
} // namespace codegen

View File

@ -1,198 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "codegen/style/structure_types.h"
namespace codegen {
namespace style {
namespace structure {
struct Value::DataTypes {
class TInt : public DataBase {
public:
TInt(int value) : value_(value) {
}
int Int() const override { return value_; }
private:
int value_;
};
class TDouble : public DataBase {
public:
TDouble(double value) : value_(value) {
}
double Double() const override { return value_; }
private:
double value_;
};
class TString : public DataBase {
public:
TString(std::string value) : value_(value) {
}
std::string String() const override { return value_; }
private:
std::string value_;
};
class TPoint : public DataBase {
public:
TPoint(data::point value) : value_(value) {
}
data::point Point() const override { return value_; }
private:
data::point value_;
};
class TSize : public DataBase {
public:
TSize(data::size value) : value_(value) {
}
data::size Size() const override { return value_; }
private:
data::size value_;
};
class TColor : public DataBase {
public:
TColor(data::color value) : value_(value) {
}
data::color Color() const override { return value_; }
private:
data::color value_;
};
class TMargins : public DataBase {
public:
TMargins(data::margins value) : value_(value) {
}
data::margins Margins() const override { return value_; }
private:
data::margins value_;
};
class TFont : public DataBase {
public:
TFont(data::font value) : value_(value) {
}
data::font Font() const override { return value_; }
private:
data::font value_;
};
class TIcon : public DataBase {
public:
TIcon(data::icon value) : value_(value) {
}
data::icon Icon() const override { return value_; }
private:
data::icon value_;
};
class TFields : public DataBase {
public:
TFields(data::fields value) : value_(value) {
}
const data::fields *Fields() const override { return &value_; }
data::fields *Fields() override { return &value_; }
private:
data::fields value_;
};
};
Value::Value() : Value(TypeTag::Invalid, std::make_shared<DataBase>()) {
}
Value::Value(data::point value) : Value(TypeTag::Point, std::make_shared<DataTypes::TPoint>(value)) {
}
Value::Value(data::size value) : Value(TypeTag::Size, std::make_shared<DataTypes::TSize>(value)) {
}
Value::Value(data::color value) : Value(TypeTag::Color, std::make_shared<DataTypes::TColor>(value)) {
}
Value::Value(data::margins value) : Value(TypeTag::Margins, std::make_shared<DataTypes::TMargins>(value)) {
}
Value::Value(data::font value) : Value(TypeTag::Font, std::make_shared<DataTypes::TFont>(value)) {
}
Value::Value(data::icon value) : Value(TypeTag::Icon, std::make_shared<DataTypes::TIcon>(value)) {
}
Value::Value(const FullName &type, data::fields value)
: type_ { TypeTag::Struct, type }
, data_(std::make_shared<DataTypes::TFields>(value)) {
}
Value::Value(TypeTag type, double value) : Value(type, std::make_shared<DataTypes::TDouble>(value)) {
if (type_.tag != TypeTag::Double) {
type_.tag = TypeTag::Invalid;
data_ = std::make_shared<DataBase>();
}
}
Value::Value(TypeTag type, int value) : Value(type, std::make_shared<DataTypes::TInt>(value)) {
if (type_.tag != TypeTag::Int && type_.tag != TypeTag::Pixels) {
type_.tag = TypeTag::Invalid;
data_ = std::make_shared<DataBase>();
}
}
Value::Value(TypeTag type, std::string value) : Value(type, std::make_shared<DataTypes::TString>(value)) {
if (type_.tag != TypeTag::String &&
type_.tag != TypeTag::Align) {
type_.tag = TypeTag::Invalid;
data_ = std::make_shared<DataBase>();
}
}
Value::Value(Type type, Qt::Initialization) : type_(type) {
switch (type_.tag) {
case TypeTag::Invalid: data_ = std::make_shared<DataBase>(); break;
case TypeTag::Int: data_ = std::make_shared<DataTypes::TInt>(0); break;
case TypeTag::Double: data_ = std::make_shared<DataTypes::TDouble>(0.); break;
case TypeTag::Pixels: data_ = std::make_shared<DataTypes::TInt>(0); break;
case TypeTag::String: data_ = std::make_shared<DataTypes::TString>(""); break;
case TypeTag::Color: data_ = std::make_shared<DataTypes::TColor>(data::color { 0, 0, 0, 255 }); break;
case TypeTag::Point: data_ = std::make_shared<DataTypes::TPoint>(data::point { 0, 0 }); break;
case TypeTag::Size: data_ = std::make_shared<DataTypes::TSize>(data::size { 0, 0 }); break;
case TypeTag::Align: data_ = std::make_shared<DataTypes::TString>("topleft"); break;
case TypeTag::Margins: data_ = std::make_shared<DataTypes::TMargins>(data::margins { 0, 0, 0, 0 }); break;
case TypeTag::Font: data_ = std::make_shared<DataTypes::TFont>(data::font { "", 13, 0 }); break;
case TypeTag::Icon: data_ = std::make_shared<DataTypes::TIcon>(data::icon {}); break;
case TypeTag::Struct: data_ = std::make_shared<DataTypes::TFields>(data::fields {}); break;
}
}
Value::Value(TypeTag type, std::shared_ptr<DataBase> &&data) : type_ { type }, data_(std::move(data)) {
}
} // namespace structure
} // namespace style
} // namespace codegen

View File

@ -1,230 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include <memory>
#include <vector>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtCore/QtMath>
namespace codegen {
namespace style {
namespace structure {
// List of names, like overview.document.bg
using FullName = QStringList;
inline std::string logFullName(const FullName &name) {
return name.join('.').toStdString();
}
struct Variable;
class Value;
enum class TypeTag {
Invalid,
Int,
Double,
Pixels,
String,
Color,
Point,
Size,
Align,
Margins,
Font,
Icon,
Struct,
};
struct Type {
TypeTag tag;
FullName name; // only for type == ClassType::Struct
explicit operator bool() const {
return (tag != TypeTag::Invalid);
}
};
inline bool operator==(const Type &a, const Type &b) {
return (a.tag == b.tag) && (a.name == b.name);
}
inline bool operator!=(const Type &a, const Type &b) {
return !(a == b);
}
namespace data {
struct point {
int x, y;
};
struct size {
int width, height;
};
struct color {
uchar red, green, blue, alpha;
QString fallback;
};
struct margins {
int left, top, right, bottom;
};
struct font {
enum Flag {
Bold = 0x01,
Italic = 0x02,
Underline = 0x04,
};
std::string family;
int size;
int flags;
};
struct monoicon;
struct icon {
std::vector<monoicon> parts;
};
struct field; // defined after Variable is defined
using fields = QList<field>;
} // namespace data
class Value {
public:
Value();
Value(data::point value);
Value(data::size value);
Value(data::color value);
Value(data::margins value);
Value(data::font value);
Value(data::icon value);
Value(const FullName &type, data::fields value);
// Can be only double.
Value(TypeTag type, double value);
// Can be int / pixels.
Value(TypeTag type, int value);
// Can be string / align.
Value(TypeTag type, std::string value);
// Default constructed value (uninitialized).
Value(Type type, Qt::Initialization);
Type type() const { return type_; }
int Int() const { return data_->Int(); }
double Double() const { return data_->Double(); }
std::string String() const { return data_->String(); }
data::point Point() const { return data_->Point(); }
data::size Size() const { return data_->Size(); };
data::color Color() const { return data_->Color(); };
data::margins Margins() const { return data_->Margins(); };
data::font Font() const { return data_->Font(); };
data::icon Icon() const { return data_->Icon(); };
const data::fields *Fields() const { return data_->Fields(); };
data::fields *Fields() { return data_->Fields(); };
explicit operator bool() const {
return type_.tag != TypeTag::Invalid;
}
Value makeCopy(const FullName &copyOf) const {
Value result(*this);
result.copyOf_ = copyOf;
return result;
}
const FullName &copyOf() const {
return copyOf_;
}
private:
class DataBase {
public:
virtual int Int() const { return 0; }
virtual double Double() const { return 0.; }
virtual std::string String() const { return std::string(); }
virtual data::point Point() const { return {}; };
virtual data::size Size() const { return {}; };
virtual data::color Color() const { return {}; };
virtual data::margins Margins() const { return {}; };
virtual data::font Font() const { return {}; };
virtual data::icon Icon() const { return {}; };
virtual const data::fields *Fields() const { return nullptr; };
virtual data::fields *Fields() { return nullptr; };
virtual ~DataBase() {
}
};
struct DataTypes;
Value(TypeTag type, std::shared_ptr<DataBase> &&data);
Type type_;
std::shared_ptr<DataBase> data_;
FullName copyOf_; // for copies of existing named values
};
struct Variable {
FullName name;
Value value;
QString description;
explicit operator bool() const {
return !name.isEmpty();
}
};
namespace data {
struct field {
enum class Status {
Uninitialized,
Implicit,
Explicit
};
Variable variable;
Status status;
};
struct monoicon {
QString filename;
Value color;
Value offset;
explicit operator bool() const {
return !filename.isEmpty();
}
};
} // namespace data
struct StructField {
FullName name;
Type type;
explicit operator bool() const {
return !name.isEmpty();
}
};
struct Struct {
FullName name;
QList<StructField> fields;
explicit operator bool() const {
return !name.isEmpty();
}
};
} // namespace structure
} // namespace style
} // namespace codegen

View File

@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/crash_reports.h"
#include "platform/platform_specific.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "core/launcher.h"
#include <signal.h>

View File

@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "platform/platform_launcher.h"
#include "platform/platform_specific.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "ui/main_queue_processor.h"
#include "core/crash_reports.h"
#include "core/update_checker.h"

View File

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "core/sandbox.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "mainwidget.h"
#include "mainwindow.h"
#include "storage/localstorage.h"

View File

@ -12,7 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_controller.h"
#include "core/application.h"
#include "media/player/media_player_instance.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "base/parse_helper.h"
#include "facades.h"

View File

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "core/update_checker.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "base/timer.h"
#include "base/bytes.h"
#include "base/unixtime.h"

View File

@ -31,7 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/streaming/media_streaming_loader.h" // unique_ptr<Loader>
#include "media/streaming/media_streaming_reader.h" // make_shared<Reader>
#include "boxes/abstract_box.h"
#include "platform/platform_info.h"
#include "passport/passport_form_controller.h"
#include "window/themes/window_theme.h"
#include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
@ -48,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_poll.h"
#include "data/data_scheduled_messages.h"
#include "data/data_cloud_themes.h"
#include "base/platform/base_platform_info.h"
#include "base/unixtime.h"
#include "facades.h"
#include "app.h"

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "ui/widgets/widgets.style";

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "ui/widgets/widgets.style";
using "boxes/boxes.style";

View File

@ -16,9 +16,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "storage/localstorage.h"
#include "core/file_utilities.h"
#include "platform/platform_info.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "base/platform/base_platform_info.h"
#include "base/unixtime.h"
#include "facades.h"
#include "styles/style_export.h"

View File

@ -13,7 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "media/clip/media_clip_reader.h"
#include "window/window_session_controller.h"
#include "history/history_item_components.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "data/data_peer.h"
#include "data/data_user.h"
#include "observer_peer.h"

View File

@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_cursor_state.h"
#include "chat_helpers/message_field.h"
#include "boxes/sticker_set_box.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "mainwindow.h"
#include "mainwidget.h"
#include "core/application.h"

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "dialogs/dialogs.style";
using "ui/widgets/widgets.style";

View File

@ -36,6 +36,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/message_field.h"
#include "chat_helpers/stickers.h"
#include "history/history_widget.h"
#include "base/platform/base_platform_info.h"
#include "base/unixtime.h"
#include "mainwindow.h"
#include "mainwidget.h"
@ -43,7 +44,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h"
#include "core/application.h"
#include "apiwrap.h"
#include "platform/platform_info.h"
#include "lang/lang_keys.h"
#include "data/data_session.h"
#include "data/data_media_types.h"

View File

@ -30,7 +30,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h"
#include "data/data_file_origin.h"
#include "core/file_utilities.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "window/window_peer_menu.h"
#include "window/window_session_controller.h"
#include "lang/lang_keys.h"

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "boxes/boxes.style";
using "ui/widgets/widgets.style";

View File

@ -29,7 +29,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h"
#include "styles/style_overview.h"
#include "styles/style_info.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "media/player/media_player_instance.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/confirm_box.h"

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "ui/widgets/widgets.style";
countryRipple: defaultRippleAnimation;

View File

@ -31,7 +31,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_slide_animation.h"
#include "window/window_connecting_widget.h"
#include "window/window_lock_widgets.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "data/data_user.h"
#include "window/themes/window_theme.h"
#include "lang/lang_cloud_manager.h"

View File

@ -10,9 +10,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "storage/serialize_common.h"
#include "storage/localstorage.h"
#include "platform/platform_info.h"
#include "boxes/confirm_box.h"
#include "lang/lang_file_parser.h"
#include "base/platform/base_platform_info.h"
#include "base/qthelp_regex.h"
namespace Lang {

View File

@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_translator.h"
#include "lang/lang_keys.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
namespace Lang {

View File

@ -38,7 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h"
#include "settings/settings_intro.h"
#include "platform/platform_notifications_manager.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "window/layer_widget.h"
#include "window/notifications_manager.h"
#include "window/themes/window_theme.h"

View File

@ -6,7 +6,7 @@ For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "ui/widgets/widgets.style";
using "overview/overview.style";

View File

@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h"
#include "core/file_utilities.h"
#include "core/mime_type.h"
#include "platform/platform_info.h"
#include "ui/widgets/popup_menu.h"
#include "ui/widgets/buttons.h"
#include "ui/image/image.h"
@ -42,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "window/window_session_controller.h"
#include "window/window_controller.h"
#include "main/main_account.h" // Account::sessionValue.
#include "base/platform/base_platform_info.h"
#include "base/unixtime.h"
#include "observer_peer.h"
#include "main/main_session.h"

View File

@ -6,7 +6,7 @@ For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "ui/widgets/widgets.style";
mediaviewOverDuration: 150;

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "history/history.style";
using "ui/widgets/widgets.style";
using "media/view/mediaview.style";

View File

@ -5,7 +5,7 @@ the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
using "basic.style";
using "ui/basic.style";
using "ui/widgets/widgets.style";
using "boxes/boxes.style";

View File

@ -9,7 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "passport/passport_panel_controller.h"
#include "lang/lang_keys.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "ui/widgets/input_fields.h"
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"

View File

@ -1,59 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/linux/info_linux.h"
#include <QLocale>
namespace Platform {
QString DeviceModelPretty() {
#ifdef Q_OS_LINUX64
return "PC 64bit";
#else // Q_OS_LINUX64
return "PC 32bit";
#endif // Q_OS_LINUX64
}
QString SystemVersionPretty() {
const auto result = getenv("XDG_CURRENT_DESKTOP");
const auto value = result ? QString::fromLatin1(result) : QString();
const auto list = value.split(':', QString::SkipEmptyParts);
return list.isEmpty() ? "Linux" : "Linux " + list[0];
}
QString SystemCountry() {
return QLocale::system().name().split('_').last();
}
QString SystemLanguage() {
const auto system = QLocale::system();
const auto languages = system.uiLanguages();
return languages.isEmpty()
? system.name().split('_').first()
: languages.front();
}
QDate WhenSystemBecomesOutdated() {
return QDate();
}
int AutoUpdateVersion() {
return 2;
}
QString AutoUpdateKey() {
if (IsLinux32Bit()) {
return "linux32";
} else if (IsLinux64Bit()) {
return "linux";
} else {
Unexpected("Platform in AutoUpdateKey.");
}
}
} // namespace Platform

View File

@ -1,55 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
#include "platform/platform_info.h"
namespace Platform {
inline constexpr bool IsLinux() {
return true;
}
inline constexpr bool IsLinux32Bit() {
#ifdef Q_OS_LINUX32
return true;
#else // Q_OS_LINUX32
return false;
#endif // Q_OS_LINUX32
}
inline constexpr bool IsLinux64Bit() {
#ifdef Q_OS_LINUX64
return true;
#else // Q_OS_LINUX64
return false;
#endif // Q_OS_LINUX64
}
inline constexpr bool IsWindows() { return false; }
inline constexpr bool IsWindowsStoreBuild() { return false; }
inline bool IsWindowsXPOrGreater() { return false; }
inline bool IsWindowsVistaOrGreater() { return false; }
inline bool IsWindows7OrGreater() { return false; }
inline bool IsWindows8OrGreater() { return false; }
inline bool IsWindows8Point1OrGreater() { return false; }
inline bool IsWindows10OrGreater() { return false; }
inline constexpr bool IsMac() { return false; }
inline constexpr bool IsMacOldBuild() { return false; }
inline constexpr bool IsMacStoreBuild() { return false; }
inline bool IsMac10_6OrGreater() { return false; }
inline bool IsMac10_7OrGreater() { return false; }
inline bool IsMac10_8OrGreater() { return false; }
inline bool IsMac10_9OrGreater() { return false; }
inline bool IsMac10_10OrGreater() { return false; }
inline bool IsMac10_11OrGreater() { return false; }
inline bool IsMac10_12OrGreater() { return false; }
inline bool IsMac10_13OrGreater() { return false; }
inline bool IsMac10_14OrGreater() { return false; }
} // namespace Platform

View File

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/linux/launcher_linux.h"
#include "platform/platform_info.h"
#include "base/platform/base_platform_info.h"
#include "core/crash_reports.h"
#include "core/update_checker.h"

View File

@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/mac/file_utilities_mac.h"
#include "platform/mac/mac_utilities.h"
#include "base/platform/mac/base_platform_mac_utilities.h"
#include "lang/lang_keys.h"
#include "styles/style_window.h"

View File

@ -1,9 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
For license and copyright information please follow this link:
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "platform/mac/info_mac.h"

Some files were not shown because too many files have changed in this diff Show More