mirror of
https://github.com/vale981/tdesktop
synced 2025-03-05 09:41:41 -05:00
Use lib_ui from submodule.
This commit is contained in:
parent
1b89348d89
commit
246c45ce0e
255 changed files with 122 additions and 61372 deletions
6
.gitmodules
vendored
6
.gitmodules
vendored
|
@ -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
|
||||
|
|
|
@ -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;
|
|
@ -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
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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 ©Of) const {
|
||||
Value result(*this);
|
||||
result.copyOf_ = copyOf;
|
||||
return result;
|
||||
}
|
||||
|
||||
const FullName ©Of() 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
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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
Loading…
Add table
Reference in a new issue